第 1 卷三集全部转为成稿(分块扩写 + AI 腔定点返修),文档同步

- 每集两稿:NNN-….md 为成稿(9492/11260/11869 字),NNN-….skeleton.md 为过闸门的骨架稿
- 新增 expand.repair_style:只重写含 AI 腔标记的段落(本例出现了 14 处破折号),
  三集硬伤密度 1.2/1.0/1.5 → 0.1/0.3/0.0(每千字)
- episode 命令接入定点返修;expand 命令保留 A/B 对照用法
- README 同步文笔层、本土化、子命令、以及本轮新踩的坑(\b 盲点、枚举大小写、自制小节标记)
- 第 3 集骨架连换三条线索仍未过闸门(真实拒绝),其成稿基于既有骨架扩写
This commit is contained in:
Chen Yi
2026-10-05 23:14:24 +08:00
parent 16b476167e
commit 70ce758e8d
11 changed files with 1470 additions and 458 deletions
+51 -12
View File
@@ -19,7 +19,10 @@
世界大事只在影响到主角时提及
⑤ 自评闸门 5 个维度各 0–5 分,总分 <12 判"流水账" → 先重写一次 →
仍不合格就换下一条线索,并把放弃原因写进 notes/
⑥ 发布 git commit & push 到本仓库(原始史料与存档不入库)
⑥ 文笔层 骨架稿扩写成成稿:按年份场景分块扩写(目标 8000 字),
全程做 AI 味机检、标点规范化,并用专名白名单防幻觉
⑦ 本土化 英文专名换成中文译名:人名=音译+绰号意译,地名整体意译
⑧ 发布 git commit & push 到本仓库(原始史料与存档不入库)
```
## 二、这一卷的主角
@@ -27,7 +30,7 @@
| | |
|---|---|
| 世界 | Mon Sagus(The Plane of Dawn),Medium 129×129 / 250 年历史 |
| 主角组 | Guspu Frillyknots(鹰人掮客)、Ral Fastenhatchets(矮人)、Alath Blottedmine(矮人) |
| 主角组 | 古斯普·褶结(Guspu Frillyknots,鹰人掮客)、拉尔·缚斧(Ral Fastenhatchets,矮人)、阿拉斯·污矿(Alath Blottedmine,矮人) |
| 线索 | 46 年间一个掮客反复收买、构陷一位管货栈的矮人,屡屡失手 |
| 选线依据 | 挖掘器排序第一名:有效互动 11 次、转折 11 次、阴谋×8 + 构陷×3 |
@@ -37,15 +40,21 @@
| 路径 | 内容 | 入库 |
|---|---|---|
| `output/volumes/v<N>/chapters/` | 连载正文 | ✅ |
| `output/volumes/v<N>/chapters/` | 连载正文:`NNN-….md` 是成稿,`NNN-….skeleton.md` 是过闸门的骨架稿 | ✅ |
| `output/volumes/v<N>/cast.md` | 人物表(表格 + 小传) | ✅ |
| `output/volumes/v<N>/timeline.md` | 迷你年表 | ✅ |
| `output/volumes/v<N>/figures.md` | 人物索引(按史料出场次数排序) | ✅ |
| `notes/switched-threads.md` | 被放弃的线索及原因 | ✅ |
| `dfannals/` | 管道源码 | ✅ |
| `prompts/biographer.md` | 写作规范(决定视角、边界、篇幅) | ✅ |
| `prompts/literary-expander.md` | 扩写规范(逐事件展开、禁用词、标点) | ✅ |
| `dfannals/glossary.py` | **冻结的译名表**(词干/姓/地名/种族) | ✅ |
| `dfannals/localize.py` | 专名本土化(渲染层,不改史料) | ✅ |
| `dfannals/deslop.py` | AI 味机械诊断(禁用词/句式/标点,按千字密度) | ✅ |
| `dfannals/normalize.py` | 标点与结构清理(引号、重复段、模型自加的小节标记) | ✅ |
| `dfannals/expand.py` | 分块大量扩写 + 专名白名单 + 段级返修 | ✅ |
| `scripts/` | 安装、部署、驱动、启动器 | ✅ |
| `tests/` | 回归测试(解析、切集、闸门、事实校验) | ✅ |
| `tests/` | 回归测试(解析、切集、闸门、事实校验、机检、清理、本土化) | ✅ |
| `data/exports/` | 原始 legends 数据与预处理缓存 | ❌ |
| `data/state.json`、`data/threads.json`、`data/selection.json` | 进度、候选、选线依据 | ❌ |
@@ -57,7 +66,8 @@
| DFHack | **53.16-r2**,Windows 版经 `dfhooks.dll` 注入,不必替换启动器 |
| Python | 3.12 |
| defusedxml | `sudo apt install -y python3-defusedxml` |
| 写作/自评模型 | 经 `https://wb2api.hajim1.art/v1`(OpenAI 兼容)调用 `cn:deepseek-v4-pro` |
| 写作/自评模型 | 经 `https://wb2api.hajim1.art/v1`(OpenAI 兼容)调用 `cn:deepseek-v4-pro`(骨架与自评) |
| 扩写模型 | 同上网关的 `cn:minimax-m3`:叙事向模型写场景更贴地,推理向模型天然偏总结陈词 |
| 发布 | 现有 `~/.ssh/id_ed25519`(已注册到 Gitea 账号 gitadmin) |
**启动器**:一律用 `scripts/dfannals ...`,不要直接 `python3 -m dfannals.cli`。
@@ -90,13 +100,17 @@ scripts/dfannals episodes --thread 1
# 5) 生成人物表
scripts/dfannals cast --thread 1
# 6) 逐集产出(自动过闸门;不合格自动重写,再不合格自动换线索)
# 6) 逐集产出:骨架过闸门 → 分块扩写成成稿(骨架另存 .skeleton.md)
scripts/dfannals episode --index 1
scripts/dfannals episode --index 2
scripts/dfannals episode --index 3
# 7) 只跑文笔层:拿已有的某一集骨架做大量扩写(A/B 对照,不覆盖原稿)
scripts/dfannals expand --index 1 --target 8000
```
实测:**单集约 2–4 分钟**(写作 1 次 + 自评 1 次,若判平淡会多一次重写),篇幅 800–1500 字。
实测:**单集 3–8 分钟**(骨架写作 1 次 + 自评 1 次 + 分块扩写 5 次,若判平淡会多一次重写);
骨架稿 800–1500 字,成稿约 8000–12000 字。
## 七、配置在哪
@@ -107,8 +121,11 @@ scripts/dfannals episode --index 3
| 切集参数(间隔 >2 年断开、每集预算) | `dfannals/episodes.py` 顶部常量 |
| 自评维度、阈值 12、重写/换线上限 | `dfannals/gate.py` 的 `DIMENSIONS` / `THRESHOLD` / `MAX_REWRITES` / `MAX_CANDIDATES` |
| 视角、文风、演绎边界、篇幅 | `prompts/biographer.md` |
| 扩写规格(逐事件展开、禁用词、标点) | `prompts/literary-expander.md` |
| 译名(人名/地名/种族) | `dfannals/glossary.py`——**译名必须冻结**,否则同一角色每集会换名字 |
| 扩写目标字数、分块大小、AI 味规则 | `dfannals/config.py` 的 `EXPAND_*`、`dfannals/deslop.py` 的 `HARD_WORDS`/`PATTERNS` |
| 同人同类事件的重复上限 | `dfannals/volume.py` 的 `PER_KEY_CAP` |
| 模型与网关 | `dfannals/config.py` 的 `PROVIDER` / `MODEL` |
| 模型与网关 | `dfannals/config.py` 的 `PROVIDER` / `MODEL` / `LITERARY_MODEL` |
| 发布目标、仓库名 | `dfannals/config.py` 的 `GITEA_*` |
**API key 不入库**:运行时从 `~/.pi/agent/auth.json` 读取,只在内存中,不打印、不落盘、不进 git。
@@ -121,10 +138,14 @@ scripts/dfannals index 解析 exports → 年表 + 人物索引
scripts/dfannals threads 候选线索与信号明细
scripts/dfannals episodes --thread N 某条线索的剧集规划与预算
scripts/dfannals cast --thread N 人物表(--no-bios 只出表格)
scripts/dfannals episode --index N 产出第 N 集(--dry-run 不落盘)
scripts/dfannals episode --index N 产出第 N 集(骨架 + 扩写)
--no-expand 只出骨架|--target 8000|--model 换模型
--dry-run 不落盘、不推送
scripts/dfannals expand --index N 只跑文笔层:把已有的一集大量扩写(A/B 对照)
```
测试:`/usr/bin/python3 tests/test_factcheck.py`、`test_episodes.py`、`test_gate.py`。
测试:`for t in tests/test_*.py; do /usr/bin/python3 "$t"; done`
(六个文件:解析与事实校验 12 项、切集 7 项、闸门 6 项、AI 味机检 7 项、标点清理 9 项、专名本土化 13 项)
## 九、世界是怎么造出来的
@@ -155,6 +176,12 @@ scripts/dfannals episode --index N 产出第 N 集(--dry-run 不落盘)
| `dfhack-run "cmd a b"` 报 not recognized | 参数要分开传:`dfhack-run dfannals/click 10 2`,不要整体加引号 |
| Bay12 下载只有 ~10KB/s;GitHub 直连 0 字节 | 官网无国内镜像,放后台断点续传;GitHub 用 `https://gh-proxy.com/` 前缀 |
| 自定义 init 脚本不生效 | 53.16 只执行固定几个 init 文件。改用 `dfhack-run` 从外部驱动更可靠 |
| 编造的地名混过校验 | 正则里的 `\b` 在 Unicode 下把汉字也当词字符,`在Mistbreath的` 这种**紧贴汉字的英文名根本不会被检查**。改成前后看「是不是英文字母」后才抓得住 |
| 校验器把 `Yeti` 报成可疑 | 种族值在史料里是全大写(`YETI`),展示形式是 `Yeti`。只改首字母不转小写就永远对不上;现在两种写法都收,且比较时忽略大小写 |
| 一集里冒出 14 处破折号 | 扩写提示词明确禁用也拦不住。确定性替换会读着别扭,改为**定点返修**:只把命中的段落发回去重写(硬伤密度 1.5→0.0/千字) |
| 正文里出现 `## 一`、孤立的「七」 | 分块扩写时模型每写一块就自制编号小节。清理阶段按「非首行的标题行/纯数字行」删除 |
| 同一句被写两遍、段落跨块重复 | 分块之间会重写上一块的结尾。清理阶段合并相邻重复段落与段内重复句子 |
| 扩写稿里满屏英文引号 | 模型用 ASCII `"`,统计对白时会误判成「没有对白」。按出现顺序配对换成中文引号 |
## 十一、闸门到底能不能判出流水账
@@ -168,15 +195,27 @@ scripts/dfannals episode --index N 产出第 N 集(--dry-run 不落盘)
阈值 12 在两者之间,区分有效。注意:阈值是校准出来的,不是拍出来的——
第一版用"梗概体"当好稿样本时只得 11 分,换了场景级样本才验证出尺度没偏严。
**真实的拒绝案例**:第 3 集的骨架连换三条线索、每轮重写一次,仍然没过
(11→10、8→11、7→8 分)。管道按设计报"需要人工干预"而不是硬推。
这说明闸门在"薄材料"上会持续拒绝——正确的反应是回退到已有骨架再扩写,
而不是把阈值调低。
## 十二、当前状态与限制
- **已完成**:第 1 卷 3 集上线 + 人物表 + 年表;旧的两章流水账已用普通提交删除(历史保留)
- **已完成**:第 1 卷 3 集成稿(9492 / 11260 / 11869 字)+ 人物表 + 年表;
旧的两章流水账已用普通提交删除(git 历史保留)
- **每集两稿**:`NNN-….md` 是成稿,`NNN-….skeleton.md` 是过闸门的骨架稿,便于溯源
- **定时任务未启用**:cron / 开机自启等稳定运行一段时间后再落地,目前按需手动执行
- **原始数据不入库**:`data/` 已在 `.gitignore` 中
- **已知局限**:
- 关系图的边来自成对事件,独狼型人物(独自创作、被自然杀死)进不了线索挖掘
- 多参与者事件(比赛、集体迫害)会把同场者两两连边,圈子的"关系"偏共处而非互动
- 事实校验只拦得住凭空专名;动机、心理、对白全是允许的演绎,没有自动防线
- 事实校验只拦得住凭空专名,**且只认英文形态**:本土化之后,编造的中文名只能靠
"发给模型的白名单 + 段级返修"来防,没有机械防线
- 闸门判的是**骨架稿**,不判扩写后的成稿。成稿有机械指标(AI 味密度、专名校验),
但戏剧性没有自动闸门
- 译名表是手工冻结的:遇到表外的新名字会原样保留英文并进待译清单,
不会自己造一个音译(宁可难看,也不让同一角色每集换名字)
## 十三、声明