news 2026/9/13 10:48:30

OmniRoute A2A Server 实战指南:以 Agent-to-Agent 协议暴露智能路由能力

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OmniRoute A2A Server 实战指南:以 Agent-to-Agent 协议暴露智能路由能力

OmniRoute A2A Server 实战指南:以 Agent-to-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 将自身 AI 网关的智能路由、配额管理、健康监测等能力,通过 Agent-to-Agent(A2A)协议 v0.3 封装为标准 JSON-RPC 2.0 接口,让 Claude、Codex、Cline 等外部 Agent 可以直接以"对 Agent 说话"的方式调用网关能力。本文基于 docs/frameworks/A2A-SERVER.md 展开,结合 src/app/a2a/route.ts、src/lib/a2a/taskManager.ts 等源码,完整讲解 Agent 发现、认证、六个 JSON-RPC 方法、六大 Skills、任务生命周期与错误码,并给出可直接复制的 Python / TypeScript 集成示例。读完本文,你将掌握如何以标准 A2A 协议对接 OmniRoute,将 352 个 Provider、1200+ 模型的智能路由能力无缝接入自己的 Agent 工作流。

一、双面接口总览:JSON-RPC 与 REST

A2A 协议是 Google 发起的 Agent 互操作开放协议,OmniRoute 在其之上实现了一层"智能路由 Agent"表面。从源码结构看,A2A 表面(surface)共有两个入口(见 docs/frameworks/A2A-SERVER.md):

  • JSON-RPC 2.0,位于POST /a2a,是规范意义上的唯一入口,实现于 src/app/a2a/route.ts。所有 Agent 间的消息收发、任务查询与取消都走这里。
  • REST 辅助接口,位于/api/a2a/*,供仪表盘与外部工具查看状态、任务列表与执行取消。

任务由A2ATaskManager统一管理(src/lib/a2a/taskManager.ts,默认 TTL 5 分钟),技能则通过A2A_SKILL_HANDLERS注册表分发(src/lib/a2a/taskExecution.ts)。

二、Agent Discovery:让其他 Agent 找到你

A2A 协议要求每个 Agent 通过 Agent Card 描述自身能力。OmniRoute 在标准路径/.well-known/agent.json暴露 Agent Card:

curl http://localhost:20128/.well-known/agent.json

返回内容描述 OmniRoute 的能力、技能列表与认证要求。该 Agent Card 是动态生成的(实现于 src/app/.well-known/agent.json/route.ts),有两个值得注意的细节:

  1. 版本号自动同步version字段取自process.env.npm_package_version(route.ts#L17),因此每次发布package.json版本号变化时,Agent Card 会随之自动更新,无需手工维护。
  2. 与运行时注册表保持对齐:技能列表基于可用组合(combos)动态生成,卡片中还包含来自 OmniConductor 集群的 fleet skills(有约 60 秒缓存,集群未配置或离线时该字段为空数组,不影响卡片整体可用性)。

Agent Card 中的capabilities.streamingtrue,表明该 Agent 支持流式输出。

三、启用开关:默认关闭

A2A 端点由Endpoints(端点)→ A2A开关控制,默认禁用。这是重要的安全前提,具体行为如下(见 docs/frameworks/A2A-SERVER.md):

  • 禁用时,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标志,为 false 时直接拦截所有方法调用。

四、Authentication:Bearer Token 认证

所有/a2a请求都需要通过Authorization头携带 API Key:

Authorization: Bearer YOUR_OMNIROUTE_API_KEY

认证逻辑统一收敛在 src/lib/a2a/authenticate.ts 的authenticateA2ARequest中,其判定顺序如下:

  1. 若开启了REQUIRE_API_KEY模式isRequireApiKeyEnabled()):必须携带有效 OmniRoute Key,否则拒绝;
  2. 若配置了OMNIROUTE_API_KEY环境变量:请求头中的 Bearer Token 必须与之精确匹配(使用timingSafeEqual进行常数时间比较,防时序侧信道攻击,authenticate.ts#L16-L21);
  3. 若两者都未配置:认证被绕过,进入本地优先(keyless)模式——这也是出厂默认行为。

此外,同一文件中的resolveA2AOwner会将调用方 API Key 的 SHA-256 前缀(32 位 hex)作为任务的 owner 标识,用于任务可见性隔离(对应安全公告 GHSA-jcm5-6wpp-wjj8):带 owner 的任务只对同一 owner 可见/可取消,无 key 的任务对所有调用方可见,相关验证见 tests/unit/a2a-task-owner-idor.test.ts。

五、JSON-RPC 2.0 Methods 详解

POST /a2a统一处理 JSON-RPC 2.0 请求。下面是四个标准方法的完整说明。

5.1message/send— 同步执行

向指定 skill 发送消息并等待完整响应。返回结果中包含任务状态、artifacts 以及路由决策元数据。

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" } } } }

这些元数据字段并非凭空而来,而是由smart-routing技能的实现 src/lib/a2a/skills/smartRouting.ts 真实产出:

  • routing_explanation:由实际路由结果拼装,格式为Selected <model> via provider "<provider>" (latency: <ms>ms, cost: $<cost>)
  • cost_envelope.estimated:基于prompt_tokens / 1_000_000 × 3.0的粗略估算(美元);
  • resilience_trace:记录primary_selected事件;当上游触发 fallback 时追加fallback_needed事件;
  • policy_verdict:当params.metadata中传入budget时,对实际成本做预算校验,allowed反映是否在预算内。

5.2message/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 <ISO时间>注释行,防止代理/网关断开空闲连接(streaming.ts#L58-L60);
  • 分块事件:技能完成后,artifacts 被逐个封装为chunk事件发出(对非流式技能做模拟流式输出);
  • 终止事件:以state: "completed"+ 完整metadata收尾,异常路径则发出state: "failed"
  • 取消支持:监听AbortSignal,客户端断开或主动中止时立即发送Cancelled失败事件并关闭流;
  • 响应头Content-Type: text/event-streamCache-Control: no-cache, no-transformX-Accel-Buffering: no(禁用 Nginx 等反向代理的缓冲,保证逐字推送)。

5.3tasks/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"}}'

实现上,getTask会先检查expiresAt:若任务已过期且仍处于submitted/working,会先将其置为failed("Task expired")再返回(taskManager.ts#L231-L241)。

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 探测(taskManager.ts#L268-L278)。

5.5 兼容 A2A 1.0 客户端(v1.0 ↔ v0.3 兼容层)

值得注意的是,src/app/a2a/route.ts 内置了一个 A2A 1.0 ↔ 0.3 兼容层:1.0 将message/send改名为SendMessagemessage/stream改名为SendStreamingMessage,且同步响应的读取位置也不同(1.0 客户端读task.status.message.parts[].text,而 OmniRoute 0.3 返回顶层artifacts/metadata)。该兼容层对 1.0 方法名做别名映射,并把同步响应重塑为 1.0 的Task结构,使得 a2a-sdk 1.x、Hermes 等 1.0 客户端可以不改代码直接调用;0.3 客户端则完全不受影响。相关验证见 tests/unit/a2a-v1-compat-10839.test.ts。

六、Available Skills:OmniRoute 暴露的六大技能

OmniRoute 通过A2A_SKILL_HANDLERS注册表(src/lib/a2a/taskExecution.ts)暴露 6 个技能,每个技能模块位于 src/lib/a2a/skills/ 目录:

SkillID说明Tags调用示例
Smart Routingsmart-routing使用组合引擎 + 评分,将提示词路由到最优 Provider/Comborouting, providers"Route this prompt via the best model"
Quota Managementquota-management报告各 Provider 的配额状态,帮助调用方决定何时限流/切换quota, providers"Check quota for anthropic"
Provider Discoveryprovider-discovery列出已安装 Provider 的能力、免费层标志与 OAuth 状态providers, discovery"What providers are available?"
Cost Analysiscost-analysis基于目录与近期用量估算请求/对话成本cost, usage"Estimate cost for this conversation"
Health Reporthealth-report聚合各 Provider 的熔断器、冷却、锁定状态health, resilience"Show health status of all providers"
List Capabilitieslist-capabilities以 Markdown 表格返回完整的 45 项 Agent Skills 目录(23 API + 21 CLI + 1 config)及 SKILL.md 原始链接catalog, discovery, skills"List all OmniRoute capabilities"

Agent Card 与实时的 Provider 目录保持一致(当前运行时注册表维护 352 个 Provider 的元数据,免费/免认证标志均来自运行时注册表)。

list-capabilities技能详解

对于需要在发送 API 调用前先摸清 OmniRoute 能力的外部 Agentlist-capabilities尤其有用。它返回结构化 Markdown 表格 artifact,每行包含 ID、名称、类别、区域、端点/命令与原始 URL:

| 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字段镜像目录规模。实现位于 src/lib/a2a/skills/listCapabilities.ts,技能目录体系见 docs/frameworks/AGENT-SKILLS.md。

七、REST API(辅助面)

JSON-RPC 端点/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 Card(A2A 发现)公开,缓存 3600s
/api/a2a/tasksPOST入站委托给 OmniConductor 集群Bearer vsOMNIROUTE_API_KEY+a2aEnabled

入站 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? } } }。只有 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查看。

八、Task 生命周期与 TTL

任务状态机如下:

submitted → working → completed → failed → cancelled
  • TTL:任务在ttlMinutes后过期,默认 5 分钟(由A2ATaskManager构造函数配置,taskManager.ts#L139-L150)。如需自定义,可以new A2ATaskManager(15)实例化一个 15 分钟 TTL 的管理器;
  • 后台清理:每 60 秒执行一次过期清理;非终止状态的任务过期后标记为failed("TTL expired"),终止状态任务在超过 2× TTL 后从内存移除;历史表清理按OMNIROUTE_A2A_HISTORY_RETENTION_DAYS(默认 30 天)保留,且每天最多 purge 一次;
  • 终止状态completedfailedcancelled
  • 事件日志:每次状态转换都会追加到task.events,同时通过emit("agent.task.updated", ...)广播给编排画布(best-effort,监听器抛错不会破坏任务写入路径);
  • 并发流计数beginStream/endStream维护activeStreams统计,可在getStats()中查询。

状态转换的合法性由VALID_TRANSITIONS表强制校验,非法跳转(如completed → working)会抛错。任务 ID 使用 UUID v4(randomUUID()),内存 Map 是活跃任务的真相来源,同时通过 DI 接缝将历史写入 SQLite(对应src/lib/db/a2aTasks.ts,测试注入 fake 以避免触碰真实数据库)。

九、Error Codes:JSON-RPC 错误码

Code含义
-32700解析错误(JSON 无效)
-32600无效请求 / 未授权
-32601方法或技能不存在
-32602参数无效
-32603内部错误
-32000A2A 端点已禁用

HTTP 状态码映射规则(route.ts#L136-L141):-32600→ 400,-32601→ 404,-32603→ 500,其余返回 200 并在 JSON-RPCerror对象中携带错误信息;-32000禁用场景返回 HTTP 503。

十、Integration Examples:开箱即用的集成代码

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);

注意:请求体同时支持params.messages(消息数组)与params.message(单条消息)两种形态,params.message还兼容{ content }{ parts: [...] }两种结构,方便接入不同生态的 Agent SDK(见 route.ts#L80-L121 的toMessageArray归一化逻辑)。

十一、扩展新 Skill:源码级接入指南

如果你希望 OmniRoute 的 A2A 表面暴露自定义技能,文档给出了完整流程(对应文件均已存在可参照):

  1. 创建技能文件:在src/lib/a2a/skills/<your-skill>.ts导出异步函数(task: A2ATask) => Promise<{ artifacts, metadata }>,参照smartRouting.ts的结构;
  2. 注册处理器:在 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); }, };
  3. 暴露到 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"] }
  4. 编写测试:新增tests/unit/a2a-<your-skill>.test.ts,覆盖正常路径与错误路径。仓库已有 11 个 A2A 相关测试可作参考,涵盖认证(tests/unit/a2a-auth-timing-safe.test.ts)、启用开关(tests/unit/a2a-enabled-route.test.ts)、任务持久化(tests/unit/a2a-task-persistence.test.ts)、owner 隔离(tests/unit/a2a-task-owner-idor.test.ts)与 1.0 兼容层等;
  5. 更新文档:在本文档对应的技能表中补充新条目。

十二、补充实现细节:内存命中(Memory Hits)

在执行技能前,A2A 任务会尝试做一次仅用于观测的内存召回(src/lib/a2a/taskExecution.ts):以任务最后一条 user 消息为查询,在 1.5 秒时限(MEMORY_RECALL_TIMEOUT_MS)内检索内存,命中结果以memoryHits写入task.metadata并记录memory_hits历史事件,供仪表盘展示"该任务参考了哪些记忆"。关键在于:

  • 召回的命中文案截断到 200 字符,绝不注入技能提示词或影响行为;
  • 可通过环境变量OMNIROUTE_A2A_MEMORY_HITS=0一键关闭;
  • 任何召回失败都静默降级为[],绝不拖垮任务主链路(对应测试见 tests/unit/a2a-memory-hits.test.ts)。

总而言之,OmniRoute 的 A2A Server 用标准 JSON-RPC 2.0 + Agent Card 发现机制,把网关最核心的路由决策、配额、健康与成本能力开放给了任意 A2A 客户端。结合 src/lib/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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 10:46:44

基于YOLOv8的工地安全帽智能检测系统开发实战

1. 项目概述&#xff1a;工地安全帽检测系统的核心价值在建筑工地这个高风险作业环境中&#xff0c;安全帽佩戴检测是保障工人生命安全的重要防线。传统的人工巡检方式存在效率低、覆盖范围有限等问题&#xff0c;而基于YOLOv8的智能检测系统能够实现724小时不间断监控&#xf…

作者头像 李华
网站建设 2026/9/13 10:45:55

ANSYS切削加工温度场模拟与工艺优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 10:44:15

实测VeapAI:开源RAG知识库全链路平台搭建指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 10:41:53

Anaconda 安装 TensorFlow 全流程:环境管理、版本配对与报错排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 10:40:44

深度学习实现图像风格迁移:从CNN原理到Python实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 10:38:36

PostgreSQL到Oracle迁移全流程实战:类型映射、增量同步与踩坑记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华