Skip to content

API 参考 ​

示例使用本地运行时,其中 productId 是设备智能体 ID,deviceId 是真实设备 ID,HTTP 路径里的 products 按设备智能体理解。

选择协议 ​

使用场景推荐协议说明
真实设备长期在线、上报状态、接收命令MQTT适合设备端连接、状态同步和命令响应。
其他智能体发现并调用已启用 A2A 的设备智能体A2A over MQTT使用 MQTT v5 的发现和请求/回复主题。
业务系统创建或执行多智能体场景与一次性任务HTTP + SSE使用 HTTP 提交持久化 Run,再通过 SSE 回放并持续接收事件。
业务系统、控制台扩展或自动化脚本调用 Device AgentHTTP适合一次性请求、查询和命令下发。
实时语音交互WebSocket适合连续音频输入、实时识别结果和语音合成输出。
浏览器或设备端通过 WebSocket 连接 MQTT BrokerMQTT over WebSocket这是 MQTT 的传输方式,不是 Device Agent 的语音 WebSocket。

地址与认证 ​

txt
HTTP: http://127.0.0.1:3000
Voice WebSocket: ws://127.0.0.1:3001/ws/voice

启用直接 HTTPS 后,主 HTTP 地址使用 https://。MQTT Broker 和语音服务需单独配置 TLS,浏览器连接时使用 wss://。使用私有 CA 时,向客户端提供 CA 文件并保留证书校验。

本地 Gateway 的主 HTTP 和 SSE 入口支持可选的服务 Bearer Token 和提供商控制台 SSO。语音 WebSocket 不继承这些认证,除设备或平台绑定校验外仍需另行保护。本地运行时不提供通用的多租户隔离、会话归属或资源级授权;共享或公网接入需在入口校验用户与资源权限。HTTPS/WSS 只保护传输。认证配置见环境变量。

配置 AGENT_GATEWAY_API_TOKEN 后,服务客户端在请求头中传入:

http
Authorization: Bearer <AGENT_GATEWAY_API_TOKEN>

配置控制台 SSO 后,也可使用有效的登录会话 Cookie。认证失败返回 401 和 { "error": "Unauthorized" }。/health、/livez 和 /readyz 免鉴权;/metrics 使用已配置的服务或控制台认证。

MQTT ​

设备通过 MQTT 上线、上报状态和事件、接收命令并返回结果。服务地址、凭证、主题和消息体由当前 IoT 平台接入方式决定;从控制台获取所选设备的协议。

由 Device Agent 直接管理的设备使用 MQTT 接入中的完整协议:

A2A over MQTT ​

A2A 客户端通过 MQTT v5 发现并调用实时网络中已启用 A2A 的设备智能体:

用途主题
发现智能体卡片$a2a/v1/discovery/{org_id}/{unit_id}/{agent_id}
发现当前组织$a2a/v1/discovery/{org_id}/+/+
发现全部组织$a2a/v1/discovery/+/+/+
发送请求$a2a/v1/request/{org_id}/{unit_id}/{agent_id}
接收回复通过 MQTT v5 responseTopic 指定,通常位于 $a2a/v1/reply/... 下

智能体卡片使用 QoS 1 的 retained 消息;空 retained 消息表示移除卡片。Device Agent 处理 JSON-RPC SendMessage 请求。请求使用 QoS 1,并且必须携带 responseTopic;可选的 correlationData 会原样返回。需要延续同一上下文时使用 params.message.contextId;需要指定设备时使用 params.metadata.deviceId。

请求示例:

json
{
  "jsonrpc": "2.0",
  "id": "task-001",
  "method": "SendMessage",
  "params": {
    "message": {
      "role": "user",
      "parts": [
        {
          "type": "text",
          "text": "检查当前温度。如果温度过高,把空调切换到制冷模式。"
        }
      ],
      "taskId": "task-001",
      "contextId": "server-room-session"
    },
    "metadata": {
      "sender": "my-a2a-client",
      "deviceId": "air-conditioner-01"
    }
  }
}

回复使用 QoS 1,并发送到 responseTopic:

JSON-RPC 结果内容
result.statusUpdatetaskId、可选 contextId,以及 status.state、status.timestamp、可选 status.message。
result.artifactUpdatetaskId、可选 contextId,以及 artifact.artifactId、artifact.parts、append、lastChunk。

完整文本先通过 artifactUpdate.artifact.parts[].text 返回,再以 TASK_STATE_COMPLETED 结束;失败以 TASK_STATE_FAILED 结束。客户端应校验相同的 JSON-RPC id 和 correlationData,并为等待回复设置超时。

启用智能体、使用任务与场景以及排错方式见 A2A 多智能体协作。

HTTP ​

除健康检查和指标接口外,HTTP API 路径均以 /api 开头,没有版本号。平台的 /api/chat 返回持久化 Agent Run 事件流;A2A 提交接口返回 JSON,并提供独立事件流。其他接口使用 JSON。升级前请查看发布历史。

GET /health 只表示 HTTP 进程可响应,不检查大模型、MQTT 或设备是否可用。

JSON 错误响应包含 error,可选字段为 code、message、details 和外部平台错误码 providerCode。按 HTTP 状态码和返回的 code 处理错误,并检查可选字段是否存在。

对话和视觉 ​

方法路径用途
GET/health检查 HTTP 进程是否可用。
GET/livez检查进程存活状态。
GET/readyz查询就绪检查;就绪返回 200,未就绪返回 503。
GET/metrics获取 Prometheus 文本格式指标。
GET/api/capabilities查询当前分角色部署能力清单及不可用原因。
POST/api/chat发起文本对话,必须设置 stream: true。
POST/api/cloud/runs提交持久化平台 Agent Run。
GET/api/cloud/runs/:runId查询持久化平台 Agent Run。
POST/api/cloud/runs/:runId/cancel取消排队中或运行中的平台 Agent Run。
GET/api/cloud/runs/:runId/events订阅持久化 Run 事件,支持 Last-Event-ID。
GET/api/sessions/:sessionId/history查询会话历史。
GET/api/sessions/:sessionId/artifacts列出 Session 工作区生成的持久化文件。
GET/api/sessions/:sessionId/artifacts/:encodedPath下载列表中的一份 Artifact,支持 ETag 和字节范围。
POST/api/sessions/:sessionId/interrupt中断当前会话。
DELETE/api/sessions/:sessionId归档当前对话并清空上下文;之后仍可使用相同的 sessionId。
POST/api/sessions/:sessionId/tool-approvals授权或撤销高风险工具。仅限可信管理客户端。
POST/api/vision/frames上传视觉帧,供后续对话使用。

部署能力清单 ​

开放平台功能前,调用 GET /api/capabilities 确认部署能力,而非根据 API 进程环境推断其他角色。响应包含版本、manifest digest、配置版本与摘要、API/Worker/Ingress 健康状态、可用 Feature、Tool、Skill、主动 IM Channel 及机器可读的禁用原因。角色报告缺失、过期或不一致时返回 503 和 CLOUD_DEPLOYMENT_CAPABILITIES_UNAVAILABLE。

Run 固定接收时的 manifest;滚动升级期间,只能由报告完全兼容 manifest digest 的 Worker 执行。

文本对话 ​

请求字段要求
message必填,非空文本。
stream必须为 true。
sessionId终端接入建议自行生成并保存;历史、中断、清空和工具授权使用同一 ID。
metadata.productId、metadata.deviceId需要设备上下文时传入。
visionRefs可选,引用同一会话上传的视觉帧。

对话示例:

bash
$ curl -N http://127.0.0.1:3000/api/chat \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  -d '{
    "message": "查看当前温度,并把目标温度设置为 24 度",
    "stream": true,
    "sessionId": "demo-session",
    "metadata": {
      "productId": "thermostat",
      "deviceId": "thermostat-001"
    }
  }'

SSE 事件:

事件数据是否结束
statustype 为 start、thinking、thinking_delta、text_block、tool_start、tool_end、complete、error、compaction_start 或 compaction_end。否
message{ "text", "sessionId", "timestamp" }是
error{ "error", "sessionId", "timestamp?" }是

本地运行时中,同一 sessionId 同时只能有一个对话请求,否则返回 429。平台将请求存入持久化队列,同一会话内串行执行。客户端断开只会关闭事件投递,Run 会继续执行。

平台对话的重试与重连规则:

  • 保存 sessionId,每轮请求使用一个 Idempotency-Key 请求头;重试时复用相同的 POST 请求体和 Key。内容不同返回 409,省略 Key 则每次重试都创建新请求。
  • 重连时传入 Last-Event-ID 或 ?after=<最后的 id>,回放最后收到的 SSE id 之后的事件。
  • /api/chat 不返回 Run ID;中断会话使用 POST /api/sessions/:sessionId/interrupt。

提交平台 Run ​

需要 Run ID 以查询状态、接收事件或取消执行时,使用 POST /api/cloud/runs。此接口供可信业务系统调用,需通过控制台会话或服务认证,并在集成入口校验资源权限;它不执行下文的外部用户设备绑定校验。请求使用 prompt 和顶层设备标识:

bash
$ export BASE=https://agent.example.com
$ curl "$BASE/api/cloud/runs" \
  -H 'Content-Type: application/json' \
  -d '{
    "sessionId": "demo-session",
    "idempotencyKey": "temperature-query-001",
    "prompt": "查看当前温度",
    "productId": "thermostat",
    "deviceId": "thermostat-001"
  }'

sessionId、idempotencyKey 和非空 prompt 必填。新请求返回 201 { "run": {...}, "created": true };相同请求重试返回 200、原 Run 和 created: false。

保存 run.id,按上表查询、订阅事件或取消 Run。事件流支持 Last-Event-ID 或 after。取消不能撤销已完成的设备或外部操作,再次提交前先核对结果。

会话历史与上下文 ​

GET /api/sessions/:sessionId/history 从最新消息向前分页。limit 默认为 20,范围为 1–100。返回 sessionId、messages、messageOffset、nextCursor,上下文用量可用时还包含 contextUsage。

向 before 原样传入上一页的 nextCursor 以读取更早内容,nextCursor: null 表示结束。分页保留用户轮次边界,消息数可能超过 limit。清空会话或改变历史分支可能使游标失效;收到 400 时重新加载最新页。

contextUsage 包含 tokens、contextWindow 和 percent。刚恢复或整理后的用量可能尚不可用,应等待后续模型响应,不能把未知值显示为 0。对话 status 事件可携带上下文用量,compaction_start 与 compaction_end 用于显示整理进度;这些事件不是最终回答。

清空会归档当前对话并重置上下文,不删除长期记忆或设备。界面说明见对话与记忆。

耐久 Session 控制 ​

平台的 POST /api/sessions/:sessionId/interrupt 和 DELETE /api/sessions/:sessionId 使用 Idempotency-Key 请求头支持安全重试:

  • Key 长度为 1 至 256 个字符,不含 ASCII 控制字符。每次 Stop 或 Clear 使用新 Key,同一操作的重试复用原 Key。
  • 重试只回放首次接受的目标和结果,不影响之后创建的工作。不同 Session、操作类型或目标不能共用 Key。
  • 兼容请求可以省略该头,但每次重试都成为新控制操作,不能回放原结果。

Stop 或 Clear 可能先于某轮对话到达时,另用 X-Device-Agent-Target-Idempotency-Key 传入该轮的 idempotencyKey。匹配请求即使稍后到达,也会被标记为取消而不执行。相同目标 Key 对应不同内容返回 409;没有待接收的对话请求时省略此头。

Interrupt 会立即取消排队中的 Run,并请求取消运行中的 Run;响应分别返回 pendingAdmissionCancelled、queuedCancelled、runningCancellationRequested 和 runningAwaitingAcknowledgement。存在排队或运行中的 Run 时,Clear 返回 409 AGENT_SESSION_ACTIVE;Stop 完成后,使用相同操作 Key 和目标 Key 重试 Clear。

操作回放与延迟请求拦截仅在 DEVICE_AGENT_RUN_RETENTION_HOURS 配置的 Agent Run 明细保留期内有效。客户端重试期限须更短;记录过期后,新的用户操作使用新 Key。

Session Artifact ​

GET /api/sessions/:sessionId/artifacts 只列出 Agent Session 工作区中的文件,并返回每个文件的 path、不透明 encodedPath、字节数和 SHA-256 摘要。下载时必须原样使用响应里的 encodedPath,不要自行编码路径。Session 历史和内部 JSONL 文件不会作为 Artifact 暴露。

Artifact 下载使用 application/octet-stream、ETag: "sha256:<digest>"、Accept-Ranges: bytes 和 Cache-Control: private, no-cache。传 If-None-Match 可获得 304;传一个 Range: bytes=<start>-<end> 区间可获得 206。无效范围返回 416,未知 Session 或 Artifact 返回 404。

耐久产品事件流 ​

路径SSE 事件内容
/api/device-events/stream?namespace=&productId=&deviceId=device持久化 Provider 与浏览器模拟器设备事件;三个筛选项都可省略。
/api/timers/eventstimer持久化 Timer changed、fired、complete 和 error 转换。
/api/agent-replies/eventsagent_reply可信 Workflow、Timer、A2A、IM 和 MQTT 调度的持久化回复;普通对话和 PromptTask 不会广播到这里。

事件流的游标和错误规则:

  • 不传游标时只接收新事件。回放时将最后收到的 SSE id 传给 Last-Event-ID 或 ?after=<cursor>,Header 优先。
  • 首个事件为 ready,携带 { "ok": true };keepalive 注释不推进游标。
  • 非法游标返回 400;游标超出已保留的历史边界返回 409 并给出恢复边界;存储预检失败返回 503。
  • 流启动后存储不可用时,服务端发送不含敏感信息的 error 事件并关闭连接。

平台 PromptTask ​

平台只接受 POST /api/chat 或 POST /api/cloud/runs 顶层 promptTask 对象表示的公开 PromptTask:

json
{
  "promptTask": { "kind": "device.modeling" }
}

支持的 kind 为 device.modeling、device.simulator.behavior、device.icon.select 和 device.event.payload.generate。不要把 taskKind 或 taskScope 放入 metadata;平台会以 400 和 PROMPT_TASK_CONTRACT_REQUIRED 拒绝该旧格式。

PromptTask 使用专用系统提示词和独立临时 Session,禁用工具,不读写普通 Agent Session。sdk.bundle.enhance 和未知 kind 返回 400 和 PROMPT_TASK_UNSUPPORTED,不会作为普通对话执行。持久化请求在 /api/cloud/runs 中使用稳定的 sessionId 与 idempotencyKey。

工具授权 ​

平台模式的 approval_required SSE 事件包含持久化 approvalId、toolName、toolCallId、精确 target、提示信息和过期时间。可信管理客户端核对这些字段后,在 5 分钟内批准操作:

json
{
  "approvalId": "approval-id-from-sse",
  "decision": "approve",
  "scope": "once"
}

decision 支持 approve、reject 和 revoke。只有 approve 可以携带 scope;scope 支持 once、session,默认 once。服务端会把决策绑定到已存储的会话、工具调用、输入摘要和目标;客户端不需要重新提交或修改 target。

平台会为 write_file、execute_command、mqtt_publish、webhook 和 send_im_message 请求授权。授权被批准、拒绝、过期、消费或撤销时,SSE 会发送 approval_decision。优先使用单次授权。通过 use_command、update_device_state 和 use_device_job 执行的模型化设备操作仍由持久化执行栅栏避免重复执行,但不要求单独授权。

Standalone 仍使用本地 action、toolName 和精确 target 请求契约,只为 write_file 和 execute_command 授权;它不使用平台持久化 approvalId。

视觉帧 ​

视觉帧先通过 /api/vision/frames 上传,再把返回的 frameId 和 capturedAt 作为 visionRefs 传给 /api/chat。mimeType 支持 image/jpeg、image/png 和 image/webp。

bash
$ curl http://127.0.0.1:3000/api/vision/frames \
  -H 'Content-Type: application/json' \
  -d '{
    "sessionId": "demo-session",
    "deviceId": "thermostat-001",
    "mimeType": "image/png",
    "imageBase64": "<base64>",
    "source": "camera"
  }'

上传成功返回 201,包含 frameId、capturedAt、source 和 mimeType;请求无效返回 400,视觉帧存储不可用返回 503。imageBase64 字符串最大 6 MiB;每个会话作用域最多保留 20 帧,本地部署 5 分钟后过期,平台部署默认 15 分钟后过期。

平台部署会把每一帧绑定到服务端确定的租户、主体、命名空间、会话、产品和设备作用域。引用帧时必须使用相同的 sessionId、productId 和 deviceId。每个请求可以引用 1 至 3 帧。帧不存在、已过期、重复或作用域不匹配时,接口返回 400 和 VISION_FRAME_REFERENCE_INVALID。

带视觉帧发起对话:

json
{
  "message": "根据这张图判断设备屏幕是否异常",
  "stream": true,
  "sessionId": "demo-session",
  "metadata": {
    "productId": "thermostat",
    "deviceId": "thermostat-001"
  },
  "visionRefs": [
    {
      "frameId": "frame-001",
      "capturedAt": "2026-05-11T10:00:00.000Z",
      "source": "camera"
    }
  ]
}

持久化平台 Agent Run 也在顶层接受相同的 visionRefs:

bash
$ curl http://127.0.0.1:3000/api/cloud/runs \
  -H 'Content-Type: application/json' \
  -d '{
    "sessionId": "demo-session",
    "idempotencyKey": "inspect-frame-001",
    "prompt": "使用这张图片检查设备屏幕是否异常",
    "productId": "thermostat",
    "deviceId": "thermostat-001",
    "visionRefs": [
      {
        "frameId": "frame-001",
        "capturedAt": "2026-05-11T10:00:00.000Z",
        "source": "camera"
      }
    ]
  }'

平台在 Run 入队前解析全部引用,并把不可变图片字节写入持久化请求。因此,帧过期、API 重启、租约丢失或 Worker 替换都不会影响该 Run。使用同一 idempotencyKey 提交不同帧内容时,接口返回 409,不会修改已有 Run。

平台 Skill 与动态 Tool ​

平台把租户的用户 Skill 和动态 Tool 扩展保存为不可变版本。动态 Tool 源码在 Worker 中执行,以下管理接口只接受可信服务端内容。

方法路径用途
GET/api/skills查询当前用户 Skill 及其启用状态。
POST/api/skills/import从 multipart 字段 file 导入 Skill ZIP。
GET/api/skills/:name/export把当前 Skill 版本导出为 ZIP。
PATCH/api/skills/:name使用 { "enabled": true|false } 启用或禁用 Skill。
DELETE/api/skills/:name从当前目录中移除 Skill。
GET/api/tools查询当前动态 Tool、描述符、版本和启用状态。
PATCH/api/tools/:name使用 { "enabled": true|false } 启用或禁用一个动态 Tool。
GET/api/tool-editor/extensions查询当前 Tool 扩展包。
GET/api/tool-editor/extensions/:name/source读取一个当前 Tool 扩展的源码。
POST/api/tool-editor/extensions/save校验并保存一个不可变 Tool 扩展版本。
DELETE/api/tool-editor/extensions/:name从当前目录中移除 Tool 扩展。

Skill ZIP 必须包含一个 <skill-folder>/SKILL.md,YAML frontmatter 声明 name 和 description;可包含 references/、templates/ 和 assets/。压缩后最大 5 MiB、最多 256 个文件条目、解压后最大 25 MiB。

bash
$ curl "$BASE/api/skills/import" \
  -F 'file=@customer-diagnostics.zip'
$ curl "$BASE/api/skills/customer-diagnostics" \
  -X PATCH \
  -H 'Content-Type: application/json' \
  -d '{"enabled":false}'

Tool 扩展导出默认注册函数,使用系统提供的 registerTool 和 Type。每个 Tool 必须声明执行描述符:只读操作使用 { "execution": "read", "approval": "none" };需要审批的副作用操作声明 approval: "required",审批请求只包含允许公开的 values 和 SHA-256 digests。缺少描述符、与静态 Tool 或扩展内 Tool 重名、Schema 无效或导入不支持的 npm 包时,返回 400,不创建版本。

json
{
  "name": "customer-tools",
  "code": "export default function ({ registerTool, Type }) { registerTool({ name: 'customer_lookup', description: 'Look up one customer.', parameters: Type.Object({ customerId: Type.String() }), async execute(_id, params) { return { content: [{ type: 'text', text: params.customerId }], details: {} }; } }, { execution: 'read', approval: 'none' }); }"
}

向 POST /api/tool-editor/extensions/save 提交上述请求体,响应包含不可变的 revision 和 contentDigest。

Agent Run 固定接收时启用的 Skill 和 Tool 版本,目录变更只影响新 Run。Worker 替换或幂等重试仍使用原版本;Worker 校验发现版本、摘要或 Tool 策略不匹配时,Run 失败。

平台 MQTT 操作与 Zero EMQX ​

平台 MQTT 订阅作为持久化操作由 Ingress 执行,API 不另开 Broker 连接;已接收的请求在 API 或 Ingress 替换后保留。

方法路径用途
POST/api/mqtt/subscriptions启动一次有边界的 MQTT 订阅。
GET/api/mqtt/subscriptions/:operationId查询持久化订阅的状态或结果。
POST/api/mqtt/subscriptions/:operationId/cancel请求取消并从 Broker 退订。
GET、PUT/api/agent-config查询或替换带 revision 的非敏感平台运行配置。
POST/api/agent-config/zero-emqx创建并应用一个托管的 Zero EMQX 实例。
GET/api/agent-config/zero-emqx/operations/:operationId查询持久化创建操作的状态或结果。
GET/api/agent-config/mqtt-status查询当前由 Ingress 持有的 Broker 连接状态。

订阅和 Zero EMQX 创建接口均要求 Idempotency-Key 请求头,长度为 1 至 200 个字符。相同 Key 与请求返回原操作;相同 Key 携带不同内容返回 409 和 CLOUD_MQTT_OPERATION_CONFLICT。

使用主题过滤器和明确的资源上限启动订阅:

bash
$ curl "$BASE/api/mqtt/subscriptions" \
  -X POST \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: collect-demo-events-001' \
  -d '{
    "topic": "demo/+/events",
    "qos": 1,
    "duration": 10,
    "maxMessages": 20,
    "maxBytes": 65536
  }'

订阅参数:

  • qos:默认 0。
  • duration:默认 10 秒,范围 1 至 60。
  • maxMessages:默认 100,范围 1 至 100。
  • maxBytes:默认 262,144,范围 1 至 1,048,576。
  • MQTT 5 可选 subscriptionIdentifier(1 至 268,435,455)及最多 32 个 userProperties;属性名和值各不超过 512 个 UTF-8 字节。

操作状态为 pending、running、succeeded、failed、cancelled 或 uncertain。新操作在短暂等待内完成时返回 201,否则返回 202 和 Location,继续查询该地址直至终态。成功结果包含 messages、messageCount、totalBytes 和 limitReached。

仅 cancelled 表示取消完成。uncertain 表示 Broker 清理结果未知;检查 Broker 状态后再决定是否用新 Key 重试。

只有在用户明确确认后才能创建 Zero EMQX:

bash
$ curl "$BASE/api/agent-config/zero-emqx" \
  -X POST \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: provision-demo-broker-001' \
  -d '{"confirmed":true,"tag":"customer-demo"}'

tag 可省略,长度为 1 至 63 个字符。部署需配置 Zero EMQX Manager,并有就绪的 Ingress。新凭据加密保存,与非敏感配置一同提交;Ingress 按该版本重连。API 不返回 Broker 用户名或密码,成功结果包含 instanceId、不带凭据的 brokerUrl 和 wsUrl、expiresAt、configRevision、applyStatus: "applied"。

实例可能已创建但回复丢失时,操作以 uncertain 和 ZERO_EMQX_PROVISION_UNCERTAIN 结束,不自动重试。

使用状态接口确认 Ingress 当前实际持有的配置:

bash
$ curl "$BASE/api/agent-config/mqtt-status"

响应包含 state、connected、不带凭据的 brokerUrl、revision、activeConfigRevision、restartRequired、updatedAt 和可选的脱敏 lastError,不返回 MQTT 凭据。connected: true 表示连接与必需订阅均就绪;restartRequired: true 表示保存的版本比 Ingress 当前使用的版本更新。

GET /api/agent-config 返回 x-device-agent-config-revision、x-device-agent-restart-required 和 x-device-agent-restart-sections。最后一个头以逗号分隔需要重启的进程缓存配置节,无需重启时为空。需要重启时,受影响角色平滑退出,由部署监管器按新 revision 替换。

更新时,将读取到的 revision 放入 PUT /api/agent-config 的 x-device-agent-config-revision 请求头;缺失或无效返回 400,过期返回 409。接口保存非 Secret 配置,掩码响应中的部署 Secret,并拒绝请求体中的部署 Secret 明文。启动环境变量仅初始化 revision 1,后续通过此接口更新。

设备、命令和事件 ​

方法路径用途
GET/api/products查询设备智能体列表。
GET/api/products/:productId查询设备智能体定义,包括属性、命令、参数和事件。
GET/api/products/:productId/devices查询设备列表;支持 status 和 tags 筛选。
GET/api/products/:productId/devices/:deviceId查询设备详情。
POST/api/products/:productId/devices/:deviceId/commands向在线设备下发命令。
GET/api/products/:productId/devices/:deviceId/events查询设备事件。

先查询真实 ID 和产品定义,不要自行拼接命令名或参数:

bash
$ export BASE=http://127.0.0.1:3000
$ curl "$BASE/api/products"
$ export PRODUCT_ID=your_product_id
$ curl "$BASE/api/products/$PRODUCT_ID"
$ curl "$BASE/api/products/$PRODUCT_ID/devices?status=online&tags=site:lab"
$ export DEVICE_ID=your_device_id

设备列表返回 status、state、tags、modelStatus 和 lastSeenAt。status 支持 online、offline、error、all;多个 tags=key:value,key:value 条件需同时匹配。

命令名和参数必须来自产品定义:

bash
$ curl "$BASE/api/products/$PRODUCT_ID/devices/$DEVICE_ID/commands" \
  -H 'Content-Type: application/json' \
  -d '{
    "command": "set_drive_mode",
    "params": {
      "mode": "eco"
    },
    "timeoutMs": 30000
  }'

timeoutMs 默认 30,000,最大 120,000。命令响应:

HTTP结果
200{ "result": <设备响应> }
400COMMAND_VALIDATION_FAILED
404DEVICE_NOT_FOUND
409DEVICE_NOT_ONLINE 或 DEVICE_COMMAND_FAILED
504COMMAND_TIMEOUT

收到 504 时,设备仍可能稍后执行命令;确认设备状态后再决定是否重试。

设备事件按新到旧返回,limit 默认 50、最大 200。下一页把本页最后一条事件的 id 作为 beforeId。

产品 A2A 卡片生命周期 ​

产品 A2A 卡片接口用于发布或删除根据产品运行时模型生成的 retained 发现卡片。

方法路径用途
POST/api/products/:productId/a2a-card发布产品 Agent Card。
DELETE/api/products/:productId/a2a-card删除 retained 产品 Agent Card。
GET/api/products/:productId/a2a-card/operations/:operationId查询一条平台发布操作。
DELETE/api/products/:productId删除产品及其 retained Agent Card。

发布请求体支持 publishToA2ARegistry,默认值为 false:

bash
$ curl "$BASE/api/products/$PRODUCT_ID/a2a-card" \
  -X POST \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: publish-product-card-001' \
  -d '{"publishToA2ARegistry":true}'

平台 Ingress 在等待时间内完成操作时,发布和取消发布返回 200,产品删除返回 204。否则返回 202、{ "operationId": "...", "status": "pending" } 和指向产品操作 URL 的 Location。查询该 URL,直到 status 为 applied 或 failed:

json
{
  "operationId": "operation-001",
  "productId": "thermostat",
  "action": "unpublish",
  "status": "applied",
  "desiredRevision": 2,
  "appliedRevision": 2,
  "createdAt": "2026-08-20T01:00:00.000Z",
  "updatedAt": "2026-08-20T01:00:01.000Z"
}

同一变更复用 Idempotency-Key,返回原操作,不创建新的产品 revision。响应不暴露 Broker 凭据、连接错误或已存储的 Agent Card。

外部平台用户绑定 ​

ThingsNeural 接入的下列设备入口会通过 Client API 校验用户与设备的绑定关系,结果短暂缓存。

外部调用方须用 X-External-User-Id 请求头传入平台用户 ID,缺失或为空返回 401。对话和 Vision 上传可通过有效的控制台会话跳过此校验;其他 IoT 平台不要求该头。

入口校验时机
设备、命令和事件接口每次请求;设备列表只返回该用户已绑定的设备。
POST /api/chatmetadata 携带 deviceId 时;只带 productId 不带设备的请求会被拒绝。
POST /api/vision/frames请求体携带 deviceId 时。
/ws/voice产品为 ThingsNeural 时,URL 必须携带 ?userId=,见下方 WebSocket 部分。
HTTP错误码含义
401EXTERNAL_IDENTITY_REQUIRED缺少外部用户 ID 或设备标识。
403EXTERNAL_DEVICE_NOT_BOUND该用户未绑定此设备,或请求只引用受守卫的产品而未指定设备。
503EXTERNAL_BINDING_UNAVAILABLE绑定查询暂时不可用,请稍后重试。

「设备不存在」和「未绑定」均返回 403,不对外区分。

多智能体编排 ​

平台将 A2A 场景创建、执行和一次性任务作为持久化 Run 交给 Worker。调用前配置可用的大模型,从 /api/a2a/agents 返回的 cards 中选取 status: online 的 agentId。visibility 支持 public、private、all,默认 all。需要本地设备时,用卡片顶层 productId 查询 deviceId,不要自行拼接标识。

方法路径用途
GET/api/a2a/agents?visibility=all查询当前 A2A 组织和单元中的智能体卡片及在线状态。
GET/api/a2a/scenes查询已保存场景。
POST/api/a2a/scenes提交持久化场景创建和流程生成。
GET/api/a2a/scenes/:sceneId查询场景详情和生成状态。
POST/api/a2a/scenes/:sceneId/input为处于 needs_input 的场景提交持久化输入。
POST/api/a2a/scenes/:sceneId/recreate提交持久化的场景替换和重新生成。
DELETE/api/a2a/scenes/:sceneId删除场景。
POST/api/a2a/chat提交持久化的全局场景对话或执行。
POST/api/a2a/scenes/:sceneId/chat提交指定已就绪场景的持久化对话或执行。
POST/api/a2a/tasks提交持久化的一次性多智能体任务。
GET/api/a2a/runs/:runId查询一条持久化编排 Run。
POST/api/a2a/runs/:runId/cancel请求取消一条 Run。
GET/api/a2a/runs/:runId/events回放并持续接收一条 Run 的持久化 SSE 事件。

提交并监控 Run ​

上表的 A2A 提交 POST 均要求 Idempotency-Key 请求头(POST /api/a2a/runs/:runId/cancel 除外)。相同 Key 与请求返回原 Run,不同内容返回 409。

bash
$ curl -i "$BASE/api/a2a/scenes" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: create-server-room-scene-001' \
  -d '{
    "name": "机房响应",
    "instructions": "检查机房温湿度,超过阈值时调节空调并返回处理报告",
    "agentIds": ["<sensor-agent-id>", "<air-conditioner-agent-id>"]
  }'

所有提交返回 202、含 run 的 JSON 和 Location: /api/a2a/runs/<runId>;场景生成另含 sceneId 与场景 status。保存 Run 和场景标识。

创建和重新创建使用 name、instructions、agentIds;场景输入使用 { "answers": "..." };场景和全局对话使用各自的对话输入;一次性 Task 使用 text,可带 devices。

轮询 GET /api/a2a/runs/:runId;pending 和 running 表示未结束,终态为 input_required、succeeded、failed、cancelled 或 uncertain。

input_required 结束当前 Run。需要绑定设备时,终态 SSE 提供一次性 continuationId;将它与设备绑定选择、一个新的 Idempotency-Key 提交到对应场景、全局对话或 Task 接口。继续查询返回的新 Run,原 Run 不会恢复。场景生成结束后还需查询场景:

status处理方式
creating继续等待并查询。创建和重新生成都使用此状态。
needs_input读取 pendingInput,提交补充信息后继续查询。
ready场景可以执行。
failed读取 failureReason;根据原因处理后调用 recreate。

使用 curl -N "$BASE/api/a2a/runs/$RUN_ID/events" 接收事件,重连时传入递增的 SSE id:Last-Event-ID: <last-id> 或 ?after=<last-id>。断开流只取消订阅;停止执行需调用 cancel 接口。uncertain 表示外部动作可能已完成,提交新请求前先核对目标状态。

控制台操作见 A2A 多智能体协作。

工作流 ​

HTTP 接口管理工作流及运行记录;对话中的工作流工具也支持创建和完整替换。使用方式见 工作流。

方法路径用途
GET/api/workflow-capabilities查询工作流可用工具、技能和 IM 通道。
GET/api/tool-catalog查询当前已启用工具的名称与描述。
GET/api/workflows查询已保存工作流。
PUT/api/workflows/:workflowId创建或完整替换一份工作流定义。
PATCH/api/workflows/:workflowId启用或停用工作流。请求体为 { "enabled": true } 或 { "enabled": false }。
DELETE/api/workflows/:workflowId删除工作流。
GET/api/workflow-runs查询工作流运行记录,支持 limit、offset 和 q。
GET/api/workflows/:workflowId/runs查询指定工作流的运行记录。
GET/api/workflow-runs/:runId查询一次运行及各步骤结果。

PUT 替换完整定义,须包含所有要保留的字段。请求体的 id 可省略,传入时须与路径一致;省略 enabled 则设为 true,包括替换已有工作流。创建返回 201,替换返回 200。

bash
$ export WORKFLOW_ID=battery-low-alert
$ curl "$BASE/api/workflows/$WORKFLOW_ID" \
  -X PUT \
  -H 'Content-Type: application/json' \
  -d '{
    "trigger": {
      "kind": "device_event",
      "eventName": "battery_low_warning"
    },
    "actions": [
      {
        "type": "log_event",
        "eventType": "device_event"
      }
    ]
  }'

无效 JSON 对象或工作流定义返回 400,错误码为 WORKFLOW_DEFINITION_INVALID。请求体中的 id 与路径不一致时返回 400,错误码为 WORKFLOW_ID_MISMATCH。

列表返回 { "workflows": [...] };修改返回 { "workflow": {...} };删除成功返回 204。运行列表返回 runs、total、limit 和 offset;传入 q 时还会返回 query。limit 默认 50、最大 100。

定时任务 ​

定时任务通过设备智能体对话创建。HTTP 可以查询、暂停、恢复、修改、取消任务,并读取运行历史。

方法路径用途
GET/api/timers?status=active&limit=50按 active、paused、running、completed 或 deleted 查询任务。
PATCH/api/timers/:timerId设置 enabled,或替换 instruction,也可以同时修改。
DELETE/api/timers/:timerId取消任务。
GET/api/timers/:timerId/runs?limit=25&offset=0分页查询该任务的运行历史。

对话工具 update_scheduled_task 可修改计划类型、规则与时区,保留任务 ID 和目标范围;HTTP PATCH 仅支持指令与启停。更换设备范围须取消并重新创建任务。

任务列表返回 { "timers": [...] },默认只查询 active,limit 最大 100;修改返回 { "timer": {...} },取消成功返回 204。运行历史返回 { "runs", "total", "limit", "offset" }。

控制台中的创建和管理流程见 定时任务。

Webhook ​

方法路径用途
GET/api/webhooks查询已保存连接,不返回 URL、Header 或签名密钥。
POST/api/webhooks创建连接。
PATCH/api/webhooks/:webhookId修改名称、启用状态、凭据、消息体模板或响应条件。
DELETE/api/webhooks/:webhookId删除未被使用的连接;仍被工作流引用时返回 409。
POST/api/webhooks/:webhookId/test发送测试消息并等待投递结果。

创建一个基础自定义连接:

bash
$ curl "$BASE/api/webhooks" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "operations-alerts",
    "enabled": true,
    "credential": {
      "url": "https://hooks.example.com/device-agent"
    },
    "preset": "custom",
    "bodyTemplate": {
      "message": "{{message}}"
    }
  }'

通过 POST /api/webhooks/:webhookId/test 和 { "message": "Device Agent test" } 测试连接。

JSON 请求体最大 64 KiB。创建成功返回 201 { "webhook": {...} },修改返回 200 { "webhook": {...} },删除返回 204,测试成功返回 200 { "ok": true, "httpStatus": <状态码> }。

预设、签名、模板、投递限制和工作流用法见 Webhook。

平台监控、额度与授权 ​

以下接口仅用于平台部署,使用交付地址与部署约定的认证方式。界面操作见监控、设备 AI 额度和授权管理。

方法路径用途与主要参数
GET/api/monitoring平台指标;必填 section,可选 range。
GET/api/monitoring/ai-usage/devices搜索设备;q 为关键词,limit 为 1–20。也可传入 productId 和 deviceId 查询指定设备。
GET/api/monitoring/ai-usage指定设备的用量;必填 productId、deviceId、from、to。
GET/api/monitoring/ai-usage/cumulative累计用量;可选 from 和成对的 productId、deviceId。
GET/api/monitoring/voice语音指标;可选 range 和成对的设备标识。
GET/api/monitoring/voice/turns设备逐轮语音指标;必填设备标识、from、to,可选 cursor。
GET/api/monitoring/voice/turn一轮语音详情;必填设备标识与 UUID 格式的 id。
GET/api/ai-credits/accounts读取设备余额;必填 productId、deviceId。
POST/api/ai-credits/adjustments增加或扣减指定设备的人工余额。
GET/api/cloud/license读取当前部署的授权、用量、排队数量和有效期。
POST/api/cloud/license导入新的授权文件;需要独立的授权管理令牌。

监控查询 ​

/api/monitoring 的 section 支持 overview、usage、cluster、connections、latency;range 支持 1h、24h、7d、30d,默认为 24h。返回所选分区的快照、时间窗口和采集状态。未配置监控或暂时无法查询时返回 503,分别使用 CLOUD_MONITORING_NOT_CONFIGURED 或 CLOUD_MONITORING_UNAVAILABLE。

bash
curl 'https://agent.example.com/api/monitoring?section=overview&range=24h'

设备用量查询的 from、to 为带时区的 ISO 时间,范围向前且最长 31 天。按调用结束时间统计,包含 summary、byModel 和 series。可选筛选参数为 kind(llm、asr、tts)、provider、model、source 和 outcome(completed、failed、cancelled);直接传入这些参数,不使用 filters 查询参数。

累计查询不限制为 31 天,按调用开始时间统计。省略设备标识时汇总当前部署作用域内已记录的设备调用,提供设备标识时只汇总该设备;省略 from 时从首条记录开始。费用为已计价调用的估算,未计价调用不表示免费。

语音汇总的 range 支持 1h、24h、7d,默认 24h。逐轮查询返回 turns 与 nextCursor,翻页时复用原设备和时间范围,并把返回的游标传入 cursor。语音逐轮指标保留 7 天,详情不存在时返回 404。

设备余额调整 ​

余额快照包含 currency、limit、paidBalance、allowanceBalance、totalBalance 和 used。查询现有设备的余额不会创建持久额度账户。

调整请求的 JSON 格式如下:

json
{
  "productId": "thermostat",
  "deviceId": "device-001",
  "amount": 10,
  "reason": "补充试用额度",
  "idempotencyKey": "credit-adjustment-example-001"
}

调整规则:

  • amount 使用当前结算货币,非零且最多 6 位小数;正数增加、负数扣减余额。
  • reason 必填,最长 512 个字符。
  • 同一调整复用 idempotencyKey 和请求内容,新调整使用新 Key。Key 按设备账户隔离,同一账户内内容不同返回 409。
  • 成功返回更新后的余额快照。

统一额度和计费规则通过已有的 GET / PUT /api/agent-config 管理 aiCredits 配置节,更新时保留完整配置并携带读取到的 revision。已有额度账户时不能更改结算货币。

导入平台授权 ​

GET /api/cloud/license 返回当前授权状态、授权内容和用量:used 为最近 60 秒已启动的请求数,hardLimit 为包含弹性额度的每分钟请求上限,queued 为排队请求数。

导入时,POST 请求体为完整授权文件文本,Content-Type 使用 text/plain,并提供 X-Device-Agent-License-Admin-Token 请求头。该令牌与日常登录和服务凭据独立;普通登录或服务认证仍需满足部署要求。仅从可信管理端调用,令牌不应写入浏览器公开配置。

成功返回新的授权快照,当前部署无需重启。令牌缺失或错误、文件不匹配、过期或尚未生效时返回 403;授权版本冲突返回 409。导入失败保留已安装的授权。License 不包含模型和语音等第三方服务额度。

平台 运行日志 ​

平台部署会把 API、Worker 和 Ingress 三个角色产生的脱敏 JSON 日志持久化归档到 PostgreSQL。以下接口不会暴露 Pod 文件路径,仅供可信的服务端管理客户端使用。

方法路径用途
GET/api/logs/files?role=<角色>&instanceId=<实例>&limit=200&cursor=<游标>使用稳定快照游标查询按天归档的日志文件。两个筛选项都可省略,但传 instanceId 时必须同时传 role;limit 范围为 1–500。
GET/api/logs/tail?role=<角色>&instanceId=<实例>&file=<文件>&lines=200&before=<游标>每次读取最多 1,000 条日志,并使用 nextBeforeOffset 向前翻页。
GET/api/logs/download?role=<角色>&instanceId=<实例>&file=<文件>把一份精确归档下载为换行分隔的 JSON。

role 支持 api、worker 和 ingress;单进程 cloud-all 开发运行时使用 all。应先查询可用的角色、实例和文件,不要自行拼接实例 ID。归档文件名使用 UTC 日期,格式为 device-agent-YYYY-MM-DD.log。

bash
$ curl "$BASE/api/logs/files?role=worker&limit=200"
$ curl "$BASE/api/logs/tail?role=worker&instanceId=worker-a&file=device-agent-2026-08-20.log&lines=200"

分页与错误处理:

  • 文件列表的 nextCursor 原样传入下一次请求的 cursor,直到 null。游标固定首批归档快照,避免角色替换或新日志导致漏页。
  • 每页 tail 记录按旧到新排列,nextBeforeOffset 不为空时传入 before 读取更早记录;读完返回 200 和空 records。
  • 归档不存在返回 404;下载超过 64 MiB 返回 413 和 CLOUD_LOG_DOWNLOAD_TOO_LARGE;归档暂不可用返回 503 和 CLOUD_LOG_ARCHIVE_UNAVAILABLE。

默认每进程缓冲最多 2,000 条、每批写入 200 条、保留 14 天。角色在启动标记归档后就绪;瞬时失败会重试,写入超时未确认、持续失败或累计丢弃 100 条时标为不健康,由部署替换。

可通过 DEVICE_AGENT_CLOUD_LOG_BUFFER_RECORDS、DEVICE_AGENT_CLOUD_LOG_BATCH_SIZE、DEVICE_AGENT_CLOUD_LOG_FLUSH_INTERVAL_MS、DEVICE_AGENT_CLOUD_LOG_RETENTION_DAYS、DEVICE_AGENT_CLOUD_LOG_PRUNE_INTERVAL_MS 和 DEVICE_AGENT_CLOUD_LOG_PRUNE_BATCH_SIZE 调整策略,滚动升级时全部角色保持一致。

WebSocket ​

语音通道使用 /ws/voice。连接时可带 Header:

Header说明
Protocol-Version协议版本,当前为 3。
Device-Id当前设备 ID。
Client-Id客户端 ID,不传时默认使用设备 ID。

使用提供用户绑定校验的接入方式时,外部平台用户在 URL 携带 userId;hello 传入 userId 时必须与 URL 一致。服务端校验设备绑定,连接期间周期性复查,绑定解除后以 1008 关闭。

提供设备凭证认证的接入方式,也支持设备端在 URL 传入 productKey、deviceKey、sign。这三个值由对应平台设备凭证规则生成,hello.deviceId 必须与 deviceKey 一致,hello.productId 使用 Device Agent 中的设备智能体 ID。设备认证与外部用户绑定是两种接入路径,按部署提供的认证方式使用。不要将平台管理凭证当作设备签名。认证未通过返回 1008,认证服务不可用返回 1011。

语音连接建立后,先发送 hello:

json
{
  "type": "hello",
  "version": 3,
  "audio_params": {
    "format": "pcm",
    "sample_rate": 16000,
    "channels": 1
  },
  "sessionId": "demo-session",
  "productId": "thermostat",
  "deviceId": "thermostat-001",
  "provider": "aliyun"
}

JSON 控制消息可使用 WebSocket 文本帧,也可放入类型 1 的二进制帧。音频使用类型 0 的二进制帧,格式由 audio_params 约定。二进制帧头固定为 4 字节:

字节内容
0类型:0 音频,1 UTF-8 JSON。
1保留位,设为 0。
2..3payload 长度,big-endian uint16。

16 位 PCM payload 应保持偶数字节,单帧不超过 65,534 字节。客户端应为每轮生成 taskId,并在 listen、stop 和 abort 中使用同一值。

一次语音交互的消息顺序通常是:

方向消息说明
客户端 -> Device Agenthello建立语音会话,提交音频参数、设备上下文和语音服务商。
Device Agent -> 客户端hello返回会话 ID 和服务端语音输出参数。
客户端 -> Device Agentlisten发送 `mode: auto
客户端 -> Device Agent音频二进制帧发送语音数据。
Device Agent -> 客户端asr返回 text、definite、utterances 和 taskId。
客户端 -> Device Agentstop发送同一 taskId,可携带 visionRefs。
Device Agent -> 客户端agent_reply返回 text 和 taskId;streaming: true 表示增量文本。
Device Agent -> 客户端语音合成音频二进制帧播放语音回复。
Device Agent -> 客户端tts_complete表示本轮完成或已中断,可带 taskId。
客户端 -> Device Agentabort使用当前 taskId 中断本轮。
客户端 -> Device Agentgoodbye携带服务端 hello 返回的 session_id。

服务端 hello.audio_params 约定下行 TTS 格式,按其中的 sample_rate 播放 PCM。即使 listen.mode 为 auto,每轮录音结束也须发送 stop。

关闭码:1000 为正常关闭;1008 为绑定校验失败(userId 缺失、不一致或未绑定);1011 为语音服务或绑定查询不可用;1012 为配置已重载,可重新连接。

语音配置见 语音配置,使用方式见 语音交互。