多 Agent 协作到底靠什么?从 Codex 内部机制到 A2A 企业服务

0 阅读11分钟

源码分析基于 OpenAI Codex 提交 d1e3f9dfe3d5f6105b7e4c3958b9bf2ac6c32818,不代表所有版本或某个桌面安装包。A2A 以本文核对的 1.0.0 规范为准。图中角色和任务均为通用示例;架构建议没有经过生产或互通测试。

给几个 Agent 分别起名“架构师”“开发”“测试”,再让它们互相发消息,就能协作了吗?

真正需要回答的是:实例由谁创建?名字在哪里登记?消息发到哪里?等待期间程序在做什么?接收失败以后,谁知道任务没完成?

理解这些问题,也就能分清 Codex 的内部协作、A2A 协议和官方 SDK 各自负责什么。

1. 先分清:协议、SDK 与协作运行时

层次主要职责本文中的例子
通信协议约定独立系统交换什么信息、如何交互A2A
协议 SDK用代码实现客户端、服务端和传输适配A2A Java SDK
协作运行时创建或调用 Agent,组织上下文、消息和执行Codex 内部协作、各类编排框架

A2A 规定独立 Agent 系统之间的交互契约。Codex 的 collaboration 则是本文所分析的一套内部协作机制。“Codex 有具体实现”不等于“Codex 实现了 A2A”。 前面这条内部链路使用自身的注册与消息机制。A2A 规范Codex 协作控制源码

类似地,A2A SDK 可以替应用处理协议通信,具体任务如何完成仍要由应用实现。它并不要求内部必须采用某一种 Agent 框架。

2. Agent 地址从哪里来?运行时维护路径与实例

角色配置与运行实例要分开看。“测试角色”描述职责和配置;一次真正创建出来的测试 Agent,还需要自己的运行上下文、标识和状态。配置一个角色,不等于已经启动了一个常驻服务。Codex 子 Agent 配置与编排

在本文的源码快照中,同一根任务树共享协作控制与注册信息。调用方指定子任务名,运行时结合父路径构造完整路径,并在创建过程中登记实例。

例如:

  • 当前调用方是 /root,子任务名为 testing,得到 /root/testing
  • 当前调用方是 /root/architect,同名子任务得到 /root/architect/testing

这些是运行时中的逻辑地址,不是文件夹,也不是 HTTP 地址。注册表维护路径、实例元数据及反向查询关系。路径构造Agent 注册表

下面是据固定源码快照整理的简化逻辑图,展示创建操作涉及的对象,不展开内部执行顺序和错误分支:

Codex 创建子 Agent 时涉及的运行实例、注册信息与任务路径

所以,“架构师怎么知道测试 Agent 的地址”有具体的信息来源:创建工具的返回结果,以及查询已有实例的工具。并不是模型凭角色名称猜出一个地址。这一版本的 spawn 返回结果包含规范化后的 task_name创建工具实现

3. target 负责路由,message 提供任务内容

下面是一次消息工具调用的参数示意,假设目标实例已经存在:

{
  "target": "/root/testing",
  "message": "只读检查订单分页的边界行为,返回失败场景和依据;不要修改代码。"
}

target 让运行时找到接收者;message 是提供给接收 Agent 的内容。示例中的限制属于任务约定,实际文件和工具访问仍受运行环境约束。

消息投递成功,不代表测试已经执行。接收者需要结合上下文理解任务、请求工具执行,再根据真实结果作出判断。任务约定不能单独改变运行环境权限。

常用协作操作的区别可以这样理解:

操作作用
spawn_agent创建一个新的子 Agent
send_message向已有实例投递消息
followup_task继续分配任务,并可让空闲实例开始新的执行轮次
wait_agent等待消息或状态变化,不等于指定任务必然成功

运行中的 Agent 会在合适的处理边界接收消息;不能把“已投递”理解为“立刻打断所有正在执行的工具”。消息工具实现

事件驱动,为什么源码里仍然能看到循环?

接收循环负责反复处理事件;是否轮询,要看没有事件时怎样等待。

在这条源码链路中,操作通过异步通道交给会话处理器,消息写入 mailbox 队列,再通过 watch 通知状态变化。等待端使用异步等待和超时机制。会话事件处理邮箱与通知等待工具

下面是据固定源码快照整理的简化逻辑图,展示消息与通知的关系,不表示真实线程拓扑:

Codex 消息投递、邮箱队列与变化通知的关系

队列保存消息,通知帮助等待者知道发生了变化,两者职责不同。没有消息时,等待端不持续主动检查队列,而是等待变化通知或超时等条件。

Java 开发者可以类比 BlockingQueue.take():队列为空时等待元素,而不是循环调用 isEmpty()。但它与 Rust 的异步任务等待并非同一种线程模型,也不能因此推断“每个 Agent 对应一个独占线程”。Java BlockingQueue

4. 接收失败与执行失败,需要分别处理

“在线程里 catch 住异常”只能说明异常得到了某种处理,还要继续确认任务状态是否正确、调用方是否获知结果。

发生的情况应当确认什么
目标不存在或投递失败调用方收到错误了吗,任务是否真的交出?
接收通道关闭等待是否结束,实例是否仍可运行?
工具或业务执行失败是否形成失败结果,下一步由谁决定?
提交中断或取消执行是否已经停止,已有副作用是什么?

在本文快照中,可以看到投递错误的处理和终态通知路径;但不能据此宣称所有失败都会自动重试、所有实例都会自动重启。通知本身也不等于一套持久化、保证重投的消息系统。协作错误处理完成通知

中断同样要看实际状态:发出了中断请求,不足以证明正在进行的操作已经全部停止。中断处理

5. 换成 A2A 后,通信边界发生了变化

假设一个 Java 服务负责需求分析,另一个 Python 服务负责代码检查。双方希望交换任务和结果,同时各自保留内部实现,就进入了 A2A 解决的问题域。

下面是根据协议整理的边界示意。两个服务可以部署在同一台机器,也可以独立部署:

两个独立 Agent 系统通过 A2A 客户端与服务端交换任务和结果

A2A 的几个核心对象是:

对象用途
Agent Card描述能力、服务接口和认证要求
Message一次交流,承载文本、文件引用或结构化数据等内容
Task有标识、有状态、可跟踪的工作单元
Artifact任务产生的报告、文件或结构化结果
contextId关联一组相关交互,不等于双方共享模型上下文

发送消息后,服务端可以直接返回 Message,也可以返回 Task。对端内部用了几个 Agent、怎样选模型,属于对端的实现。A2A 核心概念任务生命周期

A2A 有没有注册中心?

A2A 标准化 Agent Card。调用方可以通过已知地址、配置、目录服务,或约定路径 /.well-known/agent-card.json 获取卡片。目录服务可以集中管理卡片,但当前规范没有规定统一的目录 API。

这与 Codex 在创建实例时登记本地路径不同。A2A 调用方仍需先知道一个域名、卡片地址或可查询的目录。Agent 发现机制

A2A 是轮询还是事件驱动?

协议支持多种更新方式:查询任务状态;在服务支持时接收流式更新;或者配置支持的 Webhook 推送。JSON-RPC 和 HTTP+JSON 绑定的流式更新使用 SSE,gRPC 则使用服务端流式 RPC。异步与流式交互gRPC 流式规范

这些方式都不规定服务内部必须使用什么线程池或队列。协议中的取消是请求取消,也不能直接推导出业务已回滚。取消语义

6. 放在一张表里,区别更清楚

维度Codex 本文分析的内部协作A2A
面向对象同一根任务树中的实例独立 Agent 系统
定位方式任务路径及实例注册信息Agent Card 中的服务接口
工作表达协作工具参数、任务上下文标准化消息、任务和产物
运行责任Codex 的运行时与执行配置各服务自行实现
更新机制异步通道、邮箱、变化通知查询、流式或推送
互通条件遵守该运行时的内部机制双方支持兼容的协议与能力

因此,不能给 Codex 套上一个 HTTP 接口,就认为它已经支持 A2A。若做适配,还需要处理消息转换、任务状态、结果与错误映射,以及身份和权限边界。这是集成设计要求,本文没有实现这样的适配器。

同时,内部协作使用本地机制,也不意味着 Codex 只能通过界面使用。它另有 SDK 和 App Server 等程序化集成入口。Codex SDKApp Server

7. 官方 Java SDK 能用于企业服务吗?

可以。官方 A2A Java SDK 已提供客户端和服务端支持,包含 JSON-RPC、gRPC、REST 传输,要求 Java 17+,采用 Apache-2.0 许可。协议规范版本与 SDK 发布版本需要分别核对。A2A Java SDK

一个初期企业 Agent 服务可以采用下面的建议架构。它不是 SDK 内部类图,也不表示图中能力安装依赖后就自动就绪:

企业 A2A 服务的接入、执行、任务状态和可观测性建议架构

SDK 负责协议接入。应用负责定义能力、执行业务和产生结果;生产运行还需要结合已有设施解决:

  • 身份与资源权限:认证调用者,并校验其对具体任务、数据和动作的权限。
  • 任务状态与恢复:保存必要状态,明确服务重启、执行超时和多实例之间如何协调。
  • 执行与重试边界:限制并发和等待时间;有写入副作用的业务必须考虑幂等,避免重试造成重复操作。
  • 运行证据:关联任务标识、业务请求和追踪信息,能够区分“请求已收到”“任务已执行”“结果已确认”。

这些是企业接入职责,不是 A2A 单独替应用完成的功能。官方企业指南也强调与既有身份、授权、网关和可观测性体系结合。A2A 企业接入指南

例如,一个“文档分析 Agent”可以先由普通 Java 代码接收任务、读取调用者有权访问的文档、调用模型并返回报告。只有内部步骤和角色变复杂时,才需要进一步评估编排框架。

8. 还有哪些开源实现值得看?

以下是不同侧重点的源码入口,不是性能或成熟度排名:

项目适合观察的实现许可
Deep Agents子 Agent 委派、上下文隔离、文件和工具能力组合MIT
Google ADK JavaJava Agent 层级与执行编排,可接入 A2A;与协议层的 A2A Java SDK 是不同项目Apache-2.0
LangGraph有状态执行图、检查点、持久化与人工介入MIT
CrewAI角色、任务、协作团队与事件流程MIT
Microsoft Agent Framework顺序、并发、任务移交和群组编排MIT

这些是可复用的实现,但没有义务采用 Codex 相同的注册表或邮箱模型。Microsoft 官方把 Agent Framework 定位为 AutoGen 与 Semantic Kernel 的直接继任者,阅读旧教程时应注意项目演进。官方定位

另两个常见协议也需要放对位置:MCP 主要连接 AI 应用与工具、资源;Agent Client Protocol 主要连接客户端或编辑器与编码 Agent。MCP 服务能力Agent Client Protocol

对 Java 开发者,一条便于理解的阅读路线是:先通过 ADK Java 看任务如何在服务内部执行,再通过 A2A Java SDK 看这些能力如何对外提供。若重点研究子 Agent 的上下文隔离与委派,可以补看 Deep Agents。

建设系统时也可以采用同样的分层:先确定任务如何可靠完成,再确定哪些能力需要跨服务互通。内部编排与对外协议各自清楚,后续更换模型、框架或部署方式时,边界才容易保持稳定。