runAgent 解析

定位

runAgent() 是子 agent 的真正运行入口。

文件:

  • src/tools/AgentTool/runAgent.ts

一句话:

runAgent() 不是一个新引擎,而是“为子 agent 重新构造运行上下文,然后再次调用统一的 query() 主循环”。

核心职责

1. 生成子 agent 身份

  • 生成 agentId
  • 设置 transcript sidechain
  • 记录 metadata
  • 注册 tracing / analytics context

2. 构造初始消息

  • 普通 agent:用简单 prompt message
  • fork agent:携带父上下文 forkContextMessages
  • 并过滤 incomplete tool calls,避免 API 协议错误

3. 构造 agent-specific context

  • userContext
  • systemContext
  • toolPermissionContext
  • agent-specific appState view
  • agent-specific tool pool
  • agent-specific abortController

4. 构造 system prompt

普通 agent:

  • 使用 agent 自己的 system prompt

fork agent:

  • 使用父 agent 已渲染好的 system prompt bytes
  • 目标是 prompt cache byte-identical

5. 创建子上下文

核心调用:

  • createSubagentContext(...)

这一步决定:

  • 哪些状态从父级继承
  • 哪些隔离
  • 哪些 callback 共享
  • 哪些缓存复制

6. 最终重新进入 query()

真正执行发生在:

for await (const message of query({...}))

这是理解整套 agent 体系最关键的一点:

  • 主 agent 用 query()
  • 子 agent 也用 query()

重要设计点

子 agent 与主 agent 同构

这意味着:

  • 主 / 子 agent 共用同一套 tool system
  • 共用同一套 permission system
  • 共用同一套 transcript / message 机制

上下文隔离是通过重新构造 ToolUseContext 实现的

而不是通过重写整个 runtime。

async agent 与 sync agent 的差别不在 runAgent 本身

runAgent() 负责“跑”。

是否:

  • 前台跑
  • 后台跑
  • 变成 task

这些决策主要在 AgentTool.call() 和 task 层完成。

与其他模块关系

上游

  • AgentTool.call() 调用它

下游

  • query() 真正执行 loop
  • recordSidechainTranscript() 写 sidechain transcript
  • createSubagentContext() 派生上下文
  • initializeAgentMcpServers() 附加 agent-specific MCP servers

当前理解

runAgent() 才是子 agent runtime 的门槛文件。

如果要真正看懂:

  • 子 agent 如何继承父上下文
  • 为何 fork child 能与父请求共享 prompt cache
  • 子 agent 为什么仍然能安全跑同一套 query loop

那必须继续读:

  • createSubagentContext
  • query.ts
  • toolOrchestration