所有端点的完整请求/响应规范。Base URL 示例:
http://localhost:4000
目录
- 认证
- 通用错误格式
- 健康检查
- Agent Card / A2A
- Session 管理
- Prompt API
- 事件流
- Workflow / AgentOps Replay
- 控制 API
- Provider Registry
- 统计与历史
- SDK 兼容与迁移说明
- Gateway 边界
- 回调工具注册
认证
所有 /v1/* 端点需要 API Key 认证。支持两种方式(二选一):
| 方式 | 请求头 | 示例 |
|---|---|---|
| X-API-Key 头 | X-API-Key | X-API-Key: sk-abc123 |
| Bearer Token | Authorization | Authorization: Bearer sk-abc123 |
API Key 在服务端配置中绑定 tenant_id,用于多租户隔离:
config :cmdc_gateway, CMDCGateway.Plugs.Auth,
api_keys: %{
"sk-abc123" => "tenant-a",
"sk-xyz789" => "tenant-b"
}认证失败响应:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error": "unauthorized",
"message": "Missing or invalid API key"
}
/healthz端点无需认证。
通用错误格式
所有错误响应遵循统一 JSON 结构:
{
"error": "<error_code>",
"message": "<人类可读描述>"
}| HTTP 状态码 | error 代码 | 触发条件 |
|---|---|---|
| 400 | bad_request | 缺少必要参数(如 prompt 的 text、approve 的 approvalId) |
| 401 | unauthorized | API Key 无效、缺失、或未配置 |
| 404 | not_found | Session ID 不存在 |
| 404 | session_dead | Session 存在但 Agent 进程已终止 |
| 422 | create_failed | Agent 创建失败(模型不支持、参数非法等) |
| 422 | registration_failed | 回调工具注册失败 |
| 429 | rate_limited | 超出请求频率限制 |
429 特殊响应头:
HTTP/1.1 429 Too Many Requests
Retry-After: 15
Content-Type: application/json
{
"error": "rate_limited",
"message": "Rate limit exceeded. Retry after 15 seconds.",
"retryAfter": 15
}404 路由不匹配:
{
"error": "not_found",
"message": "No route matches GET /v1/unknown"
}健康检查
检查 Gateway 服务状态,无需认证。
GET /healthz
请求:
GET /healthz HTTP/1.1
Host: localhost:4000无请求体、无认证。
响应 200:
{
"status": "ok",
"version": "0.6.0",
"sessions": {
"active": 5
},
"meter": {
"tracked_keys": 3
},
"timestamp": "2026-04-08T12:00:00.000000Z"
}| 字段 | 类型 | 说明 |
|---|---|---|
status | string | 固定 "ok" |
version | string | Gateway 版本号 |
sessions.active | integer | 当前存活的 Session 数量 |
meter.tracked_keys | integer | 有用量记录的 API Key 数量 |
timestamp | string | ISO 8601 UTC 时间戳 |
Agent Card / A2A
Agent Card 用于外部 registry / orchestrator 发现 Gateway 的协议能力。A2A
JSON-RPC 端点复用相同 API Key 认证;/.well-known/agent.json 可按部署公开。
GET /.well-known/agent.json
响应 200:
{
"a2aVersion": "1.0",
"name": "cmdc-gateway",
"version": "0.6.0",
"capabilities": {
"a2a": true,
"jsonRpc": true,
"streaming": true,
"serverSentEvents": true,
"webSocket": true,
"webhook": true,
"taskPolling": true,
"groupEvents": true,
"auditProjection": true,
"sessionReplay": true,
"workflowReplay": true
},
"endpoints": {
"a2aSend": {"method": "POST", "url": "http://localhost:4000/v1/a2a/tasks/send"},
"a2aSendSubscribe": {"method": "POST", "url": "http://localhost:4000/v1/a2a/tasks/sendSubscribe"},
"a2aSendWithWebhook": {"method": "POST", "url": "http://localhost:4000/v1/a2a/tasks/sendWithWebhook"},
"a2aTaskStatus": {"method": "GET", "url": "http://localhost:4000/v1/a2a/tasks/{taskId}"}
}
}POST /v1/a2a/tasks/send
同步 JSON-RPC task。Gateway 会创建或复用 task-scoped session,投递单次 prompt,并等待 Agent 完成或超时。
{
"jsonrpc": "2.0",
"id": "req-1",
"method": "tasks/send",
"params": {
"id": "task-001",
"message": {
"role": "user",
"parts": [{"type": "text", "text": "总结这个仓库"}]
},
"agent_config": {
"model": "deepseek:deepseek-chat",
"workingDir": "."
},
"timeout_ms": 60000
}
}POST /v1/a2a/tasks/sendSubscribe
流式 JSON-RPC task。请求体与 tasks/send 相同,响应为 SSE,事件 payload
为 A2A TaskStatusUpdateEvent / TaskArtifactUpdateEvent 形态。
POST /v1/a2a/tasks/sendWithWebhook
异步 webhook task。立即返回 accepted;后续状态通过 callbackUrl 接收。
{
"jsonrpc": "2.0",
"id": "req-2",
"method": "tasks/sendWithWebhook",
"params": {
"id": "task-002",
"callbackUrl": "https://client.example.com/cmdc/webhook",
"webhookSecret": "shared-secret",
"message": {
"role": "user",
"parts": [{"type": "text", "text": "跑一次长任务"}]
}
}
}Webhook 会携带 X-CMDC-Signature: sha256=<hex>;签名输入为原始 JSON body。
同步派发失败后进入 dead-letter dispatcher,后台按指数退避重试。
GET /v1/a2a/tasks/:task_id
查询 webhook / SSE 客户端漏接时的短期 task 兜底状态。
{
"taskId": "task-002",
"status": "completed",
"lastEvent": "completed",
"payload": {"event": "task.completed", "taskId": "task-002"},
"updatedAtMs": 1780267200000,
"ttlUntilMs": 1780267800000
}该缓存由 CMDCGateway.TaskStore 提供,默认短期 ETS 存储,不替代业务持久化。
Session 管理
创建 Session
创建一个 CMDC Agent Session,Agent 进程随即启动并进入 idle 状态。
POST /v1/sessions
请求头:
Content-Type: application/json
X-API-Key: sk-abc123请求体:
{
"model": "deepseek:deepseek-chat",
"sessionId": "my-session-001",
"systemPrompt": "你是一个专业的编程助手",
"workingDir": "/home/user/project",
"tools": ["CMDC.Tool.Shell", "CMDC.Tool.ReadFile", "CMDC.Tool.WriteFile"],
"plugins": ["CMDC.Plugin.Builtin.ApprovalGuard"],
"blueprint": "CMDC.Blueprint.Base",
"groupId": "agentops-run-001",
"eventBufferSize": 512,
"maxSteeringQueue": 5,
"maxTurns": 50,
"maxTokens": 4096,
"skillsDirs": ["/home/user/skills"],
"messages": [
{"role": "user", "content": "之前我们在审查 billing 模块"},
{"role": "assistant", "content": "我已经看过入口文件,下一步看测试。"}
],
"providerOpts": {
"temperature": 0.7,
"top_p": 0.95
}
}请求体字段:
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
model | string | 是 | — | LLM 模型标识。格式 provider:model,如 "deepseek:deepseek-chat"、"anthropic:claude-sonnet-4-20250514" |
sessionId | string | 否 | 自动生成 16 位 hex | 自定义 Session ID,用于后续所有操作的标识符 |
systemPrompt | string | 否 | Blueprint 默认值 | Agent 系统提示词,决定 Agent 的行为和人格 |
workingDir | string | 否 | "." | 工具(Shell、ReadFile 等)的工作根目录 |
tools | string[] | 否 | [] | 启用的 CMDC Tool 模块名列表,使用 Elixir 模块全名 |
plugins | string[] | 否 | [] | 启用的 CMDC Plugin 模块名列表 |
blueprint | string | 否 | nil | Blueprint 模块名,定义 Agent 的完整配置模板 |
groupId / group_id | string | 否 | nil | core 0.6 group event stream 标识 |
hibernateAfterMs / hibernate_after_ms | integer | 否 | core 默认 | idle 后 hibernate 配置 |
eventBufferSize / event_buffer_size | integer | 否 | 0 | per-session EventBus ring buffer 大小;开启后可用 SSE since replay |
maxSteeringQueue / max_steering_queue | integer | 否 | core 默认 | steering queue 上限 |
memory | string[] | 否 | [] | AGENTS.md 等服务端工作目录内记忆文件 |
subagents | object[] | 否 | [] | 子 Agent 安全 JSON 规格,字段同 core whitelist |
interruptImmuneTools / interrupt_immune_tools | string[] | 否 | [] | abort/steer 时不杀掉的工具名 |
responseFormat / response_format | object | 否 | nil | 透传给 core 的响应格式约束 |
messages | object[] | 否 | [] | 安全历史导入;仅允许 user / assistant / tool_result |
maxTurns | integer | 否 | 100 | 最大 Agent 轮次数(一次 prompt → response 为一轮) |
maxTokens | integer | 否 | 模型默认 | LLM 单次回复最大输出 token 数 |
skillsDirs | string[] | 否 | [] | Skill 文件扫描目录列表 |
providerOpts | object | 否 | {} | 透传给 LLM Provider 的额外参数(如 temperature、top_p) |
messages 导入限制:
- 角色只允许
"user"、"assistant"、"tool_result";"system"会返回invalid_messages。 - 单次最多 100 条,单条文本最多 32KB,总 JSON 内容最多 256KB。
assistant.toolCalls[]只接受callId/call_id、name、argumentsobject。- Gateway 会重新构造
%CMDC.Message{},忽略外部id/parent_id;任何__struct__/module字段都会被拒绝。 - 任意
skillSelector/skill_selector模块注入不通过 public JSON;请在宿主 Elixir app 服务端配置。
响应 201 Created:
{
"sessionId": "my-session-001",
"status": "created",
"groupId": "agentops-run-001",
"importedMessages": 2
}| 字段 | 类型 | 说明 |
|---|---|---|
sessionId | string | Session 标识符(自定义或自动生成) |
status | string | 固定 "created" |
groupId | string | null | 当前 session 的 group id |
importedMessages | integer | 本次安全导入的历史消息数 |
响应 422 Unprocessable Entity:
{
"error": "create_failed",
"message": ":unknown_provider"
}cURL 示例:
curl -X POST http://localhost:4000/v1/sessions \
-H "X-API-Key: sk-abc123" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek:deepseek-chat",
"systemPrompt": "你是助手",
"tools": ["CMDC.Tool.Shell"],
"maxTurns": 20
}'
查询 Session
获取 Session 的当前运行状态。GET /v1/sessions/:id 与
GET /v1/sessions/:id/status 当前返回相同结构;保留两个路径是为了兼容旧
SDK 和 dashboard。
GET /v1/sessions/:id
GET /v1/sessions/:id/status
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求:
GET /v1/sessions/my-session-001 HTTP/1.1
X-API-Key: sk-abc123响应 200:
{
"sessionId": "my-session-001",
"state": "idle",
"model": "deepseek:deepseek-chat",
"groupId": "agentops-run-001",
"workingDir": "/home/user/project",
"turns": 3,
"toolCalls": 5,
"totalTokens": 12800,
"costUsd": "0.0123",
"tokenUsage": {
"promptTokens": 8200,
"completionTokens": 4600,
"totalTokens": 12800,
"costUsd": "0.0123",
"cachedTokens": 0
},
"uptimeMs": 45200,
"activeSinceMs": null,
"timestampMs": 1780267200000,
"messagesCount": 8,
"pendingTools": [],
"pendingApprovals": [],
"queues": {
"promptQueue": 0,
"steeringQueue": 0
},
"eventBufferSize": 512,
"lastEventIndex": 42,
"eventBufferCount": 42,
"maxSteeringQueue": 5,
"hibernateAfterMs": 60000
}| 字段 | 类型 | 说明 |
|---|---|---|
sessionId | string | Session ID |
state | string | Agent 状态机当前状态,如 "idle" / "running" / "streaming" / "executing_tools" |
model | string | 当前模型,模型切换为异步控制,最终值以 status 为准 |
groupId | string | null | 当前 session 所属 group |
workingDir | string | 服务端校验后的工作目录 |
turns | integer | 已完成的对话轮次数 |
toolCalls | integer | 累计工具调用次数 |
totalTokens | integer | 累计消耗 token 数 |
costUsd | number | string | null | core 统计的成本估算 |
tokenUsage | object | core CMDC.TokenUsage 摘要 |
uptimeMs | integer | Session 存活时间(毫秒) |
pendingTools | array | 等待中的工具调用摘要 |
pendingApprovals | array | 等待人类审批的请求 |
queues.promptQueue | integer | prompt 队列长度 |
queues.steeringQueue | integer | steering 队列长度 |
eventBufferSize | integer | session replay ring buffer 配置大小 |
lastEventIndex | integer | 当前 EventBus 最新事件 index |
eventBufferCount | integer | ring buffer 当前保留事件数 |
响应 404:
{
"error": "not_found",
"message": "Session my-session-001 not found"
}删除 Session
停止 Agent 进程,清理 Session 数据和已注册的回调工具。
DELETE /v1/sessions/:id
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求:
DELETE /v1/sessions/my-session-001 HTTP/1.1
X-API-Key: sk-abc123响应 200:
{
"sessionId": "my-session-001",
"status": "deleted"
}| 字段 | 类型 | 说明 |
|---|---|---|
sessionId | string | 被删除的 Session ID |
status | string | 固定 "deleted" |
副作用:
- Agent 进程被
CMDC.stop/1终止 SessionStore中的条目被删除- 该 Session 注册的所有
CallbackTool被清理 - 活跃的 SSE/WebSocket 连接收到最终事件后断开
Prompt API
向 Agent 发送用户消息(异步)。消息被投递到 Agent 状态机后立即返回 202,后续处理结果通过 SSE 或 WebSocket 推送。
POST /v1/sessions/:id/prompt
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求头:
Content-Type: application/json
X-API-Key: sk-abc123请求体:
{
"text": "帮我用 Python 写一个快速排序"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
text | string | 是 | 用户消息文本。也接受 "prompt" 作为别名字段 |
响应 202 Accepted:
{
"requestId": "a1b2c3d4e5f67890",
"sessionId": "my-session-001",
"queued": false
}| 字段 | 类型 | 说明 |
|---|---|---|
requestId | string | 本次请求的唯一 ID(16 位 hex) |
sessionId | string | Session ID |
queued | boolean | 是否排队等待(Agent 正忙时为 true) |
响应 400:
{
"error": "bad_request",
"message": "Missing 'text' field"
}典型调用流程:
1. POST /v1/sessions/:id/prompt → 202 (消息已投递)
2. GET /v1/sessions/:id/events → SSE 流接收处理事件
- event: agent_start
- event: message_delta (多次,逐 token)
- event: tool_execution_start (如果 Agent 调用了工具)
- event: tool_execution_end
- event: agent_end (本轮完成)cURL 示例:
curl -X POST http://localhost:4000/v1/sessions/my-session-001/prompt \
-H "X-API-Key: sk-abc123" \
-H "Content-Type: application/json" \
-d '{"text": "帮我用 Python 写一个快速排序"}'
事件流
SSE 事件流
建立 Server-Sent Events 长连接,实时接收 Agent 事件推送。
GET /v1/sessions/:id/events
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求:
GET /v1/sessions/my-session-001/events HTTP/1.1
X-API-Key: sk-abc123
Accept: text/event-streamQuery 参数:
| 参数 | 类型 | 说明 |
|---|---|---|
since | integer | 从指定 EventBus index 之后 replay;也可使用 Last-Event-ID 请求头 |
types | string | 逗号分隔的 core event type 白名单,如 message_delta,agent_end |
mode | string | 默认轻量事件;audit 时投影为 audit_event |
since/types 只支持 session SSE。group SSE 是 live-only,带 replay 参数会返回
group_replay_not_supported。
响应头:
HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-cache
X-Accel-Buffering: no
Transfer-Encoding: chunkedSSE 数据格式:
每个事件由 event: 行(事件类型)和 data: 行(JSON 载荷)组成,以双换行分隔:
event: agent_start
data: {}
event: message_delta
data: {"delta":"你"}
event: message_delta
data: {"delta":"好"}
event: message_delta
data: {"delta":"!"}
event: agent_end
data: {"messageCount":2,"lastMessage":{"content":"你好!","role":"assistant"},"tokenUsage":{"promptTokens":50,"completionTokens":5,"totalTokens":55}}
心跳:
每 30 秒发送一次 SSE 注释行,保持连接:
: heartbeat
连接终止条件:
- 客户端主动断开
- 收到
agent_end或agent_abort事件后 SSE 流自动关闭 - 60 秒无事件超时自动断开
cURL 示例:
curl -N http://localhost:4000/v1/sessions/my-session-001/events \
-H "X-API-Key: sk-abc123"
Group SSE 事件流
订阅同一 groupId 下多个 session 的实时事件,适合 AgentOps run console
按 group 聚合展示。
GET /v1/groups/:group_id/events
Query 参数:
| 参数 | 类型 | 说明 |
|---|---|---|
mode | string | 默认轻量事件;audit 时投影为 audit_event |
group SSE 是 live-only:since、types 和 Last-Event-ID 都会返回
group_replay_not_supported。断线补帧请使用每个 session 的
/v1/sessions/:id/events?since=...&types=...。
事件示例:
event: message_delta
data: {"sessionId":"my-session-001","groupId":"agentops-run-001","delta":"hello"}SSE 事件类型详解
基础事件按 Agent 生命周期阶段分组;启用 cmdc_rag_arcana 时还会输出
RAG / GraphRAG / Eval 专用 trace 事件。
1. 会话生命周期
agent_start — Agent 开始处理当前 prompt
event: agent_start
data: {}| data 字段 | 类型 | 说明 |
|---|---|---|
| (空对象) | — | 无额外数据 |
agent_end — Agent 完成当前 prompt 处理
event: agent_end
data: {"messageCount":4,"lastMessage":{"content":"代码已完成","role":"assistant"},"tokenUsage":{"promptTokens":500,"completionTokens":200,"totalTokens":700}}| data 字段 | 类型 | 说明 |
|---|---|---|
messageCount | integer | 当前对话总消息数 |
lastMessage | object | null | 最后一条 assistant 消息,含 content(string) 和 role(string) |
tokenUsage | object | Token 用量统计 |
tokenUsage.promptTokens | integer | 输入 token 数 |
tokenUsage.completionTokens | integer | 输出 token 数 |
tokenUsage.totalTokens | integer | 总 token 数 |
agent_abort — Agent 被中止
event: agent_abort
data: {"reason":"max_turns_exceeded"}| data 字段 | 类型 | 说明 |
|---|---|---|
reason | string | 中止原因,如 "max_turns_exceeded"、"user_cancelled"、"aborted" |
prompt_received — Agent 确认收到用户 prompt
event: prompt_received
data: {"text":"帮我写个排序算法"}| data 字段 | 类型 | 说明 |
|---|---|---|
text | string | 用户发送的 prompt 文本 |
2. 流式响应
message_start — LLM 开始生成回复
event: message_start
data: {}| data 字段 | 类型 | 说明 |
|---|---|---|
| (空对象) | — | 无额外数据 |
message_delta — 流式文本片段(逐 token)
event: message_delta
data: {"delta":"def quick_sort"}| data 字段 | 类型 | 说明 |
|---|---|---|
delta | string | 文本增量片段,客户端应追加到当前消息 |
一次 LLM 回复会产生大量
message_delta事件,客户端应逐个追加拼接完整回复。
thinking_start — Agent 思考链(Chain-of-Thought)开始
event: thinking_start
data: {}| data 字段 | 类型 | 说明 |
|---|---|---|
| (空对象) | — | 仅在模型支持 thinking 功能时触发 |
thinking_delta — 思考链文本片段
event: thinking_delta
data: {"delta":"让我分析一下这个问题..."}| data 字段 | 类型 | 说明 |
|---|---|---|
delta | string | 思考过程的文本增量 |
3. 工具执行
tool_calls — Agent 本轮决定调用的工具数量
event: tool_calls
data: {"count":2}| data 字段 | 类型 | 说明 |
|---|---|---|
count | integer | 本轮工具调用总数 |
tool_execution_start — 工具开始执行
event: tool_execution_start
data: {"toolName":"Shell","callId":"tc_a1b2c3","args":{"command":"python3 sort.py","working_dir":"."}}| data 字段 | 类型 | 说明 |
|---|---|---|
toolName | string | 工具名称 |
callId | string | 本次调用的唯一 ID |
args | object | 工具调用参数(key-value 均为 string,超过 1024 字节的值会被截断并追加 ...[truncated]) |
tool_execution_end — 工具执行完成
event: tool_execution_end
data: {"toolName":"Shell","callId":"tc_a1b2c3","status":"ok","result":"[1, 2, 3, 4, 5]"}| data 字段 | 类型 | 说明 |
|---|---|---|
toolName | string | 工具名称 |
callId | string | 调用 ID(与 tool_execution_start 的 callId 对应) |
status | string | 执行状态:"ok" 成功 / "error" 失败 |
result | string | 执行结果文本(超过 4096 字节会被截断并追加 ...[truncated]) |
rag | object | null | 仅 RAG 工具成功时出现,含 query、answer、citations、grounding、pipelineRunSummary、graphEvidence、graphStatus 或 ingestStatus 摘要;默认不包含 chunk 原文 |
RAG 工具包括 rag_search、rag_answer、rag_pipeline_answer、
rag_graph_search、rag_graph_status、rag_ingest_status。Gateway 会保留
原始 result 字段以兼容旧客户端,同时补充 rag 结构化摘要供 AgentOps
Trace Viewer / Run Console 使用。
3.1 RAG / GraphRAG Trace
以下事件来自 cmdc_rag_arcana Phase 18 插件、pipeline、maintenance、GraphRAG
和 Eval telemetry bridge。事件 payload 统一 camelCase,并默认移除
text / chunkText / content / prompt / chunks / results 等正文或
chunk 字段。
| 事件 | 说明 |
|---|---|
rag_acl_blocked | collection ACL 阻断 |
rag_retrieved | rag_search / rag_graph_search 检索摘要 |
rag_answered | rag_answer / rag_pipeline_answer 回答摘要 |
rag_citation_used | citation provenance |
rag_pipeline_step | Pipeline step timeline |
rag_ingestion_progress | ingestion job 进度 |
rag_reembed_progress | reembed job 进度 |
rag_graph_progress | GraphRAG rebuild/embed/community 进度 |
rag_graph_audit | GraphRAG 只读查询审计 |
rag_eval_progress | RAG Eval / 发布门禁进度 |
示例:
event: rag_citation_used
data: {"toolName":"rag_answer","callId":"tc_1","collections":["policies"],"citationCount":1,"citation":{"documentId":"doc-1","sourceUri":"kb://policies/approval"}}4. 人机交互(HITL)
approval_required — Agent 需要人类审批才能继续
event: approval_required
data: {"approvalId":"apr_x7y8z9","toolName":"Shell","args":{"command":"rm -rf /tmp/old"},"hint":"危险操作:删除文件","requestedAt":"2026-04-08T12:00:00Z"}| data 字段 | 类型 | 说明 |
|---|---|---|
approvalId | string | 审批请求 ID,用于后续 approve / reject 操作 |
toolName | string | 待审批的工具名称 |
args | object | 工具调用参数 |
hint | string | 审批提示信息(可能为空字符串) |
requestedAt | string | null | 审批请求时间(ISO 8601) |
收到此事件后,Agent 会暂停等待,直到客户端发送
approve或reject。
approval_resolved — 审批已决定
event: approval_resolved
data: {"approvalId":"apr_x7y8z9","status":"approved"}| data 字段 | 类型 | 说明 |
|---|---|---|
approvalId | string | 审批请求 ID |
status | string | 审批结果:"approved" / "rejected" |
ask_user — Agent 向用户提问,等待回答
event: ask_user
data: {"ref":"ask_001","question":"你希望使用哪种排序算法?","options":["快速排序","归并排序","堆排序"]}| data 字段 | 类型 | 说明 |
|---|---|---|
ref | string | 提问引用 ID,用于后续 respond 操作 |
question | string | Agent 的问题文本 |
options | array | null | 可选的预设选项列表,null 表示自由文本回答 |
收到此事件后,Agent 会暂停等待,直到客户端发送
respond。
5. 错误
error — 运行时错误
event: error
data: {"reason":"Provider stream failed: connection timeout"}| data 字段 | 类型 | 说明 |
|---|---|---|
reason | string | 错误原因描述 |
WebSocket 双向通信
全双工连接,出站推送事件 + 入站接收控制消息。适合需要实时双向交互的场景(如审批、对话式 Agent)。
WS /v1/sessions/:id/ws
连接参数:
| 参数 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
id | 路径 | string | 是 | Session ID |
api_key | Query String | string | 是 | API Key(WebSocket 不支持自定义请求头,故用 query 传递) |
连接 URL 示例:
ws://localhost:4000/v1/sessions/my-session-001/ws?api_key=sk-abc123连接失败响应:
| 场景 | HTTP 状态码 | 响应体 |
|---|---|---|
| 未提供 API Key | 401 | {"error":"unauthorized"} |
| Session 不存在 | 404 | {"error":"session_not_found"} |
连接配置:
| 参数 | 值 | 说明 |
|---|---|---|
| idle_timeout | 300,000 ms (5 分钟) | 无消息超时断开 |
| heartbeat | 30 秒 | WebSocket ping 帧保活 |
入站消息(客户端 → Gateway)
所有入站消息为 JSON 格式,必须包含 action 字段。
1. 发送 Prompt
{
"action": "prompt",
"text": "帮我写一个 Hello World"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
action | string | 是 | 固定 "prompt" |
text | string | 是 | 用户消息文本 |
成功响应:
{"ok": true, "action": "prompt"}2. 审批通过
{
"action": "approve",
"approvalId": "apr_x7y8z9"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
action | string | 是 | 固定 "approve" |
approvalId | string | 是 | 来自 approval_required 事件的审批 ID |
成功响应:
{"ok": true, "action": "approve"}3. 审批拒绝
{
"action": "reject",
"approvalId": "apr_x7y8z9"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
action | string | 是 | 固定 "reject" |
approvalId | string | 是 | 来自 approval_required 事件的审批 ID |
成功响应:
{"ok": true, "action": "reject"}4. 回答 Agent 提问
{
"action": "respond",
"ref": "ask_001",
"response": "快速排序"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
action | string | 是 | 固定 "respond" |
ref | string | 是 | 来自 ask_user 事件的引用 ID |
response | string | 是 | 用户的回答文本 |
成功响应:
{"ok": true, "action": "respond"}入站错误响应:
| 场景 | 响应 |
|---|---|
| JSON 解析失败 | {"error": "invalid_json", "message": "Failed to parse JSON"} |
缺少 action 字段 | {"error": "missing_action", "message": "Message must contain 'action' field"} |
| 未知 action | {"error": "unknown_action", "action": "xxx"} |
| Session 进程已终止 | {"error": "session_not_found"} |
出站消息(Gateway → 客户端)
与 SSE 事件内容一致,但封装为 JSON 对象:
{
"event": "<event_type>",
"data": { ... }
}示例:
{"event": "agent_start", "data": {}}
{"event": "message_delta", "data": {"delta": "Hello"}}
{"event": "message_delta", "data": {"delta": " World"}}
{"event": "tool_execution_start", "data": {"toolName": "Shell", "callId": "tc_001", "args": {"command": "echo hi"}}}
{"event": "tool_execution_end", "data": {"toolName": "Shell", "callId": "tc_001", "status": "ok", "result": "hi\n"}}
{"event": "approval_required", "data": {"approvalId": "apr_001", "toolName": "Shell", "args": {"command": "rm -rf /tmp"}, "hint": "危险操作", "requestedAt": null}}
{"event": "agent_end", "data": {"messageCount": 3, "lastMessage": {"content": "完成", "role": "assistant"}, "tokenUsage": {"promptTokens": 100, "completionTokens": 50, "totalTokens": 150}}}JavaScript 客户端示例:
const ws = new WebSocket('ws://localhost:4000/v1/sessions/my-session-001/ws?api_key=sk-abc123');
ws.onopen = () => {
ws.send(JSON.stringify({ action: 'prompt', text: '你好' }));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.event) {
case 'message_delta':
process.stdout.write(msg.data.delta);
break;
case 'approval_required':
// 自动审批示例
ws.send(JSON.stringify({ action: 'approve', approvalId: msg.data.approvalId }));
break;
case 'ask_user':
ws.send(JSON.stringify({ action: 'respond', ref: msg.data.ref, response: '是的' }));
break;
case 'agent_end':
console.log('\nAgent 完成');
ws.close();
break;
}
};Workflow / AgentOps Replay
Gateway 只读取并翻译 workflow event ledger,不启动 workflow executor,不持有 RunStore,也不处理审批人解析、RBAC 或业务审计。
宿主应用需要在服务端配置事件来源:
config :cmdc_gateway,
workflow_event_source: MyApp.WorkflowRunStore事件来源模块必须实现 list_events/2 或 events/2,签名为
(run_id, opts) -> {:ok, events} | {:error, reason}。opts 支持
limit、after_id、after_seq、type、node_id。
GET /v1/workflows/runs/:run_id/events
Query 参数:
| 参数 | 类型 | 说明 |
|---|---|---|
limit | integer | 单页数量,默认 50,最大 500 |
afterId | string | 上一页最后一个 event id |
afterSeq | integer | 上一页最后一个 seq |
type | string | 原始 RunEvent.type 过滤,如 human_task.created |
nodeId | string | 节点 ID 过滤 |
响应 200:
{
"runId": "run_123",
"events": [
{
"event": "workflow.human_task.created",
"run_id": "run_123",
"node_id": "legal_review",
"trace_id": "trace_123",
"span_id": "run_123:legal_review:human_task.created",
"timestamp": "2026-06-01T00:00:00.000Z",
"payload": {
"task_id": "task_legal",
"approval_mode": "any_of",
"correlation_id": "corr_legal"
}
}
],
"page": {
"limit": 50,
"count": 1,
"nextCursor": "run_123:evt:3",
"nextSeq": 3
}
}标准事件名:
| RunEvent.type | Gateway event |
|---|---|
run.started/resumed/paused/waiting/completed/failed/cancelled/retry | workflow.run.* |
orchestrator.node.started/completed/failed/skipped/retry | workflow.node.* |
orchestrator.node.* 且 node_type=fork | workflow.fork.* |
orchestrator.node.* 且 node_type=join | workflow.join.* |
orchestrator.edge.signaled / edge.signaled / signal.edge | workflow.signal.edge |
human_task.created/progress/completed/timeout/failed | workflow.human_task.* |
human_task.decision_recorded | workflow.human_task.decision |
未配置事件来源:
{
"error": "workflow_event_source_not_configured",
"message": "Configure :cmdc_gateway, :workflow_event_source with a module exposing list_events/2 or events/2"
}企业平台必须自行实现:
ApprovalService:审批人解析、通知、幂等提交和CMDCOrchestrator.submit_human_task_decision/4调用。RunStore:生产级 event ledger、分页索引、并发锁和恢复游标。audit hooks:RBAC/ABAC、审批业务审计、租户隔离和合规留痕。
控制 API
REST 版本的控制接口,适合不使用 WebSocket 的场景(如服务端集成、CLI 工具)。功能与 WebSocket 入站消息等价。
审批通过
POST /v1/sessions/:id/approve
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求头:
Content-Type: application/json
X-API-Key: sk-abc123请求体:
{
"approvalId": "apr_x7y8z9"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
approvalId | string | 是 | 来自 approval_required 事件的审批 ID |
响应 200:
{
"ok": true,
"action": "approve",
"approvalId": "apr_x7y8z9"
}响应 400:
{
"error": "bad_request",
"message": "Missing 'approvalId'"
}审批拒绝
POST /v1/sessions/:id/reject
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求头:
Content-Type: application/json
X-API-Key: sk-abc123请求体:
{
"approvalId": "apr_x7y8z9"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
approvalId | string | 是 | 来自 approval_required 事件的审批 ID |
响应 200:
{
"ok": true,
"action": "reject",
"approvalId": "apr_x7y8z9"
}响应 400:
{
"error": "bad_request",
"message": "Missing 'approvalId'"
}回答 Agent 提问
POST /v1/sessions/:id/respond
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求头:
Content-Type: application/json
X-API-Key: sk-abc123请求体:
{
"ref": "ask_001",
"response": "快速排序"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
ref | string | 是 | 来自 ask_user 事件的引用 ID |
response | string | 是 | 用户的回答文本 |
响应 200:
{
"ok": true,
"action": "respond",
"ref": "ask_001"
}响应 400:
{
"error": "bad_request",
"message": "Missing 'ref' or 'response'"
}切换模型
POST /v1/sessions/:id/switch_model
异步调用 CMDC.switch_model/2 或 CMDC.switch_model/3。返回 202 只表示
控制请求已进入 Agent;最终结果请监听 model_switched /
model_switch_failed 事件或查询 status。
请求体:
{
"model": "openai:gpt-4o",
"providerOpts": {
"baseUrl": "https://llm.example.com",
"timeout": 30000
}
}providerOpts 只接受已存在 atom key,未知 key 返回
invalid_provider_opts,避免外部 JSON 动态创建 atom。
响应 202:
{
"ok": true,
"action": "switch_model",
"model": "openai:gpt-4o",
"async": true,
"providerOptsKeys": ["base_url", "timeout"]
}运行时工具控制
POST /v1/sessions/:id/attach_tool
单个 Tool attach,tool 必须是已加载的 Elixir Tool 模块名。
{
"tool": "CMDC.Tool.ReadFile"
}DELETE /v1/sessions/:id/tools/:tool_name
按 Tool name 卸载单个工具。需要批量或原子替换时使用
/v1/sessions/:id/tools/batch。
批量工具控制
POST /v1/sessions/:id/tools/batch
批量暴露 core 0.6 的 CMDC.attach_tools/2、detach_tools/2、replace_tools/2。
这是原子控制面:请求进入 core 前会先完成 Gateway 侧 JSON 校验;core validation 失败时工具表不变。
注意:该端点与
POST /v1/sessions/:id/tools回调工具注册不同。/tools/batch只操作已加载且服务端 allowlist 允许的 Elixir Tool 模块。
请求体:
{
"action": "replace",
"tools": ["CMDC.Tool.ReadFile", "CMDC.Tool.Grep"]
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
action | string | 是 | "attach" / "detach" / "replace" |
tools | string[] | 是 | attach/replace 为 Tool 模块名;detach 为 tool name 字符串 |
服务端 allowlist:
config :cmdc_gateway, :tool_module_allowlist,
[CMDC.Tool.ReadFile, CMDC.Tool.Grep, "CMDC.Tool.ListDir"]未配置时默认允许 CMDC 内置 Tool。生产环境可以收窄该列表;CMDC.Tool.Shell
这类高权限工具尤其建议按租户部署策略显式配置。
响应 200:
{
"ok": true,
"action": "replace_tools",
"attached": ["grep"],
"detached": ["shell"]
}attach 成功时 action 为 "attach_tools",attached 为新增工具名;
detach 成功时 action 为 "detach_tools",detached 为移除工具名;
replace 成功时同时返回 diff。
校验失败:
{
"error": "tool_not_allowed",
"message": "batch tool request failed validation",
"details": [
{"index": 0, "tool": "CMDC.Tool.Shell", "reason": "tool_not_allowed"}
]
}core 原子校验失败返回 422:
{
"error": "attach_tools_failed",
"message": "core validation failed; no tools were changed",
"details": [
{"target": "CMDC.Tool.ReadFile", "reason": "already_attached"}
]
}成功批量变更会通过 SSE/WS 发出 tools_updated:
{
"attached": ["read_file"],
"detached": []
}中段注入与中止
POST /v1/sessions/:id/steer
把一条 steering 文本插入当前或下一轮 Agent 执行。
{
"text": "优先检查测试失败,不要重构无关代码"
}响应 200:
{
"ok": true,
"action": "steer",
"ref": "#Reference<0.1.2.3>"
}POST /v1/sessions/:id/abort
异步中止当前执行。killTools / kill_tools 可取 "none"、
"killable"、"all";clearQueue / clear_queue 为 boolean。
{
"reason": "user_cancelled",
"killTools": "killable",
"clearQueue": true
}响应 202:
{
"ok": true,
"action": "abort",
"opts": {
"reason": "user_cancelled",
"kill_tools": "killable",
"clear_queue": true
},
"async": true
}Plugin opts 热更新
PATCH /v1/sessions/:id/plugins/:plugin/opts
调用 core 0.6 CMDC.update_plugin_opts/3。Gateway 只允许服务端
:plugin_opts_allowlist 中的 Plugin 被更新。
config :cmdc_gateway, :plugin_opts_allowlist,
[CMDC.Plugin.Builtin.CostGuard]请求体:
{
"opts": {
"maxTokens": 120000
}
}opts / pluginOpts / plugin_opts 均可;key 必须能转换为已存在 atom。
响应 202:
{
"ok": true,
"async": true,
"sessionId": "my-session-001",
"plugin": "CMDC.Plugin.Builtin.CostGuard"
}Checkpoint / Resume
POST /v1/sessions/:id/checkpoints
保存当前 session checkpoint。checkpoint backend 只能由服务端配置,客户端传
backend / backendModule / backendOpts 会被拒绝。
{
"label": "before-risky-change",
"metadata": {
"source": "dashboard"
}
}响应 201:
{
"checkpointId": "chk_123",
"sessionId": "my-session-001",
"label": "before-risky-change",
"metadata": {"source": "dashboard"},
"strippedFields": ["pending_approvals"],
"savedAt": "2026-06-01T00:00:00Z",
"schemaVersion": 1
}POST /v1/sessions/resume
从已保存 checkpoint 恢复到新的 Gateway session。
{
"sessionId": "my-session-001",
"checkpointId": "chk_123",
"newSessionId": "my-session-001-resumed",
"workingDir": "/home/user/project",
"groupId": "agentops-run-001",
"eventBufferSize": 512,
"providerOpts": {
"temperature": 0.2
}
}请求体字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
sessionId / session_id | string | 是 | checkpoint 来源 session |
checkpointId / checkpoint_id | string | 否 | 不传时由 backend 决定默认 checkpoint |
newSessionId / targetSessionId | string | 否 | 新 session id;默认自动生成 |
workingDir / working_dir | string | 否 | 恢复后的工作目录,仍经过 WorkingDirPolicy 校验 |
groupId / group_id | string | 否 | 恢复 session 所属 group |
eventBufferSize / event_buffer_size | integer | 否 | 新 session replay ring buffer |
hibernateAfterMs / hibernate_after_ms | integer | 否 | 新 session hibernate 配置 |
providerOpts / provider_opts | object | 否 | 透传 provider opts,只接受已存在 atom key |
响应 201:
{
"sessionId": "my-session-001-resumed",
"status": "resumed",
"checkpointId": "chk_123",
"groupId": "agentops-run-001"
}Provider Registry
Provider Registry 是 admin-only 控制面,用于运行时注册服务端 provider
profile。admin key 通过 :admin_api_keys 配置;普通 API key 返回 403。
config :cmdc_gateway, :admin_api_keys, ["admin-key"]GET /v1/provider_profiles
{
"profiles": [
{
"name": "prod-openai",
"provider": "openai",
"optsKeys": ["api_key", "base_url"],
"registeredAtMs": 1780267200000
}
]
}POST /v1/provider_profiles
{
"name": "prod-openai",
"provider": "openai",
"opts": {
"apiKey": "sk-prod",
"baseUrl": "https://api.openai.com/v1"
}
}响应只回显 optsKeys,不回显密钥值。Gateway JSON API 明确拒绝
resolverFn / resolver_fn;resolver 函数只能由宿主 Elixir app 服务端配置。
DELETE /v1/provider_profiles/:name
{
"ok": true,
"name": "prod-openai",
"status": "deleted"
}统计与历史
用量统计
获取 Session 运行状态 + API Key 维度的用量计量数据。
GET /v1/sessions/:id/stats
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求:
GET /v1/sessions/my-session-001/stats HTTP/1.1
X-API-Key: sk-abc123响应 200:
{
"sessionId": "my-session-001",
"state": "idle",
"turns": 5,
"toolCalls": 12,
"totalTokens": 25600,
"costUsd": "0.0251",
"tokenUsage": {
"promptTokens": 15000,
"completionTokens": 10600,
"totalTokens": 25600,
"costUsd": "0.0251",
"cachedTokens": 2048
},
"uptimeMs": 120000,
"meter": {
"promptCount": 5,
"totalPromptTokens": 15000,
"totalCompletionTokens": 10600,
"totalTokens": 25600,
"totalCostUsd": "0.0251",
"cachedTokens": 2048,
"lastActivityAt": "2026-06-01T00:00:00Z"
},
"eventBufferSize": 512,
"lastEventIndex": 42,
"eventBufferCount": 42
}| 字段 | 类型 | 说明 |
|---|---|---|
sessionId | string | Session ID |
state | string | Agent 状态机当前状态 |
turns | integer | 已完成的对话轮次数 |
toolCalls | integer | 累计工具调用次数 |
totalTokens | integer | 累计消耗 token 数(Agent 侧统计) |
costUsd | number | string | null | Agent 侧成本估算 |
tokenUsage | object | Agent 侧 CMDC.TokenUsage 摘要 |
uptimeMs | integer | Session 存活时间(毫秒) |
meter.promptCount | integer | 该 API Key 发送的 prompt 次数(跨 Session 累计) |
meter.totalPromptTokens | integer | 该 API Key 的累计输入 token 数 |
meter.totalCompletionTokens | integer | 该 API Key 的累计输出 token 数 |
meter.totalTokens | integer | 该 API Key 的累计总 token 数 |
meter.totalCostUsd | number | string | null | 该 API Key 的累计成本估算 |
meter.cachedTokens | integer | 该 API Key 累计缓存命中 token |
meter.lastActivityAt | string | null | 最近一次计量活动时间 |
eventBufferSize | integer | session replay ring buffer 配置大小 |
lastEventIndex | integer | 当前 EventBus 最新事件 index |
eventBufferCount | integer | ring buffer 当前保留事件数 |
meter数据是 API Key 维度的全局统计,不仅限于当前 Session。
对话历史
获取 Session 的完整消息列表。
GET /v1/sessions/:id/messages
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求:
GET /v1/sessions/my-session-001/messages HTTP/1.1
X-API-Key: sk-abc123响应 200:
{
"sessionId": "my-session-001",
"messages": [
{
"id": "msg_001",
"role": "system",
"content": "你是一个专业的编程助手",
"toolCalls": null,
"callId": null,
"name": null,
"isError": false
},
{
"id": "msg_002",
"role": "user",
"content": "帮我用 Python 写一个快速排序",
"toolCalls": null,
"callId": null,
"name": null,
"isError": false
},
{
"id": "msg_003",
"role": "assistant",
"content": "好的,这是一个 Python 快速排序实现:\n\n```python\ndef quick_sort(arr):\n ...\n```",
"toolCalls": [
{
"call_id": "tc_001",
"name": "WriteFile",
"arguments": {"path": "sort.py", "content": "def quick_sort(arr): ..."}
}
],
"callId": null,
"name": null,
"isError": false
},
{
"id": "msg_004",
"role": "tool_result",
"content": "File written successfully",
"toolCalls": null,
"callId": "tc_001",
"name": "WriteFile",
"isError": false
}
]
}Message 对象字段:
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 消息唯一 ID |
role | string | 角色:"system" / "user" / "assistant" / "tool_result" |
content | string | null | 消息文本内容 |
toolCalls | array | null | 仅 assistant 消息可能包含,工具调用列表 |
toolCalls[].call_id | string | 工具调用 ID |
toolCalls[].name | string | 工具名称 |
toolCalls[].arguments | object | 工具调用参数 |
callId | string | null | 仅 tool_result 消息包含,关联的工具调用 ID |
name | string | null | 仅 tool_result 消息包含,工具名称 |
isError | boolean | 是否为错误消息 |
SDK 兼容与迁移说明
cmdc_gateway 0.6 对 HTTP/JSON 客户端保持加性升级:已有路径、字段名和
成功状态码尽量不变,新能力通过新增字段或新增端点暴露。
兼容承诺
- Python / Node / Go 旧 SDK 可以继续只依赖
sessionId、status、requestId、 SSEevent/data和 HITLapprovalId/ref字段;新增字段可以忽略。 POST /v1/sessions/:id/prompt仍异步返回 202;排队语义仍通过queued表达。POST /v1/sessions/:id/approve默认autoResume=true,reject默认autoResume=false;显式autoResume: false/auto_resume: false会被保留。GET /v1/sessions/:id/events不带since/types时仍是实时 SSE;带since后才启用 replay。POST /v1/sessions/:id/tools仍是外部 HTTP callback tool 注册;POST /v1/sessions/:id/tools/batch是 core Tool attach/detach/replace, 两者不能混用。
0.6 迁移重点
- 创建 session 时建议显式传
eventBufferSize,否则断线后无法通过since补 replay。 - 需要跨 session 聚合展示时传
groupId并订阅/v1/groups/:group_id/events;group stream 不保证 replay。 - 恢复会话优先使用 checkpoint/resume。
messages仅用于客户端已持有的、 受大小限制的安全历史导入。 workingDir会被服务端WorkingDirPolicy约束;旧客户端如果传绝对路径, 需要确保该路径位于服务端允许 root 内,且路径段不含 symlink escape。providerOpts、plugin opts和 Provider Profileopts只接受已存在 atom key。SDK 不应发送任意动态 key。skillSelector、checkpoint backend、provider resolver、plugin module 注入均不接受 public JSON,请迁移到宿主 Elixir app 服务端配置。
最小 replay 调用示例
curl -N \
"http://localhost:4000/v1/sessions/my-session/events?since=42&types=message_delta,agent_end" \
-H "X-API-Key: sk-abc123" \
-H "Accept: text/event-stream"
Gateway 边界
Phase 20E 明确以下能力不下沉为 public HTTP JSON API:
| core 能力 | Gateway 边界 | 原因 |
|---|---|---|
skill_selector | 不接受 skillSelector / skill_selector JSON 字段 | 任意模块注入风险;Skill 选择器应由宿主 Elixir app 在服务端配置 |
| system-wide telemetry audit | 不提供 /v1/audit/events 之类全局 SSE | Gateway 的 ?mode=audit 只投影当前 EventBus 事件;全局 telemetry 审计应由宿主 app 使用 :telemetry.attach_many/4 接入观测栈 |
| group replay | GET /v1/groups/:group_id/events 是 live-only;带 since/types/Last-Event-ID 返回 group_replay_not_supported | core 0.6 只提供 per-session ring buffer replay |
CMDC.monitor/1 / demonitor/2 | 不提供 HTTP monitor endpoint | 这是 BEAM 进程内生命周期集成;跨语言客户端请使用 SSE/WS terminal events 和 HTTP status |
| checkpoint backend / provider resolver / plugin module | 不接受客户端 JSON 指定任意模块或 resolver 函数 | 这些属于服务端信任边界和部署配置 |
外部系统恢复会话优先使用 checkpoint/resume。仅当宿主系统已经有安全、尺寸受控的 JSON
历史时,才使用 POST /v1/sessions 的 messages 导入。
回调工具注册
为 Session 动态注册外部 HTTP 回调工具。注册后 Agent 可像使用内置工具一样调用它,Gateway 会代理 HTTP 请求到你的服务。
POST /v1/sessions/:id/tools
路径参数:
| 参数 | 类型 | 说明 |
|---|---|---|
id | string | Session ID |
请求头:
Content-Type: application/json
X-API-Key: sk-abc123请求体:
{
"name": "query_database",
"description": "Run a SQL query on the production database",
"callbackUrl": "https://my-service.example.com/tools/query",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "SQL query to execute"
},
"database": {
"type": "string",
"description": "Target database name",
"enum": ["production", "staging"]
}
},
"required": ["query"]
},
"timeoutMs": 30000
}| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
name | string | 是 | — | 工具名称,Agent 调用时使用此名称 |
description | string | 否 | "External tool: <name>" | 工具描述,帮助 Agent 理解何时使用此工具 |
callbackUrl | string | 是 | — | 你的 HTTP 服务端点 URL,Gateway 向此 URL 发 POST |
parameters | object | 否 | {"type":"object","properties":{}} | JSON Schema 格式的参数定义,Agent 根据此 schema 构造调用参数 |
timeoutMs | integer | 否 | 30000 | HTTP 请求超时时间(毫秒) |
响应 201 Created:
{
"ok": true,
"sessionId": "my-session-001",
"toolName": "query_database"
}响应 422:
{
"error": "registration_failed",
"message": "Missing required fields: name, callbackUrl"
}回调执行流程
当 Agent 决定调用已注册的回调工具时:
1. Gateway → 你的服务(POST 请求)
Gateway 向 callbackUrl 发送 POST 请求:
POST https://my-service.example.com/tools/query HTTP/1.1
Content-Type: application/json
{
"callId": "tc_a1b2c3d4",
"toolName": "query_database",
"args": {
"query": "SELECT count(*) FROM users WHERE active = true",
"database": "production"
},
"sessionId": "my-session-001"
}| 字段 | 类型 | 说明 |
|---|---|---|
callId | string | 工具调用唯一 ID |
toolName | string | 工具名称 |
args | object | Agent 传入的调用参数 |
sessionId | string | Session ID |
2. 你的服务 → Gateway(HTTP 响应)
成功返回(HTTP 2xx):
{
"result": "Active users: 42,851"
}| 字段 | 类型 | 说明 |
|---|---|---|
result | string | 工具执行结果文本,将作为工具返回值传给 Agent |
错误返回(HTTP 2xx,业务错误):
{
"error": "Permission denied: read-only user cannot execute write queries"
}| 字段 | 类型 | 说明 |
|---|---|---|
error | string | 错误描述,Agent 会收到此错误信息并据此调整策略 |
HTTP 非 2xx 状态码或网络错误会被 Gateway 转换为工具执行失败,Agent 会收到包含错误详情的
tool_execution_end事件(status: "error")。
完整回调工具示例(Python Flask)
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/tools/query', methods=['POST'])
def handle_query():
data = request.json
call_id = data['callId']
args = data['args']
query = args.get('query', '')
try:
result = execute_sql(query) # 你的业务逻辑
return jsonify({"result": str(result)})
except Exception as e:
return jsonify({"error": str(e)})
if __name__ == '__main__':
app.run(port=9999)对应注册请求:
curl -X POST http://localhost:4000/v1/sessions/my-session-001/tools \
-H "X-API-Key: sk-abc123" \
-H "Content-Type: application/json" \
-d '{
"name": "query_database",
"description": "Execute SQL queries on the database",
"callbackUrl": "http://localhost:9999/tools/query",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "SQL query"}
},
"required": ["query"]
}
}'