02.1 Query Loop

关注源码

  • src/query.ts
  • src/query/config.ts
  • src/query/tokenBudget.ts
  • src/query/stopHooks.ts

核心心智模型

query.ts 不是“问一次模型拿一个字符串”,而是一台消息状态机。它把下面这些对象放进同一条事件流:

  • request start
  • assistant streaming 片段
  • tool use / tool result
  • compact boundary
  • tombstone / 恢复消息
  • tool use summary

因此它天然可以被:

  • TUI 消费
  • SDK/structured I/O 消费
  • 远端会话消费

循环里的稳定输入

进入 query() 时,会先冻结一批本轮稳定数据:

  • systemPrompt
  • userContext
  • systemContext
  • canUseTool
  • fallbackModel
  • querySource
  • QueryConfig

query/config.ts 还会把部分 runtime gate 快照化,例如:

  • streaming tool execution 是否开启
  • 是否发 tool use summary
  • 当前是否 ant 构建
  • fast mode 是否开启

这一步的意义是让一次 query 内部行为稳定,不被中途的 settings/gate 变化扰动。

循环里的可变状态

query.ts 维护的 State 主要包含:

  • messages
  • toolUseContext
  • autoCompactTracking
  • maxOutputTokensRecoveryCount
  • pendingToolUseSummary
  • stopHookActive
  • turnCount
  • transition

也就是说,query loop 的推进不是靠隐式全局变量,而是靠显式状态迁移。

主回合里真正发生什么

1. 组装模型输入

每一轮会把:

  • 历史消息
  • user/system context
  • 附件
  • tool schema

整理成发给模型的请求体。

2. 消费流式模型输出

模型输出可能产生:

  • 普通 assistant 内容
  • thinking / redacted thinking
  • tool use blocks
  • API error message

query loop 会边消费边构造新的 transcript message。

3. 进入工具阶段

如果 assistant 输出中带了 tool use,query.ts 会把这一轮转给 services/tools/* 执行,再把 tool result 重新写回 messages

4. 决定是否继续

继续的原因可能包括:

  • 模型刚刚拿到 tool result,需要下一轮继续思考
  • 达到 token budget continuation 条件
  • 发生了 max_output_tokens 恢复
  • 发生了 compact,需要基于摘要继续

关键恢复路径

max_output_tokens 恢复

query.tsmax_output_tokens 有专门恢复逻辑,并且会刻意“暂不向 SDK 暴露中间错误”,避免上层误判整个会话已经终止。

自动压缩 / reactive compact

当 token 紧张或 API 返回 prompt too long 时,query loop 会进入 compact 路径,把消息摘要化后再继续。

stop hooks

回合完成后,stop hooks 可能触发:

  • session memory
  • extract memories
  • prompt suggestion
  • 其他 post-turn side effects

因此“一轮 query 完成”不只是生成最后一条 assistant message。

设计上值得注意的点

1. 消息是第一公民

系统几乎所有重要事件都被表示为消息或消息边界,而不是只存在于内部回调里。这样可以:

  • 统一 transcript
  • 统一 UI
  • 统一远端同步

2. 恢复路径是主路径的一部分

压缩、fallback、恢复并不是异常分支,而是核心运行时内建能力。

3. query loop 尽量保持“稳定输入 + 显式状态”

这为后续抽出纯 reducer、做 SDK 复用或写测试提供了结构基础。