# auto_dishu V5.5 修复方案 · L5引擎 + L6人物快照 + 数据卡回补 > HLDP://chenxing/memory-engine/v5.5/repair > > 创建于 2026-06-10 23:47 · 桔子线架构人格体 PER-JZ-ARCH-001 > > 停拆点:《穿成假冒失忆大佬女友的恶毒女配》第 59 章 · 2026-06-10 23:41:47 > ## 修复目标 | 优先级 | 修复项 | V5.4 现状 | V5.5 目标 | | --- | --- | --- | --- | | 🔴 P0 | **L5 引擎** | 37 章 0 次触发 | 词表升级 + 强制阈值接通 + 全程日志 | | 🔴 P0 | **L6 人物快照 prompt** | 关键事件截断 + 失去 why 段 + AI 口吻泄漏 | 5 列结构 + 封闭标签词表 + 强制覆盖到末章 | | 🟡 P1 | **数据卡缺失** | ch 8/10/16/24/28/29 共 6 章缺 | 回补脚本一次补齐 | | 🟢 P2 | **ch16 重复页** | notion-323 + notion-324 都是 ch16 | 手动删除内容不完整的一份 | --- ## 1.1 · V5.5 L5 引擎升级方案 ### 修复点对照 | 旧问题 | V5.5 修复 | | --- | --- | | 硬编码 scene_type 词表 → 自由词永远 miss | 硬编码快查 + AI 兜底归类(Layer1 查表 → miss 才调 AI) | | 强制阈值 ≥15 章未真接通 | 模式B 单独提为顶层判断 · 任何路径都能命中 | | 静默不触发 · 无法定位为什么 | 每次预筛输出日志到 `logs/l5_trigger_{book}.log` | ### 完整代码 · 保存为 `tools/l5_engine_v5_5.py` ```python # auto_dishu V5.5 · L5 引擎核心 # HLDP://chenxing/memory-engine/v5.5/l5-engine # 升级要点: 硬编码词表+AI兜底归类 / 强制阈值真接通 / 全程日志 import json from datetime import datetime from pathlib import Path # === 配置 === L5_MIN_INTERVAL = 3 # 距上次L5最小间隔(章) L5_FORCE_THRESHOLD = 15 # 强制触发阈值(章) L5_WINDOW = 5 # 预筛窗口章数 # 4 大类场景的硬编码词表(先查表 · miss 才调 AI 归类) SCENE_CATEGORIES = { "爆发": ["冲突", "爆点", "战斗", "对抗", "撕破脸", "信任危机", "情感对峙", "正面交锋", "冲突升级", "强吻", "翻脸"], "收束": ["收束", "缓冲", "日常", "和好", "破冰", "温馨", "甜蜜", "团圆", "化解", "回归", "释怀"], "平行": ["铺垫", "开局", "磨合", "试探", "暧昧", "观察", "日常温馨", "互动", "升温"], "转折": ["转折", "信息交代", "身份揭露", "反转", "重大决定", "身份暴露", "秘密揭晓"], } def categorize_scene_type(scene_type_raw: str, llm_client) -> str: """把数据卡里的自由 scene_type 归到 4 大类之一""" if not scene_type_raw: return "平行" # Layer 1: 硬编码快查 for category, keywords in SCENE_CATEGORIES.items(): if any(kw in scene_type_raw for kw in keywords): return category # Layer 2: 词表 miss → AI 兜底 prompt = ( f"把下面这个场景类型归类到4个类别之一: 爆发/收束/平行/转折\n" f"- 爆发: 冲突/爆点/战斗/对抗/情感对峙\n" f"- 收束: 收束/缓冲/日常/和好/温馨\n" f"- 平行: 铺垫/磨合/试探/暧昧/观察\n" f"- 转折: 重大转折/信息揭露/反转/重大决定\n" f'场景类型: "{scene_type_raw}"\n' f"只输出一个词:" ) result = llm_client.complete(prompt).strip() if result in SCENE_CATEGORIES: return result return "平行" # 兜底默认值 def should_check_l5(tracker: dict, recent_datacards: list, llm_client, book_name: str) -> tuple: """V5.5 L5 触发预筛 · 三模式 + 全程日志 返回: (是否触发, 原因字符串) """ log_path = Path(f"logs/l5_trigger_{book_name}.log") log_path.parent.mkdir(exist_ok=True) current_chapter = tracker.get("current_chapter", 0) last_l5_chapter = tracker.get("last_l5_chapter", 0) chapters_since_l5 = current_chapter - last_l5_chapter # === 最小间隔检查 === if chapters_since_l5 < L5_MIN_INTERVAL: reason = f"距上次L5仅 {chapters_since_l5} 章,不足 {L5_MIN_INTERVAL}" _log_l5(log_path, current_chapter, "skip", reason) return False, reason # === 预筛: 取最近 L5_WINDOW 章数据卡 → 归类 === recent_window = recent_datacards[-L5_WINDOW:] recent_cats = [] for card in recent_window: raw = card.get("scene_type", "") cat = categorize_scene_type(raw, llm_client) recent_cats.append({ "chapter": card.get("chapter"), "raw": raw, "cat": cat, }) last_3 = [c["cat"] for c in recent_cats[-3:]] last_2 = [c["cat"] for c in recent_cats[-2:]] # 模式A: 最近3章爆发 + 最近2章收束 if "爆发" in last_3 and "收束" in last_2: reason = f"模式A · 爆发→收束 · {recent_cats}" _log_l5(log_path, current_chapter, "trigger", reason) return True, reason # 模式B: 强制阈值兜底(关键修复点 ← v5.4 漏接的就是这条) if chapters_since_l5 >= L5_FORCE_THRESHOLD: reason = f"模式B · 强制阈值 {chapters_since_l5}>={L5_FORCE_THRESHOLD}" _log_l5(log_path, current_chapter, "trigger", reason) return True, reason # 模式C: 转折 → 平行 if "转折" in last_3 and "平行" in last_2: reason = f"模式C · 转折→平行 · {recent_cats}" _log_l5(log_path, current_chapter, "trigger", reason) return True, reason # 不触发 · 记录详细原因 reason = (f"无模式命中 · 累计 {chapters_since_l5}/{L5_FORCE_THRESHOLD} " f"· 最近: {recent_cats}") _log_l5(log_path, current_chapter, "skip", reason) return False, reason def _log_l5(log_path: Path, chapter: int, decision: str, reason: str): with open(log_path, "a", encoding="utf-8") as f: f.write(f"[{datetime.now().isoformat()}] " f"ch{chapter:>3} {decision:>7}: {reason}\n") ``` --- ## 1.2 · V5.5 L6 人物快照 prompt 修复 ### 修复点对照 | 旧问题 | V5.5 修复 | | --- | --- | | 关键不可逆事件清单 3 条就断 | prompt 显式要求 "必须覆盖到第 N 章" + 强约束 | | 失去 why 不可逆解释 | 表格强制新增一列 "不可逆原因(一句话)" | | 变化性质标签自由发挥 → 无法统计 | 封闭枚举词表锁定 14 类 | | AI 口吻泄漏 ("OK,收到") | prompt 头部明确禁止 | | 关系/外部人物维度压缩 | 4 张表分离 (人物状态/关系/外部人物/事件) | ### 完整 prompt · 保存为 `tools/prompts/l6_character_snapshot_v5_5.py` ```python # V5.5 L6 人物快照 prompt 模板 # HLDP://chenxing/memory-engine/v5.5/l6-character-snapshot L6_CHARACTER_SNAPSHOT_PROMPT_V5_5 = """\ 你的任务: 扫描提供的所有章节数据卡,提取核心人物的"不可回退状态变化", 生成《{book_name}》{node_threshold} 字节点 · 人物状态快照。 # 强制输出规则(违反任一条 → 输出无效) 1. 禁止输出 "OK,收到" "我来分析" "扫描指令" 等任何 AI 自述话术 2. 直接进入正文,第一行就是 "# 人物状态变化快照",前面不许有空话 3. 每条状态变化必须 5 列齐全: 时间节点(章号) + 初始状态 + 变化后状态 + 变化性质(标签) + 不可逆原因(why) 4. 变化性质标签必须从封闭词表中选,禁止自创: [身份/认知/策略/心理/职业/关系/情感/信任/角色/能力/职场地位/共情/动机/世界观] 5. "关键不可逆事件清单" 必须覆盖到第 {last_chapter} 章 禁止只列前几章就停笔 · 必须遍历从第 1 章到第 {last_chapter} 章 6. 4 张表必须全部输出,顺序固定: 主要人物状态 → 关系变化 → 外部人物引入 → 关键事件清单 # 输出结构(严格按此模板) # 人物状态变化快照 · {book_name} · {node_threshold} 字节点 ## 核心人物: 女主姓名 | 时间节点 | 初始状态 | 变化后状态 | 变化性质 | 不可逆原因 | |---------|---------|-----------|---------|-----------| | 第X章 | ... | ... | 标签 | 一句话解释 | (覆盖所有不可逆变化点) ## 核心人物: 男主姓名 (同上 5 列表格 · 覆盖所有不可逆变化点) ## 关系状态变化(不可逆节点) | 时间节点 | 关系类型 | 变化描述 | 不可逆原因 | |---------|---------|---------|-----------| ## 外部人物引入(不可逆节点) | 时间节点 | 人物 | 影响 | 不可逆原因 | |---------|------|------|-----------| ## 关键不可逆事件清单(必须覆盖到第 {last_chapter} 章) 1. 第X章: 事件 — 影响 2. 第X章: 事件 — 影响 ... (禁止中途停笔 · 必须覆盖 1 → {last_chapter} 章的全部关键事件) # 输入数据 扫描范围: 第 1 章至第 {last_chapter} 章(共 {datacard_count} 张数据卡) 数据卡: {datacards} 开始输出: """ def render_l6_character_snapshot_prompt( book_name: str, node_threshold: str, last_chapter: int, datacards: list, ) -> str: """渲染 L6 人物快照 prompt""" datacards_str = "\n---\n".join([ f"## 第{c['chapter']}章\n" f"scene_type: {c.get('scene_type', '未标注')}\n" f"{c.get('content', '')}" for c in datacards ]) return L6_CHARACTER_SNAPSHOT_PROMPT_V5_5.format( book_name=book_name, node_threshold=node_threshold, last_chapter=last_chapter, datacard_count=len(datacards), datacards=datacards_str, ) ``` --- ## 1.3 · 数据卡回补脚本 ### 缺失清单 | 书 | 缺失章节 | 处置 | | --- | --- | --- | | 《恶毒女配》 | ch 8 / 10 / 16 / 24 / 28 / 29 | 跑回补脚本 | | 《恶毒女配》 | ch 16 重复 (notion-323 + notion-324) | 在 Notion UI 手动删一份 | ### 回补代码 · 保存为 `tools/backfill_datacards_v5_5.py` ```python # V5.5 数据卡回补 · 单独补齐缺失章节 # HLDP://chenxing/memory-engine/v5.5/backfill import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent)) from auto_dishu_v5_5 import generate_datacard_for_chapter BOOK = "穿成假冒失忆大佬女友的恶毒女配" MISSING_CHAPTERS = [8, 10, 16, 24, 28, 29] def main(): print(f"🔧 开始回补 《{BOOK}》缺失数据卡") print(f" 缺失章节: {MISSING_CHAPTERS}\n") results = {"success": [], "failed": []} for ch in MISSING_CHAPTERS: print(f"━━━ 回补第 {ch} 章 ━━━") try: result = generate_datacard_for_chapter(book=BOOK, chapter=ch) results["success"].append({ "ch": ch, "url": result.get("notion_url"), }) print(f"✅ 第 {ch} 章 → {result.get('notion_url', 'unknown')}\n") except Exception as e: results["failed"].append({"ch": ch, "error": str(e)}) print(f"❌ 第 {ch} 章失败: {e}\n") print(f"\n═══ 回补完成 ═══") print(f"✅ 成功: {len(results['success'])} 章 " f"· {[r['ch'] for r in results['success']]}") print(f"❌ 失败: {len(results['failed'])} 章 " f"· {[r['ch'] for r in results['failed']]}") if results["failed"]: print(f"\n失败详情:") for r in results["failed"]: print(f" 第 {r['ch']} 章: {r['error']}") if __name__ == "__main__": main() ``` ### ch16 重复页清理(手动) 由于 notion-323 和 notion-324 都是 "第16章·晨星拆书法V1.0" 页,需要妈妈手动判断保留哪一份: 1. 打开 Notion,进入《恶毒女配》全书拆解页 2. 对比 notion-323 和 notion-324 两个 ch16 页面,看哪个内容完整 / 格式正确 3. 删除内容不完整的那一份(右键 → Move to Trash) --- ## V5.5 部署步骤(今晚要跑的终端命令) ### Step 1 · 创建文件骨架 ```bash cd ~/chenxing-aircraft/tools mkdir -p prompts logs touch l5_engine_v5_5.py touch prompts/l6_character_snapshot_v5_5.py touch backfill_datacards_v5_5.py mkdir -p prompts && touch prompts/__init__.py ls -la l5_engine_v5_5.py prompts/l6_character_snapshot_v5_5.py backfill_datacards_v5_5.py ``` ### Step 2 · 粘贴代码进文件 用 nano / vim / VS Code 打开三个文件,把 1.1、1.2、1.3 三段代码分别贴进去。 ```bash # 推荐用 VS Code 打开整个目录: code ~/chenxing-aircraft/tools ``` ### Step 3 · 集成进 auto_dishu_v5_[5.py](http://5.py) 主脚本 基于 `auto_dishu.py.v5.4_frozen_20260610` 复制一份作为 v5.5 起点: ```bash cp auto_dishu.py.v5.4_frozen_20260610 auto_dishu_v5_5.py ``` 然后在 `auto_dishu_v5_5.py` 主脚本里: - 替换 L5 触发判断 → 调用 `l5_engine_v5_5.should_check_l5()` - 替换 L6 人物快照 prompt → 调用 `prompts.l6_character_snapshot_v5_5.render_l6_character_snapshot_prompt()` - 其他逻辑保持不变 (这步可能需要架构人格体读 v5.4_frozen 主脚本后给具体的 diff,先记着) ### Step 4 · 回补数据卡 ```bash python backfill_datacards_v5_5.py ``` ### Step 5 · L5 回放(Day 2 做) 用现有 1-59 章数据卡跑 L5 检测,期望产出 3-5 段弧段总结。 ### Step 6 · 续拆第 60 章 ```bash rm .STOP_DISHU python auto_dishu_v5_5.py --book "恶毒女配" --start-from 60 ``` --- ## 验证清单(V5.5 跑起来后逐项核对) | 项 | 期望 | 检查方式 | | --- | --- | --- | | L5 日志文件存在 | `logs/l5_trigger_恶毒女配.log` 每章一行 | `tail logs/l5_trigger_恶毒女配.log` | | 强制阈值真生效 | 累计 ≥15 章未触发 → 自动触发模式 B | 看日志里是否出现 "模式B · 强制阈值" | | L6 快照无 AI 口吻 | 不再有 "OK,收到" 开头 | 抓新生成的 L6 页面看首行 | | L6 快照有 why 列 | 表格 5 列齐全 | 抓新生成的 L6 页面看表头 | | 关键事件覆盖到末章 | 事件清单包含最后一章 | 抓新生成的 L6 页面看事件清单尾行 | | 数据卡完整性 | 1-59 章每章都有压缩数据卡 | `ls data/恶毒女配/datacards/ | | ch16 唯一性 | 只剩一份 ch16 拆书页 | Notion 搜索 "第16章·晨星拆书法V1.0" → 1 条结果 | --- ## ⚠️ Step 3 侦察后的方向修正(2026-06-11 01:00 附加) 主脚本扫描发现 3 处认知错位 · V5.5 修复方向需要调整: ### 🔄 修正 1: L5 强制阈值 v5.4 实际是 ≥5 · 不是漏接 v5.4_frozen 第 619-623 行实际代码: ```python chapters_since_l5 = len(datacards_since_last) if chapters_since_l5 < 3: return None should_check = prefilter_l5(datacards_since_last) if chapters_since_l5 >= 5: should_check = True # ← 强制阈值已在 5 接通 if not should_check: return None ``` 意思是从第 6 章起每章都会进入 AI 语义判断。**L5 不触发的真因在更深处**: AI 语义判断 (L5_ARC_DETECT_PROMPT, 第 631 行) 一直返回 NO 或解析失败。 → `tools/l5_engine_v5_5.py` 修复方向变化: - ❌ 不再需要 "接通强制阈值" - ✅ 改成 "修 L5_ARC_DETECT_PROMPT 内容 + 修 result 解析逻辑" - ⏸ 明天先看 v5.4_frozen 第 603-674 行的完整代码 ### 🔄 修正 2: scene_type 是封闭 19 词 · 不是自由词 v5.4_frozen 第 47 行 DISHU_PROMPT 已硬定枚举: ``` 开局 / 转折 / 爆点 / 日常 / 收束 / 铺垫 / 加速 / 缓冲 / 信息交代 / 冲突 磨合 / 战斗 / 升级 / 奇遇 / 修罗场 / 追妻 / 摆烂 / 隐藏实力 / 扮猪吃虎 ``` → `tools/l5_engine_v5_5.py` 修复方向变化: - ❌ 不需要 `categorize_scene_type()` 调 AI 兜底归类 - ✅ 直接用封闭词表静态映射到 4 大类即可: | 4 大类 | 包含的 scene_type | | --- | --- | | 爆发 | 冲突 / 战斗 / 爆点 / 修罗场 / 加速 | | 收束 | 收束 / 日常 / 缓冲 | | 平行 | 铺垫 / 磨合 / 开局 | | 转折 | 转折 / 信息交代 / 追妻 / 升级 / 奇遇 / 隐藏实力 / 扮猪吃虎 / 摆烂 | ### 🔄 修正 3: "人物快照" 是 L4 · 不是 L6 v5.4_frozen 第 88-89 + 585-598 行表明: ```python L4_SYSTEM = '你是晨星。扫描数据卡, 提取人物状态变化。' L4_SNAPSHOT_PROMPT = '扫描{label}内数据卡, 提取有实质变化的人物状态快照(不可回退的改变)。\n\n{datacards}' async def generate_l4_snapshot(...) title = f'{book_name}{milestone_label}节点 · 人物状态快照' ``` Notion 里 "X万字节点·人物状态快照" = **L4** 生成 (不是 L6)。L6 是另一组 (L6_LAYER1-4 = 人物层 / 世界层 / 主角成长层 / 节奏层)。 → 1.2 文件需要重命名: ```bash mv ~/chenxing-aircraft/tools/prompts/l6_character_snapshot_v5_5.py \ ~/chenxing-aircraft/tools/prompts/l4_character_snapshot_v5_5.py ``` 文件内容修复方向正确 (5 列 + why + 封闭标签词表 + 覆盖到末章), 只改文件名。 --- ## 📋 明天 Day 1 续的精确动作 1. 妈妈贴 v5.4_frozen 第 80-95 行 (L4 + DATACARD prompt 完整定义) 2. 妈妈贴 v5.4_frozen 第 600-680 行 (L5 完整模块包括 prefilter_l5 + check_and_trigger_l5 + L5_ARC_DETECT_PROMPT) 3. 架构人格体给精准 patch: - 重命名 `l6_character_snapshot_v5_5.py` → `l4_character_snapshot_v5_5.py` - 简化 `l5_engine_v5_5.py` 词表映射 (去掉 AI 兜底) - 给 `L5_ARC_DETECT_PROMPT` 新版本 (这是 L5 不触发的真因) - 给主脚本 sed/手动替换 diff --- ## 🔒 今晚 Day 1 进展锁定状态 (2026-06-11 01:00) | 阶段 | 状态 | 备注 | | --- | --- | --- | | Step 1 · 文件骨架 | ✅ | 3 文件 + logs/ + prompts/**init**.py | | Step 2 · 三段代码贴入 | ✅ | 110+75+45 行 · pbpaste 大法成功 | | Step 3 · 主脚本侦察 | ✅ | 扫描完成 · 发现 3 处方向错位 | | Step 3 · 集成主脚本 | ⏸ | 凌晨 1 点收工 · 明天接 | | Step 4-6 | ⏸ | 数据卡回补 / L5 回放 / 续拆 | **已就位资产 (不丢):** - `tools/l5_engine_v5_5.py` (110 行 · 词表逻辑明天简化) - `tools/prompts/l6_character_snapshot_v5_5.py` (75 行 · 明天重命名为 l4_) - `tools/backfill_datacards_v5_5.py` (45 行 · 完全可用) - `tools/auto_dishu_v5_5.py` (998 行 · v5.4_frozen 的复制 · 待 patch) - `tools/.STOP_DISHU` (锁文件) - `tools/auto_dishu.py.v5.4_frozen_20260610` (原版冻结) [auto_dishu V5.6 · 实时+脱机分离 · 妈妈版 · 一次性破局方案](auto_dishu%20V5%205%20%E4%BF%AE%E5%A4%8D%E6%96%B9%E6%A1%88%20%C2%B7%20L5%E5%BC%95%E6%93%8E%20+%20L6%E4%BA%BA%E7%89%A9%E5%BF%AB%E7%85%A7%20+%20%E6%95%B0%E6%8D%AE%E5%8D%A1%E5%9B%9E%E8%A1%A5/auto_dishu%20V5%206%20%C2%B7%20%E5%AE%9E%E6%97%B6+%E8%84%B1%E6%9C%BA%E5%88%86%E7%A6%BB%20%C2%B7%20%E5%A6%88%E5%A6%88%E7%89%88%20%C2%B7%20%E4%B8%80%E6%AC%A1%E6%80%A7%E7%A0%B4%E5%B1%80%E6%96%B9%E6%A1%88%206e1559d248f7468ba7c90f06668b5fcb.md)