本章总览
查询引擎是 Claude Code 的心脏。本章深入 query.ts 与 QueryEngine.ts:tool_use 循环如何启动、何时停止、SDK 与交互式 CLI 如何共用同一套逻辑。
总览图
渲染图表中…
学完本章你应该能
- 读懂 while(true) 主循环各阶段
- 区分 QueryEngine 与 query() 的职责
- 理解 StreamingToolExecutor 在循环中的位置
核心概念(先读懂这些)
一轮 turn 的生命周期
用户消息入队 → compact 准备 → 调 API(stream) → 解析 content blocks。源码不依赖 API stop_reason 判断循环,而是在流式阶段只要看到 tool_use 就设置 needsFollowUp;有 tool 就执行工具并进入 next_turn,无 tool 才进入 Stop hooks / token budget / Terminal 出口。
QueryEngine 是 SDK 外壳
Headless/IDE 插件不跑 REPL,而是 new QueryEngine().submitMessage(),消费 AsyncGenerator<SDKMessage>。内部仍调用同一个 query(),保证行为一致。
建议学习步骤
- 对照 queryLoop 伪代码逐步推演
- 查看调用方表理解入口多样性
- 阅读「核心流程」串联启动到结束
query.ts 主循环(活动图)
渲染图表中…
QueryEngine vs query 职责划分
渲染图表中…
StreamingToolExecutor 调度(状态图)
渲染图表中…
QueryEngine 类
职责
SDK/Headless 模式的查询生命周期管理器。封装 submitMessage() 为 AsyncGenerator,供外部 SDK 消费。
export class QueryEngine {
private config: QueryEngineConfig
private mutableMessages: Message[]
private abortController: AbortController
async *submitMessage(prompt, options?): AsyncGenerator<SDKMessage> {
// 1. processUserInput — 斜杠命令拦截
// 2. fetchSystemPromptParts — 组装系统提示
// 3. query() — 主循环
// 4. yield SDKMessage 流
}
}
query.ts 主循环
export async function* query(deps, config): AsyncGenerator {
while (true) {
// 1. stream_request_start
// 2. microcompact / autocompact
// 3. call Claude API (streaming)
// 4. needsFollowUp? execute tools : turn-end pipeline
// 5. return Terminal or continue next_turn
}
}
关键子模块
| 文件 | 职责 |
|---|---|
| query/config.ts | 构建 query 运行时配置 |
| query/tokenBudget.ts | Token 预算追踪 |
| query/transitions.ts | Continue/Terminal 状态机 |
| query/stopHooks.ts | 停止钩子 |
| query/deps.ts | 依赖注入 (productionDeps) |
StreamingToolExecutor
并发模型
concurrency-safe 工具(Read/Grep/WebFetch)可并行;exclusive 工具(Bash/Edit)独占队列。Bash 错误触发 siblingAbortController 终止兄弟进程但不 abort 整个 turn。
调用方 (CodeGraph)
| 调用方 | 类型 | 文件 |
|---|
本章小结与延伸
掌握 query 循环 = 掌握 Agent 行为。所有工具、权限、压缩都是循环内的钩子。 继续学习: