阿里云服务器过,还是分布式任务调度过?都不是——今天聊的是 Agent 之间的“自我介绍”。最近在做多智能体协同的时候,接了一个第三方 Agent 服务,文档只给了一个 URL,说要先“读一下 Agent Card”。一开始我以为是类似 OpenAPI 的接口文档,结果发现它比接口文档更轻、更偏声明式。如果你也在对接 A2A(Agent2Agent)协议,或者正被 “Agent Card 怎么取、技能怎么读、认证怎么核对” 这类问题卡住,这篇文章就是给你准备的。
下面我会从概念讲起,再给出一套完整的读卡、解析技能、核对认证的实操流程,所有命令和代码都保证能直接复制使用。无论你是在做 Spring AI 集成、自研 Agent 平台,还是只写一个调用 Agent 服务的客户端,都应该能从中找到可落地的部分。
1. 为什么 Agent 互联需要一张“卡”
1.1 A2A 协议到底解决什么问题
A2A(Agent2Agent)是 Google 在 2025 年提出并推动的开源 Agent 互操作协议,目标是让不同厂商、不同框架的 Agent 能够通过统一的方式发现彼此、调用彼此。它解决的不是“单个 Agent 有多强”,而是“多个 Agent 怎么互相找到并且协作”。
在 A2A 出现之前,Agent 之间的对接基本是“点对点硬编码”:A 系统要调用 B 系统的能力,得先看 B 的 API 文档,再手工写一套 HTTP 调用逻辑。一旦 Agent 数量变多,这种网状对接方式会迅速失控。A2A 的思路与 Web 的发展路线很相似——通过一个标准化的“入口描述文件”暴露能力,然后由客户端按统一流程去消费。
目前 A2A 协议已经进入 Linux 基金会托管阶段,Spring AI 等主流框架也提供了对应的 A2A Starter 支持,开始从“提案”走向“工程落地”。所以现在花时间理解 A2A 的核心理念,并不是追热点,而是为多 Agent 协作做基础储备。
1.2 Agent Card 在 A2A 协议中的具体位置
Agent Card 是 A2A 协议里的“名片文件”。每个 A2A Agent 服务都必须暴露一个 JSON 格式的 Agent Card,用来声明这个 Agent 是谁、能做什么、需要什么样的认证、如何发起请求。
可以这样理解:传统 Web 服务对外暴露的是 OpenAPI 文档,调用方拿着它生成客户端代码;而 A2A 服务对外暴露的是 Agent Card,调用方拿着它决定“能不能调、怎么调、用什么安全方式调”。虽然没有 OpenAPI 那么细的接口级描述,但它的重点是描述“Agent 的意图和能力”。
在 A2A 的交互模型中,Agent Card 是第一步。一个客户端要调用远端 Agent,通常流程是:
- 读取远端 Agent Card。
- 判断自己的能力需求是否与 Card 中声明的 skills/能力匹配。
- 核对认证方案,获取合法调用凭证。
- 建立 A2A 会话,发送任务(Task)。
- 轮询或通过流式方式接收任务结果。
也就是说,Agent Card 是所有后续交互的起点。如果第一步“读卡”读错了,后面一切都无从谈起。
1.3 Agent Card 与 OpenAPI、普通 JSON 配置的区别
常见的一个困惑是:Agent Card 和 OpenAPI 有什么区别?和普通的 JSON 描述文件又有什么区别?
从形态上看,它们都是 JSON;从作用上看,它们有本质差异:
| 对比项 | Agent Card | OpenAPI | 普通 JSON 配置 |
|---|---|---|---|
| 描述对象 | Agent 的能力与协作方式 | HTTP 接口的请求/响应结构 | 应用自身的运行参数 |
| 消费方式 | 由 A2A 客户端做能力发现与路由 | 由代码生成器生成 SDK 或文档 | 由配置加载器读取 |
| 是否关注认证 | 是,显式声明认证方案 | 通过 securitySchemes 声明 | 通常不涉及 |
| 是否关注技能 | 是,通过 skills 数组声明 | 不直接等价 | 不涉及 |
Agent Card 更偏向“语义层”。它告诉调用方“这个 Agent 能完成什么目标”,而 OpenAPI 告诉调用方“这个接口的每个字段怎么拼”。在多 Agent 编排场景中,Agent Card 是路由器做决策的依据。
1.4 读卡这件事为什么容易踩坑
读卡看似简单——无非是一个 HTTP GET 请求,拿到 JSON 再解析。但实际对接中常遇到的情况是:
- 拿到的 Agent Card 结构不符合当前 A2A 版本,字段缺失或命名不一致。
- skills 里写的能力名称是“人话”,但请求时需要用另一个 taskId 或 message 结构。
- authentication 部分只有 scheme 类型,没有提供令牌获取方式,调用方不知道去哪申请凭证。
- 同一个 Agent 在不同环境(dev / staging / prod)下暴露的 Agent Card 内容不同,导致联调和生产行为不一致。
这些问题的根源,都是没有把 Agent Card 当作“一等公民”来对待。下面我会从环境准备开始,完整过一遍取卡、读卡、核验认证的流程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与版本说明
2.1 演示环境与工具
本文的演示以“一个标准 A2A Agent 服务”为假设对象,客户端使用 HTTP 工具和 Python 脚本完成读卡与调用。具体环境如下:
- 操作系统:Windows 10 / macOS / Linux 均可,命令兼容主流 Shell。
- 客户端工具:curl 8.x 以上版本,用于发送 HTTP 请求。
- 脚本语言:Python 3.9+,用于解析 Agent Card 并验证字段。
- 请求目标:一个运行中的 A2A Agent 服务,假设地址是
http://localhost:8080/a2a。
如果你对接的是远程 Agent,只需要把命令里的 host 和端口替换成真实地址即可。不需要本地启动 Agent,读卡和调用都是标准的 HTTP 交互。
A2A 协议版本方面,目前社区常见的协议版本标识有 0.1 和 0.2 两个阶段,字段上存在一定差异。本文示例以较为完整的声明式结构为例,不锁定具体协议版本,你只需要关注字段语义,实际对接时以对方暴露的 Card 为准。
2.2 取卡入口:/.well-known/agent-card.json
A2A 协议定义了一个默认的 Agent Card 获取路径:/.well-known/agent-card.json。这个路径的设计与 /.well-known/acme-challenge 类似,属于 Web 生态中常见的“约定入口”模式。
对于一个运行在 http://your-agent-host:8080 的服务,Agent Card 的完整访问地址就是:
code复制http://your-agent-host:8080/.well-known/agent-card.json
有些 Agent 服务也允许通过根路径 / 直接返回 Agent Card,或通过配置中心指定自定义路径。但从标准化角度,优先使用 well-known 路径是最稳妥的。
如果你在对接过程中发现目标服务在 /.well-known/agent-card.json 路径下返回 404,可以再尝试:
/agent-card.json/a2a/agent-card.json- 服务商文档中指定的自定义路径
2.3 Spring AI 等框架里的 A2A 支持情况
如果你是 Java 技术栈开发者,可以留意 Spring AI 对 A2A 协议的支持。自 Spring AI 1.1 开始,官方引入了 A2A 相关 Starter(例如 spring-ai-starter-model-a2a 和对应的 A2A Client Starter),用来简化 Agent 服务的发布与调用。
使用 Spring AI 的 A2A Starter 时,服务端会自动生成并暴露 Agent Card,你只需要通过配置项声明 Agent 的名称、描述、技能信息。客户端则通过读取远端 Agent Card 来构建调用请求,减少手写 HTTP 拼接的工作量。
具体的依赖版本需要根据你的 Spring Boot 和 Spring AI 版本确定,不要直接照搬网上的坐标。下面实战部分我会优先展示通用的 HTTP 流程,这对任何语言和框架都适用。
3. 读懂 Agent Card 的字段结构
3.1 一个最小 Agent Card 完整示例
先给出一份完整的 Agent Card 示例,后面逐个字段说明。这是一个“订单查询助手”的声明卡片:
json复制{
"protocolVersion": "0.2",
"name": "订单查询助手",
"description": "提供订单状态查询、发货提醒等能力,适合电商客服场景。",
"url": "https://agent.example.com/a2a",
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": false,
"state": {
"maxInputSize": 8000,
"maxOutputSize": 8000
}
},
"skills": [
{
"id": "order.query",
"name": "查询订单状态",
"description": "根据订单号查询最新物流状态",
"inputModes": ["application/json"],
"outputModes": ["application/json"],
"tags": ["order", "query"]
}
],
"authentication": {
"schemes": [
{
"scheme": "bearer",
"credentials": "调用 token 由服务方线下提供,不应出现在 Card 文件中"
}
]
},
"defaultInputModes": ["application/json"],
"defaultOutputModes": ["application/json"],
"security": {
"trust": {
"domain": ["*.example.com"]
}
}
}
上面的示例不是某家厂商的真实配置,而是把 Agent Card 最常见的字段都列出来,方便我们逐项拆解。注意 credentials 字段在真实场景中不应该存放明文密钥,这里只做占位说明。
3.2 核心元数据字段
protocolVersion 表示这个 Agent 遵循的 A2A 协议版本。如果你发现对方返回的 Card 里没有这个字段,说明对方可能还没有完全遵循 A2A 标准,兼容时要先确认能力边界。
name 和 description 是给“人”看的,也是给“路由器”看的。在多 Agent 编排系统里,编排器会读取 description 来判断一个任务该路由给哪个 Agent,因此这段描述要尽量准确,不能写得太泛。
url 是发起真实 A2A 请求的端点地址。调用方后续所有 task 请求都发往这个地址。很多对接问题都出在“读 Card 的地址”和“发任务的地址”混为一谈。
version 是 Agent 自身业务的版本号。当 Agent 能力发生不兼容变更时,这个版本号应该同步变化,方便调用方做兼容判断。
3.3 skills 技能声明如何阅读
skills 是整个 Agent Card 中调用方最关心的部分。它声明了该 Agent 支持哪些技能,每个技能可以看作一个可调用的“能力单元”。
每个 skill 对象的关键字段如下:
id:技能的唯一标识,调用时通常需要携带。name:技能的人类可读名称。description:技能完成的目标。inputModes:该技能支持的输入格式。outputModes:该技能支持的输出格式。tags:用于检索和路由的标签。
在对接第三方 Agent 时,建议把对方暴露的 skills 数组做成一个清单,确认自己要用的技能是否在列。不要凭文档猜测技能名称,一切以 Card 中实际的 id 和 name 为准。
当 Agent Card 中完全没有 skills 字段时,说明对方可能只支持通用对话能力,或者还没有声明结构化技能。这种情况下,要把调用预期调整为“开放式任务”,而不是精确技能触发。
3.4 authentication 认证信息如何核对
认证核对是读卡时最容易出问题的环节。authentication.schemes 数组声明了调用方需要使用的认证方式。
常见的 scheme 类型有两种:
| scheme 值 | 含义 | 请求头示例 |
|---|---|---|
bearer |
Bearer Token 认证 | Authorization: Bearer <token> |
oauth2 |
OAuth 2.0 流程 | 需要先获取 access token,再作为 Bearer Token 使用 |
还有部分 Agent 会声明 apiKey 或 basic 认证,具体以 Card 中的 scheme 字段值为准。
核对认证信息时,要做三件事:
- 确认 scheme 类型属于你的客户端能支持的认证模式。
- 确认凭证获取渠道。如果 Card 里只写了
bearer,但没有说明 token 从哪里获取,需要联系服务方获取合法授权。 - 确认 token 的传递方式。绝大多数 A2A Agent 使用 HTTP Authorization 头传递凭证。
这里必须强调安全底线:无论对接哪个 Agent,都只能使用通过合法渠道申请、在当前授权范围内的凭证。不提供、不讨论、不尝试任何绕过认证或窃取凭证的方法。
3.5 capabilities 与输入输出模式
capabilities 字段描述 Agent 在传输层的能力支持情况。例如 streaming 为 true 时,客户端可以建立流式连接接收增量结果;pushNotifications 为 true 时,Agent 可以主动推送任务状态变更。
defaultInputModes 和 defaultOutputModes 表示该 Agent 默认接受的输入输出格式。一般情况下使用 application/json 即可。如果 Agent 同时支持 text/plain,则可以实现更轻量的交互。
读 Card 时,对 capabilities 的关注点不是“字段多不多”,而是“调用方需要的能力是否在列”。比如你的场景要求流式输出,但 Card 里 streaming 为 false,就必须调整交互模式为轮询,否则后续会不断遇到超时问题。
4. 完整实战:取卡、解析技能、核对认证
下面进入实操环节。我们从一个假设的 A2A Agent 服务地址出发,完整走一遍读取 Agent Card、解析技能列表、核对认证信息并发起请求的流程。
4.1 第一步:用 curl 拉取 Agent Card
假设目标服务地址为 http://your-agent-host:8080,首先读取 Agent Card:
bash复制curl -s http://your-agent-host:8080/.well-known/agent-card.json
如果请求成功,会返回一段 JSON。为了便于查看,可以借助 Python 格式化输出:
bash复制curl -s http://your-agent-host:8080/.well-known/agent-card.json | python3 -m json.tool
输出示例(节选):
json复制{
"protocolVersion": "0.2",
"name": "订单查询助手",
"description": "提供订单状态查询、发货提醒等能力",
"url": "https://agent.example.com/a2a",
"skills": [
{
"id": "order.query",
"name": "查询订单状态",
"description": "根据订单号查询最新物流状态"
}
]
}
如果 curl 请求返回 401 或 403,说明这个服务的 Agent Card 本身受保护,需要带凭证访问。可以把从服务方获得的凭证加到请求头中:
bash复制curl -s \
-H "Authorization: Bearer <your-token>" \
http://your-agent-host:8080/.well-known/agent-card.json
注意:这一步的前提是你已经通过合法渠道拿到访问凭证。
4.2 第二步:用 Python 解析技能声明
拿到 Agent Card 的 JSON 后,不要直接用眼睛看,建议写一个简单脚本做结构化解析。下面这个 Python 脚本会读取本地保存的 agent-card.json 文件,解析出 Agent 的基本信息、技能列表和认证方案:
python复制import json
def load_agent_card(file_path):
with open(file_path, "r", encoding="utf-8") as f:
return json.load(f)
def inspect_agent_card(card):
print("== Agent 基础信息 ==")
print("协议版本:", card.get("protocolVersion", "未声明"))
print("Agent 名称:", card.get("name", "未声明"))
print("Agent 描述:", card.get("description", "未声明"))
print("任务端点:", card.get("url", "未声明"))
print("Agent 版本:", card.get("version", "未声明"))
print()
print("== 技能列表 ==")
skills = card.get("skills", [])
if not skills:
print("未声明任何技能,可能仅支持通用对话任务。")
for skill in skills:
print("- 技能ID:", skill.get("id"))
print(" 技能名称:", skill.get("name"))
print(" 技能描述:", skill.get("description"))
print(" 输入格式:", skill.get("inputModes"))
print(" 输出格式:", skill.get("outputModes"))
print()
print("== 认证方式 ==")
auth = card.get("authentication", {})
schemes = auth.get("schemes", [])
if not schemes:
print("未声明认证要求,按匿名请求处理。")
for scheme in schemes:
print("- 认证类型:", scheme.get("scheme"))
print(" 说明:", scheme.get("credentials", "无额外说明"))
if __name__ == "__main__":
card = load_agent_card("agent-card.json")
inspect_agent_card(card)
把上面的 Agent Card 示例保存为 agent-card.json 后运行:
bash复制python3 inspect_card.py
预期输出:
text复制== Agent 基础信息 ==
协议版本: 0.2
Agent 名称: 订单查询助手
Agent 描述: 提供订单状态查询、发货提醒等能力
任务端点: https://agent.example.com/a2a
Agent 版本: 1.0.0
== 技能列表 ==
- 技能ID: order.query
技能名称: 查询订单状态
技能描述: 根据订单号查询最新物流状态
输入格式: ['application/json']
输出格式: ['application/json']
== 认证方式 ==
- 认证类型: bearer
说明: 调用 token 由服务方线下提供,不应出现在 Card 文件中
这个脚本的价值在于:当 Agent Card 中技能很多时,它能帮你快速提取出真正关心的字段,而不是反复肉眼看 JSON。
4.3 第三步:核对认证方案与调用前置条件
解析出认证方案后,下一步是判断是否需要申请凭证。核对流程可以概括为:
- 找到
authentication.schemes。 - 如果 scheme 为
bearer,确认是否有合法可用的 token。 - 如果 scheme 为
oauth2,确认是否有 clientId / clientSecret,并了解获取 access token 的接口。 - 如果 scheme 为
none或完全没有认证字段,按匿名请求处理,但敏感性任务依然要遵守服务方安全要求。
在实际项目中,建议把“认证方案核对”固化成代码逻辑,避免手工遗漏。下面是一个简单的核对示例:
python复制def check_auth(card):
auth = card.get("authentication", {})
schemes = auth.get("schemes", [])
if not schemes:
return "anonymous"
for scheme in schemes:
scheme_type = scheme.get("scheme", "").lower()
if scheme_type in ("bearer", "oauth2"):
return scheme_type
if scheme_type in ("api_key", "apikey"):
return "api_key"
if scheme_type == "basic":
return "basic"
return "unknown"
拿到认证类型后,调用方业务代码就可以决定如何构造 HTTP 请求头。这里强烈不建议把 token 硬编码在代码或配置仓库中,应该使用环境变量或专用的密钥管理服务。
4.4 第四步:发起一次带认证的任务请求
A2A 的核心交互单位是 Task。客户端向 Card 中 url 指定的端点发送任务请求,A2A 服务端返回任务 ID,客户端再通过结果获取接口拿到最终输出。
下面用 Python 展示一个最简任务请求流程,假设 Agent Card 中的 url 为 https://agent.example.com/a2a,认证方式为 Bearer Token:
python复制import json
import time
import urllib.request
A2A_URL = "https://agent.example.com/a2a"
TOKEN = "your-legal-access-token"
def build_task_request(skill_id, params):
return {
"jsonrpc": "2.0",
"id": 1,
"method": "tasks/send",
"params": {
"taskId": f"task-{int(time.time())}",
"message": {
"role": "user",
"parts": [
{
"kind": "text",
"text": json.dumps(params)
}
]
},
"metadata": {
"skillId": skill_id
}
}
}
def send_task(payload):
data = json.dumps(payload).encode("utf-8")
req = urllib.request.Request(
A2A_URL,
data=data,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {TOKEN}"
},
method="POST"
)
with urllib.request.urlopen(req) as resp:
return json.load(resp.read().decode("utf-8"))
if __name__ == "__main__":
request_body = build_task_request("order.query", {"orderId": "20250101001"})
response = send_task(request_body)
print(json.dumps(response, ensure_ascii=False, indent=2))
运行脚本前,需要把 TOKEN 替换为合法获得的凭证,并把 A2A_URL 换成实际 Agent 的 url 字段值。不同 A2A 实现的方法名可能略有差异(例如 tasks/send、message/send),如果返回方法不存在,需要以对方实际的接口文档为准。
4.5 验证结果与日志输出
请求发出后,A2A 服务通常会返回一个任务状态。常见的状态包括 submitted、working、completed、failed。如果返回 completed,并且结果中包含 Agent 的输出,说明整个流程已经走通;如果返回 working 或 submitted,说明任务进入异步处理,需要继续轮询任务结果。
为了便于排错,建议调用方把每次请求的以下信息记录到日志中:
- Agent Card 中的
url - 使用的技能 ID
- 任务 ID
- HTTP 状态码
- 响应体摘要
- 认证类型(不要记录 token 本身)
这样一旦后续出现超时或任务失败,可以快速定位是认证问题、路由问题还是 Agent 侧逻辑问题。
5. 常见问题与排查思路
5.1 高频问题速查表
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
/.well-known/agent-card.json 返回 404 |
Agent 服务未实现 A2A 标准路径,或路径有差异 | 查看服务文档,尝试 /agent-card.json 等备选路径 |
| 读 Card 返回 401 | Agent Card 本身受保护 | 先在请求头携带合法凭证再访问 |
Card 中 skills 为空 |
Agent 未声明结构化技能 | 按开放对话任务方式调用,或与服务方确认 |
| 请求时提示技能不存在 | 技能 ID 与 Card 中声明不一致 | 以 Card 中的实际 id 字段为准,不要用 name 代替 |
streaming 能力未生效 |
对方 Card 中 streaming 为 false,或请求参数遗漏 |
改用轮询模式获取任务结果 |
| Token 鉴权失败 | token 过期、凭证错误或 Authorization 头格式不对 | 检查 Authorization: Bearer <token> 格式,重新申请凭证 |
| 协议版本字段不匹配 | 对方使用的 A2A 版本与你侧不一致 | 以对方 Card 声明的版本为准,调整请求结构 |
5.2 读卡失败排查清单
如果连 Agent Card 都读不到,按下面顺序排查:
- 先用浏览器或 curl 访问
/.well-known/agent-card.json,确认服务在监听。 - 查看服务端日志,确认请求是否到达。
- 检查是否有反向代理或防火墙规则拦截了该路径。
- 确认路径是否区分大小写。
- 确认使用的协议是 HTTP 还是 HTTPS,证书是否有效。
绝大多数情况下,读卡失败是路径或网络访问策略导致的,而不是 Agent 服务本身的问题。
5.3 技能 ID 与认证信息不一致的处理
常见的一种情况是:Agent Card 中声明了某个技能,但请求时提示“未找到技能”。这不是随机问题,通常有两个原因。
第一个原因是技能 ID 写错。调用方往往把技能的 name 当成 ID 使用,但协议里的业务标识是 id。代码中应该优先读取 id 字段,而不是 name。
第二个原因是服务端存在多个版本的 Agent Card。你在联调环境读到的 Card 与生产环境读到的 Card 可能不同。遇到这种问题,建议调用方每次发起任务前实时拉取一次当前环境下的 Agent Card,而不是把 Card 静态缓存下来长期使用。
认证信息不一致则往往表现为:Card 声明 oauth2,但调用方拿到的是固定 token;或者 Card 声明 none,请求时却被拒绝访问。这种问题的本质是发布方没有把“真实生效的认证配置”同步到 Card 中。作为调用方,应当以服务端实际校验行为为准,并推动发布方修正 Card 描述。
6. 最佳实践与工程建议
6.1 发布方如何写好 Agent Card
如果你自己是 Agent 服务的发布方,Agent Card 的编写质量直接决定了接入方的联调成本。几个值得坚持的原则:
description 要具体。不要只写“提供查询能力”,而是写清楚适用的场景、输入的关键信息、输出的目标。因为下游编排器会把这段描述作为路由决策的重要依据。
技能 id 要稳定。一旦对外发布,技能 ID 尽量不要随意变更。如果必须变更,要同时提升 Agent 的 version 字段,并在变更日志中说明,避免调用方因为 ID 失效而出现隐性故障。
认证字段必须与真实生效策略保持一致。如果服务实际要求 Bearer Token,Agent Card 中就一定要声明 bearer,并在接入文档中说明凭证申请渠道。不要出现“Card 写着匿名,实际鉴权”的情况。
capabilities 中的流式、推送能力也要如实声明。如果服务端实际不支持流式协议,却把 streaming 写为 true,调用方会反复遇到连接中断。
6.2 调用方如何安全读卡
调用方读卡是高频操作,建议做成统一模块,而不是每个业务函数单独发请求。可以设计一个 A2AClient,在内部封装读卡、缓存、解析和认证信息核对逻辑。
安全方面,有三个底线必须守住:
不记录和打印完整 token。日志中只保留 token 的末四位或哈希值,方便排查时做粗粒度定位。
不把 token 写入代码仓库。使用环境变量或专用配置中心管理凭证,并且遵循最小权限原则:一个调用方只能使用与业务匹配的凭证,不要申请和分发超出需求权限的令牌。
不信任来源不明的 Agent Card。如果是互联网上的远端 Agent,要对下载的卡内容进行结构校验,避免因恶意字段导致客户端注入或异常行为。至少检查 url 是否指向可信域名,protocolVersion 是否为预期的字符串。
6.3 生产环境的认证与权限管理
当 Agent 调用进入生产环境后,认证核对不再是“能不能拿到 token”的问题,而是“凭证生命周期”的管理问题。需要关注几个点:
- 凭证有效期。如果使用 OAuth 2.0 或短期 token,要有自动刷新机制。
- 权限收敛。给每个调用方分配独立凭证,不要所有系统共用一个 token。
- 变更流程。Agent 的认证策略一旦调整,要同步重新下发凭证,并通知调用方更新配置。
- 审计记录。所有携带凭证的请求都应该有审计日志,避免出现异常调用时无法追溯。
这些原则同样适用于普通 API 对接,但在 Agent 场景中尤其重要——因为一个 Agent 可能被多个上流系统调用,凭证一旦泄露,影响面会呈网状扩散。
7. 总结与下一步学习路线
本文围绕 A2A 协议中的 Agent Card,讲清楚了它的定位、字段结构,以及“取卡 → 解析技能 → 核对认证 → 发起任务”的完整流程。核心要记住的点有三个:Agent Card 是 A2A 交互的第一步,技能以 skills 数组中的 id 为准,认证核对必须与实际生效策略保持一致。
学完这篇文章后,下一步你可以继续沿着两条线深入:一是读一遍 A2A 协议的官方规范,重点看 Task 的状态流转,搞清楚 tasks/send、tasks/get、tasks/cancel 这些请求在不同场景下的用法;二是实际把某个 Agent 服务暴露成 A2A Agent,自己生成一份 Agent Card,再去手动调用一次。只有亲手发布一次、消费一次,才会真正理解 Agent Card 为什么要这样设计。
如果你当前在做的项目正好要引入多 Agent 协作,建议先把 Agent Card 的读取和校验做扎实。后面无论接入多少个 Agent,只要每家服务都能提供结构正确的 Card,协作复杂度就能被控制在可维护的范围内。遇到读卡或认证问题时,也可以把本文的排查清单打印出来对照操作。
