OmniRoute A2A Server 实战指南:把 OmniRoute 打造成可被任意 Agent 调用的智能路由 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-to-Agent Protocol(A2A)v0.3 规范的 A2A Server,它把 OmniRoute 暴露为一个"智能路由 Agent":外部 Agent 通过标准的 JSON-RPC 2.0 协议调用smart-routing、quota-management等技能,即可复用 OmniRoute 的模型路由、成本核算、配额管理与弹性回退能力。读完本文,你将掌握 A2A 端点的发现、鉴权、四大核心 RPC 方法、六大内置技能、任务生命周期与错误码,并能在 Python/TypeScript 中完成真实调用,还能按规范扩展自己的 A2A 技能。
本文以 docs/i18n/fr/docs/frameworks/A2A-SERVER.md(法文版)与 docs/frameworks/A2A-SERVER.md(英文原版)为主体,结合仓库源码(
src/lib/a2a/、src/app/a2a/)进行原理级佐证。
一、A2A Server 概览:双面接口
OmniRoute 的 A2A 面由两个互补的入口构成:
- JSON-RPC 2.0 规范入口:
POST /a2a(规范入口,实现在src/app/a2a/route.ts),承载message/send、message/stream、tasks/get、tasks/cancel四个方法; - REST 辅助入口:
/api/a2a/*,为仪表盘和外部工具提供状态查询、任务列表、取消等辅助能力。
所有任务的追踪由A2ATaskManager负责(src/lib/a2a/taskManager.ts,默认 5 分钟 TTL),技能的调度则通过A2A_SKILL_HANDLERS注册表完成(src/lib/a2a/taskExecution.ts)。
二、Agent Discovery:发现 Agent Card
与其他 A2A Agent 一样,外部调用方首先通过 well-known 端点发现 OmniRoute 的能力声明:
curl http://localhost:20128/.well-known/agent.json该端点返回描述 OmniRoute 能力、技能清单与鉴权要求的Agent Card。Agent Card 中的version字段取自process.env.npm_package_version,因此每次发布都会自动与package.json保持同步,无需手工维护版本号。
Agent Card 应始终与运行时 352+ 提供方目录保持一致,提供方数量与 free/no-auth 元数据均来自运行时注册表。
三、启用 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)。
四、Authentication:Bearer API Key
所有/a2a请求都需要通过Authorization头携带 API Key:
Authorization: Bearer YOUR_OMNIROUTE_API_KEY如果服务器上未配置任何 API Key,则鉴权被跳过(keyless 本地优先模式)。鉴权逻辑集中实现在 src/lib/a2a/authenticate.ts:
- 若
REQUIRE_API_KEY特性开启,则校验请求携带的 key 是否为合法 OmniRoute key(isValidApiKey); - 否则若配置了
OMNIROUTE_API_KEY环境变量,则用timingSafeEqual做常量时间比较; - 否则(既无要求也无配置)放行所有请求。
同时,resolveA2AOwner会对调用方的 API Key 做 SHA-256 哈希并截取前 32 位作为任务的 owner 标识(GHSA-jcm5-6wpp-wjj8),用于任务可见性隔离:带 owner 的任务只对同一 owner 可见,keyless 下产生的无主任务对所有调用方可见。tasks/get、tasks/cancel、listTasks均执行该 owner 作用域检查。
五、JSON-RPC 2.0 方法
所有方法统一走POST http://localhost:20128/a2a,请求体遵循 JSON-RPC 2.0 规范(jsonrpc、id、method、params)。
5.1message/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" } } } }metadata是smart-routing技能的精华所在:routing_explanation解释选中了哪个模型与提供方,cost_envelope给出预估/实际成本,resilience_trace记录弹性回退事件,policy_verdict给出预算与配额策略裁决。
5.2message/stream— SSE 流式返回
与message/send相同,但通过 Server-Sent Events 实时推送:
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":{...}}}流式过程中会周期性发送: heartbeat ...注释行保活;A2ATaskManager内部通过beginStream()/endStream()跟踪活跃流数量(activeStreams),统计信息可通过getStats()获取。
5.3tasks/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"}}'底层由 src/lib/a2a/taskManager.ts 的getTask实现:读取时若发现任务已超过expiresAt且仍处于非终态,会先将其标记为failed("Task expired"),再按 owner 可见性过滤返回。
5.4tasks/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"}}'cancelTask在变更状态之前执行 owner 检查,且对"任务不存在"与"任务存在但不属于你"返回相同的 not-found 错误,防止 IDOR 探测。
六、Available Skills:六大内置技能
OmniRoute 通过 src/lib/a2a/taskExecution.ts 中的A2A_SKILL_HANDLERS注册了 6 个 A2A 技能,每个技能模块位于src/lib/a2a/skills/目录下:
| 技能 | ID | 描述 | 标签 | 示例调用 |
|---|---|---|---|---|
| Smart Routing | smart-routing | 使用 OmniRoute 的 combo 引擎 + 评分将提示词路由到最优提供方/combo | 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 配置)的 markdown 表格,附带原始 SKILL.md URL | catalog, discovery, skills | "List all OmniRoute capabilities" |
以smart-routing为例,其实现位于 src/lib/a2a/skills/smartRouting.ts:它从任务输入中读取metadata.model(默认"auto")、metadata.combo与metadata.budget,随后调用 OmniRoute 自身的/v1/chat/completions完成路由(请求超时 30 秒),最后组装routing_explanation、cost_envelope、resilience_trace(包含可选的fallback_needed事件)与policy_verdict(当实际成本超出budget时裁决allowed: false)。
list-capabilities技能对外部 Agent 尤其有用:它返回结构化的 markdown 表格 artifact,每行包含 ID、名称、类别、区域、端点和rawUrl列,Agent 拿到rawUrl后即可立即抓取完整 SKILL.md 注入上下文;metadata.totalSkills字段镜像目录大小(当前为 45)。实现见src/lib/a2a/skills/listCapabilities.ts,可配合 docs/frameworks/AGENT-SKILLS.md 阅读。
七、Task 生命周期与 TTL
任务遵循如下状态机:
submitted → working → completed → failed → cancelled- 任务默认在 5 分钟后过期(
ttlMinutes,可在A2ATaskManager构造时传入其他值,如new A2ATaskManager(15)表示 15 分钟 TTL); - 终态为
completed、failed、cancelled; - 事件日志记录每一次状态迁移。
源码层面(src/lib/a2a/taskManager.ts)有更多细节值得关注:
- 合法迁移表:
VALID_TRANSITIONS明确定义了每个状态允许的后续状态(如submitted只能转working/failed/cancelled),非法迁移直接抛错; - 后台清理:构造函数启动一个每 60 秒执行一次的
cleanupExpired定时器,将过期且未处于终态的任务标记为failed(消息 "TTL expired"),并清理超过 2×TTL 的终态任务; - 历史持久化:任务通过
upsertA2ATask、appendA2ATaskEvent写入 SQLite 历史表(尽力而为,失败仅告警不影响内存主路径),并通过purgeA2AHistory按保留天数清理历史,保留天数由环境变量OMNIROUTE_A2A_HISTORY_RETENTION_DAYS控制(默认 30 天); - 可观测性:每次状态迁移会通过事件总线发布
agent.task.updated事件(监听器异常不会破坏任务写入路径);任务执行时还会以最后一条用户消息为查询词做记忆检索(OMNIROUTE_A2A_MEMORY_HITS=0可关闭),命中结果仅作为可观测数据写入metadata.memoryHits,不会注入技能提示词。
八、Error Codes:错误码表
| 代码 | 含义 |
|---|---|
| -32700 | 解析错误(非法 JSON) |
| -32600 | 无效请求 / 未授权 |
| -32601 | 方法或技能未找到 |
| -32602 | 无效参数 |
| -32603 | 内部错误 |
| -32000 | A2A 端点未启用 |
九、REST 辅助 API
/a2a是规范的 JSON-RPC 入口,以下 REST 端点为仪表盘与外部工具提供辅助访问(详见 docs/frameworks/A2A-SERVER.md):
| 端点 | 方法 | 描述 | 鉴权 |
|---|---|---|---|
/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 编队入站委派(Conductor PRD RF5) | Bearer 与OMNIROUTE_API_KEY+a2aEnabled |
入站 Conductor 委派:外部 A2A Agent 可通过POST /api/a2a/tasks将编码工作委派给 OmniConductor 编队。请求体为{ skill: "conductor" | "conductor-cli-<profile>", messages: [{role, content}], metadata: { conductor: { repo: { url, base_ref? }, mode?, cli?, model? } } }—— 只有 Agent Card 上公布的 Conductor 编队技能可被委派,且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查看。
十、集成示例
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);两个示例都读取了result.artifacts[0].content(技能输出文本)与result.metadata.routing_explanation(路由解释),前者拿到答案,后者拿到"为什么选了这条路由"的可审计证据。
十一、扩展指南:添加一个新 Skill
若需为 A2A Server 增加自定义技能,可按以下五步操作(前提是 fork 或本地运行此仓库,仓库本身是只读的):
创建技能文件:
src/lib/a2a/skills/<your-skill>.ts,导出一个异步函数(task: A2ATask) => Promise<{ artifacts, metadata }>,参考现有技能(如smartRouting.ts)的形状;注册 handler:在
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,覆盖正常路径与错误路径;更新文档:在本文对应的
Available Skills表格中登记新技能。
十二、源码脉络速览
- 任务状态机与 TTL:src/lib/a2a/taskManager.ts ——
A2ATaskManager类、VALID_TRANSITIONS、60 秒清理定时器、SQLite 历史持久化; - 技能调度:src/lib/a2a/taskExecution.ts ——
A2A_SKILL_HANDLERS注册表、executeA2ATaskWithState(统一完成/失败收尾与记忆命中采集); - 技能实现:src/lib/a2a/skills/ ——
smartRouting.ts、quotaManagement.ts、providerDiscovery.ts、costAnalysis.ts、healthReport.ts、listCapabilities.ts; - 鉴权与 owner 隔离:src/lib/a2a/authenticate.ts ——
authenticateA2ARequest、resolveA2AOwner; - 入口路由:
src/app/a2a/route.ts(JSON-RPC 规范入口)、src/app/api/a2a/*(REST 辅助端点); - 官方文档:docs/frameworks/A2A-SERVER.md(英文原版,含 Enablement、REST 表、扩展指南等更完整的说明)。
至此,你已拥有从"发现 → 鉴权 → 调用 → 追踪 → 扩展"的完整 A2A 接入链路:用message/send获得带路由解释与成本核算的同步结果,用message/stream获得实时流式输出,用tasks/get/tasks/cancel管理异步任务,再用六大内置技能覆盖路由、配额、发现、成本、健康与能力目录等场景——让任意遵循 A2A 协议的 Agent 都能把 OmniRoute 当成一个可信的"智能路由大脑"来使用。
【免费下载链接】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),仅供参考