OmniRoute A2A Server 全解析把 AI 网关变成 A2A 协议智能路由 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, RTKCaveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550 contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRouteOmniRoute 除了作为 OpenAI 兼容网关还内置了一个 A2AAgent-to-Agent Protocol v0.3服务端让外部智能体可以通过标准的 JSON-RPC 2.0 接口把请求路由、配额查询、成本分析、健康报告等能力当作技能Skill调用。本文以仓库中的 A2A 文档为骨架结合 JSON-RPC 路由、任务管理器、技能执行器 等源码讲清 A2A 端点的发现、认证、四个核心方法、六类技能、任务生命周期以及如何扩展新技能——读完后你既能直接接入 OmniRoute 的 A2A 服务也能读懂其底层的任务状态机与流式实现。整体架构一个协议两张脸A2A 表面在 OmniRoute 中分为两个入口这一点在源码中体现得非常清晰JSON-RPC 2.0 规范入口POST /a2a定义在 src/app/a2a/route.ts是 A2A 协议客户端如 a2a-sdk、Hermes 等的标准对接面REST 辅助入口/api/a2a/*状态、任务列表、取消面向仪表盘与外部工具代码位于 src/app/api/a2a/。任务统一由A2ATaskManagersrc/lib/a2a/taskManager.ts默认 5 分钟 TTL跟踪技能则通过 src/lib/a2a/taskExecution.ts 中的A2A_SKILL_HANDLERS注册表分发。route.ts的主处理流程可以概括为认证 → 解析 JSON-RPC 信封 → 检查 A2A 开关 → 解析调用方身份owner→ 按method分发到四个 case 之一。Agent 发现Agent Discovery其他 Agent 首先通过 well-known 地址发现 OmniRoute 的能力curl http://localhost:20128/.well-known/agent.json该端点返回 Agent Card描述 OmniRoute 的能力、技能清单和认证要求。实现位于 src/app/.well-known/agent.json/route.ts有三个值得注意的实现细节版本自动同步Card 的version字段取自process.env.npm_package_version见 route.ts因此每次发版都与package.json保持一致无需手工维护动态技能合并Card 的技能数组在内置 6 个技能之后还会追加getFleetSkills()返回的 OmniConductor 车队技能缓存约 60 秒Hub 离线时为空数组Card 依然有效缓存策略响应头携带Cache-Control: public, max-age3600即 Agent Card 被 CDN/客户端缓存 1 小时。Agent Card 同时声明capabilities.streaming: true、pushNotifications: false以及认证方式api-keyAuthorization头调用方据此即可完成对接。文档也提醒Agent Card 应始终与实时的 352 提供商目录保持对齐提供商数量与 free/no-auth 元数据均来自运行时注册表。认证与启用开关认证所有/a2a请求都要求通过Authorization头携带 API KeyAuthorization: Bearer YOUR_OMNIROUTE_API_KEY如果服务器未配置 API Key 则跳过认证——这句话在源码中对应一个更完整的三级策略见 src/lib/a2a/authenticate.ts 的authenticateA2ARequestREQUIRE_API_KEY开启时必须携带有效的 OmniRoute API Key没有 Key 的请求还会回退到仪表盘会话认证isDashboardSessionAuthenticated这样仪表盘内置的 A2A playground 仅凭会话 Cookie 也能工作配置了OMNIROUTE_API_KEY时用timingSafeEqual做常量时间比较校验 Bearer Key防止时序侧信道两者都未配置保持 keyless local-first 默认姿态直接放行。JSON-RPC 路由与 REST 任务路由共用这一份实现注释中明确提到这是安全修复 GHSA-jcm5-6wpp-wjj8 的成果——此前 REST 任务路由完全没有认证调用两个入口从此不可能再各自漂移。启用开关A2A 由Endpoints → 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.。判断依据是getSettings()返回的settings.a2aEnabled字段。JSON-RPC 2.0 方法详解message/send— 同步执行向某个技能发送消息并等待完整响应。skill参数缺省时回退为smart-routingmessages既支持数组形式也兼容单条message.content/message.parts旧格式见 route.ts 的toMessageArray归一化逻辑。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 字段不是摆设——对照 src/lib/a2a/skills/smartRouting.ts 的实现技能内部以 30 秒超时调用本机的/v1/chat/completionscombo映射到x-combo请求头然后自己拼出routing_explanation含模型、provider、实测延迟与成本、cost_envelope按 prompt token 数粗估 vs 上游返回的实际值、resilience_trace上游触发回退时会追加fallback_needed事件以及policy_verdict当metadata.budget给出时用实际成本与预算比较得到 allowed/denied。执行成功后路由层还会把这次决策写入 routingLogger 的路由决策日志。message/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:{...}}}streaming.ts 给出了这条流的完整机制心跳每 15 秒发一条: heartbeat ISO时间注释行保活防止中间代理掐断空闲连接响应头Content-Type: text/event-stream、Cache-Control: no-cache, no-transform、X-Accel-Buffering: no关闭 Nginx 缓冲确保事件即时送达事件序列技能执行完成后逐条 artifact 发chunk事件state 为working最后发一条携带完整 metadata 的completed事件失败则发failed事件并附metadata.error取消传播监听请求的AbortSignal客户端断开时向流内写入 Cancelled 失败事件并关闭。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}}taskId也可写成id源码同时接受两种写法。任务不存在时返回-32601。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}}取消走updateTask(id, cancelled, ...)状态迁移注意终态任务无法再迁移见下文状态机对已终态任务调用取消会返回-32603。A2A 1.0 方法名兼容层除了 v0.3 的四个方法route.ts 还内置了一个 v1.0 ↔ v0.3 兼容层A2A 1.0 把方法重命名为SendMessage/SendStreamingMessage且同步响应中回复文本位于task.status.message.parts[].text。OmniRoute 通过V1_METHOD_ALIASES把 1.0 方法名映射回 v0.3再用buildV1Task把结果重塑成 1.0 客户端期望的形状state 字符串加TASK_STATE_前缀使 a2a-sdk 1.x 等客户端可以不改代码直接调用v0.3 客户端不受影响。该行为由 tests/unit/a2a-v1-compat-10839.test.ts 覆盖。可用技能SkillsOmniRoute 暴露 6 个 A2A 技能统一注册在 taskExecution.ts 的A2A_SKILL_HANDLERS中每个技能模块位于 src/lib/a2a/skills/技能名称ID说明标签示例Smart Routingsmart-routing通过 combo 引擎 评分将 prompt 路由到最优 provider/comborouting, providersRoute this prompt via the best modelQuota Managementquota-management报告各 provider 配额状态帮助调用方决定何时限流/切换quota, providersCheck quota for anthropicProvider Discoveryprovider-discovery列出已安装 provider 及其能力、free-tier 标记、OAuth 状态providers, discoveryWhat providers are available?Cost Analysiscost-analysis基于目录 近期用量估算请求/会话成本cost, usageEstimate cost for this conversationHealth Reporthealth-report汇总各 provider 的断路器、冷却、锁定状态health, resilienceShow health status of all providersList Capabilitieslist-capabilities返回完整 Agent Skills 目录API CLI 配置项的 Markdown 表格含 SKILL.md 原始 URLcatalog, discovery, skillsList all OmniRoute capabilities对应实现文件依次为 smartRouting.ts、quotaManagement.ts、providerDiscovery.ts、costAnalysis.ts、healthReport.ts、listCapabilities.ts。list-capabilities技能详解list-capabilities对需要先发现能力、再发调用的外部 Agent 尤其有用。它返回结构化的 Markdown 表格 artifact| ID | Name | Category | Area | Endpoints/Commands | Raw URL | | --- | --- | --- | --- | --- | --- | | omni-auth | Auth Sessions | api | auth | POST /api/auth/login, ... | https://raw.githubusercontent.com/... | ...每一行都带rawUrl列Agent 可以立即拉取完整的 SKILL.md 注入上下文metadata.totalSkills字段镜像目录规模。技能清单与仓库 skills/ 目录下的 SKILL.md 文件一一对应如 skills/omni-auth/ 等。技能执行前的记忆命中采集还有一个源码层面才可见的细节每个技能真正执行前taskExecution.ts 的collectMemoryHits会先按最后一条 user 消息做一次记忆召回。它有严格的防御边界默认 1500ms 超时MEMORY_RECALL_TIMEOUT_MS任何失败/超时都退化为空命中、绝不让任务失败结果只写入task.metadata.memoryHits并追加memory_hits历史事件纯粹用于可观测性不会注入技能提示词。环境变量OMNIROUTE_A2A_MEMORY_HITS0是总开关直接短路返回空数组。REST 辅助 APIJSON-RPC 端点/a2a是规范入口以下 REST 端点为仪表盘和外部工具提供辅助访问实现位于 src/app/api/a2a/端点方法说明认证/api/a2a/statusGET服务器状态、已注册技能公开/api/a2a/tasksGET按过滤条件列出任务management/api/a2a/tasks/[id]GET按 ID 获取任务management/api/a2a/tasks/[id]/cancelPOST取消运行中的任务management/.well-known/agent.jsonGETAgent CardA2A 发现公开缓存 3600s/api/a2a/tasksPOST向 OmniConductor 车队提交入站委派Conductor PRD RF5Bearer vsOMNIROUTE_API_KEYa2aEnabled入站 Conductor 委派POST /api/a2a/tasks外部 A2A Agent 可以通过 OmniRoute 把编码工作委派给 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 镜像回流RF1可通过GET /api/a2a/tasks?skillconductor查询。该路径有专门的测试 tests/unit/conductor-a2a-post.test.ts。任务生命周期与 TTLsubmitted → working → completed → failed → cancelledTTL任务默认 5 分钟后过期。TTL 在A2ATaskManager构造函数中配置taskManager.tsttlMinutes参数默认 5如需定制可自行实例化例如new A2ATaskManager(15)得到 15 分钟 TTL。注意生产路由通过getTaskManager()获取的全局单例使用默认值过期处理一个 60 秒周期的后台定时器扫描过期任务——未到期前访问getTask若发现非终态任务已过期会即时将其迁移为failed消息 Task expired回收终态任务在超过 2 倍 TTL 后从内存中删除终态completed、failed、cancelled一旦进入不可再迁移事件日志每次状态迁移都推入task.events数组时间戳 状态 可选消息。状态机的合法性由 taskManager.ts 的VALID_TRANSITIONS表严格约束const VALID_TRANSITIONS: RecordTaskState, TaskState[] { submitted: [working, failed, cancelled], working: [completed, failed, cancelled], completed: [], failed: [], cancelled: [], };所有者隔离Owner Scoping每次调用都通过resolveA2AOwner解析出 owner——有 API Key 时为 Key 的 SHA-256 前 32 位仪表盘会话为dashboardkeyless 姿态下为undefined。带 owner 的任务只对同一 owner 可见/可取消取消前会先做 owner 检查并用与任务不存在相同的错误掩盖存在但不是你的使 IDOR 探测无法区分两者。keyless 创建的任务保持对所有人可见本地优先姿态。行为由 tests/unit/a2a-task-owner-idor.test.ts 锁定。持久化与保留内存Map是活动任务的唯一事实来源同时每次写入都会 best-effort 落到 SQLite 历史表upsertA2ATask/appendA2ATaskEvent见 src/lib/db/a2aTasks.ts 的引入持久化失败只记日志、不阻断写入路径。历史行保留天数由OMNIROUTE_A2A_HISTORY_RETENTION_DAYS控制缺省 30 天清理动作被节流为每天至多一次。错误码错误码含义-32700解析错误JSON 非法-32600请求非法 / 未授权-32601方法或技能不存在-32602参数无效-32603内部错误技能执行失败等-32000A2A 端点被禁用HTTP 状态码与 JSON-RPC 码存在映射-32600→ 400-32601→ 404-32603→ 500其余为 200见 route.ts 的jsonRpcError而禁用端点-32000单独返回 503。如何新增一个技能文档给出五步扩展流程与源码结构完全对应创建技能文件src/lib/a2a/skills/your-skill.ts导出异步函数(task: A2ATask) Promise{ artifacts, metadata }可参照 smartRouting.ts 的形状注册处理器在 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); }, };采用动态import()意味着新技能模块只在首次被调用时才加载不影响启动时延在 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] }Agent Card 是外部 Agent 的发现依据不登记的技能对协议客户端不可见编写测试tests/unit/a2a-your-skill.test.ts覆盖正常路径与错误路径。可参考现有测试组织如 tests/unit/t09-a2a-lifecycle.test.ts生命周期、tests/unit/a2a-enabled-route.test.ts开关行为、tests/unit/a2a-cost-analysis-numeric-fallback.test.ts数值回退文档在本文件的Available Skills表格中补上新技能条目保持文档与A2A_SKILL_HANDLERS一致。集成示例Pythonrequestsimport 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])TypeScriptfetchconst 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);两个示例都省略了metadata如果需要约束路由可以追加{metadata: {model: auto, combo: fast-coding, budget: 0.5}}——model缺省为autocombo会映射为上游请求的x-combo头budget触发policy_verdict的预算裁决。小结与适用前提适用前提A2A 端点默认关闭需先在 Endpoints 页打开 A2A 开关默认端口为 20128示例中的localhost:20128需按实际部署地址替换本地 keyless 部署下/a2a直接放行与/v1的本地优先姿态一致生产环境建议启用OMNIROUTE_API_KEY或REQUIRE_API_KEY以启用 Key 校验与按 owner 的任务隔离协议版本为 A2A v0.3并附带 1.0 方法名兼容层同步响应中路由解释、成本包络、韧性轨迹都在metadata内流式场景则在最后的 completed 事件中给出任务默认 5 分钟 TTL、终态 2 倍 TTL 后从内存回收、历史保留 30 天可经OMNIROUTE_A2A_HISTORY_RETENTION_DAYS调整长任务或审计场景需要留意这些边界。由此OmniRoute 把智能路由网关的每一项核心能力——combo 路由、配额、成本、健康度、技能目录——都收敛成一个对 Agent 世界开放的 A2A 面外部智能体只需一次 Agent Card 发现加标准的 JSON-RPC 调用就能把整个网关当作自己的路由后端使用。【免费下载链接】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, RTKCaveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550 contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考