

从 Agent-as-Tool 到 Agent Team:为什么多个 Agent 仍然需要人来协调?
从 smolagents 的委派源码出发,讨论多 Agent 协作中的共同决策、变更影响与交付有效性,以及为什么不必直接照搬人类团队。
多个 Agent 能够各自正确地完成任务,却未必能够共同交付一个正确的结果。
研究对象: Hugging Face / smolagents v1.26.0
研究方向: Agent-as-Tool · Multi-Agent Collaboration · Decision Consistency
系列: Agent 源码研究 · Part 03
TL;DR: smolagents 能把任务交给子 Agent,也能并行处理同一轮的多个调用。但在持续协作中,真正麻烦的往往不是任务怎么分出去,而是任务开始以后,决定又变了。共享聊天记录并不自动形成共同决策;通知成员也不等于调整了已经开始的工作;收齐报告,更不等于完成了当前交付。这篇从委派机制的源码出发,讨论多 Agent 协作究竟需要维持什么,并在最后追问:我们为什么一定要把 Agent 组织成人类式的 Team?
写在前面:多了几个 Agent,为什么我还在做交接?#
我最初用 OpenBot 组织多个 Agent,主要是想减少开发中的等待:与 Codex 讨论方案和实现,再让 Claude 做独立 Review;相互独立的事情,尽量同时推进。
但工作分出去之后,我依然经常要解释:这项功能为什么这样设计?为什么放弃了另一个方案?Reviewer 这次到底该按什么标准检查?尤其是当我能直接进入某个成员的会话讨论时,一个问题变得很明显:我在这里改了决定,为什么还要去别的会话再说一遍?
假设我们在开发 Web Search。初始要求是:搜索任务超过 30 秒,返回超时失败。Implementer 负责实现,Tester 负责测试,Reviewer 负责审查。
后来,我和 Implementer 讨论发现:如果多个检索来源中只有一个超时,其他来源已经返回了有价值的结果,直接丢弃所有结果可能不合适。于是确认新原则:允许返回部分结果,但必须明确标识搜索不完整。
Implementer 开始按新原则修改,Tester 却还在按旧规则写断言,Reviewer 也可能在审旧设计下的提交。各自的工作未必马虎,放到一起却可能互相冲突。
这个情境只是示意,但它准确表达了我遇到的困惑:各个 Agent 都在认真做事,不代表它们在做同一件事。
最初我以为这是上下文不够共享。顺着 smolagents 的源码往下看后,我开始怀疑,真正缺少的是对共同决策及其变化的管理。
一、从源码看,一次委派解决了什么?#
1. Managed Agent:把执行者接到调用接口上#
在 agents.py 的 _setup_managed_agents() 中,框架按名称注册子 Agent,并为已有实例设置工具调用所需的输入输出元数据。省略描述字段,结构如下:
agent.inputs = {
"task": {"type": "string"},
"additional_args": {"type": "object", "nullable": True},
}
agent.output_type = "string"python子 Agent 没有因此变成普通函数。它仍有自己的模型、工具和执行循环。Manager 需要知道能找谁、交付什么输入、收到什么形式的结果;至于具体怎样完成任务,由子 Agent 自己组织。
这是一种清楚的分工。但 output_type = "string" 只规定返回类型,并不保证报告包含它依据的需求、检查的提交或覆盖的验收范围。这些可以由应用层另行约定,却不是接口自动提供的保证。
2. 统一调用,不等于共享上下文#
对 ToolCallingAgent 而言,普通 Tool 和 Managed Agent 可以进入同一份可调用能力列表。execute_tool_call() 找到对象、替换状态变量并验证参数后,在字典参数分支中这样分流:
return tool(**arguments) if is_managed_agent else tool(**arguments, sanitize_inputs_outputs=True)pythonManaged Agent 进入自己的 __call__();MultiStepAgent.__call__() 再以任务模板包装输入,调用自己的 run(),最后将输出包装为报告。打开 provide_run_summary 还可以附上一部分由运行记录转换的消息,但这不等于两个 Agent 共享一段持续同步的会话。
在 run() 中还有一个容易误读的细节:
if reset:
self.memory.reset()
self.monitor.reset()pythonAgentMemory.reset() 清除历史步骤,保留系统提示;该分支并不清空 self.state。而调用方传入的 additional_args 可以更新 State。因此,复用同一实例时,历史步骤重置与状态变量保留可以同时发生。
但无论信息保存在 Memory、State,还是外部文档里,能够读到一份信息,都不等于知道它是否是当前有效的决定。 这已经不是单纯的存储问题。
3. 并发调用,不等于任务协调#
process_tool_calls() 可以把同一轮的多个调用送入线程池,并按完成顺序收取结果:
for future in as_completed(futures):
tool_output = future.result()
outputs[tool_output.id] = tool_output
yield tool_outputpython调用 ID 让框架能够把结果对应回请求。查接口用法和查测试覆盖可以并行;但「Reviewer 必须等实现提交后才能开始」「审查中途验收标准变了怎么办」,不是线程池该回答的问题。
并发解决已经决定要做的调用怎样一起跑;协调解决哪些工作应当开始、依据什么执行,以及决定变化后怎么办。
到这里,smolagents 展示的是一套明确的委派机制,而不是一个自动维护项目决策一致性的协议。这个判断针对上述接口与执行路径,不意味着用 Agent-as-Tool 搭建的上层系统不能实现这些能力。
二、共享聊天记录,为什么仍然不够?#
1. 看见同一段聊天,为什么仍然无法确定要求?#
回到搜索策略的讨论。聊天里可能先后出现「超时就失败」「是否保留部分结果」「部分结果会不会误导调用方」「确认允许部分结果,但必须标记不完整」。
把这些话全部交给 Reviewer,确实保留了来龙去脉。但 Reviewer 仍需要判断:哪些只是提议,哪些被否定,哪项要求真正生效。最后出现的一句话,也未必拥有最高的决策权。
2. 会话、决策和任务,分别应该记录什么?#
因此,我更倾向于把三类信息分开:会话保留讨论与理由;决策记录保留已经确认的取舍;任务记录把决定落实为成员可以执行和验收的约束。
这也是我整理 Repo Harness 时最想解决的事。成员不必共享所有聊天记录,却需要知道与自己任务有关的有效要求;想追究「为什么」时,再回头看讨论。
3. 谁能够改变团队的共同决定?#
不过,把决定写进文档还只是第一步。谁有权改变它?
Implementer 可以自主调整不改变外部契约的实现细节,也应该能够提出新方案。但如果它打算改变 API 的返回语义,影响了测试、审查乃至调用方,就不该仅凭一个成员的局部判断悄悄替换团队标准。
不同系统可以赋予 Agent 不同的自主决策权。关键并非凡事都要人工批准,而是要明确:什么属于局部执行选择,什么属于影响共同约束的决定,以及后者怎样确认和生效。
共享上下文解决的是信息可见性;决策一致性还需要解决信息的效力与权限。
三、决定生效之后,为什么通知仍然不够?#
1. 需要通知的是所有 Agent,还是受影响的任务?#
假设新的 Web Search 策略已经确认。协调层向相关成员发出通知,是否就完成了协作更新?
未必。对 Implementer,新要求涉及返回结构;对 Tester,它改变部分断言;对 Reviewer,它改变验收依据。一个处理无关功能的 Agent 则不应被打断。协调层首先需要识别哪些任务依赖了被改变的决定,而不是机械地向所有成员广播。
2. 通知已经送达,正在执行的工作怎么办?#
接下来还有时间差。任务可能尚未开始、正在执行,或已经交付。尚未开始的任务可以更新要求;正在执行的任务可能需要调整或重启,但能否中断还取决于运行环境;已完成的任务则要判断其结果是否仍然适用。
3. 局部完成,为什么不等于整体交付?#
这时最容易犯的错误,是把多个局部完成状态直接相加:Implementer 已提交,Tester 已测试,Reviewer 报告 PASS,于是整个项目也被标成完成。可如果三份结果依据的是不同的要求或代码,就不能仅靠这些状态证明当前交付有效。
4. 旧结果还能用吗?#
我更愿意让报告至少能回答三个问题:
- 决策依据: 它按照哪些要求执行?
- 交付对象: 它针对哪份代码或其他产物?
- 验证范围: 它实际证明了哪些行为?
例如,旧测试证明「所有来源都正常时能返回结果」,如果相关代码和条件未变,这份证据仍可能有价值;但「任何来源超时都必须整体失败」若已被新决定推翻,就不能继续充当当前方案的通过标准。新增加的「部分结果必须标记不完整」,则需要补验。
这也不是说每次改需求都要推倒重来。恰恰相反,只有知道旧结果究竟证明了什么,才可能既不误用它,也不浪费它。
如果要求和代码还在并发变化,最终写入验收状态时也必须确认依据没有过期。这需要上层协调系统设计版本条件与状态更新规则,不能从 smolagents 的一次工具调用中直接得到。
所以,真正需要管理的不是消息有没有送达,也不只是哪个 Agent 最后说了 PASS,而是有效决策、执行中的任务与交付证据之间的关系。
四、Team 的价值,是否就在于维持这些关系?#
现在我更愿意从三个问题理解持续协作。
1. 共同决策依据:什么决定有效?#
成员可以有不同上下文,但必须能取得与自身任务相关的共同约束。
这并不要求 Coordinator 成为全部知识的主人。项目决策和任务状态可以由持久化的 Repo Harness 等机制维护,协调层围绕这些记录组织执行,Agent 则保留分析和实施的自主性。
2. 变更影响管理:哪些工作需要调整?#
一项决定改变后,协调层需要分析任务依赖、执行进度和影响范围,而不是只把新消息转发给成员。
3. 交付有效性验证:哪些结果仍可接受?#
收齐报告后,还需要判断报告依据的要求、对应的交付物和验证范围,是否仍能支持当前目标。
4. 什么时候值得引入这些机制?#
也没有必要因此把 Agent-as-Tool 和 Agent Team 排成低级与高级两档。以 Agent-as-Tool 为执行方式的系统,同样可以在上层加入状态与决策管理;一个拥有多个长期会话、成员通信和统一面板的 Team 产品,也未必自动解决了一致性问题。
对于一次独立的代码查询,委派后拿报告就足够。只有当项目持续推进、任务彼此依赖、决定还会在执行中改变,额外的协调机制才可能值得。否则,复杂的记录、同步与复核流程也可能只是把人工交接换成机器流程。
这些机制给 Team Bot 类产品留下了一个可检验的假设:明确记录有效决定、追踪其对任务的影响,并核对结果的适用范围,能否在要求变化频繁的协作中,减少人的重复转述和返工?
要验证它,不能只看 Agent 数量或最终是否完成任务。还应比较人工介入次数、遗漏的受影响任务、误用旧证据的次数,以及协调机制自身的时间与执行成本。现在它仍是设计假设,而不是经过实验确认的结论。
五、写在最后:我们为什么一定要模仿人类团队?#
从一次源码中的委派往外看,我原本想找到一个让多 Agent 更高效的办法。最后却发现,真正拖慢协作的不总是执行速度,而是改变决定之后,谁来让分散的工作重新对齐。
多个 Agent 不必知道同样多的事,却需要在相互依赖的工作上遵守相容且有效的约束。 这或许比让每个成员复制同一段聊天历史更重要。
但写到这里,我又开始怀疑文章一直默认的前提:我们为什么如此自然地把 Agent 组织成 Manager、Implementer、Tester、Reviewer,再让它们像人类团队一样分工、沟通和交接?
这套组织方式并非毫无道理。它容易理解,方便划定责任,也适合人随时介入。但 Agent 不是人:它们可以按需实例化,读取结构化状态,通过工具交换结果,也不一定需要一段长期成员会话来维持协作。
或许用户面对的仍然可以是一个「团队」,底层却不必照搬人类开会、转述与交接的方式。协作也可能围绕任务、状态与约束动态形成,而不是始终围绕固定角色运转。
当然,结构化状态不会自动消除模型对需求的不同理解;动态分工也不会自动解决并发修改、执行副作用或责任归属。抛弃「团队」这个外壳,并不意味着这些难题会消失。
所以我不想在这里直接宣布一种新范式。至少这篇文章还没有提供足够的比较与实验依据。
但在努力解决 Agent Team 的协作问题之前,也许值得先问一句:
Agent Team 究竟是适合 Agent 的组织方式,还是我们借用人类团队经验,为一种尚在形成中的协作范式搭建的过渡形态?
参考资料#
- Hugging Face · smolagents 官方仓库 ↗
- Hugging Face · smolagents 官方文档 ↗
- Hugging Face · Multi-Agent Systems 示例 ↗
- Anthropic · Claude Code Agent Teams 文档 ↗
注:源码部分针对 smolagents v1.26.0,代码片段经过节选;Web Search 案例是用于说明问题的假设情境,不代表一次真实实验记录。关于决策管理、协调层和结果有效性的内容是分析与待验证的设计假设,不代表上述框架或产品已完整实现这些机制。