muse-agent-example/docs/2026-07-16-升格卡改造设计.md

317 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 升格卡改造设计(P0 补骨架记里程碑 · P1 串链语义判重)
- 版本:v2(2026-07-20 已实现;新增里程碑章号原文证据绑定)
- 目标读者:创始人 + 后续实现的子代理
- 边界:只改**参考书作品面升格卡**(`source_type='upgrade_book'`)的字段骨架、抽取提示词、判重方式、上下文裁剪标注;不碰范式卡、不碰主仓。概念定义对齐 `../design-docs/`(专题-06、架构-02)与源方案 `docs/2026-07-14-参考书作品面数据入库方案.md`,本稿是对该方案 §五「实体卡生命周期」的一次修订,不自立门户。
- 一句话结论:给机甲/武器/力量体系这些型补上一条"从登场到结局"的**演变历程**明细,用**真实章号**当索引(不用会变的运行时窗号),并把这条上万字的明细**只给一致性检查看、不给续写看**(续写只看当前态和一句话梗概);再用**语义判重**把改了名、换了型的同一条成长线并回一起、串成前后链。
---
## 一、先说人话:这次要解决什么
升格卡 = 每本参考书里"会随剧情长大的实体卡"——人物、机甲、武器、力量体系都算。它的价值是:当 muse 学一本书时,能看清"这台生物机甲是怎么从 4 级一步步打到 7 级对决"的完整成长线。
现在这条线**维护不出来**。典型病例:一台生物机甲开头 4 级出场,中段 5 级 6 级,终局 7 级对决——卡里完全看不到这条演变线,只剩一个"最终态",中间全被抹平了。
这次改造分两步走:
- **第一步(先补骨架、记里程碑)**:给机甲/武器/力量体系这些型补上"演变历程"字段,把每一次进化台阶记成一条条里程碑,不再被覆写抹平、不再塞错字段。
- **第二步(再串链、上语义判重)**:加"前身/后继"把散落的碎卡串成一条进化链;判重从"只比名字字符串"升级到"比语义",治改名和跨型漏并。
三个硬要求贯穿全程:**里程碑用真实章号索引(不用窗号)**;**章号必须由所标章节原文短引机械证明**;**卡片分两层给——续写只看当前态,一致性检查才看全历史**。
---
## 二、病根复述(都有真实卡为证)
以下四条病根,均从库里真实升格卡(work_id=8)抽样核实,不是推断。
### 病根 1:只有"人物"型有成长字段,机甲/武器/力量体系型是"骨架残缺"
库里 23 型中,只有两个型有"记录演变"的数组字段:`character`(成长弧线)、`character_relation`(演变轨迹)。机甲/武器/战舰所属的 `item` 型、力量体系所属的 `power_system` 型,以及 `faction`/`location`/`event`,**一个演变字段都没有**。
- 铁证:机甲卡「铁头机甲(一代)」(id=1154)出场横跨 64 章(第 10 章到第 548 章),字段却只有"品阶/类别/知情范围/能力与限制",全是会被覆写的当前值。它的一句话摘要是"已退役,封存于暗龙会仓库"——这是**最终态**,把它 10 章出场时"崭新训练机"的样子彻底抹平了。一台陪跑全书的机甲,成长线为零。
### 病根 2:进阶要么被覆写抹平,要么塞进错字段成流水账
没有专门的演变字段,模型只能把"进化"硬塞进当前态字段,结果字段语义被污染:
- 机甲卡「影杀者」(id=4935):把"被重创 → 修复 → 配合突围"这条演变线,塞进了「流转计划」(本该写未来伏笔)和「能力与限制」(本该写当前能力),还带着"本窗/前窗"这种一旦脱离运行环境就没法读的相对指代。
- 力量体系卡「生物机甲」(id=6223,正是病例本体):把"第 420 章 V 代编队 → 477 章拦截 → 489 → 490-492 → 549-550 → 572 章"整条升级线,塞进了「戏剧作用」(本该一句话写规则)和「跨体系换算」(本该写强弱对照),写成一大段散文流水账。它的摘要同样是最终态(200% 同步率对决 VI 型领主),4 级出场的初始态被抹平。
- 对照「机甲世代」(id=4995)的「境界阶梯」="一代 < 二代 < 三代 < 四代 < 伪 V 代"——这是**静态的世界规则**(一把标尺),不是某台机甲实际爬台阶的历程。说明现有字段能记"规则",但记不了"某个实体自己怎么一步步长大"。
### 病根 3:一条成长主线被拆成 30+ 张碎卡,跨多个型,彼此无链接
"机甲代际"这条主线,散落在:力量体系型的「生物机甲」「机甲世代」「旧联邦机甲代际」,加上 item 型的「铁头机甲」「铁卫」「枪骑士」「影杀者」等一堆具体机甲卡。它们之间**没有任何链接**,看不出"铁头(一代)→ 铁卫(二代)→ 枪骑士(三代)→ 影杀者(四代)→ 生物机甲(V 代)"是同一条进化链。
### 病根 4:判重只比名字字符串,语义判重是关着的
现在的判重(在 `parse_upgrade.py` 的判重步)只有三招:名字/别名精确匹配、模型自报的疑似别名、同型名字互为子串。**没有接语义判重**——代码里白纸黑字写着"嵌入延后:试跑期不调嵌入 API"。
后果:同一实体一旦改名(如"影杀者"→"IV 代纯机械机甲·影杀者")或跨型指代,字符串对不上就漏并,进一步加剧病根 3 的碎卡。
> 补充:源方案 v6 本来就设计了"名字精确 → 嵌入近邻 → AI 终判"三级判重,只是试跑期把嵌入近邻这级**关掉了**。所以第二步的语义判重不是发明新东西,是**把 v6 已设计好、暂时关着的那一级打开**。
---
## 三、方案总览
```mermaid
flowchart TB
subgraph P0["第一步 P0:补骨架 + 记里程碑(治病根 1、2)"]
A1["给 item/power_system 等型<br/>加『演变历程』数组字段"]
A2["加进追加白名单 APPEND_FIELDS<br/>(只往后加,永不覆写抹平)"]
A3["改抽取提示词:<br/>每次进化记一条里程碑<br/>标生命周期(登场→高光→退场→结局)<br/>当前态字段保持干净、不塞历史"]
end
subgraph P1["第二步 P1:串链 + 语义判重(治病根 3、4)"]
B1["加『前身/后继』链接字段<br/>把碎卡串成一条进化链"]
B2["判重打开语义近邻(≥0.60·小样实测校准)<br/>+ M3 终判 → 治改名/跨型漏并"]
end
subgraph FIX["两点硬性细化(贯穿全程)"]
C1["① 里程碑用真实章号索引<br/>绝不用运行时窗号 [窗N]"]
C2["② 卡片分两层披露<br/>续写只看当前态+梗概<br/>一致性检查才看全历史<br/>(复用现成的 aiContext 按用途裁剪)"]
end
P0 --> P1
C1 -.贯穿.-> P0
C2 -.贯穿.-> P0
```
---
## 四、第一步 P0:补骨架 + 记里程碑
### 4.1 加什么字段
给缺演变字段的实体型补一个统一的「演变历程」数组字段。核心是 `item`(机甲/武器/战舰)和 `power_system`(力量体系/机甲代际);`faction`/`location`/`event` 按需跟进(组织兴衰、地点易主、事件推进同样能用,建议同批补上,避免二次改)。
配套还要两个字段(详见第五节 schema 定义):
- 「演变概括」——一句话说清整条线的来龙去脉(现状层,续写可见)。
- 「前身」「后继」——串链用(第二步 P1 用,schema 一次性加齐)。
### 4.2 抽取提示词怎么改(要点,不写代码)
改 `parse_upgrade.py` 里的两段提示词:
- **实体观察提示词(observe)**:新实体立卡时,如果这一窗就观察到它的登场/首个进化台阶,产出「演变历程」的第一条里程碑(带真实章号 + 生命周期标记)。
- **卡更新提示词(update)**:从现在的"记新信息"改为明确的"**记进化台阶 + 生命周期**"——每当实体发生境界/代际/形态/能力的跃迁,或到达登场、高光、退场、结局这些节点,就**追加**一条里程碑,绝不覆写、绝不抹平旧台阶。同时要求:当前态字段(品阶/能力与限制/摘要)只写"现在是什么样"的干净全量值,**不许把历史流水塞进来**(直接治病根 2 的"塞错字段")。
提示词纪律要加两条硬约束:
1. 每条里程碑**必须带真实章号**,并临时附一段来自所标章节正文的连续短引;系统按章读取原文做逐字机械核验,错章、缺证据或改写证据均拒收入库。**禁止 [窗N] 窗号、禁止"本窗/近期/前段"这类相对指代**——因为卡会脱离运行环境被单独阅读。证据只用于写入前校验,校验后删除,不进入正式卡体。
2. 生命周期标记从固定枚举里选:**登场 / 成长 / 高光 / 退场 / 结局**。这一条直接治"铁头机甲摘要跳到已退役、中间态全丢"——有了登场和退场节点,一台机甲什么时候来的、什么时候谢幕,一目了然。
### 4.3 合并逻辑要跟着改
「演变历程」进 `APPEND_FIELDS` 追加白名单后,`parse_upgrade.py` 的合并/立卡/撤销逻辑(`merge_card`/`new_card`/`undo_window`)要从"处理带 [窗N] 前缀的字符串条目"适配为"处理带章号的里程碑条目":去重按里程碑内容、排序按**章号**(不再按窗号)。现有那套针对脏数据的守卫(剥前缀、剥尾部残渣、拦垃圾、拆巨型粘连)要保留并改造,别丢。
> 顺带清理:`APPEND_FIELDS` 里的「大事记」「经历」两个名字在任何 schema 里都没有定义(是孤儿字段,模型就算输出也会被字段校验裁掉)。这次统一到「演变历程」,孤儿名可保留做向后兼容但不再新用。
---
## 五、「演变历程」字段的 schema 定义
### 5.1 字段清单(加在 item.yaml / power_system.yaml 等型的"特有字段")
| 字段名 | 类型 | 说明 | 属哪层 |
|---|---|---|---|
| 演变历程 | 里程碑对象数组 | 已发生的进化台阶明细,一条一台阶,永远追加不覆写 | 完整历史层 |
| 演变概括 | 一句话文本 | 整条线的来龙去脉梗概(如"4 级训练机出场 → V 代编队主力 → 300% 同步率对决 VI 型领主") | 现状层 |
| 前身 | 链接(卡号 + 名称) | 这张卡的上一代/来源,指向同书另一张卡 | 现状层 |
| 后继 | 链接(卡号 + 名称) | 这张卡的下一代/继承者,指向同书另一张卡 | 现状层 |
### 5.2 里程碑对象结构(每条演变历程条目长这样)
正式卡体使用**极简三字段结构化对象**,既能按章号排序/定位,又不会因字段太多让模型抽崩。抽取模型输出时临时增加第四个 `证据` 字段,写入前机械核验并删除:
```json
{
"章": 420,
"台阶": "V代编队首战:暗星帝国出动十台生物机甲配合亡灵号,扭转多瑙星海战场局势",
"周期": "高光",
"证据": "所标章节正文中的连续短句"
}
```
- **章**:绝对章号,用于索引与排序。单章事件填整数(`420`);跨多章的连续事件填区间字符串(`"420-423"`)。**这是"用绝对章号索引"的落点**。
- **台阶**:一句话讲清"进化到什么 + 靠什么事件"。刻意把"结果 + 经过"合成一句,不再往下拆细字段,避免模型输出格式崩。
- **周期**:生命周期枚举,取值 `登场 / 成长 / 高光 / 退场 / 结局`。
- **证据**:仅存在于抽取传输层,必须逐字出现在「章」所指正文中;机械核验后删除,正式卡仍保持三字段。
这个结构是有真实依据的:库里关系卡「苏铭×林初雨」早期就自发用过 `{状态, 章区间: "Ch.21", 转折事件}` 这种带章号的结构化格式,证明模型抽得出、章号索引可行。三字段是"结构化可查询"和"模型稳定"之间的最优平衡点。
> 备选(若评审认为对象改造风险太大):退一步用**带章号前缀的字符串条目**,即把现在的 `[窗2] xxx` 换成 `[第420章·高光] xxx`。改动更小、迁移更平滑,但章号在文字前缀里,得靠正则抽取、不能直接用 SQL 过滤。推荐前者、备选后者,见第九节拍板点。
### 5.3 一条真实成长线改造后长什么样(以病例"生物机甲"示意)
现在(病根 2 的样子):升级史一大段塞在「戏剧作用」字段里,散文流水,摘要只剩最终态。
改造后:
- 演变概括(现状层,续写可见):"卡依斯生物反应驱动的奇袭兵器,从 4 级机体登场,历经 V 代编队、300% 同步率解放,终局对决近帝皇级 VI 型领主。"
- 演变历程(完整历史层,只给一致性检查):
- `{章: 332, 台阶: "生物机甲首次登场,作为突破哈姆斯要塞的核心奇袭兵器", 周期: 登场}`
- `{章: 420, 台阶: "V代编队首战,配合亡灵号扭转多瑙星海战场", 周期: 成长}`
- `{章: "490-492", 台阶: "苏铭精神链接同步率飙至300%,压制伪VI代、夺龙基之枪贯穿拜鲁特", 周期: 高光}`
- `{章: "549-550", 台阶: "300%同步率独战近帝皇级VI型英雄级伊琳莉丝,龙基之枪刺穿其AT立场", 周期: 结局}`
一条清清爽爽、按章号排序、能看出起承转合的成长线就立住了。
---
## 六、两点硬性细化的落地
### 6.1 细化①:真实章号索引 + 现有 [窗N] 卡的迁移办法
**为什么不能用窗号**:窗号([窗N])是抽取时"切正文窗"的运行产物——切窗规则是"每窗 12 章或 3.5 万字",规则一改窗号全变。它是运行时定义、会变、不该当卡的稳定属性。真实章号(第 420 章)才是永远不变的锚。
**存量卡怎么迁移**(一次性迁移脚本,本稿只给办法、不实现):遍历所有 `upgrade_book` 卡的演变类字段(成长弧线/演变轨迹等)里每一条带 [窗N] 的条目,按下面优先级把 [窗N] 换成真实章号:
1. **优先——抽条目文字里已有的真实章号**:很多条目正文里模型自己就写了"第 420 章""163 章""Ch.21",正则抽出来直接当「章」。这是最准的。
2. **兜底——用窗到章的映射表**:条目里没有内嵌章号的,查 `example_upgrade_window` 表(窗号 → from_chapter~to_chapter),把 [窗N] 映射成该窗的**章区间**(如 [窗2] → "13-24"),并打一个"章区间近似"标记,表明这是窗级近似、不是精确到章。
3. **顺手清脏**:迁移时一并清掉已知垃圾——`[窗17] ['窗23` 这类残片、重复条目、尾部 JSON 拼接残渣。复用 `parse_upgrade.py` 里现成的剥前缀/剥尾残/拦垃圾逻辑。
> 诚实边界:上万字的历史里,兜底那批(无内嵌章号、只能靠窗映射)会损失精度——只能定位到 12 章宽的区间。这是存量迁移的固有局限,新抽取的卡因为提示词强制带真实章号不受影响。
### 6.2 细化②:渐进式披露 = 现状层 + 完整历史层,接现有 aiContext 按用途裁剪
**这不是新机制**。本仓早就有"字段按用途可见性裁剪"的机制:每个 schema 字段有个 `aiContext` 标注——`true` 任何场景可见、`false` 一律不给、`[用途…]` 只有列出的场景可见。用途有四种:`generation`(续写)、`detection`(一致性检查)、`planning`(规划)、`extraction`(抽取判重)。`read-context`(组装上下文)、`search`(检索)、`detect`(一致性检查)三个 skill 都已经按这个标注干活。渐进披露就是**给演变字段填对 aiContext**,一行标注的事。
分两层:
- **现状层**——写作 agent 续写时默认只读这层。包括实体当前态(品阶/能力与限制/当前持有者/一句话摘要)+ 演变概括 + 前身后继。让续写者知道"这台机甲现在几级、一路怎么来的梗概、前身是谁",但不被上万字明细淹没。
- **完整历史层**——只有一致性检查(detector)读全。就是「演变历程」那个可能上万字的里程碑数组。续写用不上这么细,一致性检查却必须有它才能查出"上一章还 4 级、这一章怎么突然 7 级"这种跳级穿帮。
**用途可见性分层表**(这张表就是要填进各型 schema 的 aiContext 标注):
| 层 | 字段 | aiContext 标注 | 续写<br/>generation | 一致性检查<br/>detection | 规划<br/>planning | 判重<br/>extraction |
|---|---|---|:---:|:---:|:---:|:---:|
| 现状层 | 当前态(品阶/能力与限制/当前持有者) | `true` | ✓ | ✓ | ✓ | ✓ |
| 现状层 | 一句话摘要 | `true` | ✓ | ✓ | ✓ | ✓ |
| 现状层 | 演变概括 | `true` | ✓ | ✓ | ✓ | ✓ |
| 现状层 | 前身 / 后继 | `true` | ✓ | ✓ | ✓ | ✓ |
| **完整历史层** | **演变历程(全里程碑)** | **`[detection, extraction]`** | **✗** | ✓ | (可选) | ✓ |
关键就是最后一行:把「演变历程」标成 `[detection, extraction]`——续写(generation)自动看不到,一致性检查(detection)和判重(extraction)才看得到。填完这一行,三件事**自动发生、不用再写别的代码**:
1. `detect` skill 会自动把「演变历程」纳入检查项(它的规则就是"凡 aiContext 含 detection 的字段,自动成为一个检查项")——一致性检查从此多一道"演变连续性/跳级穿帮"关卡。
2. `read-context` 组装续写上下文时,自动把「演变历程」裁掉——写作 agent 的上下文不会被上万字历史撑爆。
3. `search --purpose generation` 检索时也自动裁掉它。
> 一个要讲清的区别:`character` 现有的「成长弧线」标的是 `[planning, detection]`,它的语义是"**计划中**的变化,防抢进度剧透"——防的是"未来"。而「演变历程」是"**已发生**的历史",它对续写不可见的原因不是防剧透(都发生过了),而是**怕上万字撑爆上下文**。两者都对续写关闭,但一个防未来、一个防过载,别混为一谈。也因此,关系卡的「演变轨迹」保持 `true`(续写可见)是合理的——俩人现在什么关系、怎么走到这一步,对续写笔触很重要,且量不大;而机甲的上万字升级史续写用不上,只需当前几级。
---
## 七、改动清单(涉及哪些文件 / schema / prompt / skill)
> 全部是设计稿层面的改动点,本稿不实现、不执行、不重跑种子、不提交。
| 文件 | 改什么 | 属 |
|---|---|---|
| `meta/schemas/item.yaml` | 加「演变历程」`[detection,extraction]`、「演变概括」`true`、「前身」「后继」`true` 四字段 | schema |
| `meta/schemas/power_system.yaml` | 同上 | schema |
| `meta/schemas/faction.yaml` `location.yaml` `event.yaml` | 同上(建议同批补,避免二次改;见拍板点) | schema |
| `meta/schemas/character.yaml` | 语义对齐:明确「成长弧线」=未来计划,已发生台阶归「演变历程」(是否给 character 也加演变历程见拍板点) | schema |
| `meta/schemas/character_relation.yaml` | 「演变轨迹」说明里"章区间"沿用真实章号(本就如此),确认 aiContext 保持 `true` | schema |
| `.claude/skills/parse-book/scripts/parse_upgrade.py` | ①`APPEND_FIELDS` 加「演变历程」②observe/update 提示词改为记台阶+生命周期+真实章号+原文证据、禁窗号相对指代、当前态字段保持干净 ③合并/立卡/撤销逻辑适配里程碑对象(证据绑定、去重按内容、排序按章号)④判重步接语义近邻+M3 终判(P1) | prompt + 逻辑 |
| `.claude/skills/db/scripts/seed_schemas.py` | 不改脚本;schema YAML 改完后**需重跑一次**把新字段和 aiContext 灌进库(本稿不跑,实施时跑) | 种子 |
| `.claude/skills/read-context/SKILL.md` | Layer 2 知识卡裁剪说明里,点名"演变历程完整层仅一致性检查可见、续写不给"(机制已支持,补一句说明) | skill 文档 |
| `.claude/skills/detect/SKILL.md` | 检查项示例表补一行"演变历程 → 演变连续性/跳级穿帮"(实际自动生成,文档补例) | skill 文档 |
| 新增一次性迁移脚本(如 `docs/` 或 skill scripts 下) | 存量卡 [窗N] → 真实章号 + 字符串条目 → 里程碑对象 + 清脏(见 6.1) | 迁移 |
**不需要改的**:数据库表结构(演变历程/前身/后继都是知识卡 JSON 里的字段,不需要 DDL 建表/改表);语义判重复用现成的 `example_knowledge_embedding` 向量表和 `embed`/`search` skill,不新建表。
---
## 八、第二步 P1:串链 + 语义判重(细化)
### 8.1 串链:前身/后继
用第五节已定义的「前身」「后继」链接字段(指向同书另一张卡的卡号 + 名称),把"铁头(一代)→ 铁卫(二代)→ 枪骑士(三代)→ 影杀者(四代)→ 生物机甲(V 代)"这条链串起来。链接机制照抄现成的做法——关系卡早就用"甲方卡号/乙方卡号"指向人物卡,一样的路子。标 `aiContext: true`,续写要知道"这台机甲的前身是谁"。
### 8.2 语义判重:打开嵌入近邻 + M3 终判
在判重步,除现有的名字精确匹配/子串外,新增(就是打开 v6 已设计、暂时关着的那级):
1. 对候选新实体算向量(走 `embed` skill),用 `search` 召回**同书同型**相似度 ≥ 0.60 的近邻卡(原定 0.78,2026-07-18 零落库小样实测校准:同实体改名对余弦 0.63-0.66、不同体系对 ≤0.58,0.78 永不触发语义层;依据详见 parse_upgrade.py 阈值注释)。
2. 召回的近邻交给 M3 终判(走 `parse_llm.py` 的 `m3_json` 入口,构造"这两张卡是不是同一实体 / 是不是前身后继关系"的判断),判定三选一:**同一实体**(并卡)/ **前身后继**(不并卡但串链)/ **无关**(各自立卡)。
这样"影杀者"和"IV 代纯机械机甲·影杀者"这种改名、以及跨型指代同一条线的,都能被语义召回并正确处理。
**跨型判重要收着点**:`item`(具体某台机甲)和 `power_system`(一整套体系)很容易被向量拉近却其实不是一回事(如"生物机甲体系"vs 具体机甲"伊琳娅丝")。所以跨型只做"提示 + 串链候选",不自动并卡;同型才允许自动并。
---
## 九、风险与验证办法
### 9.1 批量抽取的连接与恢复合同(2026-07-22 补充)
升格抽取不得在模型或嵌入调用期间持有业务数据库连接。一次窗口只保留一个可恢复的中间提交点,避免把每个更新批次都变成独立恢复协议:
1. **无连接计算**:读取正文、既有卡和 revision 后立即关闭连接;完成观察、证据修复、语义判重和全部实体更新输出。此阶段失败时没有知识写入,只把窗口留成干净失败断点。
2. **实体短写事务**:先复验模型输入快照,再复验模型所见 revision,原子写入新卡、别名、留档和实体更新;新卡 `INSERT` 同时返回数据库实际 `revision`,同窗后续更新的 CAS 必须绑定该值,不能假定初始版本。把窗口置为 `processing`,并在同一事务记录本书受控域的完整状态摘要。进程此后崩溃,续跑必须先复验摘要,再精确撤销本窗。
3. **无连接关系计算**:实体阶段提交后重新读取人物与关系快照,关闭连接,再生成或重整关系。首次同 pair 冲突只调用一次模型 repair;repair 仍按同 pair 拆条时,只允许机械合并关系类型、本窗演变和无字段冲突的其他维度,非空未知顶层键或同字段异值继续失败关闭,不调用第三次模型。卡片已更新后的状态仍能进入关系判断,但模型调用期间没有长事务。
4. **关系短写事务**:复验 `processing` 摘要、输入快照、人物 revision 和关系集合,写关系与剩余出场章;窗口仍保持 `processing`,原子刷新状态摘要。
5. **严格嵌入与完成事务**:在无业务数据库连接状态下计算本窗 touched 卡向量,再用短事务复验摘要和向量 owner 后落库;只有嵌入成功且摘要再次一致,窗口才置 `done`。普通嵌入失败也按窗口失败处理,不允许后窗在缺向量状态下继续语义判重。
```mermaid
flowchart LR
R[短读: 正文/卡/revision] --> C1[无连接: 观察/证据/判重/实体更新]
C1 --> W1[短写: 实体阶段<br/>processing + 完整摘要]
W1 --> R2[短读: 更新后人物/关系]
R2 --> C2[无连接: 关系生成/重整]
C2 --> W2[短写: 复验摘要<br/>关系/出场章/刷新摘要]
W2 --> E[无连接: 计算增量向量]
E --> W3[短写: 复验摘要/owner<br/>落向量/done]
E -->|失败或 owner 冲突| X[锁内复验完整摘要<br/>通过才撤销]
```
恢复与并发纪律:
- 普通错误只允许同窗完整撤销后重试一次;第二次仍失败立即停止当前作品,保留断点,不跳窗、不换作品内顺序。
- `processing` marker 固定为 `upgrade-fence:v1:<stage>:<inputSha256>:<stateSha256>`;`stage` 只允许 `entity` 或 `relation`。非法、缺失或版本不认识的 marker 一律写 durable 断点并停止,不能猜测恢复。
- `inputSha256` 绑定实际送模输入:作品标题、窗口边界、每章 `id/title/status/revision`、每个 block 的 `id/order/type/revision/content_text`,以及实体 schema active version/字段合同、抽取合同和当前 `parse_upgrade.py` SHA-256。按章拼接正文的规则必须与送模文本一致;实体阶段写入前与关系阶段写入前都要重算,章域不完整、无 block 或拼接正文为空直接失败关闭。
- `stateSha256` 覆盖本书卡、别名、留档、水位、审计、向量和全部窗口不变元数据;窗口自身可变的状态/错误文本不参与自引用 hash。`processing` 恢复和失败补偿都要在固定顺序的七域锁内复验它。
- 任何外部确认、改版、删除或辅助域漂移都写 `compensation-failed` 并停止,禁止覆盖外部状态。嵌入落库与 `done` 在同一最终短事务完成,消除 `done` 已提交但向量尚未落库的崩溃缝隙。
- 普通无 marker clean 的产物检查只豁免完整满足墓碑条件且 `updater` 为 `upgrade-reset` 或 `upgrade-undo` 的 alias/new_card/embedding;presence、card_state、audit 仍有行即阻断。该上下文用于识别 `undo_window` 合法留下的新卡墓碑,不得放宽 status、entity owner 或其他 updater。
- 当前数据库没有所有写方共同遵守的 work 级互斥键。为保证摘要无幻读,七个受控域使用**只跨短写事务**的固定表锁;它会短暂串行化不同作品的写阶段,但绝不跨模型或嵌入计算。待真实耗时证明成为瓶颈后,再单独设计全系统统一的 work 级写锁,不能在本次修复里用弱锁换假安全。
- fence 引入前的 `legacy failed` 窗不能自动猜测。`recover-legacy-failed` 是唯一恢复入口:preview 输出窗口、七域本窗产物计数与确认 hash;execute 必须取得同书锁、精确匹配 hash、确认无 `processing` 窗且**阻断产物计数为 0**,才可标成 `retryable-clean`。计数只豁免能机械证明来源的全书 reset 墓碑:draft 必须同时 `deleted=true`、`updater='upgrade-reset'`、`status='pending'`;embedding 还必须 `entity_id IS NULL` 且自身 `deleted=true`;alias 必须 `deleted=true` 且 `updater='upgrade-reset'`。`upgrade-undo` 只属于普通 clean,在 legacy 上仍必须阻断,两种上下文不得混用。presence 没有 reset 来源标记,任何本窗行(包括软删墓碑)都阻断;其他软删、活跃行、已有 entity owner,以及任何 card_state/audit 行也都阻断。完整 `stateSha` 仍覆盖所有 deleted 行,不能混淆。操作人仍须先确认旧进程与数据库 backend 已结束,工具不得把“查不到已提交行”误当成“旧事务不存在”。
- 显式 `--redo-window` 需要把 redo 前完整快照持久化到进程外,单靠内存快照无法承受崩溃。该持久快照协议另立设计前,命令机械拒绝;历史修正走已有全书 backup/reset/rebuild,不提供不完整的 redo。
### 风险
1. **条目从字符串变对象,合并逻辑改动面不小**。`parse_upgrade.py` 现有一大套针对字符串条目的守卫(剥前缀、剥尾残、拦垃圾、拆粘连、窗序归位),全要跟着改造成处理里程碑对象;存量卡还是字符串,迁移要兼容。→ 缓解:分两步落,先补字段和提示词(新卡受益),再做存量迁移;对象结构压到三字段降低崩坏面。
2. **模型可能漏证据或给出错章证据**。→ 缓解:卡体仍保持三字段;模型输出临时附证据,缺失时只对当前窗候选发一次有界证据修复调用;最终仍无法由所标章节原文证明的条目拒收并留审计。完整性不能优先于章号真实性。
3. **语义判重增加调用量和跨型误并风险**。→ 缓解:0.60 阈值(实测校准) + 同型才自动并 + 跨型仅提示;判重近邻可像 v6 说的那样批量做、不逐条烧。
4. **存量迁移精度损失**:无内嵌章号的条目只能靠窗映射到 12 章宽区间。→ 接受为存量固有局限,新卡不受影响。
5. **改 schema 后必须重跑种子**,否则库里字段合同和 YAML 不一致,抽取会按旧合同把新字段裁掉。→ 实施清单里钉死"改 YAML 后重跑 `seed_schemas.py`"这一步。
6. **受控域表锁会压缩多作品写入并发**。→ 模型调用仍可并行,表锁只覆盖短写和摘要复验;实施后记录阶段耗时与锁等待,若写阶段成为瓶颈,再以统一 work 级锁替换,禁止局部脚本自创弱互斥。
### 验证办法(实施后怎么确认真治好了)
- **成长线立得住**:挑病例"生物机甲"这条线(work_id=8)跑改造后的抽取,看能不能维护出"4 级登场 → V 代 → 300% 高光 → 结局对决"这条按章号排序、带生命周期的清晰台阶,以及"铁头 →…→ 生物机甲"的前身后继链。
- **分层裁剪真生效**:`search --purpose generation` 查一张机甲卡,确认「演变历程」被裁掉、只回现状层;`--purpose detection` 确认「演变历程」保留。
- **一致性检查自动加项**:对一章跑 `detect`,确认检查清单里自动多了"演变连续性"这一项。
- **语义判重召回**:造一个改名样例(影杀者 / IV 代纯机械机甲·影杀者),确认语义判重能召回并由 M3 判为同一实体或前身后继。
- **章号无窗号残留**:迁移后全库扫一遍,确认演变类字段里不再有 [窗N] 前缀。
- **章号证据真绑定**:用跨章窗口重抽验证,错标章节的里程碑必须拒收;正确章节原文短引通过后,正式卡只保留章 / 台阶 / 周期,不残留证据字段。
- **连接红线**:离线门禁覆盖观察、证据修复、判重、实体更新、关系生成/重整和嵌入;每次外部调用时业务数据库连接数必须为零。
- **真实数据库恢复冒烟**:在回滚事务内验证公共 `example_upgrade_window` 完整行的 `processing` marker 写入、复验和回滚;同事务把一张 `upgrade_book` draft 的 revision 加一,确认 marker 复验明确拒绝,最后用新连接确认窗口完整行和 draft revision 均未改变。不满足 pending 窗或无可用 draft 时明确失败。纯 fake 连接测试不能替代这项。
---
## 十、需要创始人拍板的点
> **✅ 已拍板(2026-07-17,创始人 4 项 + 主代理定 #4)——实现以此为准,下方原始理由留档:**
> 1. 里程碑卡体格式 = **结构化对象(章 / 台阶 / 周期)**;抽取传输临时增加原文证据,机械核验后删除。无法证明所标章号的条目拒收并留审计,不做猜测性降级。
> 2. 补齐范围 = **五型全补**:item / power_system / faction / location / event。
> 3. character = **一起改**:给人物也加「演变历程」、「成长弧线」回归"未来计划"本义,连带迁移 200+ 张存量人物卡。
> 4. 演变概括 = **独立字段**(主代理定:摘要管当前态、概括管轨迹,职责清晰)。
> 5. 存量迁移 = **人工精确化**:不接受 12 章宽近似——无内嵌章号的老条目靠再抽原文 / 人工核对精确到真实章号。
1. **里程碑用"结构化对象"还是"带章号前缀的字符串"?** 本稿推荐三字段对象(章/台阶/周期,可 SQL 按章排序、结构清晰),备选字符串前缀 `[第420章·高光] …`(改动最小、迁移最平滑)。取舍是"规整可查询"对上"改动小、模型稳"。
2. **演变字段是否给全部实体型补齐?** 核心必补 `item`/`power_system`。`faction`/`location`/`event` 建议同批补(组织兴衰、地点易主、事件推进同样用得上),避免将来二次改 schema。请拍板范围。
3. **`character` 型怎么处理?** 现在它把"已发生历程"记在名为「成长弧线(未来计划)」的字段里,语义拧巴。是否给 character 也加「演变历程」、让「成长弧线」回归"未来计划"本义?这会牵动 200+ 张存量 character 卡的迁移,请拍板做还是暂缓。
4. **「演变概括」是独立字段还是并进「一句话摘要」?** 本稿推荐独立字段(摘要管当前态、概括管轨迹,职责清晰);也可并进摘要写法要求省一个字段(更省、但摘要职责变重)。
5. **存量迁移的精度损失可接受吗?** 无内嵌章号的老条目只能定位到 12 章宽区间。确认接受,还是要投入人工/再抽一遍把这批精确化。