引擎:主循环与 Prompt 编排
query.ts 是整个系统的心脏——1729 行的状态机。从请求链路到 prompt 组装,拆解 Claude Code 的核心引擎。
打开 Claude Code,输入一条指令,背后发生了什么?从按下回车到模型开始回答,请求经过了一条固定的链路:cli.tsx 收到输入,main.tsx 完成初始化(注册工具、构造 ToolUseContext、建立渲染层),然后进入 query(),再进入 queryLoop()——一个 while(true) 主循环。
这个循环不会主动退出。它一直跑,直到模型返回 stop reason、token budget 耗尽、或者用户按下 Ctrl+C。
query.ts 的状态机设计
query.ts 有 1729 行,是整个系统最核心的文件。它实现的不是递归调用,而是 async generator + while(true) + 显式 state 对象的状态机。
早期版本用递归:模型调用工具,工具执行完再递归调用模型,一轮一轮往下。这个方案在短对话里没问题,但长会话会爆栈。现在的设计把状态显式存在对象里,每次迭代读取上一轮的输出,决定下一步怎么走。
每次循环迭代走 10 个步骤,按顺序执行。
首先是上下文预处理,四道压缩:删除过期工具结果、截断超长输出、合并连续同类消息、触发 summary 压缩。目的是在进 API 前把 token 用量压到合理范围。接着做 Token 预算检查,计算当前上下文长度,判断是否要强制压缩或终止。
然后调用模型 API,把完整上下文发出去,开启流式响应。收到响应后流式处理——已完整输出的 tool_use block 立即开始执行,不等后面的 block,这是 Streaming Tool Execution 的关键,后面单独讲。
中间插着错误恢复(API 失败/超时/拒绝的处理)和 Stop hooks(模型输出结束后触发,外部脚本可以检查内容并决定是否继续),以及 Token budget 更新(记录本轮消耗)。
最后三步:工具执行(对本轮所有 tool_use 调用走完整 14 步 pipeline,第三章详述)、附件注入(把执行结果作为 tool_result 写回上下文),再决定是否继续——stop reason 是 tool_use 就回到步骤一,是 end_turn 就退出循环。
Streaming Tool Execution
这是一个值得单独说的优化。
传统做法是等模型输出完成,拿到完整的 tool_use 列表,再批量执行。Claude Code 不是这样——流式处理时,每当一个 tool_use block 的 JSON 输入完整收到(不需要等后面的 block),就立即开始执行这个工具。
结果是:如果模型决定先读文件、再搜代码、再编辑,文件读取在模型还在输出搜代码的指令时就已经在跑了。用户看到的响应速度快了一截,不是因为模型更快,而是工具执行和模型输出在并行。
Prompt 组装
每次进入 API 调用前,getSystemPrompt() 把 system prompt 组装好。它返回一个字符串数组,分两部分:静态部分和动态部分,中间用 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 标记分隔。
这个边界存在的理由是 prompt cache。Anthropic API 支持缓存 prompt 前缀——只要前缀字节级一致,后续请求不需要重新处理这部分。静态内容放前面,不变,缓存命中率高;动态内容放后面,每次根据会话状态重算。
静态部分包含:身份定位(Claude Code 是什么)、系统运行规范、行为规范、风险动作规范、工具语法说明、语气风格、输出效率要求。这些内容在一个会话里不会变,适合缓存。
动态部分按需注入:session guidance(基于当前任务类型)、Memory(项目 CLAUDE.md 和用户全局 CLAUDE.md 的内容)、环境信息(操作系统、当前目录、git 状态)、MCP server instructions(已连接的 MCP server 各自的使用说明)。
Prompt Section Registry
动态 section 有两种创建方式,差异很关键。
systemPromptSection() 创建的 section 会被缓存——内容一旦确定,在同一会话里不会重算。DANGEROUS_uncachedSystemPromptSection() 创建的 section 每次调用 API 前都会重新执行生成函数,拿到最新值。
名字里的 DANGEROUS 不是说有安全风险,是提醒:这个 section 会破坏 prompt cache 前缀,每次请求都要重新处理整个 system prompt,成本更高。用它的地方必须有明确理由——比如需要实时注入当前时间、或者当前会话的动态状态。
行为规范的具体内容
getSimpleDoingTasksSection() 生成的行为规范是 system prompt 里直接约束模型行为的部分,值得列出来看:不添加用户没有要求的功能,不做过度抽象,不给没有修改的代码添加注释,不做不必要的错误处理,先阅读代码再修改代码,不给时间估计,遇到失败先诊断再重试。
这些规则的存在说明:不加约束的话,模型默认行为会做这些事。行为规范是 Anthropic 工程师通过大量实测发现问题后,一条条加进去的。
参考来源: 本文内容参考 Xiao Tan(@tvytlx)的《Claude Code 源码架构深度解析 V2.0》,基于原报告的分析框架和研究成果整理。