返回《Claude Code 架构设计指南》

主循环,一个 while(true) 驱动整个 Agent

IT周瑜

src/query.ts 里的 queryLoop 是整个程序的中枢。它是一个 async generator 函数,主体是一个 while(true) 循环,每一轮迭代完成一次完整的模型交互。压缩检查,调模型,收工具调用,执行工具,把结果拼回去,然后进入下一轮。模型不再请求工具时循环退出。全部两万行左右的核心逻辑,就挂在这个循环的各个阶段上。

先看循环体怎么管理状态。跨迭代的状态收在一个叫 State 的对象里,字段包括消息列表、工具上下文、自动压缩的跟踪信息、输出长度恢复的计数器等。每轮迭代开头把这些字段解构成局部变量,用起来就像普通函数参数。要进入下一轮时,代码构造一个全新的 State 整体替换旧的,绝不原地修改。这个写法有一个直接的好处,每次继续循环都是一次干净的重置,上一个状态里没有清理干净的残留不会悄悄漏进下一轮。

State 里最有意思的字段叫 transition,它记录上一轮为什么继续。正常走完一轮工具调用,值是 next_turn。出错后重试也走 continue,值各不相同。上下文折叠后要重排水,记 collapse_drain_retry。遇到请求过长错误做了响应式压缩,记 reactive_compact_retry。输出长度超限,先升级上限记 max_output_tokens_escalate,升级后还不够就注入一条续写提示记 max_output_tokens_recovery。注释里写明了这个字段的用途,让测试断言恢复路径确实触发过,不用去翻消息内容。

新一轮迭代 压缩前置检查 snip → microcompact → collapse → autocompact 调用 API 流式生成 边流式边收集 tool_use 块 本轮有 tool_use 吗 没有 Stop hook return 完成 执行工具 runTools 或 StreamingToolExecutor 结果拼回消息列表 tool_result 以 user 角色成对回灌 next_turn 进入下一轮 五个 continue 点 next_turn 正常下一轮 collapse_drain_retry reactive_compact_retry max_output_tokens escalate / recovery
图 2-1 单轮迭代的全貌,五个 continue 点各自带着 transition 原因

一轮迭代的顺序值得细看。开头先做压缩前置检查,按固定顺序过四道工序,snip 裁早期历史,microcompact 清旧工具结果,context collapse 折叠摘要,最后才轮到代价最高的 autocompact。这套顺序第 8 章展开。检查完毕后组装 API 请求开始流式生成。流式过程中每个 tool_use 块一到就收集起来,收集到与否是唯一的循环出口信号,代码注释特意提醒 stop_reason 字段并不可靠,不能拿它判断。

流式结束后进入工具执行阶段,产出若干条工具结果消息,拼回消息列表,构造下一轮的 State,continue。一轮没有收集到任何 tool_use,说明模型认为任务完成了,此时还有最后一道 Stop hook,用户注册的 hook 可以否决这次结束并把一段新内容塞回去强制再来一轮。hook 放行了,循环才 return,附带结束原因。

选 async generator 来做这件事有一层设计考虑。主循环生成的是消息和流事件,消费节奏由调用方决定。终端界面可以边收边画,无头的 SDK 模式可以攒起来转发,子代理可以收下来写进自己的记录。循环本身随时可以在任意 yield 点暂停,中断、限流、降速都落在消费侧,核心循环不用为任何一种前端写特殊分支。

query.ts 里还有一段工程师的幽默值得原文转述。thinking 块的处理规则旁边写着,这些规则冗长而碰巧,需要一位巫师长时间深思才能参透,大意是凡包含 thinking 块的消息必须属于 thinking 长度大于零的请求,thinking 块不能是消息里的最后一个块,同一段助手轨迹里的 thinking 块必须保留。注释结尾警告,不遵守这些规则的人将受到惩罚,被罚掉一整天来调试和薅头发。玩笑之下是真约束,第 3 章讲消息归一化时会看到代码怎样执行这三条。

输出超限的恢复策略也可以在这里交代。恢复尝试的上限是三次,常量名叫 MAX_OUTPUT_TOKENS_RECOVERY_LIMIT。第一次先尝试把输出上限升到六万四,仍然截断的话,注入一条要求模型直接续写的恢复消息再试。三次都失败才把错误抛给上层。每一级恢复都对应一个独立的 transition 值,出问题时从日志就能看出走到了哪一级。