Guanghu Domestic Migration a27e87cb99 chore: import sanitized domestic snapshot for REPO-007
Source snapshot: 97d7f0fae96dc04b7ddad56fc1db6a108ed662cc

[SEC-CLEAN] · pre-push-clean v1.0 · 109处敏感信息已自动转乱码
2026-07-17 15:59:55 +08:00

18 KiB
Raw Permalink Blame History

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

# 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

# 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

# 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 · 创建文件骨架

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 三段代码分别贴进去。

# 推荐用 VS Code 打开整个目录:
code ~/chenxing-aircraft/tools

Step 3 · 集成进 auto_dishu_v5_5.py 主脚本

基于 auto_dishu.py.v5.4_frozen_20260610 复制一份作为 v5.5 起点:

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 · 回补数据卡

python backfill_datacards_v5_5.py

Step 5 · L5 回放Day 2 做)

用现有 1-59 章数据卡跑 L5 检测,期望产出 3-5 段弧段总结。

Step 6 · 续拆第 60 章

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 行实际代码:

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 行表明:

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 文件需要重命名:

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.pyl4_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 · 实时+脱机分离 · 妈妈版 · 一次性破局方案