# 第四阶段 · 模块四 · 第一节:上下文状态管理
上下文是如何管理的?消息状态是如何维护的?上下文与工具调用的关系是什么?
Claude Code 全局架构
┌─────────────────────────────────────────────────────────────────────┐
│ 查询引擎层(QueryEngine) │
│ │
│ QueryEngine │
│ ├── mutableMessages ──> 上下文状态 ← 本节 │
│ ├── permissionDenials │
│ └── totalUsage │
└─────────────────────────────────────────────────────────────────────┘
源码位置:`src/QueryEngine.ts` 第 186 行
export class QueryEngine {
private mutableMessages: Message[] = [];
constructor(config: QueryEngineConfig) {
this.mutableMessages = config.initialMessages ?? [];
}
async *submitMessage(prompt): AsyncGenerator<SDKMessage> {
// 1. 添加用户消息
this.mutableMessages.push(createUserMessage(prompt));
// 2. 调用 query()
for await (const message of query({
messages: this.mutableMessages,
// ...
})) {
// 3. 添加助手消息
this.mutableMessages.push(message);
yield toSDKMessage(message);
}
}
}
问 1:为什么叫 mutableMessages?
// 消息列表是可变的(mutable)
// 每次对话后都会追加新消息
mutableMessages: Message[] = [];
// Turn 1
mutableMessages.push(userMsg);
mutableMessages.push(assistantMsg);
// Turn 2
mutableMessages.push(userMsg2); // 追加
mutableMessages.push(assistantMsg2); // 追加
问 2:消息的类型有哪些?
type Message =
| UserMessage
| AssistantMessage
| ToolUseMessage
| ToolResultMessage
| SystemMessage;
问 3:消息的结构?
interface UserMessage {
type: 'user';
content: string | ContentBlock[];
timestamp: number;
}
interface AssistantMessage {
type: 'assistant';
content: ContentBlock[];
stopReason?: string;
}
用户输入: "Hello"
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ QueryEngine.submitMessage() │
│ │
│ 1. mutableMessages.push(userMessage) │
│ │
│ 2. for await (const msg of query({ messages: mutableMessages })) │
│ │ │
│ ├── API 调用 │
│ └── 返回 AssistantMessage │
│ │
│ 3. mutableMessages.push(assistantMessage) │
│ │
│ 4. yield toSDKMessage(msg) │
└─────────────────────────────────────────────────────────────────────┘
问 1:消息追加的顺序?
// 1. 用户消息先追加
this.mutableMessages.push(createUserMessage(prompt));
// 2. 然后调用 query()
for await (const msg of query({ messages: this.mutableMessages })) {
// 3. 每次收到 API 响应就追加
this.mutableMessages.push(msg);
yield msg;
}
问 2:工具调用的消息如何处理?
// ToolUse 消息
{
type: 'assistant',
content: [{
type: 'tool_use',
name: 'Read',
input: { file_path: 'a.txt' },
}]
}
// ToolResult 消息
{
type: 'user',
role: 'tool',
content: 'file content...',
tool_use_id: 'xxx',
}
问 3:上下文大小有限制吗?
// API 有 token 限制
const MAX_CONTEXT_TOKENS = 200000;
// 检查是否超限
if (countTokens(mutableMessages) > MAX_CONTEXT_TOKENS) {
// 触发 compact
await compact();
}
// query() 接收消息列表
for await (const message of query({
messages: this.mutableMessages, // 传递消息历史
tools: allowedTools,
})) {
// 处理消息
}
// 工具通过 ToolContext 访问上下文
interface ToolContext {
messages: Message[]; // 对话历史
cwd: string; // 当前目录
}
// 工具示例:读取历史中的文件操作
class Read {
async execute(args, context) {
// 检查历史中是否有相同的文件读取
const previousReads = context.messages
.filter(msg => msg.type === 'tool_result')
.filter(msg => msg.tool_name === 'Read');
// 如果之前读取过,可以返回缓存
}
}
问 1:工具可以修改上下文吗?
// 工具执行后,结果会作为 ToolResultMessage 添加
// 工具本身不能直接修改消息历史
const toolResult = await tool.execute(args);
// ToolResultMessage 会自动追加到 mutableMessages
this.mutableMessages.push(toolResultMessage);
问 2:上下文溢出怎么办?
// 超出限制时触发 compact
async function checkContextLimit() {
const usage = await countTokens(mutableMessages);
const limit = getModelLimit();
if (usage > limit * 0.9) {
await compact(); // 压缩上下文
}
}
┌─────────────────────────────────────────────────────────────────────┐
│ 用户消息 │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ AssistantMessage (思考) │
│ content: [TextBlock] │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ AssistantMessage (工具调用) │
│ content: [ToolUseBlock] │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ UserMessage (工具结果) │
│ type: 'user', role: 'tool' │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ ... (继续循环) │
└─────────────────────────────────────────────────────────────────────┘
问 1:每轮对话的状态?
// 初始状态
mutableMessages = [];
// 用户发送消息
mutableMessages = [userMessage];
// API 返回助手消息(可能包含工具调用)
mutableMessages = [userMessage, assistantMessage];
// 工具返回结果
mutableMessages = [userMessage, assistantMessage, toolResultMessage];
// 继续循环...
问 2:stopReason 是什么?
// 助手消息的 stopReason 表示结束原因
{
type: 'assistant',
content: [...],
stopReason: 'tool_use' | 'end_turn' | 'max_tokens',
}
问题:关闭程序后,上下文状态会丢失吗?
答案:
// 默认不持久化
// 每次启动都是新的会话
// 如果需要持久化,使用 session
const engine = new QueryEngine({
sessionId: 'abc123', // 指定 session
});
// session 会自动保存消息历史
问题:什么时候清理上下文?
答案:
// 时机:
// 1. 用户执行 /clear
// 2. 用户执行 /compact
// 3. 自动 compact(上下文超限)
// 4. 新会话开始
问题:多个并发对话,上下文如何隔离?
答案:
// 每个对话有独立的 QueryEngine 实例
const engine1 = new QueryEngine(); // 对话1
const engine2 = new QueryEngine(); // 对话2
// 各自有独立的 mutableMessages
engine1.mutableMessages !== engine2.mutableMessages // true
| 文件 | 核心内容 |
| `src/QueryEngine.ts` | 上下文管理 |
| `src/types/message.ts` | 消息类型 |
| `src/query.ts` | 核心循环 |
下一节我们将深入 上下文压缩机制:
- autoCompact 的触发条件
- 压缩算法
- 压缩后的消息格式
*- 第一轮:□ 事实准确性*
*- 第二轮:□ 深度与洞见*
*- 第三轮:□ 可读性与价值*