docs(plans): W-CFG-EXT/KB 配置外置执行计划(Opus 单评「可执行·小修」→九条返修·待创始人拍)
Some checks failed
contract-gates / contract-gates (push) Has been cancelled
docs-gate / docs-gate (push) Has been cancelled

D11 质量参数(全硬编码 ReadinessScorer.java:33-50 带"待外置"注释)+ 知识件(Nacos 100KB 装不下、
需文件存储)收进阶段二配置集治理。Opus 单评判可执行·小修,九条逐条修回(主控亲自落盘,因返修
子代理工具通道故障、我的主通道经 grep 逐批亲验):
- ①K1 决策材料轴向硬伤(git-ref 其实可热换:read_file cheap_run.py:84 现读盘无缓存,git checkout
  知识件路径后下次生成即读到新内容、不重启)——真轴改为「带内 config-center 推送 vs 带外整树 git op」
  + 补码/进程 skew 运维告警(这反使 MVP 先路甲更成立);
- ④E3 降风险改述:激活编排一行不改,codec 对 readiness-only 产空载荷、现有 dispatch/reconcile 天然
  短路(码证 GenConfigActivationServiceImpl.dispatch:178-182),不碰双路激活主链与补偿不变量;
- ②readiness ⊻ routeA/routeB 互斥 sanity(防误配拼伪 dataId);③ReadinessConfigProvider 依赖的
  getActiveVersionBySetKey 服务方法当前不存在、一并补契约;⑤MinIO read_file 根改造成本;
  ⑥NO_ROUTE 判据扩 !hasReadiness 保既有断言;⑦provider 定死短 TTL;⑧readiness 表单是新增非纯复用;
  ⑨doc↔code 锚 BUDGET_RMB:50=0.6 + 清 :19/:151 陈旧注释。
待创始人拍:D11 建模(枚举 vs kind)+ K1 存储(git-ref vs MinIO,材料已按真轴摆正)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lili 2026-07-05 13:11:43 -07:00
parent e21ab160e2
commit 51482b758f

View File

@ -0,0 +1,276 @@
---
date: 2026-07-05
topic: 配置外置(W-CFG-EXT D11质量参数 / W-CFG-KB 知识件)执行计划
status: 二轮返修Opus 单评「可执行·小修」)—— K1 材料轴向修正git-ref 对 read_file 知识件可热换、真轴是带内 vs 带外)+ ②③契约补 + E3 改述codec 短路、非加激活分支)已修,待创始人拍 D11 建模 / K1 存储
sot-impact: 不新建 canonical topic。落地牵动 `架构/生成引擎/游戏质量与爆火能力`(D11 就绪评分四维权重 + efficiency 预算阈值从 game-cloud 代码常量抬为受治理配置集,是该档 §10「校准走配置、不在现场拍脑袋」的配置载体,不改任何命题)与 `架构/生成引擎/agentic运行时架构图说`(配置控制面消费侧新增第三条「game-cloud 进程内读激活版」消费路,补齐阶段二「账本单点·投影两处·消费两路」的第三消费口)。W-CFG-KB 若采 MinIO 存储路,牵动 `架构/契约总览`(知识包寻址契约点登记),与 W-ASSET-SRC 源工程存储正交、不合表。不碰便宜档 prompt/模型/参数(已受治理)与九门判据 SoT。
上级: docs/agent-specs/2026-07-02-配置控制面阶段二-yudao配置中心-设计.md
关联:
- game-cloud/game-module-aigc/game-module-aigc-server/src/main/java/com/wanxiang/huijing/game/module/aigc/service/task/ReadinessScorer.java(D11 打分器:四维权重 :33-39 / NEUTRAL :42 / BUDGET_RMB :50 / 自述外置的注释 :43-50 / stability 阈值 :144-147,全 private static final 硬编码)
- game-cloud/game-module-aigc/.../service/config/GenConfigContentCodec.java(content_json routeA/routeB 契约 :20-44 + 值域 sanity;D11 加第三块 readiness 的落点)
- game-cloud/game-module-aigc/.../service/config/GenConfigActivationService.java(双路激活 + 账本收口 :29-76;D11 走「纯账本收口、无外投影」——readiness-only 空载荷、现有 dispatch 天然短路,不新增编排分支)
- game-cloud/game-module-aigc/.../enums/AigcConfigTierEnum.java(档位枚举 CHEAP/TIER2 :21-26,tier 绑 Nacos 路B dataId)
- game-cloud/huijing-server/src/main/resources/db/migration/V29.0.0__create_aigc_config_governance.sql · V30.0.0__aigc_config_add_activating_pointer.sql(配置集治理表)
- cheap-worker/cheap_roles.py(知识件加载铁律 :4-5「绝不手抄 skill、指 agent 自己 read_file」+ 按品类指路 :47-55)· cheap-worker/cheap_run.py(read_file repo 根只读 + 200KB 截断 :6,:24,:78-81)
- .agents/skills/*.md(设计 skill:littlejs-game-dev 24KB / puzzle 20KB / narrative·heritage·trpg·sim-business 16-17KB)· game-runtime/games/_template-*/ · _fewshot-*/(黄金脚手架/首例,多文件源工程 56-124KB)· tier2/skills/phaser/(SKILL.md 12KB + rag/ 七件)
- tier2/gen-worker/worker/genconfig.py(进程内配置门面 get 三级回落 + Nacos 热源 :305-335)· worker/genconfig_nacos.py(按档 dataId 热源工厂 :90-140)
- docs/plans/2026-07-05-001-feat-asset-src-源工程存储-plan.md(姊妹计划:W-CFG-KB 若走 MinIO 复用其存储范式)
- docs/plans/2026-06-30-便宜档M3-生产cutover-plan.md(S3:D11 起步值落定 :209-216、efficiency BUDGET_RMB 错配 :211,真校准留质量轨)
- commit hash:待收口填
图清单: [图0 两件各自的外置形态与工单, 图1 D11 参数三层回落与激活路, 图2 知识件外置的机制岔路]
---
# 配置外置(W-CFG-EXT / W-CFG-KB)执行计划
配置控制面阶段一到四已经把生成行为里最热的一批旋钮——prompt、模型、参数、软预算、门阈值——收进了 yudao 的受治理配置集:改一版、激活、下一次生成即生效、可回滚、可归因。但还有两处配置散落在配置集之外。一处是 D11 就绪评分的四维权重和效率预算阈值,写死在 game-cloud 的打分器里,放量校准时想把「可玩性」调重一点得改代码、重编 jar、重发一整条后端。另一处是喂给生成 agent 的知识件——设计 skill、黄金脚手架、few-shot 首例、tier2 的 Phaser RAG——现在躺在 worker 的 repo 检出里,由 agent 运行时 `read_file` 按路径自取,想改一条设计范式只能改文件、重新部署。这份计划把这两处收进配置集治理,但它们的外置形态差得很远,分成两组独立工单,互不拖累。
## 0 一图看懂
D11 参数是十来个标量,天然装得进配置集的一行版本记录,且它的消费方(打分器)就在 game-cloud、和配置集账本同库同进程,外置几乎是把阶段二现成的治理机器接一根线。知识件是 markdown 加多文件源工程,读取走的是文件路径不是键值,塞不进 Nacos 的键值格子,得先在两条存储机制里拍一条路才能往下做。
```mermaid
flowchart TB
subgraph EXT["W-CFG-EXT · D11 质量参数(可直接做,低风险)"]
direction TB
E0["现状:ReadinessScorer.java<br/>四维权重 + BUDGET_RMB + stability 阈值<br/>= private static final 硬编码"]
E1["质量评分配置集<br/>(yudao 版本行 content_json.readiness)"]
E2["激活 = 纯账本收口<br/>(无路A PATCH、无路B Nacos)"]
E3["ReadinessScorer 进程内读激活版<br/>硬编码常量降为回落默认"]
E0 --> E1 --> E2 --> E3
end
subgraph KB["W-CFG-KB · 知识件(先拍机制,再做)"]
direction TB
K0["现状:.agents/skills/*.md + _template-*/_fewshot-*<br/>+ tier2/skills/phaser<br/>= agent read_file 从 repo 检出按路径读"]
KX{"机制岔路(待拍板)<br/>Nacos KV 不适用:<br/>100KB 上限 + 多文件源工程 + 读的是路径"}
KA["路甲:git-ref 指针<br/>带外整树 git op、生效靠部署机 git"]
KB2["路乙:MinIO 知识包 + manifest<br/>带内 config-center 推送、scoped"]
K0 --> KX
KX --> KA
KX --> KB2
end
EXT -.->|"两件正交,不共表、不互相阻塞"| KB
```
## 1 现状核实(带行号证据)
### 1.1 D11 参数现在住哪:硬编码在 game-cloud 打分器里,代码自己都写了「待外置」
`ReadinessScorer.java` 是 D11 就绪评分的打分器,读 worker 落库的 9d trace,按加权公式算 0-100 的就绪分挂到审核台。它的全部可调参数都是 `private static final double` 常量:四维权重 `W_PLAYABILITY=0.50``W_FIRST_PLAY=0.25``W_STABILITY=0.15``W_EFFICIENCY=0.10`(:33-39),缺数据时的中性子项分 `NEUTRAL=0.5`(:42),效率维的预算阈值 `BUDGET_RMB=0.6`(:50),还有稳定性维的两级修复轮阈值(`repairs<=2 → 0.7`,否则 `0.4`,:144-147)和效率维的线性衰减形状(超预算到两倍预算之间从 1.0 线性降到 0)。
这几个常量的外置不是我们凭空提出来的——写这个类的人在 `BUDGET_RMB` 头上留了一段注释(:43-50),原话是「当前为硬编码常量:改要动代码 + 换 jar;待随 D11 就绪评分参数(本阈值 + 四维权重)外置进配置中心(决策6『进配置集』涟漪),届时本常量降为热源缺失时的回落默认、真值由激活版本行下发」。换句话说,外置的方向、回落语义(常量降为默认、真值走激活版本行)在写代码时就想清楚了,只是当时配置集治理还没落地,先留了个待接的口。这段注释就是 W-CFG-EXT 的施工图。
D11 的质量 SoT 也把这件事挂在案上。`游戏质量与爆火能力.md` §9(:140)写明「D11 就绪分权重自本档起归质量轨管辖:放量前起步值由生产 cutover plan 落定,其部署步已在修 efficiency 维 BUDGET_RMB 与便宜档实际成本的错配,该项列为质量轨接管后的首个复核件」;§10(:148)进一步定「此后一切档位线、rubric 维度、D11 权重的修订都走同一条路:质量轨提案、回流环校准单机制——不在现场拍脑袋改阈值」。这句「不在现场拍脑袋改阈值」正好要求一个配置载体:改动得可版本、可归因、可回滚,而不是散在代码里靠改 jar。W-CFG-EXT 就是把这个载体接上。
一处必须记牢的定性:**D11 就绪分只观测、不拦发布**(SoT §5:32「L2L4 任何分数都不拦发布」,§9:140「D11 只观测不拦」)。这条把外置的风险压到近乎零——即便外置后某次激活把权重配错了,受影响的只是审核台就绪分的排序展示,不会误拦或误放任何一款游戏,回退也只是重放上一版。
### 1.2 知识件现在住哪、怎么被生成线加载:agent 按路径 read_file,读的是 worker 的 repo 检出
便宜档降 AI 参与那条线立过一条铁律,写在 `cheap_roles.py` 开头(:4-5):「绝不手抄 skill——只给薄指令 + 指 agent 自己 read_file 读真 skill / 插件 api.d.ts / 起点范例;L3 context 由 agent 经 read_file 按需自取,不预塞」。所以知识件不是塞进 prompt 的文本,而是一批文件,prompt 里只留路径,由生成 agent 在运行时用 `read_file` 工具按需读。按品类指路的清单也在这个文件里(:47-55):写码手册指向 `.agents/skills/littlejs-game-dev.md`,经营品类指向 `.agents/skills/sim-business-game-design.md`,叙事指向 `narrative-game-design.md`,TRPG、非遗、解谜各有一份 skill,还有 `game-runtime/games/_fewshot-feiyi/src/game-logic.js``_template-story/src/game-logic.js` 这些当过门正例读的源工程。
`read_file` 这个工具读的是哪儿?`cheap_run.py` 定死了根:`_REPO_ROOT = Path(__file__).resolve().parents[1]` 就是 repo 检出根(:24),`read_file` 做的是「repo 根只读 + 200KB 截断 + 路径不得逃出 repo 根」(:6,:78-81)。也就是说,知识件的「版本」现在等于 worker 那台机器上 git 检出的版本;想改一条设计范式,得改仓里的文件、把新检出部署到 worker 上才生效。配置集治理的缺口就在这:没有配置中心记录「当前激活的是哪个版本的知识件」,没有不重部署就能换的通路,也没有把每次知识件改动挂进审计、归因、回滚的地方。
知识件的形态有两类,尺寸和结构差得很远,这直接决定了外置机制:
| 类别 | 位置 | 形态 | 尺寸 |
|---|---|---|---|
| 设计 skill | `.agents/skills/*.md` | 单个 markdown | 最大 littlejs-game-dev 24KB,puzzle 20KB,其余 16-17KB |
| 黄金脚手架 / few-shot 首例 | `game-runtime/games/_template-*/``_fewshot-*/` | **多文件源工程**(src/ 下若干文件) | 每个目录 56-124KB |
| tier2 Phaser 知识 | `tier2/skills/phaser/` | SKILL.md(12KB)+ rag/ 七个 md(各 3-9KB) | 目录合计 52KB |
设计 skill 是单文件 markdown,单个都在 24KB 以内,理论上塞得进 Nacos 单配置的 100KB 格子;但黄金脚手架和 few-shot 是多文件源工程,一个目录几十上百 KB、结构是一棵文件树,既装不进一个键值,形态上又和 W-ASSET-SRC 要存的源工程是一路货。这一层的判断见 §1.3 末尾。
### 1.3 配置控制面现有外置机制:阶段二治理机器已落地,但为路A/路B 设计,D11 与知识件都不完全对得上
阶段二把配置集治理的机器造好并落了地,代码就在 `game-module-aigc` 里:档位枚举、状态机、版本行、激活编排、漂移对账全在(`AigcConfigTierEnum``GenConfigServiceImpl``GenConfigActivationService``GenConfigActivationSnapshot``GenConfigReconcileService`,治理表 DDL 见 `V29.0.0__create_aigc_config_governance.sql``V30.0.0__aigc_config_add_activating_pointer.sql`)。这套机器的形状要看清,才知道 D11 和知识件能接上多少、差在哪。
**配置集的内容按「下发路由」分块,不是按业务语义分块。** `GenConfigContentCodec`(:20-44)定义版本行 = `prompt` 正文列(MEDIUMTEXT)+ `content_json` 结构化旋钮,而 content_json 里按激活时往哪儿发,劈成两块:`routeA` 是 agent + session(system_prompt 取 prompt 列、chat_model_config 带 model/parameters),激活时 PATCH 进 AgentScope Service 的 Redis;`routeB``budget.cheap_rmb_hard_limit``gates.qpass_go_min` 这种 `area.key` 扁平键(worker genconfig 的复合键口径),激活时 publish 进 Nacos 生效 dataId。激活前 codec 先做值域 sanity(:43-44:模型四字段齐全、max_tokens>thinking、预算为正落合理区间、prompt 非空),不过就整批拒、一路都不发。
**档位绑的是 Nacos 路B 的 dataId,不是业务分类。** `AigcConfigTierEnum`(:21-26)只有 CHEAP 和 TIER2 两个值,枚举的 javadoc 说得很直白:档位决定「路 B 在 Nacos 上的按档隔离 dataId 与 key 命名」——cheap-worker 用 `cheap_rmb_hard_limit`、tier2 用 `rmb_hard_limit`,两档各接一个 NacosHotConfig、各读各的 dataId,激活哪档就只 publish 那档的 dataId(设计 §3.3,防止 publish 整替换把另一档的 key 冲掉)。
**激活是「账本单点、投影两处、消费两路」。** `GenConfigActivationService`(:29-76)把激活编排成:值域 sanity → 路A PATCH(失败则路B 不发)→ 路B publish → 全成功才账本收口(转 ACTIVE、前移当前激活版指针);任一路失败就把已下发的成功路补偿重推回上一激活版、复位状态、判可重试。账本(MySQL 版本行)是权威,Redis 和 Nacos 是它的两处生效投影,Service 每 POST 现装配和 worker 的 genconfig 热读是两路消费。
对着这套机器看 D11 和知识件:
- **D11 参数对得上账本、对不上两条投影路。** D11 的十来个标量装进版本行的 content_json 毫无问题,治理状态机、版本、激活、回滚全能复用。但它既不是 prompt/模型(不走路A),也不是 worker genconfig 读的预算/门阈值(不走路B):它的消费方 `ReadinessScorer` 就在 game-cloud、和配置集账本同库同进程。所以 D11 不需要任何外部投影——它可以直接在进程内读账本的激活版本行。这反而是最简单的激活:没有跨系统的非原子写,也就没有补偿的必要,激活退化成「sanity + 账本指针前移」。代价是要给 codec 加第三块内容(既非 routeA 也非 routeB)、让它对 readiness-only 产出空载荷(现有激活编排在空载荷下天然短路,不用新增分支——§4 E3 已用码坐实)、给档位模型开一个「不绑 Nacos dataId」的口子(D11 是跨档统一的一套权重,同时给便宜档和 tier2 的游戏打分,不属于 CHEAP 也不属于 TIER2)。这几处是 W-CFG-EXT 的真实工作面,详见 §4。
所以 W-CFG-KB 不能照搬路B,需要一个文件存储 + 版本指针的机制——要么把知识件当受治理的 git ref(配置集记「当前激活哪个 ref」,靠部署机切知识件路径生效;`read_file` 现读盘、无缓存,切完下次生成即读到新内容、不重启),要么把它们搬进 MinIO 存成知识包 + manifest(复用 W-ASSET-SRC 的存储范式、带内激活推送)。两路都能不重启热换,真正的区分是带外整树 git op(路甲)vs 带内 scoped 推送(路乙),详见 §4 K1。这条岔路谁都不能替创始人拍,列 §4 K1 与 §7。
## 2 已可热配 vs 本次要外置(别把已经外置的重复做一遍)
摸底最实的一条产出是划清「哪些参数早就能热配了、这次别碰」和「哪些还写死、这次才外置」。下面这张表按证据落定,施工只做右列。
| 配置内容 | 现状 | 归属 | 本计划动不动 |
|---|---|---|---|
| 便宜档/tier2 的 prompt、system_prompt | 阶段二已受治理:版本行 prompt 列,激活走路A PATCH | 阶段二(已落地) | **不动** |
| 模型、type、credential、max_tokens、thinking、temperature | 阶段二已受治理:content_json.routeA.session,路A PATCH | 阶段二(已落地) | **不动** |
| 软预算目标、九门门阈值档(`budget.*``gates.*`) | 阶段二已受治理:content_json.routeB,路B publish Nacos,worker genconfig 热读 | 阶段二(已落地) | **不动** |
| 退路树四阈值、business-sim 品类 driver 参数 | genconfig 内置默认 + YAML + Nacos 热源三级回落,已可热配(`genconfig.py` :128-146) | 阶段二机制内 | **不动** |
| **D11 四维权重**(0.50/0.25/0.15/0.10) | 硬编码 `ReadinessScorer.java` :33-39,改要换 jar | 质量轨(SoT §9) | **W-CFG-EXT 外置** |
| **D11 efficiency 预算阈值** `BUDGET_RMB`=0.6 | 硬编码 :50,SoT 列为质量轨接管后首个复核件 | 质量轨 | **W-CFG-EXT 外置** |
| **D11 stability 修复轮阈值 / 中性分 / 效率衰减形状** | 硬编码 :42,:144-147 | 质量轨 | **W-CFG-EXT 外置**(随四维权重一起,同一配置集) |
| **设计 skill**(`.agents/skills/*.md`) | repo 检出,agent read_file 按路径读,改要重部署 | 生成线知识件 | **W-CFG-KB 外置**(机制待拍) |
| **黄金脚手架 / few-shot 首例**(`_template-*``_fewshot-*`) | repo 检出,多文件源工程,改要重部署 | 生成线知识件 | **W-CFG-KB 外置**(机制待拍,与 W-ASSET-SRC 存储形态同源) |
| **tier2 Phaser SKILL + RAG**(`tier2/skills/phaser/`) | repo 检出,tier2 agent 读,改要重部署 | 生成线知识件 | **W-CFG-KB 外置**(机制待拍) |
## 3 范围边界(两件各自 in/out,别互相拖累)
**W-CFG-EXT 只做 D11 就绪评分参数这一组标量的外置。** in:四维权重、效率预算阈值、稳定性两级阈值、中性子项分、效率线性衰减形状,连同它们的配置集类型、值域 sanity、激活的「纯账本收口」分支、`ReadinessScorer` 的进程内读线与硬编码回落。out:不碰九门判据本身(判据 SoT 是验收门,D11 只消费其结果)、不碰便宜档/tier2 已受治理的 prompt/模型/预算/门阈值、不真做放量校准(SoT §10 明定校准归质量轨的回流环校准单,本计划只把「改一版即生效」的载体接上、把起步值原样搬进去)、不改 D11 只观测不拦的定性。
**W-CFG-KB 只做知识件的受治理外置,且先拍机制再往下做。** in:设计 skill、黄金脚手架/few-shot、tier2 Phaser 知识这三批文件的版本化治理——存储机制拍板、寻址契约、激活/生效路径、审计与回滚。out:不改知识件的内容(外置的是治理载体不是范式本身)、不动 W-ASSET-SRC 的源工程存储(那是生成产物、这是生成输入,两件正交,详见下)、不把知识件塞进 Nacos KV(§1.3 已证不适用)。
**两件的正交关系要说清,免得后面合表。** W-ASSET-SRC 存的是**生成产物**——tier2 富游戏生成出来的源工程,存进 MinIO + manifest 供二次装载。W-CFG-KB 治的是**生成输入**——喂给 agent 学怎么生成的知识。两者内容不同、生命周期不同,但如果 K1 拍成走 MinIO,W-CFG-KB 可以**复用** W-ASSET-SRC 建立的存储与 manifest 范式(不是共用同一张表、同一个 bucket,是共用「MinIO 存文件树 + DB 存 manifest + 版本指针寻址」这套已被验证的模式)。这层复用写进 §5 契约,避免各造一套。
## 4 实施工单(六要素)
W-CFG-EXT 三个工单(E1 建配置集类型与内容契约、E2 接消费线与回落、E3 挂激活与后台),E1→E2→E3 顺序依赖但都小。W-CFG-KB 以 K1 机制拍板开头(它是决策闸,不拍不动后面),K2/K3 的形态随 K1 的选择定型。真跑步骤里凡触真基建(MySQL 版本行、MinIO、真激活)的都排 mini-infra / mini-desktop 窗口,6c6g 只做本地单测与代码编辑。
### E1 · 质量评分配置集:类型、内容契约、值域 sanity
**背景/目标。** D11 参数要进阶段二的配置集治理,但现有配置集按档位(CHEAP/TIER2)组织、内容按路A/路B 分块,D11 两样都不完全对得上。目标是给配置集治理开一个能装 D11 的类型,把十来个参数定成一份 content_json 契约,并给它配一套值域 sanity,让激活前能挡住配错的权重。
**方案(WHAT+HOW)。** D11 是跨档统一的一套权重(同时给便宜档和 tier2 的游戏打分),不属于任何生成档位,所以给配置集加一个「质量评分」类型,和「生成档位」正交。落地上有两条路,倾向前者、但属材料级判断列 §7 待拍:其一,给 `AigcConfigTierEnum` 加一个 `READINESS("readiness","质量评分")` 值,并在 javadoc 里明记它是「无路B、无 Nacos dataId、进程内消费」的特例;其二,给配置集引入一个和 tier 正交的「用途/kind」维度(tier 保持纯生成档语义,kind ∈ {generation, readiness})。前者改动最小、复用现成枚举,后者语义更干净、但要动配置集的分组模型和 admin 筛选。内容契约上,给 content_json 加第三块 `readiness`(平级于 routeA/routeB),字段用直白 snake_case,值就是 `ReadinessScorer` 现在的常量:
```json
{ "readiness": {
"playability_weight": 0.50, "first_play_weight": 0.25,
"stability_weight": 0.15, "efficiency_weight": 0.10,
"neutral_score": 0.5, "efficiency_budget_rmb": 0.6,
"stability_repairs_good_max": 0, "stability_repairs_ok_max": 2,
"stability_ok_score": 0.7, "stability_bad_score": 0.4 } }
```
`GenConfigContentCodec` 加一段 readiness sanity(激活前置,不过即拒):四维权重各 ∈[0,1] 且和 ≈1.0(容差 ±0.001)、budget>0、`repairs_good_max ≤ repairs_ok_max`、各子项分 ∈[0,1]。codec 现在的 `build()`(:75)按 `!hasA && !hasB` 判 NO_ROUTE、既有 `GenConfigContentCodecTest` 锚定空串/`{}`→NO_ROUTE;这里把判据扩为 `!hasA && !hasB && !hasReadiness`——真空内容仍保持红/抛(既有两条断言不删),新增「只有 readiness 块」的绿路:该配置集组不出 pathA/pathB,只做 sanity、不产外投影载荷(接 E3)。
**contract-first。** 见 §5:content_json 新增 `readiness` 块的字段契约、配置集类型的取值、sanity 规则,先落契约(schema + codec 断言)再改打分器。
**验收。** 能在配置中心建一个质量评分配置集、填一版 readiness 内容、定版;权重和不为 1.0(如填成 0.5/0.5/0.5/0.5)时定版/激活被 sanity 挡下并给出明确错误;字段名、取值范围与 `ReadinessScorer` 常量逐项对齐(doc↔code 断言点,§6)。
**风险与回滚。** 风险是配置集类型建模若选「加枚举值」路,`READINESS` 混进按档位筛选的既有查询里可能露到不该露的地方——用 javadoc 特例 + admin 查询显式过滤兜。回滚:E1 只加类型与契约、不改打分逻辑,未接 E2 前 `ReadinessScorer` 仍读硬编码,删掉新类型即回到现状。
### E2 · ReadinessScorer 接进程内读线 + 硬编码降为回落默认
**背景/目标。** 参数进了配置集,还要让打分器真的去读激活版、并在读不到时安全回落。目标是把 `ReadinessScorer` 里那几个 `static final` 常量从「真值」降为「热源缺失时的回落默认」,真值来自当前激活的质量评分配置集——正是代码注释(:43-50)写的语义。
**方案(WHAT+HOW)。** 新增一个 `ReadinessConfigProvider`(game-module-aigc 内),读当前激活的质量评分配置集版本行的 `content_json.readiness`,解析成一个带类型 getter 的只读视图,进程内缓存、**用短 TTL 失效**(比激活链发进程内失效信号更解耦、不把激活耦合进 provider,避免每次打分打 DB;打分本在回调落库这条 DB-heavy 路上,一次缓存读可忽略)。`ReadinessScorer.score()` 从 provider 取权重与阈值,provider 取不到(配置集未激活 / DB 读异常 / 字段缺失)就逐项回落到原 `static final` 常量——这批常量因此保留,身份从「真值」变成「回落默认」,一个都不删。回落必须是逐字段的:某个字段缺失只回落那一个,不因一个字段缺就整份弃用激活版。
```mermaid
flowchart LR
A["ReadinessScorer.score(trace)"] --> P["ReadinessConfigProvider.get()"]
P -->|"命中激活版"| V["content_json.readiness 各字段"]
P -->|"未激活/读失败/字段缺"| D["static final 常量(回落默认)"]
V --> W["加权算分"]
D --> W
W --> S["0-100 就绪分(只展示、不拦发)"]
```
**contract-first。** provider 读的字段名、类型、回落语义(逐字段回落)在 §5 契约里和 E1 的 content_json 契约对齐;provider 是新接口,连同它的调用点(`ReadinessScorer`)、缓存失效路径、失败模式(读失败回落不抛)一并交付,不留孤儿(§6)。
**验收。** 配置中心激活一版把 `playability_weight` 从 0.50 改成 0.60,不重编 jar、不重启 game-cloud,下一次生成回调算出的就绪分按新权重变化(用一条固定 trace 前后对比就绪分);把质量评分配置集停用 / 制造 DB 读异常,打分器回落到硬编码默认、算分不中断、不抛异常把回调打挂;content_json 里故意缺一个字段,只有该字段回落、其余仍用激活版。
**风险与回滚。** D11 只观测不拦(SoT §9),外置后配错权重最坏也只是审核台就绪分排序失真、不误拦不误放,SoT §10 明记「D11 权重变更整体可回退上一版、回退零风险」。回滚:provider 读取加一个总开关(读不到激活版即全回落,等价于未接 E2),异常时永远向回落默认收敛,保证「配置集出任何问题,打分退化成现状、绝不更差」。
### E3 · 质量评分配置集的激活挂载:纯账本收口,无外投影
**背景/目标。** 质量评分配置集的激活和生成配置集不一样——它没有路A 也没有路B,不该走「双路下发 + 补偿」那套跨系统非原子写。好消息是**不需要给激活编排加新分支**:只要 codec(E1)对 readiness-only 配置集产出空载荷(不抛 NO_ROUTE),现有 `dispatch` 就天然短路。目标因此收窄为——让 codec 接纳 readiness-only 并产空载荷、确认激活编排在空载荷下退化成 sanity + 账本收口、并在 admin 端把它挂进配置控制台。
**方案(WHAT+HOW)。** `GenConfigActivationService.activate()` 现在的流程是 sanity→路A→路B→账本收口。已亲验 `GenConfigActivationServiceImpl.dispatch`(:178-182):载荷无 pathA/pathB 时 aAttempted/bAttempted 保持 false、aRes/bRes 取 `succeeded()` 默认,`allOk()` 直接为真→走 `markVersionActivated`,补偿路(:87-92)永不触达;`reconcile` 同样 hasPathA/hasPathB 均 false → 两路 applicable=false → 判 IN_SYNC。所以质量评分配置集的激活**一行编排代码都不用改**:codec 产空载荷,现有 dispatch/reconcile 自然短路成「sanity + 账本指针前移、无投影、恒一致」。真实工作面只在 codec 停止抛 NO_ROUTE(接受 readiness-only)+ E1/E2 那几处,不碰双路激活主链、不碰补偿不变量。admin 端把质量评分配置集作为新类型挂进阶段三的配置控制台列表;但要如实说明——阶段三的编辑表单是为 routeA/routeB(prompt/模型/预算)建的,readiness 的十来个数字字段表单是**一处新增**,不是纯复用。
**contract-first。** 「readiness-only 空载荷 → 现有 dispatch/reconcile 自然短路」写进 §5:判定条件 = codec 产出既无 pathA 也无 pathB、账本收口语义、对账豁免规则,和 codec 的 build 契约对齐;激活编排本身不改。
**验收。** 激活一个质量评分配置集,`aigc_config` 治理表的当前激活版指针前移、状态转 ACTIVE、留下激活审计(谁、何时),但 Nacos 生效 dataId 与 Service Redis 全程不被触碰(抓包/日志证两处投影零写入);回滚到历史版本行,重放激活、指针回退,打分器下次读到旧权重;漂移对账对质量评分配置集报「无投影、无漂移」。
**风险与回滚。** 风险很小——激活编排一行不改(已验空载荷天然短路),真实改动集中在 codec 接受 readiness-only,不碰双路激活的公共路径与补偿不变量。回滚:E3 不动既有 CHEAP/TIER2 配置集的激活行为(它们仍有 pathA/pathB、仍走原路),readiness 类型的短路只在其配置集生效,删类型即回退。
### K1 · 知识件外置的机制拍板(决策闸,不拍不动 K2/K3)
**背景/目标。** 知识件塞不进 Nacos KV(§1.3 已证),外置得先在两条存储机制里拍一条路。这个工单的产出不是代码,是一份摆清两条路工作量/风险/收益、交创始人拍板的决策材料——因为它决定 K2/K3 的全部形态,属材料级架构选择,不自己拍。
**方案(WHAT+HOW):两条路摆开。**
路甲——**git-ref 指针,配置集记「当前激活哪个 ref」,靠部署机上的 git 操作让新知识件生效。** 知识件继续躺在 repo 检出、`read_file` 照旧按路径读,一个字不改;配置集里存的是「当前激活的知识件 commit ref + 一份人读的变更说明」,激活一版 = 记录新 ref 指针 + 审计归因。这里要纠正一个想当然的前提:让新知识件生效**不必重启进程**——`read_file`(`cheap_run.py:84`)每次调用都现读磁盘、进程内零缓存,根就是 worker 的 repo 检出(`:24`),所以在部署机上 `git checkout <新 ref>`(只切知识件那些文件)后,下一次生成的 `read_file` 就读到新内容了,跑着的 worker 进程不用重启。收益是几乎零改动、零新增运行期依赖,把「哪个版本的知识件在生产、谁在何时改的」纳入配置中心可查可回滚(回滚 = 指针指回旧 ref + 在部署机切回旧检出)。真正的代价不在「能不能热换」,而在两点:一是生效靠人在生产机上跑 git、而非配置中心的激活推送驱动,链路是带外的、和阶段二「激活即生效」的口径不齐;二是 `git checkout` 是整树操作,若某个 ref 同时动了知识件和代码,切过去会把磁盘上的代码也换新、而在跑的进程仍持旧码,形成码/进程 skew(纯知识件的 ref 无此问题——切换范围要限定在知识件路径,这条运维告警得写进 runbook)。
路乙——**MinIO 知识包 + manifest,复用 W-ASSET-SRC 存储,带内激活推送、按包 scoped。** 把三批知识件打成知识包存进 MinIO,manifest(文件清单 + 内容 hash + 版本)落 DB,配置集记当前激活的知识包版本;worker 启动时(或激活推送时)把激活版知识包物化到 `read_file` 的读取根下,`read_file` 读的还是路径、但内容来自激活版知识包而非固定检出。收益是改一条设计范式、定版、激活,worker 拉新包即生效,全程带内、不碰代码检出、和阶段二「激活即生效」同口径。代价是重得多:要建知识包的存储、物化、刷新,还要处理「物化失败 / 半拉子知识包」的回落,运行期多一条对 MinIO 的依赖。尤其 `read_file` 根映射改造这一项被低估了:现在的读取根就是整个 repo 检出(`cheap_run.py:24`)、知识路径硬编码在 `cheap_roles.py`,把知识包「物化到读取根」要么把包写进 git 跟踪树(检出变脏)、要么把 read_file 根收窄到一个专用挂载点(动 `parents[1]` 的安全边界、还得改所有硬编码的 skill 路径),两种都不轻。
**决策要素(交创始人拍)。** 两条路都能让改动生效、也都能不重启热换(见路甲的纠正),真正的区分轴是**带内还是带外**:路乙是带内——激活推送驱动、按知识包 scoped、代码检出不动、和阶段二「激活即生效」同一套口径;路甲是带外——生效靠人在部署机上跑整树 git、要靠 runbook 约束切换范围防码/进程 skew。加上知识件的改动频率远低于 D11 权重(D11 放量校准反复调,知识件是设计范式、改一次影响一批生成),MVP 阶段是否值得为「带内、scoped」的干净上路乙的复杂度(建知识包存储、物化、刷新、read_file 根映射改造),还是路甲的「记 ref + 部署机切知识件路径」已够、把复杂度留到放量后?倾向:MVP 先路甲(治理与归因补上、机制轻、能热换),路乙作为放量后按需升级项——但这条得创始人拍,不默认。
**contract-first。** K1 只产决策材料,不落契约;拍板后 K2/K3 按选定路补契约(§5 已按两路各列骨架)。
**验收。** 一份决策材料:两条路的工作量、风险、运行期依赖、回滚方式、对 read_file 边界的影响各列清楚,附倾向与理由,用 AskUserQuestion 交创始人选。选定即解锁 K2/K3。
**风险与回滚。** 风险是不拍 K1 就动 K2 会白做——所以 K1 是硬闸。无回滚问题(纯决策)。
### K2 · 知识包寻址与生效契约(形态随 K1)
**背景/目标。** K1 拍板后,把知识件的版本化落成可验证的寻址与生效链:配置集里的知识件版本指针,如何映射到 worker 上 agent 真读到的那份内容。
**方案(WHAT+HOW)。** 若 K1 选路甲:契约就是「配置集版本行记 commit ref + 变更说明」,生效链是「部署脚本据激活 ref 检出 → read_file 读检出」,寻址断言 = worker 上 `git rev-parse HEAD` 等于配置中心当前激活 ref。若 K1 选路乙:契约是知识包 manifest(文件树清单 + 各文件 content hash + 版本 id)、MinIO object key 前缀约定(复用 W-ASSET-SRC 的 `版本/路径` 范式)、物化落点(read_file 读取根),生效链是「激活 → worker 拉激活版知识包 → 物化到读取根 → read_file 读物化内容」,寻址断言 = 物化后各文件 hash 等于 manifest。两路共同的失败模式都要有回落:路甲部署失败保留上一检出;路乙物化失败保留上一份已物化知识包、绝不让 agent 读到半拉子知识包(缺文件时 read_file 显式失败、不静默读到空,类比 W-ASSET-SRC 的「缺源显式失败不静默」)。
**contract-first。** 见 §5:知识包 manifest 契约、寻址 key 约定、物化落点、生效断言,先落契约再实现。
**验收(随 K1 定型,以路乙为例)。** 改一条 skill、定版、激活,worker 拉到新知识包、物化后 agent read_file 读到的是新内容(用一句会命中该 skill 的 brief 生成、验 agent 读到的 skill 版本);制造知识包缺文件,read_file 对缺失文件显式失败、不静默读空;回滚到上一版,agent 读回旧内容。
**风险与回滚。** 路乙的运行期 MinIO 依赖是主风险,物化失败必须能回落到上一份;回滚 = 激活指针回退、重物化上一版。
### K3 · 知识件治理的审计、归因与回滚挂载
**背景/目标。** 把知识件的每次改动纳入配置中心的审计与归因,和生成配置集用同一套 yudao 原生能力,不另造。
**方案(WHAT+HOW)。** 知识件配置集复用阶段二的状态机(改→版本→审核→激活→回滚)、creator 归因、操作审计——这些都是 yudao 后台框架的原生件,配置集只要作为一个新类型进这套治理即可。admin 端把知识件配置集挂进配置控制台,编辑视图按 K1 选路呈现(路甲是 ref + 变更说明,路乙是知识包文件列表 + 上传/替换)。
**contract-first。** 无新增跨系统契约,复用阶段二治理表与状态机。
**验收。** 知识件的每次定版可查 creator 与时间、可看两版 diff(路甲是 ref diff,路乙是 manifest/文件 diff)、可一键回滚;权限受 yudao 写操作门约束。
**风险与回滚。** 复用现成治理,风险低;回滚随配置集状态机。
## 5 contract-first 契约变更清单
先落契约、再落实现,凡涉及 doc↔code 双写的都钉一条可跑的对账断言。
**W-CFG-EXT:**
1. **content_json `readiness` 块契约**(新增)。字段:`playability_weight`/`first_play_weight`/`stability_weight`/`efficiency_weight`/`neutral_score`/`efficiency_budget_rmb`/`stability_repairs_good_max`/`stability_repairs_ok_max`/`stability_ok_score`/`stability_bad_score`,类型与取值范围如 E1。落点:`GenConfigContentCodec` 的 content_json 契约 javadoc + 一份 schema(与既有 routeA/routeB 契约同处登记)。**doc↔code 断言**:字段名与默认值 = `ReadinessScorer` 常量,单测逐项 assert 相等(改一处漏改另一处即红);`efficiency_budget_rmb`**`ReadinessScorer.java:50`=0.6**(创始人 2026-07-04 决策5 值),并顺手清理 `:19``:151` 仍写「预算<=0.15」的陈旧注释(与 :50 矛盾、非本计划引入,触类时一并改)。
2. **质量评分配置集类型契约**(新增)。取值 = `readiness`(K1-EXT 建模选枚举值或 kind 维度,§7 待拍);语义:跨档、无 Nacos dataId、进程内消费。落点:`AigcConfigTierEnum` 或新 kind 维度 + javadoc 特例。
3. **readiness 值域 + 互斥 sanity 契约**(新增)。值域:四维权重和 ≈1.0(±0.001)、各权重 ∈[0,1]、budget>0、`repairs_good_max ≤ repairs_ok_max`、子项分 ∈[0,1]。**互斥**:readiness 块存在时禁 pathA/pathB(反之生成配置集禁 readiness 块),激活前置拒——否则质量评分配置集若被误配 routeB,`resolveDataId`(:54-56 `{tier}``readiness`)会拼出伪 dataId `gen-hot-params-readiness` 真 publish;误配 routeA 则 `resolveServiceUrl`(:221-229)返 null 下发失败。有此互斥,enum-value 建模对既有按档查询/dataId 拼装即安全。落点:`GenConfigContentCodec` sanity 段 + 单测正反例。
4. **`ReadinessConfigProvider` 读取契约**(新增接口)。方法:读当前激活质量评分配置集的 readiness,返回带类型 getter 的只读视图;失败/缺字段逐项回落硬编码默认。**依赖一个当前不存在的服务方法**:`GenConfigService` 查询面只有 getConfigSet/getVersion/listVersions/getConfigSetPage(`selectBySetKey` 只在 `GenConfigServiceImpl` mapper 层、未上服务接口),provider 要「按 setKey/tier 取当前激活版」须先在 `GenConfigService``getActiveVersionBySetKey/ByTier`,否则半悬空——本条一并补进契约。落点:新 provider 接口 + `GenConfigService` 新查询方法 + `ReadinessScorer` 调用点。
5. **readiness-only 空载荷契约**(codec 改述,非新增激活分支)。判定:codec `build()` 对只含 readiness 块的配置集产出既无 pathA 也无 pathB 的空载荷(不抛 NO_ROUTE);现有 `dispatch`/`reconcile` 在空载荷下天然短路成 sanity + 账本收口 + 无投影恒一致(已验 `GenConfigActivationServiceImpl.dispatch:178-182`),不新增激活分支、不动补偿不变量。落点:`GenConfigContentCodec.build` + 既有激活/对账路(只读、不改)。
**W-CFG-KB(随 K1 拍板定型,两路各列骨架):**
6. **路甲:知识件 ref 指针契约**。配置集版本行记 `knowledge_ref`(commit ref)+ 变更说明;寻址断言 = worker `HEAD` == 激活 ref。
7. **路乙:知识包 manifest 与寻址契约**。manifest(文件树清单 + content hash + 版本 id)、MinIO object key 前缀(复用 W-ASSET-SRC `版本/路径` 范式,登记进 `架构/契约总览` 寻址契约点,不与 W-ASSET-SRC 合表)、物化落点(read_file 读取根)、缺文件显式失败语义。
## 6 不留孤儿:新结构与接口交付清单
每个新结构连同读取入口、生效路径、失败模式一并交付。
| 新结构/接口 | 读取入口 | 生效路径 | 失败模式 | 交付于 |
|---|---|---|---|---|
| content_json `readiness` 块 | `GenConfigContentCodec` 解析 | 激活写账本版本行 | JSON 非法/字段缺 → sanity 拒或逐项回落 | E1 |
| 质量评分配置集类型 | admin 配置控制台 + 治理查询 | 状态机改→版本→激活 | 混入按档查询 → 显式过滤兜 | E1 |
| readiness 值域 sanity | 激活前置校验 | 不过即拒、不动账本 | 权重和≠1 等 → 拒并给错误 | E1 |
| `ReadinessConfigProvider` | `ReadinessScorer.score()` | 读激活版 + 缓存,激活失效刷新 | 读失败/未激活/字段缺 → 逐项回落硬编码默认、不抛 | E2 |
| 无外投影激活分支 | `GenConfigActivationService.activate` | sanity + 账本收口 | 无下发即无补偿;既有双路不受影响 | E3 |
| 知识件配置集(ref 或知识包) | admin 配置控制台 | K1 选定:ref+重部署 / 知识包物化 | 部署失败留上一检出 / 物化失败留上一包、缺文件显式失败 | K1→K2→K3 |
## 7 待拍板点
1. **W-CFG-EXT 的配置集建模**(E1):给 `AigcConfigTierEnum``READINESS` 值(改动最小、复用现成枚举,但 tier 语义被撑宽),还是引入和 tier 正交的 kind 维度(语义干净、但动分组模型与 admin 筛选)?属材料级判断,倾向前者,交创始人拍。
2. **W-CFG-KB 的存储机制**(K1,决策闸):**两路都能热换**(git-ref 靠部署机切知识件路径、read_file 现读盘即生效),真正的轴是**带外 vs 带内**——路甲 git-ref(轻、MVP 够用,但生效靠部署机跑整树 git、要 runbook 防码/进程 skew)vs 路乙 MinIO 知识包(重、建存储+物化+read_file 根改造,但带内、激活推送驱动、scoped、复用 W-ASSET-SRC 存储、放量后受益)?倾向 MVP 先路甲、路乙留放量后,但这条决定 K2/K3 全部形态,不默认、用 AskUserQuestion 交创始人。
3. **D11 起步值是否原样搬**:SoT §10 定校准归质量轨回流环,本计划把载体接上、起步值(0.50/0.25/0.15/0.10、budget 0.6)原样搬进第一版激活内容——确认这版起步值即当前 `ReadinessScorer` 常量、不在本计划里改任何数值(改值是质量轨校准单的活,不是外置的活)。
## 8 验收总线
**W-CFG-EXT:** 运营在配置中心改一版 D11 权重(如 playability 0.50→0.60)、激活,不重编 jar、不重启 game-cloud,下一次生成回调的就绪分按新权重变化;每次改动可查 creator/时间/diff、可一键回滚;质量评分配置集停用或 DB 读异常时,打分器逐项回落硬编码默认、算分不中断不抛异常;激活质量评分配置集时 Nacos 与 Service Redis 两处投影零写入(纯账本收口);content_json 缺字段只回落该字段;起步值与 `ReadinessScorer` 常量逐项一致由对账单测钉成机器门。
**W-CFG-KB:** K1 决策材料摆清两路、创始人拍板;按选定路,知识件的当前激活版本在配置中心可查、可归因、可回滚;改一条设计范式经定版→激活能让 agent read_file 读到新内容(路甲经重部署、路乙不重部署);知识包缺文件时 read_file 显式失败不静默读空;全程不把知识件塞进 Nacos KV。
---
**本计划为 draft,列清了两件的现状证据、已热配/待外置的边界、六要素工单与契约清单;W-CFG-EXT 可直接排期,W-CFG-KB 以 K1 机制拍板为闸。待 Codex + Opus 双评审、把设计前提喂进去、文内修掉发现项后方可执行。**