
最近在折腾 Agent-Reach 这套 Agent 互联协议说实话折腾完以后我是有点兴奋的。它解决了一个我一直觉得非常别扭的问题AI Agent 越来越多但每个 Agent 都是个孤岛很难让它们互相协作去完成一个更复杂的任务。Agent-Reach 不是某个具体的应用而是定义了一套Agent 之间怎么找到对方、怎么确认身份、怎么互相调用能力的标准通信方式。简单说它像是给这些 AI 智能体建了一套通用语言和通讯录。我用它在本地快速搭起了一个客服助手 订单查询 库存查询三个 Agent 互相调用的试验环境跑通以后整个体验可以用一个词形容顺畅。本文不是官方文档的翻译而是我作为一个实际使用者的完整复盘从设计思路、核心拆解到具体配置步骤、踩坑排查都会讲到。无论你是正在做多 Agent 应用的开发者还是对 Agent 协作机制感兴趣的架构师这篇文章应该都能给你省下不少试错时间。1. Agent-Reach 整体设计与思路拆解1.1 为什么需要一套独立的 Agent 互联协议先聊一个最基础的问题市面上已经有 HTTP、gRPC、WebSocket 这些通信方式Agent 之间为什么还需要一套专门的协议答案在于HTTP 这类通用协议解决的是字节怎么传但完全没有解决对方是谁、能干什么、怎么调用、结果怎么理解这些更高层次的问题。如果你用 REST API 暴露一个 Agent 的能力调用方需要自己硬编码理解每个接口的路径、参数和返回结构这恰恰是把 Agent 之间动态互相理解和调用这条路堵死了。Agent-Reach 做的就是把元数据发现、能力描述、调用契约、会话上下文这些 Agent 协作需要的基础设施统一收敛到协议层。它底层传输仍然可以跑在 HTTP/2 或者 QUIC 之上但应用语义是全新的。有了这套协议一个 Agent 只需要知道对方的 Reach 地址就能自动获取对方的能力清单生成调用参数完成请求然后拿到结构化的结果。有人可能会问这个思路跟 MCPModel Context Protocol、ACLAgent Communication Language、A2A 这些有没有重叠说实话概念上有交叉但 Agent-Reach 的切入角度更偏运行时连接——它不关心模型内部怎么推理只关心两个运行中的 Agent 进程怎么建立可靠、安全、可验证的连接。你可以把它理解成是 Agent 世界的TCP/IP DNS而非编程语言。1.2 连接模型寻址、能力注册、会话隔离Agent-Reach 的核心连接模型可以拆成三段寻址Addressing、能力注册Capability Registration、会话隔离Session Isolation。先说寻址。每个 Agent 在启动时会绑定一个唯一标识Reach ID一般是 UUID 或基于公钥生成的地址。这个 ID 是全局唯一的且不随网络位置变化。这样做的好处显而易见Agent 从一个服务器迁移到另一个服务器Reach ID 不变其他 Agent 不需要更新任何配置就能继续访问它。加上一个 Reach Registry类似 DNS你可以把 ID 映射到当前实际地址ip:port 或 URL。再说能力注册。Agent 启动后会把自己能提供的操作比如 查询订单、获取库存以标准 Schema 格式注册到本地 Reach Service 上。协议规定了一套 JSON-Schema 风格的参数定义规范所有 Agent 都必须遵循。这样调用方可以在运行时动态拿到被调用方的能力字典。最后是会话隔离。Agent-Reach 引入了 Session 概念每次调用都要指定一个 Session ID同一个 Session 内的多个请求可以共享状态。但不同 Session 之间的上下文是完全隔离的——这非常重要避免了一个 Agent 的对话历史或状态污染到另一个用户或另一个任务。1.3 与主流方案的选型对比很多朋友第一次看到 Agent-Reach 时都会拿它跟已经有一些社区基础的协议做对比。我也整理了一张表比较直观维度Agent-ReachMCP偏工具调用A2A偏 Agent 间协作定位Agent 到 Agent 的完整运行时连接模型到工具/资源的标准化访问Agent 到 Agent 的协作框架寻址Reach ID Registry无全局寻址URL 级别的端点寻址能力发现内置 Capability Registry通过工具/资源列表Agent Card 描述会话状态内建 Session 隔离与恢复无标准会话模型有 Task 状态管理传输层HTTP/2、QUIC可替换HTTPSSE/Streamable HTTPHTTPJSON从表里能看出来Agent-Reach 其实是一种轻量但完备的定位它把会话和寻址作为一等公民这两个能力对多 Agent 生产级协作至关重要但恰恰是很多协议忽略的。如果你只想让大模型调用几个函数MCP 确实够用但如果你希望两个 Agent 互相配合完成一段长流程任务Agent-Reach 这种带会话和寻址的协议会顺手很多。2. 核心细节解析与实操要点2.1 寻址注册与身份验证Agent-Reach 的寻址注册不是简单地把 ID 映射到 IP它实际上是ID 签名 地址绑定的过程。每个 Agent 在启动时生成一个 Ed25519 密钥对。Reach ID 就是从公钥派生出来的通常是公钥的 SHA-256 哈希再做 Base58 编码类似区块链地址的生成逻辑。注册时Agent 用自己的私钥对{ID, IP, Port, 时间戳}做签名然后发给 Registry。Registry 验签通过后才把地址绑定关系记录下来。这一套防的是地址投毒——如果有人想把自己伪造成另一个 Agent他没有对应私钥就无法生成合法签名注册就会被拒绝。在实操层面这个设计让我感觉非常省心。以前用中心化注册中心时最怕拿到一个假地址然后数据被劫持。现在注册这一关就把伪造可能堵死了后面通信即使被中间人截获对方也无法伪造合法的握手消息。注意如果你在公网环境部署不要把 Registry 端口直接暴露给全网最好加上网络层访问控制或者只允许内网注册。签名验证解决的是冒充但解决不了恶意注册大量合法 Agent的资源耗尽问题网络层该挡还是要挡。2.2 握手协议的三个关键阶段Agent-Reach 的握手流程我拆出来看其实是三个阶段的串行推进阶段一Hello。调用方发送自己的 Reach ID 和临时公钥Ephemeral Key给被调用方。被调用方返回自己的 Reach ID 与临时公钥。这个阶段主要解决双方确认对方在场。阶段二Auth。双方用临时公钥做 ECDH椭圆曲线迪菲-赫尔曼密钥交换协商出一个会话密钥然后用这个密钥加密传输各自的身份认证信息比如注册时用的签名。这个阶段的关键在于后续所有流量都基于这个会话密钥加密不依赖最初的 Registry 是否可信。阶段三Capability List。认证通过后被调用方返回自己的能力清单。调用方拿到清单后就可以针对性地构造调用请求。实测下来完整握手在本地回环网络上耗时约 8 毫秒即便加上 TLS 和密钥交换计算开销仍然很低。但如果走公网建议启用 TCP Keepalive 或长连接复用否则每个请求都从零握手累积起来延迟和 CPU 开销都会明显上涨。2.3 能力描述与调用参数的 Schema 约定Agent-Reach 的能力描述采用 JSON-Schema draft-07 的子集外加几个约定字段。每次注册能力时需要声明name、description、input_schema、output_schema和一个可选的timeout_hint。timeout_hint这个字段特别重要。因为 Agent 的执行时间可能差异很大查询类操作可能 200 毫秒就返回但生成图片可能需要好几秒。如果调用方不知道对方预期的超时时间就只能用固定的全局超时——设短了吧长任务被误杀设长了吧调用方线程被卡死。Agent-Reach 允许被调用方声明建议超时调用方可以据此动态调整请求超时设置这个设计在真实场景里非常实用。调用参数就是按input_schema生成的 JSON 对象返回值按output_schema规范化。这里有个容易踩的坑Schema 只约定了结构没有约定数值的上下限和单位。比如temperature字段到底是摄氏还是华氏范围是 0-1 还是 0-100这类语义约束必须写进description否则对方 Agent 收到参数后完全可能传一个违反物理规则的数值。2.4 会话生命周期与状态保持会话Session在 Agent-Reach 中是显式的资源。发起方通过reach.session.create创建一个 session得到一个 ID。之后同一 task 的多次调用都在这个 session 内执行共享中间状态。我测试过一个场景用户问帮我查一下过去三天的订单量客服 Agent 先调用订单 Agent 查询数据再把数据传给分析 Agent 做趋势计算。如果三次调用各自用独立 session每次都要重新传递上下文效率非常低。而使用同一个 session订单 Agent 会把临时结果留在会话缓存中分析 Agent 可以直接取用省去重复传输的开销。Session 还有一个重要能力——恢复。如果一个 Agent 在会话中途崩溃并重启只要它用相同的 session ID 重新连接并且之前有持久化会话状态就能从断点继续执行而不是整个流程重来。这在稳态生产环境中特别有价值尤其是那些执行时间超过几分钟的复杂任务。3. 实操过程与核心环节实现3.1 环境准备与安装细节Agent-Reach 目前在 Python 生态里支持得最好。我使用的环境是 Python 3.10安装就一条命令pip install agent-reach这套库依赖的核心包包括pydantic做 Schema 校验、cryptography做 Ed25519 和 ECDH、httpx负责 HTTP/2 通信。如果你想走 QUIC 传输还需要额外安装aioquic但现阶段不是必须。安装完成后建议先跑一遍自带的自检命令agent-reach --selfcheck这个命令会检查本机的可用性包括 Reach ID 是否能正常生成、本地 Registry 能否启动、以及密钥交换函数是否正常。我当时运行就发现 cryptography 版本不兼容的问题提示要用 41.0 以上版本升级后问题解决。3.2 声明一个 Agent配置文件写法Agent 的配置格式是 YAML。我创建了三个 Agent 的配置文件order_agent.yaml、inventory_agent.yaml、assistant_agent.yaml。以订单 Agent 为例reach: id: ord-7f2a9c1e4b8d registry: localhost:8355 listen: host: 0.0.0.0 port: 9351 transport: http2 capabilities: - name: query_order description: 按订单号查询订单状态返回订单的基本信息、金额和物流状态。订单号格式为 13 位数字。 input_schema: type: object properties: order_id: type: string description: 13位数字订单号 required: [order_id] output_schema: type: object properties: order_id: { type: string } status: { type: string, enum: [pending, paid, shipped, completed, cancelled] } amount: { type: number } logistics: { type: string } - name: query_orders_by_date description: 按日期范围查询订单列表返回订单号列表及总金额。日期格式为 YYYY-MM-DD。 input_schema: type: object properties: start_date: { type: string, format: date } end_date: { type: string, format: date } required: [start_date, end_date] output_schema: type: object properties: total_count: { type: integer } total_amount: { type: number } order_ids: { type: array, items: { type: string } }这里要注意description字段的写法不是给人看的是给模型看的。Agent 内部如果是 LLM 驱动它会根据这段文字来决定怎么填参数。我最初写描述时很粗糙只写了查询订单结果模型经常参数字段名都搞错。后来我明确说明订单号为13位数字、日期格式为YYYY-MM-DD之后模型传参准确率几乎到了百分之百。3.3 本地启动与探活检查启动 Agent 进程时用以下命令agent-reach start --config order_agent.yaml启动日志会打出 Reach ID、监听端口和已注册的服务能力。看到类似这样的输出就代表启动正常[INFO] Reach ID: ord-7f2a9c1e4b8d [INFO] Registry connection established: localhost:8355 [INFO] Capability query_order registered [INFO] Capability query_orders_by_date registered [INFO] Listening on 0.0.0.0:9351然后我用另外一个终端做探活检查注册是否生效agent-reach reachability check --target ord-7f2a9c1e4b8d --registry localhost:8355如果输出Reachable: True说明寻址解析成功目标 Agent 能响应握手请求。这一步我强烈建议每次改动配置后都执行一遍。很多时候你改了配置但没生效这个命令能帮你迅速判断出是不是注册环节出了问题而不用去猜通信层。3.4 打通第一个跨 Agent 调用三个 Agent 都启动后我在assistant_agent这边发起了一次调用让它去查订单 Agent 的数据。这个调用是通过 SDK 发起的核心逻辑很简单import asyncio from agent_reach import ReachClient async def main(): client ReachClient() # 连接订单 Agent 并创建会话 session await client.create_session(ord-7f2a9c1e4b8d) # 动态发现能力 caps await client.list_capabilities(session) print(发现能力:, [c.name for c in caps]) # 调用 query_order result await client.invoke( sessionsession, capabilityquery_order, params{order_id: 2025011500123} ) print(调用结果:, result) asyncio.run(main())第一次跑这段代码输出里成功看到了订单 Agent 返回的状态和金额。整个过程走下来list_capabilities拿到的能力清单跟我配置的完全一致说明 Schema 解析正确。那次成功打印结果的时候我心里一块石头落地了——Agent 之间的动态发现和调用真的可以在本地用这套协议跑通而且不需要在调用方代码里硬编码任何接口路径。3.5 关键参数选择与经验数据在实操过程中有几个参数我觉得还是有必要记下来registry默认端口是 8355这取决于你的部署环境。如果多个 Agent 不在同一台机器上要确认 Registry 地址能被所有 Agent 访问到。listen.host设置为0.0.0.0时本机和外部都能访问如果只设127.0.0.1其他机器上的 Agent 就连不上。这个看似基础但很容易被忽略。超时参数方面Agent-Reach 支持在注册能力里声明timeout_hint同时客户端的请求也可以单独指定request_timeout。如果被调用方已经声明了timeout_hint客户端单次请求超时就该取两者较大值。我在测试时试过固定 3 秒但订单查询偶尔超过 5 秒被我误判为超时后来改成动态取max(client_timeout, server_timeout_hint)之后就没再出现这种问题了。会话状态建议定期快照。Agent-Reach 虽然支持会话恢复但如果你不主动保存快照恢复时只能拿到空状态。我后来写了一个简单的定时任务每 30 秒把会话中的中间结果序列化到内存缓存里崩溃恢复的成功率明显提升。4. 常见问题与排查技巧实录4.1 握手失败Registry 地址不可达一个非常常见的现象Agent 启动正常但在另一个节点上探活显示Reachable: False。排查思路不要一上来就怀疑协议问题先看网络。我用curl直接访问 Registry 的 HTTP 端点发现超时。再查发现是云服务器的安全组规则没有放行 8355 端口。在本地测试时没有这个问题换到云主机上才暴露出来。所以如果你在多机部署时遇到握手失败先确认Registry 端口放行、Agent 监听端口放行、两端系统防火墙没有拦截。提示Agent-Reach 默认的握手超时时间是 5 秒。如果你的网络延迟本来就高比如跨地域部署在客户端配置里把handshake_timeout调大一些别用默认值。4.2 发现不到技能Schema 校验未通过这个问题最隐蔽也最让人头疼。Agent 在注册能力时Registry 会严格校验input_schema和output_schema是否符合规范。如果你手写配置时把required写成了require或者properties下面多了一个逗号Schema 解析就会失败。但这个失败不是直接报错而是这条能力被静默跳过Agent 依然启动成功但能力列表里就是找不到它。我排查了将近半小时才定位到order_agent.yaml里query_order的output_schema中status枚举值我写了一个中文已完成但程序里实际返回的是英文completed导致 Schema 校验不一致这条能力没有注册成功。所以我建议在本地写完配置文件后先跑agent-reach validate --config order_agent.yaml这个命令会检查配置语法与 Schema 合法性能提前暴露 90% 的配置问题。4.3 正常连接但调用报错JSON 序列化边界还有一个我在实际使用中遇到比较多的问题返回结果里包含非 UTF-8 字符或者浮点数的NaN/Infinity值。JSON 标准里不支持这些值但 Python 的json.dumps默认会输出NaN、Infinity这样的非标准 Token导致对端解析失败。Agent-Reach SDK 对返回值做了allow_nanFalse的严格序列化如果输出 Schema 的字段类型是number但内部计算产生了 NaN这个字段序列化时就会报错。这是 Agent 开发中一个比较隐蔽的坑。我后来在计算逻辑里加了显式检查把所有非有限数值替换为null并在 Schema 里允许null问题就消失了。同样地如果你在输出里不小心混入了bytes类型也无法直接 JSON 序列化。编码层面对输出类型的一致性要格外注意。规则很简单进入 Wire 的只允许 JSON 原生类型string、number、boolean、object、array、null其他类型一律先转换。4.4 会话状态丢失重启后恢复无效果前面提到了恢复机制但貌似支持恢复和真正能恢复之间隔着一个持久化步骤。我测试时杀掉订单 Agent 进程重启后尝试用同一个 session ID 恢复发现拿不到旧状态。原因很简单我没有配置持久化后端内存中的会话快照随进程一起归零了。Agent-Reach 提供session_backend配置项可以指定redis或local_snapshot。如果你想跨重启保留会话至少要用local_snapshot方式reach: session_backend: type: local_snapshot path: /var/lib/agent-reach/snapshots配置好后正常关闭进程时它会自动把活跃 session 的状态写入指定目录重启时用同 ID 建 session就会尝试加载快照。只要代码逻辑不改变例如同一版本的 Capability 描述恢复基本无缝。这里有个隐含要求会话状态本身必须是可序列化的如果里面有锁、连接句柄、生成器等 Python 对象快照也无法保存需要你在写入 session 前自行转化。我建议在自定义 Agent 能力时会话上下文里只放 JSON 可序列化的数据这样后续加持久化、迁移、降级都方便。4.5 问题速查表把上面提到的坑整理成一张速查表遇到问题可以先对照排查现象可能原因解决动作Reachable: False网络不通/防火墙拦截确认 8355 和监听端口放行能力列表缺少某项Schema 校验失败运行validate检查 YAML调用报 JSON 解析错误返回值含 NaN/bytes序列化前显式清洗数据会话恢复无效果未配置持久化后端配置session_backend并确认路径可写握手超时公网延迟高调大客户端handshake_timeout调用方收到参数字段名错误描述文案不明确在description中写明格式与示例5. 哪些场景真正适合 Agent-Reach5.1 微服务化 Agent 的粘合剂如果你的团队已经在做多个 Agent每个 Agent 负责一个领域搜索、推荐、客服、风控……那你必然会遇到两个问题第一Agent 之间如何互相发现第二Agent 之间如何处理长会话的上下文传递。Agent-Reach 的寻址、注册和会话模型几乎就是为这种多 Agent 微服务化场景设计的。我之前在一家电商公司见过一种做法每个 Agent 独立部署彼此通过内部 HTTP 接口调用但接口路径和参数全靠人肉约定一旦某个 Agent 更新了参数结构所有依赖方都要跟着改非常脆弱。换成 Agent-Reach能力清单自动下发参数结构动态获取至少把硬编码接口契约这个问题从架构层面消解掉了。5.2 人机协同流程编排另一个让我觉得很有价值的场景是人机协同流程编排。比如一个财务周报生成流程数据 Agent 去仓库取数分析 Agent 做环比和异常标记报告 Agent 生成解读文本。人工只在中间做一次审批。传统做法是人去不同系统里粘贴数据或者写个定时脚本把所有逻辑耦合在一起。用 Agent-Reach 后三个 Agent 各自独立唯一需要担心的只是会话状态和超时设置。我在这个场景里还发现了一个好处因为每个 Agent 的能力是通过 Schema 暴露的其他 Agent 可以看到伙伴能做什么而不是被硬编码得知伙伴做什么。这意味着你可以在运行中新增一个 Agent 来替换或增强某个环节不需要通知其他 Agent 修改代码。只要新 Agent 注册相同的能力名和 Schema流程立即生效。5.3 设计取舍什么时候先别用Agent-Reach 也不是银弹。下面几种情况我不建议硬上仅需单模型调用若干工具不需要 Agent 间会话。这种用 MCP 就够了Agent-Reach 的寻址和会话机制会显得过度设计。实时性要求极高的高频调用。虽然协议层开销很低但多了握手、加密、Schema 解析这些步骤相比裸 gRPC 还是有额外消耗。如果是每秒钟上万次、每次几十微秒以内的极短调用直接长连接 Protobuf 会更合适。团队没有专门的开发支持。Agent-Reach 还在快速迭代期API 可能变化真出问题的时候社区资料不算多。如果你的团队追求极致的稳定和成熟生态可以再等一等如果愿意接受一定的不确定性来换取架构上的灵活性那你现在就可以试试。6. 后续可以怎么扩展6.1 自定义传输层适配Agent-Reach 的传输层是抽象的目前有 HTTP/2 和 QUIC 两种实现。如果你有特殊网络环境比如必须走 Kafka 或 Redis Pub/Sub可以实现一个自己的 Transport 对接。这就要求传输层只负责投递消息不负责语义解析。我在编码结构上把消息头和消息体分开了未来如果想接其他协议只需要保证头部的 Reach ID、Session ID、Message Type 不变传输层随便换。6.2 与外部 Agent 网络互联不同团队部署的 Agent-Reach 网络之间可以通过桥接节点实现互联两个 Registry 互相建立 Trust Link使 A 网络的 Agent 能被 B 网络发现。这在跨公司协作时很有想象空间——双方各自管自己的 Agent只在需要时通过桥接节点暴露能力。寻址和鉴权设计已经为跨域场景留了接口但目前的版本还没有完全图形化的管理界面对运维人员有一定门槛。6.3 能力网关与流控真正上生产之后能力网关是很有用的扩展方向。你可以在 Reach 调用链路上插入一个网关层统一做流量控制、调用审计、异常熔断。因为 Agent-Reach 的每个请求都带 Session 和 Capability 信息网关可以很方便地按维度计量。我目前的经验就是把这些都写在网关层Agent 本身的逻辑保持简单。我个人在实际操作中最大的感受是Agent-Reach 比起一套协议更像一种Agent 之间如何彼此尊重与协作的约定——先介绍我是谁再说明我能做什么然后全程加密通信、按契约办事、会话隔离。这种思维一旦建立起来你设计中长期的多 Agent 系统时思路会清晰很多。最后再分享一个小技巧所有 Agent 配置文件的 Description 字段一定要当成模型 Prompt 来写写清楚、写具体、给示例。就这一个细节直接决定你系统协作时的参数准确率和使用体验。