
1. 从两个 Agent 互相“装死”说起A2A 协作链路到底卡在哪如果你已经用谷歌 Agent2Agent 协议A2A搭过两个智能体大概率遇到过这种场面Agent A 明明发出了tasks/sendAgent B 的日志里却什么都没有或者 B 回了结果A 那边一直卡在submitted状态不动。我试过最离谱的一次两个 Agent 各自跑得好好的一连起来就互相“装死”排查半天发现是两边的模型通道 Key 不统一一个走的是 A 供应商一个走的是 B 供应商返回结构对不上解析直接抛异常。这就是 A2A 落地时最真实的痛点。协议本身解决的是“智能体之间怎么说话”的问题它定义了 Agent Card、Task、Message、Artifact 这些概念让不同框架的 Agent 能互相发现、协商、回传结果。但协议不解决“每个 Agent 背后调用的模型从哪来、Key 怎么管、Base URL 怎么配”的问题。当你有三个、五个甚至更多 Agent 时每个 Agent 都去单独申请一套模型凭证维护成本会迅速失控而且一旦某个供应商的接口格式有差异协作链路就会在某个环节断掉。所以这篇内容聚焦一个具体场景用 TaoToken 作为统一的模型调用入口给多个 A2A Agent 提供一致的 Base URL 和 Key让任务分发和结果回传这条链路真正跑通。适合谁看已经在写 Agent 代码、想让两个以上智能体协作、但被多套凭证和多套接口格式折腾过的开发者。你不需要先精通 A2A 的全部规范只要能把两个 HTTP 服务跑起来就能跟着下面的步骤验证链路。核心检索词先明确Agent2Agent 协议是谷歌推出的开源智能体互操作协议TaoToken 是统一模型 API 通道两者结合解决的是“多 Agent 协作时模型调用层不统一”的问题。下面从环境准备开始一步步给出可复制的配置和验证方法。2. 前置准备TaoToken 统一 Key 与 A2A Agent 环境搭建在动手写 Agent 互调代码之前先把两件事准备好一个是 TaoToken 的 API Key 和 Base URL另一个是本地能跑起来的 A2A Agent 骨架。这两件事都不复杂但顺序不能乱否则后面验证时会分不清是 Key 的问题还是 Agent 代码的问题。先说 TaoToken 这边。你需要拿到一个 API Key这个 Key 会同时给多个 Agent 使用所以不要把它硬编码在某个 Agent 的源码里而是通过环境变量注入。Base URL 统一用https://taotoken.net/api注意这个地址不带任何查询参数直接作为 OpenAI 兼容接口的根路径使用。Key 的获取入口在控制台的 API Keys 页面登录后创建一个新 Key复制出来先存到本地临时文件里后面配置环境变量时要用。这里有个容易踩的坑很多人会把 Base URL 写成带/v1的完整路径然后在代码里又拼一次/v1结果请求发到了/v1/v1/chat/completions直接 404。TaoToken 的 Base URL 就是https://taotoken.net/api至于要不要加/v1取决于你用的 SDK。如果你用的是 OpenAI 官方 Python SDK它内部会自动拼/chat/completions所以 Base URL 保持https://taotoken.net/api即可如果你用的是裸requests发请求那完整路径就是https://taotoken.net/api/v1/chat/completions。这一点在后面的配置片段里会再强调。再说 A2A Agent 骨架。你不需要从零实现 A2A 协议的全部方法先用一个最小化的 HTTP 服务模拟两个 Agent 即可。Agent A 负责接收用户任务然后通过 A2A 的tasks/send把子任务转发给 Agent BAgent B 处理完后把结果作为 Artifact 回传给 Agent A。两个 Agent 背后都调用同一个 TaoToken 通道来生成内容。这样你就能在一个可控的环境里观察任务分发和结果回传的完整日志。环境变量建议统一放在一个.env文件里两个 Agent 启动时都加载同一个文件。这样做的目的是保证两个 Agent 用的是同一套凭证和同一个 Base URL排除“两边配置不一致”这个最常见的干扰因素。具体变量名和值在下一节给出你可以直接复制。另外提醒一点A2A 协议里的 Agent Card 通常放在/.well-known/agent.json里面会声明 Agent 的能力和端点 URL。在本地验证阶段你可以先手动写一个静态的 JSON 文件不需要动态生成。等链路跑通之后再考虑把 Agent Card 做成动态接口。这个顺序很重要先通链路再补规范细节。3. 可复制配置环境变量、Base URL 与 Agent 调用片段这一节给出可以直接复制到项目里的配置片段。我按文件类型分开写你照着放到对应位置即可。所有片段里的 Key 都用占位符sk-taotoken-xxxxxxxx表示你替换成自己创建的那个 Key。首先是.env文件放在项目根目录两个 Agent 共用# TaoToken 统一通道配置 TAOTOKEN_API_KEYsk-taotoken-xxxxxxxx TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_MODEL_IDclaude-sonnet-4-20250514 # Agent A 配置 AGENT_A_PORT8001 AGENT_A_CARD_URLhttp://localhost:8001/.well-known/agent.json # Agent B 配置 AGENT_B_PORT8002 AGENT_B_CARD_URLhttp://localhost:8002/.well-known/agent.json注意TAOTOKEN_MODEL_ID这一项它决定了两个 Agent 背后调用的具体模型。你可以在 TaoToken 的模型列表里选一个支持长上下文和工具调用的模型这里用claude-sonnet-4-20250514作为示例。两个 Agent 用同一个 Model ID是为了保证返回结构一致减少解析层的分支判断。接下来是 Agent B 的调用片段用 Python 的 OpenAI SDK 写。Agent B 收到任务后调用 TaoToken 生成内容然后把结果封装成 A2A 的 Artifact 返回import os from openai import OpenAI from fastapi import FastAPI, Request from pydantic import BaseModel app FastAPI() client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) class TaskRequest(BaseModel): task_id: str message: str app.post(/tasks/send) async def handle_task(req: TaskRequest): completion client.chat.completions.create( modelos.environ[TAOTOKEN_MODEL_ID], messages[ {role: system, content: 你是一个负责子任务的智能体请简洁回答。}, {role: user, content: req.message}, ], temperature0.3, ) result_text completion.choices[0].message.content return { task_id: req.task_id, status: completed, artifacts: [ {type: text, content: result_text} ], }这段代码的关键点有三个。第一base_url直接读环境变量不写死方便切换。第二model也读环境变量和 Agent A 保持一致。第三返回结构里带了task_id和status这是 A2A 协议里 Task 生命周期的一部分客户端可以根据status判断任务是否完成。然后是 Agent A 的调用片段它负责把用户任务分发给 Agent B并接收回传结果import os import httpx from fastapi import FastAPI from pydantic import BaseModel app FastAPI() AGENT_B_URL fhttp://localhost:{os.environ[AGENT_B_PORT]}/tasks/send class UserTask(BaseModel): task_id: str query: str app.post(/dispatch) async def dispatch(req: UserTask): async with httpx.AsyncClient(timeout60) as http: resp await http.post( AGENT_B_URL, json{task_id: req.task_id, message: req.query}, ) resp.raise_for_status() data resp.json() return { task_id: data[task_id], status: data[status], final_output: data[artifacts][0][content], }Agent A 本身不直接调用模型它只负责转发和汇总。这样做的好处是职责清晰Agent A 是协调者Agent B 是执行者模型调用统一在 Agent B 这一层通过 TaoToken 完成。如果你想让 Agent A 也具备模型能力可以再给它加一个独立的调用函数但 Base URL 和 Key 仍然复用同一套环境变量。最后附一个 Agent Card 的静态 JSON 示例放在/.well-known/agent.json路径下两个 Agent 各一份内容按各自能力改一下{ name: agent-b-executor, description: 负责执行子任务并返回文本结果, url: http://localhost:8002, capabilities: [text-generation, task-execution], endpoints: { tasks/send: http://localhost:8002/tasks/send } }这个 JSON 在本地验证阶段不是必须的但加上之后后面如果你想用 A2A 的发现机制去动态获取 Agent 能力就有地方可读。先放着不影响链路跑通。4. 验证请求两个 Agent 互调后的日志与成功结果判断配置写完之后最关键的一步是验证链路是否真的通了。很多人到这里会直接看 Agent A 的返回看到有内容就以为成功了但其实可能只是 Agent A 自己编了一段话Agent B 根本没被调用。所以验证要看三个层面的日志Agent A 的转发日志、Agent B 的接收日志、以及 TaoToken 侧的调用记录。先启动两个服务。Agent B 先起因为它要监听端口等请求uvicorn agent_b:app --port 8002 --reload然后起 Agent Auvicorn agent_a:app --port 8001 --reload两个服务都起来后用 curl 发一个测试请求给 Agent A 的/dispatch端点curl -X POST http://localhost:8001/dispatch \ -H Content-Type: application/json \ -d {task_id: test-001, query: 用一句话说明什么是A2A协议}预期返回类似这样{ task_id: test-001, status: completed, final_output: A2A协议是谷歌推出的开源智能体互操作协议让不同框架的AI智能体能够相互通信和协作。 }看到这个返回说明 Agent A 成功把请求转发给了 Agent BAgent B 通过 TaoToken 调用了模型并把结果回传给了 Agent A。但这还不够你要去 Agent B 的终端看日志确认它确实收到了task_id为test-001的请求。Agent B 的日志里应该出现类似这样的记录INFO: 127.0.0.1:xxxxx - POST /tasks/send HTTP/1.1 200 OK如果 Agent B 的日志里没有这条记录那说明 Agent A 的转发地址配错了或者 Agent B 没启动成功。这时候先检查AGENT_B_PORT环境变量是否和 Agent B 实际监听的端口一致。再进一步你可以去 TaoToken 控制台的调用记录页面看是否有一次模型调用发生在你发请求的时间点附近。如果有说明整条链路从 Agent A 到 Agent B 再到 TaoToken 是通的。如果 TaoToken 侧没有记录但 Agent B 返回了 200那可能是 Agent B 用了缓存或者 mock 数据需要检查代码里是否真的执行了client.chat.completions.create。还有一个验证技巧故意把 Agent B 的TAOTOKEN_API_KEY改成一个错误的值再发一次请求。如果 Agent A 返回的是 500 错误并且 Agent B 日志里出现 401 相关的报错说明链路是通的只是 Key 不对。这个反向验证能帮你快速区分“链路不通”和“凭证错误”两种情况。成功跑通之后你可以把task_id换成动态生成的 UUID连续发多个请求观察两个 Agent 是否能并发处理。如果并发时出现结果错乱比如task_id对不上那说明你的 Agent B 在处理时没有正确隔离每个请求的上下文需要检查是否有全局变量被多个请求共享。5. 本篇常见错排查401、local proxy failed、reading choices 与 OAuth 报错链路跑不通时报错信息通常集中在几个固定的位置。下面按真实遇到的频率从高到低排列每条都给出触发条件和处理方式。401 Unauthorized是最常见的。触发条件通常是TAOTOKEN_API_KEY没有正确注入到环境变量里或者 Key 复制时带了多余的空格。检查方法很简单在 Agent B 的代码里加一行print(os.environ.get(TAOTOKEN_API_KEY))看输出的值是否和你创建的一致。如果输出是None说明.env文件没有被加载需要确认你用的框架是否自动读取.env如果没有手动用python-dotenv加载。另外注意Key 不要写在代码里然后提交到 Git环境变量是最低要求。local proxy failed这个报错通常出现在你本地设置了 HTTP 代理但代理没有正确处理taotoken.net的请求。触发条件是系统环境变量里有HTTP_PROXY或HTTPS_PROXY而代理服务没有运行或者规则不匹配。处理方式是检查你的终端环境临时取消代理设置再试。如果你确实需要通过代理访问外网确保代理规则里把taotoken.net加入直连或正确转发。这个报错和 TaoToken 本身无关是本地网络环境的问题。reading choices 报错完整信息通常是KeyError: choices或者TypeError: NoneType object is not subscriptable。触发条件是模型返回的 JSON 结构里没有choices字段而你的代码直接取了completion.choices[0]。这种情况多半是因为请求发到了错误的端点比如 Base URL 拼错导致返回了一个 HTML 错误页解析 JSON 时失败。检查你的 Base URL 是否是https://taotoken.net/api以及 SDK 是否自动拼接了正确的路径。如果你用的是裸requests确认完整 URL 是https://taotoken.net/api/v1/chat/completions。OAuth 相关报错比如invalid_grant或unauthorized_client通常出现在你尝试用 OAuth 方式获取 Token 而不是直接用 API Key 的场景。TaoToken 的 API 通道用的是 Key 认证不需要走 OAuth 流程。如果你在代码里引入了 OAuth 库或者配置了client_id、client_secret先去掉这些改用api_key参数。这个报错和 A2A 协议本身无关是认证方式选错了。还有一个不报错但链路不通的情况Agent A 返回了结果但内容是空的字符串。触发条件是 Agent B 的temperature设得过高或者max_tokens设得太小导致模型返回了空内容。检查 Agent B 的调用参数把temperature降到 0.3 左右max_tokens至少设成 256。另外如果你在 system message 里写了“请简洁回答”模型可能会返回一个空字符串改成“请用一句话回答”更稳妥。最后提醒一个配置层面的坑如果你同时用了 CC Switch 或 Cline MCP 这类工具来管理多个模型通道确保它们指向的 Base URL、Key、Model ID 三件套和你在.env里写的一致。三件套里任何一个不一致都会导致 Agent 之间的返回结构出现差异进而让 A2A 的结果回传解析失败。排查时先把三件套对齐再去看协议层的日志。6. 把统一通道用起来从两个 Agent 到可持续的协作链路链路跑通之后你可以做几件让这套东西真正可用的事。第一件是把 Agent Card 从静态 JSON 改成动态接口让 Agent A 在分发任务前先拉取 Agent B 的卡片确认对方具备处理该任务的能力。这样当你有多个执行型 Agent 时协调者可以根据能力标签做路由而不是硬编码一个 URL。第二件是把task_id的生成和追踪做成一个轻量的任务表。每次 Agent A 分发任务时记录task_id、目标 Agent、发起时间Agent B 回传后更新状态和结果。这样当某个任务卡住时你能快速定位是哪个环节没有返回。这个任务表不需要数据库用一个内存字典或者本地 JSON 文件就够验证阶段够用。第三件是给 TaoToken 的调用加上重试和超时。Agent B 在调用模型时如果遇到网络抖动一次失败不应该让整个 A2A 任务失败。用tenacity或者手写一个简单的重试循环设置最多重试 2 次每次间隔 1 秒。超时时间设成 60 秒因为有些模型在长上下文下响应会慢一些。这些参数在.env里也可以配方便调整。如果你打算把这条链路用到更接近生产的场景建议把 Agent A 和 Agent B 的日志统一收集到一个地方按task_id串联。这样排查问题时你能一眼看到从请求进入到模型返回的完整路径。日志里至少记录四个时间点Agent A 收到请求、Agent A 发出转发、Agent B 收到请求、Agent B 收到模型返回。这四个时间点之间的差值能帮你判断瓶颈在转发层还是模型层。最后关于模型通道的选择TaoToken 的 API 入口是https://taotoken.net/apiKey 在控制台的 API Keys 页面创建。如果你后面要接入更多 Agent每个 Agent 都复用同一套环境变量即可不需要单独申请凭证。需要看接入细节的话接入文档在https://taotoken.net/doc想先验证模型返回是否正常可以用模型对话页面发一条测试消息如果打算长期跑编码类或 Agent 类任务Coding Plan 的入口在https://taotoken.net/coding-plan。这几个入口按需取用先把两个 Agent 的链路跑稳再逐步扩展。