openclaw源码解读(8)—— Agent执行链路1:agent-run-dispatch.ts 调度分发
从入口开始——agent-run-dispatch.ts(283 行),消息进来后分发给 Agent 的第一站。
agent-run-dispatch.ts 是个调度包装层,核心是把 Gateway 请求转成 agentCommandFromGatewayIngress,然后处理成功/超时/错误三种结果。真正的执行逻辑在 agent-run-handler.ts
|
行号 |
你的记录 |
准确度 |
说明 |
|
L29-38 |
读取 agent 运行超时的参数 |
✅ |
readAgentRunTimeoutAttribution — 从 meta 中提取 timeoutPhase 和 providerStarted |
|
L40-42 |
网关中断的显示原因 |
⚠️ |
isGatewayAbortSignalReason — 更准确说是判断是否为网关中断信号(undefined / AbortError / TimeoutError),不是"显示" |
|
L44-58 |
网关中断的拒绝原因 |
⚠️ |
isGatewayAgentAbortRejection — 返回 boolean,判断当前 error+signal 组合是否构成网关中断拒绝,不是"原因"是"判断" |
|
L60-65 |
解析网关中断的原因 |
✅ |
resolveGatewayAgentAbortStopReason — 返回 "restart" | "rpc" | "timeout" 三选一 |
|
L67-69 |
解析网关中断的任务状态 |
✅ |
resolveAbortedAgentTaskStatus — "timeout" → "timed_out",其他 → "cancelled" |
|
L71-75 |
解析网关中断的超时阶段 |
✅ |
resolveGatewayAgentAbortTimeoutPhase — restart 时返回 "gateway_draining" |
|
L77-79 |
解析网关中断的原因 |
⚠️ |
resolveAbortedAgentStopReason — 从 ChatAbortControllerEntry 解析,和 L60-65 不同(彼从 signal 解析,此从 entry 解析),两者容易混淆 |
|
L81-88 |
删除重复的网关实体 |
✅ |
deleteGatewayDedupeEntries — 遍历 keys 删除 dedupe map 中的条目 |
|
L90-283 |
agent 从网关进入后的运行逻辑 |
✅ |
dispatchAgentRunFromGateway — 核心函数,入口到出口的完整调度 |
两个小纠正 + 补充理解
1. L40-42 vs L44-58:判断链,不是"原因"
这两个函数的关系是层层递进的:
isGatewayAbortSignalReason(reason) ← "这是个普通的网关中断信号吗?"
↓
isGatewayAgentAbortRejection(error, signal) ← "这是一次 agent 中断拒绝吗?"
↓ (更严格:除了检查 signal,还要检查 error)
resolveGatewayAgentAbortStopReason(signal) ← "具体是什么原因?restart/timeout/rpc"
isGatewayAgentAbortRejection 的判断逻辑:
- signal 没 abort → 直接 false
- signal.reason 是 restart → true(重启中断必算)
- signal.reason 是 TimeoutError → true
- signal.reason 不是普通中断信号 → false(比如用户主动取消)
- 最后兜底:error 本身是 AbortError/TimeoutError → true
2. L77-79 和 L60-65 不是一回事
L60-65: resolveGatewayAgentAbortStopReason(signal)
从 AbortSignal 解析 → "restart" | "rpc" | "timeout"
L77-79: resolveAbortedAgentStopReason(entry)
从 ChatAbortControllerEntry 解析 → string (default "rpc")
前者在 .catch() 分支用(直接从 signal 拿),后者是导出函数,外部拿 entry 就能解析。你笔记里两处都写了"解析网关中断的原因",建议区分一下。
L90-283 核心函数结构速览
dispatchAgentRunFromGateway 就是整个文件的主菜,193 行的异步调度逻辑:
1. L112-133: Task Tracking 初始化
├── shouldTrackTask? → createRunningTaskRun (最佳努力,失败不阻塞)
2. L134-144: settle 闭包定义
└── 调用 onSettled 回调,决定是否持久化
3. L145-235: agentCommandFromGatewayIngress(...).then(...)
├── 成功路径:
│ ├── 检查 aborted 状态
│ ├── 读取 timeoutAttribution
│ ├── 终结 tracked task (tryFinalizeTrackedAgentTask)
│ ├── 构建 payload (status/summary/stopReason/timeoutPhase)
│ ├── 构建 terminalOutcome (buildAgentRunTerminalOutcome)
│ ├── settle → 持久化 dedupe → respond
│ └── send second res frame (for TS clients)
│
└── .catch(...): 错误路径
├── isGatewayAgentAbortRejection 判断
├── 构建 errorShape(UNAVAILABLE, ...)
├── 构建 terminalOutcome (timeout/error)
├── settle → persist → respond
4. L236-239: .finally()
├── clearAgentRunContext (清理运行上下文)
└── cleanupAbortController (清理中断控制器)
关键设计要点:
- Task tracking 是 best-effort:失败了 log warning,不阻塞 agent run
- settle 闭包控制持久化节奏:先回调再写 dedupe
- 两次 respond:Swift 客户端用第一次,TS 客户端等第二次 expectFinal
- .finally() 保证无论如何都会清理 run context 和 abort controller
主要就两处需要微调——L40-42 和 L44-58 是"判断"而非"显示/解析原因",L77-79 和 L60-65 来源不同。
更多推荐


所有评论(0)