ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

OmniRoute A2A Server 接入指南:把 AI 路由网关打造成智能路由 Agent

OmniRoute A2A Server 接入指南:把 AI 路由网关打造成智能路由 Agent OmniRoute A2A Server 接入指南把 AI 路由网关打造成智能路由 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/OmniRouteAgent-to-Agent Protocol v0.3 —— 将 OmniRoute 以「智能路由 Agent」的身份暴露给其他 AI Agent。本篇指南围绕 OmniRoute 的 A2AAgent-to-Agent服务端展开它以POST /a2a作为 JSON-RPC 2.0 入口、以/api/a2a/*提供 REST 辅助接口并暴露 6 个可直接调用的技能Skill。读完本文你将掌握 Agent 发现Agent Card、鉴权与开关控制、message/send/message/stream/tasks/get/tasks/cancel四个核心 JSON-RPC 方法的完整调用方式理解任务生命周期与 TTL 清理机制并能照着步骤在仓库中新增一个自定义 A2A 技能。全文以 docs/frameworks/A2A-SERVER.md 为主体结合仓库源码给出实现级佐证。一、A2A 表面的双面结构OmniRoute 的 A2A 能力由两个互补的入口组成分别服务不同的调用方JSON-RPC 2.0POST /a2a是规范意义上的 A2A 标准入口专为其他 Agent 设计。路由实现在 src/app/a2a/route.ts支持message/send、message/stream、tasks/get、tasks/cancel四个方法。REST/api/a2a/*为仪表盘与外部工具提供辅助能力包括状态查询、任务列表、任务取消等。任务的跟踪由A2ATaskManagersrc/lib/a2a/taskManager.ts负责默认 TTL 为 5 分钟技能的分发则通过A2A_SKILL_HANDLERSsrc/lib/a2a/taskExecution.ts完成。从源码结构看A2A 是 OmniRoute 路由能力的「面向 Agent 的封装层」它并不自己实现模型推理而是把收到的消息转交给内部技能模块如smart-routing技能内部实际调用/v1/chat/completions走完整路由管线再把结果以 A2A 协议规定的任务与工件artifact形式返回给调用方。二、Agent 发现Agent CardA2A 协议要求服务端通过.well-known目录暴露「Agent Card」让其他 Agent 在调用前先发现其能力、技能与鉴权要求。curl http://localhost:20128/.well-known/agent.json返回内容即 OmniRoute 的 Agent Card描述了网关的能力、技能列表与认证方式。该端点的实现位于 src/app/.well-known/agent.json/route.ts有两点值得注意版本号自动同步Agent Card 的version字段取自process.env.npm_package_version与package.json的版本保持自动一致每次发布无需手工维护源码中带1.8.1兜底值。技能动态生成skills数组不仅包含固定的 6 个内置技能还会通过getFleetSkills()src/lib/conductor/fleetSkills.ts合并 OmniConductor 集群的技能缓存约 60 秒Hub 未配置或离线时返回[]Agent Card 依然有效。Agent Card 的认证部分声明了schemes: [api-key]与apiKeyHeader: Authorization与下面介绍的鉴权方式一致。该响应带有Cache-Control: public, max-age3600缓存 3600 秒。三、鉴权Bearer API Key所有发往/a2a的请求都需要在Authorization头携带 API KeyAuthorization: Bearer YOUR_OMNIROUTE_API_KEY如果服务器未配置任何 API Key鉴权将被绕过keyless 本地优先模式。完整的判定逻辑在 src/lib/a2a/authenticate.ts 的authenticateA2ARequest()若开启了「强制 API Key」特性isRequireApiKeyEnabled()则必须提供且校验通过的 OmniRoute Key 才能访问否则若配置了OMNIROUTE_API_KEY环境变量则用常量时间比较timingSafeEqual校验请求携带的 Key两者都不满足时直接放行——即默认的 keyless 本地优先姿态。同一文件中的resolveA2AOwner()还负责把调用方解析为「所有者 ID」对 API Key 做 SHA-256 哈希并取前 32 位十六进制前缀。该 owner 会用于任务的作用域隔离详见「任务可见性与安全模型」一节确保一个调用方不能读取或取消另一个调用方的任务。四、开关控制Endpoints → A2AA2A 由Endpoints端点页面中的 A2A 开关控制默认是关闭的。开关状态从设置库读取getSettings()当a2aEnabled ! true时GET /api/a2a/status报告status: disabled与online: false对POST /a2a的 JSON-RPC 调用返回HTTP 503并携带 JSON-RPC 错误码-32000A2A endpoint is disabled. Enable it from the Endpoints page.。该判定在 src/app/a2a/route.ts 的rejectIfA2ADisabled()中实现——它在鉴权通过、请求体解析之后立即执行属于所有方法共用的前置守卫。五、JSON-RPC 2.0 方法详解所有调用统一走POST http://localhost:20128/a2a请求体为标准的 JSON-RPC 2.0 结构{jsonrpc: 2.0, 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 } } } }从路由源码看message/send的处理链路是解析并归一化messages兼容message.content与 legacy 的message.parts两种形态→ 在A2A_SKILL_HANDLERS中查找技能 →createTask创建任务 →updateTask(task.id, working)→ 执行技能 handler →updateTask(task.id, completed, result.artifacts)。若执行抛错任务会被标记为failed并附上 error 类型工件返回-32603内部错误码。值得补充的是参数归一化规则见 src/app/a2a/route.ts 的toMessageArray()messages数组中的每条消息role缺省时补为usercontent为空的消息会被过滤也接受单条消息形态{message: {role, content}}兼容 legacy 的{message: {parts: [...]}}会把各 partcontent或text字段拼接为整段文本。5.2message/stream—— SSE 流式执行与message/send参数完全相同但响应改为 Server-Sent EventsSSE适合需要实时输出场景的 Agentcurl -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.tscreateA2AStream()接收任务、执行回调executeA2ATaskWithState与请求的AbortSignal并通过onStart/onEnd回调维护A2ATaskManager的活跃流计数beginStream()/endStream()供/api/a2a/status的activeStreams统计使用。事件中穿插: heartbeat注释行以维持连接活性任务最终以completed状态事件收尾携带完整 metadata。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}}路由接受params.taskId或params.id两种字段名。值得注意的是getTask()的过期处理如果任务已超过expiresAt且仍处于submitted/working状态查询会把它标记为failedTask expired后返回任务不存在时返回-32601。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()把任务状态迁移到cancelled事件消息为 Cancelled by client。取消与查询一样受 owner 作用域约束。5.5 附加A2A 1.0 方法别名兼容层从 src/app/a2a/route.ts 的注释可以看出路由内置了 A2A v1.0 ↔ v0.3 的兼容层A2A 1.0 将方法改名为SendMessage/SendStreamingMessage并把同步响应改到task.status.message.parts[].text与task.artifacts之下。该层用V1_METHOD_ALIASES做方法别名映射用buildV1Task()重塑同步响应结构因此a2a-sdk 1.x、Hermes 等 1.0 客户端可以直接调用v0.3 客户端则完全不受影响。v1 方法调用时也支持通过params.message.contextId传递上下文 ID。六、内置技能一览OmniRoute 通过 src/lib/a2a/taskExecution.ts 中的A2A_SKILL_HANDLERS暴露6 个 A2A 技能每个技能模块位于src/lib/a2a/skills/目录采用动态import()懒加载方式注册技能ID描述标签调用示例Smart Routingsmart-routing使用 OmniRoute 的 combo 引擎 评分体系把提示词路由到最优提供者/组合routing, providersRoute this prompt via the best modelQuota Managementquota-management报告各提供者的配额状态帮助调用方决定何时限流/切换quota, providersCheck quota for anthropicProvider Discoveryprovider-discovery列出已安装提供者包含能力、免费档位标记、OAuth 状态providers, discoveryWhat providers are available?Cost Analysiscost-analysis基于目录与近期用量估算请求/会话成本cost, usageEstimate cost for this conversationHealth Reporthealth-report汇总各提供者的熔断器、冷却期、锁定状态health, resilienceShow health status of all providersList Capabilitieslist-capabilities以 markdown 表格返回完整 Agent Skills 目录及原始 SKILL.md URL便于上下文注入catalog, discovery, skillsList all OmniRoute capabilities文档同时强调Agent Card 应与实时目录保持对齐提供者数量与免费/无鉴权元数据均取自运行时注册表runtime registry不写死在静态 JSON 中。6.1smart-routing技能的实现细节以最核心的smart-routing为例src/lib/a2a/skills/smartRouting.ts它内部通过routeFetch()调用 OmniRoute 自身的/v1/chat/completions端点30 秒 AbortSignal 超时把完整路由管线作为技能执行体从task.input.metadata读取model默认auto、combo透传为x-combo请求头与budget预算上限响应中提取模型输出、provider、实际成本cost、usage.prompt_tokens并据此估算成本prompt_tokens / 1e6 * 3.0的粗略估算公式返回四项结构化 metadatarouting_explanation含延迟与成本的一句话解释、cost_envelopeestimated / actual / currency、resilience_traceprimary_selected事件若触发回退则追加fallback_needed、policy_verdictallowed与reason预算超限时allowed: false。这就是「A2A 技能 路由能力的 Agent 化封装」这一设计的最直接体现。6.2list-capabilities技能详解list-capabilities对外部 Agent 特别有用——它在发送 API 调用之前先用它来发现 OmniRoute 暴露了什么。实现位于 src/lib/a2a/skills/listCapabilities.ts返回结构化 markdown 表格工件| 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字段反映目录规模当前文档记载为 45 项Agent Card 中描述为 42 项两者口径略有差异。相关技能目录可参见 docs/frameworks/AGENT-SKILLS.md。七、REST 辅助接口/a2a是规范的 A2A 入口以下 REST 端点面向仪表盘与外部工具提供辅助访问端点方法描述鉴权/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查询。八、新增一个自定义技能下面依据文档与源码给出完整的五步扩展流程第一步创建技能文件在src/lib/a2a/skills/your-skill.ts新建文件导出一个接收任务的异步函数返回{ artifacts, metadata }形状参考smartRouting.tsimport type { A2ATask } from ../taskManager; export async function executeYourSkill(task: A2ATask) { // 读取 task.input.messages / task.input.metadata // 执行逻辑… return { artifacts: [{ type: text, content: ... }], metadata: { /* 自定义结构化元数据 */ }, }; }第二步注册 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覆盖 happy path 与 error path 两条路径仓库现有 A2A 相关单测可作为参照。第五步更新文档在本文A2A-SERVER.md的「Available Skills」表格中登记新技能。九、任务 TTL 与生命周期9.1 TTL 与清理任务在expiresAt之后过期ttlMinutes默认5 分钟配置于 src/lib/a2a/taskManager.ts 的A2ATaskManager构造函数constructor(ttlMinutes: number 5, ...)。如需定制可 forkA2ATaskManager的实例化并传入不同值例如new A2ATaskManager(15)表示 15 分钟 TTL。构造函数内部启动一个后台清理间隔每 60 秒扫描一次setInterval(() this.cleanupExpired(), 60_000)并对unref()以免阻止进程退出。cleanupExpired()的清理逻辑包含两条规则已过期且仍处于submitted/working的任务 → 标记为failed事件消息 TTL expired处于终态completed/failed/cancelled且超过2 倍 TTL未再更新的任务 → 从内存 Map 中移除。从源码看任务管理还集成了可选的 SQLite 历史持久化src/lib/db/a2aTasks.ts每次状态变更都会 best-effort 写入历史表并追加事件失败只记日志、绝不拖垮写入路径历史记录按OMNIROUTE_A2A_HISTORY_RETENTION_DAYS环境变量保留默认 30 天由maybePurge()每 24 小时最多清理一次。9.2 任务状态机submitted → working → completed → failed → cancelled任务默认 5 分钟过期见上文 TTL 小节终态completed、failed、cancelled每个状态迁移都会写入事件日志task.events同时通过emit(agent.task.updated, ...)发布事件best-effort监听器抛错不打断写入路径。状态机在 src/lib/a2a/taskManager.ts 中由VALID_TRANSITIONS严格约束——非法迁移如completed → working会直接抛出Invalid transition错误。9.3 任务可见性与安全模型src/lib/a2a/taskManager.ts 中任务结构包含可选的owner字段即调用方 API Key 的哈希见鉴权一节。可见性规则为带 owner 的任务仅对同一 owner 可见查询、取消、列表都会用isVisibleTo()过滤无 owner 的任务keyless 本地优先姿态下创建的对所有调用方可见保持原有行为取消他人任务时返回与「任务不存在」相同的错误防止 IDOR 探测区分「存在但非你的」与「不存在」。十、错误码对照JSON-RPC 错误码遵循标准约定同时扩展了 A2A 特有码代码含义-32700解析错误非法 JSON-32600无效请求 / 未授权-32601方法或技能不存在-32602参数无效-32603内部错误-32000A2A 端点已禁用从 src/app/a2a/route.ts 的jsonRpcError()看错误响应的 HTTP 状态码也做了映射-32600→ 400、-32601→ 404、-32603→ 500其余含-32000禁用态→ 200但禁用态由rejectIfA2ADisabled()单独返回 HTTP 503。因此判断 A2A 是否可用应以「HTTP 状态码 JSON-RPC 错误码」两者结合为准。十一、集成示例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);十二、关键环境变量与调参速查结合上述源码分析整理 A2A 模块相关的可调项项默认值说明任务 TTL5 分钟A2ATaskManager构造参数可 fork 实例化传值清理间隔60 秒后台cleanupExpired()扫描周期OMNIROUTE_API_KEY未设置设置后/a2a要求 Bearer 鉴权未设置则 keyless 放行OMNIROUTE_A2A_HISTORY_RETENTION_DAYS30A2A 任务历史的 SQLite 保留天数OMNIROUTE_A2A_MEMORY_HITS开启设为0时关闭 A2A 任务的记忆命中收集仅观测不注入提示词CONDUCTOR_ORCHESTRATOR_TOKEN—入站 Conductor 委派的 Hub 令牌回退CONDUCTOR_HUB_TOKEN十三、相关文档延伸Agent Skills 目录与 SKILL.md 组织方式docs/frameworks/AGENT-SKILLS.mdA2A 核心源码入口路由 src/app/a2a/route.ts、任务管理 src/lib/a2a/taskManager.ts、技能分发 src/lib/a2a/taskExecution.ts、鉴权 src/lib/a2a/authenticate.ts、流式输出 src/lib/a2a/streaming.ts技能实现目录src/lib/a2a/skills/Agent Card 端点src/app/.well-known/agent.json/route.ts【免费下载链接】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),仅供参考
返回列表