Files

184 lines
20 KiB
Markdown
Raw Permalink Normal View History

2026-08-26 06:41:07 +08:00
# 内部运营配置、发布门禁与灰度控制开发说明
## 1. 实现范围
V19—V20及V34已经形成内部运营配置的本地纵向闭环:运营身份、五类运行配置、不可变草稿、结构校验、离线评测证据、受控资源引用、双人审批、按范围灰度、确定性分桶、并发影响上限、投放台账、带版本校验的暂停恢复、历史差异、不可变回滚和新通话配置冻结。
本批次覆盖的配置类型为`PERSONA`、`VOICE`、`MODEL`、`ROUTING`和`AGENT_BUNDLE`。灰度范围支持整个客户组织、单个试验对象和设备组;运行时命中优先级固定为“试验对象 > 设备组 > 客户组织”。
以下能力仍不冒充已完成:真实KMS取密、供应商在线资源探测、真实模型兼容性调用和成本预测尚未接入外部服务;当前资源引用的“已核验”状态由受控运营流程登记,并由类型、状态及180天有效期门禁复核。费用、额度和告警处置属于后续批次。
## 2. 运营角色与租户边界
2026-08-29 21:56:51 +08:00
维护人员使用独立OIDC账号登录`/maintenance`,不得复用家庭用户身份。账号表以`account_kind`建立强制边界:`customer`只能使用`platform_role=customer`并通过家庭成员关系取得业务权限;`operator`不得具有家庭成员关系,只能使用下表的维护角色。研发环境提供`operator`和`operator-approver`两个合成账号,生产环境必须由独立员工身份源、MFA和入离职流程维护。
2026-08-26 06:41:07 +08:00
| 角色 | 权限 |
|---|---|
| `customer` | 无内部运营接口权限 |
| `ops_viewer` | 查看配置、评测证据、资源引用、设备组和投放台账 |
| `ops_operator` | 查看、创建草稿、提交审批、登记评测/资源/设备组、撤销资源引用 |
| `ops_approver` | 具备操作员能力,并可审批发布、回滚、暂停和恢复投放 |
| `ops_admin` | 平台管理员能力;仍受创建人与审批人分离约束 |
2026-08-29 21:56:51 +08:00
运营角色保存在`user_account.platform_role`,账号类别保存在`user_account.account_kind`,不会信任OIDC令牌中的自报角色。每次运营请求用OIDC签名身份与`X-Tenant-Id`共同解析当前客户组织的活动operator账号;家庭角色不会隐式获得运营权限,跨客户组织的试验对象和设备编号均由服务端拒绝。生产环境仍需由身份系统强制MFA和受控授权流程。
### 2.1 运维设备管理
`/maintenance?area=devices`提供租户内设备资产清单,展示设备公开编号、型号、状态、密钥版本、最后上报时间及当前家庭/长者归属。`ops_viewer`和`ops_approver`只读;`ops_operator`与`ops_admin`可登记、修改、调整归属、轮换密钥和删除设备。
- 登记时服务端生成UUID设备编号、不可逆随机序列摘要和32字节随机HMAC密钥;设备、影子、可选绑定、加密凭据和审计记录在同一事务提交。
- 密钥明文只在创建或轮换响应出现一次,网页立即下载为本地凭据文件;服务端数据库只保留AES-256-GCM密文,不提供历史密钥下载接口。
- 编辑使用强ETag防止覆盖并发修改;家庭和长者必须来自同一客户组织且保持有效。
- 删除采用软删除,设备进入`revoked`、当前密钥立即吊销、当前绑定关闭;历史通话、绑定历史和审计记录不物理删除。
2026-08-26 06:41:07 +08:00
## 3. 配置与投放状态
```text
DRAFT ──结构校验/提交──> PENDING_APPROVAL ──评测与资源门禁+另一审批人──> PUBLISHED
│
激活一个或多个灰度投放范围
↓
ACTIVE ──暂停──> PAUSED
↑ │
└────恢复─────┘
│
同范围新版本替代ACTIVE或PAUSED
↓
ENDED
│
无任何活动投放仍引用该配置版本 ───────────┘
↓
RETIRED
```
- `tenant_id + config_key`内的`version_no`严格递增,事务级咨询锁防止并发分配重复版本。
- 草稿和历史版本不可覆盖;提交、发布、资源撤销及投放暂停恢复均使用强`If-Match`并发控制。
- 配置创建人不得审批自己的变更,违反时返回`SELF_APPROVAL_FORBIDDEN`。
- 同一配置键可以同时存在多个已发布版本,但每个具体投放目标只能有一个活动版本。
- 租户、每个试验对象、每个设备组分别由数据库部分唯一索引保护活动投放唯一性。
- 替换某一目标时只结束该目标的旧投放;旧版本仍被其他活动目标使用时保持`PUBLISHED`。
- 暂停只阻止后续新通话命中,不改变已经建立通话的冻结配置;恢复要求配置仍为`PUBLISHED`且同一键和范围没有其他活动投放。
- 新版本会同时结束同范围的`ACTIVE`和`PAUSED`旧投放;被替代的暂停记录不能恢复,避免旧灰度意外复活。
- 当旧版本不再被任何活动投放使用时才转为`RETIRED`。
- 回滚复制历史正文创建新的已发布版本,不复活或修改历史行;`rollback_of_id`记录内容来源,`supersedes_id`记录被该目标替换的版本。
- 创建草稿、评测、设备组、资源引用和回滚要求`Idempotency-Key`,正文摘要冲突返回HTTP 409。
## 4. 发布门禁
### 4.1 结构与敏感信息
配置正文最大64KiB,拒绝任意层级包含`secret`、`password`、`token`、`private_key`、`api_key`或`credential`含义的键。供应商凭据只通过`secret_refs`保存引用,接口、数据库、页面和日志均不保存密钥明文。
`runtime-config-v1`本地结构检查如下:
- 人设要求`system_prompt`和`tone`。
- 音色要求`provider`、`voice_resource_ref`及`credential_ref`。
- 模型要求`provider`、`model_id`及`credential_ref`。
- 路由要求`default_engine_mode`属于`s2s_pure`、`s2s_hybrid`或`cascade`,且存在`routes`。
- Agent组合要求人设、音色、模型和路由四个版本引用。
Agent组合的四个字段不是任意字符串:`persona_version_id`、`voice_version_id`、`model_version_id`和`routing_version_id`必须是UUID,并分别指向当前客户组织内对应`PERSONA`、`VOICE`、`MODEL`和`ROUTING`类型的不可变配置版本。组件必须已经发布过;当前为`PUBLISHED`或因后续版本替换而成为`RETIRED`都可复用,`DRAFT`、`PENDING_APPROVAL`、跨客户、错误类型和不存在的编号均会被拒绝。提交审批时把四个组件的编号、类型和状态写入结构校验结果,发布及回滚事务再次复核,避免审批期间引用失效。
### 4.2 离线评测
每个待发布版本至少需要一条`PASSED`评测记录。评测通过`evidence_type`区分通用证据`GENERIC`和标准Agent质量证据`AGENT_QUALITY_V1`,并保存套件版本、结果、指标快照、报告引用、操作者和时间。除Agent组合外的配置继续接受通用证据;`AGENT_BUNDLE`发布必须存在一条`PASSED`的`AGENT_QUALITY_V1`证据,手工登记的通用`PASSED`不能放行。
标准Agent质量证据只能登记到`AGENT_BUNDLE`。服务端校验报告结构版本、评测集SHA-256、Agent和模型配置引用、执行环境与时间、十个以上用例、通过/失败计数以及全部强制质量指标,并校验结论与失败计数一致。登记器注入的`config_version_id`必须等于目标组合编号,`model_config_ref`必须等于`runtime-config-version:<model_version_id>`,另一组合或另一模型的报告不能复用。页面不向Agent组合提供手工通用评测按钮;标准证据应由`cmd/agent-eval-register`在重新计算报告并完成防篡改检查后登记。失败记录不会被删除,也不能单独放行发布。所有评测都只允许在`DRAFT`或`PENDING_APPROVAL`状态登记,防止发布后补写证据。
非Agent组合的通用评测从具体配置版本卡片打开页面内登记表单,不再连续调用浏览器输入和确认弹窗。表单固定配置公开编号、类型、版本族、版本号及行版本;套件版本必填且最长100字符,报告引用必填且最长300字符,`PASSED/FAILED`均必须由操作者明确选择,默认不预选结论。选择`PASSED`后还必须确认报告内容和指标支持通过结论,并理解该记录可能参与发布门禁;选择`FAILED`不会沿用该确认。取消、切换客户组织或配置列表刷新会清除草稿,不创建评测。页面仍不允许为`AGENT_BUNDLE`手工登记通用证据,服务端继续校验配置状态、租户、幂等和证据类型。
### 4.3 资源引用
门禁按用途匹配资源类型:
| 配置字段 | 必需资源类型 |
|---|---|
| `secret_refs.*` | `PROVIDER_CREDENTIAL` |
| `VOICE.voice_resource_ref` | `VOICE_RESOURCE` |
| `MODEL.model_id` | `MODEL_RESOURCE` |
引用必须属于当前客户组织、状态为`ACTIVE`,且`verified_at`在180天内。仅名称相同但类型错误、已撤销或过期的引用均不能放行。资源撤销使用`If-Match`并形成事务审计;撤销后后续发布会被阻断。真实KMS读取、供应商在线校验和自动轮换仍需外部系统接入。
资源撤销从资源台账打开页面内受控操作区,不再使用浏览器输入弹窗。确认区固定资源公开编号、显示名称、类型、引用标识、当前状态和行版本;原因必填且最长500字符,并要求操作者确认后续发布门禁影响、替代资源已经核对,以及本操作不会删除外部密钥或改写历史会话。取消、客户组织切换或列表刷新会清除草稿,不发送撤销请求。
## 5. 灰度选择与影响上限
发布或回滚请求必须提供:`scope_type`、可选`scope_id`、`rollout_percent`和`max_concurrent_calls`。
- `TENANT`不允许填写`scope_id`。
- `SUBJECT`要求有效试验对象公开编号且比例固定为100%。
- `DEVICE_GROUP`要求当前客户组织的活动设备组,可设置1%—100%。
- 设备组百分比使用设备公开编号、投放固定种子和PostgreSQL稳定哈希进行确定性分桶;同一投放下设备不会在请求间随机跳组。
- 运行时先尝试试验对象范围,再尝试设备组,最后尝试租户范围。
- 当前冻结该配置版本的未结束通话数达到`max_concurrent_calls`时,该投放不再接纳新通话,继续尝试较低优先级范围或本地开发回退配置。
## 6. 接口
| 接口 | 作用 |
|---|---|
| `GET /api/v1/ops/config-versions` | 查询配置版本历史 |
| `POST /api/v1/ops/config-versions` | 幂等创建不可变草稿 |
| `PATCH /api/v1/ops/config-versions/{id}/submit` | 结构校验并提交审批 |
| `GET /api/v1/ops/config-versions/{id}/evaluations` | 查询离线评测证据 |
| `POST /api/v1/ops/config-versions/{id}/evaluations` | 幂等登记评测证据 |
| `PATCH /api/v1/ops/config-versions/{id}/publish` | 双人审批并激活指定灰度范围 |
| `POST /api/v1/ops/config-versions/{id}/rollback` | 幂等复制历史内容并投放新的回滚版本 |
| `GET /api/v1/ops/config-rollouts` | 查询活动及历史投放台账 |
| `PATCH /api/v1/ops/config-rollouts/{id}/pause` | 使用`If-Match`暂停活动投放,仅影响后续新通话 |
| `PATCH /api/v1/ops/config-rollouts/{id}/resume` | 使用`If-Match`恢复未被替代的暂停投放 |
| `GET/POST /api/v1/ops/resource-references` | 查询或登记受控资源引用 |
| `PATCH /api/v1/ops/resource-references/{id}/revoke` | 使用`If-Match`撤销资源引用 |
| `GET/POST /api/v1/ops/device-groups` | 查询或创建静态设备灰度组 |
所有接口要求OIDC和`X-Tenant-Id`;所有写入形成事务审计,不记录密钥明文。
## 7. 新通话配置冻结
设备创建新通话时,在同一数据库事务内读取设备活动绑定,并执行目标匹配、稳定分桶及并发上限判断。平台优先选择活动投放所绑定的`AGENT_BUNDLE`版本,`config_key`只表示运营定义的版本族,不是运行时保留开关;页面建议的`default-agent-bundle`可以直接使用,也可以按业务命名多个灰度版本族。优先级固定为“试验对象 > 设备组 > 客户组织”,同一范围存在多个可命中组合时选择最近激活的一项。命中后读取组合引用的`ROUTING.default_engine_mode`,并把`cfg:{Agent组合公开编号}`写入`context_snapshot.prompt_bundle_version`。组合所引用的路由版本允许处于`PUBLISHED`或`RETIRED`,因为已发布版本不可变且退役只阻止新的直接发布选择,不破坏已审核组合和回滚的可追溯性。后续发布、暂停、撤销或回滚只改变新通话选择,已创建通话始终使用自己的冻结组合。
Worker启动Agent时按冻结的组合公开编号重新读取同一客户组织内、类型严格匹配的`PERSONA`、`VOICE`和`MODEL`版本,把组合编号、四个组件编号、系统提示、表达风格、音色资源引用和模型资源引用放入统一供应商启动请求。火山适配器会把系统提示、表达风格和最小必要背景按顺序加入系统消息,并支持在正式模板中使用`{{MODEL_RESOURCE_REF}}`、`{{VOICE_RESOURCE_REF}}`、`{{AGENT_BUNDLE_VERSION}}`、`{{PERSONA_VERSION}}`、`{{VOICE_VERSION}}`、`{{MODEL_VERSION}}`和`{{ROUTING_VERSION}}`占位符。模型和音色字段的实际落点由供应商官方模板决定,平台不猜测未确认的专有字段。
为兼容已有数据,未命中Agent组合时继续选择活动投放所绑定的`ROUTING`版本,并冻结该路由公开编号;页面建议键`default-agent-routing`不是强制值。若同一范围存在多个直接路由,也按最近激活时间选择。本地开发环境在两类投放都不可用时回退到`minimal-v2 + s2s_hybrid`;生产环境没有任何可命中的Agent组合或旧路由时拒绝创建并返回`RUNTIME_CONFIG_UNAVAILABLE`,避免静默切换。冻结的三种模式由`voice-chat-template-bundle-v1`选择同名供应商请求模板;真实模板字段、资源兼容性和语音效果仍需供应商账号联调。
## 8. 管理端
非`customer`账号可见“配置发布与版本控制”工作台,支持:
- 切换有运营权限的客户组织。
- 登记并撤销受控资源引用、创建设备灰度组。
- 查看活动/暂停投放的范围、比例、并发上限和行版本,并按权限暂停或恢复。
- 创建五类配置草稿并查看相邻版本差异;创建Agent组合时直接从当前客户组织已经发布过的人设、音色、模型和路由版本中选择,不需要手工复制UUID或编辑组合JSON。
- 登记和查看离线评测证据。
- 配置租户、设备组或试验对象投放参数并审批发布/回滚。
- 根据角色隐藏写入控件,并显示空数据、失败、并发冲突与操作结果。
Agent组合选择器优先预选最近发布的组件版本,并明确标记仍可追溯复用的已退役版本。任一组件类型尚无已发布版本时,页面显示缺失类型并禁用创建按钮;切换客户组织会重置未提交配置和通用评测表单,避免把上一客户组织的组件编号或评测结论带入新草稿。页面校验只用于减少录入错误,提交和发布时服务端仍重新验证客户组织、组件类型以及`PUBLISHED/RETIRED`状态。
投放暂停和恢复同样使用页面内受控操作区,固定投放公开编号、配置键、范围、当前状态、比例、并发上限和行版本。暂停要求原因、观察依据及“只停止后续新通话命中”的影响确认;恢复要求问题消除证据、观察/回退方案,并确认配置仍可发布且同一范围不存在其他活动投放。三类受控操作在页面提交前只编辑本地草稿,实际请求继续由服务端执行角色、租户、状态和`If-Match`校验。
草稿提交审批、待审批版本发布和已退役历史内容回滚也使用具体版本卡片内的全宽受控流转区。确认区固定版本公开编号、类型、配置键、版本号、状态、行版本、创建人和变更说明;提交审批只要求确认目标与内容,不会直接发布。发布与回滚在当前受控区内单独填写客户组织、设备组或试验对象范围、灰度比例、并发影响上限和最长500字符的依据,选择其他版本时不会隐式沿用这些参数。范围或目标变化会重新要求影响确认;发布明确只影响后续新通话,回滚明确创建新的不可变版本而不复活历史行。取消、客户组织切换或列表刷新会清除草稿且不发送流转请求,服务端双人审批、评测、资源、租户、状态、幂等和并发门禁保持不变。
## 9. 验证
```powershell
pwsh -NoProfile -File .\scripts\smoke-ops-config.ps1
```
主烟测使用真实Keycloak账号和真实设备HMAC,覆盖普通账号403、敏感键拒绝、幂等、创建人自批拒绝、无评测发布阻断、评测证据查询、缺少模型/凭据资源阻断、正确类型资源放行、资源撤销、投放台账、租户与设备组并存、设备组优先、回滚版本链、新通话冻结和事务审计,并清理所有合成数据。日常低强度验证另以真实PostgreSQL的定向HTTP用例覆盖暂停、缺少`If-Match`、过期版本冲突、恢复和审计,不要求重复执行整套主烟测。
页面可用性增量采用一次真实OIDC登录的无提交冒烟,不重复整套回归:`test/e2e/visual-ops-config-evaluation.mjs`以临时资源、活动投放和暂停投放为目标,检查三类确认区的目标与行版本绑定、原因门禁、影响确认、取消关闭、零撤销/暂停/恢复PATCH、零横向溢出、零控制台错误和零非预期失败请求;`finally`按公开夹具范围精确清理。该检查证明页面不会误提交且操作前置约束可用,不代替服务端已有状态机烟测或目标环境真实资源验证。
配置版本流转另使用`test/e2e/visual-ops-config-version-transition.mjs`进行单条真实OIDC无提交验证:脚本创建`DRAFT`、`PENDING_APPROVAL`和`RETIRED`三条临时版本,检查提交、发布、回滚目标及行版本绑定,原因/范围/二次确认门禁,试验对象范围强制100%及修改范围后重新确认,取消关闭和数据库状态不变,同时检查零流转请求、零横向溢出、零控制台错误和零失败请求。截图仅用于确认全宽表单可读性,脚本结束后精确清理三条配置夹具。
实际Chrome页面检查:
```powershell
$env:PLAYWRIGHT_REQUIRE_FROM='C:\Users\19926\.cache\codex-runtimes\codex-primary-runtime\dependencies\node\node_modules'
$env:CHROME_PATH='C:\Program Files\Google\Chrome\Application\chrome.exe'
node .\scripts\visual-ops-config-ui.cjs
```
检查覆盖OIDC登录、运营工作台可见性、资源/设备组/投放/版本区布局、接口失败、控制台错误和横向溢出;截图保存到`test/e2e/screenshots/operations/ops-config-workspace.png`。
通用评测表单另使用`test/e2e/visual-ops-config-evaluation.mjs`进行单条轻量验证:脚本创建一条临时路由草稿,经真实OIDC深链进入配置工作台,验证目标/行版本绑定、空字段与未选结论阻断、通过结论二次确认、失败结论显式选择、取消关闭、零评测POST、零控制台错误和零横向溢出,最后精确清理配置及评测夹具。该检查不替代服务端评测登记、发布门禁和标准Agent报告验证。