QueryEngine:会话入口
QueryEngine.ts 是学习 Claude Code Agent 主循环的好入口,因为它比交互式 REPL.tsx 更少 UI 噪音。
QueryEngine 的职责可以概括为:
text
保存会话状态
-> 接收一条新用户输入
-> 处理命令、附件、系统上下文
-> 调用 query()
-> 把 query() 的流式事件转换成 SDK/headless 输出
-> 更新会话状态1
2
3
4
5
6
2
3
4
5
6
源码锚点
阅读时可以抓住两个位置:
QueryEngineConfig:定义 headless/SDK 会话需要的所有依赖,包括cwd、tools、commands、mcpClients、canUseTool、getAppState、setAppState、模型配置和预算。QueryEngine.submitMessage():处理一次用户输入,把用户输入、命令、附件、系统初始化消息和会话状态组合起来,然后调用query()。
这说明 QueryEngine 的重点不是“直接调用模型”,而是把一轮提交变成可执行的 Agent 查询。
为什么不是直接从 REPL 开始
REPL.tsx 负责很多交互式产品细节:
- 输入框。
- 权限弹窗。
- 消息列表。
- 通知。
- IDE 集成。
- 背景任务。
- prompt suggestion。
这些对产品很重要,但不是 Agent 主循环的最小核心。
QueryEngine 更接近一个纯会话引擎。它把 messages、tools、commands、mcpClients、canUseTool、getAppState 等依赖放进 QueryEngineConfig,然后通过 submitMessage() 启动一轮。
QueryEngine 的关键状态
一个 Agent 会话至少需要这些状态:
ts
type AgentSession = {
messages: Message[]
tools: Tool[]
abortController: AbortController
totalUsage: Usage
readFileCache: FileStateCache
}1
2
3
4
5
6
7
2
3
4
5
6
7
Claude Code 的 QueryEngine 比这复杂得多,但本质仍然是保存一组可跨轮复用的状态。
设计启发
自己写 Agent 时,可以先照这个分层:
text
CLI / SDK 输入层
-> QueryEngine
-> query loop
-> ModelClient
-> ToolExecutor1
2
3
4
5
2
3
4
5
这样 UI、SDK、测试都可以复用同一个 QueryEngine。
小结
QueryEngine 不是 Agent 的全部,但它是一个很好的边界:它接收用户输入,维护会话状态,把真正复杂的模型和工具循环交给 query()。