OmniRoute A2A Server 接入指南:Agent-to-Agent Protocol v0.3 智能路由代理服务详解
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
本文基于仓库文档 docs/i18n/ar/docs/frameworks/A2A-SERVER.md(阿拉伯语版)与 docs/frameworks/A2A-SERVER.md(英文原版)整理编写,并以仓库源码 src/app/a2a/route.ts、src/lib/a2a/taskManager.ts 等实现细节交叉验证。
OmniRoute 作为统一 AI 网关,除了标准的 OpenAI 兼容 API 之外,还提供了一面完整的Agent-to-Agent(A2A)协议服务,让其他 Agent 框架可以把 OmniRoute 当作一个"智能路由代理"来调用——把提示词交给它,由它完成模型选型、配额检查、成本预估与回退容错。本文面向希望在 Claude Code、自研 Agent 或任意支持 JSON-RPC/SSE 的程序中接入 OmniRoute A2A Server 的开发者,读完你将掌握:如何发现 Agent Card、如何完成认证与开关启用、四个核心 JSON-RPC 方法的完整调用格式与响应结构、六个内置技能的能力边界、任务生命周期与 TTL 机制,以及如何用 Python / TypeScript 在 10 行代码内完成首个调用。
A2A 服务的双面形态:JSON-RPC 主入口与 REST 辅助接口
从源码结构看,A2A 表面(surface)分为两个层面,职责各有分工:
- JSON-RPC 2.0,入口为
POST /a2a,是规范的(canonical)入口点,定义在 src/app/a2a/route.ts。message/send、message/stream、tasks/get、tasks/cancel四个方法都在这里分发。 - REST 辅助接口,位于
/api/a2a/*之下,面向仪表盘与外部工具,提供状态查询、任务列表、任务取消等操作。
任务由A2ATaskManager统一管理(src/lib/a2a/taskManager.ts,默认 5 分钟 TTL);技能(Skill)通过 src/lib/a2a/taskExecution.ts 中的A2A_SKILL_HANDLERS注册表分发执行。
端口说明:文档中的示例统一使用
http://localhost:20128作为 A2A Server 监听地址,实际端口以你启动 OmniRoute 时的配置为准。
Agent Discovery:通过 Agent Card 发现能力
任何 A2A 客户端的第一步都是发现对端能力。OmniRoute 按照 A2A 规范暴露了一个标准的发现端点:
curl http://localhost:20128/.well-known/agent.json该端点返回Agent Card,其中描述 OmniRoute 的能力(capabilities)、可用技能(skills)以及认证要求(authentication requirements)。外部 Agent 可以先读取这份卡片,再决定调用哪个技能。
据官方文档说明,Agent Card 中的version字段取自process.env.npm_package_version,因此每次发版时都会与package.json自动同步,不会出现版本号漂移。Agent Card 也应与实时更新的 352+ 供应商目录保持对齐,其中的供应商数量与 free/no-auth 元数据均来自运行时注册表。
认证机制与启用开关
Bearer Token 认证
所有/a2a请求都需要通过Authorization头携带 API Key:
Authorization: Bearer YOUR_OMNIROUTE_API_KEY从 src/lib/a2a/authenticate.ts 的authenticateA2ARequest实现可以看出认证的完整判定逻辑,分为三层:
- 若全局启用了
REQUIRE_API_KEY策略(isRequireApiKeyEnabled()为真),则请求必须携带有效的 OmniRoute API Key(通过isValidApiKey校验)。 - 若未启用强制策略、但配置了环境变量
OMNIROUTE_API_KEY,则用timingSafeEqual做常数时间比对,防止时序侧信道攻击。 - 若服务器上既未启用强制 Key、也未配置任何 Key,则认证直接放行——这是与
/v1一致的"本地优先、免钥(keyless)"默认姿态。
此外,resolveA2AOwner会对调用方的 API Key 做 SHA-256 哈希并取前 32 位十六进制字符作为任务的"所有者 ID",用于任务可见性隔离(对应安全公告 GHSA-jcm5-6wpp-wjj8):带所有者的任务仅对该所有者可见,而免钥调用产生的任务对所有调用者可见。
Endpoints 开关:A2A 默认关闭
A2A 功能由Endpoints 页面中的 A2A 开关控制,默认处于关闭状态。未启用时的行为(对应 src/app/a2a/route.ts 的rejectIfA2ADisabled):
GET /api/a2a/status返回status: "disabled"与online: false;- 对
POST /a2a的 JSON-RPC 调用返回HTTP 503,错误码为 JSON-RPC 标准扩展码-32000,消息为A2A endpoint is disabled. Enable it from the Endpoints page.
对应的单元测试见 tests/unit/a2a-enabled-route.test.ts。
JSON-RPC 2.0 核心方法
所有方法都遵循 JSON-RPC 2.0 规范:请求体包含jsonrpc: "2.0"、id、method、params四个字段。下面逐一说明。
message/send:同步执行
向某个技能发送消息并等待完整响应。这是最常用的方法:
curl -X POST http://localhost:20128/a2a \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_KEY" \ -d '{ "jsonrpc": "2.0", "id": "1", "method": "message/send", "params": { "skill": "smart-routing", "messages": [{"role": "user", "content": "Write a hello world in Python"}], "metadata": {"model": "auto", "combo": "fast-coding"} } }'params的三个字段说明:
| 字段 | 说明 |
|---|---|
skill | 要调用的技能 ID,缺省时路由到smart-routing(源码中params?.skill || "smart-routing") |
messages | 消息数组,每项为{ role, content };兼容 A2A v1.0 的message.content与message.parts[]形态 |
metadata | 可选,透传给技能的附加参数,例如model、combo、role等 |
成功响应示例:
{ "jsonrpc": "2.0", "id": "1", "result": { "task": { "id": "uuid", "state": "completed" }, "artifacts": [{ "type": "text", "content": "..." }], "metadata": { "routing_explanation": "Selected claude-sonnet via provider \"anthropic\" (latency: 1200ms, cost: $0.003)", "cost_envelope": { "estimated": 0.005, "actual": 0.003, "currency": "USD" }, "resilience_trace": [ { "event": "primary_selected", "provider": "anthropic", "timestamp": "..." } ], "policy_verdict": { "allowed": true, "reason": "within budget and quota limits" } } } }响应中的metadata是 OmniRoute A2A 最具价值的输出:
routing_explanation:人类可读的路由决策说明,例如选择了哪个模型、经由哪个供应商、延迟与成本;cost_envelope:成本信封,给出预估成本(estimated)与实际成本(actual)及币种;resilience_trace:韧性追踪,逐条记录primary_selected、fallback等事件,完整还原回退链路;policy_verdict:策略裁定,说明该请求是否在预算与配额限制之内。
在message/send的执行路径上,源码还会对smart-routing技能调用 src/lib/a2a/routingLogger.ts 的logRoutingDecision记录路由决策,供后续分析与审计。
message/stream:SSE 流式输出
message/stream与message/send参数完全一致,但响应改为Server-Sent Events(SSE),适合需要实时展示生成内容的场景:
curl -N -X POST http://localhost:20128/a2a \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_KEY" \ -d '{ "jsonrpc": "2.0", "id": "1", "method": "message/stream", "params": { "skill": "smart-routing", "messages": [{"role": "user", "content": "Explain quantum computing"}] } }'SSE 事件流示例:
data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"working"},"chunk":{"type":"text","content":"..."}}} : heartbeat 2026-03-03T17:00:00Z data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"completed"},"metadata":{...}}}从 src/lib/a2a/streaming.ts 源码可以看到三个关键实现细节:
- chunk 事件:
state: "working"状态下逐块推送chunk(文本片段); - heartbeat 心跳:每 15 秒发送一条
: heartbeat <ISO 时间>注释行保活,防止代理或负载均衡器断开空闲连接; - 完成事件:任务结束时推送
state: "completed"并携带完整metadata。
流式路径通过executeA2ATaskWithState(见 src/lib/a2a/taskExecution.ts)包装,任务执行期间还会尝试收集记忆命中(memory hits,可通过OMNIROUTE_A2A_MEMORY_HITS=0关闭)写入task.metadata.memoryHits,纯为可观测性用途,不会注入技能提示词。
tasks/get:查询任务状态
异步任务可以随时通过任务 ID 回查状态:
curl -X POST http://localhost:20128/a2a \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_KEY" \ -d '{"jsonrpc":"2.0","id":"2","method":"tasks/get","params":{"taskId":"TASK_UUID"}}'tasks/get在返回前会检查任务是否已过期:若任务处于submitted或working状态且已超过expiresAt,会先将其标记为failed(消息为Task expired)。同时执行所有者可见性校验——带所有者的任务对其他调用方返回"Task not found",避免 IDOR 探测(对应 tests/unit/a2a-task-owner-idor.test.ts)。
tasks/cancel:取消任务
curl -X POST http://localhost:20128/a2a \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_KEY" \ -d '{"jsonrpc":"2.0","id":"3","method":"tasks/cancel","params":{"taskId":"TASK_UUID"}}'tasks/cancel先将任务从submitted/working迁移到cancelled,随后技能执行线程会在executeA2ATaskWithState捕获状态迁移异常并保留原始错误,确保取消不会破坏正在进行的写路径。
版本兼容性提示:源码中还实现了一层A2A v1.0 与 v0.3 的兼容层(src/app/a2a/route.ts 顶部的
V1_METHOD_ALIASES):v1.0 客户端使用SendMessage/SendStreamingMessage方法名同样可以调用,且同步响应会被重塑为 v1.0 的task.status.message.parts[].text形态。因此 a2a-sdk 1.x、Hermes 等 v1.0 客户端可以直接接入,v0.3 客户端不受影响。
可用技能(Skills)清单
OmniRoute 通过 src/lib/a2a/taskExecution.ts 的A2A_SKILL_HANDLERS注册了 6 个 A2A 技能,每个技能模块位于 src/lib/a2a/skills/ 目录下:
| 技能 | ID | 说明 | 标签 | 示例问法 |
|---|---|---|---|---|
| Smart Routing | smart-routing | 使用 OmniRoute 的组合引擎与评分机制,将提示词路由到最优供应商/组合 | routing, providers | "Route this prompt via the best model" |
| Quota Management | quota-management | 报告各供应商配额状态,帮助调用方决定何时限流或切换 | quota, providers | "Check quota for anthropic" |
| Provider Discovery | provider-discovery | 列出已安装的供应商及其能力、免费额度标记、OAuth 状态 | providers, discovery | "What providers are available?" |
| Cost Analysis | cost-analysis | 基于目录与近期用量估算一次请求/一段对话的成本 | cost, usage | "Estimate cost for this conversation" |
| Health Report | health-report | 聚合各供应商的熔断器、冷却、锁定状态 | health, resilience | "Show health status of all providers" |
| List Capabilities | list-capabilities | 返回完整的 45 项 Agent 技能目录(23 项 API + 21 项 CLI + 1 项 config)为 Markdown 表格,附带原始 SKILL.md URL 供上下文注入 | catalog, discovery, skills | "List all OmniRoute capabilities" |
每个技能的实现都对应一个独立模块,例如:
- src/lib/a2a/skills/smartRouting.ts(
executeSmartRouting) - src/lib/a2a/skills/quotaManagement.ts
- src/lib/a2a/skills/providerDiscovery.ts
- src/lib/a2a/skills/costAnalysis.ts
- src/lib/a2a/skills/healthReport.ts
- src/lib/a2a/skills/listCapabilities.ts
list-capabilities 技能详解
对于需要在发送 API 调用前先了解 OmniRoute 暴露了哪些能力的外部 Agent 而言,list-capabilities尤其有用。它返回一个结构化的 Markdown 表格 artifact:
| ID | Name | Category | Area | Endpoints/Commands | Raw URL | | --- | --- | --- | --- | --- | --- | | omni-auth | Auth & Sessions | api | auth | POST /api/auth/login, ... | https://raw.githubusercontent.com/... | ...从 src/lib/a2a/skills/listCapabilities.ts 源码可见:
- 数据来自
getCatalog()与computeCoverage()(定义于 src/lib/agentSkills/catalog.ts); - 每行包含
rawUrl列,Agent 可以立即抓取完整的 SKILL.md 注入上下文; - 响应的
metadata.totalSkills反映目录规模(当前为 45 项),metadata.coverage分别给出 API(23)/CLI(21)/config(1)的覆盖统计。
REST 辅助 API:面向仪表盘与工具链
/a2a是规范的 JSON-RPC 入口,而下列 REST 端点提供辅助访问,供仪表盘与外部工具使用:
| 端点 | 方法 | 说明 | 认证 |
|---|---|---|---|
/api/a2a/status | GET | 服务器状态、已注册技能 | 公开 |
/api/a2a/tasks | GET | 带过滤条件列出任务 | management |
/api/a2a/tasks/[id] | GET | 按 ID 获取任务 | management |
/api/a2a/tasks/[id]/cancel | POST | 取消运行中的任务 | management |
/.well-known/agent.json | GET | Agent Card(A2A 发现) | 公开,缓存 3600 秒 |
/api/a2a/tasks | POST | 入站委派(inbound delegation)到 OmniConductor 舰队 | Bearer 与OMNIROUTE_API_KEY+a2aEnabled |
其中最后一个端点值得单独说明:入站 Conductor 委派允许外部 A2A Agent 通过 OmniRoute 把编码工作委派给 OmniConductor 舰队。请求体格式为:
{ "skill": "conductor" | "conductor-cli-<profile>", "messages": [{ "role": "...", "content": "..." }], "metadata": { "conductor": { "repo": { "url": "...", "base_ref": "..." }, "mode": "...", "cli": "...", "model": "..." } } }约束与流程要点:
- 只有 Conductor 舰队技能(即 Agent Card 上公布的技能)可被委派;
metadata.conductor.repo.url为必填(舰队在 git 仓库上工作);- 该路由使用服务端
CONDUCTOR_ORCHESTRATOR_TOKEN(回退CONDUCTOR_HUB_TOKEN)转换为 hub 的POST /v1/tasks,返回201 { conductor_task_id, state: "submitted" }; - 任务状态通过 SSE→A2A 镜像回流,可通过
GET /api/a2a/tasks?skill=conductor查看。
任务生命周期与 TTL
A2A 任务的状态机如下:
submitted → working → completed → failed → cancelled- 任务默认在5 分钟后过期(可配置);
- 终态为
completed、failed、cancelled; - 事件日志记录每一次状态迁移。
上述状态机在 src/lib/a2a/taskManager.ts 中有完整实现:
VALID_TRANSITIONS表严格约束合法迁移(例如completed之后不能再次迁移);createTask生成 UUID v4 任务 ID,并将input.metadata做浅拷贝写入任务自身的metadata运行时袋,避免运行时写入污染调用方原始输入;- 每次状态迁移都会:
- 追加
TaskEvent(时间戳 + 状态 + 可选消息); - 通过事件总线发出
agent.task.updated事件(供编排画布监听,且监听器异常绝不会拖垮写路径); - 尽力持久化到 SQLite 历史表(src/lib/db/a2aTasks.ts 的
upsertA2ATask/appendA2ATaskEvent),失败仅告警不阻断。
- 追加
TTL 配置
TTL 在A2ATaskManager构造函数中配置(src/lib/a2a/taskManager.ts,constructor(ttlMinutes: number = 5),默认 5 分钟)。要自定义,只需 forkA2ATaskManager实例化并传入不同值,例如new A2ATaskManager(15)即为 15 分钟 TTL。
后台定时器每60 秒清理一次过期任务:
- 非终态且超过
expiresAt的任务被标记为failed(消息TTL expired); - 终态且超过 2 倍 TTL 的任务从内存 Map 中删除;
- 历史表按
OMNIROUTE_A2A_HISTORY_RETENTION_DAYS(默认 30 天)清理,每天最多触发一次。
错误码对照表
遵循 JSON-RPC 2.0 标准错误码约定,并扩展了两个 A2A 专用错误码:
| 代码 | 含义 |
|---|---|
| -32700 | 解析错误(无效 JSON) |
| -32600 | 无效请求 / 未授权 |
| -32601 | 方法或技能不存在 |
| -32602 | 参数无效 |
| -32603 | 内部错误 |
| -32000 | A2A 端点未启用 |
HTTP 状态码与 JSON-RPC 错误码的映射(见 src/app/a2a/route.ts 的jsonRpcError):-32600→ 400,-32601→ 404,-32603→ 500,其余返回 200;-32000(端点禁用)返回 503。
集成示例:Python 与 TypeScript
Python(requests)
import requests resp = requests.post("http://localhost:20128/a2a", json={ "jsonrpc": "2.0", "id": "1", "method": "message/send", "params": { "skill": "smart-routing", "messages": [{"role": "user", "content": "Hello"}] } }, headers={"Authorization": "Bearer YOUR_KEY"}) result = resp.json()["result"] print(result["artifacts"][0]["content"]) print(result["metadata"]["routing_explanation"])TypeScript(fetch)
const resp = await fetch("http://localhost:20128/a2a", { method: "POST", headers: { "Content-Type": "application/json", Authorization: "Bearer YOUR_KEY", }, body: JSON.stringify({ jsonrpc: "2.0", id: "1", method: "message/send", params: { skill: "smart-routing", messages: [{ role: "user", content: "Hello" }], }, }), }); const { result } = await resp.json(); console.log(result.metadata.routing_explanation);两个示例都只依赖标准 HTTP 客户端,无需额外 SDK。注意:流式场景请改用message/stream并监听 SSE 事件。
扩展新技能:从零注册一个 A2A Skill
如果你需要让 OmniRoute 的 A2A 面暴露自有能力,可以按以下五步扩展:
创建技能文件
src/lib/a2a/skills/<your-skill>.ts,导出一个异步函数(task: A2ATask) => Promise<{ artifacts, metadata }>,参考现有技能(如 src/lib/a2a/skills/smartRouting.ts)的形态。注册处理器:在 src/lib/a2a/taskExecution.ts 的
A2A_SKILL_HANDLERS中添加条目:export const A2A_SKILL_HANDLERS = { // ...existing skills "your-skill": async (task) => { const skillModule = await import("./skills/yourSkill"); return skillModule.executeYourSkill(task); }, };暴露到 Agent Card:在
src/app/.well-known/agent.json/route.ts的skills数组中追加:{ "id": "your-skill", "name": "Your Skill", "description": "Brief, intent-focused description", "tags": ["routing", "quota"], "examples": ["Sample natural-language invocation"] }编写测试:创建
tests/unit/a2a-<your-skill>.test.ts,覆盖成功路径与错误路径(仓库现有测试可参考 tests/unit/a2a-cost-analysis-numeric-fallback.test.ts、tests/unit/a2a-routing-logger.test.ts 等)。更新文档:在本文档的 Available Skills 表中补充新技能条目。
延伸阅读与源码索引
- 协议总览:官方文档 A2A-SERVER.md、AGENT_PROTOCOLS_GUIDE.md
- 技能目录体系:AGENT-SKILLS.md、SKILLS.md
- 关键源码:src/app/a2a/route.ts(路由分发与 v1.0 兼容层)、src/lib/a2a/taskManager.ts(任务状态机与 TTL)、src/lib/a2a/taskExecution.ts(技能注册表)、src/lib/a2a/authenticate.ts(认证)、src/lib/a2a/streaming.ts(SSE 流式)
- 单元测试:仓库
tests/unit/a2a-*.test.ts系列覆盖启用开关、认证、任务所有权隔离、历史持久化、v1.0 兼容等场景
OmniRoute 的 A2A Server 把路由决策、配额管理、成本估算、健康报告与韧性追踪这些网关核心能力,封装成了标准 JSON-RPC 方法与可发现的技能目录。无论是把 OmniRoute 接入自研 Agent 编排、交给 Conductor 舰队做代码委派,还是让外部 Agent 在调用前先完成能力发现,这面 A2A 表面都提供了开箱即用的协议级入口——你只需要一个 API Key 和一次POST /a2a。
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考