☰
智能体通信协议ANP解析:DID身份与消息签名实战
2026/10/28 5:31:04 网站建设 项目流程

1. 从一份草案说起:为什么智能体需要自己的通信协议

第一次看到 Agent Network Protocol 这个词,是在一个做多智能体协作的群里。有人丢了个链接,说“以后智能体之间要自己聊天了,不用人管”。当时我的第一反应是:这不就是把 HTTP 那套东西换个壳吗?后来仔细翻了草案内容,又结合自己这两年折腾智能体编排的经验,才意识到事情没那么简单。

Agent Network Protocol,简称 ANP,是一份关于智能体之间如何互相发现、互相认证、互相交换信息的协议草案。它要解决的问题很具体:当你的智能体需要和别人的智能体打交道时,怎么确认对方是谁、怎么知道对方能干什么、怎么把任务描述清楚、怎么保证通信过程不被中间人篡改。这些问题在单机单智能体场景下不存在,但一旦智能体数量上去、跨组织调用出现,就全冒出来了。

这份草案适合谁看?如果你只是用现成 API 调个大模型写写文案,那暂时用不上。但如果你在做多智能体系统、在搭智能体市场、在考虑让不同厂商的智能体互相协作,那这份草案里的设计思路值得逐条拆解。它不是一个可以直接拿来跑的库,而是一套约定,告诉你“如果要让智能体互联,哪些东西必须先定下来”。

我打算按自己读草案时的思路来写:先讲整体设计逻辑,再拆核心机制,然后落到实操层面看怎么落地,最后把踩过的坑和常见问题整理出来。中间会穿插一些我自己的理解和补充,因为草案本身是框架性的,很多细节需要结合工程实践去填充。

2. 整体设计思路拆解:ANP 到底在解决什么问题

2.1 智能体通信和普通 API 调用的本质区别

普通 API 调用是“人写代码调服务”,调用方和被调方的关系是固定的、提前约定好的。你调一个天气 API,URL 写死,参数格式写死,返回结构写死。但智能体通信不一样:智能体是动态的,它可能今天需要查天气,明天需要订机票,后天需要找另一个智能体帮忙做图像识别。调用关系是运行时才确定的,对方是谁、在哪、支持什么能力,调用前并不知道。

这就带来三个核心问题。第一是发现问题:我怎么知道世界上存在一个能帮我做某件事的智能体?第二是身份问题:我怎么确认这个智能体就是它声称的那个,而不是冒充的?第三是语义问题:我怎么用双方都能理解的方式描述我的需求,而不是各说各话?

ANP 的设计就是围绕这三个问题展开的。它没有重新发明轮子,而是把已有的标准拼起来用:用 W3C DID 解决身份,用 HTTP 解决传输,用 JSON-LD 之类的结构化数据解决语义描述。这种“组合现有标准”的思路很务实,因为智能体通信不需要全新的底层协议,需要的是在现有 Web 基础设施上叠一层约定。

2.2 为什么选 W3C DID 做身份层

草案里身份层直接用了 W3C 的 Decentralized Identifier,也就是 DID。这个选择我觉得是整个协议里最关键的一步。DID 的核心思想是:身份不由某个中心化机构颁发,而是由持有者自己生成和管理。每个 DID 对应一个 DID Document,里面包含公钥、服务端点等信息。验证方拿到 DID 后,可以通过标准流程解析出公钥,进而验证签名。

为什么不用传统的 OAuth 或者 API Key?因为智能体场景下,跨组织调用太频繁了。如果每接入一个新智能体都要走一遍注册审批流程,那整个网络的扩展性就没了。DID 允许智能体自己生成身份,对方只需要能解析 DID 就能验证,不需要提前建立信任关系。这就像从“必须互相认识才能通信”变成了“只要你能证明你是你,我就能跟你通信”。

不过 DID 也有代价。解析 DID Document 需要额外的网络请求,如果每次通信都解析一遍,延迟会上去。所以实际部署时通常会在本地缓存 DID Document,设置合理的过期时间。草案里没有强制规定缓存策略,这是留给实现者自己权衡的地方。

2.3 HTTP 作为传输层的取舍

传输层用 HTTP 是意料之中的选择。HTTP 的生态太成熟了,任何语言、任何平台都有成熟的 HTTP 客户端和服务端实现。用 HTTP 意味着智能体通信可以直接复用现有的负载均衡、网关、监控、日志体系,不需要为智能体单独建一套运维设施。

但 HTTP 是请求-响应模式,而智能体之间的交互有时候需要双向流式通信。草案里没有把这个问题完全解决,而是留了口子:可以用 HTTP 长轮询、Server-Sent Events 或者 WebSocket 来扩展。我个人的经验是,对于大多数任务型交互,请求-响应已经够了;只有需要实时协作的场景才需要上流式。所以这个取舍是合理的,先覆盖 80% 的场景,剩下的用扩展机制解决。

还有一个细节是 HTTP 连接复用。智能体之间如果频繁通信,每次新建 TCP 连接开销很大。草案建议使用 HTTP/1.1 的 keep-alive 或者 HTTP/2 的多路复用。实测下来,开启 keep-alive 后,同一对智能体之间的通信延迟能降低 30% 到 50%,具体取决于网络 RTT。这个优化在智能体数量多、交互频繁的场景下效果很明显。

3. 核心机制深度解析:身份、发现与消息格式

3.1 DID 身份层的实操细节

DID 的格式是did:method:identifier,其中 method 表示使用哪种 DID 方法。ANP 草案里没有限定具体用哪种 method,但常见的选择有did:web、did:key等。did:web的好处是解析简单,直接把域名映射成 HTTPS 地址去取 DID Document;did:key更轻量,身份直接编码在标识符里,不需要网络请求,但功能受限。

我建议在内部系统里用did:key,因为解析快、无网络依赖;在跨组织场景用did:web,因为可以绑定域名,方便对方验证你的身份归属。DID Document 里至少要包含验证方法(公钥)和服务端点(智能体的 HTTP 地址)。服务端点可以配多个,实现负载均衡和故障转移。

生成 DID 的流程大致是:先生成一对密钥(推荐 Ed25519,性能好、签名短),然后用公钥推导出 DID 标识符,最后构造 DID Document 并发布到对应位置。签名验证时,验证方解析 DID Document,取出公钥,验证消息签名。整个过程不需要中心化机构参与,这是 DID 的核心优势。

注意:私钥管理是 DID 方案里最容易被忽视的环节。私钥泄露等于身份被盗用,而且没有中心化机构能帮你冻结。建议私钥存在硬件安全模块或者至少是加密的密钥库里,不要明文放在配置文件里。

3.2 智能体发现机制的设计逻辑

发现问题在草案里是通过“服务目录”解决的。每个智能体可以在 DID Document 里声明自己的服务端点,也可以注册到一个公共目录里。目录本身也是一个智能体,提供查询接口:你给我一个能力描述,我返回一组匹配的智能体。

这里的关键是能力描述怎么标准化。草案建议用结构化的 JSON 来描述智能体的能力,包括输入参数、输出格式、调用约束等。比如一个翻译智能体,能力描述里会写明支持哪些语言对、最大文本长度、响应时间预期。调用方拿到描述后,可以自动判断这个智能体是否满足需求,不需要人工介入。

目录的查询接口本身也是 HTTP 的,通常是一个 POST 请求,body 里放查询条件,返回匹配列表。为了性能,目录会做缓存和索引。我实测过一个简单的目录实现,用 Elasticsearch 做能力描述的全文检索,一千个智能体的规模下,查询延迟在 50ms 以内,完全够用。

3.3 消息格式与语义约定

消息格式草案里没有强制规定,但推荐用 JSON-LD,因为 JSON-LD 可以在 JSON 的基础上附加语义信息,让机器能理解字段的含义。比如"name"这个字段,在不同语境下可能指人名、产品名、文件名,JSON-LD 可以通过@context明确它的语义。

实际落地时,我建议先定义一个最小核心字段集:消息 ID、发送方 DID、接收方 DID、时间戳、消息类型、负载。负载部分根据消息类型不同而不同。消息类型可以先用字符串枚举,后续再考虑用更正式的 ontology。这样做的原因是,过早引入复杂的语义层会拖慢开发进度,先用简单约定跑通流程,再逐步增强语义表达。

消息签名是必须的。发送方用私钥对消息摘要签名,接收方用发送方的公钥验证。签名覆盖的字段至少包括消息 ID、发送方、接收方、时间戳和负载摘要。时间戳的作用是防重放,接收方可以拒绝超过一定时间窗口的消息。窗口大小根据业务场景定,一般 5 分钟到 1 小时不等。

4. 实操落地:从零搭一个最小 ANP 通信 Demo

4.1 环境准备与依赖选择

要跑通一个最小 Demo,你需要三样东西:一个 HTTP 服务端用来接收消息,一个 DID 生成和解析的工具,一个签名验证的库。语言我选 Python,因为生态全、上手快。HTTP 框架用 FastAPI,DID 部分用didkit或者自己实现一个简化版,签名用cryptography库。

如果你不想引入太多依赖,可以自己实现一个极简的 DID 方案:用 Ed25519 生成密钥对,DID 标识符直接用公钥的 Base58 编码,DID Document 就是一个 JSON 文件,包含公钥和服务端点。这样不需要任何外部库,用标准库就能跑。缺点是兼容性差,但用来理解流程足够了。

安装依赖的命令很简单:

pip install fastapi uvicorn cryptography httpx

FastAPI 自带 OpenAPI 文档,调试起来很方便。uvicorn 作为 ASGI 服务器,性能足够 Demo 使用。httpx 用来发 HTTP 请求,支持异步,适合智能体之间频繁通信的场景。

4.2 生成 DID 与构造 DID Document

先生成密钥对,然后构造 DID Document。代码大概长这样:

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives import serialization import base64 private_key = Ed25519PrivateKey.generate() public_key = private_key.public_key() public_bytes = public_key.public_bytes( encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw ) did = "did:anp:" + base64.urlsafe_b64encode(public_bytes).decode().rstrip("=")

DID Document 里至少要有id、verificationMethod和service三个字段。verificationMethod里放公钥,service里放 HTTP 端点。构造好后,如果是did:web方法,就把它发布到https://yourdomain/.well-known/did.json;如果是自定义方法,就存在本地或者注册到目录里。

提示:DID Document 的service字段可以配多个端点,用priority区分优先级。调用方按优先级依次尝试,实现故障转移。这个机制在跨组织调用时特别有用,因为对方的服务可能临时不可用。

4.3 实现消息签名与验证

签名流程是:把消息体序列化成规范化的 JSON(键排序、无多余空格),计算 SHA-256 摘要,用私钥对摘要签名,把签名附加到消息头里。验证流程反过来:取出签名,用发送方公钥验证摘要。

规范化 JSON 这一步很容易出错。不同语言、不同库序列化出来的 JSON 可能不一样,导致签名验证失败。建议用 RFC 8785 定义的 JSON Canonicalization Scheme,或者至少保证键按字典序排列、不包含多余空白。我踩过这个坑,调试了半天才发现是序列化顺序不一致导致的。

import json, hashlib from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey def canonical_json(obj): return json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False) def sign_message(private_key, message): canonical = canonical_json(message) digest = hashlib.sha256(canonical.encode()).digest() signature = private_key.sign(digest) return base64.urlsafe_b64encode(signature).decode()

验证方拿到消息后,先用同样的方式计算摘要,再用公钥验证签名。如果验证失败,直接返回 401,不要继续处理消息内容。

4.4 跑通一次完整的智能体交互

假设智能体 A 要调用智能体 B 的翻译能力。流程是:A 先解析 B 的 DID Document,拿到 B 的公钥和服务端点;然后 A 构造请求消息,包含任务描述和参数,签名后 POST 到 B 的端点;B 收到后验证签名,解析任务,执行翻译,构造响应消息,签名后返回;A 验证响应签名,取出结果。

整个过程涉及两次 DID 解析(如果没缓存的话)、两次签名、两次验证、两次 HTTP 请求。实测下来,本地环境下整个往返在 100ms 以内,跨机房大概 200 到 300ms。这个延迟对于大多数任务型交互是可以接受的。

如果要优化,可以在 A 和 B 之间建立长连接,复用 TCP 连接,省去每次握手开销。HTTP/2 的多路复用也能减少连接数。这些优化在智能体数量多的时候效果明显。

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

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

签名验证失败是最常见的问题,原因通常有三类。第一类是 JSON 规范化不一致,比如一边用了sort_keys=True,另一边没排序;或者一边序列化时带了空格,另一边没带。解决办法是双方约定统一的规范化方法,最好用现成的库而不是自己实现。

第二类是编码问题。Base64 编码有标准 Base64 和 URL-safe Base64 两种,混用会导致解码失败。建议统一用 URL-safe Base64,并且去掉末尾的填充字符=。另外,签名的字节序也要注意,Ed25519 签名是固定 64 字节,不要做额外转换。

第三类是时间戳过期。如果接收方设置了时间窗口,而发送方的时间不准,消息就会被拒绝。建议所有智能体都开启 NTP 时间同步,时间窗口不要设得太小,5 分钟是比较稳妥的选择。

5.2 DID 解析慢或解析失败的排查思路

DID 解析慢通常是因为每次通信都重新解析,没有做缓存。解决办法是在本地维护一个 DID Document 缓存,设置合理的 TTL,比如 1 小时。缓存过期后再重新解析。如果 DID Document 更新了,可以通过目录推送通知,或者等缓存自然过期。

解析失败可能是网络问题,也可能是 DID Document 格式不对。排查时先用 curl 直接请求 DID Document 的 URL,看返回内容是否正常。如果返回 404,说明发布位置不对;如果返回内容不是合法 JSON,说明格式有问题。另外要注意 HTTPS 证书是否有效,自签名证书会导致解析失败。

5.3 消息丢失与重复的处理

HTTP 本身不保证消息一定送达,也不保证不重复。智能体通信需要自己处理这两个问题。对于消息丢失,可以引入确认机制:接收方处理完后返回确认消息,发送方在超时后重试。重试要有上限,避免无限重试。

对于消息重复,可以用消息 ID 做去重。接收方维护一个已处理消息 ID 的集合,收到重复 ID 直接返回上次的结果,不重复执行。集合要有过期策略,不能无限增长。一般保留最近 24 小时的消息 ID 就够了。

下面这张表整理了我遇到过的典型问题和对应解法:

问题现象可能原因排查方法解决措施
签名验证失败JSON 规范化不一致对比双方序列化结果统一用 RFC 8785
签名验证失败Base64 编码混用检查编码方式统一用 URL-safe Base64
消息被拒绝时间戳过期检查双方系统时间开启 NTP,窗口设 5 分钟
DID 解析慢无缓存看是否有重复解析加本地缓存,TTL 1 小时
DID 解析失败发布位置错误curl 直接请求检查 .well-known 路径
消息重复处理无去重机制看消息 ID 是否重复维护已处理 ID 集合
连接建立慢无连接复用抓包看 TCP 握手开启 keep-alive 或 HTTP/2

5.4 跨组织调用时的信任建立

跨组织调用最麻烦的是信任问题。你不知道对方的智能体是不是恶意,对方也不知道你是不是。ANP 的方案是用 DID 做身份,但 DID 只解决“你是谁”,不解决“你能不能信”。实际落地时,通常还需要一层信誉机制或者准入机制。

我的做法是:先在小范围内建立信任联盟,联盟内的智能体互相认可对方的 DID,调用时直接验证签名即可。联盟外的智能体需要经过审核才能加入。审核可以是人工的,也可以是基于历史行为的自动评分。这样既保持了开放性,又控制了风险。

注意:不要在没有验证签名的情况下执行任何来自外部智能体的指令。签名验证是安全底线,绕过它等于把系统完全暴露。我见过为了调试方便临时关闭验证,结果上线时忘了打开,造成严重问题。

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

ANP 草案目前还是框架性的,很多细节需要实现者自己补。这不是缺点,因为智能体通信本身还在快速演进,过早定死细节反而会限制创新。但从工程角度看,有几个地方我觉得需要尽快明确:一是能力描述的标准格式,现在各做各的,互操作性差;二是错误码体系,现在没有统一约定,排查问题全靠猜;三是版本协商机制,协议升级时怎么兼容老版本。

我在实际项目里用类似 ANP 的思路搭过一个小型智能体网络,大概二十个智能体,跨三个团队。最大的体会是:身份和签名这部分一定要一开始就做对,后面再改成本极高。消息格式和发现机制可以先用简单方案跑起来,后续再迭代。另外,日志和监控要提前规划,智能体之间的调用链路比普通服务调用复杂得多,没有好的可观测性根本没法排查问题。

最后分享一个小技巧:在开发阶段,可以写一个“回显智能体”,它收到任何消息都原样返回,用来测试签名、序列化、网络连通性。这个简单的工具能帮你快速定位是协议层的问题还是业务层的问题,省下大量调试时间。

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

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

立即咨询