agent-run-execution-phase.ts Agent 循环调用 LLM、解析 Tool Call、执行工具、再把结果喂回 LLM 的整个闭循环这个文件只有一个导出函数:startAgentRunExecution。它是 agent-run-handler 第⑨阶段的实现——不执行 LLM,而是做最后的上下文组装 + 发射

整体结构

startAgentRunExecution(params)        // 入口

  │

  ├─ 1. 获取 admission 锁              // retainGatewayRootWorkAdmissionContinuation 防止                                                                        gateway 在执行期间关闭  │     

  │

  ├─ 2. prepared.activeGatewayWorkAdmission.run(async () => {

  │      │                               // 真正的 session 级准入锁

  │      ├─ 3. 中止检查                  // 已被 abort?直接返回 timeout

  │      ├─ 4. 子 Agent session 恢复     // reactivateCompletedSubagentSession

  │      ├─ 5. 发射 session 变更事件     // emitSessionsChanged (create/send)

  │      ├─ 6. 标注消息来源              // annotateInterSessionPromptText

  │      ├─ 7. 创建转录记录器            // userTurnTranscriptRecorder (条件创建) 

  │      ├─ 8. 处理 exec 审批延续        // consumeExecApprovalFollowupRuntimeHandoff

  │      ├─ 9. 解析插件工具授权          // runtimePluginToolGrant

  │      ├─ 10. 组装 runContext          // channel/account/sender/thread

  │      ├─ 11. 组装 ingressOpts         // 所有 LLM 调用需要的参数 

  │      └─ 12. dispatchAgentRunFromGateway()  //  发射!

  │

  └─ 13. catch/finally 清理             // 失败时恢复 + 释放所有锁


逐层解析

层 1-2:双重准入锁

let releaseGatewayRootContinuation = retainGatewayRootWorkAdmissionContinuation();

// ↑ 第一层:gateway 级别的锁,防止进程在 run 执行中关闭

void prepared.activeGatewayWorkAdmission.run(async () => {

// ↑ 第二层:session 级别的锁,同一 session 同时只能有一个 run

retainGatewayRootWorkAdmissionContinuation() 告诉 gateway "还有工作在跑,别关"。activeGatewayWorkAdmission.run() 确保同一 session 不会有两个并发 run。

层 3:中止检查 L118-142

if (prepared.activeRunAbort.controller.signal.aborted) {

  // 已被 abort(比如用户取消了),直接返回 timeout 状态

  setAbortedAgentDedupeEntries({...});

  params.respond(true, { runId, status: "timeout", summary: "aborted", ... });

  return;

}

在真正执行之前,检查是否已经收到取消信号

层 4:子 Agent session 恢复 L144-150

if (!params.isOneShotModelRun && params.resolvedSessionKey) {

  await reactivateCompletedSubagentSession({

    sessionKey: params.resolvedSessionKey,

    runId: params.runId,

    task: params.message,

  });

}

这是多 Agent 场景的关键! 如果当前 session 是一个已经完成的子 Agent session(比如上一个任务完成了,现在又来新消息),重新激活它。不是创建新 session,而是复用旧 session,保持上下文连续性。

层 5:Session 变更事件广播 L151-173

// 新建 session → emit "create"

if (isNewSession) emitSessionsChanged(..., "create");

// 每次 run → emit "send"  

emitSessionsChanged(..., "send");

通知所有 WebSocket 客户端 session 列表有变化。

层 6-7:消息标注 + 转录记录 L175-236

// 在消息中标注跨 session 的来源信息,inputProvenance 记录了消息的来源链路(比如"来自子 Agent X 的结果")

message = annotateInterSessionPromptText(message, params.inputProvenance);

// 条件创建转录记录器(记录用户消息到 session transcript),userTurnTranscriptRecorder 把用户消息写入 session 持久化存储。

const userTurnTranscriptRecorder = params.resolvedSessionKey && ...

  ? createUserTurnTranscriptRecorder({...})

  : undefined;

层 8:Exec 审批延续 L246-268

// 如果这个 run 是 exec approval(用户批准了一个高风险命令)的延续,

// 消费 handoff 状态,传递 bashElevated 权限

execApprovalFollowupRuntimeHandoff = consumeExecApprovalFollowupRuntimeHandoff({...});

层9:件工具授权  L271-276

// 如果 run 来自插件 spawned 的子 agent,传递插件额外授权的工具

runtimePluginToolGrant = params.client?.internal?.agentRunTracking === "plugin_subagent"

  ? params.client.internal.runtimePluginToolGrant

  : undefined;

层 10-11:组装 runContext + ingressOpts L285-298

这是最核心的数据组装:

const runContext = {

  messageChannel,     // 消息来源频道

  accountId,          // 发送者账户

  senderId,           // 发送者 ID

  groupId,            // 分组 ID(多 Agent 编组)

  groupChannel,       // 分组频道

  groupSpace,         // 分组空间

  currentChannelId,   // 当前频道

  currentThreadTs,    // 线程时间戳

};

// ingressOpts 包含了所有 LLM 调用需要的参数 L306-404

const ingressOpts = {

  message, images, imageOrder,    // 消息内容

  agentId,                        // 用哪个 Agent

  provider, model,                // 用哪个模型

  sessionId, sessionKey,          // session 信息

  thinking,                       // thinking 级别

  deliver, deliveryTargetMode,    // 投递方式

  channel, threadId,              // 频道/线程

  spawnedBy,                      // 父 Agent 标识 

  groupId, groupChannel, groupSpace, // 多 Agent 编组 

  toolsAllow,                     // 允许的工具列表

  runtimePluginToolGrant,         // 插件工具授权

  workspaceDir, cwd,              // 工作目录

  userTurnTranscriptRecorder,     // 转录器

  abortSignal,                    // 取消信号

  lifecycleGeneration,            // 生命周期版本

  // ... 还有更多

};

注意三个多 Agent 关键字段

  • spawnedBy:谁 spawn 了这个 Agent(父子关系)
  • groupId / groupChannel / groupSpace:Agent 编组信息
  • skipInitialSessionTouch:子 Agent 可能不需要触发 initial touch
层 12:发射  L305-420

dispatchAgentRunFromGateway({

  ingressOpts,           // 所有上下文

  runId,                 // 本次 run 的 ID

  dedupeKeys,            // 幂等键

  abortController,       // 取消控制器

  cleanupAbortController, // 清理函数

  onSettled: ...,        // cron 延续完成回调

  respond,               // 响应函数

  context,               // gateway 上下文

  taskTrackingMode,      // 任务追踪模式

  restoreAdmittedRecovery, // 重启恢复

});

dispatchAgentRunFromGateway(来自 agent-run-dispatch.ts)才是真正启动 Agent 循环的地方。这个函数创建 Agent 进程/线程、开始 LLM↔Tool 循环。

层 13:失败清理

catch (err) {

  // 设置错误 dedupe 项

  // 返回 error 响应给客户端

} finally {

  if (!dispatched) {

    // 恢复重启恢复状态

    // 释放 cron continuation

    // 释放 main restart recovery owner

    // 清理 admitted run

  }

}

如果发射失败,完整回滚所有状态,防止锁泄漏。


切入点细化

                                          startAgentRunExecution()

                                                         │

      ┌───────────────────┼───────────────────┐

      │                                                 │                                                 │

  spawnedBy                          groupId/group*                        workspaceDir/cwd

  (父子关系)                               (Agent编组)                             (工作目录隔离)

      │                                                │                                                  │

      ▼                                              ▼                                                 ▼

  dispatchAgentRunFromGateway() ──→ agent-run-loop ──→ LLM + Tools

  

  在这里:                                        在这里:

  • 如果主 Agent 决定拆分任务        • 子 Agent 执行自己的闭循环

  • 调用 sessions_spawn                 • 完成后返回结果给主 Agent

  • 传入 spawnedBy=主Agent ID     • session 持久化结果

关键发现spawnedBygroupIdgroupSpace 这些多 Agent 基础设施已经存在于代码中,比赛要做的是:

  1. 在 agent-run-handler 的 routing 阶段识别需要拆分
  2. 在 agent-run-execution-phase 的 ingressOpts 中正确设置 spawnedBy 和 groupId
  3. 让子 Agent 的执行结果能回流到主 Agent 的上下文
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐