🌐 Languages: 🇺🇸 English · 🇪🇹 am · 🇸🇦 ar · 🇦🇿 az · 🇧🇬 bg · 🇧🇩 bn · 🇧🇦 bs · 🇨🇿 cs · 🇩🇰 da · 🇩🇪 de · 🇬🇷 el · 🇪🇸 es · 🇪🇪 et · 🇮🇷 fa · 🇫🇮 fi · 🇫🇷 fr · 🇮🇪 ga · 🇮🇳 gu · 🇳🇬 ha · 🇮🇱 he · 🇮🇳 hi · 🇭🇷 hr · 🇭🇺 hu · 🇦🇲 hy · 🇮🇩 id · 🇳🇬 ig · 🇮🇹 it · 🇯🇵 ja · 🇬🇪 ka · 🇰🇭 km · 🇮🇳 kn · 🇰🇷 ko · 🇱🇹 lt · 🇱🇻 lv · 🇮🇳 ml · 🇮🇳 mr · 🇲🇾 ms · 🇲🇹 mt · 🇲🇲 my · 🇳🇵 ne · 🇳🇱 nl · 🇳🇴 no · 🇮🇳 or · 🇮🇳 pa · 🇵🇭 phi · 🇵🇱 pl · 🇵🇹 pt · 🇧🇷 pt-BR · 🇷🇴 ro · 🇷🇺 ru · 🇱🇰 si · 🇸🇰 sk · 🇸🇮 sl · 🇷🇸 sr · 🇸🇪 sv · 🇰🇪 sw · 🇮🇳 ta · 🇮🇳 te · 🇹🇭 th · 🇹🇷 tr · 🇺🇦 uk-UA · 🇵🇰 ur · 🇺🇿 uz · 🇻🇳 vi · 🇳🇬 yo · 🇹🇼 zh-TW
Agent-to-Agent Protocol v0.3 — OmniRoute 作为智能路由代理
A2A 层有两个入口:
- 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 派发。
curl http://localhost:20128/.well-known/agent.json返回 Agent Card,其中描述 OmniRoute 的能力、技能和认证要求。
Agent Card 的 version 字段取自 process.env.npm_package_version(参见 src/app/.well-known/agent.json/route.ts:13),因此每次发布时都与 package.json 自动保持同步。
所有 /a2a 请求均需通过 Authorization 请求头提供 API Key:
Authorization: Bearer YOUR_OMNIROUTE_API_KEY
如果服务器未配置 API Key,认证将被跳过。
A2A 通过 端点 → A2A 开关控制,默认禁用。禁用时,GET /api/a2a/status 返回 status: "disabled" 和 online: false;对 POST /a2a 的 JSON-RPC 调用返回 HTTP 503,附带 JSON-RPC 错误码 -32000。
向技能发送消息并等待完整响应。
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 相同,但返回 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":{...}}}
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"}}'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"}}'OmniRoute 暴露了 6 个 A2A 技能,连接到 src/lib/a2a/taskExecution.ts::A2A_SKILL_HANDLERS。每个技能模块位于 src/lib/a2a/skills/。
| 技能 | ID | 描述 | 标签 | 示例 |
|---|---|---|---|---|
| Smart Routing | smart-routing |
通过 OmniRoute 的 Combo 引擎与评分,将提示路由到最优服务商/Combo | routing, 服务商 | "通过最佳模型路由此提示" |
| Quota Management | quota-management |
报告每个服务商的配额状态,帮助调用方决定何时限流/切换 | 配额, 服务商 | "检查 anthropic 的配额" |
| Provider Discovery | provider-discovery |
列出已安装的服务商及其能力、免费层标志、OAuth 状态 | 服务商, 发现 | "有哪些可用服务商?" |
| Cost Analysis | cost-analysis |
根据目录和近期用量估算请求/对话的成本 | 成本, 用量 | "估算本次对话的成本" |
| Health Report | health-report |
聚合每个服务商的熔断器、冷却、锁定状态 | 健康, 容灾 | "显示所有服务商的健康状态" |
| List Capabilities | list-capabilities |
返回完整的 42 项代理技能目录,以 Markdown 表格形式列出,附带原始 SKILL.md URL 用于上下文注入 | 目录, 发现, 技能 | "列出所有 OmniRoute 能力" |
注意:Agent Card 描述目前宣传 "36+ providers"(
src/app/.well-known/agent.json/route.ts:26和:55)。实际目录已增长至 180+ 个服务商——该字符串应在后续变更中更新(作为单独的文档/代码 TODO 跟踪;此处不作修改)。
list-capabilities 技能对于需要在发送 API 调用前了解 OmniRoute 暴露了哪些内容的外部代理尤为有用。它返回结构化的 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 列,以便代理可以立即获取完整的 SKILL.md。metadata.totalSkills 字段始终为 42。实现:src/lib/a2a/skills/listCapabilities.ts。另见 AGENT-SKILLS.md。
JSON-RPC 端点 /a2a 是 A2A 的正式入口。以下 REST 端点提供仪表盘和外部工具的辅助访问:
| 端点 | 方法 | 描述 | 认证 |
|---|---|---|---|
/api/a2a/status |
GET | 服务器状态、已注册技能 | (公开) |
/api/a2a/tasks |
GET | 列出任务(支持过滤) | 管理 |
/api/a2a/tasks/[id] |
GET | 按 ID 获取任务 | 管理 |
/api/a2a/tasks/[id]/cancel |
POST | 取消运行中的任务 | 管理 |
/.well-known/agent.json |
GET | Agent Card(A2A 发现) | (公开, 缓存 3600s) |
-
创建技能文件:
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。覆盖正常路径和错误路径。 -
在本文档的
可用技能表格中记录新技能。
任务在 ttlMinutes(默认 5 分钟)后过期——可在 src/lib/a2a/taskManager.ts:82 的 A2ATaskManager 构造函数中配置。如需自定义,可复刻 A2ATaskManager 的实例化并传入不同值(例如 new A2ATaskManager(15) 设置 15 分钟 TTL)。后台定时器每 60 秒清理一次过期任务。
submitted → working → completed
→ failed
→ cancelled
- 任务默认在 5 分钟后过期(参见任务 TTL)
- 终态:
completed、failed、cancelled - 事件日志追踪每次状态转换
| Code | 含义 |
|---|---|
| -32700 | 解析错误(JSON 无效) |
| -32600 | 无效请求 / 未授权 |
| -32601 | 方法或技能未找到 |
| -32602 | 参数无效 |
| -32603 | 内部错误 |
| -32000 | A2A 端点已禁用 |
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"])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);