资讯动态

Agent Network Protocol 技术白皮书拆解:DID 身份、能力描述与通信层设计

发布时间:2026/10/27 6:22:40 来源:尧图企业网站定制
1. 为什么需要一套专属于智能体的通信协议过去两年我一直在做多智能体协作相关的项目从最早的一个主 Agent 调几个工具函数的玩具级实现到后来几十个 Agent 跨服务、跨团队协作的复杂系统踩过的坑可以说能写一本书。最让我头疼的从来不是模型能力不够而是智能体之间怎么说话这件事——每个团队都有自己的消息格式A 团队用 JSON-RPCB 团队用自定义的 HTTP 回调C 团队干脆把状态塞进数据库轮询。结果就是每接入一个新 Agent就要写一层适配代码系统越做越像一堆用胶带粘起来的积木。Agent Network Protocol后面我简称 ANP这份技术白皮书草案本质上就是在回答一个问题当智能体数量从几个涨到成千上万、从单机涨到跨组织的时候它们之间应该用什么样的普通话来交流这不是又一个 RPC 框架也不是又一个消息队列的封装它想解决的是更底层的东西——身份怎么认、能力怎么描述、消息怎么路由、信任怎么建立。如果你正在做多智能体系统、Agent 编排平台或者只是单纯好奇智能体互联网这个概念到底靠不靠谱这份草案值得认真读一遍。我先把结论放前面ANP 的核心思路是把**身份层基于 W3C DID、描述层Agent 能力自描述、通信层消息与路由**这三件事拆开各管各的。这个分层看着朴素但恰恰是过去很多方案翻车的地方——它们总想用一个协议解决所有问题最后哪个都没解决好。下面我按自己的理解把这份草案拆开揉碎讲一遍顺带补上一些白皮书里没写透、但实操中一定会遇到的细节。2. ANP 的整体设计思路与分层拆解2.1 三层架构身份、描述、通信各司其职ANP 最核心的设计决策是把整个协议栈切成三层而且这三层是松耦合的。我画不了图也不打算画但你可以这样理解身份层解决你是谁。用的是 W3C 的 DIDDecentralized Identifier去中心化标识符。每个 Agent 有一个 DID这个 DID 不依赖任何中心化服务器就能验证公钥信息直接编码在里面或者通过 DID Document 获取。描述层解决你能干什么。每个 Agent 对外暴露一份能力描述Agent Description里面写清楚它支持哪些技能、输入输出格式是什么、调用需要什么权限。通信层解决消息怎么送达。定义消息的封装格式、寻址方式、同步/异步语义以及错误处理。为什么这么分我举个实际例子你就懂了。之前我做过一个跨团队的 Agent 调用系统最开始大家约定所有 Agent 都暴露一个/invoke接口参数是{skill, input}。听起来挺统一的对吧结果上线两周就崩了——有的 Agent 需要鉴权有的不需要有的技能是同步返回有的是异步回调有的输入是文本有的是结构化数据。最后那个统一接口变成了一坨 if-else。ANP 的分层恰好把这些问题隔离开了身份层管鉴权DID 验证描述层管能力发现不用硬编码 if-else通信层管消息语义同步异步由协议规定不由业务方自己约定。每一层只解决一个问题层与层之间通过标准接口交互这是它能 scale 的根本原因。2.2 为什么选 W3C DID 而不是自建身份体系这是我在读草案时第一个停下来思考的点。自建一套 ID 体系多简单啊发个 UUID 不就完了为什么要引入 DID 这么重的东西答案在于跨组织信任。如果你的 Agent 系统只在自己公司内部跑UUID 确实够了。但 ANP 的目标是智能体互联网——意味着 A 公司的 Agent 要能调用 B 公司的 Agent甚至个人开发者的 Agent 要能被企业 Agent 调用。这时候问题就来了B 公司凭什么相信这个请求真的来自 A 公司的 Agent传统方案是搞一个中心化的认证服务大家都去它那里验证。但这又回到了中心化的老路而且跨组织场景下谁来运营这个中心谁为它背书DID 的价值就在这里它把身份验证变成了一个可以离线完成的密码学操作。A 公司的 Agent 用自己的私钥签名B 公司拿到 DID Document里面包含公钥本地就能验证签名不需要问任何第三方。DID Document 本身可以存在区块链上也可以存在一个普通的 Web 服务器上did:web 方法灵活性很高。注意DID 的去中心化程度取决于你选的 DID 方法。did:web 其实还是依赖域名所有者的只是把信任锚点从某个认证中心换成了域名。选型时要根据实际信任模型来定别被去中心化这个词忽悠了。2.3 能力自描述让 Agent 自己报菜名描述层是我个人觉得最实用的部分。它的核心思想是Agent 不应该被硬编码在调用方的代码里而应该自己声明自己能干什么。具体来说每个 Agent 在注册或者被查询时返回一份结构化的描述大致包含字段含义举例did身份标识did:web:example.com:agent:translatorname人类可读名称中英翻译助手skills技能列表translate, detect_languageinput_schema输入格式JSON Schemaoutput_schema输出格式JSON Schemaendpoint通信地址https://... 或 ANP 原生地址auth鉴权要求需要 DID 签名 / 公开这份描述的好处是调用方可以动态发现能力而不是靠人工对接。我实测过一个场景一个编排 Agent 需要翻译能力它不需要知道具体哪个 Agent 提供翻译只需要在网络上查询谁有 translate 技能拿到描述后按 schema 构造请求即可。这跟微服务里的服务发现很像但粒度更细——细到技能级别。2.4 通信层消息语义的标准化通信层定义了消息的基本结构。草案里给的是一个类似信封的格式包含发送方 DID、接收方 DID、消息类型、载荷、签名、时间戳等。这里有个设计取舍值得说ANP 没有强制规定底层传输协议。也就是说ANP 消息可以通过 HTTP 传可以通过 WebSocket 传甚至可以通过消息队列传。协议只规定消息长什么样和收到消息后怎么处理不规定怎么把消息送过去。这个设计我觉得很聪明因为它避免了和现有基础设施打架。你公司已经有 Kafka那就用 Kafka 传 ANP 消息你只有 HTTP那就用 HTTP。协议不绑架你的技术栈。但代价是互操作性会打折扣。两个都声称支持 ANP 的系统如果一个用 HTTP 一个用消息队列它们之间还是需要一个网关来桥接。草案里提到了ANP Gateway的概念但细节还比较粗这是后续需要重点关注的落地难点。3. 核心机制深度解析与实操要点3.1 DID 的生成与验证从零跑通一个例子光讲概念没意思我带你实际走一遍 DID 的生成和验证流程。这里用 did:web 方法因为它最容易上手不需要碰任何链上操作。第一步生成密钥对。用你熟悉的任何工具都行我这里用 openssl 演示# 生成 Ed25519 密钥对DID 常用曲线 openssl genpkey -algorithm ED25519 -out private.pem openssl pkey -in private.pem -pubout -out public.pem第二步构造 DID Document。假设你的域名是agent.example.com你想给翻译 Agent 分配 DIDdid:web:agent.example.com:translator。那么你需要在https://agent.example.com/translator/did.json放一份这样的文档{ context: https://www.w3.org/ns/did/v1, id: did:web:agent.example.com:translator, verificationMethod: [{ id: did:web:agent.example.com:translator#key-1, type: Ed25519VerificationKey2020, controller: did:web:agent.example.com:translator, publicKeyMultibase: z6Mk...你的公钥编码 }], authentication: [did:web:agent.example.com:translator#key-1], service: [{ id: did:web:agent.example.com:translator#anp, type: ANPMessaging, serviceEndpoint: https://agent.example.com/translator/anp }] }第三步验证。调用方拿到一个签名消息后先根据发送方 DID 解析出域名拉取 did.json取出公钥然后本地验证签名。整个过程不需要任何中心化服务参与。实操心得did:web 的解析依赖 HTTPS 和域名所以域名过期、证书失效、DNS 被劫持都会导致验证失败。生产环境一定要监控这些基础设施别以为配好了就一劳永逸。我踩过一次坑域名续费忘了结果整个 Agent 集群互相认证全部失败排查了半天才定位到是域名问题。3.2 能力描述的 schema 设计别把 schema 写成天书描述层最容易翻车的地方是 schema 设计。我见过太多团队把 input_schema 写成几百行的 JSON Schema嵌套七八层调用方看了直接放弃。ANP 草案里没有强制 schema 的复杂度但根据我的经验能力描述应该遵循最小可用原则。具体建议扁平优先能一层解决就别嵌套。{text: string, target_lang: string}比{payload: {content: {text: string}, config: {lang: string}}}好一百倍。必填项要少只把真正必需的字段标为 required其他都给默认值。枚举值要明确如果某个字段只能是几个固定值用 enum 列出来别让调用方猜。给例子在描述里附一个完整的请求示例比任何 schema 都直观。我自己的做法是在 Agent Description 里额外加一个examples字段放 2-3 个真实的请求-响应对。实测下来调用方的接入时间能缩短一半以上。3.3 消息封装与签名安全性的关键落点通信层的消息封装核心是防篡改和防重放。ANP 草案里的消息大致长这样{ from: did:web:a.example.com:agent1, to: did:web:b.example.com:agent2, type: request, id: msg-uuid-1234, timestamp: 2025-01-15T10:30:00Z, payload: { ... }, signature: base64-encoded-signature }签名覆盖的范围很关键。必须签名的字段包括from、to、type、id、timestamp、payload。少签任何一个都会留下攻击面。比如只签 payload 不签 to攻击者就能把消息转发给别的 Agent只签 to 不签 timestamp就能重放旧消息。时间戳的容差窗口也要设好。我一般设±5 分钟太短了时钟不同步会误判太长了重放窗口太大。同时配合消息 id 做去重缓存缓存时间至少覆盖两倍容差窗口。注意签名算法要和 DID Document 里声明的 verificationMethod 类型匹配。如果你 Document 里写的是 Ed25519VerificationKey2020签名就必须用 Ed25519别用 RSA 签否则验证方会直接拒绝。3.4 同步与异步两种调用模式的取舍ANP 支持同步和异步两种调用模式这个设计很务实。同步模式适合快速返回的技能比如翻译、分类异步模式适合长任务比如视频处理、复杂推理。同步模式的流程是请求发出后调用方阻塞等待响应超时时间由描述层声明。异步模式则是请求发出后立即返回一个 task_id调用方后续用 task_id 轮询或者通过回调接收结果。我的经验是默认用异步除非技能明确是毫秒级返回的。原因很简单同步调用会占用连接资源Agent 数量一多连接池瞬间打满。异步虽然多一次交互但资源利用率高得多。ANP 草案里没有强制默认值这个决策留给实现方我觉得是对的因为不同场景差异太大。4. 从零搭建一个最小 ANP 网络的完整实操4.1 环境准备与依赖选型要跑通一个最小的 ANP 网络你至少需要两个 Agent一个提供技能服务方一个调用技能消费方。我下面用 Python 演示因为生态最成熟。依赖清单组件用途推荐选型DID 库生成和解析 DID各语言官方 DID 库HTTP 框架暴露 ANP endpointFastAPI / Flask签名库Ed25519 签名验证cryptographyJSON Schema校验能力描述jsonschema存储缓存消息 id 防重放Redis / 内存字典选型逻辑FastAPI 自带异步和 schema 校验省事cryptography 是 Python 里最靠谱的密码学库Redis 用于生产环境防重放本地测试用内存字典就够。4.2 服务方 Agent 的实现步骤服务方要做三件事暴露 DID Document、暴露能力描述、处理 ANP 消息。第一步暴露 DID Document。在 FastAPI 里加一个路由app.get(/.well-known/did.json) def did_document(): return { context: https://www.w3.org/ns/did/v1, id: did:web:localhost:translator, verificationMethod: [{ id: did:web:localhost:translator#key-1, type: Ed25519VerificationKey2020, controller: did:web:localhost:translator, publicKeyMultibase: PUBLIC_KEY_MULTIBASE }], service: [{ id: did:web:localhost:translator#anp, type: ANPMessaging, serviceEndpoint: http://localhost:8000/anp }] }第二步暴露能力描述。加一个/agent-description路由返回前面说的那份结构化描述。第三步处理 ANP 消息。核心逻辑是验证签名 → 检查时间戳 → 检查消息 id 是否重复 → 执行技能 → 返回签名响应。app.post(/anp) async def handle_anp(msg: dict): # 1. 验证签名 if not verify_signature(msg): return {error: invalid signature} # 2. 检查时间戳容差 if abs(now() - parse(msg[timestamp])) 300: return {error: timestamp out of window} # 3. 防重放 if redis.exists(fmsg:{msg[id]}): return {error: duplicate message} redis.setex(fmsg:{msg[id]}, 600, 1) # 4. 执行技能 result dispatch(msg[payload]) # 5. 签名并返回 return sign_response(result)4.3 消费方 Agent 的调用流程消费方要做的是发现能力 → 构造请求 → 签名 → 发送 → 验证响应。发现能力这一步可以硬编码服务方 DID也可以走一个目录服务查询。最小实现里硬编码就够了。构造请求时payload 要严格符合服务方声明的 input_schema否则会被拒。签名这一步最容易出错。签名的内容必须是规范化后的字节串不能直接对 JSON 字符串签因为 JSON 的键顺序、空格在不同实现里可能不一样。标准做法是用 JCSJSON Canonicalization Scheme先规范化再签名。我见过太多因为没做规范化导致签名验证失败的案例。4.4 联调与验证怎么确认真的跑通了跑通的标准不是没报错而是这几条消费方能成功解析服务方的 DID Document拿到公钥。消费方构造的请求能被服务方验证通过。服务方的响应能被消费方验证通过。重放同一个消息服务方能正确拒绝。篡改消息任意字段签名验证失败。第 4、5 条是安全性的底线一定要测。我一般会写一个专门的测试脚本模拟攻击场景确保防护生效。5. 常见问题与排查技巧实录5.1 签名验证失败的五种典型原因这是最高频的问题我整理成速查表现象可能原因排查方法一直验证失败JSON 未规范化打印签名前的字节串对比偶发失败时钟不同步检查 NTP 服务换机器就失败公钥编码方式不一致确认 multibase 前缀特定消息失败签名字段范围不一致核对双方签名覆盖字段全部失败DID Document 未更新检查 did.json 是否可访问实操心得签名问题排查最有效的办法是把签名前的原始字节串打印出来双方对比。只要字节串一致签名算法正确就一定能验证通过。我靠这一招解决过无数次玄学签名失败。5.2 能力发现失败的排查思路能力发现失败通常不是协议问题而是描述文档的问题。常见的有schema 写错了导致解析失败、endpoint 地址写错、DID 解析不到。排查顺序建议是先手动 curl 一下 did.json 和 agent-description确认能拿到再用 schema 校验工具验证描述文档格式最后才怀疑协议实现。5.3 性能瓶颈消息量上来之后怎么办小规模测试一切正常一上量就崩这是分布式系统的通病。ANP 场景下的瓶颈通常在三处签名验证的 CPU 开销、DID Document 的重复解析、防重放存储的 IO。优化手段DID Document 做本地缓存带 TTL别每次都远程拉签名验证可以批量做或者用更快的曲线防重放用 Redis 的 setnx 原子操作别用先查后写。我实测下来加了 DID 缓存之后QPS 能提升 3-5 倍。5.4 跨组织互操作的真实难点最后说一个草案里没怎么展开、但落地一定会遇到的问题跨组织时双方的 ANP 实现细节可能不一致。比如 A 用 HTTP 传输B 用消息队列A 的 schema 校验严格B 的宽松。这时候就需要一个ANP 网关来做协议转换和 schema 适配。我的建议是跨组织对接前先做一次协议一致性测试把双方的消息格式、签名算法、schema 校验规则都对齐一遍。别等到上线了才发现对不上那时候排查成本高十倍。6. 我对 ANP 落地的一些个人判断写到这里我想说点掏心窝的话。ANP 这份草案的方向我是认可的分层清晰、身份用 DID、能力自描述这些都是经过验证的正确思路。但它离能大规模落地还有距离最大的不确定性在生态——协议再好没人用就是废纸。从我的经验看ANP 最可能先落地的场景是企业内部的多 Agent 协作因为信任边界清晰不需要处理复杂的跨组织问题。等企业内部跑顺了再往外扩展到跨组织这条路更现实。如果你现在就想上手我的建议是先在自己的项目里实现一个最小闭环把 DID 生成、能力描述、消息签名这三件事跑通别一上来就追求完整实现。跑通之后你自然就知道哪些地方需要补、哪些设计需要调整了。最后分享一个小技巧调试 ANP 消息时把每条消息的完整生命周期构造、签名、发送、接收、验证、处理都打上带 trace_id 的日志。多 Agent 场景下没有 trace_id 的日志等于没有日志这是我踩了无数次坑才养成的习惯。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑