Code Agent 解剖(17):AgentTeams——消息怎么在 agent 之间传递?

上一篇留下的问题

上一篇讲了 AgentTeams 的设计动机:多个 agent 并行协作,协调者负责分发任务和收集结果。

但有一个细节没有展开:协调者怎么知道某个成员 agent 已经收到并处理了它发出的任务消息?

这个问题在单 agent 里根本不存在。主 agent 调用一个工具,工具执行完就返回结果,一来一去,没有"消息在路上"的状态。

但在多 agent 系统里,情况完全不同。消息发出去之后,可能发生很多事:

  • 成员 agent 还没启动,消息堆在队列里
  • 成员 agent 收到了,但还在处理
  • 成员 agent 处理完了,但结果还没被协调者读取
  • 成员 agent 崩了,消息永远不会被处理

如果不对这些状态进行追踪,协调者就不知道该等还是该重发,整个协作就乱了。

这就是 消息 ACK(Acknowledgement,确认应答)机制存在的原因。


结论先说

AgentTeams 的消息协议围绕三个核心概念设计:

概念 作用
SendMessage 工具 agent 之间发送消息的唯一入口
ACK 三态 追踪每条消息的生命周期:pending → delivered → processed
TeamStatus 工具 查询团队和消息的当前状态

三态状态机保证了协调者随时能知道"我发出的消息,现在到哪一步了"。


一、为什么需要 ACK?

先用一个生活类比解释 ACK 是什么。

想象你是一个项目经理,你给三个程序员分配了任务,用微信发了消息。你怎么知道任务被接受并开始执行了?

  • 如果对方只是看到了你的消息,那叫"已读"
  • 如果对方回复确认了,那叫"已确认接收"(delivered)
  • 如果对方完成了任务并汇报,那才叫"已处理完毕"(processed)

如果对方既没有已读也没有回复,你需要判断:是没看到?是看到了没回复?还是手机没电关机了?

ACK 机制就是把这些模糊的中间状态变成确定的、可查询的状态。

在 AgentTeams 里,每条消息都有这三个状态:

scss 复制代码
pending
  │  (消息已发出,等待成员 agent 接收)
  ▼
delivered
  │  (成员 agent 已接收,正在处理)
  ▼
processed
   (成员 agent 已处理完毕,结果可供读取)

二、SendMessage:发消息的唯一入口

在 AgentTeams 里,agent 之间发消息只能通过 SendMessage 工具。

bash 复制代码
协调者 agent 调用 SendMessage:
  - to: "member_agent_2"           # 收件人
  - content: "请重构 utils.py"      # 消息内容
  - message_type: "task"           # 消息类型

SendMessage 不是简单地把字符串传过去。它做了几件事:

1. 把消息写入持久化存储

消息不是直接发送到对方 agent 的内存里,而是先写到一个持久化的"消息队列"中。这样即使成员 agent 还没启动,消息也不会丢失。

2. 给消息分配一个唯一 ID 并设置初始状态

每条消息创建时,状态是 pending,并有一个时间戳记录"何时发出"。

3. 返回消息 ID 给协调者

协调者拿到消息 ID 之后,可以用 TeamStatus 来查询这条消息后续的状态变化。

这个设计的好处是:发消息和处理消息是解耦的。协调者发完消息就可以去做别的事,不需要一直等着。


三、ACK 三态的状态转移逻辑

接下来看每个状态是怎么被触发的。

pending → delivered

当成员 agent 的"收件箱"被检查、消息被取出时,状态变为 delivered

markdown 复制代码
成员 agent 启动后:
1. 检查自己的"收件箱"(从消息队列取未处理消息)
2. 取到消息,立刻把状态从 pending 改为 delivered
3. 开始处理消息内容

这个设计的关键点:状态变更发生在"开始处理"之前,而不是"处理完成之后"。

为什么?因为如果等处理完再改状态,协调者无法区分"成员还没收到"和"成员收到了但还在处理"这两种情况------它们对协调者来说都表现为"消息还是 pending"。

把状态变更提前到"取到消息"的那一刻,协调者就能知道:pending = 消息还没被取到;delivered = 已经在处理了。

delivered → processed

当成员 agent 处理完消息、把结果写入输出区时,状态变为 processed

markdown 复制代码
成员 agent 处理完成后:
1. 把处理结果写到指定位置(协调者可以读取的地方)
2. 把消息状态从 delivered 改为 processed
3. 通知协调者(或等协调者主动来查)

processed 状态意味着:结果已经准备好,协调者可以来取了。


四、协调者怎么知道消息处理完了

协调者有两种方式知道消息被处理完了:

方式 1:主动轮询(polling)

协调者定期调用 TeamStatus,查询所有消息的当前状态:

ini 复制代码
协调者:
while True:
    status = TeamStatus(team_id)
    if all(msg.status == "processed" for msg in status.messages):
        break  # 所有消息都处理完了,可以 Collect 了
    wait(一段时间)
    继续做其他事...

这是最简单的方式,但有轮询延迟------从最后一条消息变成 processed,到协调者发现这个变化,中间有等待时间。

方式 2:TeamCollect 阻塞等待

TeamCollect 工具可以配置等待策略,在所有消息变为 processed 之前保持等待,然后一次性返回所有结果。

ini 复制代码
协调者:
TeamFanout(tasks=[任务1, 任务2, 任务3])  # 分发任务
results = TeamCollect(timeout=300)        # 等待所有结果,最多等5分钟

这是更推荐的方式,协调者不需要自己写轮询循环。


五、消息状态的完整生命周期

把上面的内容串起来,一条消息从发出到被消费的完整过程是这样的:

csharp 复制代码
协调者发出消息
    │
    ▼
[pending]
消息写入持久化存储
协调者继续做其他事...
    │
    ▼
成员 agent 启动/激活,取到消息
[delivered]
成员 agent 开始执行任务
    │
    ▼
成员 agent 完成任务,写入结果
[processed]
结果等待协调者读取
    │
    ▼
协调者调用 TeamCollect,读取结果
消息生命周期结束

每个状态转移都有明确的触发条件和时间戳记录,所以出了问题,协调者可以通过 TeamStatus 精确定位"卡在哪里了"。


六、边界情况:消息丢失和成员崩溃

到目前为止,我们描述的是"一切顺利"的情况。实际系统里有各种边界情况,AGentTeams 是怎么处理的?

情况 1:成员 agent 取到消息后崩溃

消息状态停留在 delivered,但成员已经挂了。协调者会看到消息一直处于 delivered 状态,超过超时时间之后,可以选择将它重新变为 pending,等待重试。

情况 2:消息发出后成员 agent 一直没启动

消息状态停留在 pending。如果超过了超时时间,协调者可以判定这个成员"不可达",选择换一个成员来处理,或者直接返回部分结果。

情况 3:协调者自己崩溃了

因为消息状态写在持久化存储里,协调者重启后可以重新读取所有消息的状态,继续从上次断掉的地方继续。这是持久化设计的核心价值。


设计亮点

1. 消息持久化,不依赖内存

消息不是靠 Python 对象传递,而是写到持久化存储里。这样任意一方崩溃重启,消息不会丢失。

2. 三态而不是二态

很多简单的消息系统只有"已发送/已完成"两个状态。AgentTeams 多出了 delivered 这个中间状态,让协调者能区分"消息还没被看到"和"消息在处理中",极大地减少了协调者的盲猜。

3. 状态机而非信号

不用"事件通知"(如回调、信号),而是用"可查询的状态"。协调者随时可以主动查状态,不依赖"对方会主动通知我"这个假设。在分布式系统里,主动拉取(pull)比被动通知(push)更可靠。


小结

设计选择 方案 工程价值
消息传递 SendMessage 工具,消息持久化 发消息和处理消息解耦,支持崩溃恢复
状态追踪 ACK 三态(pending/delivered/processed) 协调者随时知道消息到了哪一步
结果收集 TeamCollect 阻塞等待 协调者不需要自己写轮询循环
故障处理 超时 + 状态回退 成员崩溃后可以重新分配任务

下一篇讲 TeamFanout 和 TeamCollect------任务是怎么被高效分发出去、结果又是怎么被汇总回来的。


关于本系列的源码

本系列所有分析均基于开源项目 MyCodeAgent

AgentTeams 的实现已从稳定版本移除,但设计原理在 docs/research-archive.md 和移除计划文档 docs/plans/2026-07-12-lean-runtime/tasks/M2-03-remove-agent-teams.md 中有完整记录。

bash 复制代码
git clone https://github.com/chendongqi/MyCodeAgent
cd MyCodeAgent
cp .env.example .env
uv sync
uv run python main.py

欢迎访问 PrimeSkills ------ 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。

更多实用知识和有趣产品,欢迎访问我的个人主页

相关推荐
冬奇Lab12 分钟前
一天一个开源项目(第205篇):PenguinHarness - 让 AI 来构建 AI
人工智能·开源·资讯
小鹿的周先生18 分钟前
Spring-AI-第2篇-ChatClient 实战:使用 DeepSeek 完成第一次 AI 对话
java·人工智能·spring
ajassi200032 分钟前
AI语音智能体开发日记(十五)智能体LCD屏幕GIF动画显示方案——从GIF到BMP的完整实战
人工智能·ai·ai编程
Dfreedom.42 分钟前
目标检测后处理核心:NMS非极大值抑制详解
图像处理·人工智能·深度学习·目标检测·目标跟踪
枫叶丹444 分钟前
实时语音 Agent:从语音机器人到连续协作界面
人工智能·chatgpt·机器人·语音识别·agent·codex
戴西软件1 小时前
戴西iDWS.3DViz Suite数据轻量化可视化软件,从传统桌面软件向云端协同的重大突破
大数据·运维·网络·人工智能·机器学习·3d
AIkk861 小时前
2026年AI证件照工具功能速览:三款实用方案对比
人工智能
ReleaseU1 小时前
Harness + MCP:打通企业工具链的最后一步
人工智能·大模型