
1. 为什么我要用 Wireshark 拆 A2A 协议的 HTTP 报文A2A 协议Agent-to-Agent Protocol解决的是两个独立 Agent 之间怎么互相说话的问题。你可以把它理解成MCP 是 Agent 内部 LLM 调工具的约定而 A2A 是 Agent 和 Agent 之间的通信约定。每个支持 A2A 的 Agent 本质上就是一个 HTTP 服务器调度 Agent 通过 HTTP 请求把任务分发给执行 Agent执行 Agent 再把结果回传。问题在于这套通信一旦出问题日志往往只告诉你“调用失败”不告诉你失败在哪一层。是请求头缺字段是流式响应中途断了还是任务状态没置为 completed这时候光看应用日志不够得看真实的 HTTP 报文。Wireshark 就是干这个的——它能把 Agent 之间每一次 HTTP 请求和响应完整还原出来包括请求头、请求体、流式分块和最终状态。这篇文章适合两类人一是正在搭多 Agent 协作系统、想搞清楚 A2A 通信细节的开发者二是已经跑起来但遇到 Agent 通信异常、需要定位问题的排障者。我会给出可复制的 Wireshark 过滤表达式、A2A 端点配置片段以及对照 TaoToken 统一 Key/API 通道验证调用链的抓包比对步骤。实测下来把抓包和统一通道结合起来看定位效率会高很多。先说清楚一个前提A2A 的通信是明文 HTTP 还是 HTTPS直接决定你能不能抓到内容。如果是 HTTPSWireshark 只能看到 TLS 握手和加密后的数据看不到请求头。所以本地调试时我一般让 Agent 之间走 HTTP或者配置 SSLKEYLOGFILE 让 Wireshark 解密。下面所有示例都基于本地 HTTP 通信端口用 8001调度 Agent和 8002天气 Agent。2. TaoToken 前置统一 Key 与 API 通道怎么接进 Agent 调用链在拆 A2A 报文之前得先让 Agent 能正常调用模型。多 Agent 场景下最烦的是每个 Agent 各配一套 Key管理起来乱。我的做法是用 TaoToken 的统一 Key 和 API 通道所有 Agent 的模型调用都走同一个入口这样抓包时调用链是清晰的不会因为 Key 不同导致请求头差异干扰分析。TaoToken 的 API 地址是https://taotoken.net/api官网是https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end。你需要在控制台创建一个 API Key然后把它写进 Agent 的模型配置里。注意A2A 协议本身不规定模型怎么调它只管 Agent 之间的 HTTP 通信模型调用是 Agent 内部的事。但正因为如此抓包时你会看到两类流量一类是 Agent 之间的 A2A 请求另一类是 Agent 内部调模型的请求。把模型调用统一到 TaoToken第二类流量的目标地址就固定了过滤起来方便。具体操作登录控制台进入 API Keys 页面创建一个 Key复制保存。然后在你每个 Agent 的配置里把模型 Base URL 指向https://taotoken.net/apiKey 填刚创建的Model ID 按你实际用的填。这样调度 Agent 和天气 Agent 内部调模型时都会走同一条通道。如果你用的是 Claude Code 这类工具做 Agent 开发可以在 settings 里配置。如果是自己写的 Python Agent用 openai 兼容的 SDK 就行把 base_url 和 api_key 换掉即可。这一步做完抓包时你就能在 Wireshark 里同时看到 A2A 的 HTTP 流量和调模型的 HTTPS 流量前者明文可读后者加密但目标地址固定便于区分。这里有个坑要提醒有些 Agent 框架默认把模型调用和 A2A 通信混在同一个端口抓包时不好区分。建议把 A2A 服务端口和模型调用分开A2A 走 8001/8002模型调用走外部 HTTPS这样过滤表达式可以精准命中。3. 可复制配置A2A 端点与 Wireshark 过滤表达式先给 A2A 端点的配置片段。假设你用 Python 写一个天气 Agent暴露 A2A 接口。核心是定义一个 HTTP 服务接收调度 Agent 发来的任务请求返回结果。下面是一个最小化的配置示例用 JSON 描述 Agent 的能力卡片Agent Card这是 A2A 协议里 Agent 注册自己能力的方式{ name: weather-agent, description: 查询指定城市天气, url: http://127.0.0.1:8002/a2a, version: 1.0.0, capabilities: { streaming: true, pushNotifications: false }, skills: [ { id: get-weather, name: 查询天气, description: 输入城市名返回当前天气, inputModes: [text], outputModes: [text, stream] } ] }调度 Agent 注册天气 Agent 时就是把这个卡片 POST 到调度 Agent 的注册端点。抓包时你会看到这个注册请求请求体就是上面的 JSON。接下来是 Wireshark 过滤表达式。打开 Wireshark选中回环网卡Windows 上是Adapter for loopback traffic captureLinux 上是lo在过滤栏输入tcp.port 8001 || tcp.port 8002这条能抓到两个 Agent 端口的所有 TCP 流量。如果你想只看 HTTP 请求用http (tcp.port 8001 || tcp.port 8002)想只看调度 Agent 发给天气 Agent 的请求假设调度 Agent 源端口 8001天气 Agent 目标端口 8002http.request tcp.srcport 8001 tcp.dstport 8002想只看流式响应chunked 传输http tcp.port 8002 http.transfer_encoding chunked如果你要跟踪一次完整的 A2A 调用链用 Follow HTTP Stream右键某个包选 Follow → HTTP StreamWireshark 会把这次 TCP 连接上的所有请求和响应拼在一起显示流式分块也能看到。再给一个调度 Agent 发起任务分发的请求示例用 curl 模拟curl -X POST http://127.0.0.1:8002/a2a \ -H Content-Type: application/json \ -H Accept: text/event-stream \ -d { jsonrpc: 2.0, method: tasks/send, id: task-001, params: { message: { role: user, parts: [{type: text, text: 西雅图天气怎么样}] }, streaming: true } }注意Accept: text/event-stream和streaming: true这两个是触发流式响应的关键。抓包时你会看到请求头里有这两个字段响应头里会有Transfer-Encoding: chunked。4. 验证请求抓包还原任务分发与流式响应配置好之后启动两个 Agent打开 Wireshark 开始抓包然后模拟一次用户输入“西雅图天气怎么样”。调度 Agent 会先分析意图然后选择天气 Agent 进行 HTTP 通信。第一步抓注册流量。调度 Agent 启动时会把天气 Agent 的卡片注册进来这时你会看到两个 HTTP 数据流一个是 POST 注册请求一个是 200 响应。在 Wireshark 里过滤http.request.method POST找到请求体里含weather-agent的那个包展开 HTTP 层能看到完整的 JSON 卡片。第二步抓任务分发。用户输入后调度 Agent 向天气 Agent 发 POST 请求。过滤http.request.uri contains /a2a找到这个包。展开后重点看三处请求头的Accept是不是text/event-stream请求体的params.streaming是不是true以及params.message.parts里的文本是不是用户输入。第三步抓流式响应。天气 Agent 返回的是分块数据。在 Wireshark 里过滤http.response并且目标端口是 8001 的包你会看到多个 chunk。每个 chunk 是一段 SSE 格式的数据类似data: {jsonrpc:2.0,id:task-001,result:{status:working,artifact:{parts:[{type:text,text:正在查询西雅图天气...}]}}} data: {jsonrpc:2.0,id:task-001,result:{status:working,artifact:{parts:[{type:text,text:西雅图当前多云15摄氏度}]}}} data: {jsonrpc:2.0,id:task-001,result:{status:completed}}最后一个 chunk 的status是completed这表示任务结束。如果你在抓包里看到流式数据中途断了没有 completed那说明 Agent 内部处理出错或者连接被提前关闭。第四步对照 TaoToken 调用链。天气 Agent 内部调模型时会向https://taotoken.net/api发 HTTPS 请求。这部分在 Wireshark 里是加密的但你能看到目标 IP 和端口。如果你配置了 SSLKEYLOGFILE可以解密看到请求体里的 prompt 和响应。对比 A2A 的明文流量和模型调用的加密流量你能确认调度 Agent 分发的任务文本和天气 Agent 发给模型的 prompt 是否一致模型返回的天气结果和天气 Agent 回传给调度 Agent 的 artifact 是否一致。这条链对上了说明调用链没问题。实测下来最容易出问题的是流式响应的分块边界。有些框架把多个 SSE 事件塞进一个 TCP 包有些拆得很碎Wireshark 里看起来乱。用 Follow HTTP Stream 看整体最清楚。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth抓包时遇到报错对照下面几个真实场景。401 Unauthorized在 Wireshark 里过滤http.response.code 401找到响应包看WWW-Authenticate头。如果是 A2A 通信报 401说明 Agent 之间的认证没配好检查调度 Agent 发请求时有没有带正确的 token。如果是调模型报 401检查 TaoToken 的 Key 是否填对、是否过期。注意A2A 的认证和模型调用的认证是两套别混。local proxy failed这个报错通常出现在 Agent 内部调模型时说明请求没发出去。在 Wireshark 里过滤目标地址是taotoken.net的包如果完全没有说明请求在本地就被拦了。检查 Agent 的 HTTP 客户端配置是不是设了错误的 proxy 环境变量。把HTTP_PROXY和HTTPS_PROXY清掉再试。reading choices 相关报错这通常是模型返回格式和 Agent 解析代码不匹配。抓包看模型响应需解密确认返回的 JSON 结构里choices字段是否存在。如果 Agent 用的是 OpenAI 兼容格式TaoToken 返回的也是兼容格式理论上没问题。但如果 Agent 代码里写死了某个字段路径而实际返回多了层包装就会报 reading choices 失败。对照抓包里的实际响应结构改解析代码。OAuth 相关报错如果 A2A 通信用了 OAuth 做 Agent 间认证抓包时过滤http.request.uri contains oauth或http.request.uri contains token看 token 请求和响应。常见问题是 token 过期没刷新或者 scope 不对。检查请求体里的grant_type和scope字段。再补一个 Codex auth.json 的场景。如果你用 Codex 类工具做 Agent认证信息存在auth.json里。这个文件里要有三件套Base URL、Key、Model ID。Base URL 填https://taotoken.net/apiKey 填控制台创建的Model ID 按实际填。抓包时如果发现请求发到了错误的地址先检查这个文件。排查顺序建议先看 Wireshark 里有没有请求发出再看请求头对不对再看响应码最后看响应体。大部分问题在前两步就能定位。6. 把抓包习惯固化进 Agent 开发流程抓包不是出问题才做的事。我现在搭多 Agent 系统第一步就是开 Wireshark把注册、分发、流式响应三段流量各抓一次存成 pcap 文件。这样后面改代码对比新旧 pcap一眼能看出通信行为变了没有。几个实用技巧给 Wireshark 设个显示过滤器按钮把常用的tcp.port 8001 || tcp.port 8002存下来一键切换。用File → Export Specified Packets把一次完整调用链导出方便分享给同事。流式响应如果太长用tshark命令行提取tshark -r a2a.pcap -Y http -T fields -e http.file_data | grep completed这条能快速确认流式响应里有没有 completed 状态。最后说下模型通道的选择。如果你只是验证 A2A 通信用模型对话页面手动发几条请求就够了。如果你要长期跑多 Agent 协作、做 Agent 编排建议用 Coding Plan配额和稳定性更适合持续调用。接入文档里有完整的 Base URL、Key、Model ID 配置说明照着填就行。抓包验证时确保所有 Agent 的模型调用都走同一条通道这样调用链才是干净的、可对比的。