最近在调 Multi-Agent 系统的时候,正好赶上圈里在刷“cursor waiting for subagent”这个状态提示。很多人一看 waiting 就以为卡死了,其实它背后是一整套 SubAgent 调度过程,只是产品层把它简化成了一个转圈图标。我一度也在 Microsoft Agent Framework 里被这个“等待”折腾得够呛——父 Agent 把任务丢给子 Agent,然后进入一个既不报错也不返回的悬空状态,日志打到一半就没了下文。后来把概念理清楚、把编排方式改对,整套链路才算真正跑通。
这篇东西我就按自己的实际路径来写:从为什么需要 SubAgent、Agent Framework 里几个核心概念怎么区分,到环境搭建、SubAgent 实战、Orchestrator 和 GroupChat 的选型,最后落到“waiting for subagent”背后的运行机制和排查思路。适合正在做多智能体编排、或者在 Cursor 这类 AI 编码工具里见过 subagent 等待现象的开发者参考,就算你之前只写过单体 Agent,照着这篇也能把 Multi-Agent 串起来。
1. 为什么单体 Agent 会在复杂任务上失控
先说一个反直觉的结论:Agent 不是越“大”越好,指令不是越“全”越好。把一个项目级任务全部塞进一个 Agent 的 system prompt 里,短期看省事,长期看全是债。
1.1 一个 prompt 干到底的局限
我最早做 AI 内容工作流的时候,所有逻辑都堆在一个 Agent 里:又要检索资料、又要分析数据、又要写初稿、又要自查纠错。prompt 写了将近两千字,工具挂了一排,看起来功能很全,跑起来问题很多。
最典型的是上下文污染。Agent 在执行“检索资料”这一步时,会往上下文里塞大量网页摘要;等它执行到“写初稿”时,这些原始材料还占着大量 token,模型容易被无关信息带偏。我的实测结果是:单 Agent 跑长任务时,后半段输出的风格和前半段经常不一致,甚至会出现把参考资料里的错误数据原样抄进结论的情况。
其次是工具误调用。工具一多,模型自己都不知道该先调哪个。我遇到过 Agent 在“总结数据”阶段误调了“发送邮件”工具,正经输出没生成,先给测试邮箱发了一封半成品。这类问题不是调 prompt 能彻底解决的,而是架构问题——职责没有拆分。
还有一个隐性成本:不可审计。单 Agent 执行完,你只知道最终输出,中间哪一步用了哪个工具、哪一段上下文影响了决策,基本是黑盒。出了问题只能从头调 prompt,调试效率很低。
1.2 SubAgent 切分的本质:一个大脑拆成一个主管加多个专家
SubAgent 的核心思路,是把“一个全能的 Agent”拆成“一个主管 Agent + 多个专职 SubAgent”。主管负责理解用户目标、拆解任务、决定把子任务交给谁;SubAgent 只负责自己那一亩三分地。
这个模式特别像现实里的项目组:项目经理不亲自写代码,他拆需求、分派任务、验收结果;后端、前端、测试各自干活。每个角色只需要知道自己该做什么,不需要理解整个项目的所有细节。
落到工程上,SubAgent 有四个实打实的好处:
- 上下文隔离。每个 SubAgent 只接收跟自己任务相关的上下文,不会被其他环节的中间产物污染。
- 指令简化。每条指令可以写得短而精准,模型遵循度更高,不需要在超长 prompt 里“大海捞针”。
- 工具解耦。检索工具只挂在研究 SubAgent 上,写作 SubAgent 不需要挂搜索工具,误调用概率大幅下降。
- 可观测性。每个 SubAgent 的输入、输出、工具调用记录独立,出问题能定位到具体环节。
我后来重构内容工作流的时候,把单 Agent 拆成了“研究-写作-审校”三个 SubAgent,输出质量明显提升,而且调试起来舒服得多。哪一步出问题,直接看对应 SubAgent 的日志就行。
1.3 Microsoft Agent Framework 在这条路线上的定位
选 Microsoft Agent Framework 之前,我其实比较过 LangGraph 和 AutoGen。LangGraph 是把 Agent 流程定义成图,节点和边非常明确,适合流程稳定的场景,但写起来偏重,改一个分支要动图结构。AutoGen 的对话编排很灵活,但多 Agent 会话管理要是自己写,状态维护还是挺费劲的。
Agent Framework 给我的感觉是取了中间路线。它把 Agent、SubAgent、AgentGroupChat、AgentOrchestrator 这几个概念直接做成框架内置能力,不用自己实现会话路由和任务调度。同时兼容 OpenAI、Azure OpenAI 等主流模型服务,工具定义方式跟 Semantic Kernel / OpenAI Function Calling 一脉相承,上手成本不高。
有一点需要提醒:Agent Framework 的迭代速度很快,2025 年下半年的 API 到现在已经有过调整。你看到这篇文章的时候,具体方法名可能跟官方最新版有出入,但核心概念——Agent 定义、SubAgent 委派、GroupChat 协作、Orchestrator 规划——是稳定的,把概念吃透,API 变化只是查文档的事。
2. 开跑之前:Agent、SubAgent、Orchestrator 和 GroupChat 到底怎么区分
我在刚开始接触 Agent Framework 时踩过一个坑:看到文档里 SubAgent、Orchestrator、GroupChat 这几个词同时出现,就以为它们是同一件事的三种写法。实际上它们解决的是不同层级的问题,用错了轻则代码能跑但效果差,重则直接陷入死循环。
2.1 Agent 是执行单元,不是进程
Agent Framework 里的Agent就是一个带 name、instructions、model、tools 的对象。它负责接收一个任务,调用模型,执行工具调用,返回结果。
这里有个容易误解的点:一个 Agent 对象不是常驻进程,它本身不“同时”做多件事。它只是被调用时才会执行一次推理循环。真正让它有能力的是 instructions(行为准则)和 tools(能力边界)。
2.2 SubAgent 是运行期被委派的 Agent
SubAgent 不是一个新类型,它本质还是 Agent,只是它的定位是“被其他 Agent 调用”。在 Agent Framework 里,SubAgent 可以通过两种方式被使用:一种是父 Agent 在推理过程中通过工具调用把子任务委派出去;另一种是在编排层预先定义好,由 Orchestrator 或 GroupChat 决定何时调用它。
我习惯把 SubAgent 理解成“专家”。它不关心全局目标,只关心别人交给它的那件具体事。写在 SubAgent 的 instructions 里的内容,应该聚焦在“怎么做这件事”,而不是“为什么要做这件事”。
2.3 Orchestrator 和 GroupChat 是两种编排范式
这两个概念是 Agent Framework 里最核心的编排方式,但很多新手搞混。我直接说人话:
- GroupChat:一群人坐在一起开会,按一定规则轮流发言,最终达成共识或产出结果。
- Orchestrator:一个项目经理,自己先做计划,再按计划逐个或并行地分派任务,收集结果后汇总。
| 对比项 | AgentGroupChat | AgentOrchestrator |
|---|---|---|
| 决策方式 | 参与者轮流发言/按指令轮转 | 规划者先输出执行计划 |
| 适用场景 | 头脑风暴、多角色讨论、共识型任务 | 任务链清晰、可动态拆解的执行型任务 |
| 实现复杂度 | 低,配置参与者即可 | 较高,需要计划生成与执行跟踪 |
| 额外开销 | 每次轮转都有模型调用 | 规划阶段额外消耗一次模型调用 |
| 灵活性 | 中等,对话结构相对固定 | 高,计划可动态调整 |
用 GroupChat 做头脑风暴很合适,让产品经理、技术、设计三个 Agent 互相补充、互相挑战,效果比单 Agent 自问自答好很多。但如果是“检索资料-写代码-走测试”这种执行链,用 GroupChat 就会很啰嗦——每个 Agent 都要发言,白白烧 token,而且容易跑偏。
2.4 handoff 是 Multi-Agent 间的“交接班”协议
在多 Agent 系统里,Agent 之间不能直接“喊话”,它们通过 handoff 机制交接控制权。一个 Agent 在推理完成后,可以选择返回一个 handoff 信号,把当前对话上下文交给另一个 Agent 继续处理。
这有点像接力跑:上一棒把接力棒(对话上下文)交到下一棒手里,下一棒在既有上下文基础上继续跑。实现上通常是一个特殊的工具调用,框架层识别到这个调用后,会把对话会话切换给目标 Agent。
理解 handoff 对排查问题很重要。“waiting for subagent”很多时候就是 handoff 链路断了——上一棒已经触发交接,但下一棒没有正常启动或者启动后没有返回,整个会话就悬在那里。
3. 搭建开发环境与最小可运行实例
概念理清之后,直接上手搭环境。这里我不会写太多废话,给你一条我已经跑通的路径。
3.1 JavaScript/TypeScript 项目初始化
Agent Framework 官方对 TypeScript 和 Python 都支持得很好。我个人长期用 Node.js 做工具链,所以以 TypeScript 为例,Python 的套路基本一致。
mkdir agent-framework-demo cd agent-framework-demo npm init -y npm install @microsoft/agent-framework dotenv npm install -D typescript tsx @types/node这里要提醒一句:不同版本包名和导出可能有变化。如果你npm install的时候提示包不存在,去官方文档查一下最新的包名,这是 Agent Framework 迭代期的常态,不是你操作错了。
然后建一个tsconfig.json,module 设为ESNext、target 设为ES2022,跑示例脚本用tsx:
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "esModuleInterop": true } }3.2 模型配置与认证
Agent Framework 本身不提供模型,它需要接一个模型服务。我用的是 OpenAI 兼容接口,环境变量里配一个模型名就行。先建.env:
AZURE_OPENAI_API_KEY=xxx AZURE_OPENAI_ENDPOINT=https://xxx.openai.azure.com/ OPENAI_MODEL_NAME=CHAT_MODEL_DEPLOYMENT_NAME如果你用 OpenAI 官方接口,把 baseURL 指向https://api.openai.com/v1即可。记得把所有密钥放进.env,不要硬编码到代码里,否则万一仓库公开就是事故。
3.3 定义第一个 Agent
有了模型配置,就可以定义 Agent 了。这里是最小示例,只有一个 Agent,用来验证链路通不通。
import { Agent } from "@microsoft/agent-framework"; const assistant = new Agent({ name: "assistant", instructions: "你是一个乐于助人的助手,回答要简洁、准确。", model: process.env.OPENAI_MODEL_NAME!, }); const response = await assistant.run("用一句话解释什么是 Multi-Agent 系统"); console.log(response);assistant.run()这一步会完成“把任务交给模型-模型返回结果”的完整推理循环。注意,这个阶段还不能体现 Multi-Agent,它只是验证你的模型配置、SDK 安装、环境变量都没问题。
3.4 验证工具调用是否生效
跑通纯对话之后,紧接着要验证工具调用链路。因为 Multi-Agent 的核心就是工具的编排,如果工具调用不流畅,后面 SubAgent 委派一定会出问题。
import { Agent } from "@microsoft/agent-framework"; const orderAgent = new Agent({ name: "orderAssistant", instructions: "你是电商客服助手。查询订单时使用 getOrderStatus 工具。", model: process.env.OPENAI_MODEL_NAME!, tools: [ { name: "getOrderStatus", description: "根据订单号查询订单当前状态", parameters: { type: "object", properties: { orderId: { type: "string" } }, required: ["orderId"] }, execute: async ({ orderId }) => { return JSON.stringify({ orderId, status: "已发货" }); } } ] }); const result = await orderAgent.run("订单 A12345 现在到哪一步了?"); console.log(result);这个示例跑通,说明“模型决定调用工具-框架执行 execute-把结果返回给模型”这条链路是完整的。这一步是后面所有 SubAgent 委派的基础,别跳。
4. 用 SubAgent 拆分任务:一个“研究-写作-审校”的完整例子
环境通了,工具调用也通了,现在进入正题:用 SubAgent 重构一个真实任务。我拿我日常做的最多的“内容生产工作流”举例,你换成“数据分析-生成报告-人工复核”或者“需求拆解-代码实现-测试验证”都一样的逻辑。
4.1 需求拆解:为什么这三个 Agent 要拆开
我处理的原始任务很简单:给定一个主题,先生成一篇带事实依据的文章。单 Agent 的做法是让一个 Agent 既检索又写作又检查,效果我在前面已经吐槽过了。现在拆成三个:
- Researcher:负责检索资料,只输出“事实清单+来源”,不写任何成文内容。
- Writer:拿到事实清单,负责写成完整文章,不自行编造事实。
- Reviewer:检查文章是否有事实错误、逻辑漏洞、风格问题,输出修改意见或直接给修订版。
为什么这么拆?因为这三个环节的信息类型完全不一样。检索环节产生的是“外部信息”,写作环节消费的是“结构化信息”,审校环节关注的是“内容质量”。混在一个上下文里,模型要同时处理三种任务模式,指令遵循度必然下降。
4.2 定义三个 SubAgent
下面这段代码定义三个专职 Agent。核心在于 instructions 要短、边界要清晰,每个 Agent 只知道自己该干什么。
const researcher = new Agent({ name: "researcher", instructions: `你负责研究资料。给定主题后,只输出事实清单: 1. 每条事实单独一行,标明来源 2. 不要写文章、不要给建议、不要输出结论 3. 事实不明确时,标注“待核实”`, model: process.env.OPENAI_MODEL_NAME!, tools: [webSearchTool], }); const writer = new Agent({ name: "writer", instructions: `你根据研究员提供的事实清单撰写文章。 要求: 1. 只能使用事实清单中的信息,不得自行补充新事实 2. 结构清晰,段落之间有逻辑衔接 3. 面向普通读者,通俗但不失专业`, model: process.env.OPENAI_MODEL_NAME!, }); const reviewer = new Agent({ name: "reviewer", instructions: `你负责审稿。检查文章中是否存在以下问题: 1. 与事实清单不符的表述 2. 逻辑断层或前后矛盾 3. 表达冗余或口语化过度 输出修改意见列表,如果无需修改,直接输出“通过”。`, model: process.env.OPENAI_MODEL_NAME!, });这里有个细节值得展开:Researcher 的 instructions 里明确写了“不要写文章、不要给建议、不要输出结论”。这句话非常关键,它把“研究”和“写作”的边界硬性划开了。如果不写这句,模型十有八九会在研究阶段就顺手把文章写了,SubAgent 就失去了意义。
4.3 父 Agent 通过工具委派任务
有了三个 SubAgent,需要一个主管 Agent 来调度它们。在 Agent Framework 里,我用的方式是给父 Agent 挂一个“委派工具”,让模型在推理时按需调用。
const subAgents: Record<string, Agent> = { researcher, writer, reviewer, }; const managerAgent = new Agent({ name: "manager", instructions: `你是内容生产主管。任务流程如下: 1. 先用 delegate 工具调用 researcher 检索资料 2. researcher 返回事实清单后,用 delegate 调用 writer 写稿 3. writer 返回草稿后,用 delegate 调用 reviewer 审校 4. 根据 reviewer 意见决定是修改重投还是直接产出最终稿`, model: process.env.OPENAI_MODEL_NAME!, tools: [ { name: "delegate", description: "把子任务委托给指定的 subagent 执行", parameters: { type: "object", properties: { agentName: { type: "string", enum: ["researcher", "writer", "reviewer"] }, task: { type: "string" } }, required: ["agentName", "task"] }, execute: async ({ agentName, task }) => { const target = subAgents[agentName]; if (!target) return `未知 subagent: ${agentName}`; return await target.run(task); } } ] }); const finalOutput = await managerAgent.run("写一篇介绍 Microsoft Agent Framework 的科普文章"); console.log(finalOutput);这就是一个最朴素的 SubAgent 委派实现。父 Agent 通过delegate工具把子任务分发出去,SubAgent 执行完把结果返回给父 Agent,父 Agent 再决定下一步。
代码不复杂,但有几个点需要注意:
delegate工具的description要写清楚,模型会根据它决定什么时候调用工具。agentName用enum限定可选范围,避免模型生成一个不存在的 Agent 名。task字段要给足够的信息,但不要塞上下文原文,让 SubAgent 自己按指令去处理。
4.4 让子 Agent 回传结果并汇总
SubAgent 的结果默认是一段文本。父 Agent 拿到文本后,会把它作为工具执行结果放进自己的上下文中,然后继续推理。
这里有一个容易被忽略的问题:如果 SubAgent 返回的结果特别长,父 Agent 的上下文会被迅速撑大。我的经验是在 SubAgent 的 instructions 里约定输出格式和长度,比如“事实清单最多 10 条,每条不超过 50 字”。这样既保留了核心信息,又不会撑爆上下文。
汇总这一步,父 Agent 其实不需要额外做太多事。它拿着 Writer 的草稿和 Reviewer 的意见,要么直接输出最终稿,要么再把 Reviewer 的意见传给 Writer 做一轮修改。第二种情况就是“多轮 SubAgent 调用”了,本质是同一个 delegate 工具被多次调用。
4.5 实测效果与常见问题
我拿这套流程跑了一个“介绍 Agent Framework”的写作任务,和之前单 Agent 版本对比:
| 指标 | 单 Agent | 三 SubAgent 协作 |
|---|---|---|
| 事实准确性 | 出现 2 处来源不明表述 | 全部引用自检索结果 |
| 输出风格稳定性 | 前后有轻微不一致 | 稳定 |
| 单次执行时间 | 约 40 秒 | 约 75 秒(多两次模型调用) |
| 调试难度 | 黑盒,难定位 | 每个环节日志独立 |
时间变长是必然的,因为多了两次 SubAgent 调用。为了质量牺牲一些延迟是值得的,尤其是任务本身对准确性要求高的场景。
常见问题里,最让我头疼的是“SubAgent 自行发挥”。模型在扮演 Researcher 时,有时候会忍不住“顺手”给结论,直接违背 instructions。解法不是继续加长指令,而是在指令里加一个“输出格式模板”,让模型必须按模板填内容。模板比指令约束力强得多。
5. 再往上一步:Orchestrator 动态编排 vs GroupChat 固定讨论
SubAgent 解决的是“主从委派”问题,但真实业务经常需要更复杂的协作方式。Agent Framework 提供了两套更高阶的编排机制,我按实际使用场景给你拆开讲。
5.1 AgentOrchestrator 的 plan/execute 流程
AgentOrchestrator的模式是:先由规划模块产出执行计划,再按计划执行。整个过程分两个阶段,plan阶段和execute阶段。
const orchestrator = new AgentOrchestrator({ agents: [researcher, writer, reviewer], planPrompt: "根据用户目标,分配合适的 Agent 并按顺序执行。", }); const plan = await orchestrator.plan("调研 Agent Framework 最新特性并撰写摘要"); // plan 里会包含一组任务节点,每个节点指定由哪个 Agent 执行什么任务 const results = await orchestrator.execute(plan);我理解这套设计的时候,把它类比成“先写 To-do list,再逐项打勾”。plan 阶段模型会分析用户目标,分解出若干子任务,并为每个子任务匹配一个 Agent;execute 阶段则按计划依次执行。
这是和手写 delegate 工具最大的区别:手写 delegate 时,流程逻辑是写在父 Agent 的 instructions 里的,还是要靠模型自由发挥;Orchestrator 把“流程规划”变成了一个显式产物——计划。计划可以检查、可以修改、可以复用,这是工程可控性的巨大提升。
5.2 AgentGroupChat 的轮流发言与指令轮转
GroupChat 是另一套思路。它不提前规划,而是让多个 Agent 在同一对话上下文里轮流发言。
const groupChat = new AgentGroupChat({ agents: [productManager, engineer, designer], termination: (messages) => { // 当连续两轮没有新的实质内容时,结束对话 return messages.length > 6; } }); await groupChat.addUserMessage("为 AI 笔记应用设计一个新功能"); await groupChat.invoke();GroupChat 的核心是“轮转规则”和“终止条件”。默认情况下,Agent 按顺序发言,每个 Agent 都能看到前面的对话内容;终止条件需要你写清楚,否则它会一直聊下去,token 消耗是灾难性的。
我用 GroupChat 最多的是“方案评审”场景:产品经理提需求,工程师评估可行性,设计师补充体验视角,多方互相补充。这类任务没有明确的执行链,本质是“集思广益”,GroupChat 天然契合。但如果任务是一串严格依赖的执行步骤,用 GroupChat 就是在绕远路。
5.3 选型建议:什么时候用动态规划,什么时候用群聊
我把自己的选型逻辑整理成一个简单的判断标准:
- 任务步骤明确、依赖关系强(比如“先查资料-再写报告-最后审核”)→ 优先考虑手写 delegate 或 AgentOrchestrator。
- 任务步骤不明确、需要探索和权衡(比如“设计一个方案”或“技术选型讨论”)→ 用 AgentGroupChat。
- 任务依赖模型动态拆解但拆解后执行路径清晰 → 用 AgentOrchestrator,计划可审计是最大优势。
- 团队规模 3 个 Agent 以内,且流程稳定 → 手写 delegate 工具就够了,不用上框架级编排。
我不建议什么场景都堆 GroupChat。群里 Agent 一多,每轮都是全量的模型调用,成本呈指数上升,而且讨论容易原地打转。我用 GroupChat 的经验是控制在 3 到 4 个 Agent,并且严格设置终止条件。
5.4 编排层的成本与延迟控制
编排层是 Multi-Agent 开销的大头。每多一次 Agent 调用,就多一轮模型推理;每多一轮轮转,就是多倍延迟。
我的成本控制三板斧:
- 能不并行就别并行?不对,恰恰相反,能并行必须并行。如果两个子任务之间没有依赖关系(比如同时调研 A 和 B 两个方向),用
Promise.all并行跑,延迟能从“串行累加”降到“最慢的那个任务”。但要小心并行调用会同时消耗 token 配额。 - 给 SubAgent 的输出设硬性长度上限。我通常在 instructions 里加一句“回答不超过 200 字”,避免中间产物无限膨胀。
- 提前收敛终止条件。GroupChat 的终止条件要敏感一点,宁可少聊两轮也不要多聊五轮。信息冗余边际收益很低,烧 token 却是实打实的。
延迟方面要接受一个现实:Multi-Agent 必然比单 Agent 慢。这是架构换质量必然的代价。优化的思路不是让每次调用变快,而是减少不必要的调用,以及能并行时绝不串行。
6. “waiting for subagent” 是怎么发生的,以及如何不让它卡死
回到开头那个热词。Cursor 在调用 subagent 时显示 “waiting for subagent”,本质是 UI 层在等待一个异步任务完成。这个状态本身不是 bug——subagent 的工作确实需要时间——但如果你在自建的 Agent Framework 系统里看到类似“卡住不动”的迹象,那就有必要深挖一下了。
6.1 热词背后:等待子代理完成的底层机制
在 Agent Framework 里,父 Agent 调用 SubAgent 的整个链路是这样的:
- 父 Agent 推理出需要调用
delegate工具。 - 框架执行
execute函数,内部调用subAgent.run(task)。 subAgent.run()发起模型推理,等待模型返回。- 模型返回后,工具
execute返回结果给父 Agent。 - 父 Agent 把结果继续交给模型做下一轮推理。
“waiting” 就发生在第 3 步——一个模型推理调用可能要几秒到几十秒。UI 层把这个等待状态显示出来,本意是告知用户“它还在工作”。但如果你等待的时间远超预期,或者完全没有日志推进,那就说明卡住了。
6.2 常见卡死原因排查链路
我总结过几类导致“等待异常长”的原因,按排查优先级排列:
工具本身超时。SubAgent 挂了搜索、数据库查询等外部工具,外部 API 无响应,工具
execute一直 pending,SubAgent 永远得不到结果。这个是最高频的原因。上下文过大导致模型响应变慢。SubAgent 里塞了太多历史消息,模型处理时间会被拉长到不可接受的程度。
循环 handoff。Agent A 把任务交给 B,B 又传回 A,双方都没有返回最终结果,形成闭环。这种情况下日志会反复出现“A->B”“B->A”的记录,一眼就能看出来。
子 Agent 指令与任务不匹配。模型不知道该怎么处理任务,反复调用工具,每次调用都失败,然后重试,表现为“看似在等待,实际在空转”。
凭证过期或配额不足。模型 API 返回 429 或 401,但框架没有把错误显式抛出来,只是默默重试。
排查链路我个人习惯用日志驱动:先在父 Agent 和 SubAgent 的边界处打日志,确认任务有没有被分发出去、有没有返回。如果任务分发出去之后没有返回,问题在 SubAgent 内部;如果任务压根没分发,问题在父 Agent 的决策环节。
6.3 工程优化:超时、并行与降级策略
光定位问题还不够,工程上要加保护措施。我的建议是给 SubAgent 调用包一层超时控制。
function withTimeout<T>(promise: Promise<T>, ms: number, fallback: T): Promise<T> { return new Promise((resolve) => { const timer = setTimeout(() => resolve(fallback), ms); promise.then((value) => { clearTimeout(timer); resolve(value); }); }); } const result = await withTimeout( researcher.run("检索相关资料"), 30000, "检索超时,请基于已有知识作答" );Promise.race也能实现类似效果,但withTimeout这个封装的优点是它不会让底层 Promise 的报错变成 unhandledrejection。超时后给 SubAgent 返回一个降级提示,让父 Agent 能继续往下走,而不是整个流程卡死。
并行方面,如果两个 SubAgent 的任务互不依赖:
const [factList, relatedLinks] = await Promise.all([ researcher.run("检索核心事实"), researcher.run("检索相关案例"), ]);并行能显著缩短总耗时,但要留意 API 的并发限制和 token 配额。并行 5 个 SubAgent 同时跑,每秒 token 消耗可能会触顶,反而被限流。
6.4 可观测性:看到 subagent 的完整执行轨迹
这是我认为 Multi-Agent 工程化最重要、但很多人忽略的一环。单 Agent 调试靠打印还可以忍,Multi-Agent 不做好可观测性,出了问题就只能对着黑盒瞎猜。
Agent Framework 的事件系统会输出 Agent 生命周期、工具调用、token 使用等事件。我在关键节点加自定义日志:
const agentEvents = agent.events.subscribe((event) => { console.log(`[${new Date().toISOString()}] ${event.type}:`, event.data); });我重点看四类事件:
agent_run_start:确认任务是否进入了目标 SubAgent。tool_call_start/tool_call_end:确认工具调用有没有正常返回。agent_response:看到 SubAgent 的原始返回内容。error:异常信息,优先排查。
有了这套日志,再配合withTimeout的兜底,我基本告别了“waiting 到底在等什么”的玄学排查。每次运行都留档,出问题直接翻开执行轨迹看是哪一步断了,修复时间从小时级降到分钟级。
另外一个经验是:SubAgent 数量不是越多越好。我试过把内容工作流拆成 5 个 Agent,结果编排复杂度上去了,模型在“该委托给谁”这件事上频繁犯错。当前我的实践原则是:能 3 个 Agent 解决的问题,绝不用 5 个。每加一个 Agent,就多一层延迟、多一份编排失败的风险。真正该做的,是把每个 SubAgent 的指令打磨得更精准,而不是靠数量堆功能。这套“研究-写作-审校”的模板,我用到现在依然稳定,也是因为它的复杂度刚好卡在我可控的范围内。