594cf48a1a046ec0dd57b6196122b4a4150cc59b
矮人要塞编年史 · Dwarf Fortress Annals
把《矮人要塞》(Dwarf Fortress) 世界生成的历史数据,写成中文战报体连载故事,自动推送到这个仓库。
本仓库是成品仓:放小说章节、迷你年表、人物索引,以及生成它们的代码。
原始 legends 导出数据(数百 MB)与游戏存档留在本地,不入库。
一、它是怎么工作的
Windows 侧:Dwarf Fortress 53.16 + DFHack 53.16-r2
│ ① 生成世界(Medium 129×129 / 250 年历史)
│ ② 进入 legends 模式,导出 legends.xml + legends_plus.xml
▼ ③ 经 /mnt/c 文件交换(不需要 WSL↔Windows 网络互访)
WSL2 Ubuntu 侧管道
│ ④ 预处理:剔除 DF 塞进名字里的非法控制字符,CP437 → UTF-8
│ ⑤ 解析史料:40 万条事件 / 4 万位人物 → 事件流 + 年表 + 人物索引
│ ⑥ 切章:10 年一窗,每窗按显著度取前 12 条,同类事件≤4(避免整章讣告)
│ ⑦ 写作:要塞编年史官口吻 / 战报体 / 允许自由演绎
│ ⑧ 校验:正文专名必须能在史料里找到,可疑处标注但不阻断
▼ ⑨ git commit & push
Gitea:gitadmin/dwarf-fortress-annals(本仓库)
二、目录
| 路径 | 内容 | 入库 |
|---|---|---|
output/volumes/v<N>/chapters/ |
连载正文 | ✅ |
output/volumes/v<N>/timeline.md |
迷你年表 | ✅ |
output/volumes/v<N>/figures.md |
人物索引 | ✅ |
dfannals/ |
管道源码 | ✅ |
scripts/ |
安装、部署、驱动脚本 | ✅ |
prompts/chronicler.md |
写作规范(决定文风与人设) | ✅ |
tests/ |
事实校验回归测试(含合成史料样例) | ✅ |
data/exports/ |
原始 legends 数据与预处理缓存 | ❌ |
data/state.json |
进度状态(当前世界 / 卷号 / 已写章节) | ❌ |
data/GRILL-ME.md |
立项访谈记录 | ❌ |
三、依赖
| 依赖 | 版本 / 说明 |
|---|---|
| Dwarf Fortress | 53.16 免费版(Bay 12)。必须与 DFHack 严格配对 |
| DFHack | 53.16-r2。Windows 版经 dfhooks.dll 注入,不需要替换启动器 |
| Python | 3.12(用到 X | None 语法) |
| defusedxml | sudo apt install python3-defusedxml(解析不可信 XML 用) |
| 写作模型 | 经 https://wb2api.hajim1.art/v1(OpenAI 兼容)调用 cn:deepseek-v4-pro |
| 发布 | 现有 ~/.ssh/id_ed25519(已注册到 Gitea 账号 gitadmin) |
无独显也能跑:DF 本体不吃 GPU;写故事走云端 API,不做本地推理。
四、安装
# 1) Windows 侧装游戏与 DFHack(经 WSL 的 /mnt/c 操作,免管理员权限)
scripts/install-df.sh --download # Bay12 与 GitHub 在国内较慢,会断点续传
# GitHub 直连不通时改用镜像:https://gh-proxy.com/<原地址>
# 2) 装依赖
sudo apt install -y python3-defusedxml
# 3) 把驱动脚本部署进游戏目录
scripts/deploy-dfhack.sh /mnt/c/Users/$USER_WIN/DwarfFortress
五、产出一卷(生成新世界 → 导出 → 连载)
# 1) 启动游戏,停在标题界面
"/mnt/c/Users/23518/DwarfFortress/Dwarf Fortress.exe" &
# 2) 自动生成世界(Medium / 250 年)并导出 legends 数据到 data/exports/
scripts/make-world.sh 3 3
# 3) 一条命令产出下一章并推送
python3 -m dfannals.cli run
make-world.sh 全程从外部驱动游戏,不需要人工点击:
- 用
dfannals/screentext把画面读成字符网格,scripts/df-screen.py按文字定位按钮 - 用
dfannals/click模拟鼠标、dfannals/setparams直接设定世界参数 - 用
dfannals/legends进入 legends 模式(做法取自 DFHack 官方open-legends.lua) - 用
dfannals/act/devel/send-key发按键
备选:DF 自带命令行世界生成器
"Dwarf Fortress.exe" -gen 1 RANDOM "MEDIUM ISLAND", 可静默生成并导出,但不留存档,因此无法继续进 legends 导出,只适合快速验证参数。
六、子命令
python3 -m dfannals.cli status # 当前世界 / 卷号 / 已写章节
python3 -m dfannals.cli plan # 只看切章方案,不调模型
python3 -m dfannals.cli index # 解析 exports → 年表 + 人物索引
python3 -m dfannals.cli next # 生成下一章(--dry-run 只生成不推送)
python3 -m dfannals.cli run # index + next,一条命令走完
python3 tests/test_factcheck.py # 校验器回归测试
实测:约 2.5 分钟 / 章(解析 33 秒 + 模型 1–2 分钟),单章 1000–1400 字。
七、配置在哪
| 想改什么 | 改哪 |
|---|---|
| 世界大小 / 历史长度 | scripts/make-world.sh 的两个档位参数 |
| 切章粒度(几年一章、每章几条、同类上限) | dfannals/slice.py 顶部常量 |
| 哪些事件值得写(显著度评分) | dfannals/score.py 的 NARRATIVE 表 |
| 文风、人设、演绎边界 | prompts/chronicler.md |
| 章节字数区间 | dfannals/config.py 的 CHAPTER_MIN/MAX_CHARS |
| 模型与网关 | dfannals/config.py 的 PROVIDER / MODEL |
| 发布目标、仓库名 | dfannals/config.py 的 GITEA_* |
API key 不入库:脚本运行时从 ~/.pi/agent/auth.json 读取,只在内存中,不打印、不落盘、不进 git。
八、踩过的坑(失败排查参考)
| 症状 | 原因与解法 |
|---|---|
not well-formed (invalid token) |
DF 在名字里嵌了 \x10/\x11 等 CP437 控制字符,XML 1.0 禁止。管道开头的预处理会剔除并转 UTF-8,缓存到 data/exports/clean/ |
| 事件数莫名翻倍 | 预处理缓存若与原始文件同目录,会被 *.xml 扫描当成第二份史料。缓存因此放在子目录 |
素材里出现 HF#-1、Site#-1 |
DF 用负数占位表示"无此项"。查询接口对负 id 返回空串,渲染时跳过 |
| 校验器把真名报成可疑 | 史料里是全小写程序名(yemi deermoths),正文做了首字母大写。已知名字集同时收录两种形式(有回归测试) |
dfhack-run "cmd a b" 报 not recognized |
参数要分开传:dfhack-run dfannals/click 10 2,不要整体加引号 |
| 切出几千章 | 40 万条事件里绝大多数是流水账(change hf state 7.3 万条)。必须先按显著度筛选,再按时间窗分桶 |
| Bay12 下载只有 ~10KB/s | 官网无国内镜像。放后台断点续传即可(16MB 约 27 分钟) |
| GitHub release 下载 0 字节 | 直连被断。用 https://gh-proxy.com/ 前缀 |
| 观测 / init 脚本不生效 | 53.16 只执行固定几个 init 文件。改用 dfhack-run 从外部驱动,更可靠 |
九、写作设定
- 叙述者:世界档案库里的编年史官——少数亲历,多数事后依据档案与口述追述,因此对陈年旧账有看法
- 口吻:战报体,严肃的骨架 + 诙谐的血肉,允许吐槽
- 语言:中文;专有名词保留英文原文(首字母大写以便阅读)
- 演绎边界:允许虚构对白、心理、场景;不许发明史料里没有的人名地名,不许改写史实骨架(年份、参与者、胜负)
- 连载:同一世界长期连载;一部史写完则用
make-world.sh生成新世界开新卷
十、当前状态与限制
- 已完成:3 卷框架 + 第 1、2 章上线(Mon Sagus 世界,1–250 年,26 章规划)
- 定时任务未启用:
cron/ 开机自启等稳定运行一段时间后再落地;目前按需手动执行 - 原始数据不入库:
data/已在.gitignore中 - 诚实说明:世界创建各步骤均已逐步实测通过,并已整合为
make-world.sh;但该脚本尚未作为单次完整调用重跑验证(单次约 15 分钟),首次使用时请留意各阶段日志
十一、声明
《Dwarf Fortress》版权归 Bay 12 Games 所有。本仓库只包含由其生成数据的衍生文本, 不含游戏本体、美术资源或存档。
Languages
Python
92.3%
Lua
4%
Shell
3.7%