乐于分享
好东西不私藏

Claude Code源码解析-02-QueryEngine 推理引擎:Claude Code 的"大脑"到底怎么工作?

Claude Code源码解析-02-QueryEngine 推理引擎:Claude Code 的"大脑"到底怎么工作?

QueryEngine 推理引擎:Claude Code 的“大脑”到底怎么工作?

真正能落地的 AI,不是回答一次问题,而是能把任务一路做完。  当我们讨论 Claude Code 的工程能力时,最值得拆解的不是某个模型参数,而是它背后的执行中枢:QueryEngine。 这篇文章带你看清它如何在真实场景中完成“思考 -> 调工具 -> 再思考 -> 交付结果”的完整闭环。

概述:为什么 QueryEngine 值得关注?

推理引擎是 Claude Code 的”大脑”——一个 while(true) 循环,不断执行”思考 → 调用工具 → 收集结果 → 继续思考”,直到任务完成。这个循环被称为 Agentic Loop(智能体循环)
它的价值不只是“能调模型”,而是让系统从“对话助手”升级为“任务执行体”:
不只输出文字,还能主动调用工具完成动作     
不只一次回答,而是多轮迭代直到问题闭环     
不只追求聪明,更重视预算、稳定性和可恢复性     

核心文件:先看地图,再看细节

文件
职责
src/QueryEngine.ts
会话层封装(1296行)、会话管理、消息映射、转录
src/query.ts
核心推理循环(1730行)、queryLoop、模型调用、工具执行
src/query/deps.ts
依赖注入、callModel、压缩函数
src/utils/queryContext.ts
系统提示词、上下文组装
src/utils/queryHelpers.ts
消息转换SDK 消息标准化

架构分层:谁负责“对外”,谁负责“思考”?

typescript

┌───────────────────────────────────────────┐ │  调用方 (REPL / SDK / AgentTool)           │ │  for await (event of query({...}))        │ ├───────────────────────────────────────────┤ │                                           │ │  query() ─ 入口函数                        │ │    │                                      │ │    ▼                                      │ │  queryLoop() ─ 核心 while(true) 循环       │ │    │                                      │ │    ├─ 准备消息上下文                        │ │    ├─ 自动压缩 (autocompact)               │ │    ├─ Token 限制检查                       │ │    ├─ deps.callModel() ─ 流式 API 调用     │ │    ├─ 处理流式响应                          │ │    ├─ 执行工具调用                          │ │    ├─ 收集工具结果                          │ │    └─ 继续循环 / 返回终止                   │ │                                           │ ├───────────────────────────────────────────┤ │  deps (依赖注入)                           │ │  ├─ callModel → queryModelWithStreaming    │ │  ├─ microcompact → microcompactMessages   │ │  ├─ autocompact → autoCompactIfNeeded     │ │  └─ uuid → randomUUID                     │ └───────────────────────────────────────────┘

queryLoop 详细流程:一次任务是怎么跑完的?

这是整个系统最核心的循环,每一轮迭代对应一次”模型调用 + 可选工具执行”。可以把它理解为四个阶段:
1上下文准备:裁剪、压缩、拼系统提示词         
2预算校验:避免 token 爆仓         
3模型流式调用:边生成边识别工具调用         
4分支决策:无工具则结束,有工具则继续下一轮       

typescript

queryLoop() — while(true)   │   ├─ 1. 发射 stream_request_start 事件   │   ├─ 2. 查询链追踪 (queryTracking)   │     记录工具使用链和上下文   │   ├─ 3. 获取压缩边界后的消息   │     getMessagesAfterCompactBoundary()   │   ├─ 4. 工具结果预算控制   │     applyToolResultBudget() — 截断过大的工具输出   │   ├─ 5. 历史裁剪 (HISTORY_SNIP)   │     可选的消息历史截断   │   ├─ 6. 微压缩 (microcompact)   │     轻量级消息压缩   │   ├─ 7. 上下文折叠 (CONTEXT_COLLAPSE)   │     激进的上下文缩减策略   │   ├─ 8. 组装完整系统提示词   │     appendSystemContext(systemPrompt, systemContext)   │   ├─ 9. 自动压缩 (autocompact)   │     │  当 token 接近上下文窗口限制时:   │     │  - 调用压缩服务生成摘要   │     │  - 替换历史消息为压缩版本   │     │  - 更新 taskBudget 剩余量   │     │  - 发射压缩相关事件   │     ▼   │   ├─ 10. Token 阻塞检查   │      如果超出限制且无法压缩 → 返回错误   │   ├─ 11. ══════ 调用模型 ══════   │      deps.callModel(messages, tools, thinkingConfig, ...)   │      │   │      ├─ 流式接收 assistant 消息   │      ├─ 流式接收 thinking 块   │      ├─ 检测 tool_use 块   │      ├─ StreamingToolExecutor 并行预执行   │      └─ 错误恢复 (413/media/max_output)   │   ├─ 12. 后采样钩子 (post-sampling hooks)   │   ├─ 13. ═══ 分支判断 ═══   │      │   │      ├─ 无工具调用 (needsFollowUp = false)   │      │   ├─ 反应式压缩恢复   │      │   ├─ max_output_tokens 恢复   │      │   ├─ 运行 stop hooks   │      │   ├─ Token Budget 继续检查   │      │   └─ return { reason: 'completed' }   │      │   │      └─ 有工具调用 (needsFollowUp = true)   │          ├─ runTools() 或 StreamingToolExecutor   │          ├─ 收集工具结果消息   │          ├─ 获取附件消息 (attachments)   │          ├─ 内存/技能预取   │          ├─ maxTurns 检查   │          ├─ 更新 state.messages   │          └─ continue ──► 回到循环顶部   │   └─ 循环结束 → return Terminal

QueryEngine 与 query 的关系:两个名字,一套协同

typescript

┌─────────────────────────────────────────────┐ │  QueryEngine (SDK 层)                        │ │                                             │ │  submitMessage(prompt)                      │ │    │                                        │ │    ├─ processUserInput() — 解析输入          │ │    ├─ fetchSystemPromptParts() — 系统提示    │ │    ├─ buildSystemInitMessage() — 初始消息    │ │    │                                        │ │    ├─ for await (msg of query({...}))       │ │    │   │                                    │ │    │   └─ switch(msg.type)                  │ │    │       ├─ 'assistant' → SDKMessage       │ │    │       ├─ 'user'      → SDKMessage       │ │    │       ├─ 'progress'  → SDKStatus        │ │    │       ├─ 'stream_event' → 流式事件      │ │    │       ├─ 'attachment' → 附件处理        │ │    │       └─ 'tool_use_summary' → 摘要      │ │    │                                        │ │    └─ yield SDKMessage / result             │ │                                             │ │  管理: mutableMessages, transcript, usage   │ └─────────────────────────────────────────────┘          │          ▼ (共享核心) ┌─────────────────────────────────────────────┐ │  query() / queryLoop() (推理层)              │ │                                             │ │  - 模型调用 (callModel)                     │ │  - 工具执行 (runTools)                      │ │  - 压缩管理 (autocompact/microcompact)      │ │  - Token 预算 (tokenBudget)                 │ │  - 终止条件判断                              │ │                                             │ │  REPL 和 SDK 共享同一个 query() 函数         │ └─────────────────────────────────────────────┘

依赖注入设计:为什么这一步很工程化?

query.ts 通过 deps 对象实现依赖注入,便于测试和不同运行环境。这种设计直接决定了后续的可维护性和可扩展性:
更容易做单元测试和故障注入     
更容易替换模型能力或压缩策略     
让“主流程逻辑”与“具体实现细节”解耦     

typescript

// src/query/deps.ts export function productionDeps(): QueryDeps {   return {     callModel: queryModelWithStreaming,   // Anthropic API 流式调用     microcompact: microcompactMessages,   // 轻量压缩     autocompact: autoCompactIfNeeded,     // 自动压缩     uuid: randomUUID,                     // UUID 生成   } }

终止条件:什么时候这台“引擎”会停?

queryLoop 在以下情况终止:
终止原因
说明
completed
模型停止生成且无工具调用
abort
用户中断或超时
maxTurns
达到最大轮次限制
tokenLimit
Token 超出上下文窗口限制
error
API 错误且无法恢复
stopHook
停止钩子触发

关键设计特点:为什么它能从 Demo 走向生产?

1生成器模式query() 是一个 AsyncGenerator,通过 yield 流式返回事件,调用方用 for await 消费         
2状态不可变: 每轮循环通过解构获取状态,修改通过创建新状态对象         
3错误恢复: 对 413(过长)、media 错误、max_output_tokens 有自动恢复机制         
4流式工具执行StreamingToolExecutor 允许在模型生成过程中并行执行已完成的工具调用       

结语

QueryEngine 的核心意义,不在于“又封装了一层模型调用”,而在于它定义了一套可持续执行的智能体范式:
有循环,才能持续推进任务       
有预算,才能稳定落地生产       
有恢复,才能在真实世界里可靠运行     
如果你也在做 AI Agent,这套设计思路值得反复对照。

关注我们

如果这篇拆解对你有启发,欢迎 点赞 + 转发,让更多做 AI 工程的人看到。   关注公众号,下一篇我们会继续拆解 Claude Code 架构里的关键模块(工具系统 / 上下文管理 / 多智能体协同)。
你也可以在留言区告诉我:   你最想优先看哪一块源码分析?