chore: checkpoint 第一版
ci / verify (push) Canceled after 0s

This commit is contained in:
Codex Local
2026-09-06 17:04:11 +08:00
parent a8cdec98d1
commit 0c2151ace9
659 changed files with 185855 additions and 554 deletions
+55
View File
@@ -1,5 +1,21 @@
# 设备接入与启动配置联调说明
## 设备激活状态机
平台从V50开始独立维护 `activation_state`,不再用通用的 `device.status` 代替激活生命周期:
`FACTORY → CLAIMABLE → BOUND → PROVISIONING → ACTIVE → LOST/REVOKED`
- `FACTORY`:只有设备身份和平台HMAC凭据,生产质检完成后由运维放行。
- `CLAIMABLE`:可以生成一次性绑定码;设备可同步基础配置,但不能发起对话。
- `BOUND`:已经绑定家庭和长者,可以调用火山设备凭据预配接口。
- `PROVISIONING`:平台正在代设备调用火山 DynamicRegister 并保存一机一密;动态注册成功、有效绑定和设备凭据就绪后进入 `ACTIVE`。火山 License 是产品侧动态注册额度,不是设备安装包,也没有设备回执。
- `ACTIVE`:允许创建和上报对话。硬件智能体会话接口只接受该状态。
- `LOST`:设备身份认证、对话和火山运行凭据立即停用;核验归属和硬件身份后可恢复到 `ACTIVE`。
- `REVOKED`:终态。设备平台凭据、火山凭据、绑定和未使用绑定码全部吊销,不能恢复。
每次迁移都会增加 `activation_revision` 并写入 `device_activation_transition`。运维人工迁移接口为 `POST /api/v1/ops/devices/{id}/activation-transitions`,请求必须携带当前 `expected_revision`;用户认领、设备预配和删除由对应业务接口自动迁移,不应由客户端伪造状态。
## 1. 当前接口
| 方法 | 路径 | 作用 |
@@ -270,6 +286,41 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-sim-firmware
脚本会验证创建、首次激活、相同幂等键重放、Worker调用、两次签名回调去重和最终`ACTIVE`状态,并断言供应商仅启动一次。脚本只使用合成数据,验证结束后按本次通话编号精确清理数据库痕迹。
## 11.5 火山硬件对话智能体 v3 控制面
设备首次准备火山连接凭据时,先使用既有平台设备签名调用`POST /device/v3/hardware-agent-credentials:provision`并提交稳定`hardware_id`。平台从产品级配置解析Instance ID、Product Key、默认Bot、传输方式和最低SDK版本,自动建立当前设备的运行绑定;再从KMS或只读Secret文件加载Product Secret,以平台生成的Device Name代设备调用火山DynamicRegister。Product Secret永不到达设备。平台把返回的`device_secret`用独立的`VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY`执行AES-256-GCM加密后保存,只在设备认证且`Cache-Control: no-store`的响应中返回给同一设备。设备使用安全芯片、系统KeyStore或Windows Safe Storage保存这份一机一密,后续用于火山WebSocket签名。
火山硬件对话智能体 License 是产品侧可用额度。平台调用 DynamicRegister 时由火山自动消耗一份有效额度并返回对应设备的一机一密;设备不会收到 License 明文,也不需要提交安装回执。当前有效路由不包含`POST /device/v3/hardware-agent-license:ack`,预配响应也不包含`license`字段。平台可以维护额度监控或影子账本,但不得把自建池作为设备运行凭据来源。额度耗尽或到期时,应根据火山动态注册错误告警并扩容,扩容后重试原预配请求。
同一设备和Hardware ID重复调用时,平台解密返回既有一机一密,不重复注册;Hardware ID、产品或实例不一致时返回409,必须走受控轮换而不是覆盖。Product Secret、Device Secret明文不得进入日志、审计详情、错误响应、通话记录或测试报告。自动回归的mock密钥仅允许在隔离测试进程中注入,不得写入正式设备缓存。
设备期望配置中的`voice_runtime`为`volc_hardware_agent`时,不再调用`POST /device/v2/conversations`创建RTC房间。首次预配后设备要重新拉取配置版本,并读取平台发布的`minimum_sdk_version`执行本地预检;云端会以产品默认绑定中的同一版本再次门禁,过低时返回HTTP 426和`HARDWARE_AGENT_SDK_UPDATE_REQUIRED`。每次用户开始聊天必须先调用`POST /device/v3/agent-sessions`,不得复用上一次响应。创建请求必须声明SDK版本、输入/输出音频格式、传输模式、可靠字幕队列、动态会话配置、凭证轮换以及近/远端PCM录制能力。平台会在同一事务中冻结这些能力、有效授权、已发布人设、档案、最近三次已完成通话摘要和最多五条人工确认的长期记忆,并返回:
- `runtime.bot_id`、传输模式和仅本次有效的`session_nonce`;
- `session_config.dialog_id`,必须作为火山会话的业务对话标识;
- `system_prompt`、`persona_tone`和`authorized_context`,必须在开始发送音频前通过SDK动态配置或标准WebSocket的`session.update`注入;
- `config_digest`,用于设备日志和平台诊断比对,不得当作供应商凭据;
- 可选的`tool_url`和`tool_scope_token`,只允许配置为本次火山会话的只读记忆工具;令牌绑定本次通话和供应商会话引用,设备不得持久化或转给其他会话;
- 本次冻结的隐私策略和会话存活间隔。
设备收到火山`session.created`后必须先应用本次`session.update`,收到确认后才向`POST /device/v3/agent-sessions/{id}/events`提交`session_started`。开始事件必须同时提交火山实际会话关联号、`bot_id`、传输模式、平台原样返回的`config_digest`和`dynamic_config_applied=true`;任何一项不一致都不会进入`ACTIVE`,此时不得打开麦克风。通话期间按响应中的心跳间隔提交`session_updated`;响应`action=continue`时保持会话,收到`action=stop_session`时必须立即关闭火山连接,然后提交`session_ended`或`session_failed`。这样,家庭撤回授权、运营停话和设备吊销即使发生在设备直连火山期间,也会收敛到设备执行。
平台不是靠轮询火山控制台才知道通话状态:v3设备事件在同一事务内更新权威通话状态,既有数据库触发器随`ACTIVE/COMPLETED/FAILED`写入最小`web_event`,已登录SaaS页面通过认证SSE收到`conversation.started/completed/failed`并刷新列表。`session_updated`只续租并更新硬件事件水位,不向家庭页面逐心跳广播,避免每10秒放大为整页查询;运维诊断仍可读取最后事件时间和序号。
录音能力采用保守门禁:只有创建请求同时声明`near_pcm_recording=true`和`far_pcm_recording=true`时,平台才可能依据既有授权返回`transcript_and_audio`。缺任一能力都会把本次会话强制降级为`transcript_only`(没有字幕授权时为`none`),并把`record_near`、`record_far`、`upload_allowed`全部置为`false`,设备不得以混音或供应商未知格式冒充双轨录音。
最终字幕写入本地加密重试队列,使用`POST /device/v3/agent-sessions/{id}/turns:batch`上传。只上传用户最终识别事件和智能体`response.audio_transcript.done`,不得长期保存或上传增量字幕;同一`turn_key`或`sequence_no`重放必须保持正文完全一致。高质量传输可额外配置火山服务端状态/字幕回调作为旁路证据;低负载传输按火山当前约束没有该服务端回调,因此设备事件与终稿上传是强制能力,创建请求必须声明`transcript_upload=true`。
Product Secret只保存在平台KMS/Secret挂载中;平台注册结果中的`device_secret`在平台数据库加密保存,并只下发给对应设备的安全区或供应商SDK凭据存储。普通会话响应和日志不得包含该值。当前Windows标准WebSocket测试程序覆盖平台代理注册和真实低负载协议;高质量方案的火山硬件SDK二进制和板级音频HAL仍必须由目标芯片工程集成后执行真机验收。
本地控制面闭环可执行:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-hardware-agent.ps1
```
脚本先证明过低SDK被HTTP 426拒绝、未完成动态配置不能激活,再验证产品默认Bot绑定、每次会话上下文预取、录音能力安全降级、开始事件、最终字幕、平台停话、`stop_session`下发、设备结束确认、能力快照和数据库状态收敛,并精确清理临时会话及绑定。
## 12. 错误处理
| HTTP状态 | 错误码 | 处理建议 |
@@ -279,6 +330,8 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-sim-firmware
| 503 | `DEVICE_AUTHENTICATION_UNAVAILABLE` | 退避后使用新随机数重试,不关闭鉴权 |
| 503 | `DEVICE_CONFIG_UNAVAILABLE` / `DEVICE_BOOTSTRAP_FAILED` | 退避重试并保留上一次有效配置 |
| 503 | `RUNTIME_CONFIG_UNAVAILABLE` | 生产环境没有命中活动语音路由;停止创建通话,由运营发布或恢复目标范围配置 |
| 426 | `HARDWARE_AGENT_SDK_UPDATE_REQUIRED` | 当前火山硬件智能体SDK低于设备绑定门禁;停止创建会话并升级固件/SDK |
| 503 | `HARDWARE_AGENT_PROVISIONING_FAILED` | 火山动态注册失败;用原幂等键、原正文和新签名退避重试,并由运维根据`request_id`检查产品License额度、有效期和火山原始错误 |
| 403 | `CONSENT_REQUIRED` | 不重试,等待有效授权 |
| 403 | `TRANSCRIPT_STORAGE_NOT_ALLOWED` | 停止补传,等待留存策略或授权恢复,不得本地绕过 |
| 409 | `DEVICE_NOT_BOUND` / `CONVERSATION_ALREADY_ACTIVE` | 修复绑定或查询已有通话 |
@@ -288,6 +341,8 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-sim-firmware
| 409 | `CONFIG_VERSION_CONFLICT` | 先重新获取设备配置再尝试创建新通话 |
| 409 | `PROVIDER_EVENT_CONFLICT` | 供应商事件编号复用且正文不同,转人工排查 |
| 409 | `CONVERSATION_STATE_CONFLICT` | 查询云端当前通话状态,不要强制覆盖状态机 |
| 401 | `HARDWARE_SESSION_PROOF_INVALID` | 丢弃本地会话,不得尝试其他设备或其他会话的nonce;重新创建会话 |
| 409 | `HARDWARE_AGENT_EVENT_CONFLICT` | 保留原事件和单调序号,查询当前会话状态并人工核对,不得覆盖 |
| 409 | `TRANSCRIPT_TURN_CONFLICT` | 保留本地终稿和冲突明细,禁止覆盖云端终稿,转入人工对账 |
| 409 | `TELEMETRY_EVENT_CONFLICT` | 保留冲突事件并停止推进本地确认点,人工核对事件键和单调序号 |
| 404 | `TELEMETRY_CONVERSATION_NOT_FOUND` | 删除错误通话引用,不得改用任意长者编号重试 |