背景
本文研究 Claude Code 单次 agentic turn 的核心驱动逻辑,即 queryLoop 状态机及其在上下文超限、输出截断、hook 拦截等情况下的恢复/终止路径。
文件位置:src/query.ts(query() 289-391 行,queryLoop() 393-2057 行)
queryLoop 是单轮 agentic turn 的核心状态机,用 while(true) + 显式 state 对象驱动。每次迭代代表一次"模型调用 → 工具执行 → 消息合并",只要模型持续产出 tool_use,就会不断 continue 循环;直到模型给出不含工具调用的最终回复(或触发终止/恢复条件),才会 return 退出。
query() 是外层薄壳:创建/复用 Langfuse trace、调用 queryLoop、在 finally 里做 autonomy 收尾和内存清理,自身不参与状态机逻辑。
代码仓库:github.com/claude-code...
1. 流程图
图中省略了容错分支(fallback 重试、abort 中断、hook 强制停止等),只保留主干。这些分支在下方对应小节里说明。
2. 终止条件汇总
queryLoop 的 while(true) 循环没有单一的"退出点",return 散落在好几处检查里。本章把这些 return 路径按代码里出现的先后顺序串一遍;每种"怎么恢复、恢复失败前还试了什么"的具体机制,仍然在第 3 章对应小节展开,这里只说清楚"什么条件下会真正终止、终止时的 Terminal.reason 是什么"。
needsFollowUp判据 (3.2 节)------每轮迭代第一个分岔口:本轮 assistant 消息里有没有出现tool_use块。没有才会往下走终止/恢复检查;有的话直接进入工具执行、下一轮迭代(next_turn),不会终止。- 硬阻塞上限检查 (3.3 节,
query.ts:790-846)------发生在消息预处理链之后、真正调模型之前:token 数已经踩到硬上限,且没有自动压缩/响应式压缩/context-collapse 任何一种机制兜底时,直接return { reason: 'blocking_limit' },把决定权交还给用户手动/compact。 - Prompt-too-long / 媒体过大两级恢复失败 (3.5 节,
query.ts:1372-1470)------collapse drain、reactive compact 都救不回来,return { reason: 'prompt_too_long' }(或媒体错误对应的image_error)。 - Max-output-tokens 三级恢复耗尽 (3.5 节,
query.ts:1475-1543)------64k 升级、最多 3 次续写都用完了,把之前压住没抛出的错误消息真正yield出去,随后走到下面的 API 错误分支终止。 - API 错误消息 (3.5 节,
query.ts:1549-1555)------lastMessage.isApiErrorMessage(rate limit、认证失败等,含上一条恢复耗尽后的情形),说明模型没产出真实响应,return { reason: 'model_error' },跳过 stop hooks 复核。 - Stop hook 强制终止 (3.5 节,
query.ts:1568-1570)------外部 Stop hook 脚本返回preventContinuation: true,不管模型自己怎么想,直接return { reason: 'stop_hook_prevented' }。 - maxTurns 硬终止 (3.6 节,
query.ts:1802, 2033)------无论是被中断还是正常完成一轮工具调用,只要nextTurnCount > maxTurns就return { reason: 'max_turns' };这是唯一纯粹基于计数、无恢复余地的终止条件。 - 正常完成 (3.5 节)------上面所有检查都没触发、Stop hook 也没要求继续、
TOKEN_BUDGET也判定该结束,才真正return { reason: 'completed' }。
此外还有中断类的 aborted_streaming/aborted_tools(用户主动打断)和工具执行后 hook 要求停止的 hook_stopped,这两类不是"恢复失败"的产物,而是外部信号直接打断循环,具体触发点见 3.5、3.6 节及第 4 章速查表。
3. 关键机制说明
3.1 进入循环前后的准备与收尾
query() 外层(query.ts:289-391):只做三件事,本身不影响状态机走向。
- Trace 生命周期:Langfuse 是接入的 LLM 可观测性平台,
langfuseTrace(src/services/langfuse/tracing.ts)是这次 agentic turn 的一条调用链路记录,串联本轮调了什么模型、执行了哪些工具、每一步的输入输出,供事后调试/监控查看。toolUseContext.langfuseTrace如果没传入(父 agent 场景),query()内部自己createTrace()新建,ownsTrace = true;如果调用方已经传入(子 agent 场景,AgentTool的runAgent.ts会提前用createSubagentTrace()建好再传进来),就直接复用,不重复建(ownsTrace = false)。isLangfuseEnabled()门控:未启用时createTrace直接返回null,后续相关调用什么都不做(query.ts:292-306)。
queryLoop 每轮迭代开头(query.ts:460-521):
queryTracking(chainId/depth):首次迭代生成新chainId,之后每轮depth + 1,写回toolUseContext,用于全链路埋点关联(query.ts:504-521)。messagesForQuery剥离toolUseResult:每轮请求前对user消息做浅拷贝并删除toolUseResult字段,只保留message.content发给 API,防止长会话里大文件读取结果永久占用内存;用浅拷贝而非原地修改,避免和 UI 渲染产生竞态(query.ts:525-553)。- skill/tool 预取在迭代开头 (模型调用之前)就发起,与模型流式和工具执行重叠进行,工具执行完之后才消费结果(发起见
query.ts:484-493,消费见 3.7 附件消息注入);记忆预取用using声明在while循环外 ,每个 user turn 只发起一次,而不是每轮迭代都发(query.ts:454-457)。
3.2 State 设计与重置、终止判据
State(query.ts:261-274)是 queryLoop 里 while(true) 循环的循环变量集合:messages、toolUseContext、压缩/恢复计数器等字段,只要还需要"活过这一圈、带到下一圈继续用",就属于这里------不管下一圈是因为模型正常产出 tool_use 需要执行工具再继续,还是因为 413/max-tokens/hook 拦截(3.5)需要恢复重试,都是同一套机制。
跨迭代可变字段收敛到这一个对象后,每次进入下一轮迭代都是整体替换 state = { ... },而不是逐字段更新------新增一条恢复路径时不会因为漏更新某个字段引入 bug。state.transition 显式记录"上一次为什么进入下一轮",方便测试断言具体走了哪条恢复路径。
transition.reason 共有 7 种取值,对应 6 处字面 continue 语句加上 next_turn:
| reason | 位置 | 触发场景 |
|---|---|---|
collapse_drain_retry |
query.ts:1402 |
命中 413(prompt-too-long),把此前暂存但未提交的 context-collapse 折叠结果强制提交(recoverFromOverflow),如果确实清出了新内容(committed > 0)就用折叠后的消息重试;上一轮已经走过这条路径时不会重复触发,避免死循环 |
reactive_compact_retry |
query.ts:1452 |
413 或媒体过大,且 collapse drain 没能解决(或未开启),整体做一次响应式压缩(reactiveCompact.tryReactiveCompact,区别于 3.3 的自动 autocompact),压缩成功则用压缩后的历史重试;hasAttemptedReactiveCompact 置 true,避免压缩后仍 413 时无限重试 |
max_output_tokens_escalate |
query.ts:1507 |
命中 max-output-tokens 且当前用的是默认 8k 上限,直接把上限提到 64k 重发同一请求(不注入任何消息);每轮最多触发一次 |
max_output_tokens_recovery |
query.ts:1538 |
64k 上限依然被截断(或本来就不满足 escalate 条件),注入一条 isMeta 用户消息要求模型"直接继续、不要道歉或重复",最多重试 3 次(maxOutputTokensRecoveryCount),超过则放弃恢复、把错误消息真正 yield 出去 |
stop_hook_blocking |
query.ts:1595 |
模型无 tool_use 主动收尾,但 Stop hook(用户/项目配置的外部脚本)返回 decision: 'block' 否决结束,把阻断理由包装成消息塞回历史,强迫模型重新看一眼再继续 |
token_budget_continuation |
query.ts:1630 |
模型无 tool_use 就主动收尾,但本轮消耗量还不到预算的 90%(turnTokens < budget * 0.9),怀疑提前收尾,注入 nudge 消息强制继续 |
next_turn |
query.ts:2053 |
模型正常产出 tool_use,执行完工具需要继续对话(最常见路径),非字面 continue------循环体正常执行到末尾、state = next 后自然回到 while(true) 顶部 |
next_turn 是这 7 种里唯一"一切正常"的一条,核心动作是消息合并 :messagesForQuery.concat(assistantMessages, toolResults) 按顺序拼接三段内容------
messagesForQuery:这轮请求发出去之前的历史;assistantMessages:模型这轮流式吐出的 assistant 消息(含触发工具执行的tool_use块);toolResults:工具这轮执行完产出的tool_result/附件消息。
拼接结果写入 state.messages,作为下一轮请求的输入------这样下一轮模型才能看到"我刚才调了什么工具、结果是什么",从而继续对话。
驱动"走不走 next_turn 这条路径"的判据是 needsFollowUp:是否为 true 取决于本轮 assistant 消息里是否出现了 tool_use 内容块 ,而不是模型返回的 stop_reason 字段(query.ts:756, 1090-1093)。这是有意为之:不同 provider/SDK 对 stop_reason 的支持程度不一致,但 tool_use 块是否存在可以直接观测。这一个布尔值直接决定循环走"收尾恢复"分支(3.5 节)还是"执行工具并进入 next_turn"分支,是整个状态机最核心的分支点。
3.3 消息预处理链与硬阻塞检查
真正发请求前,消息历史依次经过以下五层------层与层是叠加而非互斥关系,且严格按此顺序执行:
applyToolResultBudget(query.ts:567-582,实现在src/utils/toolResultStorage.ts:925):按 wire message(发给 API 前,同角色连续消息会被normalizeMessagesForAPI合并成的最终消息单元)分组聚合tool_result体积,超过阈值(getPerMessageBudgetLimit())就把该组里体积最大的若干条结果写入磁盘、原地替换成一段提示信息 (persistToolResult+buildLargeToolResultMessage,不是摘要),替换决定按tool_use_id跨轮次冻结(seenIds/replacements),保证同一条结果不会来回变化、破坏 prompt cache;snipCompactIfNeeded(HISTORY_SNIPfeature,query.ts:589-598):不做自动判断,纯粹是执行用户已下达的删除指令 ------用户手动执行/force-snip时会把当时全部历史消息的 uuid 记录成一个snip_boundary标记消息,之后每轮这里负责把标记里列出的 uuid 从发给模型的视图中过滤掉(只影响模型视角,UI 里仍能看到完整历史);deps.microcompact(query.ts:601-624,实现在src/services/compact/microCompact.ts):只针对固定几类工具(读文件/shell/grep/glob/web 等)的旧结果生效,走两条独立路径------按时间 (距上条 assistant 消息过久、prompt cache 已过期)触发时整体清空这些工具的历史结果内容(替换为固定文案[Old tool result content cleared]);按数量阈值触发时则通过 API 的 cache-editing 能力直接删除更早的工具结果(不修改本地消息数组);两种都不生成摘要;contextCollapse.applyCollapsesIfNeeded(CONTEXT_COLLAPSEfeature,query.ts:638-645):把旧消息折叠成摘要,但只在"读取时"生效------投影出一份给这次请求用的精简视图,不修改原始历史数组,折叠记录本身持久化,下次进来重新 replay 一遍即可;deps.autocompact(query.ts:652-741,实现在src/services/compact/autoCompact.ts):前面几步都不够、历史仍然超出阈值时触发的整体压缩,用一次额外的模型调用把整段历史总结成摘要(compactConversation),成功后用postCompactMessages整体替换messagesForQuery,后续都基于压缩后的历史继续。
五层跑完之后,是硬阻塞上限检查 (query.ts:790-846):命中真实 API 413 之前主动拦截,返回 blocking_limit,目的是给用户留出手动 /compact 的空间。三个独立条件命中任意一个都会跳过这个检查:querySource 是 compact/session_memory 这类内部 forked agent(本身就是为了压缩历史,被拦住会死锁);已开启自动压缩且 reactive compact 也启用;collapseOwnsIt(reactive-compact/context-collapse 的恢复机制已经接管)。也就是说这个检查并非只由"是否关闭自动压缩"单一开关决定,而是这三个条件的合取取反。
硬阻塞检查通过之后、真正调模型之前,还有一次预测性 autocompact (query.ts:848-888):复用第 5 步同一个 deps.autocompact,但触发依据不同------不看"当前历史是否已超阈值",而是基于 estimateMaxTurnGrowth 预估这一轮模型输出会新增多少 token,提前判断加上这部分增量会不会撑破 context window,避免"这轮请求本身没超限,但这轮输出会让下一轮直接超限"。
3.4 模型调用与 Fallback
deps.callModel(query.ts:899-949)是实际发起模型请求、流式读取 assistant 消息的地方。顶层入参决定这次请求发什么内容:messages(prependUserContext(messagesForQuery, userContext),即 3.3 预处理链跑完之后的历史)、systemPrompt(fullSystemPrompt,已拼接 systemContext)、tools(toolUseContext.options.tools)、signal(用于中断);此外还有一个 options 字段,装了 model/maxOutputTokensOverride/taskBudget/langfuseTrace/queryTracking 等参数。
外层 while (attemptWithFallback)(query.ts:894-1212)允许在模型调用抛出 FallbackTriggeredError 时整体重试:清空本次已产出的 assistantMessages/toolResults/toolUseBlocks,丢弃并重建 streamingToolExecutor(避免旧 tool_use_id 的孤儿结果混入新一轮请求),切换到 fallbackModel 后重新流式调用。
流式阶段对 prompt-too-long、媒体过大、max-output-tokens 这几类可能可恢复的错误先不 yield(withheld = true,query.ts:1052-1078),继续推进 assistantMessages,留到收尾阶段(3.5 节的两级/三级恢复)判断能否恢复;恢复成功则这条错误消息相当于从未发生过,避免 UI 上出现"看似失败又自动消失"的中间状态。
3.5 收尾恢复机制(needsFollowUp = false 时)
只有本轮未产生 tool_use 才会进入这一整块(query.ts:1349-1647),内部按顺序尝试多种恢复手段,任何一步恢复成功都会 continue,全部失败或模型正常完成才会 return。
Prompt-too-long / 媒体过大两级恢复 (query.ts:1372-1470):
- Collapse drain (先试,成本低):把已暂存但未提交的折叠清空提交,如果清出新内容就重试;用
transition.reason !== 'collapse_drain_retry'防止同一错误反复走这条路径。 - Reactive compact (兜底,成本高):整体做一次响应式压缩,用
hasAttemptedReactiveCompact标志防止压缩后仍 413 时无限重试。
两级都失败则 return { reason: 'prompt_too_long' }(或媒体错误的 image_error)。
Max-output-tokens 三级恢复 (query.ts:1475-1543):
- 升级重试(一次性):若用的是默认 8k 上限,直接升到 64k 重发同一请求;
- 多轮续写恢复(最多 3 次):注入一条
isMeta用户消息要求模型"直接继续"; - 超过 3 次:放弃恢复,把错误消息真正 yield 出去。
Stop hooks 续跑 (query.ts:1549-1645):这一段要解决的问题是------模型自己觉得"这轮做完了",但这个判断不是最终结论,还要经过两轮独立检查复核,才能真正放行结束。
第一轮,判断"模型这次是不是真的正常做完了":若 lastMessage.isApiErrorMessage(rate limit、认证失败等),说明模型根本没产出真实响应,只是报错了------这种情况直接触发 stop failure hooks 并 return { reason: 'model_error' },跳过后面所有检查(对一个不存在的正常响应做复核没有意义,硬跑只会导致"报错→hook 拦截→重试→又报错"的死循环)。
确认是正常完成后,依次经过两轮复核:
handleStopHooks------把这轮结果交给外部配置的 Stop hook 脚本评审,脚本有两种否决方式:返回blockingErrors表示"理由不充分,退回重做",把理由塞进历史消息里、continue让模型重新审视;返回preventContinuation表示"不管模型怎么想,必须立刻停",直接return,比模型自己的判断更强硬。(走"退回重做"分支时,hasAttemptedReactiveCompact会保留而不重置,防止"压缩失败→报错→hook 拦截→又触发压缩"的无限循环。)TOKEN_BUDGET------如果上一步没有被拦下,再检查这次任务预算花得够不够:checkTokenBudget发现消耗量远没到预算的 90%,就判定"可能是提前收尾",注入一条 nudge 消息、continue强制模型继续干;预算判定应该结束时才放行。
以上全部没触发,才真正 return { reason: 'completed' }------模型主动结束且没有任何机制要求它继续的唯一退出点。
Stop failure hooks (executeStopFailureHooks,query.ts:1461, 1468, 1550)是与上面 handleStopHooks 平行但独立的一套 hook:只在恢复失败/API 错误这几条失败路径上触发,用于失败态的外部通知,不参与"是否继续"的判断(那是 handleStopHooks 的职责)。
3.6 工具执行两种模式
streamingToolExecutor:模型还在流式输出时,只要某个tool_use块已经完整到达,就立刻开始并发执行它,不等模型说完。执行过程中产生的进度消息(progress)不等工具跑完,随时产生随时 yield;工具的最终结果(tool_result)则是这个工具执行完才 yield,谁先完成谁先出,模型说完后再用getRemainingResults()收尾剩下还没跑完的。runTools:等模型完整输出结束、拿到全部toolUseBlocks后才开始,内部按并发安全性分批调度、批次间严格串行。
两者不是根据本轮工具情况动态选择 ,而是在 query() 调用一开始通过灰度开关一次性定死(config.gates.streamingToolExecution),跟当前这一轮模型输出了什么工具、多少个、是否可并发都无关。唯一的运行时变化是:触发 fallback 重试时会把旧的 executor 丢弃重建,但仍是同一套"用不用流式"的机制。
两者判断"某个工具是否并发安全"的依据一致(都调用工具定义的 isConcurrencySafe(input)),但调度机制不同:runTools(toolOrchestration.ts)是拿到这一轮全部 toolUseBlocks 后,先静态分组(连续的并发安全工具合成一批,不安全的单独成批),再按批次顺序执行;streamingToolExecutor 没有预先分组,工具是流式陆续到达的,每来一个就动态判断"当前正在跑的工具里有没有不安全的、这个新工具能不能立刻插进去一起跑",遇到暂时排不上的不安全工具就停止往后派发,以维持顺序。
3.7 附件消息注入与 maxTurns 硬终止
工具执行完之后才注入 AttachmentMessage(type: 'attachment',query.ts:1894-1954),它是消息历史里独立于 assistant/user 的一种类型,专门承载工具执行之外产生的旁路信息。这里注入的三类来源:
- 队列命令 (
getAttachmentMessages,query.ts:1894-1904):本轮模型/工具还在跑的时候被塞进队列的命令,例如后台任务(run_in_background的 shell/agent)状态变化的提醒------如检测到某个后台命令卡在交互式输入上,会生成一条通知说明情况并建议改用非交互方式重跑,转成附件消息让模型在当前轮次里看到并响应; - 记忆预取 (
query.ts:1913-1928):3.1 节提到的、每个 user turn 只发起一次的pendingMemoryPrefetch,在这里消费其结果; - 技能/工具发现预取 (
query.ts:1930-1954):3.1 节提到的 skill/tool prefetch,在迭代开头发起、这里消费。
之所以要等工具执行完之后才注入,是因为 API 不允许 tool_result 消息和普通 user 消息交错------必须等所有 tool_result 就位后,才能安全追加其他类型消息。记忆预取和技能/工具预取都是"尽力而为":如果 Promise 还没 settle,直接跳过、留给下一轮迭代再检查。
附件消息注入完之后,紧接着做 maxTurns 硬终止 检查:无论是"被中断"还是"正常完成一轮工具调用"之后,都会检查 nextTurnCount > maxTurns(query.ts:1802, 2033)。这是唯一一条纯粹基于计数、无恢复余地的硬停止条件,用来防止 agentic 循环失控地跑下去。
3.8 其他旁路机制
Cache warning (query.ts:1260-1284,实现在 src/utils/cacheWarning.ts):提醒用户"这次请求的 prompt cache 命中率过低",命中率低意味着更贵、更慢------通常是历史被频繁改动(压缩、工具结果替换等)打断了缓存前缀导致的。用最后一条 assistant 消息的 usage 算出 cache_read_input_tokens 占总输入的比例,低于阈值(默认 80%,settings.json 可配置)就提示,同时给出比上一次请求命中率是升是降的趋势;同一 querySource 首次请求不报(没有基线可比)。必须在 executePostSamplingHooks 之前执行,确保警告消息排在工具结果之前显示;仅在交互式会话且未关闭该提醒(isCacheWarningEnabled())时生效。这条消息纯粹是流给上层 UI 看的旁路消息,不进入历史(呼应 3.2 的 next_turn 消息合并)。
task_budget (params.taskBudget,API beta 特性):给这一整个 agentic turn 设一个 token 预算上限,随请求传给模型,让模型自己感知"还剩多少额度"、据此安排何时收尾------是服务端主导的机制。客户端唯一要做的事是维护 taskBudgetRemaining(loop-local 变量,不放进 State):未压缩历史时服务端能看到完整对话自己倒计时,不用客户端插手;一旦发生压缩,服务端就只能看到压缩后的摘要,会漏算被压缩掉的那部分消耗,所以客户端要在两处压缩发生点用 finalContextTokensFromLastResponse 算出"压缩前的窗口大小",从 remaining 里扣掉这一块,跨多次压缩要累加(query.ts:702-713, 1422-1433)。
TOKEN_BUDGET (tokenBudget/budgetTracker/checkTokenBudget):跟上面完全独立的另一套机制,方向也相反------不是"预算快用完了提醒收尾",而是"预算还没花够、模型却主动说做完了,就怀疑它提前收尾、强制继续"。是纯客户端侧的续跑判断,具体机制见 3.5 节 stop hooks 续跑部分。两者可以同时存在于同一个请求里,互不影响。
4. 速查表
4.1 Continue / Return 一览
queryLoop 的 while(true) 循环每轮迭代最终只有两种去向:continue(构造新 state,进入下一轮迭代)或 return(结束整个 queryLoop,附带一个 Terminal 结果)。下表汇总所有出现过的具体分支------continue 行对应 3.2 节 state.transition.reason 的 7 种取值,return 行对应 3.1 节 Terminal.reason 的取值,触发条件列说明每一种具体在什么情况下会走到这里。
| 类型 | reason | 触发条件 |
|---|---|---|
| continue | collapse_drain_retry |
prompt-too-long 且折叠 drain 有新内容 |
| continue | reactive_compact_retry |
prompt-too-long/媒体过大,reactive compact 成功 |
| continue | max_output_tokens_escalate |
首次命中 max tokens,升级到 64k |
| continue | max_output_tokens_recovery |
max tokens 恢复重试(≤3 次) |
| continue | stop_hook_blocking |
stop hook 返回阻断消息 |
| continue | token_budget_continuation |
token 预算要求继续 |
| continue | next_turn |
正常工具调用完成 |
| return | blocking_limit |
硬阻塞上限触发 |
| return | image_error |
图片错误 / 媒体恢复失败 |
| return | model_error |
模型调用抛出未预期异常,或 API 错误消息(rate limit 等) |
| return | prompt_too_long |
两级恢复都失败 |
| return | aborted_streaming |
流式阶段被中断 |
| return | aborted_tools |
工具执行阶段被中断 |
| return | hook_stopped |
工具执行后 hook 要求停止 |
| return | stop_hook_prevented |
stop hook 直接阻止继续 |
| return | max_turns |
超过 maxTurns |
| return | completed |
模型完成且无任何恢复/续跑机制触发 |
4.2 消息类型速览
queryLoop 生成器签名(query.ts:397-404)里 yield 的类型,加上 Message.type(MessageType)的全部取值,对照如下:
| 类型 | 说明 | 本文对应位置 |
|---|---|---|
AssistantMessage(type: 'assistant') |
模型输出,可能含 tool_use/text/thinking 块 |
3.2 终止判据、3.4 模型调用 |
UserMessage(type: 'user') |
用户输入,或工具执行后生成的 tool_result |
3.2 消息合并、3.3 预处理链 |
SystemMessage(type: 'system') |
系统级提示,如 fallback 切换通知、cache warning | 3.4 Fallback、3.8 cache warning |
AttachmentMessage(type: 'attachment') |
工具执行之外的旁路信息:队列命令、记忆/技能/工具预取结果 | 3.7 附件消息注入 |
ProgressMessage(type: 'progress') |
工具执行期间的流式进度上报 | 未展开,属于工具执行内部机制 |
GroupedToolUseMessage(type: 'grouped_tool_use') |
UI 侧对同批工具调用的折叠展示 | 未展开,纯 UI 层 |
collapsed_read_search |
UI 侧对连续读/搜索操作的折叠展示 | 未展开,纯 UI 层 |
TombstoneMessage |
标记已作废的消息(如 fallback 时丢弃的孤儿 assistant 消息),提示 UI/transcript 移除 | 3.4 Fallback |
ToolUseSummaryMessage |
工具调用批次完成后异步生成的摘要,下一轮迭代开头消费 | 3.1 迭代初始化(pendingToolUseSummary) |
RequestStartEvent |
每轮迭代开始时 yield 的标记事件({ type: 'stream_request_start' }),非最终消息 |
未展开 |
StreamEvent |
模型流式响应过程中的中间事件(非完整消息),非最终落地历史 | 3.4 模型调用 |