实习日志 / 中文 / 控制面:这一轮对话,用的是哪一套模型、规则、知识和数据?
控制面:这一轮对话,用的是哪一套模型、规则、知识和数据?
中文 | English
9 月初,项目里能被"配置"的东西越来越多:用哪个模型、走哪个协议适配器、知识库是哪个版本、Agent 的规则和提示词是哪一版、数据跑在本地还是 MongoDB、第三方服务接了哪几家。每一样单独看都能改,可它们一起决定了 Agent 在一轮对话里的行为。我开始担心一种情况:运行时同时读到了新的规则、旧的模型和另一套知识索引,出了问题,谁也说不清那一轮到底用的是什么。
9 月 6 日,我写了一份控制面的完整建设方案。它要回答的就是标题里那个问题。
它管什么,不管什么
控制面负责管理系统配置、知识、模型、Agent 规则、外部集成、评估和运行状态。它必须保证六件事:
- 所有可以发布的内容都有版本;
- 修改先进入草稿,经过验证再发布;
- 运行时只读取已发布的版本;
- 发布、回滚和失败的操作都有完整的审计;
- 管理员不能通过控制面直接修改生产环境里的案件状态;
- 不同配置之间不会出现版本组合失配。
最后两条划了边界。案件、工作项、人工交接、客户消息和理赔事实,由理赔运营那一侧管理。控制面管的是"系统怎样运行",不碰"某一单发生了什么",它的接口里不提供任何直接写案件状态、工作项、交接或报案人消息的路径。
一组对象
方案里定义了十个核心对象:
| 对象 | 作用 |
|---|---|
| Configuration Item | 一个配置域里的逻辑对象,比如某个模型配置或某套 Agent 规则 |
| Configuration Revision | 配置的不可变版本,带状态、作者、影响等级和验证结果 |
| Knowledge Source / Version | 知识来源,以及它的解析、索引和发布版本 |
| Validation Run | 针对某个版本或某个发布组合的验证记录 |
| Approval Record | 高影响变更的独立复核人、复核范围、决定和时间 |
| Release Set | 一次运行时发布所引用的完整配置版本组合 |
| Runtime Snapshot | 运行时实际加载的已发布配置快照 |
| Audit Event | 配置、权限、发布、回滚和失败操作的追加式记录 |
| Secret Reference | 外部密钥的受保护引用,不保存明文 |
| Operation Record | 导入、索引、评估、连接检查这类异步任务的状态 |
其中最关键的是 Release Set。单个配置可以各自编辑,但涉及多个域的变更不能分别激活。最终的发布要用一个 Release Set 固定一组相互兼容的版本,比如:
Model revision: 7
Data profile revision: 3
Knowledge revision: 18
Agent rules revision: 12
Integration revision: 5
Feature revision: 9
运行时只加载完整的 Release Set,并记下其中每个域的版本。这样就分成了两层:单域的版本(比如 Agent 规则第 12 版),和把它们组合在一起的一次完整发布。新规则配旧模型、再配另一套知识索引这种情况,从结构上就不会发生了。
配置域一共分了十一个:系统、模型、数据配置、知识、Agent 规则、外部集成、访问权限、评估、功能开关、运行状态和审计。数据配置一栏里列着 fixture、本地、Cloudflare、MongoDB、AWS 几种运行时,它们之间的兼容性也要由控制面检查。
生命周期和审批
每个版本的生命周期是:
draft -> validation -> published -> superseded
再加几条补充路径:验证不通过退回草稿;草稿可以撤回;回滚是一次新的发布操作;任何被接受或被拒绝的操作都产生一条审计。
规则也很直接:已发布的版本不能直接编辑,要改就新建一份草稿;验证失败不能改变当前已发布的版本;没有完整验证证据的版本不能发布;回滚创建一条新的发布记录,不覆盖历史;失败的操作也要写进审计。
审批这一块我有意做得很轻。普通影响的变更,验证通过就可以发布;高影响的变更,发布前必须存在一条由非作者产生的独立复核记录,但不建审批队列、不建审批工作流,发布接口只检查这条记录在不在。一个五人团队的原型项目,需要的是"高影响变更一定有第二个人看过"这个事实被记下来,不需要一整套流程。
第三方服务:能力和任务要分开
第三方服务在系统里有两种完全不同的存在方式,方案里把它们拆到了两边。
控制面这边是 Integration Registry,管的是"这个服务能做什么、怎么连":服务身份、类型和环境;支持的能力和能力版本;适配器类型、端点引用和区域;允许发送哪些数据字段、隐私级别;需要什么同意、需要 Northwind 哪种授权;密钥引用;超时、重试、幂等和结果未知时的策略;连接检查、健康状态和最近的失败原因;能不能进入 Release Set;以及它自己的版本、发布状态和撤回历史。
理赔运营那边是外部任务和外部请求,记的是"这一单具体请求了什么":请求内容、同意、发送状态、服务商的引用号、结果验证和安全重试。控制面不改写这些案件级的记录。
账号管理不另造一套认证
控制面也提供客户和员工账号的管理入口:查询、创建、停用、恢复、修改允许修改的资料,管理员工的角色和作用域,重置或撤销会话,查看账号变更的审计。但它不复制认证系统:继续用仓库现有的 token 库、不透明的 bearer token、服务端存的 token 哈希、角色和作用域校验;账号操作都通过现有的后端 repository 和数据库边界完成;密码、token 这类凭据只由现有的身份服务处理,控制面的接口从不返回它们的明文,也不接受浏览器直接提交的身份字段作为授权依据。
所有写操作照例要过认证、作用域、If-Match、Idempotency-Key,返回稳定的错误码,不允许部分提交,审计只追加。
运行时怎么接
运行时接入控制面只做几件事:取当前已发布的 Release Set;完整加载;校验模型、知识、Agent 规则和数据配置之间是否兼容;在每一轮对话、每一次模型调用和检索里记下版本来源。同时有三条禁止:禁止把草稿、未验证或已被取代的版本当作隐式的退路;服务商失败时,只能切换到满足同样边界的备用配置;禁止运行时自己发布、修改或降级配置。
做到这些,最终每一张理赔单、每一轮 Agent 对话、每一条审计记录,都能回答同一个问题:
这次行为使用了哪一套模型、规则、知识和数据配置?
管理端只显示真的东西
管理端前端只调用管理接口,不能直连数据库、对象存储、模型端点、向量索引或密钥管理。它沿用报案端和员工端共用的 token 和状态语义,信息密度可以更高;页面布局由真实的管理任务和后端投影决定,不能先复制一个旧原型再往里填数据;组件只展示接口返回的状态和动作,不在浏览器里推导发布、权限、连接或第三方的状态。发布和回滚按钮,只在后端返回了对应的允许动作时才出现。
方案里有一条我写得特别重:还没接通的服务商,必须显示 unconfigured、unsupported 或者真实的失败状态,不能显示伪造的"已连接""已发布"或"运行正常";导入、索引、评估、连接检查这类异步操作必须有真实的 Operation Record,不能用一个静态的进度条代替。Release Set、Runtime Snapshot、Integration Registry 这些对象,只有在后端模型、存储边界、接口和验证证据都齐了以后,才能标为已交付。
所以这份方案开头先声明了自己的身份:它是目标规格,不是现状说明,也不代表文中列出的模块已经全部实现。实现和验收时要按"设计要求、接口、持久化、前端展示、真实验证"逐项核对,没实现的列进缺口清单。
最终的链路
管理员创建草稿
-> 系统验证配置和依赖
-> 生成验证证据
-> 高影响变更记录一次非作者复核
-> 创建完整的 Release Set
-> 原子发布
-> 运行时加载并记录版本
-> 运行结果可追溯
-> 发现问题时创建一次回滚发布
-> 所有过程进入审计
这份方案里新增的三个关键设计,是 Release Set、跨域发布的一致性和 Runtime Snapshot。没有它们,控制面只是一组管理页面;有了它们,系统才真正可发布、可回滚、可追溯。
后来
"禁止把旧版本当作隐式退路"这一条,后来在 10 月初的一次冗余代码审计里被翻了出来。审计发现,系统在没有已发布 Release Set 的时候,会悄悄从环境变量拼出一份旧式的模型配置;运行时缺了某个配置,会默默退回到旧版的提示词;管理端里创建发布配置的表单,"运行环境"一栏打开时就预选了 fixture,也就是一个只在内存里、重启就丢的测试环境。这些都和这份方案写下的原则相反,它们是在各种"先跑起来再说"的时刻一点点长出来的。
那次清理以后,缺少必需配置的运行时直接拒绝启动,并说清楚缺的是哪一项;管理端的下拉框默认取服务器当前真正在跑的配置。原则写在方案里并不能保证它被守住,要靠一次次核对,把偏离的地方找出来。