OmniRoute A2A Server 接入指南:基于 JSON-RPC 2.0 的智能路由 Agent 协议
【免费下载链接】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
OmniRoute 以智能路由 Agent 的身份对外提供 Agent-to-Agent Protocol(A2A)v0.3 服务:外部 Agent、工具链与自动化脚本可以通过统一的标准接口,把提示词交给 OmniRoute 的智能路由流水线执行,并拿到路由决策解释、成本明细与弹性追踪。读完本文,你将掌握 A2A 服务的发现与认证方式、四个 JSON-RPC 2.0 方法的完整调用格式、六个内置技能的能力边界,以及如何在本地部署中扩展自定义技能并接入 REST 辅助端点。
A2A 服务在 OmniRoute 中的定位
A2A(Agent-to-Agent Protocol)是让不同 Agent 之间可以互相发现能力、委派任务并交换结果的标准协议。OmniRoute 作为 AI 网关,其 A2A Server 将"智能路由"能力封装成标准化的 Agent 服务,对外暴露两张面:
- JSON-RPC 2.0 规范入口:
POST /a2a,这是官方约定的规范调用点,定义于 src/app/a2a/route.ts。 - REST 辅助端点:
/api/a2a/*,面向仪表盘与外部工具,提供状态查询、任务列表与任务取消能力。
任务的完整生命周期由A2ATaskManager管理(src/lib/a2a/taskManager.ts,默认 5 分钟 TTL),技能通过 src/lib/a2a/taskExecution.ts 中的A2A_SKILL_HANDLERS分发。
Agent 发现:Agent Card
任何 A2A 客户端接入前,都应先通过标准发现端点获取 OmniRoute 的 Agent Card(代理能力卡),了解其能力、技能与认证要求:
curl http://localhost:20128/.well-known/agent.json该端点返回的 Agent Card 包含以下关键信息:
| 字段 | 说明 |
|---|---|
name/description | Agent 名称与能力描述 |
url | A2A 规范入口(/a2a) |
version | 当前版本号,取自process.env.npm_package_version |
capabilities | 是否支持流式(streaming: true)等能力标记 |
skills[] | 已注册的技能清单(id、名称、描述、标签、示例调用) |
authentication | 认证方案(api-key,请求头Authorization) |
从实现看,src/app/.well-known/agent.json/route.ts 是动态生成Agent Card 的:version字段直接读取process.env.npm_package_version,随每次发版自动与package.json同步,无需手工维护;skills数组除内置六个技能外,还会追加 OmniConductor 舰队技能(getFleetSkills(),当 hub 未配置或离线时返回空数组,卡片依然有效)。响应带有Cache-Control: public, max-age=3600缓存头,客户端可缓存 1 小时。
认证机制
所有/a2a请求都需要通过Authorization头携带 API 密钥:
Authorization: Bearer YOUR_OMNIROUTE_API_KEY认证的具体逻辑位于 src/lib/a2a/authenticate.ts,采用与/v1流水线一致的REQUIRE_API_KEY姿态,分为三种情况:
- 服务端配置了密钥:请求必须携带匹配的密钥(使用
timingSafeEqual进行常数时间比较,避免时序侧信道),否则返回 JSON-RPC 错误-32600(Unauthorized)。 - 要求 API 密钥的开关开启(
REQUIRE_API_KEY):必须提供合法的 OmniRoute 密钥。 - 未配置任何密钥:认证被绕过,允许无密钥调用(keyless 本地优先模式),这也是开箱即用的默认行为。
值得注意的安全细节:resolveA2AOwner()会对调用方的 API 密钥取 SHA-256 哈希并截取前 32 位作为owner id,用于任务可见性隔离——带 owner 的任务只对同一 owner 可见,无密钥的本地调用产生的任务则对所有人可见。相应的越权防护测试见 tests/unit/a2a-task-owner-idor.test.ts。
启用 A2A 服务
A2A 由Endpoints(端点)→ A2A开关控制,默认关闭。当开关关闭时:
GET /api/a2a/status报告status: "disabled"、online: false;- 对
POST /a2a的 JSON-RPC 调用返回 HTTP 503,并带 JSON-RPC 错误码-32000(A2A endpoint is disabled)。
该逻辑在 src/app/a2a/route.ts 的rejectIfA2ADisabled()中实现:读取settings.a2aEnabled,非true时直接拒绝。对应测试见 tests/unit/a2a-enabled-route.test.ts。因此接入前请先确认服务端已开启该开关。
JSON-RPC 2.0 方法详解
规范入口POST /a2a提供四个方法:message/send(同步执行)、message/stream(SSE 流式)、tasks/get(查询任务)、tasks/cancel(取消任务)。路由处理器内部还会将方法名做归一化,并兼容 A2A 1.0 客户端的方法命名(详见下文"1.0 兼容层")。
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"} } }'响应示例:
{ "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" } } } }params中的可选字段说明:
| 字段 | 默认值 | 说明 |
|---|---|---|
skill | smart-routing | 要调用的技能 id,未指定时默认走智能路由 |
messages | 必填 | [{ role, content }]消息数组;也兼容{ message: { content } }或{ message: { parts: [...] } }的旧式形状(见 src/app/a2a/route.ts 的toMessageArray()) |
metadata.model | auto | 期望的模型,auto表示由路由引擎决定 |
metadata.combo | 无 | 指定组合(combo)策略,例如fast-coding |
metadata.budget | 无 | 成本预算上限,触发策略裁决检查 |
result.metadata中的四个核心字段是智能路由技能(smart-routing)返回的可观测数据,从 src/lib/a2a/skills/smartRouting.ts 的实现可以看到它们的来源:
routing_explanation:最终选择的模型与提供者、实测延迟(latencyMs)与成本;cost_envelope:成本包络,包含estimated(按 prompt tokens 估算)与actual(上游真实返回的cost字段)以及币种(USD);resilience_trace:弹性追踪数组,记录primary_selected事件;若上游触发回退(raw.fallbacksTriggered),还会追加fallback_needed事件;policy_verdict:策略裁决结果,当请求携带budget且实际成本超限时,allowed为false并给出原因。
message/stream— SSE 流式输出
与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,其行为要点:
- 心跳:每 15 秒发送一条
: heartbeat ...注释行维持连接(某些中间层代理会因超时掐断空闲 SSE 连接); - 分块:技能执行完成后,将
artifacts逐个以chunk事件发出(对非流式技能做了"模拟流式"); - 终态:最后发送
completed事件并携带完整metadata;出错时发送failed事件并带metadata.error; - 取消:监听客户端
abortSignal,连接中断时立即终止并标记失败; - 响应头:
Content-Type: text/event-stream、Cache-Control: no-cache, no-transform、X-Accel-Buffering: no(禁用 Nginx 缓冲,保证实时性)。
tasks/get— 查询任务状态
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"}}'返回任务的完整对象(id、skill、state、events 事件日志、artifacts、metadata 等)。查询时若任务已过期(见下文 TTL),且处于submitted/working中间态,会被自动标记为failed("Task expired")后再返回。
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"}}'取消操作带 owner 校验:调用方只能取消属于自己的任务(无 owner 的本地任务对所有人开放)。为防 IDOR 探测,任务不存在与"存在但不属于你"返回相同的Task not found错误。
A2A 1.0 兼容层
src/app/a2a/route.ts 内置了一层A2A v1.0 ↔ v0.3 兼容层:A2A 1.0 将方法重命名为SendMessage/SendStreamingMessage,并把同步响应的正文放到task.status.message.parts[].text。该层会对 1.0 方法名做别名映射,并将响应重塑为 1.0 的 Task 形状,使 a2a-sdk 1.x、Hermes 等 1.0 客户端可以不改动直接调用;v0.3 客户端则完全不受影响。对应测试见 tests/unit/a2a-v1-compat-10839.test.ts。
可用技能(Available Skills)
A2A_SKILL_HANDLERS(src/lib/a2a/taskExecution.ts)目前注册了六个技能,每个技能模块位于 src/lib/a2a/skills/ 目录:
| 技能 | ID | 说明 | 标签 | 示例提问 |
|---|---|---|---|---|
| 智能路由 | smart-routing | 通过 OmniRoute 的组合引擎 + 评分,将提示词路由到最优提供者/组合 | routing, providers | "Route this prompt via the best model" |
| 配额管理 | quota-management | 报告各提供者配额状态,辅助调用方决定何时限流/切换 | quota, providers | "Check quota for anthropic" |
| 提供者发现 | provider-discovery | 列出已安装提供者的能力、免费层标记、OAuth 状态 | providers, discovery | "What providers are available?" |
| 成本分析 | cost-analysis | 依据目录与近期用量估算请求/会话成本 | cost, usage | "Estimate cost for this conversation" |
| 健康报告 | health-report | 汇总各提供者的熔断、冷却、锁定状态 | health, resilience | "Show health status of all providers" |
| 列出能力 | list-capabilities | 以 Markdown 表格返回完整 Agent 技能目录及原始 SKILL.md 链接 | catalog, discovery, skills | "List all OmniRoute capabilities" |
其中smart-routing与quota-management是两个最有代表性的技能,源码实现分别位于 src/lib/a2a/skills/smartRouting.ts 与 src/lib/a2a/skills/quotaManagement.ts:
- smart-routing内部调用自身网关的
/v1/chat/completions(30 秒超时),透传model、messages与x-combo头,把上游返回的模型、提供者、成本、回退标记组装成上文所述的routing_explanation、cost_envelope、resilience_trace、policy_verdict。 - quota-management会并行拉取
/api/usage/quota与/api/combos(各 10 秒超时),然后对自然语言问题做意图分类:包含ranking/most quota/best时返回按剩余配额排序的排行榜;包含free/suggest时推荐免费组合(combo 名称含free/gratis者);其余情况返回汇总概览,并对剩余配额 ≤10% 的提供者给出警告。
list-capabilities技能详解
该技能对外部 Agent 特别有用——在发送 API 调用之前,先让它发现自己能做什么。它返回结构化 Markdown 表格工件:
| 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()统计覆盖率,每行包含rawUrl列,Agent 可以直接拉取完整 SKILL.md 注入上下文;metadata.totalSkills镜像目录规模。技能目录的完整定义见 docs/frameworks/AGENT-SKILLS.md。
REST 辅助端点
POST /a2a是规范入口,而下面的 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 发现,缓存 3600s) | 公开 |
/api/a2a/tasks | POST | 入站委派:将编码任务转发给 OmniConductor 舰队 | Bearer vsOMNIROUTE_API_KEY+a2aEnabled |
其中GET /api/a2a/status的实现(src/app/api/a2a/status/route.ts)除返回status/online外,还会附带当前任务统计(tasks按状态计数、活动流数量、最近任务时间),并在启用时动态内嵌 Agent Card 摘要(name、description、version、skills)。
最后一个 REST 端点是入站 Conductor 委派:外部 A2A Agent 可以通过POST /api/a2a/tasks把编码工作委派给 OmniConductor 舰队。请求体形如{ skill: "conductor" | "conductor-cli-<profile>", messages: [...], metadata: { conductor: { repo: { url, base_ref? }, mode?, cli?, model? } } },其中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 任务遵循如下状态机(定义于 src/lib/a2a/taskManager.ts):
submitted → working → completed → failed → cancelled- 任务默认在5 分钟后过期(TTL 可配置);
- 终态包括:
completed、failed、cancelled; - 每次状态迁移都会记录到事件日志(
events[]),并在开启持久化时写入 SQLite 历史表(a2a_tasks),同时通过事件总线广播agent.task.updated。
实现细节:A2ATaskManager构造函数签名为new A2ATaskManager(ttlMinutes = 5, persistence),TTL 以分钟为单位换算为毫秒;后台每60 秒清扫一次过期任务(将中间态任务标记为failed、"TTL expired",并将超过 2 倍 TTL 的终态任务从内存中移除)。历史记录的保留天数由OMNIROUTE_A2A_HISTORY_RETENTION_DAYS环境变量控制,默认 30 天,清理节流为每 24 小时至多一次。
自定义 TTL 的方法:forkA2ATaskManager的实例化并传入不同的值,例如new A2ATaskManager(15)得到 15 分钟 TTL。任务执行前的可观测内存召回(memory hits)默认 1.5 秒超时,可通过OMNIROUTE_A2A_MEMORY_HITS=0关闭(见 src/lib/a2a/taskExecution.ts 与 tests/unit/a2a-memory-hits.test.ts)。
错误码
| 代码 | 含义 |
|---|---|
-32700 | 解析错误(无效 JSON) |
-32600 | 无效请求 / 未授权 |
-32601 | 方法或技能不存在 |
-32602 | 参数无效 |
-32603 | 内部错误 |
-32000 | A2A 端点已禁用 |
各错误码对应的 HTTP 状态在 src/app/a2a/route.ts 的jsonRpcError()中定义:-32600→ 400,-32601→ 404,-32603→ 500,其余为 200(JSON-RPC 语义以错误码为准)。
集成示例
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);扩展:添加新技能
如果你需要让 OmniRoute 的 A2A Server 暴露更多能力,仓库给出了清晰的五步扩展路径:
创建技能文件:
src/lib/a2a/skills/<your-skill>.ts,导出异步函数(task: A2ATask) => Promise<{ artifacts, metadata }>,参考现有技能(如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,覆盖正常路径与错误路径。更新文档:在本文对应的英文原版 docs/frameworks/A2A-SERVER.md(及其多语言译本,如 中文版)的 Available Skills 表中补充新技能。
小结
OmniRoute 的 A2A Server 让"智能路由"以标准 Agent 协议对外可发现、可调用、可观测:通过/.well-known/agent.json完成能力发现,通过POST /a2a的四个 JSON-RPC 方法完成同步/流式任务执行与生命周期管理,通过六个内置技能覆盖路由、配额、成本、健康等运维场景,并通过 REST 端点与 OmniConductor 舰队联动实现跨 Agent 的编码任务委派。对希望把 OmniRoute 接入自有 Agent 编排体系(如 OpenCode、Claude Code、Codex 生态)的开发者而言,这套接口既是网关能力的标准出口,也是可以按上文五步路径自由扩展的开放框架。
【免费下载链接】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),仅供参考