1. 多 Agent 协作卡在哪:A2A 协议到底解决什么问题
如果你最近在折腾多 Agent 系统,大概率遇到过这种尴尬:两个 Agent 各自跑得好好的,一旦要让它们互相调用,就得写一堆胶水代码——A 的输出格式 B 不认识,B 的鉴权方式 A 又对不上。更麻烦的是模型接入层,每个 Agent 可能连的是不同厂商、不同 Key、不同 Base URL,协作链路一长,光是管理这些凭证就够头疼。
A2A(Agent-to-Agent)协议就是冲着这个痛点来的。它是一套开放标准,让不同平台、不同厂商构建的 Agent 能用统一的“语言”互相通信。你可以把它理解成 Agent 世界的普通话:不管你是哪家的 Agent,只要说普通话,就能对话。它和 MCP 是互补关系——MCP 解决 Agent 怎么调用工具和数据源(垂直连接),A2A 解决 Agent 之间怎么协作(水平通信)。一个形象的比喻是:MCP 像 USB-C 接口,连接 Agent 和它的资源;A2A 像网线,连接 Agent 和 Agent。
这篇文章适合谁?如果你正在搭建多 Agent 协作原型,或者想让自己的 Agent 接入更大的协作网络,又或者你已经被多个模型供应商的 Key 管理搞得焦头烂额,那这篇内容能帮你少走弯路。我会从 A2A 的核心机制讲起,然后重点演示怎么用 TaoToken 统一 Key 和 API 通道,给多个 Agent 提供一致的模型接入层,最后给出两个 Agent 通过 A2A 消息互调、经 TaoToken 完成推理请求的完整验证步骤。
先说清楚 A2A 的几个关键设计。Agent Card 是每个 Agent 的“数字名片”,通常放在/.well-known/agent.json路径下,里面写清楚这个 Agent 叫什么、能做什么、通信端点在哪、需要什么认证方式。其他 Agent 拿到这张名片,就知道该怎么跟它打交道。任务(Task)是协作的基本单元,有完整的生命周期:Pending、InProgress、Waiting、Completed、Failed、Canceled。消息(Message)和工件(Artifact)是数据交换的两种载体,消息用于传递指令和上下文,工件代表任务完成后的最终输出。通信模式上,A2A 支持同步请求-响应、SSE 流式更新和 Webhook 异步通知三种,底层基于 HTTP/S 和 JSON-RPC 2.0。
这些设计里,对多 Agent 协作落地影响最大的是“不透明执行”原则——Agent 之间只交换完成任务必需的信息,不暴露内部状态和实现细节。这意味着你可以让两个来自不同团队、甚至不同公司的 Agent 协作,而不用担心核心逻辑泄露。但这也带来一个现实问题:每个 Agent 自己怎么调模型、用哪家 Key,是各自的事。如果协作链路里有五个 Agent,每个都配一套模型凭证,管理成本会迅速膨胀。这就是为什么需要一个统一的模型接入层。
2. TaoToken 前置:给多 Agent 一个统一的模型接入层
在 A2A 协作链路里,Agent 之间的通信走 A2A 协议,但每个 Agent 内部要完成推理任务时,还是得调模型。如果每个 Agent 各自连不同的模型供应商,就会出现几个问题:Key 分散管理容易泄露,不同供应商的接口格式有差异,切换模型时要改多处配置,成本也不好统一核算。
TaoToken 在这里扮演的角色,就是给所有 Agent 提供一个一致的模型接入层。它提供统一的 Base URL 和 API Key,兼容 OpenAI 风格的接口,各个 Agent 不管用什么框架、跑在什么环境,都连同一个入口。这样你只需要维护一份凭证,换模型时也只改一个地方。
具体来说,TaoToken 的 API 地址是https://taotoken.net/api,官网是https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=。你需要先在控制台创建一个 API Key,然后就可以在各个 Agent 的配置里使用这个 Key 和 Base URL。
对于 A2A 协作场景,TaoToken 的价值体现在几个方面。第一,一致性:所有 Agent 用同一个 Base URL 和 Key,配置模板可以复用,减少出错概率。第二,可观测:所有推理请求经过同一个通道,便于统一记录和排查。第三,灵活性:底层模型可以按需切换,Agent 侧不用改代码。第四,成本可控:统一计费,避免多个供应商账单分散。
这里要强调一点:TaoToken 是合规的模型接入服务,不是所谓的“中转”或“代理”。它提供的是标准的 API 通道,你用它来统一管理模型调用,就像用云服务统一管理服务器一样正常。
如果你还没有 Key,可以先去控制台创建一个。创建完成后,记下 Key 的值,后面配置里要用。建议把 Key 存在环境变量里,不要硬编码在代码中,这是基本的安全习惯。
对于多 Agent 协作,我建议的做法是:每个 Agent 的模型配置都指向 TaoToken 的 Base URL,使用同一个 Key(或者按 Agent 分配不同的 Key 以便区分用量)。这样 A2A 消息在 Agent 之间传递时,每个 Agent 处理消息、调用模型、返回结果的流程都是一致的,协作链路的调试也会简单很多。
3. 可复制配置:Base URL、Key 与 Model ID 三件套
这一节给出具体的配置片段,你可以直接复制到自己的项目里。核心是三件套:Base URL、API Key、Model ID。不管你是用 Claude Code、Cline、Codex 还是自己写的 Agent 框架,这三个值都是必须的。
先看通用的环境变量配置。在你的.env文件或系统环境变量里加上:
TAOTOKEN_BASE_URL=https://taotoken.net/api TAOTOKEN_API_KEY=sk-你的实际Key TAOTOKEN_MODEL_ID=claude-sonnet-4-20250514Model ID 根据你实际使用的模型填写,这里只是示例。TaoToken 支持多种模型,你可以在控制台或文档里查看可用的 Model ID 列表。
如果你用的是 Claude Code,配置文件通常在~/.claude/settings.json或项目级的.claude/settings.json。配置片段如下:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的实际Key", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" } }注意这里的 Base URL 是https://taotoken.net/api,不要加多余的路径。Key 填你创建的那个。Model 填你要用的模型 ID。
如果你用的是 Cline 或类似的 VS Code 插件,配置通常在插件的设置界面里,或者对应的 JSON 配置文件中。以 Cline 为例,在设置里选择 “OpenAI Compatible” 或类似选项,然后填入:
{ "apiProvider": "openai", "openAiBaseUrl": "https://taotoken.net/api", "openAiApiKey": "sk-你的实际Key", "openAiModelId": "claude-sonnet-4-20250514" }如果你用的是 Codex,配置文件通常在~/.codex/auth.json或项目级配置里。配置片段:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的实际Key", "model": "claude-sonnet-4-20250514" }对于自己写的 Agent,以 Python 为例,用 OpenAI SDK 的配置方式:
import os from openai import OpenAI client = OpenAI( base_url=os.getenv("TAOTOKEN_BASE_URL", "https://taotoken.net/api"), api_key=os.getenv("TAOTOKEN_API_KEY") ) response = client.chat.completions.create( model=os.getenv("TAOTOKEN_MODEL_ID", "claude-sonnet-4-20250514"), messages=[{"role": "user", "content": "你好"}] ) print(response.choices[0].message.content)这段代码里,base_url指向 TaoToken 的 API 地址,api_key从环境变量读取,model指定模型 ID。这样你的 Agent 就通过 TaoToken 统一接入模型了。
对于 A2A 协作场景,每个 Agent 都用这套配置。你可以把这段配置封装成一个共享的模块,各个 Agent 导入使用,确保一致性。比如建一个model_client.py:
import os from openai import OpenAI def get_client(): return OpenAI( base_url=os.getenv("TAOTOKEN_BASE_URL", "https://taotoken.net/api"), api_key=os.getenv("TAOTOKEN_API_KEY") ) def get_model_id(): return os.getenv("TAOTOKEN_MODEL_ID", "claude-sonnet-4-20250514")然后每个 Agent 里这样用:
from model_client import get_client, get_model_id client = get_client() response = client.chat.completions.create( model=get_model_id(), messages=[{"role": "user", "content": "处理这个任务"}] )这样配置的好处是,换模型或换 Key 时只改环境变量,所有 Agent 自动生效。对于 A2A 协作链路,这意味着你可以在不触碰任何 Agent 业务代码的情况下,统一升级模型或调整接入参数。
再强调一下三件套的对应关系:Base URL 是https://taotoken.net/api,Key 是你创建的那个sk-开头的字符串,Model ID 是你要用的模型标识。这三个值在 Claude Code、Cline、Codex 或自研 Agent 里的字段名可能不同,但本质是一样的。配置时注意不要写错路径,Base URL 后面不要加/v1或其他后缀,直接用https://taotoken.net/api即可。
4. 验证请求:两个 Agent 通过 A2A 互调完成推理
这一节我们来实际验证一下。目标是搭建两个 Agent:Agent A 作为客户端,Agent B 作为服务端。Agent A 通过 A2A 协议向 Agent B 发送任务请求,Agent B 收到后调用模型(经 TaoToken)处理,然后把结果返回给 Agent A。
先定义 Agent B 的 Agent Card。按照 A2A 规范,放在/.well-known/agent.json路径下:
{ "name": "EchoAgent", "description": "一个简单的回声 Agent,接收文本并返回模型处理结果", "version": "1.0.0", "url": "http://localhost:8001", "capabilities": { "streaming": false, "pushNotifications": false }, "skills": [ { "id": "echo", "name": "Echo", "description": "接收文本输入,返回模型生成的回复", "inputModes": ["text"], "outputModes": ["text"] } ], "authentication": { "schemes": ["none"] } }这个 Agent Card 声明了 Agent B 的名字、描述、端点地址、能力、技能和认证方式。这里为了演示简单,认证设为 none,实际生产环境应该配置 OAuth 或 JWT。
Agent B 的服务端实现(用 Python + FastAPI 示例):
from fastapi import FastAPI, Request from model_client import get_client, get_model_id import uuid from datetime import datetime app = FastAPI() @app.get("/.well-known/agent.json") async def agent_card(): return { "name": "EchoAgent", "description": "一个简单的回声 Agent", "version": "1.0.0", "url": "http://localhost:8001", "capabilities": {"streaming": False, "pushNotifications": False}, "skills": [{ "id": "echo", "name": "Echo", "description": "接收文本输入,返回模型生成的回复", "inputModes": ["text"], "outputModes": ["text"] }], "authentication": {"schemes": ["none"]} } @app.post("/tasks/send") async def handle_task(request: Request): body = await request.json() task_id = body.get("id", str(uuid.uuid4())) message = body.get("message", {}) parts = message.get("parts", []) user_text = "" for part in parts: if part.get("type") == "text": user_text += part.get("text", "") client = get_client() response = client.chat.completions.create( model=get_model_id(), messages=[{"role": "user", "content": user_text}] ) reply = response.choices[0].message.content return { "id": task_id, "status": {"state": "completed"}, "artifacts": [{ "name": "response", "parts": [{"type": "text", "text": reply}] }] }这段代码做了两件事:暴露 Agent Card,以及处理/tasks/send请求。收到请求后,从消息里提取文本,调用模型(经 TaoToken),把结果作为 artifact 返回。
Agent A 的客户端实现:
import requests import uuid def send_task_to_agent_b(text): task_id = str(uuid.uuid4()) payload = { "id": task_id, "message": { "role": "user", "parts": [{"type": "text", "text": text}] } } response = requests.post( "http://localhost:8001/tasks/send", json=payload ) result = response.json() artifacts = result.get("artifacts", []) if artifacts: for part in artifacts[0].get("parts", []): if part.get("type") == "text": return part.get("text") return None if __name__ == "__main__": reply = send_task_to_agent_b("请用一句话解释什么是 A2A 协议") print("Agent B 的回复:", reply)运行步骤:先启动 Agent B 的服务(uvicorn agent_b:app --port 8001),然后运行 Agent A 的客户端脚本。Agent A 会向 Agent B 发送 A2A 格式的任务请求,Agent B 调用模型处理后返回结果。
预期结果:Agent A 打印出 Agent B 的回复,内容是关于 A2A 协议的解释。这个过程中,Agent B 的模型调用是通过 TaoToken 完成的,Base URL 和 Key 来自环境变量。
如果你想验证流式模式,可以把 Agent B 的/tasks/send改成/tasks/sendSubscribe,用 SSE 返回增量结果。不过对于验证接入层来说,同步模式已经足够。
这个例子虽然简单,但完整展示了 A2A 协作的核心流程:Agent Card 发现、任务请求、消息传递、模型调用、结果返回。你可以在此基础上扩展:增加更多技能、支持多轮对话、加入认证、实现任务状态查询等。
关键点在于,两个 Agent 的模型调用都走 TaoToken,配置一致。这样当你要换模型或调整参数时,只需要改环境变量,两个 Agent 同时生效。对于更复杂的多 Agent 协作网络,这个统一接入层的价值会更明显。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
配置和验证过程中,有几个报错特别常见。这一节逐个分析原因和解决办法。
401 Unauthorized
这是最常见的错误,通常有几个原因。第一,Key 没填对。检查你的TAOTOKEN_API_KEY是否完整,有没有多余的空格或换行。第二,Key 没生效。如果你是在环境变量里配置的,确认当前终端或进程能读到这个变量。可以用echo $TAOTOKEN_API_KEY检查。第三,Base URL 写错了。确认是https://taotoken.net/api,不要写成https://taotoken.net/api/v1或其他路径。第四,Key 被禁用或额度用完。去控制台检查 Key 的状态和余额。
如果报错信息里提到invalid_api_key或authentication failed,基本就是 Key 的问题。重新创建一个 Key,更新配置,重启 Agent 再试。
local proxy failed
这个报错通常出现在网络配置层面。可能的原因包括:本地网络环境有特殊设置,导致请求无法到达 TaoToken 的 API 地址;或者你的代码里配置了额外的代理参数,但代理不可用。解决办法:检查你的代码或环境变量里有没有HTTP_PROXY、HTTPS_PROXY之类的设置,如果有,确认代理是否正常工作。如果没有必要,可以暂时移除这些设置,直接用默认网络请求。另外,确认你的运行环境能正常访问外部 HTTPS 地址,可以用curl https://taotoken.net/api测试连通性。
reading choices 报错
这个错误通常发生在解析模型响应时。典型报错是KeyError: 'choices'或AttributeError: 'NoneType' object has no attribute 'choices'。原因可能是:请求没有成功,返回的是错误信息而不是正常的模型响应;或者响应格式和预期不符。排查方法:先把原始响应打印出来,看看实际返回了什么。在代码里加一行print(response)或print(response.json()),确认返回结构。如果是错误信息,根据错误内容进一步排查。另外,确认你用的 SDK 版本和接口格式匹配,OpenAI SDK 的client.chat.completions.create返回的对象里才有choices属性。
OAuth 相关报错
如果你在 A2A 配置里启用了 OAuth 认证,可能会遇到 token 获取失败、scope 不匹配、token 过期等问题。排查步骤:确认 OAuth 服务端的配置正确,client_id 和 client_secret 填对;确认请求的 scope 和 Agent Card 里声明的一致;检查 token 是否过期,必要时刷新。如果只是本地验证,可以先把认证设为 none,跑通流程后再加认证。
Agent Card 获取失败
如果 Agent A 找不到 Agent B 的 Agent Card,检查路径是否正确。按照规范,Agent Card 应该在/.well-known/agent.json。确认 Agent B 的服务确实暴露了这个路径,并且返回的是合法的 JSON。可以用浏览器或 curl 直接访问http://localhost:8001/.well-known/agent.json测试。
任务状态一直是 InProgress
如果任务提交后一直不完成,可能是 Agent B 处理超时或卡住了。检查 Agent B 的日志,看模型调用是否成功返回。如果模型调用耗时较长,考虑增加超时设置,或者改用异步模式。另外,确认 Agent B 的/tasks/send接口正确处理了请求并返回了 completed 状态。
模型返回内容为空
如果模型返回的 content 是空字符串,可能是 prompt 有问题,或者模型 ID 不对。确认TAOTOKEN_MODEL_ID是有效的模型标识。可以先用一个简单的 prompt 测试,比如“你好”,看是否能正常返回。
排查问题的通用思路是:先确认配置三件套(Base URL、Key、Model ID)正确,再确认网络连通,然后看请求和响应的原始内容,最后检查业务逻辑。大部分问题都出在前两步。
6. 从原型到生产:A2A 协作链路的下一步
跑通上面的验证后,你已经有了一个可运行的 A2A 协作原型。接下来可以考虑几个方向:增加更多 Agent,每个负责不同技能,通过 A2A 协议组成协作网络;引入任务状态查询和取消接口,支持长时间运行的任务;加入认证和权限控制,让协作更安全;用 SSE 实现流式更新,提升实时性。
对于模型接入层,TaoToken 的统一 Key 和 Base URL 让你在扩展 Agent 数量时不用重复配置。新加一个 Agent,只需要复用同一套环境变量,就能接入模型。这在多 Agent 协作场景里能省不少事。
如果你想让 Agent 具备更强的编码能力,可以了解 Coding Plan,它针对长期编码和 Agent 场景做了优化。如果你想先验证模型效果,可以直接在模型对话里测试。接入文档里有更详细的配置说明和示例代码。
统一接入层的价值,在 Agent 数量少的时候可能不明显,但一旦协作链路变长、Agent 变多,优势就会体现出来。配置一致、凭证统一、切换灵活,这些特性能让多 Agent 系统的维护成本大幅降低。