# 设备注册到聊天记录上传接口文档 ## 1. 设备请求平台时的请求头 运营后台和用户 App 调用 `/api/v1/*` 时使用: ```http Authorization: Bearer <用户访问令牌> ``` 设备调用 `/device/*` 时使用以下 HMAC 请求头: | 请求头 | 中文含义 | 计算方式 | |---|---|---| | `X-Device-Id` | 平台设备编号 | 平台登记设备时返回的 `credential.device_id` | | `X-Device-Timestamp` | 请求时间 | 当前 UTC 时间,RFC3339 格式,例如 `2026-09-05T09:20:00Z` | | `X-Device-Nonce` | 防重放随机数 | 每个请求重新生成,长度 16–128 字符,不得重复 | | `X-Device-Content-SHA256` | 请求正文摘要 | `hex_lower(SHA256(rawBody))`;GET 使用空字节正文 | | `X-Device-Signature` | 请求签名 | `base64url_no_padding(HMAC-SHA256(platformDeviceSecret, canonical))` | | `Idempotency-Key` | 业务幂等键 | 仅指定接口需要;同一次请求重试必须保持不变,长度 16–128 字符 | | `Content-Type` | 正文类型 | 有 JSON 正文时为 `application/json` | 签名原文 `canonical`: ```text {大写HTTP方法}\n{请求路径和查询参数}\n{X-Device-Timestamp}\n{X-Device-Nonce}\n{X-Device-Content-SHA256} ``` 计算示例: ```text body = UTF8('{"hardware_id":"echoear-a1b2c3"}') bodySha256 = hex_lower(SHA256(body)) timestamp = "2026-09-05T09:20:00Z" nonce = "d8b81b916ddd4f4584a80a82" canonical = "POST\n" + "/device/v3/hardware-agent-credentials:provision\n" + timestamp + "\n" + nonce + "\n" + bodySha256 signature = base64url_no_padding( HMAC-SHA256(platformDeviceSecret, UTF8(canonical)) ) ``` 注意: - 摘要必须针对实际发送的原始字节计算,签名后不能再次格式化 JSON。 - 查询参数属于签名内容,例如 `/device/v2/commands?limit=10`。 - GET 空正文 SHA-256 固定为 `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`。 - `platformDeviceSecret` 是平台注册时返回的设备密钥,不是火山 `Product Secret`,也不是火山 `device_secret`。 - 平台统一响应:成功为 `{"request_id":"...","data":...}`;失败为 `{"request_id":"...","error":...}`。 ## 2. 完整流程 | 顺序 | 调用方 | 动作 | 接口/地址 | 结果 | |---:|---|---|---|---| | 1 | 运营平台 | 登记设备 | `POST /api/v1/ops/devices` | 返回平台设备 ID、平台设备密钥和绑定码 | | 2 | 用户 App | 绑定设备 | `POST /api/v1/device-claims` | 状态变为 `BOUND` | | 3 | 设备 | 获取火山运行凭据 | `POST /device/v3/hardware-agent-credentials:provision` | 平台动态注册火山设备,消耗一个 License,状态变为 `ACTIVE` | | 4 | 设备 | 检查固件 | `GET /device/v2/bootstrap` | 得到 `firmware_compatible` | | 5 | 设备 | 获取设备配置 | `GET /device/v2/config` | 得到配置版本和运行时配置 | | 6 | 设备 | 判断是否继续 | `firmware_compatible=false` 时停止聊天并进入 OTA 流程 | 只有兼容固件可以继续 | | 7 | 设备 | 创建对话会话 | `POST /device/v3/agent-sessions` | 获取 Bot ID、系统提示词、人设和授权记忆 | | 8 | 设备 | 连接火山 | 火山 WebSocket + `session.update` | 注入本次人设并建立 16 kHz PCM 对话 | | 9 | 设备 | 上报会话开始 | `POST /device/v3/agent-sessions/{id}/events` | 平台会话变为 `ACTIVE` | | 10 | 设备 | 上传最终聊天文本 | `POST /device/v3/agent-sessions/{id}/turns:batch` | 用户和智能体最终文本入库 | | 11 | 设备 | 上报会话结束 | 同一个 events 接口,`session_ended` | 平台会话变为 `COMPLETED` | 激活状态: ```text FACTORY → CLAIMABLE → BOUND → PROVISIONING → ACTIVE → LOST/REVOKED ``` 设备重置流程: ```text 停止语音 → 上传剩余聊天记录 → 上报session_ended → POST /device/v3/factory-reset → 平台解除绑定并暂停火山凭据 → 设备按local_actions清理 → 重启进入CLAIMABLE ``` ## 3. 接口 ### 3.1 登记设备 地址: ```http POST /api/v1/ops/devices ``` 输入: | 字段 | 类型 | 必填 | 中文含义 | |---|---|---:|---| | `model` | string | 是 | 设备型号 | | `hardware_revision` | string | 否 | 硬件版本 | | `minimum_security_version` | string | 否 | 最低安全版本 | | `minimum_firmware_version` | string | 否 | 最低固件版本 | | `household_id` | UUID | 否 | 直接绑定的家庭编号;走绑定码时不传 | | `subject_id` | UUID | 否 | 直接绑定的服务对象编号;走绑定码时不传 | | `voice_runtime` | enum | 是 | 传 `volc_hardware_agent` | | `hardware_agent_bot_id` | string | 否 | 兼容字段;当前方案不传,由平台产品配置决定 | | `hardware_agent_transport` | enum | 否 | `low_load` 或 `high_quality`;建议由平台配置 | | `hardware_agent_minimum_sdk_version` | string | 否 | 最低硬件智能体 SDK 版本 | | `activation_state` | enum | 是 | `FACTORY`、`CLAIMABLE` 或 `BOUND`;一般传 `CLAIMABLE` | 输出: | 字段 | 类型 | 中文含义 | |---|---|---| | `device.id` | UUID | 平台设备编号 | | `device.status` | string | 设备业务状态 | | `device.activation_state` | enum | 激活状态 | | `device.activation_revision` | integer | 激活状态版本 | | `device.model` | string | 设备型号 | | `device.hardware_revision` | string/null | 硬件版本 | | `device.minimum_security_version` | string/null | 最低安全版本 | | `device.minimum_firmware_version` | string/null | 最低固件版本 | | `device.voice_runtime` | enum | 语音运行时 | | `device.version` | integer | 设备记录版本 | | `credential.device_id` | UUID | 平台设备编号 | | `credential.secret` | string | 设备调用平台所用的 HMAC 密钥,仅明文返回一次 | | `credential.key_version` | integer | 平台设备密钥版本 | | `credential.issued_at` | datetime | 密钥签发时间 | | `claim.claim_code` | string/null | 一次性绑定码 | | `claim.issued_at` | datetime/null | 绑定码签发时间 | | `claim.expires_at` | datetime/null | 绑定码过期时间 | 示例: ```http POST /api/v1/ops/devices Authorization: Bearer Content-Type: application/json { "model": "echoear-esp32s3", "hardware_revision": "v1.0", "minimum_firmware_version": "1.0.0", "voice_runtime": "volc_hardware_agent", "activation_state": "CLAIMABLE" } ``` ```json { "request_id": "req-register-001", "data": { "device": { "id": "10000000-0000-0000-0000-000000000512", "status": "registered", "activation_state": "CLAIMABLE", "activation_revision": 1, "model": "echoear-esp32s3", "voice_runtime": "volc_hardware_agent", "version": 1 }, "credential": { "device_id": "10000000-0000-0000-0000-000000000512", "secret": "", "key_version": 1, "issued_at": "2026-09-05T09:00:00Z" }, "claim": { "claim_code": "ABCD-EFGH-IJKL-MNOP", "issued_at": "2026-09-05T09:00:00Z", "expires_at": "2026-09-06T09:00:00Z" } } } ``` ### 3.2 用户绑定设备 地址: ```http POST /api/v1/device-claims ``` 输入: | 字段 | 类型 | 必填 | 中文含义 | |---|---|---:|---| | `claim_code` | string | 是 | 一次性绑定码,不能填写设备 UUID | | `household_id` | UUID | 是 | 家庭编号 | | `subject_id` | UUID | 是 | 设备服务对象编号 | 输出: | 字段 | 类型 | 中文含义 | |---|---|---| | `binding_id` | UUID | 设备绑定关系编号 | | `device_id` | UUID | 平台设备编号 | | `household_id` | UUID | 家庭编号 | | `subject_id` | UUID | 服务对象编号 | | `bound_at` | datetime | 绑定时间 | 示例: ```http POST /api/v1/device-claims Authorization: Bearer Content-Type: application/json { "claim_code": "ABCD-EFGH-IJKL-MNOP", "household_id": "10000000-0000-0000-0000-000000000211", "subject_id": "10000000-0000-0000-0000-000000000411" } ``` ```json { "request_id": "req-bind-001", "data": { "binding_id": "10000000-0000-0000-0000-000000000611", "device_id": "10000000-0000-0000-0000-000000000512", "household_id": "10000000-0000-0000-0000-000000000211", "subject_id": "10000000-0000-0000-0000-000000000411", "bound_at": "2026-09-05T09:10:00Z" } } ``` ### 3.3 占用 License 并获取火山设备凭据 地址: ```http POST /device/v3/hardware-agent-credentials:provision ``` 输入: | 字段 | 位置 | 类型 | 必填 | 中文含义 | |---|---|---|---:|---| | `Idempotency-Key` | Header | string | 是 | 凭据申请幂等键,16–128 字符 | | `hardware_id` | Body | string | 是 | 不随固件升级变化的物理硬件标识 | 输出: | 字段 | 类型 | 中文含义 | |---|---|---| | `instance_id` | string | 火山硬件智能体实例编号 | | `product_key` | string | 火山产品标识 | | `device_name` | string | 火山设备名称 | | `hardware_id` | string | 已绑定的物理硬件标识 | | `device_secret` | string | 火山设备专属密钥,需要加密保存 | | `credential_ref` | string | 平台内部凭据引用 | | `credential_version` | integer | 火山设备凭据版本 | | `credential_source` | enum | `platform_dynamic_registration` 为新注册,`platform_stored` 为复用已有凭据 | | `issued_at` | datetime | 凭据签发时间 | 示例: ```http POST /device/v3/hardware-agent-credentials:provision Idempotency-Key: provision-device-00000512-v1 Content-Type: application/json <设备HMAC请求头> {"hardware_id":"echoear-esp32s3-a1b2c3d4"} ``` ```json { "request_id": "req-provision-001", "data": { "instance_id": "6a92945cc0c4689234aef656", "product_key": "6a9568047dd4b23eacab9b1a", "device_name": "dt-10000000000000000000000000000512", "hardware_id": "echoear-esp32s3-a1b2c3d4", "device_secret": "", "credential_ref": "volc-device-secret://10000000-0000-0000-0000-000000000512", "credential_version": 1, "credential_source": "platform_dynamic_registration", "issued_at": "2026-09-05T09:12:00Z" } } ``` ### 3.4 设备恢复出厂 地址: ```http POST /device/v3/factory-reset ``` 输入: | 字段 | 类型 | 必填 | 中文含义 | |---|---|---:|---| | Header `Idempotency-Key` | string | 是 | 本次重置幂等键,16–128 字符;重试保持不变 | | `reset_id` | UUID | 是 | 本次重置唯一编号 | | `reset_counter` | integer | 是 | 设备安全存储中的单调递增重置次数 | | `occurred_at` | datetime | 是 | 设备确认重置的 UTC 时间;不得晚于请求时间 5 分钟 | | `trigger` | enum | 是 | `physical_long_press`、`device_menu` 或 `service_tool` | | `firmware_version` | string | 是 | 当前固件版本 | | `pending_turn_count` | integer | 是 | 未上传聊天记录数;必须为 `0` | | `active_conversation_id` | UUID | 否 | 设备认为仍在运行的会话编号 | 输出: | 字段 | 类型 | 中文含义 | |---|---|---| | `reset_id` | UUID | 重置编号 | | `device_id` | UUID | 平台设备编号 | | `activation_state` | enum | 重置后固定为 `CLAIMABLE` | | `reset_counter` | integer | 平台接受的重置次数 | | `binding_removed` | boolean | 是否关闭了原用户绑定 | | `previous_binding_id` | UUID/null | 原绑定关系编号 | | `provider_credential_status` | enum | 火山设备凭据状态,通常为 `SUSPENDED` | | `license_action` | enum | 固定为 `RETAINED`,重置不释放火山 License | | `ended_conversation_count` | integer | 平台强制结束的残留会话数 | | `cancelled_command_count` | integer | 平台取消的待执行命令数 | | `config_revision` | integer | 重置后的设备配置版本 | | `claim.code` | string | 新的一次性绑定码 | | `claim.issued_at` | datetime | 绑定码签发时间 | | `claim.expires_at` | datetime | 绑定码过期时间 | | `local_actions` | string[] | 设备收到成功响应后必须执行的本地清理动作 | | `reset_at` | datetime | 平台完成重置的时间 | 平台同时把旧用户侧的设备状态置为离线,并推送 `device.binding.reset` 事件。设备收到成功响应后,按 `local_actions` 清理本地火山凭据、用户上下文、会话与网络配置;设备平台身份和 HMAC 密钥不得删除。 示例: ```http POST /device/v3/factory-reset Idempotency-Key: factory-reset-00000512-0001 Content-Type: application/json <设备HMAC请求头> { "reset_id": "10000000-0000-0000-0000-000000000901", "reset_counter": 1, "occurred_at": "2026-09-05T09:30:00Z", "trigger": "physical_long_press", "firmware_version": "1.1.0", "pending_turn_count": 0 } ``` ```json { "request_id": "req-reset-001", "data": { "reset_id": "10000000-0000-0000-0000-000000000901", "device_id": "10000000-0000-0000-0000-000000000512", "activation_state": "CLAIMABLE", "reset_counter": 1, "binding_removed": true, "previous_binding_id": "10000000-0000-0000-0000-000000000611", "provider_credential_status": "SUSPENDED", "license_action": "RETAINED", "ended_conversation_count": 0, "cancelled_command_count": 0, "config_revision": 9, "claim": { "code": "QRST-UVWX-YZ23-4567", "issued_at": "2026-09-05T09:30:01Z", "expires_at": "2026-09-06T09:30:01Z" }, "local_actions": [ "clear_volc_credential", "clear_user_context", "clear_session_state", "clear_network_credentials", "reboot_to_claimable" ], "reset_at": "2026-09-05T09:30:01Z" } } ``` 设备必须保留平台 `device_id`、平台 HMAC `secret` 和 `hardware_id`;其余用户上下文、火山凭据、会话状态和网络配置按 `local_actions` 清除。 ### 3.5 检查固件版本 地址: ```http GET /device/v2/bootstrap ``` 输入: | 字段 | 位置 | 类型 | 必填 | 中文含义 | |---|---|---|---:|---| | `firmware_version` | Query | string | 是 | 当前固件版本 | | `device_time` | Query | datetime | 是 | 设备 UTC 时间,RFC3339 | | `board_rev` | Query | string | 否 | 硬件版本 | | `rtc_sdk_version` | Query | string | 否 | 火山 SDK 版本 | | `codec` | Query | string | 否 | 音频格式,例如 `pcm16` | | `sample_rate` | Query | integer | 否 | 音频采样率,当前为 `16000` | | `network` | Query | string | 否 | 网络类型,例如 `wifi` | 输出: | 字段 | 类型 | 中文含义 | |---|---|---| | `server_time` | datetime | 平台 UTC 时间 | | `clock_skew_ms` | integer | 平台时间减设备时间的毫秒差 | | `device.device_id` | UUID | 平台设备编号 | | `device.status` | string | 设备业务状态 | | `device.model` | string | 设备型号 | | `device.hardware_revision` | string/null | 硬件版本 | | `device.minimum_security_version` | string/null | 最低安全版本 | | `device.minimum_firmware_version` | string/null | 最低固件版本 | | `device.firmware_compatible` | boolean | 当前固件是否允许继续聊天 | | `device.config_revision` | integer | 当前平台配置版本 | | `device.binding.bound` | boolean | 是否已绑定 | | `device.binding.household_id` | UUID/null | 家庭编号 | | `device.binding.subject_id` | UUID/null | 服务对象编号 | | `device.binding.subject_name` | string/null | 服务对象名称 | 示例: ```http GET /device/v2/bootstrap?firmware_version=1.0.0&board_rev=v1.0&rtc_sdk_version=1.1.0&codec=pcm16&sample_rate=16000&network=wifi&device_time=2026-09-05T09%3A15%3A00Z <设备HMAC请求头> ``` ```json { "request_id": "req-bootstrap-001", "data": { "server_time": "2026-09-05T09:15:01Z", "clock_skew_ms": 1000, "device": { "device_id": "10000000-0000-0000-0000-000000000512", "status": "registered", "model": "echoear-esp32s3", "minimum_firmware_version": "1.0.0", "firmware_compatible": true, "config_revision": 8, "binding": { "bound": true, "household_id": "10000000-0000-0000-0000-000000000211", "subject_id": "10000000-0000-0000-0000-000000000411", "subject_name": "测试用户" } } } } ``` ### 3.6 获取设备配置 地址: ```http GET /device/v2/config ``` 输入: | 字段 | 位置 | 类型 | 必填 | 中文含义 | |---|---|---|---:|---| | `If-None-Match` | Header | string | 否 | 本地配置 ETag;未变化时返回 `304` | 输出: | 字段 | 类型 | 中文含义 | |---|---|---| | Response Header `ETag` | string | 当前配置 ETag | | `device_id` | UUID | 平台设备编号 | | `revision` | integer | 配置版本 | | `desired_config` | object | 平台期望设备应用的配置 | | `desired_config.voice_runtime` | enum | 语音运行时 | | `desired_config.hardware_agent_transport` | enum | 火山传输方式 | | `desired_config.minimum_sdk_version` | string | 最低 SDK 版本 | | `reported_revision` | integer/null | 设备此前报告的配置版本 | | `binding` | object | 当前绑定信息,字段与 bootstrap 相同 | 示例: ```http GET /device/v2/config If-None-Match: "7" <设备HMAC请求头> ``` ```json { "request_id": "req-config-001", "data": { "device_id": "10000000-0000-0000-0000-000000000512", "revision": 8, "desired_config": { "voice_runtime": "volc_hardware_agent", "hardware_agent_transport": "low_load", "minimum_sdk_version": "1.1.0" }, "reported_revision": 7, "binding": { "bound": true, "household_id": "10000000-0000-0000-0000-000000000211", "subject_id": "10000000-0000-0000-0000-000000000411" } } } ``` ### 3.7 创建会话并获取系统提示词 地址: ```http POST /device/v3/agent-sessions ``` 输入: | 字段 | 类型 | 必填 | 中文含义 | |---|---|---:|---| | Header `Idempotency-Key` | string | 是 | 创建会话幂等键,16–128 字符 | | `device_time` | datetime | 是 | 设备 UTC 时间 | | `monotonic_ms` | integer | 否 | 开机后的单调时钟毫秒数 | | `capabilities.hardware_agent` | boolean | 是 | 必须为 `true` | | `capabilities.sdk_version` | string | 是 | 硬件智能体 SDK 版本 | | `capabilities.transports` | string[] | 是 | 支持的传输方式 | | `capabilities.input_audio_format` | string | 是 | 上行音频能力,当前为 `pcm_s16le_16000_mono` | | `capabilities.output_audio_format` | string | 是 | 下行音频能力,当前为 `pcm_s16le_16000_mono` | | `capabilities.server_event_callback` | boolean | 是 | 当前传 `false` | | `capabilities.transcript_upload` | boolean | 是 | 必须为 `true` | | `capabilities.dynamic_session_config` | boolean | 是 | 必须为 `true` | | `capabilities.credential_rotation` | boolean | 是 | 必须为 `true` | | `capabilities.near_pcm_recording` | boolean | 是 | 是否支持近端录音 | | `capabilities.far_pcm_recording` | boolean | 是 | 是否支持远端录音 | | `network.type` | string | 是 | 网络类型 | | `network.rssi` | integer | 否 | Wi-Fi RSSI | | `requested_goal` | string | 是 | 固定为 `open_conversation` | | `config_revision` | integer | 是 | `/device/v2/config` 返回的配置版本 | 输出: | 字段 | 类型 | 中文含义 | |---|---|---| | `conversation_id` | UUID | 平台会话编号 | | `status` | string | 正常为 `SESSION_READY` | | `runtime_type` | string | `volc_hardware_agent` | | `runtime.provider` | string | 服务商,当前为 `volcengine` | | `runtime.bot_id` | string | 本次使用的 Bot ID | | `runtime.transport` | string | 本次传输方式 | | `runtime.provider_session_ref` | string | 火山会话引用 | | `runtime.session_nonce` | string | 会话证明随机值 | | `runtime.config_digest` | string | 本次动态配置摘要 | | `runtime.wait_for_session_update` | boolean | 是否必须先应用动态人设 | | `session_config.dialog_id` | string | 本次对话标识 | | `session_config.system_prompt` | string | 系统提示词/核心人设 | | `session_config.persona_tone` | string | 人设语气和表达风格 | | `session_config.authorized_context` | string | 授权的用户背景、近期摘要和长期记忆 | | `session_config.prompt_bundle_version` | string | 提示词版本 | | `session_config.tool_url` | URL/null | 工具调用地址 | | `session_config.tool_scope_token` | string/null | 本次会话工具令牌 | | `privacy.retention_mode` | string | 数据保留模式 | | `privacy.record_near` | boolean | 是否允许近端录音 | | `privacy.record_far` | boolean | 是否允许远端录音 | | `privacy.upload_allowed` | boolean | 是否允许上传聊天记录 | | `session.engine_mode` | string | 会话引擎模式 | | `session.heartbeat_interval_sec` | integer | 会话状态上报间隔 | | `session.allocation_expires_at` | datetime | 会话分配过期时间 | 示例: ```http POST /device/v3/agent-sessions Idempotency-Key: session-device-00000512-0001 Content-Type: application/json <设备HMAC请求头> { "device_time": "2026-09-05T09:20:00Z", "monotonic_ms": 812345, "capabilities": { "hardware_agent": true, "sdk_version": "1.1.0-echoear", "transports": ["low_load"], "input_audio_format": "pcm_s16le_16000_mono", "output_audio_format": "pcm_s16le_16000_mono", "server_event_callback": false, "transcript_upload": true, "dynamic_session_config": true, "credential_rotation": true, "near_pcm_recording": false, "far_pcm_recording": false }, "network": {"type":"wifi","rssi":-52}, "requested_goal": "open_conversation", "config_revision": 8 } ``` ```json { "request_id": "req-session-001", "data": { "conversation_id": "10000000-0000-0000-0000-000000000801", "status": "SESSION_READY", "runtime_type": "volc_hardware_agent", "runtime": { "provider": "volcengine", "bot_id": "botM4gWQ9A9y", "transport": "low_load", "provider_session_ref": "provider-session-ref", "session_nonce": "", "config_digest": "", "wait_for_session_update": true }, "session_config": { "dialog_id": "dialog-00000801", "system_prompt": "你是面向长者的陪伴助手……", "persona_tone": "率真、自然", "authorized_context": "经授权的背景和记忆……", "prompt_bundle_version": "persona-v12" }, "privacy": { "retention_mode": "transcript_only", "record_near": false, "record_far": false, "upload_allowed": true }, "session": { "engine_mode": "hardware_agent", "heartbeat_interval_sec": 30, "allocation_expires_at": "2026-09-05T09:25:00Z" } } } ``` ### 3.8 连接火山并注入人设 地址: ```text wss://ai-gateway.vei.volces.com/v1/realtime?bot={bot_id}&wait_for_session_update=true ``` 输入: | 字段 | 位置 | 中文含义 | 来源/计算 | |---|---|---|---| | `bot` | Query | Bot ID | 创建会话返回的 `runtime.bot_id` | | `X-Auth-Type` | Header | 鉴权类型 | 固定为 `1` | | `X-Product-Key` | Header | 火山产品标识 | provision 返回的 `product_key` | | `X-Device-Name` | Header | 火山设备名称 | provision 返回的 `device_name` | | `X-Random-Num` | Header | 随机数 | 每次连接重新生成 | | `X-Timestamp` | Header | Unix 秒 | 当前时间 | | `X-Instance-Id` | Header | 火山实例编号 | provision 返回的 `instance_id` | | `X-Hardware-Id` | Header | 物理硬件标识 | provision 使用的 `hardware_id` | | `X-Signature` | Header | 火山鉴权签名 | `base64(HMAC-SHA256(device_secret, canonical))` | | `session.update` | WebSocket JSON | 动态会话配置 | 注入平台返回的人设和记忆 | 输出: | 事件 | 中文含义 | |---|---| | `session.created` / `session.updated` | 火山会话创建/动态人设生效 | | `conversation.item.input_audio_transcription.completed` | 用户最终识别文本 | | `response.audio_transcript.done` | 智能体最终回复文本 | | 音频数据 | 智能体返回的 PCM16 音频,按 16 kHz 单声道播放 | | error 事件 | 火山连接或会话失败 | 示例: ```text canonical = "auth_type=1&device_name={device_name}&random_num={random_num}&product_key={product_key}×tamp={timestamp}&instance_id={instance_id}" X-Signature = base64(HMAC-SHA256(device_secret, canonical)) ``` ```json { "event_id": "evt-session-update-001", "type": "session.update", "session": { "object": "realtime.session", "model": "", "input_audio_format": "pcm16", "output_audio_format": "pcm16", "config": { "ASRConfig": {"TurnDetectionMode": 0}, "LLMConfig": { "SystemMessages": [ "\n表达风格:\n授权上下文:\n会话标识:" ] } }, "agent_config": {"EnableConversationStateCallback": false} } } ``` ### 3.9 上报会话事件 地址: ```http POST /device/v3/agent-sessions/{conversation_id}/events ``` 输入: | 字段 | 类型 | 必填 | 中文含义 | |---|---|---:|---| | Path `conversation_id` | UUID | 是 | 平台会话编号 | | `event_id` | string | 是 | 事件唯一编号 | | `sequence_no` | integer | 是 | 事件顺序号,从 1 开始递增 | | `event_type` | enum | 是 | `session_started`、`session_updated`、`session_ended` 或 `session_failed` | | `occurred_at` | datetime | 是 | 事件发生时间 | | `provider_session_ref` | string | 是 | 火山会话引用 | | `session_nonce` | string | 是 | 会话证明随机值 | | `provider_connection_ref` | string | 启动时必填 | WebSocket 连接标识 | | `bot_id` | string | 启动时必填 | 实际连接的 Bot ID | | `transport` | string | 启动时必填 | 实际传输方式 | | `config_digest` | string | 启动时必填 | 已应用的人设配置摘要 | | `dynamic_config_applied` | boolean | 启动时必填 | 必须为 `true` | | `error_code` | string | 失败时填写 | 失败错误码 | 输出: | 字段 | 类型 | 中文含义 | |---|---|---| | `conversation_id` | UUID | 平台会话编号 | | `status` | string | 会话状态,开始后为 `ACTIVE`,正常结束后为 `COMPLETED` | | `action` | enum | `continue` 或 `stop_session` | | `duplicate` | boolean | 是否为重复事件 | | `accepted_at` | datetime | 平台接收时间 | 示例: 开始请求: ```json { "event_id": "device-event-start-001", "sequence_no": 1, "event_type": "session_started", "occurred_at": "2026-09-05T09:20:03Z", "provider_session_ref": "provider-session-ref", "session_nonce": "", "provider_connection_ref": "ws-connection-001", "bot_id": "botM4gWQ9A9y", "transport": "low_load", "config_digest": "", "dynamic_config_applied": true } ``` 结束示例: ```json { "event_id": "device-event-end-001", "sequence_no": 2, "event_type": "session_ended", "occurred_at": "2026-09-05T09:25:00Z", "provider_session_ref": "provider-session-ref", "session_nonce": "" } ``` 响应示例: ```json { "request_id": "req-event-001", "data": { "conversation_id": "10000000-0000-0000-0000-000000000801", "status": "ACTIVE", "action": "continue", "duplicate": false, "accepted_at": "2026-09-05T09:20:03Z" } } ``` ### 3.10 上传聊天记录 地址: ```http POST /device/v3/agent-sessions/{conversation_id}/turns:batch ``` 输入: | 字段 | 类型 | 必填 | 中文含义 | |---|---|---:|---| | Path `conversation_id` | UUID | 是 | 平台会话编号 | | Header `Idempotency-Key` | string | 是 | 本批记录幂等键,16–128 字符 | | `turns` | array | 是 | 最终聊天记录数组,每批 1–500 条 | | `turns[].turn_key` | string | 是 | 会话内唯一的轮次标识,最长 255 字符 | | `turns[].sequence_no` | integer | 是 | 对话顺序号,会话内唯一且大于 0 | | `turns[].speaker` | enum | 是 | 用户为 `subject`,智能体为 `agent` | | `turns[].start_at` | datetime | 是 | 本轮开始时间 | | `turns[].end_at` | datetime | 是 | 本轮结束时间,不早于开始时间 | | `turns[].final_text` | string | 是 | 最终文本,最长 20000 字符 | | `turns[].interrupted` | boolean | 是 | 本轮是否被打断 | 输出: | 字段 | 类型 | 中文含义 | |---|---|---| | `conversation_id` | UUID | 平台会话编号 | | `accepted_count` | integer | 本次新写入的记录数 | | `duplicate_count` | integer | 本次幂等重复的记录数 | 示例: ```http POST /device/v3/agent-sessions/10000000-0000-0000-0000-000000000801/turns:batch Idempotency-Key: turns-conversation-00000801-0001 Content-Type: application/json <设备HMAC请求头> { "turns": [ { "turn_key": "user-final-001", "sequence_no": 1, "speaker": "subject", "start_at": "2026-09-05T09:20:10Z", "end_at": "2026-09-05T09:20:12Z", "final_text": "今天天气怎么样?", "interrupted": false }, { "turn_key": "agent-final-001", "sequence_no": 2, "speaker": "agent", "start_at": "2026-09-05T09:20:12Z", "end_at": "2026-09-05T09:20:16Z", "final_text": "我来帮你看看。", "interrupted": false } ] } ``` ```json { "request_id": "req-turns-001", "data": { "conversation_id": "10000000-0000-0000-0000-000000000801", "accepted_count": 2, "duplicate_count": 0 } } ```