
前两天拿到一份 Agent Network Protocol 的技术白皮书草案从头翻到尾我最大的感受不是又看到一套新协议而是看到大家终于开始认真对待 AI Agent 之间的“网络问题”了。过去一年我很明显感觉到 Agent 圈子的重心正在从“单 Agent 能干什么”转向“多 Agent 怎么一起干活”。Agent 之间怎么发现彼此怎么证明身份消息走什么格式谁负责调度这些问题不解决Agent 就永远是信息孤岛。Agent Network Protocol 正是在这些背景下被提出来的一套连接协议草案。这篇文章不打算复述白皮书而是想站在我自己的工程视角把草案里的设计思路、关键数据结构、我在本机跑通的最小实现以及踩过的坑一起拆开讲清楚。不管你是做 Agent 应用、中间件还是想搞开放机器人网络应该都能从里面找到一点有价值的东西。1. 白皮书草案到底讲了什么1.1 一句话说清 ANP 的定位Agent Network Protocol 的目标是给所有 AI Agent 提供一套统一、开放、安全、可扩展的通信网络协议。它和目前大家更熟悉的 MCP 模型上下文协议有本质区别MCP 解决的是“一个 Agent 怎么调用外部工具”的问题而 ANP 解决的是“Agent 与 Agent 之间怎么互联互通”的问题。类比一下MCP 像是给员工接上电话线和办公系统他一个人就能干活ANP 则是把一堆员工放进同一个会议室让他们自己找人、派活、汇报结果。协议草案里没有用很多复杂的学术名词但它的分层思想很清晰身份、发现、消息、协作四层每一层都有明确的数据标准和接口约定。草案里还明确划定了边界它不做统一大脑不规定 Agent 内部的推理方式也不限定必须用哪种大模型。这意味 ANP 就像 TCP/IP 一样允许各家 Agent 在内部随意发挥对外只要遵守一套“网络语言”就行。多 Agent 系统最怕的就是“协议绑定框架”一旦绑定就变成私有协议跨平台协作全部得靠适配器硬扛。ANP 这种边界划分本质上是在追求最低的协作成本。1.2 草案中的核心目标与边界白皮书草案花了很大篇幅讨论非功能需求我归纳下来主要有几个目标开放性不能绑定任何单一云厂商或框架所有接口都公开任何团队都能基于协议实现自己的 Agent 网关安全性网络里每个 Agent 身份可验证消息可签名、可加密防伪造、防重放扩展性Agent 的能力可以动态上线、下线协议支持按需发现最小依赖尽量不要求所有参与者都采用同一个消息中间件通过可插拔传输层屏蔽底层实现差异。这些目标听起来都挺正确但落地时其实有很多取舍。比如安全与易用很难兼得草案给出的方案是用 DID 去中心化标识符承载身份同时允许多种签名算法并存。边界部分草案明确说明不负责语义理解也不定义业务逻辑——Agent 之间传递的 JSON 内容具体是什么协议层不关心。这就像邮政系统只处理信封上的地址和邮戳不拆开信看内容一样。协议层的克制往往决定了生态能长多大。1.3 为什么是“草案”而不是正式标准标题里带有“草案”我在读内容时也发现很多章节标注了 TODO 和开放问题。这说明作者团队非常清楚Agent 网络协议现在还远没到盖棺定论的时候。比如身份模型里到底用 W3C DID 标准还是自研 DID 方法通信层默认传输是走 WebRTC、WebSocket 还是 QUIC这些都还在讨论中。站在我的经验看“草案”反而比最终标准更有阅读价值因为你能看到设计者的思路演变。等到标准定稿很多“为什么这么设计”的上下文反而被削掉了。我读草案的另一个感受是它把“可演进”放到了很重要的位置。草案里明确说了预计后续版本会至少保留 12 个月的向后兼容窗口。这种对兼容性的承诺对生产系统选型非常关键。没有这个承诺没人敢把核心业务压在一个不成熟协议上。2. 协议设计的关键决策与原理2.1 身份层DID 不是炫技是信任的前提Agent 与 Agent 通信的第一个问题是我怎么确定对面这个“人”就是它声称的那个 Agent传统做法是 API Key但 API Key 只适合中心化平台。ANP 草案选择了 DID每个 Agent 拥有一个基于公私钥的身份标识格式类似 did:anp:agent:xxxxxxxxx。私钥由 Agent 自己持有公钥地址通过注册或带外方式分发。这里我要说说为什么这个选择很务实。多 Agent 网络大概率是跨组织、跨平台的A 公司的 Agent 和 B 公司的 Agent 要协作不可能互相存对方的 API Key。用非对称签名的方式只需要拿到对方的 DID 公钥再结合挑战-响应验证就能完成轻量级身份确认。具体流程是验证方给对端发送一段随机数对端用私钥对该随机数签名验证方用公钥验签。整个过程不需要一个集中式 CA也不存在密码泄露的风险。我自己的体会是DID 方案在工程上带来的额外成本主要在密钥管理。Agent 实例重启后私钥不能丢所以你需要一个安全的本地存储或者 KMS。如果私钥落在别人手里DID 就名存实亡。草案里虽然没有展开讲密钥托管但任何认真落地的团队都必须自己补上这一层。2.2 发现层注册中心与 DHT 的选择第二个问题是我知道了一个 Agent 的 DID但我怎么知道它提供什么服务草案里给出了两套发现机制一类是“轻量注册中心”类似 DNS 加黄页另一类是“分布式哈希表 DHT”支持完全点对点发现。我做实验时首选注册中心因为它最简单Agent 上线时向注册中心 POST 自己的 DID、服务地址、能力标签注册中心把这些信息存成一张表其他 Agent 通过 GET /agents?capability... 查询。这里有一个关键选型能力标签不能乱写。草案给了一个建议结构用语义化 JSON 描述方便后续做基于语义匹配的发现。比如{type:text_processing,scale:trial,models:[llama-3-8b]}这个结构既照顾了精确匹配也为向量化留了口子。DHT 方案更酷但实现复杂度高。Kademlia 这类协议天然适合“查找某个 Key 对应的 Peer 地址”可以把 Agent 的能力哈希散列到 DHT 网络上。一旦 Agent 数量达到十万级以上注册中心会成为瓶颈DHT 能扛住但代价是无法做复杂的多维查询只能按 Key 精确查。所以草案把两者并列留给部署者自行选择。我在团队内部做技术评审时通常建议早期或企业私有场景用注册中心规模化公网场景再考虑 DHT。2.3 通信层消息信封与传输协议身份和发现都解决了接下来是消息怎么传。ANP 草案设计了一个统一的消息信封所有业务数据都装在一个标准 JSON 信封里。信封字段包括 msg_id、type、sender、recipient、created_at、payload、signature。这个设计很像 email 的 RFC 822 信封——路由信息和业务内容严格分离中间传输节点只关心信封上的地址和签名不关心 payload 内部是什么。传输层方面草案支持 WebSocket、HTTP/2、QUIC甚至允许通过 MQTT 桥接。这个“可插拔传输层”不是懒设计而是考虑到 Agent 可能运行在数据中心服务器上也可能跑在树莓派或手机端网络环境差异极大。宽松的传输层能带来生态繁荣但也会导致调试困难这个代价草案是接受的。从工程角度看只要抽象一个 Transport 接口底层换协议其实并不痛苦真正痛苦的是把协议写死在每一条业务链路里所以我很认同这种开放态度。2.4 协作层任务协商和结果回传最后是协作。Agent 之间发条消息并不难难的是怎么把一个复杂任务拆解给多个 Agent并且收集结果。草案定义的协作模型目前还是相对简单的请求-响应加状态机发起方 Agent 发出 task_request包含任务描述、期望能力、超时时间执行方返回 task_accept、task_reject 或 task_result。我看草案时注意到一个很有价值的设计任务投递采用至少一次语义加幂等控制。也就是说同一个 task_request 可能因为网络重发被接收方收到多次接收方需要用 msg_id 做去重而发起方不要假定只收到一次回调。这看起来不新鲜但很多团队自己做多 Agent 系统时经常忽略幂等性导致重复扣费或重复执行任务最后查得焦头烂额。协议把这一点明确写出来能省下很多坑。3. 核心细节与数据格式3.1 消息信封标准草案里最核心的应该是消息信封我直接贴一个我实现的字段结构字段类型必填说明msg_idstring是全局唯一消息ID用于幂等和追踪typestring是消息类型如 capability, request, responsesenderstring是Agent 的 DIDrecipientstring是目标 Agent 的 DID也可以是 group:xxxcreated_atint64是Unix 毫秒时间戳payloadobject是业务数据不限制结构expires_inint否消息有效期单位秒signaturestring是sender 对上述字段的签名这个信封最让我喜欢的一点是它把 expires_in 放到一级字段而不是塞在 payload 里。消息过期时间对分布式系统太重要了很多自己写的 Agent 通信框架没考虑过期导致迟到的结果覆盖新结果。放在信封一级所有中间节点都可以在路由时直接丢弃过期消息不用解业务数据。3.2 能力声明的写法Agent 上线要广播自己的能力。草案给了一个比较宽松但带推荐字段的结构我建议按下面这个模板来写{ agent: { did: did:anp:agent:abc123, name: billing-assist, owner: org:acme }, capabilities: [ { id: cap_invoice_extract, type: doc_processing, summary: Extract invoice fields from PDF, input_schema: {file_url: string, format: string}, output_schema: {fields: object}, reliability: {average_time_ms: 5000, success_rate: 0.98} } ], endpoint: { protocol: ws, address: wss://agent.acme.io/anp/ws } }让人公开自己的 success_rate 和平均耗时这一点挺大胆但是在真实多 Agent 市场里信息透明度是信任的基础。如果你不想公开也没关系草案允许省略这些字段只是发现时排序权重会降低。我的建议是可以把 reliability 字段设为可选但对内一定要记录因为这会影响任务调度策略没有指标就只能盲目轮询。3.3 安全与幂等性设计安全层面草案重点讲了防重放和防篡改。防篡改靠签名签名覆盖整个信封的 canonical JSON而不是只签 payload。这里有个细节JSON 有多种等价序列化方式比如字段顺序、空格不同都会影响验签结果所以草案要求信封按字段名的字典序做 canonical JSON 序列化。我在实现时用 sort_keysTrue 才调通这是容易踩坑的地方。防重放则依赖 msg_id 加 created_at 加 expires_in。接收方需要缓存最近 N 分钟的 msg_id例如用 Redis 的 SETNX msg_id EX 60如果返回 0 说明是重复消息直接丢弃。另一个关键是 created_at 与当前时间差不能超过 5 分钟否则即使 msg_id 没出现过也拒绝处理。这套逻辑像一个双保险虽然会增加一点开发量但能省下无数排查重复执行的夜间电话。3.4 可插拔传输层为什么重要很多技术文档会把传输层直接固定成一种比如只支持 gRPC。ANP 草案选择支持多种传输初看会增加实现成本细想却是生态做大必须付出的代价。我身边就有项目早期用 WebSocket 做 Agent 网络后来想接入一个用 MQTT 的硬件网关结果发现协议完全不兼容只能加一层适配器硬转。ANP 的“能力发现加弹性传输适配”思路能让这类整合顺畅很多注册中心记录 endpoint 协议类型请求方收到后按协议选择对应的客户端发送器发送器内部封装不同协议差异对外统一暴露 send 和 recv业务层完全无感。4. 实操最小可用 ANP Node 的搭建过程4.1 环境准备这一段开始进入动手环节。我用 Python 3.10 加 FastAPI 加 uvicorn 写了一个最小的 ANP 注册中心和一个 Agent Node只需要几百行代码就能跑通注册、发现、请求、响应全流程。我的目录结构是这样anp-lab/ ├── registry.py # 注册中心HTTP 内存存储 ├── agent_node.py # Agent 节点WebSocket 信封印包处理 ├── anp_envelope.py # 信封构造与验签 ├── client_demo.py # 发起请求的模拟客户端 └── requirements.txt注册中心为什么用 HTTP因为发现请求是短暂的、一对一或一对多的查询HTTP 语义刚刚好。节点之间实际传输用 WebSocket原因是长连接能减少握手开销而且服务端可以主动推送任务给 AgentHTTP 也能做轮询但低延迟场景下 WebSocket 舒服得多。如果你想更硬核用 QUIC 更好但 Python 生态里没有特别成熟的 QUIC 库所以我先用 WebSocket。依赖方面只需要 fastapi、uvicorn、websockets、pycryptodome 这几个包。4.2 实现消息信封与签名信封类我用 dataclass 定义序列化后加签。这里关键点是签名前要对整个信封做 canonical 格式化我在代码里直接用 json.dumps(obj, sort_keysTrue, separators(,, :))。加密签名我用了 RSA-PSS私钥存本地公钥 DID 由注册中心保存。不要用 RSA 的 PKCS1v15PSS 的安全性更稳。import json, time, uuid from dataclasses import dataclass from Crypto.Hash import SHA256 from Crypto.Signature import pkcs1_15 from Crypto.PublicKey import RSA dataclass class Envelope: msg_id: str type: str sender: str recipient: str created_at: int payload: dict expires_in: int 60 signature: str def canonical_json(self): data { msg_id: self.msg_id, type: self.type, sender: self.sender, recipient: self.recipient, created_at: self.created_at, payload: self.payload, expires_in: self.expires_in } return json.dumps(data, sort_keysTrue, separators(,, :)) def sign(self, private_key): h SHA256.new(self.canonical_json().encode(utf-8)) sig pkcs1_15.new(private_key).sign(h) self.signature sig.hex()这段代码里的 canonical_json 与签名逻辑就是我前面说最容易出问题的地方。很多新手直接 sign 原始对象的 str()Python 默认 dict 输出带空格和单引号后端一验签就失败。用固定的 JSON 序列化参数是唯一的解法。还要注意签名时 signature 字段必须留空或从校验集合中排除我在 canonical_json 里没有包含 signature所以天然满足要求。4.3 实现注册与发现注册中心我是用 FastAPI 写的接口只有三个/register、/discover、/agents/{did}。Agent 启动时 POST 能力声明注册中心存到字典里同时保留一个心跳时间戳超过 120 秒没心跳就把 Agent 标记为离线并在发现结果中过滤掉。这个 120 秒不是什么标准而是我自己的经验值太短容易误杀慢启动的服务太长则会让列表出现僵尸节点。from fastapi import FastAPI from pydantic import BaseModel import time app FastAPI() class AgentInfo(BaseModel): did: str endpoint: str capabilities: list last_seen: int 0 agents {} app.post(/register) def register(info: AgentInfo): info.last_seen int(time.time() * 1000) agents[info.did] info return {status: ok, did: info.did} app.get(/discover) def discover(capability: str ): now int(time.time() * 1000) result [] for info in agents.values(): if now - info.last_seen 120_000: continue if not capability or any( cap.get(type) capability or capability in cap.get(id, ) for cap in info.capabilities ): result.append(info) return result这里只做了简单的能力字符串匹配。如果你想要语义匹配可以把 capability type 换成向量嵌入用余弦相似度排序。这个扩展和草案里提到的开放语义发现一脉相承也是我认为后续最会增强的方向。注册中心还应该加上身份白名单防止任何人随意注册任意 DID我为了 demo 省掉了但生产环境必须补。4.4 任务请求与响应 demoAgent Node 部分我创建一个 WebSocket 服务监听信封。收到 task_request 后检查 payload 里有没有 task_desc 字段有就执行一个假设的任务把收到的文本转成大写然后打包一个 task_result 返回。整个过程很简单但足够演示协议思路。import asyncio, websockets from anp_envelope import Envelope from Crypto.PublicKey import RSA agent_did did:anp:agent:demo-1 private_key RSA.import_key(open(demo_private.pem).read()) def handle_request(payload): text payload.get(task_desc, ) return {result: text.upper(), length: len(text)} async def on_message(websocket, message): env Envelope.from_json(message) if env.type task_request: result handle_request(env.payload) resp Envelope( msg_iduuid.uuid4().hex, typetask_result, senderagent_did, recipientenv.sender, created_atint(time.time() * 1000), payloadresult, ) resp.sign(private_key) await websocket.send(resp.to_json())这里其实还有个细节正常应该先发一个 task_accept让请求方知道任务被接受了然后再在执行完成后发 task_result。对于长时间任务如果不先发 accept请求方很容易因为超时误判整个任务失败。所以我建议按状态机走收到 request 后返回 accept再执行最后返回 result。demo 为了简单只回了 result但你写真实系统时千万补上 accept。4.5 完整运行结果与观察我实际跑通后在注册中心查到 Agent然后用一个客户端脚本发送任务请求看到了如下日志[Registry] register did:anp:agent:demo-1 endpointws://localhost:8701 [Discovery] found agent demo-1 with capability doc_processing [Client] sent task_request msg_idmsg_001 [Agent] received task_request, processing... [Agent] sent task_result msg_idmsg_002 [Client] received result: HELLO ANP LAB整个过程耗时大约 80ms其中签名和验签大概占 20ms网络占 50ms业务处理 10ms。签名开销在本地 Python RSA 下还能接受但如果你用 ECDSA 会更快。草案里也说推荐优先实现 ECDSA降低 IoT 场景开销这个方向我验证过确实值得采纳。5. 落地过程中的踩坑与排查5.1 问题一Agent 互相找不到第一个坑我把 Agent Node 放在 Docker 容器里注册中心暴露在宿主机Agent 的 endpoint 写成了 ws://localhost:8701结果另一个容器请求时根本无法连通。这不是协议问题是网络拓扑问题但非常典型。解决方法是容器内必须配置宿主机可达的 IP 或使用 Docker Compose 服务名。排查思路在发现结果里把 endpoint 打出来先看它是否从 Agent 的视角看是可达的而不是从集群外看。我还遇到过一种情况Agent 注册时正常但发现列表为空原因是我把能力标签写成了doc-processing而查询方用的是doc_processing字符匹配差异导致永远查不到。草案虽然允许自定义标签但如果你不提前约定一套受控词表跨组织协作就会在这个小地方卡住。建议团队内部维护一个 capability type 的枚举清单。5.2 问题二消息超时但任务还在跑第二个坑遇到一个长时间任务Agent 处理了 5 分钟才回结果但请求方在 60 秒时因为 expires_in 到期直接丢弃并报失败。这个问题的根源在于expires_in 只应该用于控制信使层“这个消息是否还有发送价值”不应该用于控制任务执行超时。任务执行超时必须由任务状态机单独维护。我在实现里给 task_request 的 payload 加了 execute_timeout 字段Agent 节点用 asyncio.wait_for 控制执行时间而信封的 expires_in 只保留 60 秒保证消息新鲜度。这个区分非常关键建议写进自己的设计文档。进一步地如果任务执行时间可能超过 60 秒你还应该在 accept 响应里带上预计完成时间。请求方可以根据这个信息动态调整等待策略而不是傻傻地等一个固定超时。草案里没有强制要求这一步但实践下来非常有用能让系统的容错性上一大截。5.3 问题三签名校验失败第三个坑是签名校验失败。我一开始在接收方用收到的原始 JSON 字符串做验签但只要消息从 WebSocket 重新发送或者被中间节点转包字符串的字段顺序就可能变。后来我才意识到验签时要先从信封里取出 signature 字段然后把剩余字段重新 canonical 成 JSON再验签。绝不能直接对网络原始 payload 做 hash。而且由于信封里的 signature 本身不能参与签名我要在 canonical 序列化前先把 signature 字段置空。这一步我验证过是最容易出错的地方。还有一个细节如果你用 Python 的 json.loads 解析后直接 dumps原来 payload 里的整数可能被转成浮点或者字符串导致 canonical 结果不一致。所以验证 JSON 结构时最好保留原始数据类型不要做不必要的类型转换。我就是因为把 payload 里的 count 字段从前端传成字符串后端解密后变 int签名一直对不上查了半天。5.4 排查技巧日志里要加 msg_id 全链路最后一个建议也是我踩了多次坑后的心得所有日志必须带上 msg_id。你在一个多 Agent 系统里排查一条消息的传递路径如果没有 msg_id你根本不可能把它跟其他消息区分开。这个规则适合所有分布式协议。另外建议在消息入口加一个统一的 debug 开关把 canonical 前和签名后的 content 各打印一次能省下大量的猜测时间。下面的表格是我维护的排查速查表分享出来症状可能原因快速检查方法发现列表为空心跳过期或能力标签不匹配查 /agents 原始列表看 last_seen消息发不出去endpoint 协议不匹配确认发现结果中 protocol 与连接器一致消息延迟很高发送和接收之间经过轮询检查连接是否走 WebSocket验签失败canonical JSON 不一致对比发送端和接收端 canonical_json 输出重复执行任务缺少幂等去重检查接收端 SETNX msg_id6. 应用场景与影响范围6.1 多 Agent 协同办公最直接的应用场景是让不同厂商的 Agent 能协同干活。比如一家公司的“合同审查 Agent”发现另一家公司的“风险评估 Agent”在线它直接把合同片段发过去对方返回风险分再回到自己的工作流里生成结论。整个过程不需要两边公司提前对接 API也不需要统一使用同一个 Agent 平台。这个场景一旦铺开软件集成成本会大幅降低从“点对点开发接口”变成“发布即集成”。这种跨组织的 Agent 协作对身份信任要求极高恰好 ANP 的 DID 模型天然支持。只要两边都能验证对方的 DID 签名就不需要提前建立信任。我甚至设想过一个更实际的场景审计场景下所有 Agent 的行为消息都带签名和完整链路谁在什么时间给谁发了什么全部可追溯这对企业合规非常有吸引力。6.2 开放服务市场的可能性更进一步ANP 可以支撑一个开放的 Agent 服务市场。设想将来有 Agent 专门做发票识别有 Agent 专门做航班查询它们都通过 ANP 发布能力声明。需要用到这些能力的 Agent 可以在线发现、动态调用按次计费。这里身份 DID 和签名机制就能防止有人冒充服务方也方便追责。不过要想真正跑起来还需要在协议之上补一套计费和结算机制。草案目前没有涉及但我认为恰恰是留给生态的想象空间。协议负责互联业务负责增值这个分工很清晰。对于创业者来说与其去卷另一个 Agent 平台不如先做协议层上的公共服务比如注册中心、信用评价、计费网关这些都会随着 Agent 数量的增长水涨船高。6.3 智能硬件场景草案提到的物联网场景也很实际。家里的多个设备 Agent比如音箱、门锁、扫地机如果都遵守 ANP就可以不依赖一个特定品牌的云平台而是通过本地 Hub 直接互相发现和通信。这对隐私保护是利好因为数据可以在本地闭环流转。当然低算力设备用完整 RSA 签名可能扛不住所以草案推荐 ECDSA 和轻量级实现这个方向我很赞同。试想一个更具体的场景你的门锁 Agent 发现室内温度 Agent 报告“室温过高”于是联动空调 Agent 开启制冷同时给手机上的个人助理 Agent 发一条通知。整个过程不需要任何一家厂商的云平台所有 Agent 都在本地网络完成发现和协商。这种去中心化的智能家居才是真正的用户可控。6.4 对现有 AI 中间件生态的影响最后说影响范围。如果 ANP 草案成熟并进入生产它很可能和 MCP、LangGraph 这类框架形成互补而不是替代关系。MCP 负责工具调用ANP 负责 Agent 之间的互连未来一个完整应用至少会同时用到这两者。对于正在做 Agent 平台的同学我的建议是尽早关注协议层不要在私有接口上投入太多因为一旦开放协议成为默认迁移成本会高得让人难受。从更宏观的角度看ANP 这类协议的出现意味着 AI 基础设施正在从“模型能力”往“网络能力”扩展。模型让你能理解世界网络协议让你能和世界协作。等这个协议真的跑起来很多当前的 Agent 框架会被重新洗牌真正剩下的不是某个框架的 Agent而是遵守同一套网络协议的 Agent 生态。我在跑完这个 demo 后最大的体会是Agent Network Protocol 的白皮书草案里没有一条是多余的。身份、发现、信封、幂等、可插拔传输每一项都在真实工程里对应着一个具体的痛点。作为一个天天跟分布式系统打交道的人我看到草案时最开心的是它没有去定义什么“超级智能体大脑”而是老老实实解决网络互联的基础问题。建议你也拿一份草案照着信封格式写一个最小节点试试。先不要去想宏大的应用场景就从注册中心、一个 Agent、一个客户端开始把消息打通你会对多 Agent 网络有完全不一样的理解。