# 第四阶段 · 模块四 · 第三节:上下文与工具调用
工具调用结果如何影响上下文?上下文中的工具历史如何管理?ToolUseContext 与 mutableMessages 的关系是什么?
Claude Code 全局架构
┌─────────────────────────────────────────────────────────────────────┐
│ 查询引擎层 │
│ │
│ query() │
│ ├── mutableMessages ──> 上下文状态 │
│ └── ToolUseContext ──> 工具上下文 ← 本节 │
└─────────────────────────────────────────────────────────────────────┘
源码位置:`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:ToolUseContext 和 mutableMessages 的区别?
// mutableMessages:对话消息历史
mutableMessages: Message[] = [
userMessage,
assistantMessage,
toolResultMessage, // 工具结果作为消息追加
];
// ToolUseContext:工具执行的环境
ToolUseContext = {
options: { tools, commands, ... },
abortController, // 可取消操作
readFileState, // 文件状态缓存
getAppState, // 应用状态
};
问 2:为什么需要 ToolUseContext?
// 工具执行需要访问:
// 1. 可用工具列表
// 2. 命令列表
// 3. 文件状态缓存
// 4. 中止控制器
// 这些信息不应该放在消息历史里
// 而是通过 ToolUseContext 传递给工具
// 在 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));
}
}
用户: "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' }
└─────────────────────────────────────────────────────────────────────┘
问 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);
}
}
// 工具历史用于:
// 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
问 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',
}
// AppState 存储应用级别的状态
interface AppState {
messages: Message[]; // 等同于 mutableMessages
tools: Tool[]; // 可用工具
model: string; // 当前模型
// ...
}
// 通过 ToolUseContext 访问
context.getAppState(); // 读取状态
context.setAppState(f); // 修改状态
// 工具可以修改应用状态
async function WriteTool(input, context) {
// 写入文件
await writeFile(input.file_path, input.content);
// 更新文件状态缓存
context.setAppState(prev => ({
...prev,
fileCache: updateFileCache(prev.fileCache, input.file_path),
}));
}
问 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');
}
// ...
}
答案:
| 方面 | 工具结果 | 普通消息 |
| role | 'tool' | 'user'/'assistant' |
| tool_use_id | 有 | 无 |
| is_error | 可能为 true | 无 |
答案:
// 1. 模型根据历史决定
// 2. 如果之前调用过,会复用结果
// 3. 工具实现可以检查缓存
class ReadTool {
async execute(input, context) {
// 检查缓存
const cached = context.readFileState.get(input.file_path);
if (cached) return cached;
// ...
}
}
答案:
// 工具的 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 限制
- 内存优化策略
- 大型对话的处理
*- 第一轮:□ 事实准确性*
*- 第二轮:□ 深度与洞见*
*- 第三轮:□ 可读性与价值*