第三节:上下文工具
上下文操作与查询

作者:小学子 📚 | 日期:2026年4月2日 | 第四阶段 · 模块四


# 第四阶段 · 模块四 · 第三节:上下文与工具调用

核心问题

工具调用结果如何影响上下文?上下文中的工具历史如何管理?ToolUseContext 与 mutableMessages 的关系是什么?


◇ 本节位置


        Claude Code 全局架构
        
        ┌─────────────────────────────────────────────────────────────────────┐
        │  查询引擎层                                                          │
        │                                                                      │
        │  query()                                                            │
        │  ├── mutableMessages ──> 上下文状态                                │
        │  └── ToolUseContext ──> 工具上下文 ← 本节                          │
        └─────────────────────────────────────────────────────────────────────┘
        


一、ToolUseContext

1.1 定义

源码位置:`src/Tool.ts` 第 158 行


        export type ToolUseContext = {
          options: {
            commands: Command[]
            debug: boolean
            mainLoopModel: string
            tools: Tools
            verbose: boolean
            thinkingConfig: ThinkingConfig
            mcpClients: MCPServerConnection[]
            // ...
          }
          abortController: AbortController
          readFileState: FileStateCache
          getAppState(): AppState
          setAppState(f: (prev: AppState) => AppState): void
          // ...
        }
        

1.2 五问分析

问 1:ToolUseContext 和 mutableMessages 的区别?


        // mutableMessages:对话消息历史
        mutableMessages: Message[] = [
          userMessage,
          assistantMessage,
          toolResultMessage,  // 工具结果作为消息追加
        ];
        
        // ToolUseContext:工具执行的环境
        ToolUseContext = {
          options: { tools, commands, ... },
          abortController,  // 可取消操作
          readFileState,    // 文件状态缓存
          getAppState,      // 应用状态
        };
        

问 2:为什么需要 ToolUseContext?


        // 工具执行需要访问:
        // 1. 可用工具列表
        // 2. 命令列表
        // 3. 文件状态缓存
        // 4. 中止控制器
        
        // 这些信息不应该放在消息历史里
        // 而是通过 ToolUseContext 传递给工具
        


二、工具结果与上下文

2.1 工具结果追加


        // 在 query() 中
        for await (const msg of query({ messages, toolUseContext })) {
          // 每次收到消息
          mutableMessages.push(msg);
        
          // 如果是工具调用,需要追加工具结果
          if (isToolUseMessage(msg)) {
            const result = await executeTool(msg, toolUseContext);
            mutableMessages.push(createToolResultMessage(result));
          }
        }
        

2.2 完整流程


        用户: "Read the file a.txt"
            │
            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │  mutableMessages                                                    │
        │                                                                      │
        │  [0] { type: 'user', content: 'Read the file a.txt' }              │
        └─────────────────────────────────────────────────────────────────────┘
            │
            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │  API 返回(工具调用)                                                 │
        │                                                                      │
        │  [1] { type: 'assistant', content: [{ type: 'tool_use',           │
        │       name: 'Read', input: { file_path: 'a.txt' } }]               │
        └─────────────────────────────────────────────────────────────────────┘
            │
            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │  工具执行                                                            │
        │                                                                      │
        │  const result = await readFile('a.txt');                           │
        │  // result = "Hello World"                                         │
        └─────────────────────────────────────────────────────────────────────┘
            │
            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │  工具结果追加                                                        │
        │                                                                      │
        │  [2] { type: 'user', role: 'tool',                                │
        │        content: 'Hello World',                                      │
        │        tool_use_id: 'xxx' }                                       │
        └─────────────────────────────────────────────────────────────────────┘
            │
            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │  API 返回(最终回复)                                                 │
        │                                                                      │
        │  [3] { type: 'assistant', content: 'The file contains: Hello World' }
        └─────────────────────────────────────────────────────────────────────┘
        

2.3 五问分析

问 1:工具结果放在消息历史里?


        // 是的,工具结果作为 user 消息(role: 'tool')追加
        {
          type: 'user',
          role: 'tool',
          content: 'file content...',
          tool_use_id: 'xxx',  // 关联到哪个工具调用
        }
        

问 2:工具结果的大小有限制吗?


        // 有,上下文 token 限制
        // 如果工具结果太大,会触发压缩
        // 或者被截断
        
        if (result.tokens > MAX_TOOL_RESULT_TOKENS) {
          result.content = truncate(result.content, MAX_TOOL_RESULT_TOKENS);
          result.truncated = true;
        }
        

问 3:工具可以访问历史消息吗?


        // 通过 ToolUseContext?不直接
        // 工具只能看到传入的 input
        
        class ReadTool {
          async execute(input, context) {
            // 只能看到 file_path,不知道之前的对话
            const filePath = input.file_path;
            return readFile(filePath);
          }
        }
        


三、上下文中的工具历史

3.1 工具历史的作用


        // 工具历史用于:
        // 1. 让模型知道之前调用了哪些工具
        // 2. 理解工具调用的上下文
        // 3. 避免重复调用
        
        // 模型看到的是:
        [user] Read a.txt
        [assistant] <tool_use name="Read">
        [user] <tool_result>Hello World</tool_result>
        [assistant] The file contains Hello World
        

3.2 五问分析

问 1:工具调用失败会怎样?


        // 工具失败作为错误消息追加
        {
          type: 'user',
          role: 'tool',
          content: 'Error: File not found',
          tool_use_id: 'xxx',
          is_error: true,
        }
        

问 2:工具可以返回多个结果吗?


        // 一次工具调用只能有一个结果
        // 如果需要多个结果,分多次调用
        
        // 错误
        { tool_use_id: 'xxx', content: ['result1', 'result2'] }
        
        // 正确
        { tool_use_id: 'xxx', content: 'result1\n---\nresult2' }
        

问 3:工具结果可以包含结构化数据吗?


        // 可以是任意文本
        // 模型负责解析
        
        {
          type: 'user',
          role: 'tool',
          content: JSON.stringify({
            files: ['a.txt', 'b.txt'],
            count: 2,
          }),
          tool_use_id: 'xxx',
        }
        


四、工具与状态管理

4.1 AppState


        // AppState 存储应用级别的状态
        interface AppState {
          messages: Message[];      // 等同于 mutableMessages
          tools: Tool[];          // 可用工具
          model: string;          // 当前模型
          // ...
        }
        
        // 通过 ToolUseContext 访问
        context.getAppState();   // 读取状态
        context.setAppState(f);  // 修改状态
        

4.2 setAppState 的作用


        // 工具可以修改应用状态
        async function WriteTool(input, context) {
          // 写入文件
          await writeFile(input.file_path, input.content);
        
          // 更新文件状态缓存
          context.setAppState(prev => ({
            ...prev,
            fileCache: updateFileCache(prev.fileCache, input.file_path),
          }));
        }
        

4.3 五问分析

问 1:为什么工具不能直接修改消息历史?


        // 错误做法
        mutableMessages.push(newMessage);  // ✗
        
        // 正确做法:通过 setAppState
        context.setAppState(prev => ({
          ...prev,
          messages: [...prev.messages, newMessage],
        }));
        

问 2:abortController 的作用?


        // 用户可以取消正在执行的工具
        context.abortController.abort();
        
        // 工具需要检查取消信号
        async function longRunningTool(input, context) {
          if (context.abortController.signal.aborted) {
            throw new Error('Cancelled');
          }
          // ...
        }
        


五、思考题

思考题 1:工具结果和普通消息的区别?

答案

方面工具结果普通消息
role'tool''user'/'assistant'
tool_use_id
is_error可能为 true


思考题 2:如何避免工具重复调用?

答案


        // 1. 模型根据历史决定
        // 2. 如果之前调用过,会复用结果
        // 3. 工具实现可以检查缓存
        
        class ReadTool {
          async execute(input, context) {
            // 检查缓存
            const cached = context.readFileState.get(input.file_path);
            if (cached) return cached;
            // ...
          }
        }
        


思考题 3:工具可以读取引用消息吗?

答案


        // 工具的 input 中可以包含引用
        // 但不是直接读取消息历史
        
        // 模型会决定是否引用之前的消息
        // 工具只需要处理 input
        
        {
          type: 'tool_use',
          name: 'Edit',
          input: {
            file_path: 'a.txt',
            old_text: '${previous_message.content}',  // 引用
            new_text: 'updated',
          },
        }
        


六、延伸阅读

文件核心内容
`src/Tool.ts`ToolUseContext 定义
`src/query.ts`query 循环中的工具调用


七、下节预告

下一节我们将深入 上下文限制与优化

- 上下文 token 限制

- 内存优化策略

- 大型对话的处理


*- 第一轮:□ 事实准确性*

*- 第二轮:□ 深度与洞见*

*- 第三轮:□ 可读性与价值*