GoAI API / Protocol Contract

August 13, 2026 · View on GitHub

本文档描述 GoAI 当前 V1 对外可消费的 HTTP、AG-UI 和 A2A 契约。协议 Gateway 只负责解析和编码外部协议,内部统一落成 Thread / Message / Run / Delegation,不把外部字段直接扩散到服务层。

1. Common Conventions

机器可消费的 OpenAPI 3.0 契约见 docs/openapi.yaml。本文档保留协议语义、状态机和联调约束的详细说明;两者必须保持路径、字段和鉴权边界一致。

Base URL

以下示例假定服务地址为 http://127.0.0.1:8080。生产环境的远程 Agent Endpoint 必须使用 HTTPS;本地开发才允许 loopback HTTP。

Authentication

  • /ping/auth/register/auth/login 不需要 JWT。
  • /api/* 需要 Authorization: Bearer <jwt>
  • 启用 RBAC 时,/auth/register 与管理员 POST /api/users 创建的用户会自动获得 member 角色;用户创建和角色绑定在同一事务内完成。
  • /api/agents/:agent_code/agui/api/runs 需要 run:create
  • Agent Registry 管理接口分别使用 agent:createagent:readagent:updateagent:activateagent:manage 只提供跨 owner 管理能力,不替代路由权限。
  • Workflow 管理接口复用 agent:readagent:updateagent:activateagent:manage 只提供跨 owner 管理能力,不替代路由权限。
  • MCP Server 管理接口使用 mcp:createmcp:readmcp:updatemcp:manage 允许管理员跨 owner 管理,Tool Runtime 的调用权限为 mcp:invoke
  • Loop / Trace 查询接口使用 loop:read;member 只能查询自己 Run 关联的数据,admin 可以跨 owner 查询。
  • A2A Agent Card discovery 公开;message:send、Task 查询/取消、终态 callback 及其他业务路由默认要求 GoAI-HMAC-SHA256 机器身份认证。

Trace and Errors

普通 JSON API 使用 code / message / data / trace_id envelope。请求可以携带 X-Trace-ID,服务端会在响应头和响应体中复用同一个值。

完整错误码和 SSE 契约见 统一响应契约。AG-UI 与 A2A 保持各自协议的 wire format,不把平台 JSON envelope 套在协议事件或 A2A JSON-RPC 结果外层。

2. HTTP API Matrix

MethodPathAuthResult
GET/pingpublicJSON envelope
POST/auth/registerpublicJSON envelope
POST/auth/loginpublicJSON envelope with JWT
POST/api/chatJWT + chat:usedebug SSE
POST/api/agents/:agent_code/aguiJWT + run:createAG-UI SSE
POST/GET/api/agentsJWT + agent:create / agent:readcreate or list managed Agents
GET/PUT/api/agents/:agent_codeJWT + agent:read / agent:updateAgent detail or metadata update
POST/api/agents/:agent_code/activateJWT + agent:activatevalidate and publish Agent
POST/api/agents/:agent_code/deactivateJWT + agent:activatedeactivate Agent
POST/GET/PUT/api/agents/:agent_code/capabilities[...]JWT + agent:update / agent:readmanage Capability assets
POST/GET/PUT/api/agents/:agent_code/endpoints[...]JWT + agent:update / agent:readmanage and health-check A2A Endpoints
POST/GET/PUT/api/agents/:agent_code/workflows[...]JWT + agent:update / agent:read / agent:activatemanage versioned Workflow definitions
POST/GET/PUT/api/mcp/servers[...]JWT + mcp:create / mcp:read / mcp:updatemanage MCP Servers and discovery snapshots
POST/api/runsJWT + run:createJSON envelope, 202 or idempotent 200
GET/api/runs/:run_idJWT + run:readJSON envelope
GET/api/runs/:run_id/stepsJWT + run:readJSON envelope
GET/api/runs/:run_id/workflowJWT + run:readRun 执行的 Workflow 定义(按 run 的 workflow_id 固定)
GET/api/runs/:run_id/traceJWT + loop:readRun/Step/Loop/A2A trace snapshot
GET/api/runs/:run_id/loopsJWT + loop:readLoop list
GET/api/loops/:loop_idJWT + loop:readLoop detail with evaluations
GET/api/loops/:loop_id/evaluationsJWT + loop:readEvaluation list
POST/api/runs/:run_id/replayJWT + run:replayJSON envelope, 202 or idempotent 200
POST/api/threads/:thread_id/replayJWT + run:replayThread history replay, JSON envelope, 202 or idempotent 200
GET/a2a/agents/:agent_code/.well-known/agent-card.jsonprotocol gatewayA2A Agent Card
POST/a2a/agents/:agent_code/message:sendprotocol gatewayA2A Task
GET/a2a/agents/:agent_code/tasks/:task_idprotocol gatewayA2A Task
POST/a2a/agents/:agent_code/tasks/:task_id:cancelprotocol gatewaycanceled A2A Task
POST/a2a/agents/:agent_code/callbacks/tasks/:task_idprotocol gatewaycallback accepted; target Agent is in the path, source Agent is signed in X-GoAI-Agent-Code

3. Authentication Examples

Register

POST /auth/register HTTP/1.1
Content-Type: application/json
X-Trace-ID: trace-register-001

{
  "username": "alice",
  "email": "alice@example.com",
  "password": "change-me-in-local-development"
}
{
  "code": "OK",
  "message": "register success",
  "data": {"user_id": 1, "username": "alice"},
  "trace_id": "trace-register-001"
}

Login

POST /auth/login HTTP/1.1
Content-Type: application/json

{"username":"alice","password":"change-me-in-local-development"}
{
  "code": "OK",
  "message": "login success",
  "data": {"token": "<jwt returned by the service>"},
  "trace_id": "trace-login-001"
}

不要把真实 JWT、密码或 API Key 写入仓库、日志和文档。

4. Run API Examples

Create a Run

POST /api/runs HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json
Idempotency-Key: create-demo-001
X-Trace-ID: trace-run-001

{
  "agent_code": "planner",
  "workflow_version": 1,
  "thread_id": "thread-demo",
  "trigger_type": "api",
  "input": {"prompt": "拆解这个需求"},
  "provider": "deepseek",
  "model": "deepseek-chat"
}
{
  "code": "OK",
  "message": "success",
  "data": {"run_id": "run-demo", "status": "queued"},
  "trace_id": "trace-run-001"
}

首次创建返回 202 Accepted。同一用户、operation 和 Idempotency-Key 对应相同请求时返回 200 OK 和原 Run;请求哈希不同返回 409 IDEMPOTENCY_KEY_REUSED

Query and Replay

GET /api/runs/run-demo HTTP/1.1
Authorization: Bearer <jwt>
GET /api/runs/run-demo/steps HTTP/1.1
Authorization: Bearer <jwt>
POST /api/runs/run-demo/replay HTTP/1.1
Authorization: Bearer <jwt>
Idempotency-Key: replay-demo-001

Replay 使用原 Run 的输入和 Workflow 创建新 Run;管理员可以跨用户查询或回放,普通用户只能访问自己的 Run。

Thread Replay

POST /api/threads/thread-demo/replay HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json
Idempotency-Key: thread-replay-demo-001
X-Trace-ID: trace-thread-replay-001

{"source_run_id":"run-demo"}

source_run_id 可省略;省略时选择该 Thread 中按 created_at ASC, id ASC 的稳定历史里最新的终态 Run。可回放终态为 successfailedcancelled。Thread 必须存在且为 active,来源 Run 必须属于目标 Thread。

回放会创建新的 run_idtrace_idloop_id,保留目标 Thread 以及来源 Run 的 Agent、Workflow、Provider 和 Model。新 Run 的 input_json 是不可变消息快照:messages[] 同时提供可执行的 role/content 字段和原始 content_json、发送方、接收方、Delegation 关联、状态与协议元数据,并按 created_at ASC, id ASC 排序;回放不会复制旧 Message。

首次请求返回 202 Accepted,相同调用者、operation、Thread、来源和 Idempotency-Key 返回 200 OK 与同一 Run。相同 key 对应不同 Thread 或来源时返回 409 IDEMPOTENCY_KEY_REUSED;管理员和普通用户的幂等键彼此隔离。普通用户只能回放自己的 Thread,管理员可以跨 owner 回放,但新 Run 仍归属于目标 Thread owner。

Loop / Trace 查询

Loop / Trace 查询是只读管理接口,不改变 Run、Delegation 或 Evaluation 状态。GET /api/runs/:run_id/trace 返回根 Run、可访问的 A2A 子 Run、RunStep、LoopRecord、Delegation、DelegationGroup、Message 和 LoopEvaluation;集合字段始终返回空数组而不是 null

GET /api/runs/run-demo/trace HTTP/1.1
Authorization: Bearer <jwt>
X-Trace-ID: trace-observe-001
{
  "code": "OK",
  "message": "success",
  "data": {
    "root_run": {"RunID": "run-demo", "Status": "success"},
    "runs": [],
    "steps": [],
    "loops": [],
    "delegations": [],
    "delegation_groups": [],
    "messages": [],
    "evaluations": []
  },
  "trace_id": "trace-observe-001"
}

/api/runs/:run_id/loops 只返回指定 Run 的 Loop;/api/loops/:loop_id 返回单个 Loop 和其评估结果;/api/loops/:loop_id/evaluations 只返回评估记录。普通用户访问其他 owner 的 Run 或 Loop 返回 403 AUTH_FORBIDDEN,不存在的 Loop 返回 404 LOOP_NOT_FOUND

Run 查询保持既有 Run 字段格式,并在 Parent resume 存在时增加可选的 resume 对象:

{
  "code": "OK",
  "message": "success",
  "data": {
    "RunID": "run-parent-001",
    "Status": "running",
    "CurrentStep": "summarize",
    "resume": {
      "delegation_id": "delegation-review-001",
      "status": "claimed",
      "error": "resume execution lease expired before completion; recovery event scheduled",
      "publish_attempts": 2,
      "execution_attempt": 3,
      "lease_owner": "resume-worker-2",
      "lease_claimed_at": "2026-08-03T12:00:00Z",
      "lease_heartbeat_at": "2026-08-03T12:00:05Z",
      "lease_expires_at": "2026-08-03T12:00:30Z"
    }
  },
  "trace_id": "trace-parent"
}

resume 是受 JWT、RBAC 和 owner/admin 约束的管理诊断信息。租约字段不会进入公开 A2A Task metadata;没有恢复记录时该字段省略。

5. Agent Registry Management API

Agent Registry is the management plane. It controls which Agent identities, capabilities, and protocol endpoints are eligible for discovery. It never replaces A2A execution: local Agents still use loopback HTTP and remote Agents use HTTPS through the same A2A Gateway contract.

Create and publish an Agent

  1. POST /api/agents creates an inactive Agent owned by the current user.
  2. POST /api/agents/:agent_code/capabilities adds a capability asset. V1 publication requires at least one active Workflow capability backed by the same Agent's active Workflow; Tool and Custom assets are not advertised as executable Agent Card skills yet.
  3. POST /api/agents/:agent_code/endpoints adds an inactive A2A endpoint.
  4. POST /api/agents/:agent_code/endpoints/:endpoint_code/health-check discovers the Agent Card, validates its declared agentCode, transport, binding, and Delegation extension, then marks the endpoint active.
  5. POST /api/agents/:agent_code/activate validates all publication invariants and publishes the Agent.
POST /api/agents HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json

{"agent_code":"writer","name":"Writer","description":"Generates articles"}
POST /api/agents/writer/capabilities HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "capability_code": "write",
  "name": "Write",
  "capability_type": "workflow",
  "workflow_id": 42,
  "version": "1",
  "input_schema_json": "{\"type\":\"object\"}",
  "output_schema_json": "{\"type\":\"object\"}"
}
POST /api/agents/writer/endpoints HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "endpoint_code": "primary",
  "protocol": "a2a",
  "transport": "https",
  "address": "https://agents.example.com/a2a/agents/writer",
  "auth_type": "goai_hmac_sha256",
  "credential_ref": "writer-a2a-key"
}

The API returns only credential_ref; the referenced secret is never stored in or returned by Registry records. Endpoint config_json is restricted to non-sensitive transport metadata; secret, password, token, private-key, authorization, and credential fields are rejected. Endpoint updates reset health to inactive. An active Agent cannot lose its last executable Workflow Capability or healthy Endpoint without first being deactivated.

Member access is owner-scoped. A caller with the separate agent:manage permission can bypass ownership, but still needs the route-specific action permission.

Workflow definition and version management

Workflow is an execution capability owned by one Agent. It is not the platform-wide communication mechanism; Agent-to-Agent communication remains A2A HTTP(S) through the A2A Gateway.

POST /api/agents/writer/workflows HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "version": 1,
  "definition": {
    "entry_node": "prepare",
    "nodes": [{"key": "prepare", "type": "noop"}],
    "edges": []
  },
  "layout": {"positions": {"prepare": {"x": 80, "y": 60}}}
}

The management service calls the shared Workflow parser and validator, trims stable identifiers, canonicalizes JSON, and stores a SHA-256 checksum. New versions start inactive. An active version cannot be edited in place; publish a new version instead.

The optional layout field stores editor canvas metadata (node positions) as an opaque JSON object of at most 64 KB. It never participates in the definition checksum, so two logically identical definitions with different layouts share the same checksum. On PUT, omitting layout keeps the stored layout unchanged; sending it (including {}) replaces it. Responses include layout when present.

The recommended rolling release sequence is:

  1. Create Workflow v2.
  2. Activate v2 while v1 remains active.
  3. Rebind the Agent's Workflow Capability to workflow_id=v2 and version="2".
  4. Deactivate v1 after no active Capability references it.

Workflow list/detail responses include active state, normalized definition, checksum, and Capability references. Member access is owner-scoped; administrators with agent:manage may inspect or mutate another owner's Agent while still requiring the route-specific permission. Workflow APIs never start a Run or call another Agent directly.

6. AG-UI Gateway

Request

入口:POST /api/agents/:agent_code/agui

POST /api/agents/planner/agui HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json
X-Trace-ID: trace-agui-001

{
  "threadId": "thread-demo",
  "runId": "run-demo",
  "state": {},
  "messages": [
    {
      "id": "message-demo",
      "role": "user",
      "content": "请把需求拆成三个子任务"
    }
  ],
  "tools": [],
  "context": []
}

V1 接受普通文本 user / assistant / system / developer 消息。非空 statetoolscontextforwardedProps 仍会在进入流式阶段前拒绝;多模态和高级消息字段也不在 V1 范围内。parentRunIdresume 已支持,分别用于创建 AG-UI 分支 Run 和恢复等待用户输入的 Run。

parentRunId 只表达 AG-UI Thread 内的 Run lineage:父 Run 必须属于当前用户,子 Run 默认继承父 Run 的 Thread;显式传入的 threadId 必须与父 Run 一致。它不等价于 A2A Delegation 的 Parent/Child Run 关系。

当 Run 进入 waiting_input 时,服务端会在 RUN_FINISHED 事件中返回 interrupt outcome。客户端使用同一个 runIdresume 数组重新调用本入口;每个 entry 必须包含 interruptIdstatusresolvedcancelled)和 JSON payload。所有 pending interrupt 处理完成后,Run 会重新排队,并从持久化的后继节点继续执行。

Event Stream

成功进入流式阶段后,HTTP 状态为 200,响应类型为 text/event-stream。每个 data: 是官方 AG-UI 事件 JSON:

data: {"type":"RUN_STARTED","threadId":"thread-demo","runId":"run-demo"}

data: {"type":"STEP_STARTED","stepName":"prepare"}

data: {"type":"TEXT_MESSAGE_START","messageId":"message-result","role":"assistant"}

data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"message-result","delta":"已拆分"}

data: {"type":"TEXT_MESSAGE_END","messageId":"message-result"}

data: {"type":"STEP_FINISHED","stepName":"prepare"}

data: {"type":"RUN_FINISHED","threadId":"thread-demo","runId":"run-demo"}

暂停等待用户输入时:

data: {"type":"RUN_FINISHED","threadId":"thread-demo","runId":"run-demo","outcome":{"type":"interrupt","interrupts":[{"id":"approval","reason":"approval_required","message":"Approve this action?"}]}}

恢复请求示例:

POST /api/agents/planner/agui HTTP/1.1
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "runId": "run-demo",
  "resume": [
    {
      "interruptId": "approval",
      "status": "resolved",
      "payload": {"approved": true}
    }
  ]
}

当前可能出现的事件包括 RUN_STARTEDSTEP_STARTEDSTEP_FINISHEDTEXT_MESSAGE_STARTTEXT_MESSAGE_CONTENTTEXT_MESSAGE_ENDRUN_FINISHEDRUN_ERROR。流开始前的参数错误使用普通 JSON envelope;流开始后的失败使用 AG-UI RUN_ERROR

7. A2A Gateway

A2A 是 Agent 与 Agent 之间的语义通信协议。Kafka 只能承载 GoAI 内部异步执行消息,不能替代 A2A;本地 Agent 和远程 Agent 必须经过同一 A2A Gateway 契约。

Agent Card

GET /a2a/agents/writer/.well-known/agent-card.json HTTP/1.1
Accept: application/json

最小响应形状如下,实际 skills 来自目标 Agent 当前启用的 Capability:

{
  "name": "Writer",
  "description": "负责文档写作",
  "version": "1.0",
  "supportedInterfaces": [
    {
      "url": "http://127.0.0.1:8080/a2a/agents/writer",
      "protocolBinding": "HTTP+JSON",
      "protocolVersion": "0.3.0"
    }
  ],
  "capabilities": {
    "pushNotifications": true,
    "extensions": [
      {
        "uri": "https://goai.dev/extensions/delegation/v1",
        "description": "GoAI multi-agent delegation metadata",
        "required": true
      }
    ]
  },
  "defaultInputModes": ["text/plain", "application/json"],
  "defaultOutputModes": ["application/json"],
  "skills": [
    {
      "id": "write",
      "name": "Write",
      "description": "生成文档",
      "tags": ["text", "v1"],
      "inputModes": ["text/plain", "application/json"],
      "outputModes": ["application/json"]
    }
  ]
}

本地 HTTP Endpoint 必须是 loopback 地址;远程 Endpoint 必须使用 HTTPS。

Delegate with message:send

POST /a2a/agents/writer/message:send HTTP/1.1
Content-Type: application/json

{
  "message": {
    "messageId": "msg-001",
    "contextId": "thread-demo",
    "taskId": "run-child-001",
    "extensions": ["https://goai.dev/extensions/delegation/v1"],
    "metadata": {
      "https://goai.dev/extensions/delegation/v1": {
        "sourceAgentCode": "planner",
        "capabilityCode": "write",
        "parentRunId": "run-parent-001"
      }
    },
    "parts": [{"text": "把需求整理成文档"}],
    "role": "ROLE_USER"
  }
}

出站 Runtime 会在官方 SendMessageConfig 中设置 returnImmediately=true,并携带由 A2A_CALLBACK_BASE_URL、源 Agent code 和稳定 Task ID 生成的 PushConfig。远程 callback URL 必须使用 HTTPS,本地开发只允许 loopback HTTP。

成功响应是官方 A2A Task;accepted/working 表示目标已持久化委派,不表示业务已经完成:

{
  "id": "run-child-001",
  "contextId": "thread-demo",
  "status": {"state": "TASK_STATE_SUBMITTED"},
  "history": []
}

同一协议重试使用相同标识时会复用已有协作记录;复用标识但改变请求内容会返回 A2A invalid request 错误。

Query Task

GET /a2a/agents/writer/tasks/run-child-001?historyLength=10 HTTP/1.1
Accept: application/json

返回的 Task 状态由 Child Run 和 Delegation 快照映射而来。Task 查询保留为诊断与兼容接口;Parent Worker 不轮询该接口等待终态,主链路使用 Push Notification callback。

Cancel Task

来源 Agent 只能取消自己发起且目标 Agent 匹配的 Task:

POST /a2a/agents/writer/tasks/run-child-001:cancel HTTP/1.1
Authorization: GoAI-HMAC-SHA256 ...
X-GoAI-Agent-Code: planner
X-GoAI-Timestamp: 2026-08-08T10:00:00Z
X-GoAI-Nonce: nonce-cancel-001
X-GoAI-Content-SHA256: <sha256-of-empty-body>

目标 Runtime 在事务中将可取消的 Child Run 推进为 cancelled,将活动 RunStep 推进为 skipped,再通过已有 PushConfig callback 回送取消终态。已处于 completedfailedcanceled 的 Task 重复取消直接返回当前终态快照;来源不匹配、目标不匹配或 Task 不存在分别映射为 UnauthorizedTaskNotFound

Terminal Callback and Parent Resume

Target Child Run 进入 completed / failed / canceled / rejected 后,将官方 A2A StreamResponse 事件发送到:

POST /a2a/agents/planner/callbacks/tasks/run-child-001 HTTP/1.1
Authorization: GoAI-HMAC-SHA256 ...
X-GoAI-Agent-Code: writer
A2A-Notification-Token: <task-bound-token>
X-Trace-ID: trace-parent
Content-Type: application/json

callback 处理规则:

  • Gateway 先校验 HMAC 机器身份,再校验 PushConfig 中绑定的 notification token、来源/目标 Agent、Task ID 和 Delegation。
  • 只接受终态事件;相同终态事件通过事件哈希幂等返回 202,同一 Task 的冲突终态返回 409
  • 成功事件幂等写入跨 Runtime 一致的 Result Message,并发布内部 Kafka run_resume 消息。
  • 失败或取消事件终结 Parent Run,不发布成功 resume。
  • Resume Worker 原子 claim waiting_external Parent Run,并从 Delegation 保存的后继节点游标继续 Eino Graph;重复消息安全 no-op。
  • callback 或 resume 发布失败由 RecoveryWorker 基于持久化状态恢复,进程重启不丢失恢复意图。

Parent Run 等待 callback 时,AG-UI SSE 继续观察持久化快照;AG-UI 客户端断开不会取消已持久化的 Child Run 或 Delegation。

A2A Errors

A2A 错误使用官方 JSON-RPC/HTTP+JSON 错误结构,不使用 GoAI 普通 HTTP envelope:

  • 目标 Agent 或 capability 不存在:InvalidParams
  • Task/Run 不存在:TaskNotFound
  • 稳定 Task、Message 或 Delegation 标识冲突:InvalidRequest
  • 未映射 Runtime 错误:InternalError

8. Protocol-to-Domain Mapping

External conceptInternal model
AG-UI threadIdThread.thread_id
AG-UI input messageMessage with message_type=input
AG-UI runRun with trigger_type=agui
A2A message:sendMessage + Delegation + Child Run
A2A TaskChild Run/Delegation snapshot
Eino Workflow/GraphAgent capability execution, not communication transport

协议 Gateway 不允许通过进程内 Service 直调绕过 A2A。Workflow 的 Agent 节点必须经过 A2A Client;这样本地和远程 Agent 执行的通信语义一致。

9. Compatibility and Current Limits

  • V1 只做文本消息和 callback 驱动的 Child suspend/resume;显式 agent_group 已支持多个 Child Run 的 allanyquorum fan-out/fan-in 和部分失败聚合。不在当前范围内的是多模态和任意并行 DAG。
  • AG-UI parentRunId 分支与用户主动 resume 已在 Issue #61 落地;它们与 A2A Delegation 的 Parent/Child Run 不是同一语义。当前仍不支持多模态消息和任意并行 DAG。
  • 远程来源 Delegation 当前可能使用远程 A2A Task ID 填充 ChildRunID;后续可独立建模 RemoteTaskID,不改变当前协议字段。
  • A2A 业务请求必须携带 AuthorizationX-GoAI-Agent-CodeX-GoAI-TimestampX-GoAI-NonceX-GoAI-Content-SHA256;来源身份必须匹配委派 metadata,Task 查询仅允许委派来源 Agent。
  • 外部协议版本升级时,先更新 Gateway 适配和本文件,再变更内部领域模型;不要把 SDK 类型直接作为 Service 接口。