☰
Agent Network Protocol 解析:基于 HTTP 与 DID 的智能体通信协议设计
2026/10/7 6:14:40 网站建设 项目流程

1. 为什么我们需要重新审视智能体通信这件事

过去大半年,我一直在折腾多智能体协作系统的落地。从最早用简单的函数调用把几个模型串起来,到后来尝试让不同框架下的智能体互相“对话”,踩的坑一个接一个。最让我头疼的不是模型能力不够,而是智能体之间根本没法好好说话。A框架的智能体输出一段JSON,B框架的智能体期望的是另一种结构;C平台用gRPC做传输,D平台只认HTTP回调。每接入一个新智能体,就要写一层适配代码,项目里一半的时间花在“翻译”上,而不是业务逻辑本身。

这就是Agent Network Protocol(后面简称ANP)试图解决的核心问题。它想做的事情,用一句话概括:给智能体之间的通信定一套大家都认的规矩,让不同来源、不同框架、不同部署环境的智能体能够像浏览器访问网页一样,用统一的方式发现彼此、理解彼此、协作完成任务。ANP不是一个具体的代码库,而是一份技术白皮书草案,它定义了协议的分层结构、身份机制、消息格式和交互模式。适合谁来读?如果你正在做多智能体系统、AI Agent平台、或者任何需要让多个自动化实体协同工作的项目,这份协议的设计思路值得你花时间研究。哪怕你暂时不打算实现它,理解它的取舍逻辑也能帮你在自己的系统里做出更好的架构决策。

我读这份白皮书草案最大的感受是:它没有重新发明轮子,而是很聪明地站在了HTTP和W3C DID这两个成熟标准的肩膀上。下面我就按自己的理解,把这份协议的核心设计、实操要点和落地时会遇到的问题拆开来讲。

2. 协议整体设计与分层思路拆解

2.1 为什么是“网络协议”而不是“通信框架”

市面上已经有不少智能体通信方案,比如某些平台自研的消息总线、基于消息队列的异步通信、或者直接暴露RESTful接口。这些方案在单一平台内很好用,但一旦跨平台就歇菜。ANP的定位很明确:它要做的不是又一个框架,而是协议。框架和协议的区别在于,框架规定了你必须怎么实现,协议只规定了你必须怎么交互。就像HTTP不关心你的服务器是Nginx还是Apache,ANP也不关心你的智能体是用LangChain还是AutoGen写的,只要你能按照协议规定的格式收发消息,就能接入网络。

这个定位决定了ANP的设计必须足够抽象,抽象到不依赖任何具体的技术栈。白皮书里把协议分成了四层,我从下往上梳理一下自己的理解。

最底层是传输层,直接复用HTTP/HTTPS。这个选择非常务实。HTTP的生态太成熟了,任何语言、任何平台都有成熟的HTTP客户端和服务端实现,调试工具(curl、Postman、Wireshark)一应俱全。用HTTP做传输意味着ANP天然支持请求-响应模式和流式传输(通过SSE或WebSocket升级),而且能直接受益于HTTP连接复用、缓存、压缩等现有优化。白皮书里特别提到连接复用这一点,因为智能体之间的通信往往是高频小消息,如果每次请求都新建TCP连接,握手开销会吃掉大量性能。HTTP/1.1的keep-alive和HTTP/2的多路复用都能直接拿来用。

往上一层是身份层,采用W3C DID(去中心化标识符)。这是整个协议里我觉得最值得细看的设计。传统方案里,智能体的身份通常是一个API Key或者OAuth Token,由某个中心化平台颁发。问题在于,当智能体跨平台协作时,A平台的Token在B平台不认,你又得走一遍授权流程。DID的思路是让每个智能体拥有一个全局唯一的、自证明的标识符,不需要中心化机构背书。白皮书里用的DID方法应该是did:web或类似的轻量级方案,因为纯链上DID对大多数应用来说太重了。DID文档里包含公钥信息,智能体之间可以通过签名验证消息来源,不需要每次都问“你是谁颁发的”。

再往上是消息层,定义了智能体之间交换的消息格式。这部分是协议的核心,白皮书里应该规定了消息的头部字段(发送者DID、接收者DID、消息类型、时间戳、签名等)和载荷结构。载荷部分我猜测会采用JSON-LD或者类似的语义化格式,因为不同智能体对同一个概念的理解可能不同,需要一套共享的词汇表来消除歧义。比如“任务”这个词,在客服智能体里可能指一次对话,在物流智能体里可能指一次配送,如果没有语义标注,接收方根本不知道该怎么处理。

最顶层是交互层,定义了智能体之间可以进行的交互模式。白皮书里提到了请求-响应、发布-订阅、协商等模式。这一层最像传统的多智能体系统设计,但ANP把它标准化了。比如协商模式,两个智能体可以就任务分工、价格、时间窗口等进行多轮对话,直到达成一致或失败。这种模式在自动化交易、资源调度场景里非常有用。

2.2 与HTTP的关系:不是替代,是寄生

很多人看到ANP的第一反应是“这不就是HTTP上加了一层吗”。没错,但这一层加得很有讲究。ANP没有重新定义一套传输机制,而是把HTTP当作“公路”,自己在上面跑“货车”。这样做的好处是,任何支持HTTP的环境都能跑ANP,不需要特殊的网络配置或防火墙规则。坏处是,HTTP的一些限制也会传导上来,比如请求-响应模式对长时任务的支撑不够好,需要额外的心跳或轮询机制。

白皮书里应该讨论了这个问题,我猜测解决方案是结合SSE(Server-Sent Events)做服务端推送,或者用HTTP/2的流做双向通信。实际落地时,我建议对短任务直接用请求-响应,对长任务用“提交任务-轮询状态-获取结果”的三段式模式,这样最稳妥,也最容易调试。

2.3 与W3C DID的整合:身份问题的优雅解法

DID的引入解决了一个很实际的问题:跨平台身份互认。假设你有一个客服智能体部署在阿里云,一个物流智能体部署在腾讯云,它们要协作处理一个退换货请求。传统方案下,客服智能体需要先调用腾讯云的API获取访问令牌,然后才能发消息给物流智能体。令牌有过期时间,需要刷新,刷新逻辑又要处理各种异常。用DID的话,每个智能体在初始化时生成自己的DID和密钥对,DID文档托管在一个可公开访问的URL上(比如https://example.com/.well-known/did.json)。协作时,客服智能体直接用自己的私钥签名消息,物流智能体从DID文档里拿到公钥验证签名。整个过程不需要任何中心化授权服务器。

当然,DID也不是银弹。密钥管理是个大问题。私钥丢了,智能体的身份就丢了,所有历史消息的签名都无法验证。白皮书里应该提到了密钥轮换和恢复机制,但具体实现起来复杂度不低。我的建议是,在早期落地时,先用中心化的密钥托管服务过渡,等DID生态成熟了再逐步去中心化。

3. 核心细节解析与实操要点

3.1 消息格式设计:让机器能读懂彼此

ANP的消息格式设计直接决定了协议的可用性。白皮书里应该定义了一套基础消息结构,我根据常见实践推测一下核心字段。

消息头部至少包含以下内容:

字段名类型说明
sender_didstring发送者的DID标识符
receiver_didstring接收者的DID标识符
message_idstring全局唯一消息ID,用于去重和追踪
timestampintegerUnix时间戳,毫秒级
message_typestring消息类型,如request、response、event
signaturestring对消息内容的数字签名
content_typestring载荷的MIME类型,如application/json

载荷部分则根据message_type不同而不同。请求消息的载荷包含action(要执行的操作)和parameters(操作参数)。响应消息的载荷包含status(成功/失败)、result(结果数据)和error(错误信息)。

这里有个细节值得注意:白皮书里应该规定了消息的序列化方式。JSON是最自然的选择,但JSON有个问题——数字精度。JavaScript的Number类型是双精度浮点数,处理大整数时会丢精度。如果智能体之间传递的是金额、ID之类的数据,用JSON就要特别小心。我的做法是在JSON里把大整数序列化成字符串,接收方再按需转换。这个技巧在跨语言通信时尤其重要,因为不同语言对JSON数字的解析行为不一致。

另一个细节是消息签名。签名应该覆盖哪些字段?如果只签载荷,头部可以被篡改;如果签整个消息,那message_id和timestamp就不能在签名后修改。白皮书的做法应该是签一个规范化的消息摘要,包含所有关键字段。实操时要注意JSON的规范化问题——同样的数据,字段顺序不同、空格不同,序列化出来的字符串就不同,签名验证就会失败。解决方案是用JCS(JSON Canonicalization Scheme)之类的规范化算法,确保序列化结果唯一。

3.2 身份验证流程:从DID到可信通信

DID的验证流程比传统的API Key复杂,但安全性更高。我梳理一下完整的验证步骤。

第一步,发送方构造消息,用自己的私钥对消息摘要签名。私钥存储在安全的地方,比如硬件安全模块或加密的密钥库。

第二步,发送方通过HTTP POST把消息发到接收方的ANP端点。端点地址可以从接收方的DID文档里解析出来,DID文档里会有一个service字段,指明ANP服务的URL。

第三步,接收方收到消息后,先从消息头部提取sender_did,然后解析这个DID对应的DID文档。DID文档的获取方式取决于DID方法,如果是did:web,就是访问https://<domain>/.well-known/did.json。

第四步,接收方从DID文档里提取发送方的公钥,用公钥验证消息签名。如果验证通过,说明消息确实来自该DID的持有者,且内容未被篡改。

第五步,接收方检查消息的timestamp是否在可接受的时间窗口内,防止重放攻击。通常窗口设为5分钟,超过就拒绝。

第六步,接收方根据message_type和action执行相应逻辑,构造响应消息,用自己的私钥签名后返回。

这个流程看起来步骤多,但大部分可以封装成库函数,业务代码只需要调用send_message(receiver_did, action, params)和on_message(callback)两个接口。实操时的性能瓶颈主要在DID文档的获取上,如果每次收消息都去远程拉DID文档,延迟会很高。解决方案是加缓存,DID文档通常变化不频繁,可以缓存几分钟到几小时。缓存失效策略可以用TTL加主动刷新,收到签名验证失败时强制刷新一次。

3.3 交互模式:请求-响应之外的更多可能

白皮书里定义的交互模式应该不止请求-响应一种。我根据多智能体系统的常见需求,推测还包括以下几种。

发布-订阅模式:智能体可以订阅某个主题的消息,当有其他智能体发布该主题的消息时,订阅者会收到通知。这种模式适合事件驱动的场景,比如监控智能体订阅“异常事件”主题,一旦有智能体发布异常,监控智能体立即响应。实现上可以用HTTP长轮询或SSE,但更优雅的方式是让ANP端点支持WebSocket升级。

协商模式:两个智能体就某个议题进行多轮对话,直到达成一致。比如任务分配场景,协调者智能体向多个执行者智能体发送任务提案,执行者返回接受、拒绝或还价,协调者根据反馈调整提案。这种模式需要消息里带一个conversation_id,把多轮消息关联起来。

流式模式:对于大结果集或持续输出的场景,接收方可以流式获取结果。比如一个数据分析智能体处理完数据后,不是一次性返回所有结果,而是分批次推送。这需要HTTP的chunked transfer encoding或SSE支持。

这几种模式的组合使用能让ANP覆盖大部分多智能体协作场景。但要注意,模式越多,实现复杂度越高。我的建议是,第一版实现只做请求-响应,把基础打牢,后续再逐步加其他模式。

4. 实操过程与核心环节实现

4.1 环境准备与依赖选型

要跑通一个最小的ANP demo,你需要准备以下环境。

运行时:Python 3.10+或Node.js 18+。我选Python,因为DID和加密相关的库更成熟。需要安装的包包括cryptography(密钥生成和签名)、httpx(异步HTTP客户端)、fastapi(服务端框架)、uvicorn(ASGI服务器)、pydantic(消息模型定义)。

DID方法:用did:web,因为不需要区块链,只需要一个能托管JSON文件的HTTP服务器。本地开发时可以用localhost作为域名,但要注意DID规范对localhost的支持可能不完整,建议用ngrok之类的工具暴露一个公网域名。

密钥算法:用Ed25519,因为密钥短、签名快、安全性高。cryptography库原生支持。

消息序列化:用JSON,配合JCS规范化。Python的json模块默认不保证字段顺序,需要自己实现规范化,或者用canonicaljson库。

4.2 生成DID和密钥对

第一步是生成智能体的身份。Ed25519的私钥是32字节随机数,公钥是32字节。DID的生成规则是did:web:<domain>,比如did:web:agent-a.example.com。DID文档的URL是https://agent-a.example.com/.well-known/did.json。

DID文档的内容大致如下:

{ "id": "did:web:agent-a.example.com", "verificationMethod": [ { "id": "did:web:agent-a.example.com#key-1", "type": "Ed25519VerificationKey2020", "controller": "did:web:agent-a.example.com", "publicKeyMultibase": "z6Mk..." } ], "service": [ { "id": "did:web:agent-a.example.com#anp", "type": "ANPMessaging", "serviceEndpoint": "https://agent-a.example.com/anp" } ] }

publicKeyMultibase是公钥的Multibase编码,前缀z表示base58btc。生成密钥对后,把公钥编码进去,私钥自己保存好。

4.3 实现消息签名与验证

签名流程:先把消息体(不含signature字段)用JCS规范化,得到字节串,然后用Ed25519私钥签名,签名结果用base64url编码后填入signature字段。

验证流程:收到消息后,先提取signature字段,从消息体里移除它,对剩余部分做JCS规范化,然后用发送方DID文档里的公钥验证签名。

这里有个坑:JCS规范化对浮点数的处理有明确规定,但不同库的实现可能有细微差异。我的做法是,消息里尽量避免浮点数,金额用整数分表示,比例用整数千分比表示。这样规范化结果稳定,签名验证不会因为浮点精度问题失败。

4.4 搭建ANP服务端

服务端需要暴露两个端点:/.well-known/did.json返回DID文档,/anp接收ANP消息。

用FastAPI实现的话,/anp端点接收POST请求,请求体是JSON格式的ANP消息。处理逻辑是:解析消息、验证签名、检查时间戳、根据action字段路由到对应的处理函数、构造响应、签名、返回。

响应消息的message_type设为response,status字段表示成功或失败。如果失败,error字段里放错误码和描述。错误码建议用字符串而不是数字,比如INVALID_SIGNATURE、UNKNOWN_ACTION、TIMEOUT,这样更易读。

4.5 客户端发送消息

客户端逻辑更简单:构造消息、签名、POST到接收方的ANP端点、等待响应、验证响应签名、返回结果。

发送时要注意HTTP超时设置。智能体处理任务可能需要几秒到几分钟,超时设太短会误判失败,设太长会阻塞。我的做法是,短任务超时30秒,长任务用异步模式——先发一个submit请求,接收方立即返回一个task_id,然后客户端轮询/anp/status/{task_id}获取进度,最后用/anp/result/{task_id}拿结果。

4.6 端到端测试

测试时至少需要两个智能体,分别跑在不同的端口或不同的机器上。用curl手动构造消息测试签名验证逻辑,用Python脚本测试完整的请求-响应流程。重点测试以下场景:正常请求-响应、签名错误、时间戳过期、未知action、接收方不可达。每个场景都要确认错误处理符合预期,不会泄露内部信息。

5. 常见问题与排查技巧实录

5.1 签名验证失败的几种典型原因

签名验证失败是调试ANP时最常见的问题。我整理了一个排查表。

现象可能原因排查方法
所有消息都验证失败公钥不匹配检查DID文档里的公钥是否与私钥对应
部分消息验证失败JSON规范化不一致对比发送方和接收方的规范化结果
间歇性验证失败时间戳漂移检查双方系统时间是否同步
特定字段修改后失败签名字段范围不对确认签名覆盖了所有关键字段
base64解码失败编码方式不一致统一用base64url,去掉padding

JSON规范化不一致是最隐蔽的问题。比如Python的json.dumps默认会在逗号后加空格,而JavaScript的JSON.stringify不加。如果发送方用Python序列化,接收方用JavaScript验证,规范化结果就不同。解决方案是双方都用同一套JCS实现,或者约定一个固定的序列化库。

5.2 DID文档获取超时或失败

DID文档托管在HTTP服务器上,网络问题会导致获取失败。排查步骤:先用curl直接访问DID文档URL,确认能返回200和正确的JSON;检查DNS解析是否正常;检查是否有防火墙拦截;检查DID文档的Content-Type是否是application/json。

如果DID文档服务器不稳定,可以考虑加CDN或缓存层。但要注意,DID文档里的公钥如果被缓存了,密钥轮换后缓存不会立即失效。解决方案是在DID文档里加updated字段,接收方发现缓存文档的updated时间早于消息时间戳时,强制刷新。

5.3 消息重复与幂等性处理

HTTP重试、网络抖动都可能导致消息重复。ANP消息里的message_id就是用来做去重的。接收方应该维护一个最近处理过的message_id集合,收到重复ID时直接返回上次的响应,不重复执行。

这个集合不能无限增长,需要设置过期时间。我的做法是用一个带TTL的LRU缓存,容量10000条,TTL 1小时。对于超过1小时的消息,即使重复也重新处理,因为业务上通常不会隔这么久重试。

5.4 长任务的处理策略

HTTP请求-响应模式不适合长任务。如果智能体处理一个任务需要5分钟,客户端等5分钟才收到响应,中间任何网络中断都会导致失败。我的策略是异步化:客户端发submit请求,服务端立即返回task_id,然后客户端轮询状态。

轮询间隔要合理,太短浪费资源,太长延迟高。我的经验值是:前10秒每秒轮询一次,10秒到1分钟每5秒一次,1分钟以上每30秒一次。这个退避策略能平衡实时性和资源消耗。

5.5 跨语言实现的兼容性问题

ANP是协议,不同语言都可以实现。但不同语言的加密库、JSON库、HTTP库行为有差异。我踩过的坑包括:Java的BigInteger序列化成JSON时默认输出数字,Python的int也是,但JavaScript的Number精度不够;Go的time.Time序列化格式与Python的datetime不同;Rust的serde_json默认不保证字段顺序。

解决方案是制定一份“实现者指南”,明确规定每个字段的类型、格式、序列化方式。比如时间戳统一用Unix毫秒整数,大整数统一用字符串,JSON字段顺序按字母序排列。这份指南比协议本身更重要,因为它决定了不同实现能否互通。

5.6 安全相关的注意事项

ANP的消息签名能防篡改,但不能防重放。攻击者可以截获一条合法消息,原样重发。时间戳窗口能缓解这个问题,但窗口内重放仍然可能。更严格的方案是接收方维护一个message_id黑名单,但黑名单的同步是个问题。

另一个安全问题是DID文档的托管安全。如果攻击者控制了DID文档的托管服务器,就能替换公钥,从而伪造签名。解决方案是用HTTPS加证书固定,或者把DID文档的哈希写到DNS TXT记录里,接收方验证哈希后再使用。

还有一个容易被忽视的问题:错误消息可能泄露内部信息。比如签名验证失败时,不要返回“公钥不匹配,期望的指纹是xxx”,只返回“签名验证失败”即可。详细的错误信息应该记在服务端日志里,不返回给客户端。

6. 我对ANP落地的一些个人判断

ANP的设计思路是对的,它抓住了多智能体协作的核心矛盾——身份和消息的标准化。但它毕竟是一份草案,距离生产级可用还有距离。我在实际折腾过程中最大的体会是:协议本身不复杂,复杂的是生态。DID的解析、密钥的管理、消息的规范化、错误的处理,每一项都需要大量工程投入。如果你只是想在自己的系统里让几个智能体协作,不一定非要上ANP,用简单的HTTP+JSON+API Key也能跑。但如果你要做的是一个开放平台,让第三方智能体接入,那ANP的DID身份体系和标准化消息格式就很有价值了。

最后分享一个我在调试时常用的小技巧:写一个“消息录制回放”工具,把所有进出的ANP消息原样存到文件里,包括签名和DID文档快照。出问题时,用这个工具离线重放,能快速定位是签名问题、序列化问题还是业务逻辑问题。这个工具帮我省了大量调试时间,建议你也搭一个。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询