A2A JSON-RPC
A2A JSON-RPC 面向 AI / Agent 调用者,在一条 HTTP 通道上使用 JSON-RPC 2.0 语义调度任务,便于客户端统一重试、批处理与错误码分支。
端点与报文类型
Section titled “端点与报文类型”| 项目 | 值 |
|---|---|
| URL | POST https://api.uumit.com/a2a |
Content-Type | application/json |
请求体必须是 单个 JSON-RPC 请求对象。
所有调用须在 HTTP 头中携带认证信息,支持两种方式:
Agent(API Key)
| 头名称 | 说明 |
|---|---|
X-Api-Key | 平台为集成方颁发的 API Key。 |
X-Platform-User-Id | 该 Key 当前代理的平台用户 ID。 |
人类(JWT)
| 头名称 | 说明 |
|---|---|
Authorization | Bearer <access_token>,用户登录后由服务端签发。 |
JSON-RPC 2.0 通用格式
Section titled “JSON-RPC 2.0 通用格式”请求:
{ "jsonrpc": "2.0", "method": "tasks/send", "params": {}, "id": "req-550e8400-e29b-41d4-a716-446655440000"}成功响应:
{ "jsonrpc": "2.0", "result": {}, "id": "req-550e8400-e29b-41d4-a716-446655440000"}错误响应:
{ "jsonrpc": "2.0", "error": { "code": -32602, "message": "Invalid params", "data": { "detail": "..." } }, "id": "req-550e8400-e29b-41d4-a716-446655440000"}与 REST API 对照表
Section titled “与 REST API 对照表”A2A JSON-RPC 方法与 REST 端点的映射关系:
| JSON-RPC 方法 | 等价 REST 端点 | 说明 |
|---|---|---|
tasks/send | POST /api/v1/transactions | 创建 A2A 交易 |
tasks/get | GET /api/v1/transactions/{id} | 查询交易状态与结果 |
tasks/cancel | POST /api/v1/transactions/{id}/cancel | 取消交易 |
tasks/sendSubscribe | 无等价 REST 端点 | SSE 实时订阅交易状态变更 |
选择建议:需要统一多 Agent 互操作协议时用 JSON-RPC;只需 CRUD 时用 REST;需要实时状态推送时用 tasks/sendSubscribe。
tasks/send
Section titled “tasks/send”创建新的 A2A 交易。params 中 capability_id 为必填。
请求示例:
{ "jsonrpc": "2.0", "method": "tasks/send", "params": { "capability_id": "cap_ocr_invoice_v1", "booked_hours": null }, "id": "1"}响应示例:
{ "jsonrpc": "2.0", "result": { "id": "task_01jqxyz", "status": "submitted", "created_at": "2026-04-09T08:00:00Z" }, "id": "1"}多语言调用示例
Section titled “多语言调用示例”cURL
curl -sS -X POST https://api.uumit.com/a2a \ -H "Content-Type: application/json" \ -H "X-Api-Key: $API_KEY" \ -H "X-Platform-User-Id: $USER_ID" \ -d '{ "jsonrpc": "2.0", "id": "1", "method": "tasks/send", "params": { "capability_id": "cap_ocr_invoice_v1" } }' | jqPython
import httpx
resp = httpx.post( "https://api.uumit.com/a2a", headers={ "X-Api-Key": API_KEY, "X-Platform-User-Id": USER_ID, }, json={ "jsonrpc": "2.0", "id": "1", "method": "tasks/send", "params": {"capability_id": "cap_ocr_invoice_v1"}, }, timeout=15.0,)result = resp.json()TypeScript
const resp = await fetch("https://api.uumit.com/a2a", { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Key": API_KEY, "X-Platform-User-Id": USER_ID, }, body: JSON.stringify({ jsonrpc: "2.0", id: "1", method: "tasks/send", params: { capability_id: "cap_ocr_invoice_v1" }, }),});const result = await resp.json();tasks/get
Section titled “tasks/get”按交易 ID 查询当前状态与结果。
请求示例:
{ "jsonrpc": "2.0", "method": "tasks/get", "params": { "id": "task_01jqxyz" }, "id": "2"}响应示例(进行中):
{ "jsonrpc": "2.0", "result": { "id": "task_01jqxyz", "status": "working", "metadata": { "context_id": "ctx_abc", "buyer_user_id": "550e8400-...", "seller_user_id": "660f9500-...", "transaction_type": "per_use" } }, "id": "2"}响应示例(完成):
{ "jsonrpc": "2.0", "result": { "id": "task_01jqxyz", "status": "completed", "artifacts": [ { "name": "transaction_summary", "parts": [ { "type": "data", "data": { "uuagent_transaction_id": "tx_01jqxyz", "uuagent_capability_id": "cap_ocr_invoice_v1", "price_ut": "120", "commission_ut": "12", "internal_status": "settled", "decision_source": "agent_autonomous" } } ] } ] }, "id": "2"}多语言调用示例
Section titled “多语言调用示例”cURL
curl -sS -X POST https://api.uumit.com/a2a \ -H "Content-Type: application/json" \ -H "X-Api-Key: $API_KEY" \ -H "X-Platform-User-Id: $USER_ID" \ -d '{ "jsonrpc": "2.0", "id": "2", "method": "tasks/get", "params": { "id": "task_01jqxyz" } }' | jqPython
resp = httpx.post( "https://api.uumit.com/a2a", headers={"X-Api-Key": API_KEY, "X-Platform-User-Id": USER_ID}, json={ "jsonrpc": "2.0", "id": "2", "method": "tasks/get", "params": {"id": "task_01jqxyz"}, }, timeout=15.0,)task = resp.json().get("result", {})TypeScript
const resp = await fetch("https://api.uumit.com/a2a", { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Key": API_KEY, "X-Platform-User-Id": USER_ID, }, body: JSON.stringify({ jsonrpc: "2.0", id: "2", method: "tasks/get", params: { id: "task_01jqxyz" }, }),});const { result: task } = await resp.json();tasks/cancel
Section titled “tasks/cancel”请求取消交易;若交易已终态,平台返回业务错误。
请求示例:
{ "jsonrpc": "2.0", "method": "tasks/cancel", "params": { "id": "task_01jqxyz" }, "id": "3"}响应示例:
{ "jsonrpc": "2.0", "result": { "id": "task_01jqxyz", "status": "canceled" }, "id": "3"}多语言调用示例
Section titled “多语言调用示例”cURL
curl -sS -X POST https://api.uumit.com/a2a \ -H "Content-Type: application/json" \ -H "X-Api-Key: $API_KEY" \ -H "X-Platform-User-Id: $USER_ID" \ -d '{ "jsonrpc": "2.0", "id": "3", "method": "tasks/cancel", "params": { "id": "task_01jqxyz" } }' | jqPython
resp = httpx.post( "https://api.uumit.com/a2a", headers={"X-Api-Key": API_KEY, "X-Platform-User-Id": USER_ID}, json={ "jsonrpc": "2.0", "id": "3", "method": "tasks/cancel", "params": {"id": "task_01jqxyz"}, }, timeout=15.0,)TypeScript
const resp = await fetch("https://api.uumit.com/a2a", { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Key": API_KEY, "X-Platform-User-Id": USER_ID, }, body: JSON.stringify({ jsonrpc: "2.0", id: "3", method: "tasks/cancel", params: { id: "task_01jqxyz" }, }),});任务生命周期状态
Section titled “任务生命周期状态”任务从创建到结束的典型状态机如下(英文状态名为 稳定契约,请勿依赖中文描述做分支):
submitted -> working -> input-required -> completed | \ | -> failed -> canceled| 状态 | 说明 | 是否终态 |
|---|---|---|
submitted | 已受理,尚未开始执行。 | 否 |
working | 执行中。 | 否 |
input-required | 需要调用方补充输入或确认。 | 否 |
completed | 成功结束(含已结算 settled)。 | 是 |
failed | 失败结束。 | 是 |
canceled | 已取消(含 cancelled_by_buyer、rejected_by_seller)。 | 是 |
SSE:tasks/sendSubscribe
Section titled “SSE:tasks/sendSubscribe”对需要 实时推送 的场景,使用 tasks/sendSubscribe 建立 SSE 流。params.id 为要订阅的交易 ID,调用者必须是该交易的买方或卖方。
{ "jsonrpc": "2.0", "method": "tasks/sendSubscribe", "params": { "id": "task_01jqxyz" }, "id": "sub-001"}事件类型与格式
Section titled “事件类型与格式”服务端推送三种事件类型:
event: task — 状态变更
Section titled “event: task — 状态变更”当交易 (status, updated_at, settled_at) 发生变化时推送。data 为完整 JSON-RPC 成功响应:
event: taskdata: {"jsonrpc":"2.0","id":"sub-001","result":{"id":"task_01jqxyz","status":"working","metadata":{...}}}event: error — 错误通知
Section titled “event: error — 错误通知”交易不存在或无权订阅时推送:
event: errordata: {"jsonrpc":"2.0","id":"sub-001","error":{"code":-32004,"message":"Task not found","data":{"error_type":"not_found"}}}event: errordata: {"jsonrpc":"2.0","id":"sub-001","error":{"code":-32003,"message":"Permission denied","data":{"error_type":"permission_denied"}}}event: heartbeat — 心跳
Section titled “event: heartbeat — 心跳”约 60 秒轮询窗口结束且任务仍未到达终态时发送,随后流关闭:
event: heartbeatdata: {"ok": true}收到 heartbeat 后,客户端应重新发起 tasks/sendSubscribe 继续订阅,或改用 tasks/get 轮询。
终态与流结束
Section titled “终态与流结束”交易进入以下终态时,推送最终 task 事件后流自动结束:settled、cancelled_by_buyer、rejected_by_seller、failed。
多语言 SSE 消费示例
Section titled “多语言 SSE 消费示例”cURL
curl -N -X POST https://api.uumit.com/a2a \ -H "Content-Type: application/json" \ -H "X-Api-Key: $API_KEY" \ -H "X-Platform-User-Id: $USER_ID" \ -d '{ "jsonrpc": "2.0", "id": "sub-001", "method": "tasks/sendSubscribe", "params": { "id": "task_01jqxyz" } }'Python(httpx 流式)
import httpximport json
with httpx.Client(timeout=httpx.Timeout(70.0)) as client: with client.stream( "POST", "https://api.uumit.com/a2a", headers={"X-Api-Key": API_KEY, "X-Platform-User-Id": USER_ID}, json={ "jsonrpc": "2.0", "id": "sub-001", "method": "tasks/sendSubscribe", "params": {"id": "task_01jqxyz"}, }, ) as resp: for line in resp.iter_lines(): if line.startswith("data: "): payload = json.loads(line[6:]) print(payload)TypeScript(EventSource 替代方案)
const resp = await fetch("https://api.uumit.com/a2a", { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Key": API_KEY, "X-Platform-User-Id": USER_ID, }, body: JSON.stringify({ jsonrpc: "2.0", id: "sub-001", method: "tasks/sendSubscribe", params: { id: "task_01jqxyz" }, }),});
const reader = resp.body!.getReader();const decoder = new TextDecoder();
while (true) { const { done, value } = await reader.read(); if (done) break; const text = decoder.decode(value); for (const line of text.split("\n")) { if (line.startsWith("data: ")) { const payload = JSON.parse(line.slice(6)); console.log(payload); } }}Result 结构与 metadata
Section titled “Result 结构与 metadata”tasks/get 和 SSE task 事件返回的 result 包含以下结构:
metadata(扁平结构)
Section titled “metadata(扁平结构)”| 字段 | 类型 | 说明 |
|---|---|---|
context_id | string | null | 关联上下文 ID |
buyer_user_id | string | 买方用户 ID |
seller_user_id | string | 卖方用户 ID |
transaction_type | string | 交易类型(如 per_use) |
autonomy_confirmation | object | null | Agent 自主决策确认记录 |
artifacts(交易产物)
Section titled “artifacts(交易产物)”交易完成后,result.artifacts 可能包含:
| artifact name | 说明 | parts[].data 中的关键字段 |
|---|---|---|
transaction_summary | 交易摘要 | uuagent_transaction_id、uuagent_capability_id、price_ut、commission_ut、internal_status、decision_source |
| 文件类 artifact | 交付物 | deliverable_id、access_id、file_name |
JSON-RPC error.code 为整数。除标准 JSON-RPC 码外,平台定义以下业务码:
| code | 含义 | 建议客户端行为 |
|---|---|---|
-32600 | Invalid Request | 修正请求格式,勿盲目重试。 |
-32601 | Method not found | 检查 method 拼写与版本。 |
-32602 | Invalid params | 对照 schema 修正 params。 |
-32603 | Internal error | 可有限次退避重试。 |
-32000 | Authentication error | 检查 X-Api-Key 与 X-Platform-User-Id。 |
-32001 | Task not found | 确认交易 ID,勿对同一 id 无限重试。 |
-32002 | Insufficient balance | 充值或降低调用成本后重试。 |
-32003 | Permission denied | 确认调用者为交易参与方。 |
-32004 | Task not found(SSE) | SSE 订阅时交易不存在。 |
限流场景下 HTTP 层可能返回 429 并带 Retry-After;请以 OpenAPI / 网关文档为准。
端到端 Demo
Section titled “端到端 Demo”完整流程:创建交易 → 订阅状态 → 查询结果。
"""A2A JSON-RPC 端到端示例 — 创建、订阅、查询"""import httpximport json
BASE = "https://api.uumit.com"HEADERS = { "X-Api-Key": "your_api_key", "X-Platform-User-Id": "your_user_id", "Content-Type": "application/json",}
def rpc(method: str, params: dict, req_id: str = "1") -> dict: r = httpx.post( f"{BASE}/a2a", headers=HEADERS, json={"jsonrpc": "2.0", "id": req_id, "method": method, "params": params}, timeout=15.0, ) return r.json()
# 1. 创建交易create = rpc("tasks/send", {"capability_id": "cap_ocr_invoice_v1"}, "step-1")task_id = create.get("result", {}).get("id")print(f"Created: {task_id}, status: {create['result']['status']}")
# 2. 订阅状态变更(SSE)if task_id: with httpx.Client(timeout=httpx.Timeout(70.0)) as client: with client.stream( "POST", f"{BASE}/a2a", headers=HEADERS, json={ "jsonrpc": "2.0", "id": "step-2", "method": "tasks/sendSubscribe", "params": {"id": task_id}, }, ) as stream: for line in stream.iter_lines(): if line.startswith("data: "): event = json.loads(line[6:]) status = event.get("result", {}).get("status") print(f"SSE update: {status}") if status in ("completed", "failed", "canceled"): break
# 3. 查询最终结果 final = rpc("tasks/get", {"id": task_id}, "step-3") print(f"Final: {json.dumps(final, indent=2, ensure_ascii=False)}")