篇章二 · 2.2
queryLoop 主循环
Agent 的本质是什么?一个循环:调模型 → 看它要用工具吗 → 用了就执行再回来调模型 → 没用就结束。Claude Code 把这个循环写到了工业级。
一个
while (true),六种重入query.ts 里的 queryLoop() 是个 AsyncGenerator,体内就是 while (true)。源码注释里残留的 query_recursive_call 检查点暴露了它的历史:最初是递归写法,后来重构成循环——因为递归会栈溢出,循环不会。
1
前置处理压缩 · 折叠 · 预取(6 道工序)
2
调用 API流式接收 · 边收边执行工具
3
判断后续有工具调用 → 执行后 continue;没有 → 收尾
4
附件与队列处理附件 · 消费排队的命令
第 1 步 · 前置处理的 6 道工序
每次把消息发给 API 之前,先过六道「瘦身」工序——按成本从零到高排列,能省一层是一层:
aSkill 预取异步预载可能用到的技能
bContent replacement工具结果的预算管理
cSnip compact裁掉最老的消息段
dMicrocompact清空旧 tool_result 内容
eContext collapse把工具调用折叠成摘要
fAutocompact超阈值 → 全量压缩
d/e/f 三道是篇章六的主角,这里先记住它们的位置:在循环里、每轮 API 调用之前。
State.transition · 六种「为什么又转一圈」
| transition | 触发场景 | 性质 |
|---|---|---|
| next_turn | 模型调了工具,执行完接着来 | 正常流 |
| reactive_compact_retry | API 返回 413(上下文超限),压缩后重试 | 救火 |
| max_output_tokens_recovery | 单次输出超长,调整后续发 | 救火 |
| stop_hook_blocking | Stop hook 拦住了收尾,重试 | 救火 |
| collapse_drain_retry | 上下文折叠后排空重试 | 救火 |
| token_budget_continuation | token 预算续发 | 救火 |
设计味道:与其写 6 个异常分支函数,不如把「重新进循环」统一成一个带原因标记的 State——循环体自己看原因决定怎么处理。
救火队挑战 · 你来扳道岔
已处理:0 起
点「接收警情」——警情在环轨上炸响,你选一条岔路把循环救回来。
本站要点:Agent = 循环,但工业级 Agent 的循环里长满了「救火通道」——5/6 的重入原因都是异常恢复。下一站下沉一层,看循环第 2 步调用的 API 层:它为什么敢放弃官方 SDK,以及那只 90 秒的看门狗。