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()
-
accepting = false
-
abort('agent loop is not active')
-
resolve inactive
-
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):
-
abort('lifecycle disposed')
-
removeEventListener
-
await machineReady (如果 machine 未建)
-
machine.cancel({kind:'disposed'})
-
await machine.whenIdle()
-
machine.scope.dispose()
-
detachAgent?.() + detachSession?.()
-
untrack()
-
!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}
关键设计:
-
三方 Abort 融合:callerSignal(调用者取消)、ownership.signal(工厂销毁)、lifecycle(owner fiber 卸载)三方 abort 原因融合到一个 AbortController。
-
反向 Teardown :在任何资源存在之前注册 teardown,使 setup 过程中的卸载找到可工作的 disposer 而非泄漏。teardown 是 memoized 的------所有 racing owner await 同一个 quiescence。
-
publish 原子性 :
enter→announce→session-start每步后assertLive(),同步 announce 监听器可能已启动 teardown。 -
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:
- phase.abort = new AbortController()
(新 Controller 使旧锁存失效)
- phase.wakeRequested = false
(活跃驱动器自己 claim)
- 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/inject → send → inbox.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 |
StreamChunk → ContentBlock[] |
| 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 | ToolExecutionResult → tool/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" |
配置中 sessionId 和 resumeSessionId 同时设置 |
互斥,加载时 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 工厂服务、请求重建不变量、数据流、模块间调用关系和避坑指南。