【deepseek-harness】DeepSeek Harness Agent Loop 模块深度架构分析之二

5. 工具调用调度(tool-calls.ts)

5.1 调度模型:独占屏障 + 并行池

#mermaid-svg-nf1kXJCN2Iz4NfJC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nf1kXJCN2Iz4NfJC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nf1kXJCN2Iz4NfJC .error-icon{fill:#552222;}#mermaid-svg-nf1kXJCN2Iz4NfJC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nf1kXJCN2Iz4NfJC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nf1kXJCN2Iz4NfJC .marker.cross{stroke:#333333;}#mermaid-svg-nf1kXJCN2Iz4NfJC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nf1kXJCN2Iz4NfJC p{margin:0;}#mermaid-svg-nf1kXJCN2Iz4NfJC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nf1kXJCN2Iz4NfJC .cluster-label text{fill:#333;}#mermaid-svg-nf1kXJCN2Iz4NfJC .cluster-label span{color:#333;}#mermaid-svg-nf1kXJCN2Iz4NfJC .cluster-label span p{background-color:transparent;}#mermaid-svg-nf1kXJCN2Iz4NfJC .label text,#mermaid-svg-nf1kXJCN2Iz4NfJC span{fill:#333;color:#333;}#mermaid-svg-nf1kXJCN2Iz4NfJC .node rect,#mermaid-svg-nf1kXJCN2Iz4NfJC .node circle,#mermaid-svg-nf1kXJCN2Iz4NfJC .node ellipse,#mermaid-svg-nf1kXJCN2Iz4NfJC .node polygon,#mermaid-svg-nf1kXJCN2Iz4NfJC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nf1kXJCN2Iz4NfJC .rough-node .label text,#mermaid-svg-nf1kXJCN2Iz4NfJC .node .label text,#mermaid-svg-nf1kXJCN2Iz4NfJC .image-shape .label,#mermaid-svg-nf1kXJCN2Iz4NfJC .icon-shape .label{text-anchor:middle;}#mermaid-svg-nf1kXJCN2Iz4NfJC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nf1kXJCN2Iz4NfJC .rough-node .label,#mermaid-svg-nf1kXJCN2Iz4NfJC .node .label,#mermaid-svg-nf1kXJCN2Iz4NfJC .image-shape .label,#mermaid-svg-nf1kXJCN2Iz4NfJC .icon-shape .label{text-align:center;}#mermaid-svg-nf1kXJCN2Iz4NfJC .node.clickable{cursor:pointer;}#mermaid-svg-nf1kXJCN2Iz4NfJC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nf1kXJCN2Iz4NfJC .arrowheadPath{fill:#333333;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nf1kXJCN2Iz4NfJC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nf1kXJCN2Iz4NfJC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nf1kXJCN2Iz4NfJC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nf1kXJCN2Iz4NfJC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nf1kXJCN2Iz4NfJC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nf1kXJCN2Iz4NfJC .cluster text{fill:#333;}#mermaid-svg-nf1kXJCN2Iz4NfJC .cluster span{color:#333;}#mermaid-svg-nf1kXJCN2Iz4NfJC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nf1kXJCN2Iz4NfJC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nf1kXJCN2Iz4NfJC rect.text{fill:none;stroke-width:0;}#mermaid-svg-nf1kXJCN2Iz4NfJC .icon-shape,#mermaid-svg-nf1kXJCN2Iz4NfJC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nf1kXJCN2Iz4NfJC .icon-shape p,#mermaid-svg-nf1kXJCN2Iz4NfJC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nf1kXJCN2Iz4NfJC .icon-shape .label rect,#mermaid-svg-nf1kXJCN2Iz4NfJC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nf1kXJCN2Iz4NfJC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nf1kXJCN2Iz4NfJC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nf1kXJCN2Iz4NfJC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 工具调用调度模型
模型顺序保证
结果和上下文按模型顺序提交

committed 只跨连续的 model-order slot 推进

dispatch 可重叠

policy 和 results 保持有序
并行调用 (Parallel)
有界滚动池

最多 maxParallelToolCalls 个并发

默认 10
启动前重新分类

如果后续调用变为 exclusive

→ 排空当前池后形成新屏障
独占调用 (Exclusive)
形成屏障

必须单独执行

后续调用等待
示例:

· 需要排他资源的工具

· 有副作用的工具

5.2 executeToolCalls 主入口

#mermaid-svg-FrmpITsy9MFMjULQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FrmpITsy9MFMjULQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FrmpITsy9MFMjULQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FrmpITsy9MFMjULQ .error-icon{fill:#552222;}#mermaid-svg-FrmpITsy9MFMjULQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FrmpITsy9MFMjULQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FrmpITsy9MFMjULQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FrmpITsy9MFMjULQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FrmpITsy9MFMjULQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FrmpITsy9MFMjULQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FrmpITsy9MFMjULQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FrmpITsy9MFMjULQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FrmpITsy9MFMjULQ .marker.cross{stroke:#333333;}#mermaid-svg-FrmpITsy9MFMjULQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FrmpITsy9MFMjULQ p{margin:0;}#mermaid-svg-FrmpITsy9MFMjULQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FrmpITsy9MFMjULQ .cluster-label text{fill:#333;}#mermaid-svg-FrmpITsy9MFMjULQ .cluster-label span{color:#333;}#mermaid-svg-FrmpITsy9MFMjULQ .cluster-label span p{background-color:transparent;}#mermaid-svg-FrmpITsy9MFMjULQ .label text,#mermaid-svg-FrmpITsy9MFMjULQ span{fill:#333;color:#333;}#mermaid-svg-FrmpITsy9MFMjULQ .node rect,#mermaid-svg-FrmpITsy9MFMjULQ .node circle,#mermaid-svg-FrmpITsy9MFMjULQ .node ellipse,#mermaid-svg-FrmpITsy9MFMjULQ .node polygon,#mermaid-svg-FrmpITsy9MFMjULQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FrmpITsy9MFMjULQ .rough-node .label text,#mermaid-svg-FrmpITsy9MFMjULQ .node .label text,#mermaid-svg-FrmpITsy9MFMjULQ .image-shape .label,#mermaid-svg-FrmpITsy9MFMjULQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-FrmpITsy9MFMjULQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FrmpITsy9MFMjULQ .rough-node .label,#mermaid-svg-FrmpITsy9MFMjULQ .node .label,#mermaid-svg-FrmpITsy9MFMjULQ .image-shape .label,#mermaid-svg-FrmpITsy9MFMjULQ .icon-shape .label{text-align:center;}#mermaid-svg-FrmpITsy9MFMjULQ .node.clickable{cursor:pointer;}#mermaid-svg-FrmpITsy9MFMjULQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FrmpITsy9MFMjULQ .arrowheadPath{fill:#333333;}#mermaid-svg-FrmpITsy9MFMjULQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FrmpITsy9MFMjULQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FrmpITsy9MFMjULQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FrmpITsy9MFMjULQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FrmpITsy9MFMjULQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FrmpITsy9MFMjULQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FrmpITsy9MFMjULQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FrmpITsy9MFMjULQ .cluster text{fill:#333;}#mermaid-svg-FrmpITsy9MFMjULQ .cluster span{color:#333;}#mermaid-svg-FrmpITsy9MFMjULQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FrmpITsy9MFMjULQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FrmpITsy9MFMjULQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-FrmpITsy9MFMjULQ .icon-shape,#mermaid-svg-FrmpITsy9MFMjULQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FrmpITsy9MFMjULQ .icon-shape p,#mermaid-svg-FrmpITsy9MFMjULQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FrmpITsy9MFMjULQ .icon-shape .label rect,#mermaid-svg-FrmpITsy9MFMjULQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FrmpITsy9MFMjULQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FrmpITsy9MFMjULQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FrmpITsy9MFMjULQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 组循环 (while next < planned.length)




executeToolCalls(ctx, turn, step, toolCalls, signal, acceptContext)
agent = ctx.agents.requireInitiator()
session = agent.session
planned = toolCalls.map(block => ({

block,

exec: {callId, name, arguments, agent, signal}

}))
parseArguments(block.arguments)

· 空 → {}

· 有效 JSON → parse

· 无效 JSON → 原始文本
next = 0, concluded = false
first = plannednext
mode = ctx.tools.executionMode(first.exec).kind
mode === 'parallel'?
group = planned.slice(next)

(剩余全部)
group = first

(仅一个)
runGroup(ctx, turn, step, group, mode,

signal, acceptContext)
next += outcome.consumed
concluded ||= outcome.concluded
outcome.aborted?
for call of planned.slice(next):

appendSkippedToolCall(session, turn, step, block)
return {concluded}
return {concluded}

关键设计

  • executionMode 在每次组开始时重新读取,使注册表变更影响未开始的调用
  • 独占调用只取一个,形成屏障;并行调用取剩余全部
  • abort 后剩余调用获得合成的跳过结果(保持重放有效性)

5.3 runGroup 组调度核心

#mermaid-svg-Atx3AHyPTKgjCVvR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Atx3AHyPTKgjCVvR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Atx3AHyPTKgjCVvR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Atx3AHyPTKgjCVvR .error-icon{fill:#552222;}#mermaid-svg-Atx3AHyPTKgjCVvR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Atx3AHyPTKgjCVvR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Atx3AHyPTKgjCVvR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Atx3AHyPTKgjCVvR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Atx3AHyPTKgjCVvR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Atx3AHyPTKgjCVvR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Atx3AHyPTKgjCVvR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Atx3AHyPTKgjCVvR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Atx3AHyPTKgjCVvR .marker.cross{stroke:#333333;}#mermaid-svg-Atx3AHyPTKgjCVvR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Atx3AHyPTKgjCVvR p{margin:0;}#mermaid-svg-Atx3AHyPTKgjCVvR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Atx3AHyPTKgjCVvR .cluster-label text{fill:#333;}#mermaid-svg-Atx3AHyPTKgjCVvR .cluster-label span{color:#333;}#mermaid-svg-Atx3AHyPTKgjCVvR .cluster-label span p{background-color:transparent;}#mermaid-svg-Atx3AHyPTKgjCVvR .label text,#mermaid-svg-Atx3AHyPTKgjCVvR span{fill:#333;color:#333;}#mermaid-svg-Atx3AHyPTKgjCVvR .node rect,#mermaid-svg-Atx3AHyPTKgjCVvR .node circle,#mermaid-svg-Atx3AHyPTKgjCVvR .node ellipse,#mermaid-svg-Atx3AHyPTKgjCVvR .node polygon,#mermaid-svg-Atx3AHyPTKgjCVvR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Atx3AHyPTKgjCVvR .rough-node .label text,#mermaid-svg-Atx3AHyPTKgjCVvR .node .label text,#mermaid-svg-Atx3AHyPTKgjCVvR .image-shape .label,#mermaid-svg-Atx3AHyPTKgjCVvR .icon-shape .label{text-anchor:middle;}#mermaid-svg-Atx3AHyPTKgjCVvR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Atx3AHyPTKgjCVvR .rough-node .label,#mermaid-svg-Atx3AHyPTKgjCVvR .node .label,#mermaid-svg-Atx3AHyPTKgjCVvR .image-shape .label,#mermaid-svg-Atx3AHyPTKgjCVvR .icon-shape .label{text-align:center;}#mermaid-svg-Atx3AHyPTKgjCVvR .node.clickable{cursor:pointer;}#mermaid-svg-Atx3AHyPTKgjCVvR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Atx3AHyPTKgjCVvR .arrowheadPath{fill:#333333;}#mermaid-svg-Atx3AHyPTKgjCVvR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Atx3AHyPTKgjCVvR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Atx3AHyPTKgjCVvR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Atx3AHyPTKgjCVvR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Atx3AHyPTKgjCVvR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Atx3AHyPTKgjCVvR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Atx3AHyPTKgjCVvR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Atx3AHyPTKgjCVvR .cluster text{fill:#333;}#mermaid-svg-Atx3AHyPTKgjCVvR .cluster span{color:#333;}#mermaid-svg-Atx3AHyPTKgjCVvR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Atx3AHyPTKgjCVvR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Atx3AHyPTKgjCVvR rect.text{fill:none;stroke-width:0;}#mermaid-svg-Atx3AHyPTKgjCVvR .icon-shape,#mermaid-svg-Atx3AHyPTKgjCVvR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Atx3AHyPTKgjCVvR .icon-shape p,#mermaid-svg-Atx3AHyPTKgjCVvR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Atx3AHyPTKgjCVvR .icon-shape .label rect,#mermaid-svg-Atx3AHyPTKgjCVvR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Atx3AHyPTKgjCVvR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Atx3AHyPTKgjCVvR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Atx3AHyPTKgjCVvR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 正常返回
committed !== started?

→ throw (不可达)
return {consumed: started,

aborted: false, concluded}
中断处理
aborted?
for call of group.slice(started):

appendSkippedToolCall(session, ...)
return {consumed: group.length,

aborted: true, concluded}
主调度循环
fillPool()
inFlight.size > 0?
settledIndex = await Promise.race(inFlight.values())
inFlight.delete(settledIndex)
commitReady()
signal.aborted? → aborted = true
fillPool()
fillPool()
while !aborted && nextToStart < group.length

&& inFlight.size < maxParallelToolCalls
nextToStart > 0 && mode === 'parallel'

&& executionMode(nextCall) !== 'parallel'?

→ break (形成新屏障)
startCall(nextToStart)
nextToStart++
commitReady()
signal.aborted? → aborted = true
startCall(index)
callSeqsindex =

appendToolCall(session, turn, step, block)
started++
tools.prepare(exec)
prepared.kind
dispatch → tools.dispatch(exec)

→ slotsindex = {exec, result, needsPost}

→ inFlight.set(index, promise)
post-result → slotsindex = {exec, result, needsPost: true}
final-result → slotsindex = {exec, result, needsPost: false}
error → schedulerFailure = {error}
commitReady()
while committed < group.length
slot = slotscommitted

undefined → break
needsPost?

→ tools.finalize(exec, result)

: tools.finish(exec, result)
appendToolResult(session, turn, step,

block, result, callSeqscommitted)
for context of result.additionalContexts:

acceptContext(context)
concluded ||= result.concludesTurn
committed++
runGroup(ctx, turn, step, group, mode, signal, acceptContext)
agent = ctx.agents.requireInitiator()
maxParallelToolCalls = ctx.agentLoop.config.maxParallelToolCalls
slots = group.map(() => undefined)

callSeqs = group.map(() => -1)
nextToStart = 0, committed = 0, started = 0

aborted = signal.aborted, concluded = false

schedulerFailure = undefined
inFlight = new Map()

5.4 有序提交与模型顺序保证

commitReady() 是有序提交的核心------committed 只在连续的 model-order slot 都就绪时推进:
#mermaid-svg-5Rf7OsWDly3jYv4u{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5Rf7OsWDly3jYv4u .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5Rf7OsWDly3jYv4u .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5Rf7OsWDly3jYv4u .error-icon{fill:#552222;}#mermaid-svg-5Rf7OsWDly3jYv4u .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5Rf7OsWDly3jYv4u .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5Rf7OsWDly3jYv4u .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5Rf7OsWDly3jYv4u .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5Rf7OsWDly3jYv4u .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5Rf7OsWDly3jYv4u .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5Rf7OsWDly3jYv4u .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5Rf7OsWDly3jYv4u .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5Rf7OsWDly3jYv4u .marker.cross{stroke:#333333;}#mermaid-svg-5Rf7OsWDly3jYv4u svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5Rf7OsWDly3jYv4u p{margin:0;}#mermaid-svg-5Rf7OsWDly3jYv4u .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5Rf7OsWDly3jYv4u .cluster-label text{fill:#333;}#mermaid-svg-5Rf7OsWDly3jYv4u .cluster-label span{color:#333;}#mermaid-svg-5Rf7OsWDly3jYv4u .cluster-label span p{background-color:transparent;}#mermaid-svg-5Rf7OsWDly3jYv4u .label text,#mermaid-svg-5Rf7OsWDly3jYv4u span{fill:#333;color:#333;}#mermaid-svg-5Rf7OsWDly3jYv4u .node rect,#mermaid-svg-5Rf7OsWDly3jYv4u .node circle,#mermaid-svg-5Rf7OsWDly3jYv4u .node ellipse,#mermaid-svg-5Rf7OsWDly3jYv4u .node polygon,#mermaid-svg-5Rf7OsWDly3jYv4u .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5Rf7OsWDly3jYv4u .rough-node .label text,#mermaid-svg-5Rf7OsWDly3jYv4u .node .label text,#mermaid-svg-5Rf7OsWDly3jYv4u .image-shape .label,#mermaid-svg-5Rf7OsWDly3jYv4u .icon-shape .label{text-anchor:middle;}#mermaid-svg-5Rf7OsWDly3jYv4u .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5Rf7OsWDly3jYv4u .rough-node .label,#mermaid-svg-5Rf7OsWDly3jYv4u .node .label,#mermaid-svg-5Rf7OsWDly3jYv4u .image-shape .label,#mermaid-svg-5Rf7OsWDly3jYv4u .icon-shape .label{text-align:center;}#mermaid-svg-5Rf7OsWDly3jYv4u .node.clickable{cursor:pointer;}#mermaid-svg-5Rf7OsWDly3jYv4u .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5Rf7OsWDly3jYv4u .arrowheadPath{fill:#333333;}#mermaid-svg-5Rf7OsWDly3jYv4u .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5Rf7OsWDly3jYv4u .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5Rf7OsWDly3jYv4u .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Rf7OsWDly3jYv4u .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5Rf7OsWDly3jYv4u .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Rf7OsWDly3jYv4u .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5Rf7OsWDly3jYv4u .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5Rf7OsWDly3jYv4u .cluster text{fill:#333;}#mermaid-svg-5Rf7OsWDly3jYv4u .cluster span{color:#333;}#mermaid-svg-5Rf7OsWDly3jYv4u div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5Rf7OsWDly3jYv4u .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5Rf7OsWDly3jYv4u rect.text{fill:none;stroke-width:0;}#mermaid-svg-5Rf7OsWDly3jYv4u .icon-shape,#mermaid-svg-5Rf7OsWDly3jYv4u .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Rf7OsWDly3jYv4u .icon-shape p,#mermaid-svg-5Rf7OsWDly3jYv4u .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5Rf7OsWDly3jYv4u .icon-shape .label rect,#mermaid-svg-5Rf7OsWDly3jYv4u .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Rf7OsWDly3jYv4u .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5Rf7OsWDly3jYv4u .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5Rf7OsWDly3jYv4u :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 当 slot0 就绪后
committed=0: 提交 slot0
committed=1: 提交 slot1
committed=2: 提交 slot2
committed=3: slots3 === undefined?

→ break
commitReady 逻辑
committed=0: slots0 === undefined?

→ break (不提交)
即使 slots1 和 slots2 就绪

也不能提交

必须按模型顺序
示例: 4 个并行调用
slot0: 未就绪
slot1: 已就绪
slot2: 已就绪
slot3: 未就绪

additionalContexts FIFO 注入 :每个工具结果的 additionalContexts 通过 acceptContext 回调注入到 inbox 的 next-step 队列。这些上下文在下一个 Step 边界被 claim。

5.5 中断与失败处理

#mermaid-svg-X2QQRRo9GKlg4pxg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-X2QQRRo9GKlg4pxg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-X2QQRRo9GKlg4pxg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-X2QQRRo9GKlg4pxg .error-icon{fill:#552222;}#mermaid-svg-X2QQRRo9GKlg4pxg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-X2QQRRo9GKlg4pxg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-X2QQRRo9GKlg4pxg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-X2QQRRo9GKlg4pxg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-X2QQRRo9GKlg4pxg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-X2QQRRo9GKlg4pxg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-X2QQRRo9GKlg4pxg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-X2QQRRo9GKlg4pxg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-X2QQRRo9GKlg4pxg .marker.cross{stroke:#333333;}#mermaid-svg-X2QQRRo9GKlg4pxg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-X2QQRRo9GKlg4pxg p{margin:0;}#mermaid-svg-X2QQRRo9GKlg4pxg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-X2QQRRo9GKlg4pxg .cluster-label text{fill:#333;}#mermaid-svg-X2QQRRo9GKlg4pxg .cluster-label span{color:#333;}#mermaid-svg-X2QQRRo9GKlg4pxg .cluster-label span p{background-color:transparent;}#mermaid-svg-X2QQRRo9GKlg4pxg .label text,#mermaid-svg-X2QQRRo9GKlg4pxg span{fill:#333;color:#333;}#mermaid-svg-X2QQRRo9GKlg4pxg .node rect,#mermaid-svg-X2QQRRo9GKlg4pxg .node circle,#mermaid-svg-X2QQRRo9GKlg4pxg .node ellipse,#mermaid-svg-X2QQRRo9GKlg4pxg .node polygon,#mermaid-svg-X2QQRRo9GKlg4pxg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-X2QQRRo9GKlg4pxg .rough-node .label text,#mermaid-svg-X2QQRRo9GKlg4pxg .node .label text,#mermaid-svg-X2QQRRo9GKlg4pxg .image-shape .label,#mermaid-svg-X2QQRRo9GKlg4pxg .icon-shape .label{text-anchor:middle;}#mermaid-svg-X2QQRRo9GKlg4pxg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-X2QQRRo9GKlg4pxg .rough-node .label,#mermaid-svg-X2QQRRo9GKlg4pxg .node .label,#mermaid-svg-X2QQRRo9GKlg4pxg .image-shape .label,#mermaid-svg-X2QQRRo9GKlg4pxg .icon-shape .label{text-align:center;}#mermaid-svg-X2QQRRo9GKlg4pxg .node.clickable{cursor:pointer;}#mermaid-svg-X2QQRRo9GKlg4pxg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-X2QQRRo9GKlg4pxg .arrowheadPath{fill:#333333;}#mermaid-svg-X2QQRRo9GKlg4pxg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-X2QQRRo9GKlg4pxg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-X2QQRRo9GKlg4pxg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-X2QQRRo9GKlg4pxg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-X2QQRRo9GKlg4pxg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-X2QQRRo9GKlg4pxg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-X2QQRRo9GKlg4pxg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-X2QQRRo9GKlg4pxg .cluster text{fill:#333;}#mermaid-svg-X2QQRRo9GKlg4pxg .cluster span{color:#333;}#mermaid-svg-X2QQRRo9GKlg4pxg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-X2QQRRo9GKlg4pxg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-X2QQRRo9GKlg4pxg rect.text{fill:none;stroke-width:0;}#mermaid-svg-X2QQRRo9GKlg4pxg .icon-shape,#mermaid-svg-X2QQRRo9GKlg4pxg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-X2QQRRo9GKlg4pxg .icon-shape p,#mermaid-svg-X2QQRRo9GKlg4pxg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-X2QQRRo9GKlg4pxg .icon-shape .label rect,#mermaid-svg-X2QQRRo9GKlg4pxg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-X2QQRRo9GKlg4pxg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-X2QQRRo9GKlg4pxg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-X2QQRRo9GKlg4pxg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 持久化调用/结果对
tool/call: {turn, step, callId, name, arguments}
tool/result: {turn, step, message,

error.info?, meta?}
surfaceOp: 'append'

sourceEventSeqs: callSeq
appendSkippedToolCall
appendToolCall → tool/call 事件
appendToolResult → tool/result 事件

content: 'Error: tool call aborted before dispatch'

isError: true

error.info: {name:'AbortError',

code: TOOL_ABORTED_BEFORE_DISPATCH}
调度器失败处理
startCall 或 commit 抛错 → schedulerFailure
停止新 dispatch
排空已开始 dispatch (allSettled)
不伪造合成结果
throw schedulerFailure.error
中断处理
signal.aborted → aborted = true
排空 inFlight (allSettled)
已开始的调用: 上下文已 acceptContext
未开始的调用: appendSkippedToolCall

合成 'aborted before dispatch' 结果
return {consumed: group.length,

aborted: true, concluded}

关键设计

  • 中断时,已开始的调用的结果和上下文仍然被提交(有序),未开始的调用获得合成错误结果
  • 调度器失败不伪造合成结果,保留已记录的 tool/call 事件
  • 每个 tool/result 通过 sourceEventSeqs: [callSeq] 关联到对应的 tool/call

6. AgentLoop 工厂服务(index.ts)

6.1 AgentLoop 服务类

AgentLoop 是注册为 ctx.agentLoop 的工厂服务,实现 AgentFactory 接口:
#mermaid-svg-nhfgaxeJAqce7xvK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nhfgaxeJAqce7xvK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nhfgaxeJAqce7xvK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nhfgaxeJAqce7xvK .error-icon{fill:#552222;}#mermaid-svg-nhfgaxeJAqce7xvK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nhfgaxeJAqce7xvK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nhfgaxeJAqce7xvK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nhfgaxeJAqce7xvK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nhfgaxeJAqce7xvK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nhfgaxeJAqce7xvK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nhfgaxeJAqce7xvK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nhfgaxeJAqce7xvK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nhfgaxeJAqce7xvK .marker.cross{stroke:#333333;}#mermaid-svg-nhfgaxeJAqce7xvK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nhfgaxeJAqce7xvK p{margin:0;}#mermaid-svg-nhfgaxeJAqce7xvK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nhfgaxeJAqce7xvK .cluster-label text{fill:#333;}#mermaid-svg-nhfgaxeJAqce7xvK .cluster-label span{color:#333;}#mermaid-svg-nhfgaxeJAqce7xvK .cluster-label span p{background-color:transparent;}#mermaid-svg-nhfgaxeJAqce7xvK .label text,#mermaid-svg-nhfgaxeJAqce7xvK span{fill:#333;color:#333;}#mermaid-svg-nhfgaxeJAqce7xvK .node rect,#mermaid-svg-nhfgaxeJAqce7xvK .node circle,#mermaid-svg-nhfgaxeJAqce7xvK .node ellipse,#mermaid-svg-nhfgaxeJAqce7xvK .node polygon,#mermaid-svg-nhfgaxeJAqce7xvK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nhfgaxeJAqce7xvK .rough-node .label text,#mermaid-svg-nhfgaxeJAqce7xvK .node .label text,#mermaid-svg-nhfgaxeJAqce7xvK .image-shape .label,#mermaid-svg-nhfgaxeJAqce7xvK .icon-shape .label{text-anchor:middle;}#mermaid-svg-nhfgaxeJAqce7xvK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nhfgaxeJAqce7xvK .rough-node .label,#mermaid-svg-nhfgaxeJAqce7xvK .node .label,#mermaid-svg-nhfgaxeJAqce7xvK .image-shape .label,#mermaid-svg-nhfgaxeJAqce7xvK .icon-shape .label{text-align:center;}#mermaid-svg-nhfgaxeJAqce7xvK .node.clickable{cursor:pointer;}#mermaid-svg-nhfgaxeJAqce7xvK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nhfgaxeJAqce7xvK .arrowheadPath{fill:#333333;}#mermaid-svg-nhfgaxeJAqce7xvK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nhfgaxeJAqce7xvK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nhfgaxeJAqce7xvK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nhfgaxeJAqce7xvK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nhfgaxeJAqce7xvK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nhfgaxeJAqce7xvK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nhfgaxeJAqce7xvK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nhfgaxeJAqce7xvK .cluster text{fill:#333;}#mermaid-svg-nhfgaxeJAqce7xvK .cluster span{color:#333;}#mermaid-svg-nhfgaxeJAqce7xvK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nhfgaxeJAqce7xvK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nhfgaxeJAqce7xvK rect.text{fill:none;stroke-width:0;}#mermaid-svg-nhfgaxeJAqce7xvK .icon-shape,#mermaid-svg-nhfgaxeJAqce7xvK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nhfgaxeJAqce7xvK .icon-shape p,#mermaid-svg-nhfgaxeJAqce7xvK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nhfgaxeJAqce7xvK .icon-shape .label rect,#mermaid-svg-nhfgaxeJAqce7xvK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nhfgaxeJAqce7xvK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nhfgaxeJAqce7xvK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nhfgaxeJAqce7xvK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} FactoryOwnership
track(dispose)

跟踪活跃 Agent teardown
trackStartup(job)

跟踪配置启动任务
isActive()

检查是否仍可接受新 Agent
dispose()

abort 所有活跃 Agent
AgentLoop 服务类
Prompt 变量注册
variable('provider',

context => context.agent?.options.provider)
variable('model',

context => context.agent?.options.model)
variable('cwd',

context => context.agent?.session.header.cwd)
工厂方法
create(id, options, meta)

创建新 Agent + Session
createAgent(ownerCtx, options)

创建带 setup 的 Agent
resume(ownerCtx, options)

从持久化恢复 Agent
设置系统
AGENT_LOOP_SETTINGS_NAMESPACE

'agent-loop'
maxParallelToolCalls

运行时可调
getter 读取最新值

每个调度组开始时读取
配置
maxParallelToolCalls: number

默认 10
agents: 配置数组

声明式 Agent 定义
依赖注入
static inject =

'agents', 'sessions', 'llm', 'tools', 'systemPrompt'

6.2 FactoryOwnership 工厂级生命周期管理

#mermaid-svg-opscdoweGMnM0Pby{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-opscdoweGMnM0Pby .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-opscdoweGMnM0Pby .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-opscdoweGMnM0Pby .error-icon{fill:#552222;}#mermaid-svg-opscdoweGMnM0Pby .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-opscdoweGMnM0Pby .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-opscdoweGMnM0Pby .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-opscdoweGMnM0Pby .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-opscdoweGMnM0Pby .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-opscdoweGMnM0Pby .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-opscdoweGMnM0Pby .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-opscdoweGMnM0Pby .marker{fill:#333333;stroke:#333333;}#mermaid-svg-opscdoweGMnM0Pby .marker.cross{stroke:#333333;}#mermaid-svg-opscdoweGMnM0Pby svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-opscdoweGMnM0Pby p{margin:0;}#mermaid-svg-opscdoweGMnM0Pby .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-opscdoweGMnM0Pby .cluster-label text{fill:#333;}#mermaid-svg-opscdoweGMnM0Pby .cluster-label span{color:#333;}#mermaid-svg-opscdoweGMnM0Pby .cluster-label span p{background-color:transparent;}#mermaid-svg-opscdoweGMnM0Pby .label text,#mermaid-svg-opscdoweGMnM0Pby span{fill:#333;color:#333;}#mermaid-svg-opscdoweGMnM0Pby .node rect,#mermaid-svg-opscdoweGMnM0Pby .node circle,#mermaid-svg-opscdoweGMnM0Pby .node ellipse,#mermaid-svg-opscdoweGMnM0Pby .node polygon,#mermaid-svg-opscdoweGMnM0Pby .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-opscdoweGMnM0Pby .rough-node .label text,#mermaid-svg-opscdoweGMnM0Pby .node .label text,#mermaid-svg-opscdoweGMnM0Pby .image-shape .label,#mermaid-svg-opscdoweGMnM0Pby .icon-shape .label{text-anchor:middle;}#mermaid-svg-opscdoweGMnM0Pby .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-opscdoweGMnM0Pby .rough-node .label,#mermaid-svg-opscdoweGMnM0Pby .node .label,#mermaid-svg-opscdoweGMnM0Pby .image-shape .label,#mermaid-svg-opscdoweGMnM0Pby .icon-shape .label{text-align:center;}#mermaid-svg-opscdoweGMnM0Pby .node.clickable{cursor:pointer;}#mermaid-svg-opscdoweGMnM0Pby .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-opscdoweGMnM0Pby .arrowheadPath{fill:#333333;}#mermaid-svg-opscdoweGMnM0Pby .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-opscdoweGMnM0Pby .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-opscdoweGMnM0Pby .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-opscdoweGMnM0Pby .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-opscdoweGMnM0Pby .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-opscdoweGMnM0Pby .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-opscdoweGMnM0Pby .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-opscdoweGMnM0Pby .cluster text{fill:#333;}#mermaid-svg-opscdoweGMnM0Pby .cluster span{color:#333;}#mermaid-svg-opscdoweGMnM0Pby div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-opscdoweGMnM0Pby .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-opscdoweGMnM0Pby rect.text{fill:none;stroke-width:0;}#mermaid-svg-opscdoweGMnM0Pby .icon-shape,#mermaid-svg-opscdoweGMnM0Pby .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-opscdoweGMnM0Pby .icon-shape p,#mermaid-svg-opscdoweGMnM0Pby .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-opscdoweGMnM0Pby .icon-shape .label rect,#mermaid-svg-opscdoweGMnM0Pby .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-opscdoweGMnM0Pby .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-opscdoweGMnM0Pby .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-opscdoweGMnM0Pby :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} FactoryOwnership
方法
INACTIVE_STATES
FiberState.UNLOADING
FiberState.DISPOSED
FiberState.FAILED
accepting: boolean = true

是否接受新 Agent
teardown: AbortController

工厂销毁信号
inactive: Promise.withResolvers

工厂不活跃信号
liveAgents: Set<dispose>

活跃 Agent 的 teardown 函数
startupTasks: Set<Promise>

配置启动任务
get signal()

返回 teardown.signal
isActive()

accepting && !INACTIVE_STATES.has(fiber.state)
track(dispose)

加入 liveAgents

返回 untrack
trackStartup(job)

加入 startupTasks

完成后自动移除
trackWrapper(job)

包装公共 continuation
waitWhileActive(job)

Promise.race(job, inactive)
dispose()

  1. accepting = false

  2. abort('agent loop is not active')

  3. resolve inactive

  4. await all liveAgents + startupTasks

6.3 prepare 方法------Agent 准备

prepare() 是所有创建路径的共享准备阶段,构建驱动器、作用域和反向 teardown:
#mermaid-svg-9lZEkanZ9aXVOBiz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9lZEkanZ9aXVOBiz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9lZEkanZ9aXVOBiz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9lZEkanZ9aXVOBiz .error-icon{fill:#552222;}#mermaid-svg-9lZEkanZ9aXVOBiz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9lZEkanZ9aXVOBiz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9lZEkanZ9aXVOBiz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9lZEkanZ9aXVOBiz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9lZEkanZ9aXVOBiz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9lZEkanZ9aXVOBiz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9lZEkanZ9aXVOBiz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9lZEkanZ9aXVOBiz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9lZEkanZ9aXVOBiz .marker.cross{stroke:#333333;}#mermaid-svg-9lZEkanZ9aXVOBiz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9lZEkanZ9aXVOBiz p{margin:0;}#mermaid-svg-9lZEkanZ9aXVOBiz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9lZEkanZ9aXVOBiz .cluster-label text{fill:#333;}#mermaid-svg-9lZEkanZ9aXVOBiz .cluster-label span{color:#333;}#mermaid-svg-9lZEkanZ9aXVOBiz .cluster-label span p{background-color:transparent;}#mermaid-svg-9lZEkanZ9aXVOBiz .label text,#mermaid-svg-9lZEkanZ9aXVOBiz span{fill:#333;color:#333;}#mermaid-svg-9lZEkanZ9aXVOBiz .node rect,#mermaid-svg-9lZEkanZ9aXVOBiz .node circle,#mermaid-svg-9lZEkanZ9aXVOBiz .node ellipse,#mermaid-svg-9lZEkanZ9aXVOBiz .node polygon,#mermaid-svg-9lZEkanZ9aXVOBiz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9lZEkanZ9aXVOBiz .rough-node .label text,#mermaid-svg-9lZEkanZ9aXVOBiz .node .label text,#mermaid-svg-9lZEkanZ9aXVOBiz .image-shape .label,#mermaid-svg-9lZEkanZ9aXVOBiz .icon-shape .label{text-anchor:middle;}#mermaid-svg-9lZEkanZ9aXVOBiz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9lZEkanZ9aXVOBiz .rough-node .label,#mermaid-svg-9lZEkanZ9aXVOBiz .node .label,#mermaid-svg-9lZEkanZ9aXVOBiz .image-shape .label,#mermaid-svg-9lZEkanZ9aXVOBiz .icon-shape .label{text-align:center;}#mermaid-svg-9lZEkanZ9aXVOBiz .node.clickable{cursor:pointer;}#mermaid-svg-9lZEkanZ9aXVOBiz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9lZEkanZ9aXVOBiz .arrowheadPath{fill:#333333;}#mermaid-svg-9lZEkanZ9aXVOBiz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9lZEkanZ9aXVOBiz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9lZEkanZ9aXVOBiz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9lZEkanZ9aXVOBiz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9lZEkanZ9aXVOBiz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9lZEkanZ9aXVOBiz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9lZEkanZ9aXVOBiz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9lZEkanZ9aXVOBiz .cluster text{fill:#333;}#mermaid-svg-9lZEkanZ9aXVOBiz .cluster span{color:#333;}#mermaid-svg-9lZEkanZ9aXVOBiz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9lZEkanZ9aXVOBiz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9lZEkanZ9aXVOBiz rect.text{fill:none;stroke-width:0;}#mermaid-svg-9lZEkanZ9aXVOBiz .icon-shape,#mermaid-svg-9lZEkanZ9aXVOBiz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9lZEkanZ9aXVOBiz .icon-shape p,#mermaid-svg-9lZEkanZ9aXVOBiz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9lZEkanZ9aXVOBiz .icon-shape .label rect,#mermaid-svg-9lZEkanZ9aXVOBiz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9lZEkanZ9aXVOBiz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9lZEkanZ9aXVOBiz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9lZEkanZ9aXVOBiz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} publish(source)
assertLive()
detachSession = agent.ctx.sessions.enter(session)
detachAgent = loopCtx.agents.enter(agent, ownerCtx.agent)
agent.ctx.sessions.announce(session)
loopCtx.agents.announce(agent)
emitAgentEvent('agent/session-start', {source})
assertLive() (recheck)
return {agent, dispose}
构造驱动器
agent = machine = new ReactLoopAgent(loopCtx, id, options, session)
machineReady.resolve()
assertLive()
反向 Teardown (memoized)
machine, detachSession, detachAgent = undefined

disposing = undefined

machineReady = withResolvers
dispose(ownerTriggered = false):

  1. abort('lifecycle disposed')

  2. removeEventListener

  3. await machineReady (如果 machine 未建)

  4. machine.cancel({kind:'disposed'})

  5. await machine.whenIdle()

  6. machine.scope.dispose()

  7. detachAgent?.() + detachSession?.()

  8. untrack()

  9. !ownerTriggered → await unfollowOwner()
    untrack = ownership.track(dispose)
    unfollowOwner = ownerCtx.effect(...)

owner 销毁 → dispose(true)
Abort 融合 (三方)
abort = new AbortController()
callerSignal abort → abort(caller reason)
ownership.signal abort → abort(factory reason)
addEventListener (once: true)
prepare(ownerCtx, id, options, session, callerSignal?)
assertAgentOptions(options)
ownerCtx.fiber.assertActive()
!ownership.isActive()? → throw
callerSignal?.aborted?

→ throw caller reason
return {agent, signal, publish, dispose}

关键设计

  1. 三方 Abort 融合:callerSignal(调用者取消)、ownership.signal(工厂销毁)、lifecycle(owner fiber 卸载)三方 abort 原因融合到一个 AbortController。

  2. 反向 Teardown :在任何资源存在之前注册 teardown,使 setup 过程中的卸载找到可工作的 disposer 而非泄漏。teardown 是 memoized 的------所有 racing owner await 同一个 quiescence。

  3. publish 原子性enterannouncesession-start 每步后 assertLive(),同步 announce 监听器可能已启动 teardown。

  4. disposed-cause cancel :销毁是 {kind: 'disposed'} 原因的 cancel + quiescence。新工作在此时点后发送是发送者的 bug。

6.4 create / createAgent / resume 三种创建路径

#mermaid-svg-uda24LRQwpcYfoZ9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uda24LRQwpcYfoZ9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uda24LRQwpcYfoZ9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uda24LRQwpcYfoZ9 .error-icon{fill:#552222;}#mermaid-svg-uda24LRQwpcYfoZ9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uda24LRQwpcYfoZ9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uda24LRQwpcYfoZ9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uda24LRQwpcYfoZ9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uda24LRQwpcYfoZ9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uda24LRQwpcYfoZ9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uda24LRQwpcYfoZ9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uda24LRQwpcYfoZ9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uda24LRQwpcYfoZ9 .marker.cross{stroke:#333333;}#mermaid-svg-uda24LRQwpcYfoZ9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uda24LRQwpcYfoZ9 p{margin:0;}#mermaid-svg-uda24LRQwpcYfoZ9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uda24LRQwpcYfoZ9 .cluster-label text{fill:#333;}#mermaid-svg-uda24LRQwpcYfoZ9 .cluster-label span{color:#333;}#mermaid-svg-uda24LRQwpcYfoZ9 .cluster-label span p{background-color:transparent;}#mermaid-svg-uda24LRQwpcYfoZ9 .label text,#mermaid-svg-uda24LRQwpcYfoZ9 span{fill:#333;color:#333;}#mermaid-svg-uda24LRQwpcYfoZ9 .node rect,#mermaid-svg-uda24LRQwpcYfoZ9 .node circle,#mermaid-svg-uda24LRQwpcYfoZ9 .node ellipse,#mermaid-svg-uda24LRQwpcYfoZ9 .node polygon,#mermaid-svg-uda24LRQwpcYfoZ9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uda24LRQwpcYfoZ9 .rough-node .label text,#mermaid-svg-uda24LRQwpcYfoZ9 .node .label text,#mermaid-svg-uda24LRQwpcYfoZ9 .image-shape .label,#mermaid-svg-uda24LRQwpcYfoZ9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-uda24LRQwpcYfoZ9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uda24LRQwpcYfoZ9 .rough-node .label,#mermaid-svg-uda24LRQwpcYfoZ9 .node .label,#mermaid-svg-uda24LRQwpcYfoZ9 .image-shape .label,#mermaid-svg-uda24LRQwpcYfoZ9 .icon-shape .label{text-align:center;}#mermaid-svg-uda24LRQwpcYfoZ9 .node.clickable{cursor:pointer;}#mermaid-svg-uda24LRQwpcYfoZ9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uda24LRQwpcYfoZ9 .arrowheadPath{fill:#333333;}#mermaid-svg-uda24LRQwpcYfoZ9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uda24LRQwpcYfoZ9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uda24LRQwpcYfoZ9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uda24LRQwpcYfoZ9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uda24LRQwpcYfoZ9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uda24LRQwpcYfoZ9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uda24LRQwpcYfoZ9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uda24LRQwpcYfoZ9 .cluster text{fill:#333;}#mermaid-svg-uda24LRQwpcYfoZ9 .cluster span{color:#333;}#mermaid-svg-uda24LRQwpcYfoZ9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uda24LRQwpcYfoZ9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uda24LRQwpcYfoZ9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-uda24LRQwpcYfoZ9 .icon-shape,#mermaid-svg-uda24LRQwpcYfoZ9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uda24LRQwpcYfoZ9 .icon-shape p,#mermaid-svg-uda24LRQwpcYfoZ9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uda24LRQwpcYfoZ9 .icon-shape .label rect,#mermaid-svg-uda24LRQwpcYfoZ9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uda24LRQwpcYfoZ9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uda24LRQwpcYfoZ9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uda24LRQwpcYfoZ9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 配置驱动启动 (构造函数)
for each config.agents entry
resumeSessionId 存在?
inject sessionPersistence → resumeWith
sessionId 存在 + persistence 存在?
restoreOrCreateConfigured

先尝试 resume,失败且不存在则 create
create(configuredId, options, meta)
setupAndPublish (共享)
prepare(ownerCtx, id, agentOptions, session, signal)
setup?.(agent.ctx) → AgentSetupCommit

raceAbort 包装
setupCommit?.commit()
publish(source)
catch → prepared.dispose() + throw
resume(ownerCtx, options) --- 从持久化恢复
ctx.get('sessionPersistence')

未配置 → throw
resumeWith(ownerCtx, persistence, options)
persistence.prepare(id, fusedSignal)

加载持久化会话
setupAndPublish(ownerCtx, id, preparation,

agentOptions, setup, signal, 'resume')
ownership.trackWrapper(published)
return AgentHandle
createAgent(ownerCtx, options) --- 带 setup
sessions.prepare(options.sessionId, {seed?, meta?})
setupAndPublish(ownerCtx, id, preparation,

agentOptions, setup, signal, 'startup')
ownership.trackWrapper(published)
return AgentHandle
create(id, options, meta) --- 同步创建
sessions.prepare(id, {meta})
prepare(ctx, id, options, session)
publish('startup')
return agent

6.5 配置体系与声明式 Agent

#mermaid-svg-9jsb1sxHPW3HsacV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9jsb1sxHPW3HsacV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9jsb1sxHPW3HsacV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9jsb1sxHPW3HsacV .error-icon{fill:#552222;}#mermaid-svg-9jsb1sxHPW3HsacV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9jsb1sxHPW3HsacV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9jsb1sxHPW3HsacV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9jsb1sxHPW3HsacV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9jsb1sxHPW3HsacV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9jsb1sxHPW3HsacV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9jsb1sxHPW3HsacV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9jsb1sxHPW3HsacV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9jsb1sxHPW3HsacV .marker.cross{stroke:#333333;}#mermaid-svg-9jsb1sxHPW3HsacV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9jsb1sxHPW3HsacV p{margin:0;}#mermaid-svg-9jsb1sxHPW3HsacV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9jsb1sxHPW3HsacV .cluster-label text{fill:#333;}#mermaid-svg-9jsb1sxHPW3HsacV .cluster-label span{color:#333;}#mermaid-svg-9jsb1sxHPW3HsacV .cluster-label span p{background-color:transparent;}#mermaid-svg-9jsb1sxHPW3HsacV .label text,#mermaid-svg-9jsb1sxHPW3HsacV span{fill:#333;color:#333;}#mermaid-svg-9jsb1sxHPW3HsacV .node rect,#mermaid-svg-9jsb1sxHPW3HsacV .node circle,#mermaid-svg-9jsb1sxHPW3HsacV .node ellipse,#mermaid-svg-9jsb1sxHPW3HsacV .node polygon,#mermaid-svg-9jsb1sxHPW3HsacV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9jsb1sxHPW3HsacV .rough-node .label text,#mermaid-svg-9jsb1sxHPW3HsacV .node .label text,#mermaid-svg-9jsb1sxHPW3HsacV .image-shape .label,#mermaid-svg-9jsb1sxHPW3HsacV .icon-shape .label{text-anchor:middle;}#mermaid-svg-9jsb1sxHPW3HsacV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9jsb1sxHPW3HsacV .rough-node .label,#mermaid-svg-9jsb1sxHPW3HsacV .node .label,#mermaid-svg-9jsb1sxHPW3HsacV .image-shape .label,#mermaid-svg-9jsb1sxHPW3HsacV .icon-shape .label{text-align:center;}#mermaid-svg-9jsb1sxHPW3HsacV .node.clickable{cursor:pointer;}#mermaid-svg-9jsb1sxHPW3HsacV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9jsb1sxHPW3HsacV .arrowheadPath{fill:#333333;}#mermaid-svg-9jsb1sxHPW3HsacV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9jsb1sxHPW3HsacV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9jsb1sxHPW3HsacV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9jsb1sxHPW3HsacV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9jsb1sxHPW3HsacV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9jsb1sxHPW3HsacV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9jsb1sxHPW3HsacV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9jsb1sxHPW3HsacV .cluster text{fill:#333;}#mermaid-svg-9jsb1sxHPW3HsacV .cluster span{color:#333;}#mermaid-svg-9jsb1sxHPW3HsacV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9jsb1sxHPW3HsacV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9jsb1sxHPW3HsacV rect.text{fill:none;stroke-width:0;}#mermaid-svg-9jsb1sxHPW3HsacV .icon-shape,#mermaid-svg-9jsb1sxHPW3HsacV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9jsb1sxHPW3HsacV .icon-shape p,#mermaid-svg-9jsb1sxHPW3HsacV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9jsb1sxHPW3HsacV .icon-shape .label rect,#mermaid-svg-9jsb1sxHPW3HsacV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9jsb1sxHPW3HsacV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9jsb1sxHPW3HsacV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9jsb1sxHPW3HsacV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 配置体系
运行时设置
installSettingsSection

'agent-loop' namespace
maxParallelToolCalls 运时可调

getter 读取最新值

每个调度组开始时读取
validate: resolveMaxParallelToolCalls

拒绝时保持上一个有效值
Launcher 身份覆盖
CONFIGURED_AGENT_IDENTITIES_KEY

ctx.provide() 在 Loader 前
applyLauncherIdentities

launcher 身份替换配置身份
验证规则
sessionId 和 resumeSessionId 互斥
exactIdentity 不可重复
每个 agent 配置项
id: string

稳定配置标签
sessionId?: SessionId

可选稳定身份
resumeSessionId?: SessionId

持久化会话恢复
cwd?: string

工作区路径
provider?: string
model?: string
maxTokens?: number


7. Invariant 请求重建不变量

invariant.ts 实现了请求重建不变量 ,通过 llm/stream 事件拦截校验 Loop 构建的请求:
#mermaid-svg-dfHljnL0M01LgNYo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dfHljnL0M01LgNYo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dfHljnL0M01LgNYo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dfHljnL0M01LgNYo .error-icon{fill:#552222;}#mermaid-svg-dfHljnL0M01LgNYo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dfHljnL0M01LgNYo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dfHljnL0M01LgNYo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dfHljnL0M01LgNYo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dfHljnL0M01LgNYo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dfHljnL0M01LgNYo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dfHljnL0M01LgNYo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dfHljnL0M01LgNYo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dfHljnL0M01LgNYo .marker.cross{stroke:#333333;}#mermaid-svg-dfHljnL0M01LgNYo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dfHljnL0M01LgNYo p{margin:0;}#mermaid-svg-dfHljnL0M01LgNYo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dfHljnL0M01LgNYo .cluster-label text{fill:#333;}#mermaid-svg-dfHljnL0M01LgNYo .cluster-label span{color:#333;}#mermaid-svg-dfHljnL0M01LgNYo .cluster-label span p{background-color:transparent;}#mermaid-svg-dfHljnL0M01LgNYo .label text,#mermaid-svg-dfHljnL0M01LgNYo span{fill:#333;color:#333;}#mermaid-svg-dfHljnL0M01LgNYo .node rect,#mermaid-svg-dfHljnL0M01LgNYo .node circle,#mermaid-svg-dfHljnL0M01LgNYo .node ellipse,#mermaid-svg-dfHljnL0M01LgNYo .node polygon,#mermaid-svg-dfHljnL0M01LgNYo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dfHljnL0M01LgNYo .rough-node .label text,#mermaid-svg-dfHljnL0M01LgNYo .node .label text,#mermaid-svg-dfHljnL0M01LgNYo .image-shape .label,#mermaid-svg-dfHljnL0M01LgNYo .icon-shape .label{text-anchor:middle;}#mermaid-svg-dfHljnL0M01LgNYo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dfHljnL0M01LgNYo .rough-node .label,#mermaid-svg-dfHljnL0M01LgNYo .node .label,#mermaid-svg-dfHljnL0M01LgNYo .image-shape .label,#mermaid-svg-dfHljnL0M01LgNYo .icon-shape .label{text-align:center;}#mermaid-svg-dfHljnL0M01LgNYo .node.clickable{cursor:pointer;}#mermaid-svg-dfHljnL0M01LgNYo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dfHljnL0M01LgNYo .arrowheadPath{fill:#333333;}#mermaid-svg-dfHljnL0M01LgNYo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dfHljnL0M01LgNYo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dfHljnL0M01LgNYo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dfHljnL0M01LgNYo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dfHljnL0M01LgNYo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dfHljnL0M01LgNYo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dfHljnL0M01LgNYo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dfHljnL0M01LgNYo .cluster text{fill:#333;}#mermaid-svg-dfHljnL0M01LgNYo .cluster span{color:#333;}#mermaid-svg-dfHljnL0M01LgNYo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dfHljnL0M01LgNYo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dfHljnL0M01LgNYo rect.text{fill:none;stroke-width:0;}#mermaid-svg-dfHljnL0M01LgNYo .icon-shape,#mermaid-svg-dfHljnL0M01LgNYo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dfHljnL0M01LgNYo .icon-shape p,#mermaid-svg-dfHljnL0M01LgNYo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dfHljnL0M01LgNYo .icon-shape .label rect,#mermaid-svg-dfHljnL0M01LgNYo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dfHljnL0M01LgNYo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dfHljnL0M01LgNYo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dfHljnL0M01LgNYo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 校验规则
agent-loop invariant

Loop 请求校验
Object.isFrozen(options)?

否 → fail('must be frozen')
options.sessionId !== undefined?

否 → fail('must carry session id')
ctx.sessions.get(sessionId) 存在?

否 → fail('must carry live session id')
Object.isFrozen(options.messages)?

否 → fail('must carry frozen messages')
events.some(step/start)?

否 → fail('no step/start in session log')
foldRequestHeader(events) !== undefined?

否 → fail('no request/header in session log')
JSON.stringify(options.messages)

=== JSON.stringify(deriveMessages())?

否 → fail('log-reconstruction desync')
options.model/system/temperature/maxTokens/stop/tools

=== header?

否 → fail('diverges from folded header')
ctx.invariants.register(PACKAGE_NAME, install)
install(ctx, fail)
ctx.on('llm/stream', handler,

{global: true, prepend: true})
prepend: 防止短路 replay 监听器

静默检查
isAgentLoopRequest(options)?

否 → next() (非 Loop 请求不检查)
全部通过 → next()

核心不变量"Loop 构建的请求消息必须等于从日志派生的消息" ------JSON.stringify(options.messages) !== JSON.stringify(expected) 则 fail。这确保了请求内容是日志的纯函数(可重建性 Agent Note)。


8. 循环机制全景

8.1 三层嵌套循环模型

#mermaid-svg-tu5lW1Br0DJ3vbo5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .error-icon{fill:#552222;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .marker.cross{stroke:#333333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tu5lW1Br0DJ3vbo5 p{margin:0;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .cluster-label text{fill:#333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .cluster-label span{color:#333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .cluster-label span p{background-color:transparent;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .label text,#mermaid-svg-tu5lW1Br0DJ3vbo5 span{fill:#333;color:#333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .node rect,#mermaid-svg-tu5lW1Br0DJ3vbo5 .node circle,#mermaid-svg-tu5lW1Br0DJ3vbo5 .node ellipse,#mermaid-svg-tu5lW1Br0DJ3vbo5 .node polygon,#mermaid-svg-tu5lW1Br0DJ3vbo5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .rough-node .label text,#mermaid-svg-tu5lW1Br0DJ3vbo5 .node .label text,#mermaid-svg-tu5lW1Br0DJ3vbo5 .image-shape .label,#mermaid-svg-tu5lW1Br0DJ3vbo5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .rough-node .label,#mermaid-svg-tu5lW1Br0DJ3vbo5 .node .label,#mermaid-svg-tu5lW1Br0DJ3vbo5 .image-shape .label,#mermaid-svg-tu5lW1Br0DJ3vbo5 .icon-shape .label{text-align:center;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .node.clickable{cursor:pointer;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .arrowheadPath{fill:#333333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tu5lW1Br0DJ3vbo5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tu5lW1Br0DJ3vbo5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tu5lW1Br0DJ3vbo5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .cluster text{fill:#333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .cluster span{color:#333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tu5lW1Br0DJ3vbo5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .icon-shape,#mermaid-svg-tu5lW1Br0DJ3vbo5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .icon-shape p,#mermaid-svg-tu5lW1Br0DJ3vbo5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .icon-shape .label rect,#mermaid-svg-tu5lW1Br0DJ3vbo5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tu5lW1Br0DJ3vbo5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tu5lW1Br0DJ3vbo5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tu5lW1Br0DJ3vbo5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Turn 层 (输入排空)
Step 层 (模型请求 + 工具调用)
连续 Turn
inbox.hasPending?
abort = new AC

wakeRequested = false

step = 0
return true → kick() 继续
return false → kick() 停止
Turn 停止决策
turnEnds && nextStep.length === 0?
agent/turn-stopping (serial)
turnEnds && nextStep.length === 0?
break (停止 Turn)
target = 'next-step'

继续下一个 Step
请求内重试 (while true)
agent/request-error waterfall

返回 {kind:'retry'} → 重试

返回 undefined → 抛出 LlmError
Round 层 (外层策略迭代)
如 goal round

由 goal-round-driver 管理

非 Loop 内置

每个 round 物化为一个 Turn
从 claim 首个输入

到模型和工具停止
turn/start
turn/end
一次完整的 LLM 交互
step/start
user/message (claim 的输入)
buildRequest

构建冻结请求
llm/stream

流式模型响应
assistant/chunk*

逐块持久化
assistant/message

完整消息
tool/call* → tools/pre-execute

→ tools/execute → tools/post-execute

→ tool/result*
step/end

8.2 Turn 完整流程时序图

Dispatch Session executeToolCalls() ctx.llm buildRequest() step() preStep() turn() Driver (kick) Agent 用户/SDK Dispatch Session executeToolCalls() ctx.llm buildRequest() step() preStep() turn() Driver (kick) Agent 用户/SDK #mermaid-svg-0ijImxMKZ80GGxDE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0ijImxMKZ80GGxDE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0ijImxMKZ80GGxDE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0ijImxMKZ80GGxDE .error-icon{fill:#552222;}#mermaid-svg-0ijImxMKZ80GGxDE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0ijImxMKZ80GGxDE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0ijImxMKZ80GGxDE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0ijImxMKZ80GGxDE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0ijImxMKZ80GGxDE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0ijImxMKZ80GGxDE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0ijImxMKZ80GGxDE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0ijImxMKZ80GGxDE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0ijImxMKZ80GGxDE .marker.cross{stroke:#333333;}#mermaid-svg-0ijImxMKZ80GGxDE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0ijImxMKZ80GGxDE p{margin:0;}#mermaid-svg-0ijImxMKZ80GGxDE .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-0ijImxMKZ80GGxDE text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-0ijImxMKZ80GGxDE .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-0ijImxMKZ80GGxDE .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-0ijImxMKZ80GGxDE .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-0ijImxMKZ80GGxDE .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-0ijImxMKZ80GGxDE #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-0ijImxMKZ80GGxDE .sequenceNumber{fill:white;}#mermaid-svg-0ijImxMKZ80GGxDE #sequencenumber{fill:#333;}#mermaid-svg-0ijImxMKZ80GGxDE #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-0ijImxMKZ80GGxDE .messageText{fill:#333;stroke:none;}#mermaid-svg-0ijImxMKZ80GGxDE .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-0ijImxMKZ80GGxDE .labelText,#mermaid-svg-0ijImxMKZ80GGxDE .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-0ijImxMKZ80GGxDE .loopText,#mermaid-svg-0ijImxMKZ80GGxDE .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-0ijImxMKZ80GGxDE .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-0ijImxMKZ80GGxDE .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-0ijImxMKZ80GGxDE .noteText,#mermaid-svg-0ijImxMKZ80GGxDE .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-0ijImxMKZ80GGxDE .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-0ijImxMKZ80GGxDE .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-0ijImxMKZ80GGxDE .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-0ijImxMKZ80GGxDE .actorPopupMenu{position:absolute;}#mermaid-svg-0ijImxMKZ80GGxDE .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-0ijImxMKZ80GGxDE .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-0ijImxMKZ80GGxDE .actor-man circle,#mermaid-svg-0ijImxMKZ80GGxDE line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-0ijImxMKZ80GGxDE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} altrejectedenter loopfor each message loopfor each chunk continue (重试请求) altretryterminal loopfor each tool group Step 循环继续,buildRequest 重新派生 altconcludednot concluded alterror \| abortedmax-tokensnormal (no tool calls)normal (with tool calls) loop请求循环 (while true) altstill turnEnds \&\&nextStep.length === 0steering arrived altturnEnds \&\& nextStep.length === 0nextStep has work loopStep 循环 altinbox.hasPendingno pending followup(content)send(input, 'next-turn', true)inbox.splice('next-turn', ..., message)agent/inbox/insertedwakeDriver()agent/status (running)kick()while (await turn()) {}turn/start {turn}turnEnds = null, target = 'next-turn'preStep(target, {turn, step})inbox.claim(target, turn)agent/inbox/claimedsystem-prompt/assembleagent/pre-step (waterfall){kind: 'reject'}turnEnds = {kind: 'blocked'}return false{kind: 'enter', messages, assembly}step/start {turn, step}user/message {message, surfaceOp: 'append'}step(assembly)buildRequest(turn, step, tools, system, deriveMessages(), signal)agent/request (waterfall)llm.prepareCall(config)request/header (首次或变更)request/context (变更时){request, preparedCall}preparedCall.stream(request) ?? llm.stream(request)assistant/chunk {turn, step, chunk}session/eventassembler.finishagent/request-error (waterfall){kind: 'retry'}undefinedthrow LlmErrorassistant/messagereturn {kind: 'max-tokens'}assistant/messagereturn {kind: 'completed'}assistant/messageexecuteToolCalls(ctx, turn, step, toolCalls, signal, acceptContext)tool/call {turn, step, callId, name, arguments}tools.prepare → tools.dispatch → tools.finalize/finishtool/result {turn, step, message, surfaceOp: 'append'}acceptContext(additionalContexts){concluded}return {kind: 'completed'}return nullstep/end {turn, step}if turnEnds === null || !== 'max-tokens': turnEnds = stepEndsignal.throwIfAborted()agent/turn-stopping (serial, 无 next)signal.throwIfAborted()break (Turn 停止)target = 'next-step', continuetarget = 'next-step', continueturn/end {turn, reason: turnEnds}abort = new AC, wakeRequested = false, step = 0return true (继续下一个 Turn)return false (停止)if wakeRequested && hasPending: wakeDriver()setPhase({kind: 'idle', lastTurn: turn})agent/status (idle)

8.3 Step 完整流程时序图

Dispatch Session executeToolCalls ctx.llm buildRequest() step() turn() Dispatch Session executeToolCalls ctx.llm buildRequest() step() turn() #mermaid-svg-dBXMTy8R6t95Wiac{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dBXMTy8R6t95Wiac .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dBXMTy8R6t95Wiac .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dBXMTy8R6t95Wiac .error-icon{fill:#552222;}#mermaid-svg-dBXMTy8R6t95Wiac .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dBXMTy8R6t95Wiac .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dBXMTy8R6t95Wiac .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dBXMTy8R6t95Wiac .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dBXMTy8R6t95Wiac .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dBXMTy8R6t95Wiac .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dBXMTy8R6t95Wiac .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dBXMTy8R6t95Wiac .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dBXMTy8R6t95Wiac .marker.cross{stroke:#333333;}#mermaid-svg-dBXMTy8R6t95Wiac svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dBXMTy8R6t95Wiac p{margin:0;}#mermaid-svg-dBXMTy8R6t95Wiac .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-dBXMTy8R6t95Wiac text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-dBXMTy8R6t95Wiac .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-dBXMTy8R6t95Wiac .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-dBXMTy8R6t95Wiac .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-dBXMTy8R6t95Wiac .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-dBXMTy8R6t95Wiac #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-dBXMTy8R6t95Wiac .sequenceNumber{fill:white;}#mermaid-svg-dBXMTy8R6t95Wiac #sequencenumber{fill:#333;}#mermaid-svg-dBXMTy8R6t95Wiac #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-dBXMTy8R6t95Wiac .messageText{fill:#333;stroke:none;}#mermaid-svg-dBXMTy8R6t95Wiac .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-dBXMTy8R6t95Wiac .labelText,#mermaid-svg-dBXMTy8R6t95Wiac .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-dBXMTy8R6t95Wiac .loopText,#mermaid-svg-dBXMTy8R6t95Wiac .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-dBXMTy8R6t95Wiac .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-dBXMTy8R6t95Wiac .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-dBXMTy8R6t95Wiac .noteText,#mermaid-svg-dBXMTy8R6t95Wiac .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-dBXMTy8R6t95Wiac .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-dBXMTy8R6t95Wiac .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-dBXMTy8R6t95Wiac .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-dBXMTy8R6t95Wiac .actorPopupMenu{position:absolute;}#mermaid-svg-dBXMTy8R6t95Wiac .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-dBXMTy8R6t95Wiac .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-dBXMTy8R6t95Wiac .actor-man circle,#mermaid-svg-dBXMTy8R6t95Wiac line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-dBXMTy8R6t95Wiac :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} altpreparedCall 存在 loopfor await chunk of stream continue (回到请求循环) altaction = {kind: 'retry'}action = undefined altparallelexclusive loopfillPool + commitReady altabortednormal loop组循环 Step 继续循环 altconcludednot concluded altfinish = error \| abortedfinish = max-tokensfinish = stop (no tool calls)finish = stop (with tool calls) loop请求循环 step(assembly)system = renderPrompt(assembly)buildRequest(turn, step, tools, system, deriveMessages(), signal)解析 persistedHeader + route构建 seedConfig (frozen)agent/request waterfallllm.prepareCall(proposedConfig, signal)构建 canonicalHeaderrequest/header (首次: initial/resume, 变更: change)request/context (变更时)markAgentLoopRequest(deepFreeze(request)){request, preparedCall}assembler = new BlockAssembler()chunkSeqs = \[\]preparedCall.stream(request)ctx.llm.stream(request)signal.throwIfAborted()assistant/chunk {turn, step, chunk}chunkSeqs.push(seq)assembler.push(chunk)signal.throwIfAborted()finish = assembler.finishagent/request-error waterfallthrow LlmError(failure)assistant/message {message, usage?, sourceEventSeqs}return {kind: 'max-tokens'}assistant/message {message, usage?, sourceEventSeqs}return {kind: 'completed'}assistant/message {message, usage?, sourceEventSeqs}executeToolCalls(ctx, turn, step, toolCalls, signal, acceptContext)planned = toolCalls.map(...)mode = executionMode(first)group = planned.slice(next)group = firstrunGroup(...)tool/call {turn, step, callId, name, arguments}tools.prepare → dispatchtool/result {turn, step, message, surfaceOp: append}acceptContext(additionalContexts)appendSkippedToolCall (未开始的){concluded}{concluded}return {kind: 'completed'}return null

8.4 Turn 结束决策逻辑

#mermaid-svg-h24K6bleA7C4a3Ed{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-h24K6bleA7C4a3Ed .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-h24K6bleA7C4a3Ed .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-h24K6bleA7C4a3Ed .error-icon{fill:#552222;}#mermaid-svg-h24K6bleA7C4a3Ed .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h24K6bleA7C4a3Ed .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-h24K6bleA7C4a3Ed .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h24K6bleA7C4a3Ed .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h24K6bleA7C4a3Ed .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-h24K6bleA7C4a3Ed .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h24K6bleA7C4a3Ed .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h24K6bleA7C4a3Ed .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h24K6bleA7C4a3Ed .marker.cross{stroke:#333333;}#mermaid-svg-h24K6bleA7C4a3Ed svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h24K6bleA7C4a3Ed p{margin:0;}#mermaid-svg-h24K6bleA7C4a3Ed .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-h24K6bleA7C4a3Ed .cluster-label text{fill:#333;}#mermaid-svg-h24K6bleA7C4a3Ed .cluster-label span{color:#333;}#mermaid-svg-h24K6bleA7C4a3Ed .cluster-label span p{background-color:transparent;}#mermaid-svg-h24K6bleA7C4a3Ed .label text,#mermaid-svg-h24K6bleA7C4a3Ed span{fill:#333;color:#333;}#mermaid-svg-h24K6bleA7C4a3Ed .node rect,#mermaid-svg-h24K6bleA7C4a3Ed .node circle,#mermaid-svg-h24K6bleA7C4a3Ed .node ellipse,#mermaid-svg-h24K6bleA7C4a3Ed .node polygon,#mermaid-svg-h24K6bleA7C4a3Ed .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h24K6bleA7C4a3Ed .rough-node .label text,#mermaid-svg-h24K6bleA7C4a3Ed .node .label text,#mermaid-svg-h24K6bleA7C4a3Ed .image-shape .label,#mermaid-svg-h24K6bleA7C4a3Ed .icon-shape .label{text-anchor:middle;}#mermaid-svg-h24K6bleA7C4a3Ed .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-h24K6bleA7C4a3Ed .rough-node .label,#mermaid-svg-h24K6bleA7C4a3Ed .node .label,#mermaid-svg-h24K6bleA7C4a3Ed .image-shape .label,#mermaid-svg-h24K6bleA7C4a3Ed .icon-shape .label{text-align:center;}#mermaid-svg-h24K6bleA7C4a3Ed .node.clickable{cursor:pointer;}#mermaid-svg-h24K6bleA7C4a3Ed .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-h24K6bleA7C4a3Ed .arrowheadPath{fill:#333333;}#mermaid-svg-h24K6bleA7C4a3Ed .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-h24K6bleA7C4a3Ed .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-h24K6bleA7C4a3Ed .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h24K6bleA7C4a3Ed .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-h24K6bleA7C4a3Ed .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h24K6bleA7C4a3Ed .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-h24K6bleA7C4a3Ed .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-h24K6bleA7C4a3Ed .cluster text{fill:#333;}#mermaid-svg-h24K6bleA7C4a3Ed .cluster span{color:#333;}#mermaid-svg-h24K6bleA7C4a3Ed div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-h24K6bleA7C4a3Ed .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-h24K6bleA7C4a3Ed rect.text{fill:none;stroke-width:0;}#mermaid-svg-h24K6bleA7C4a3Ed .icon-shape,#mermaid-svg-h24K6bleA7C4a3Ed .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h24K6bleA7C4a3Ed .icon-shape p,#mermaid-svg-h24K6bleA7C4a3Ed .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-h24K6bleA7C4a3Ed .icon-shape .label rect,#mermaid-svg-h24K6bleA7C4a3Ed .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h24K6bleA7C4a3Ed .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-h24K6bleA7C4a3Ed .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-h24K6bleA7C4a3Ed :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Turn 结束决策逻辑
turn/end reason 值
completed

· 模型正常停止无工具调用

· 或工具 concludesTurn

· 或首步空 Turn
max-tokens

· 模型达到 token 上限

(粘性:后续 completed 不降级)
blocked

· agent/pre-step 返回 reject
aborted

· signal.aborted

· reason = AgentCancelCause
error

· LlmError: 保留 failure

· 其他: errorChain + UNKNOWN
停止检查
turnEnds !== null

&& inbox.nextStep.length === 0
dispatch.serial('agent/turn-stopping')

监听器可 steer 延续
再次检查

turnEnds !== null

&& inbox.nextStep.length === 0
→ break (Turn 停止)
→ target = 'next-step'

→ 继续 Step 循环
Step 结束后的 turnEnds 更新
turnEnds === null?

→ turnEnds = stepEnd
turnEnds.kind === 'max-tokens'?

→ 不覆盖 (粘性)
→ turnEnds = stepEnd

8.5 连续 Turn 驱动机制

#mermaid-svg-MLSvsfIdDFG2vWPk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MLSvsfIdDFG2vWPk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MLSvsfIdDFG2vWPk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MLSvsfIdDFG2vWPk .error-icon{fill:#552222;}#mermaid-svg-MLSvsfIdDFG2vWPk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MLSvsfIdDFG2vWPk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MLSvsfIdDFG2vWPk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MLSvsfIdDFG2vWPk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MLSvsfIdDFG2vWPk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MLSvsfIdDFG2vWPk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MLSvsfIdDFG2vWPk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MLSvsfIdDFG2vWPk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MLSvsfIdDFG2vWPk .marker.cross{stroke:#333333;}#mermaid-svg-MLSvsfIdDFG2vWPk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MLSvsfIdDFG2vWPk p{margin:0;}#mermaid-svg-MLSvsfIdDFG2vWPk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MLSvsfIdDFG2vWPk .cluster-label text{fill:#333;}#mermaid-svg-MLSvsfIdDFG2vWPk .cluster-label span{color:#333;}#mermaid-svg-MLSvsfIdDFG2vWPk .cluster-label span p{background-color:transparent;}#mermaid-svg-MLSvsfIdDFG2vWPk .label text,#mermaid-svg-MLSvsfIdDFG2vWPk span{fill:#333;color:#333;}#mermaid-svg-MLSvsfIdDFG2vWPk .node rect,#mermaid-svg-MLSvsfIdDFG2vWPk .node circle,#mermaid-svg-MLSvsfIdDFG2vWPk .node ellipse,#mermaid-svg-MLSvsfIdDFG2vWPk .node polygon,#mermaid-svg-MLSvsfIdDFG2vWPk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MLSvsfIdDFG2vWPk .rough-node .label text,#mermaid-svg-MLSvsfIdDFG2vWPk .node .label text,#mermaid-svg-MLSvsfIdDFG2vWPk .image-shape .label,#mermaid-svg-MLSvsfIdDFG2vWPk .icon-shape .label{text-anchor:middle;}#mermaid-svg-MLSvsfIdDFG2vWPk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MLSvsfIdDFG2vWPk .rough-node .label,#mermaid-svg-MLSvsfIdDFG2vWPk .node .label,#mermaid-svg-MLSvsfIdDFG2vWPk .image-shape .label,#mermaid-svg-MLSvsfIdDFG2vWPk .icon-shape .label{text-align:center;}#mermaid-svg-MLSvsfIdDFG2vWPk .node.clickable{cursor:pointer;}#mermaid-svg-MLSvsfIdDFG2vWPk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MLSvsfIdDFG2vWPk .arrowheadPath{fill:#333333;}#mermaid-svg-MLSvsfIdDFG2vWPk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MLSvsfIdDFG2vWPk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MLSvsfIdDFG2vWPk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MLSvsfIdDFG2vWPk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MLSvsfIdDFG2vWPk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MLSvsfIdDFG2vWPk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MLSvsfIdDFG2vWPk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MLSvsfIdDFG2vWPk .cluster text{fill:#333;}#mermaid-svg-MLSvsfIdDFG2vWPk .cluster span{color:#333;}#mermaid-svg-MLSvsfIdDFG2vWPk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MLSvsfIdDFG2vWPk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MLSvsfIdDFG2vWPk rect.text{fill:none;stroke-width:0;}#mermaid-svg-MLSvsfIdDFG2vWPk .icon-shape,#mermaid-svg-MLSvsfIdDFG2vWPk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MLSvsfIdDFG2vWPk .icon-shape p,#mermaid-svg-MLSvsfIdDFG2vWPk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MLSvsfIdDFG2vWPk .icon-shape .label rect,#mermaid-svg-MLSvsfIdDFG2vWPk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MLSvsfIdDFG2vWPk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MLSvsfIdDFG2vWPk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MLSvsfIdDFG2vWPk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 连续 Turn 驱动
唤醒锁存场景
维护任务运行中收到 waking 输入

→ wakeRequested = true

→ 维护完成后 wakeDriver()
活跃 Turn 被 abort 后收到 waking 输入

→ wakingAfterAbort = true

→ target 重定向到 next-turn

→ wakeRequested = true

→ Turn 结束后 wakeDriver()
disposed 原因的 abort

→ 不锁存

→ teardown 不等待 model turn
kick() finally 逻辑
phase.kind === 'running'?
setPhase({kind: 'idle', lastTurn: turn})
wakeRequested && inbox.hasPending?
wakeDriver()

(锁存的唤醒重放)
驱动器结束
turn() 返回值决策
inbox.hasPending?
return false

(无 pending → 停止)
return true

(有 pending → 继续)
如果 return true:

  1. phase.abort = new AbortController()

(新 Controller 使旧锁存失效)

  1. phase.wakeRequested = false

(活跃驱动器自己 claim)

  1. phase.step = 0

(新 Turn 从 Step 1 开始)


9. 数据流分析

9.1 核心数据流

#mermaid-svg-Tt7YczRBIFxOCciK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Tt7YczRBIFxOCciK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Tt7YczRBIFxOCciK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Tt7YczRBIFxOCciK .error-icon{fill:#552222;}#mermaid-svg-Tt7YczRBIFxOCciK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Tt7YczRBIFxOCciK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Tt7YczRBIFxOCciK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Tt7YczRBIFxOCciK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Tt7YczRBIFxOCciK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Tt7YczRBIFxOCciK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Tt7YczRBIFxOCciK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Tt7YczRBIFxOCciK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Tt7YczRBIFxOCciK .marker.cross{stroke:#333333;}#mermaid-svg-Tt7YczRBIFxOCciK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Tt7YczRBIFxOCciK p{margin:0;}#mermaid-svg-Tt7YczRBIFxOCciK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Tt7YczRBIFxOCciK .cluster-label text{fill:#333;}#mermaid-svg-Tt7YczRBIFxOCciK .cluster-label span{color:#333;}#mermaid-svg-Tt7YczRBIFxOCciK .cluster-label span p{background-color:transparent;}#mermaid-svg-Tt7YczRBIFxOCciK .label text,#mermaid-svg-Tt7YczRBIFxOCciK span{fill:#333;color:#333;}#mermaid-svg-Tt7YczRBIFxOCciK .node rect,#mermaid-svg-Tt7YczRBIFxOCciK .node circle,#mermaid-svg-Tt7YczRBIFxOCciK .node ellipse,#mermaid-svg-Tt7YczRBIFxOCciK .node polygon,#mermaid-svg-Tt7YczRBIFxOCciK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Tt7YczRBIFxOCciK .rough-node .label text,#mermaid-svg-Tt7YczRBIFxOCciK .node .label text,#mermaid-svg-Tt7YczRBIFxOCciK .image-shape .label,#mermaid-svg-Tt7YczRBIFxOCciK .icon-shape .label{text-anchor:middle;}#mermaid-svg-Tt7YczRBIFxOCciK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Tt7YczRBIFxOCciK .rough-node .label,#mermaid-svg-Tt7YczRBIFxOCciK .node .label,#mermaid-svg-Tt7YczRBIFxOCciK .image-shape .label,#mermaid-svg-Tt7YczRBIFxOCciK .icon-shape .label{text-align:center;}#mermaid-svg-Tt7YczRBIFxOCciK .node.clickable{cursor:pointer;}#mermaid-svg-Tt7YczRBIFxOCciK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Tt7YczRBIFxOCciK .arrowheadPath{fill:#333333;}#mermaid-svg-Tt7YczRBIFxOCciK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Tt7YczRBIFxOCciK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Tt7YczRBIFxOCciK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tt7YczRBIFxOCciK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Tt7YczRBIFxOCciK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tt7YczRBIFxOCciK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Tt7YczRBIFxOCciK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Tt7YczRBIFxOCciK .cluster text{fill:#333;}#mermaid-svg-Tt7YczRBIFxOCciK .cluster span{color:#333;}#mermaid-svg-Tt7YczRBIFxOCciK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Tt7YczRBIFxOCciK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Tt7YczRBIFxOCciK rect.text{fill:none;stroke-width:0;}#mermaid-svg-Tt7YczRBIFxOCciK .icon-shape,#mermaid-svg-Tt7YczRBIFxOCciK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tt7YczRBIFxOCciK .icon-shape p,#mermaid-svg-Tt7YczRBIFxOCciK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Tt7YczRBIFxOCciK .icon-shape .label rect,#mermaid-svg-Tt7YczRBIFxOCciK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tt7YczRBIFxOCciK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Tt7YczRBIFxOCciK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Tt7YczRBIFxOCciK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Session 层
工具层
模型层
请求层
提示层
驱动器层
Inbox 层
输入层
输出层
agent/status

(emit)
agent/inbox/*

(emit)
agent/error

(emit)
用户输入

followup/steer/inject
取消

cancel(cause)
维护任务

runMaintenance(job)
next-turn 队列
next-step 队列
agent/inbox/spliced

(持久)
wakeDriver
kick()

while(turn())
turn()

Step 循环
step()

请求循环
systemPrompt.assemble
RuntimeContextProjection
agent/pre-step waterfall
buildRequest
agent/request waterfall
llm.prepareCall
request/header

(持久)
frozen request

markAgentLoopRequest
ctx.llm.stream
assistant/chunk*

(持久)
BlockAssembler
assistant/message

(持久)
tool/call*

(持久)
tools/pre-execute

→ execute → post-execute
tool/result*

(持久)
additionalContexts

→ next-step inbox
turn/start

(持久)
step/start

(持久)
step/end

(持久)
turn/end

(持久)
deriveMessages()

从日志派生模型历史

9.2 数据传递机制

数据流路径 传递机制 数据格式
用户 → Agent followup/steer/injectsendinbox.splice UserMessage
Inbox → Driver inbox.claim(target, turn) UserMessage[]
Driver → Prompt systemPrompt.assemble(context) PromptAssembly
Driver → LLM ctx.llm.stream(request) GenerateOptions (frozen)
LLM → Driver StreamChunk 流 → BlockAssembler StreamChunkContentBlock[]
Driver → Session session.append(type, data, options) SessionEvent
Session → Driver session.deriveMessages() Message[]
Driver → Tools executeToolCalls(ctx, turn, step, toolCalls, signal, acceptContext) ToolCallBlock[]
Tools → Driver ToolExecutionResulttool/result 事件 ToolExecutionResult
Tools → Inbox acceptContext(context)inbox.splice('next-step', ...) UserMessage
Driver → UI/SDK agent/* 事件 + session/event 事件流

10. 模块间调用关系

#mermaid-svg-mMOHKdujEWlVBKWP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mMOHKdujEWlVBKWP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mMOHKdujEWlVBKWP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mMOHKdujEWlVBKWP .error-icon{fill:#552222;}#mermaid-svg-mMOHKdujEWlVBKWP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mMOHKdujEWlVBKWP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mMOHKdujEWlVBKWP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mMOHKdujEWlVBKWP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mMOHKdujEWlVBKWP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mMOHKdujEWlVBKWP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mMOHKdujEWlVBKWP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mMOHKdujEWlVBKWP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mMOHKdujEWlVBKWP .marker.cross{stroke:#333333;}#mermaid-svg-mMOHKdujEWlVBKWP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mMOHKdujEWlVBKWP p{margin:0;}#mermaid-svg-mMOHKdujEWlVBKWP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mMOHKdujEWlVBKWP .cluster-label text{fill:#333;}#mermaid-svg-mMOHKdujEWlVBKWP .cluster-label span{color:#333;}#mermaid-svg-mMOHKdujEWlVBKWP .cluster-label span p{background-color:transparent;}#mermaid-svg-mMOHKdujEWlVBKWP .label text,#mermaid-svg-mMOHKdujEWlVBKWP span{fill:#333;color:#333;}#mermaid-svg-mMOHKdujEWlVBKWP .node rect,#mermaid-svg-mMOHKdujEWlVBKWP .node circle,#mermaid-svg-mMOHKdujEWlVBKWP .node ellipse,#mermaid-svg-mMOHKdujEWlVBKWP .node polygon,#mermaid-svg-mMOHKdujEWlVBKWP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mMOHKdujEWlVBKWP .rough-node .label text,#mermaid-svg-mMOHKdujEWlVBKWP .node .label text,#mermaid-svg-mMOHKdujEWlVBKWP .image-shape .label,#mermaid-svg-mMOHKdujEWlVBKWP .icon-shape .label{text-anchor:middle;}#mermaid-svg-mMOHKdujEWlVBKWP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mMOHKdujEWlVBKWP .rough-node .label,#mermaid-svg-mMOHKdujEWlVBKWP .node .label,#mermaid-svg-mMOHKdujEWlVBKWP .image-shape .label,#mermaid-svg-mMOHKdujEWlVBKWP .icon-shape .label{text-align:center;}#mermaid-svg-mMOHKdujEWlVBKWP .node.clickable{cursor:pointer;}#mermaid-svg-mMOHKdujEWlVBKWP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mMOHKdujEWlVBKWP .arrowheadPath{fill:#333333;}#mermaid-svg-mMOHKdujEWlVBKWP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mMOHKdujEWlVBKWP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mMOHKdujEWlVBKWP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mMOHKdujEWlVBKWP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mMOHKdujEWlVBKWP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mMOHKdujEWlVBKWP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mMOHKdujEWlVBKWP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mMOHKdujEWlVBKWP .cluster text{fill:#333;}#mermaid-svg-mMOHKdujEWlVBKWP .cluster span{color:#333;}#mermaid-svg-mMOHKdujEWlVBKWP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mMOHKdujEWlVBKWP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mMOHKdujEWlVBKWP rect.text{fill:none;stroke-width:0;}#mermaid-svg-mMOHKdujEWlVBKWP .icon-shape,#mermaid-svg-mMOHKdujEWlVBKWP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mMOHKdujEWlVBKWP .icon-shape p,#mermaid-svg-mMOHKdujEWlVBKWP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mMOHKdujEWlVBKWP .icon-shape .label rect,#mermaid-svg-mMOHKdujEWlVBKWP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mMOHKdujEWlVBKWP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mMOHKdujEWlVBKWP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mMOHKdujEWlVBKWP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外部调用者
Loop 模块 (core/agent-loop)
Agent 模块 (core/agent)
核心模块
followup
create
followup
监听 pre-step
监听 request-error
session/event
dsh-scope

createScope / scopeTarget
dsh-llm

ctx.llm / BlockAssembler / LlmError
dsh-session

ctx.sessions / Session / SessionEvent
dsh-system-prompt

ctx.systemPrompt / PromptAssembly
dsh-tools

ctx.tools / executionMode / scheduler
dsh-settings

installSettingsSection
dsh-invariants

ctx.invariants.register
Agent 接口

followup/steer/inject/cancel
AgentRegistry

enter/announce/setFactory/withInitiator
Inbox

splice/claim/append/clear
AgentEventDispatch

emit/serial/waterfall
ConsumedWork

foldConsumedWork
ModelSelection

installModelSelection
ReactLoopAgent

kick/turn/preStep/step/buildRequest
AgentLoop

create/createAgent/resume/prepare
executeToolCalls

runGroup/commitReady/startCall
RuntimeContextProjection

project/track
invariant

llm/stream 校验
FactoryOwnership

track/dispose/isActive
client-runtime

通过 WebSocket 驱动 Agent
dsh-headless

一次性运行
goal-round-driver

goal round 物化为 Turn
compaction-basic

监听 agent/pre-step + request-error
ui-conversation

渲染 agent/* + session/event


11. 避坑指南

11.1 事件监听器相关

陷阱 正确做法 源码依据
agent/pre-step waterfall 忘记调 next() 必须调 next() 委托,否则管道卡住 agent.ts:preStep() --- dispatch.waterfall(..., () => defaultDecision)
agent/request waterfall 修改 messages 不能修改 messages,只能替换 LlmCallConfig runtime-types.ts --- agent/request JSDoc: "Model-visible content must use logged channels"
agent/turn-stopping 期望有 next() next(),是 serial 事件,不能委托 runtime-types.ts --- @mode serial
agent/turn-stopping 监听器直接停止 Turn 不能直接停止,只能 steer() 延续,数据决定是否停止 agent.ts:turn() --- if (turnEnds && this.inbox.nextStep.length === 0)
agent/request-error 不调 next() 也不返回 retry 返回 undefined(默认)使失败终止,返回 {kind:'retry'} 拥有恢复 agent.ts:step() --- if (action?.kind !== 'retry') throw new LlmError(...)

11.2 Inbox 相关

陷阱 正确做法 源码依据
inject() 期望立即触发驱动器 inject() 不唤醒驱动器,需另一条 waking 消息 runtime-types.ts --- inject JSDoc: "without waking the driver"
inject() 可能错过当前 Step 是的,如果 pre-step 已 claim 批次,inject 在下一个 Step 边界才消费 runtime-types.ts --- "It may miss a request whose pre-step already claimed its batch"
重复消息 ID 进入 inbox validate() 检查去重,重复抛错 inbox.ts:validate() --- if (ids.has(message.id)) throw
cancel() 后期望 pending 工作保留 默认 keepInbox=false,清空 inbox;设 keepInbox: true 保留 runtime-types.ts --- CancelOptions.keepInbox
在 maintenance 期间发送 waking 输入期望立即处理 waking 被锁存为 wakeRequested,maintenance 完成后才 wakeDriver() agent.ts:wakeDriver() --- maintenance 分支

11.3 请求构建相关

陷阱 正确做法 源码依据
修改 Loop 构建的 request 对象 request 被 deepFreeze,修改抛错 agent.ts:buildRequest() --- markAgentLoopRequest(deepFreeze(...))
期望 agent/request waterfall 能改变 messages 不能,messages 从日志派生,waterfall 只能改 config agent.ts:buildRequest() --- messages: boundaryMessages 不受 waterfall 影响
未设置 provider/model 期望默认值 未设置时 throw 'no provider/model' agent.ts:buildRequest() --- `if (!proposedConfig.provider
请求的 messages 与日志派生不一致 invariant 校验会 fail('log-reconstruction desync') invariant.ts --- JSON.stringify(options.messages) !== JSON.stringify(expected)

11.4 工具调度相关

陷阱 正确做法 源码依据
工具结果不按模型顺序到达 commitReady() 保证有序提交,committed 只跨连续 slot 推进 tool-calls.ts:commitReady()
并行调用中混入独占调用 fillPool() 重新分类,发现非 parallel 时 break 形成新屏障 tool-calls.ts:fillPool() --- executionMode(nextCall).kind !== 'parallel' → break
abort 后期望工具结果完整 已开始的调用结果有序提交;未开始的获得合成错误结果 tool-calls.ts:runGroup() --- appendSkippedToolCall
additionalContexts 期望在当前 Step 消费 通过 acceptContext 注入 next-step inbox,在下一个 Step 边界消费 tool-calls.ts:commitReady() --- acceptContext(context)
maxParallelToolCalls 修改后期望立即生效 每个调度组开始时读取最新值,当前组不受影响 index.ts --- getter 读取 settings source

11.5 生命周期相关

陷阱 正确做法 源码依据
runMaintenance 在 Agent 活跃时调用 同步 throw 'already has active work' agent.ts:runMaintenance() --- if (this.phase.kind !== 'idle') throw
disposed 后发送 waking 输入 disposed 原因不锁存唤醒,teardown 不等待 agent.ts:wakeDriver() --- reason?.kind !== 'disposed' 检查
工厂销毁后期望创建新 Agent ownership.isActive() 返回 false,创建被拒绝 index.ts:prepare() --- if (!this.ownership.isActive()) throw
whenIdle() 期望标识特定消息的完成 whenIdle() 跟踪整个 Agent 活动,不标识特定消息 runtime-types.ts --- "does not identify the settlement of any particular message"
配置中 sessionIdresumeSessionId 同时设置 互斥,加载时 throw index.ts:validateConfiguredAgents()

附录:关键数值与默认值

参数 默认值 来源
DEFAULT_MAX_PARALLEL_TOOL_CALLS 10 constants.ts
AgentStatus 'idle'(初始) runtime-types.ts
Phase 初始 {kind: 'idle', lastTurn: 最后一个 turn/start} agent.ts 构造函数
requestHeaderLogged false(初始) agent.ts
InboxTarget 'next-turn'(首个 Step)、'next-step'(后续 Step) agent.ts:turn()
maxTokens 摘要生成 cap 8192 AgentOptions.maxTokens(可选)

报告说明 :本报告基于 packages/core/agent-loop/(6 个源码文件,1662 行)和 packages/core/agent/(8 个源码文件,1636 行)共 14 个 TypeScript 源码文件逐行分析编写。所有架构图、时序图、流程图、状态图均使用 Mermaid 模板绘制,内容严格基于代码实际实现,无臆造内容。报告覆盖了 Agent 接口设计、ReactLoopAgent 驱动器实现、Turn/Step/Round 三层循环机制、工具调用调度、AgentLoop 工厂服务、请求重建不变量、数据流、模块间调用关系和避坑指南。

相关推荐
掰头战士1 小时前
AgentLoop: 从 while(true) 到生产级循环
typescript·llm·agent
用户8082598666871 小时前
给 Agent 装上记忆:多轮对话的历史管理——token 预算、按轮裁剪与滚雪球摘要
agent
用户8082598666871 小时前
给 Agent 接上知识库:RAG 检索链路——分块策略、混合检索与重排
agent
山间小僧1 小时前
「AI学习笔记」Agent Memory(一)会话内记忆
aigc·agent·vibecoding
科技每日热闻2 小时前
AI创业公司需要GPU算力进行模型训练和推理,推荐选择哪些云平台?
ai
可乐ea3 小时前
DeepSeek-V4.1-Flash 拆解:1M 上下文,KV cache 只剩 1/4
ai智能体·长上下文·deepseek·kvcache
科技每日热闻3 小时前
AI 创业公司开展模型训练与推理需要 GPU 算力,该怎么挑选云平台?
ai
事圆则缓3 小时前
遗留项目改造实战指南:从可读性、代码质量到性能和 AI 约束
android·ai·代码规范
染指11104 小时前
113.Agent-LangChain核心组件-大模型Short-term_memory短期记忆和PostgreSQL记忆存储
人工智能·langchain·agent·agents