这篇“Agent Network Protocol 技术白皮书(草案)”出现的时间点很微妙——大家手里已经攒了一堆能聊天、能写代码、能查资料的Agent,但彼此之间基本是孤岛。各家Agent各有各的工具调用方式、消息格式和身份体系,让它们协作就像让说不同方言的人开圆桌会议,得靠翻译、靠磨合、靠各种临时的胶水代码。ANP草案想做的,就是给Agent之间定一套“普通话”。
如果你正在做多Agent系统、Agent平台,或者准备让自家Agent接入外部生态,这份草案值得细读。它不解决“单个Agent怎么更聪明”,解决的是“一群Agent怎么好好说话”。这篇文章我结合草案思路,从设计动机、协议分层、关键机制到动手搭最小链路,把核心内容拆开讲,也会补上一些实践中大概率踩到的坑。
1. 白皮书的真实动机:Agent之间为什么需要一套网络协议
先想一个实际场景。你有一个个人助理Agent,它能帮你管理日程、读邮件、订机票。现在你希望它帮你完成一次跨机构协作:让翻译Agent把合同翻成英文,再让法律Agent检查条款风险,最后让财务Agent估算预算。如果每个Agent都是独立的API孤岛,你的助理Agent就得分别对接三家不同的接口规范、鉴权方式和返回结构,每接一个就写一套适配逻辑。Agent数量一多,这种两两之间的“点对点适配”会呈指数级爆炸,维护成本高到没法看。
1.1 从API调用到Agent互联:一个需求层次的变化
过去的集成方式是把Agent当成API来调,本质还是“中心化调度+点对点集成”。调用方要知道每个Agent的地址、参数格式、鉴权方式,写死调用关系。这种模式在系统规模小、Agent数量有限时没什么问题,但一旦Agent变成一种常态化的数字服务,问题就来了:能力发现靠人工对接,消息格式各自为政,状态管理散落各处,失败重试逻辑每对接一个都要重新实现。
ANP想要解决的,是把“调用一个API”升级成“进入一个网络”。类比一下:没有TCP/IP之前,两台计算机要通信就得拉专线、对参数、约定信号格式;有了网络协议之后,设备只要入网,就能被找到、能通信、能协作。Agent网络协议做的事情类似——给Agent一个标准化的身份、一套标准化的消息语言、一套可被发现的能力目录,以及一套可靠的消息投递机制。
1.2 “白皮书草案”意味着什么:协议仍处于快速演进期
看这份草案,要有正确预期。它不像是RFC那种已冻结多年的成熟标准,更像是一个正在快速迭代的设计文档。这意味着三件事:第一,协议核心骨架已经划定——身份、发现、路由、消息、安全这些层次都在,方向是清晰的;第二,很多细节(比如消息包大小的上限、重试退避的具体算法、加密套件的强制项)还在讨论中;第三,现在基于草案做的实现,大概率需要跟着版本演进做兼容性调整。
对开发者来说,草案期恰恰是入局的好时机。协议还没被某一家大厂绑死,你的实现有机会影响后续标准走向。但也意味着要做兼容性设计——不要把草案里的字段当作不可变契约,要做好版本协商和字段扩展的准备。
2. 核心设计拆解:ANP如何为Agent构建“通用语言”
一套网络协议能不能立住,看两个东西:一是它对“通信双方”的抽象是否清晰,二是它对“通信过程”的约定是否完整。ANP的设计思路,是把Agent当成网络中的节点,参考了互联网协议分层思想,但针对Agent的特点做了几个关键调整。
2.1 Agent身份体系:不仅仅是ID,而是一套可验证的实体标识
ANP给每个Agent分配一个全局唯一的Agent ID。这个设计看起来很基础,但细节里有讲究。Agent ID不只是UUID那串字符串,它承载了三层信息:一是身份标识,让消息可以寻址到具体Agent;二是验证凭证,搭配密钥材料使用,防止有人冒充某个Agent身份;三是属性载体,可以携带Agent的类型、所属组织、能力域名等元数据。
我在本地测试时,最直观的感受是:身份设计决定了后面所有机制的可行性。没有严格身份体系,就会发现路由、鉴权、审计都是空中楼阁。草案里把身份验证材料设计成可轮换的,这很关键——Agent实例的部署周期短、版本迭代快,固定的长期密钥风险太大。
2.2 能力注册与发现:让Agent可以被“找到并理解”
过去要找一个能翻译PDF的Agent,你得知道它在哪、它的API长什么样、怎么鉴权。ANP引入了能力目录服务(Agent Directory),每个Agent上线时向目录注册自己的ID、端点地址、技能列表和约束条件。调用方只需要问目录:谁有翻译能力?目录返回符合条件的候选列表。
这里有一个容易被忽略的设计点:能力描述不能只是自然语言标签——比如“我能翻译”——还要有结构化参数,比如支持的语言、输入输出格式、费用模型、并发上限。这样才能让调用方在真正发起消息之前,判断这个Agent适不适合这项任务。实测下来,翻译类能力用结构化描述,让上层Agent做决策的准确率明显高于纯文本描述,因为决策模型能直接解析参数而不必依赖语义猜测。
2.3 消息与会话模型:从“一问一答”升级到“多轮协作”
单个Agent对话是简单的请求-响应,一个提问,一个回答。但Agent之间的协作往往是多轮的:A请求B翻译,B却说需要先确认术语表,A返回术语表,B再给出译文;之后C还要对译文做合规检查,可能会提出修改意见。每一条消息都属于同一个会话,ANP为此设计了会话上下文管理机制——不是简单地把所有历史消息堆在一起,而是让会话具备状态:当前上下文、待办事项、已完成步骤、阻塞原因。
这个总结和分析工作是这次拆解中最有价值的部分。多Agent协作中的大量“翻车”现场,根源都不在某个Agent模型能力不行,而是上下文断链。A在等待B的翻译结果时,自己新增了一条消息引用旧结果;C发起合规检查时,看不到A和B之前的术语约定。ANP的会话模型就是为了解决这种“跨Agent上下文断裂”而生的。
2.4 消息路由与可靠投递:Agent之间通信不能靠“发个请求等个响应”
Agent之间的通信不是简单的HTTP调用,因为对方可能暂时不可用、可能处理一个任务需要几分钟、可能在等待外部输入。草案把消息投递设计成异步可靠的模式:发送方先把消息交给消息队列或转发节点,由中转层负责重试、排队、超时处理;接收方处理完后,再通过同样的通道把结果回传。
有人会问:为什么不用同步HTTP?因为同步模型在Agent协作场景下的容错性太差。一次协作里有5个Agent参与,中间有一个卡了20秒,整个链路都被阻塞;更麻烦的是,一个Agent宕机时,同步调用直接失败,上游必须自己做重试逻辑、自己决定是丢弃还是缓存任务。而异步投递把“这活儿干了没有”的责任,从业务层下沉到了协议层,业务层只需要关心事件通知。
2.5 安全与权限控制:让陌生人Agent可以协作,但不能乱来
匿名Agent之间的协作会带来一系列安全问题:对方会不会窃取会话数据?会不会在消息里夹带恶意指令?会不会冒充已授权Agent?草案在安全设计上至少考虑了四个维度——传输层加密(防窃听)、身份验证(防冒充)、能力授权(防越权)、内容审计(事后追溯)。
这四层缺一个都容易出事故。最容易被忽视的是“内容审计”,一开始我也觉得传输加密就够了,后来跑了一次协作链路才明白:多Agent协作出错时,最大的痛苦是不知道哪一步出了问题、哪个Agent发了一条错误消息。有了审计日志,排查效率能提升一个量级。
3. 定位与边界:ANP和MCP、工作流引擎到底是什么关系
看过草案的开发者都有一个共同困惑:ANP和最近很火的MCP(模型上下文协议)是什么关系?是替代还是互补?我的判断很明确:互补,但定位不同。
3.1 ANP和MCP:一个管“横向互联”,一个管“纵向连接”
MCP解决的是Agent访问工具、数据、模型的问题——一个Agent要通过MCP去调用一个搜索API、读一个数据库、触发一个函数,相当于打通了Agent和能力之间的纵向通道。ANP解决的是Agent与Agent之间的横向互联——让A能发现B、给B发消息、协作完成一个跨Agent流程。
一个类比:MCP是标准化的“电源插座”,让Agent能插上各种外部工具和模型;ANP是标准化的“电网”,让每个接上电的设备能够相互供电、彼此协作。两者服务层次不同,但都是Agent生态的基础设施。一个成熟的Agent系统,很可能同时使用MCP(接入工具)和ANP(跨Agent协作),它们各管一段。
3.2 和工作流引擎:ANP不是替代品,而是更底层的通信契约
传统工作流引擎(比如n8n、LangGraph里的流程编排)解决的是“任务步骤怎么编排”——先做A再做B,条件分支怎么走,失败怎么重试。ANP解决的是“编排中的每一步,消息怎么在Agent之间传输”。如果你用一个工作流引擎,可以在编排层决定“先让翻译Agent干活,再让法律Agent检查”,到了真正执行时,这每一步的Agent间通信就可以走ANP。
所以合理的架构是:上层有编排引擎,底层有通信协议。编排引擎关心业务流程,ANP关心消息能否可靠、安全地到达对端。两者不是二选一,而是不同抽象层级。
3.3 为什么AI Agent场景需要“新协议”而不是直接复用HTTP/RPC
这是很多人的第一个问题:直接用HTTP+JSON不就行了吗?如果你只做单次API调用,当然可以。但Agent协作有几个HTTP没解决的场景:第一,Agent之间需要动态发现,而HTTP调用要求调用方提前知道URL;第二,Agent需要能力协商——你找翻译Agent,但它可能只支持中译英、不支持法译中,这需要在调用前完成协商;第三,Agent任务的执行时长是不确定的,HTTP的常规超时设计在这种场景下会很尴尬;第四,多Agent间的会话上下文要保持一致,这在无状态的HTTP请求里很难做。
这些需求叠加起来,就需要一套面向会话、面向发现、面向协作的协议层。ANP不是重造轮子,而是针对Agent协作场景做一个更贴合的轮子。
4. 动手跑通最小ANP链路:注册、发现、消息、回调全流程
只看文档容易飘,实际跑一遍才能理解这套协议解决的真实问题。我这里分享一个基于草案思路的最小实现,不依赖完整实现,用简单的Python脚本把核心机制走通。底层传输先用HTTP+JSON模拟,理解核心逻辑之后再替换成完整的ANP实现也不难。
4.1 最小环境准备与身份初始化
先做两件事:启动一个目录服务(用于Agent注册和发现),准备两个Agent。目录服务不需要重,一个FastAPI应用就够,核心接口就两个:注册能力和查询能力。
# directory.py — 最小能力目录服务 from fastapi import FastAPI, HTTPException from pydantic import BaseModel import uuid app = FastAPI() class AgentRegistration(BaseModel): agent_id: str endpoint: str skills: list[str] # 如 ["translate", "legal_check"] params: dict = {} # 可选,如 {"languages": ["zh", "en"]} # 内存目录,正式实现里通常是带索引的数据库 REGISTRY = {} @app.post("/agent/register") def register(reg: AgentRegistration): if reg.agent_id in REGISTRY: raise HTTPException(409, "agent already exists") REGISTRY[reg.agent_id] = reg.model_dump() return {"status": "registered", "agent_id": reg.agent_id} @app.get("/agent/discover") def discover(skill: str): matches = [info for info in REGISTRY.values() if skill in info["skills"]] # 真实协议里这里会做更复杂的能力过滤和打分 return {"agents": matches}这里要提醒一个身份初始化的细节:Agent ID 最好由目录服务统一签发或做幂等校验,不要让Agent自己随便填。否则你会在实践中遇到ID冲突、重名、伪造等情况。我之前的做法是:Agent启动时发送签名注册请求,目录在校验签名后签发ID,并把公钥指纹存入目录。
4.2 注册:Agent上线后如何“自我介绍”
有了目录服务,Agent启动后第一步就是注册。注意注册内容不是一成不变的——技能、端点、状态都会变化,所以注册接口应该支持重复调用(update语义),这比只支持一次性注册更贴合真实场景。
# agent_a.py — 翻译Agent import requests AGENT_SELF = { "agent_id": "agent-translate-001", "endpoint": "http://localhost:8100/agent/invoke", "skills": ["translate"], "params": {"languages": ["zh", "en", "ja"], "max_doc_size_mb": 10} } # 注册并定期发送心跳/更新 def register(): resp = requests.post("http://localhost:8000/agent/register", json=AGENT_SELF) print(resp.json())注册之后,目录里就能被搜索到“谁会翻译”。这一步看似简单,但有个实操坑:注册信息里不要写死临时端口或内网IP,否则在容器化部署或跨网络环境下,其他Agent根本连不上你。更好的做法是注册一个稳定的外部网关地址,内部路由由网关处理。
4.3 发现与协商:调用方Agent如何找到合适的协作方
现在让一个“协调Agent”来发起协作:它需要翻译服务,于是向目录发起查询,按技能筛选,再按参数做二次过滤(比如要求支持中文到英文)。
# agent_b.py — 协调Agent(部分逻辑) def find_translator(src_lang, dst_lang): # 1. 按技能找候选 candidates = requests.get( "http://localhost:8000/agent/discover", params={"skill": "translate"} ).json()["agents"] # 2. 按语言参数过滤出真正满足要求的 usable = [] for agent in candidates: langs = set(agent["params"].get("languages", [])) if src_lang in langs and dst_lang in langs: usable.append(agent) return usable这里有个容易踩的坑:只按技能名筛选。技能名太粗,我要找的是“中文翻译成英文”,结果候选Agent都会标记“translate”,但真正支持中英互译的可能只有一半。所以发现阶段一定要做参数级过滤。ANP草案里也在强调结构化能力描述,为的就是让这层过滤可计算,而不是靠人去读自然语言描述。
4.4 消息交互与回调:如何异步拿到跨Agent的返回结果
发现目标之后,协调Agent向目标Agent发送任务消息。这里最核心的设计是:不要等同步响应,而是把这次请求标记为一个可以回传结果的异步任务。
# 协调Agent发起翻译请求(异步模式) task_payload = { "task_id": "task-20240601-001", "session_id": "session-abc-123", "source_text": "Hello, this is an agent network test.", "target_lang": "zh", "callback_url": "http://localhost:8200/callback/translate" } resp = requests.post("http://localhost:8100/agent/invoke", json=task_payload)被调用的翻译Agent收到后,做自己的事(这里可以直接调任何内部模型或外部API),完成后把结果回传给协调Agent指定的回调地址。翻译Agent可以选择在同一个HTTP响应里直接返回,也可以延后回传——这在长任务场景非常有用:
# 翻译Agent完成任务后回调协调Agent result_payload = { "task_id": "task-20240601-001", "session_id": "session-abc-123", "status": "completed", "result": { "translated_text": "你好,这是一次Agent网络测试。", "model_used": "demo-model", "tokens": 27 } } requests.post("http://localhost:8200/callback/translate", json=result_payload)这个模式解决了一个实际问题:如果翻译Agent的执行时间不确定,同步等待会让调用方一直挂着。异步回调则安排双方都不阻塞,调用方可以去处理其他任务,翻译完成后通过回调自动恢复流程。
4.5 最小链路整体验证与关键观察点
把上面四步串起来,就是一条完整的“注册-发现-调用-回调”最小链路。我建议你在本地用三个终端分别跑目录服务、翻译Agent和协调Agent,然后用协调Agent发起一次翻译任务,重点观察几个现象:
- 第一次调用,协调Agent返回了一个任务ID,并没有立刻拿到翻译结果——因为异步模式不阻塞。
- 隔几秒再查任务状态,看到状态变成completed,回调里带了译文字段。
- 如果你同时注册两个翻译Agent,一个只支持中英、一个支持中英日,发现接口返回的候选列表不同,能验证参数过滤是否生效。
这一步跑通后,你就理解了ANP最核心的骨架。后面的路由优化、安全认证、会话状态同步,都是在这条最小链路上做加固和扩展。
5. 常见问题与排查技巧实录:Agent互联过程中的实际坑
协议草案好看,落地全是坑。把这段时间实际遇到的高频问题整理成一个速查表,按出现的频率从高到低排:
| 问题现象 | 根因分析 | 排查方法 | 解决方案 |
|---|---|---|---|
| A找不到B的能力 | 能力注册遗漏或技能标签不一致 | 查目录服务的注册记录,确认B上线时是否成功调用了注册接口 | 统一技能标签词表,注册后主动触发一次目录查询自检 |
| 调用超时但任务其实完成了 | 同步等待太长,任务实际异步执行 | 看Agent端日志,确认任务执行时长 | 改成异步回调模式,不要用HTTP同步等待 |
| 回调丢失导致调用方永远在等待 | 回调URL不可达或回调消息无确认机制 | 查回调目标端访问日志,确认是否有请求到达 | 增加回调重试机制,回调失败时发送通知并记录到待处理队列 |
| 同一个会话上下文错乱 | 消息缺少会话ID,或者会话ID生成逻辑不一致 | 检查所有协作消息是否都携带了统一session_id | 在消息结构里强制要求session_id,并增加会话维度的日志索引 |
| 两个Agent互相认为对方是冒牌货 | 身份验证不通过,密钥或证书不匹配 | 检查双方配置的私钥/公钥是否匹配 | 把密钥轮换流程做成自动化,轮换后自动重新注册 |
| 目录数据越来越脏 | 下线Agent没有反注册,更新注册信息失败 | 定期抽查目录中有多少Agent是“僵尸” | 增加“心跳+租约”机制,超期未续约的Agent自动标记为不可用 |
5.1 最隐蔽的坑:能力标签的“同级不同名”问题
两个Agent都宣称自己有“translate”能力,但A内部定义的是“translation”,B定义的是“translate”——即便技能本质相同,发现机制也匹配不上。这种问题的隐蔽性在于,代码和日志看起来一切正常,但协作就是建立不起来。建议从一开始就定义一套共享的能力词表(类似协议里的枚举值),新Agent上线时做词表校验,不存在的标签不允注册。
5.2 测试环境里容易忽略的“时间同步”问题
在多Agent协作场景,消息可能经过多个中转节点,会话超时、任务过期都要依赖时间戳。如果各节点的系统时间不一致,就会出现“回调比请求还早”、任务被误判为过期的诡异现象。在这个问题上,建议所有Agent节点统一启用NTP时间同步,并在关键判断逻辑里计算时间差容忍度,而不是用死值做对比。
5.3 网络拓扑变化对发现机制的影响
容器化部署、动态扩缩容环境下,Agent的IP和端点是会漂移的。如果你在注册信息里写死了IP,Agent重启后目录里还是旧地址,调用方自然会失败。这也是草案里强调“端点用稳定的服务名或网关地址”的原因。在测试环境我用的是localhost,没什么问题,但一旦跨容器、跨机器部署,必须一开始就设计好地址注册与更新机制。
6. 草案落地建议:如果你要接ANP,从这几个优先级开始
最后聊点实际建议。如果你所在团队正在考虑接入ANP,我建议不要一开始就追求全量协议特性,而是按优先级逐步落地。
6.1 第一步先把“身份与注册”做扎实
无论你用不用协议的其他部分,一套统一的Agent身份体系和注册发现机制,都能立竿见影地降低系统复杂度。这一步做不好,后面所有功能都很难可靠运行。建议先统一Agent ID生成规则、注册流程、密钥管理,让每个Agent都能被目录准确找到。
6.2 第二步把消息交互改成“异步+回调”
把同步调用逐步替换成异步消息+回调,是投入产出比非常高的一次改造。你会发现调用方Agent的并发能力明显提升,不再被长任务阻塞,失败重试的逻辑也更好写。注意设计好回调重试和超时告警,避免任务“悄无声息”地丢失。
6.3 第三步再上能力协商与安全加固
能力协商(不只是发现,还包括确认参数、约束、费用等)和安全加固(传输加密、身份验证、审计日志)可以放到后续阶段。协议草案期,先把核心链路跑通、形态稳定,再逐步补安全水位,是更稳妥的路径。安全和协议演进可以并行做,但别让安全设计成为验证核心机制的阻碍。
6.4 最后,积极回馈草案演进
这一点容易被技术团队忽略,但非常重要。草案期的协议是需要各方输入才能成熟的。如果你在实现过程中发现了字段缺失、流程矛盾、边界场景空白,主动记录并向草案维护方反馈,不仅能优化协议本身,也能让你的实现与协议演进保持一致。自己做一套“私有方言”是最后的路,短期方便、长期锁死。
我个人在跑通最小链路后又做了一件小事:把注册、发现、调用、回调的接口定义固化成一份OpenAPI描述文件,让不同语言的Agent都能按这份定义生成客户端。这事不用等协议完全冻结,越早做,后面整个团队接入的摩擦就越小。Agent之间的“普通话”,早学会的人早受益。