State 是 queryLoop 唯一的跨轮可变量,所有"记忆"都在这里:
typescript
type State = {
// 完整消息历史
messages: Message[]
// 工具上下文,包含 AbortController、工具列表、agentId 等,迭代内可能被更新
toolUseContext: ToolUseContext
// 自动压缩跟踪
autoCompactTracking: AutoCompactTrackingState | undefined
// max_output_tokens 恢复重试次数(上限3)
maxOutputTokensRecoveryCount: number
// 是否已尝试,防止 reactive compact 陷入无限循环的守卫位
hasAttemptedReactiveCompact: boolean
// 临时 token 上限覆盖,设置后下一轮将以 64k token 上限重试
maxOutputTokensOverride: number | undefined
// 异步预生成的工具摘要,上一轮工具调用摘要的异步 Promise,下一轮 yield 给前端
pendingToolUseSummary: Promise<ToolUseSummaryMessage | null> | undefined
// stop hook 是否活跃(防止 hook 错误触发死循环)
stopHookActive: boolean | undefined
// 当前轮次,用于 maxTurns 限制检查
turnCount: number
// 上一轮 continue 的原因
transition: Continue | undefined
}
迭代内临时变量
这些变量每轮重新声明,相当于当前轮的"草稿纸":
typescript
// 流式收集本轮模型产生的所有 assistant 消息(包括思考、文本、tool_use)
const assistantMessages: AssistantMessage[] = []
// 工具执行完毕后的结果消息,将作为下一轮模型的输入
const toolResults: (UserMessage | AttachmentMessage)[] = []
// 从流中提取的所有 tool_use 块,决定本轮需要调用哪些工具
const toolUseBlocks: ToolUseBlock[] = []
// 只要流中出现了 tool_use 块就置为 true,是"有工具要执行"的信号
let needsFollowUp = false
迭代末尾,这些"草稿"被合并进持久状态:
typescript
state.messages = [...messagesForQuery, ...assistantMessages, ...toolResults]
状态枚举:继续循环
以下场景触发state = next; continue,即不退出,开始下一轮:
next_turn------ 正常推进
触发条件:模型输出了 tool_use,工具执行完毕,一切正常。 这是最常见的 continue 原因,代表 agent 正在正常工作中。工具结果被追加到 messages 后进入下一轮,让模型基于结果继续推理。collapse_drain_retry------ 上下文折叠恢复
触发条件:收到 HTTP 413(prompt too long),且 context collapse 功能开启,且本轮并非已经从 collapse 中恢复过。 做法是将部分上下文"折叠压缩"释放空间,然后用更短的 messages 重新请求模型,不增加 turnCount。reactive_compact_retry------ 响应式压缩恢复
触发条件:收到 413 或媒体大小错误,尝试对整个对话做一次 compact(生成摘要替换历史)。hasAttemptedReactiveCompact被设为 true,确保此路径只走一次,防止无限压缩。max_output_tokens_escalate------ 提升 token 上限
触发条件:模型输出被max_output_tokens截断,且本轮还没有尝试过提升上限。 下一轮将以 64k tokens 的上限重试同样的请求。此路径只触发一次(由maxOutputTokensOverride === undefined守卫)。max_output_tokens_recovery------ 注入恢复提示
触发条件:max_output_tokens 截断,且 escalate 也没有解决(或不适用)。 向对话注入一条 meta 消息,告知模型"输出被截断,直接继续,不要道歉",最多重试 3 次(由maxOutputTokensRecoveryCount < 3守卫)。stop_hook_blocking------ Stop Hook 阻塞重试
触发条件:模型给出了正常响应(非 API 错误),stop hook 执行后发现有阻塞性错误需要反馈给模型。 将 hook 产生的错误消息追加到 messages,让模型看到并修正。stopHookActive 被设为 true 以标记 hook 正在活跃。token_budget_continuation------ Token Budget 未耗尽
触发条件:TOKEN_BUDGET 功能开启,模型给出了响应,但 budget 检查认为还有配额,应该继续。 向对话注入一条"nudge"消息引导模型继续输出,直到 budget 耗尽或任务完成。
状态枚举 退出循环
以下场景触发 return { reason: '...' },循环终止:
blocking_limit
token 数量已经达到硬性上限,且用户关闭了 autocompact。此时不允许继续,输出错误消息后退出。这是一个"安全阀",为用户保留手动运行 /compact 的空间。model_error/image_error
模型调用抛出了无法恢复的异常,或图片大小超限且无法压缩恢复。aborted_streaming
用户在模型流式输出过程中按下了中断(Ctrl+C)。aborted_tools
用户在工具执行过程中中断。hook_stopped
某个工具的 hook 明确返回了"阻止继续"的指令shouldPreventContinuation = true。prompt_too_long
收到 413,且所有恢复手段(collapse drain + reactive compact)都已用尽,只能放弃。stop_hook_prevented
stop hook 明确返回了preventContinuation: true,通常表示某个外部策略要求停止。completed
正常完成。分两种情况:- 模型返回了 API 错误(非 tool_use)→ 上报 failure hook 后退出
- 模型给出正常文本响应,stop hook 无异议,token budget 也耗尽或不需要继续 → 正常完成
max_turns
turnCount超过了调用方传入的 maxTurns 上限。
latex
queryLoop 进入
│
▼
初始化 state(第268行)
turnCount=1, transition=undefined
│
╔═════════════════════════════════════════════╗
║ while(true) 循环体 ║
║ ║
║ Step 1: 消息预处理 ║
║ snip(截断过长历史) ║
║ → microcompact(轻量压缩) ║
║ → contextCollapse(上下文折叠) ║
║ → autoCompact(token超阈值时自动压缩) ║
║ │ ║
║ Step 2: blocking_limit 检查 ║
║ token超限且autocompact关闭? ║
║ 是 ──────────────────────────────► return ║
║ │ 否 'blocking_ ║
║ Step 3: callModel() 流式调用 limit' ║
║ │ ║
║ ┌─────▼──────────────────────┐ ║
║ │ 内层 while(attemptWithFallback)│ ║
║ │ 收集 assistantMessages │ ║
║ │ 收集 toolUseBlocks │ ║
║ │ 设置 needsFollowUp=true │ ║
║ │ FallbackTriggeredError? │ ║
║ │ → 切换模型,continue(内层) │ ║
║ └─────────────────────────────┘ ║
║ │ ║
║ 其他异常 ──────────────────────► return ║
║ │ 'model_error' ║
║ 用户中断 ──────────────────────► return ║
║ │ 'aborted_streaming' ║
║ │ ║
║ ┌──────▼──────────────────────────────┐ ║
║ │ needsFollowUp == false? │ ║
║ │ (模型没有产生 tool_use) │ ║
║ └──────┬──────────────────────────┬───┘ ║
║ true │(无工具) false │(有工具)║
║ │ │ ║
║ ┌───────▼──────────┐ ┌────────────▼──────┐ ║
║ │ 停止条件分支 │ │ 工具执行分支 │ ║
║ │ │ │ │ ║
║ │ ①413恢复路径: │ │ runTools()执行 │ ║
║ │ collapse_drain │ │ 所有 toolUseBlocks│ ║
║ │ → continue │ │ │ ║
║ │ reactive_compact│ │ 用户中断? │ ║
║ │ → continue │ │ → return │ ║
║ │ 都失败 → return │ │ 'aborted_tools' │ ║
║ │ │ │ │ ║
║ │ ②max_output截断: │ │ hook阻止? │ ║
║ │ escalate │ │ → return │ ║
║ │ → continue │ │ 'hook_stopped' │ ║
║ │ recovery(×3) │ │ │ ║
║ │ → continue │ │ maxTurns超限? │ ║
║ │ 耗尽 → yield err │ │ → return │ ║
║ │ │ │ 'max_turns' │ ║
║ │ ③API error? │ │ │ ║
║ │ → return │ │ 一切正常: │ ║
║ │ 'completed' │ │ messages合并 │ ║
║ │ │ │ turnCount++ │ ║
║ │ ④stop hook检查: │ │ transition= │ ║
║ │ prevented │ │ 'next_turn' │ ║
║ │ → return │ │ state=next │ ║
║ │ blocking │ │ → continue ────┐ │ ║
║ │ → continue │ └────────────────│──┘ ║
║ │ │ │ ║
║ │ ⑤token budget: │ │ ║
║ │ continue budget │ │ ║
║ │ → continue │ │ ║
║ │ done │ │ ║
║ │ → return │ │ ║
║ │ 'completed' │ │ ║
║ └──────────────────┘ │ ║
║ │ return │ ║
╚═════════╪═══════════════════════════════╪════╝
│ │
▼ ┌──────────┘
循环终止 │ 回到循环顶部
▼
解构新的 state
开始下一轮
| 函数 | 触发的状态流转 |
|---|---|
callModel() |
驱动主循环,通过流产生 assistantMessages 和 toolUseBlocks |
runTools() |
执行工具,填充toolResults,可触发 aborted_tools/hook_stopped |
handleStopHooks() |
返回 preventContinuation/blockingErrors,触发 stop_hook_* 系列转换 |
contextCollapse.recoverFromOverflow() |
触发 collapse_drain_retry |
reactiveCompact.tryReactiveCompact() |
触发 reactive_compact_retry |
checkTokenBudget() |
触发 token_budget_continuation或 completed |
calculateTokenWarningState() |
触发 blocking_limit |
isMaxOutputTokensTruncated() |
触发 max_output_tokens_escalate/recovery |