novel-postprocess
长篇 AI 小说(爽文/网文)写完 50~100 章后,直接进入"后处理流水线":先去名字疲劳,再统一排版,最后打包成手机可读的单文件 HTML 发邮箱。本 skill 把这套已验证流程(含脚本)固化下来,下次写长篇直接套用。
适用输入格式
每章一个 .md 文件,命名 第NN章_章节名.md,文件内含:
# 第NN章 章节名
【概要】...
【爽点】...
【情绪曲线】...
【正文】
(正文段落,逐行;【频道】xxx: / 【系统公告:...】 / 【悬赏者·【代号】】 等方头括号标记为系统/频道UI)
> 若你的章节不用这种模板,先做一次"格式归一"(统一为 【正文】 起头的段落流),再跑本流水线。
流水线总览
0. 竞争蒸馏→五维优化 → scripts/apply_optimizations.py(对照外网同类书→五维模板注入,跑前自动备份 _backup_opt/)
0.5 POV统一为第一人称 → scripts/fix_pov.py(段落级焦点追踪,1889处他/1964处林默→我,跑前自动备份 _backup_pov/)
1. 备份基线 → 复制所有章节到 _backup_xxx/(每步前必做,防误改可回滚)
2. 名字去重多样化 → scripts/name_dedup.py (自带备份 _backup_dedup/ + 残留校验)
3. 排版规范化 → scripts/format_text.py (自带备份 _backup_fmt/ + 残留校验,幂等可重跑)
4. 手机版阅读器 → scripts/build_reader.py (默认读当前工作目录;生成 深渊方舟_手机版.html)
5. 章节标题优化 → optimize-titles skill (全文最后一步,基于概要重命名+同步标题行,跑后重跑 build_reader)
6. 发邮箱 → send_qqmail.py --to 75536278@qq.com
> 调用方式(v1.1 起统一):脚本默认目录 = 当前工作目录(即你在哪个目录执行命令,就处理哪个目录),不再依赖脚本自身位置。标准用法:
> bash
> cd <你的项目目录,含 output/>
> python "C:/Users/xigua/.workbuddy/skills/novel-postprocess/scripts/format_text.py" output
> python "C:/Users/xigua/.workbuddy/skills/novel-postprocess/scripts/build_reader.py" output
>
> 也可省略参数(默认即当前目录)或显式传 <章节目录>。
0. 竞争性蒸馏与五维优化落地(scripts/apply_optimizations.py)
触发时机:写完长篇后,希望对标外网同类网文(番茄/起点/七猫等),做竞争性分析并把借鉴项落地到章节。
调用:python apply_optimizations.py <章节目录>(追加注意:脚本跑完后务必再跑一遍 format_text.py——注入文本可能引入 ASCII 引号残留)。
工作流三步走:
① 联网检索同类书 → ② 用 cangjie-skill 的 RIA-TV++ 框架蒸馏每本体裁/系统/合成机制/载具线/人设/循环/节奏/爽点/缺陷 → ③ 出对照分析.md(含8维矩阵+借鉴清单+Mermaid谱系图)
↓
用户确认借鉴清单 → 跑 apply_optimizations.py 注入章节
脚本内置五维模板(已填入《深渊方舟》实测数据,换题材时替换各区块):
| 维度 | 模板区块 | 对标来源 | 深渊方舟 落位 |
|---|---|---|---|
| P1 章尾微条 | STATE dict:各章排名/载具/能量值 → 章末 > ⚡ 合成能量 X/Y · 排名 Z · 载具 XX(等级) |
《炼金术士》章尾催化值 | 55章(46-100) |
| P1 频道人格 | PERSONALITY_INJECTS list:毒舌/乐子人/分析师/寻路人 4 人格在指定章注入对话 |
《河南人船长》20+群穿声量 | 23处 |
| P2 实物悬念 | CAN_YE_TEXTS dict:前代残页段落(齿轮符+378次循环+遗书),与方舟草图并列 |
《造神计划》前代日记 | 3章(21/45/76) |
| P2 弹幕吐槽 | DANMU_TEXTS dict:排名关键章弹幕式频道吐槽 |
暴击/融合类传播梗 | 5章(8/20/45/75/90) |
| P3 非载具代价 | COST_TEXTS dict:淡水膜老化/铜板名册/情报泄露等"不流血但要命"的损失 |
"只升不降"外的代价感 | 3章(52/59/87) |
注入策略: 章尾微条为机械追加(定位章末钩子前插入);频道人格在最近一个频道段后插入;残页/代价/弹幕为正文段落注入(优先插在相关关键词段落后,找不到则插在钩子前)。所有注入文本均为正文格式(首行空两格+全角引号+左对齐),与源文件一致。
换题材迁移: 只需整段替换 STATE(排名/载具数据按你的大纲填)、PERSONALITY_INJECTS(对话行换成你的世界观语言)、CAN_YE_TEXTS/DANMU_TEXTS/COST_TEXTS(情节段落换成你的伏笔设计)。换完即跑,跑完联调 format_text.py 清理残留引号。
校验: 关键词存在性 + ASCII 引号 = 0 + 读者 HTML 重建后章节数 = 预期。
1. 主角POV统一为第一人称(scripts/fix_pov.py)
触发时机:用户发现"他撑起身子"类第三人称描写主角错误,或生成时混用了人称。
调用:python fix_pov.py <章节目录>(跑前自动备份 _backup_pov/,跑后务必联调 format_text.py)。
算法核心:段落级焦点追踪
- 全局替换:
林默的→我的(无歧义) - 段落焦点判定(满足任一即视为林默聚焦):
- 段落含林默(保护区外)
- 前一段是林默聚焦(焦点延续)
- 段落开头是他+ 汉字(主语位置)+ 无其他角色名
- 段内有非保护区的他+ 无其他角色名 - 林默聚焦段内替换:
-他(排除他人/他们/其他)→我
-林默→我 - 保护区(独立计算,段内各自识别):
-【...】系统/UI 标签 → 整个标签内原样保留
- 全角对话引号"..."→ 引号内原样保留
- 其他角色名出现(韩雨/老猫头/独眼 等30+)→ 该段跳过,避免误改对话中"他白天还嘲笑人运气货"类指代他人/玩家的"他"
关键坑:
- 段内独立算保护区——若用整章的保护区列表去比段内 m.start() 会位置错位(早期 bug,已修)。
- 【】标签行不重置焦点——【系统公告】 这类行处理时原样返回,让焦点跨标签行延续。
- 排除词: TA_PAT = (?<!其)他(?!人|们) ——跳过"其他""他人""他们"。
换题材迁移:修改 other_chars 正则清单(覆盖全书所有有名角色+代号),把 林默 替换为你的主角名。
校验标准:剩余"他"应全在"其他角色焦点段"或"对话/系统标签保护区"内=0 误改;剩余"林默"应全在【】系统消息内。深渊方舟实跑 1889 处他 + 1964 处林默 + 54 处林默的→我的,119 个他全部正确保留(对话中指代他人/玩家)。
2. 名字去重多样化(消除审美疲劳 + 守护人设)
核心判断:先把名字分三类,处理方式完全不同。
| 类型 | 判定 | 处理 | 误改风险 |
|---|---|---|---|
| A. 纯路人匿名ID | 频道昵称/说话人,按"段"复用(冷面、夜行者、海上漂、北风…跨10~19章反复当不同炮灰) | 真轮换:每个旧名在每一章换一个不同新名,章内一致、跨章最大化区分 | 低 |
| B. 固定角色 | 主角团、具名配角、代号猎人(老周=北屿首领、老猫=具名配角、独眼=【悬赏者·独眼】) | 称呼多样化:同一人不换身份,但跨章交替用多种称呼(老周/铁匠老周/北屿周首/周铁匠) | 硬换身份会穿帮 |
| C. 成语/比喻用法 | "硬骨头啃穿""高排名的孤狼"等 | 保留不碰 | 当昵称误改会破坏语义 |
关键步骤与坑:
1. 脚本已内置《深渊方舟》实测数据,直接运行即等效多轮去重——scripts/name_dedup.py 已填好:PROTECT(主角团16名)、ANON_FULL/ANON_SPEECH(16个复用旧昵称)、ANON_POOL(217个荒海风新名实测池,去重后)、FIXED(老周/阿土/老猫/独眼 4组称呼变体)。换题材时只需整段替换这四个区块。
2. A 类真轮换:旧匿名ID → 按章号哈希 hash(旧名+章号) 确定性映射到 ANON_POOL 下标(章内一致、跨章差异、可复现、零碰撞)。
3. B 类称呼多样化:称呼池必须含本体(如独眼池含"独眼")以保辨识;用占位符法替换(先全部换成 \x00N\x00 再还原)防自指污染。
4. 成语/比喻守护(脚本内置):硬骨头("硬骨头啃穿")、孤狼("高排名的孤狼")、海上漂("海上漂着"动词)、冷眼旁观(成语)仅"说话语境"(后接说/道/:/喊…)才替换,其余保留;冷眼 全替换但用 冷眼(?!旁观) 负向前瞻跳过成语子串。
5. 结构守护:代号类(【悬赏者·【独眼】】)替换后校验标签闭合(accept 单层 】 与嵌套 】】)。
6. 主角团建 PROTECT 名单,替换时 skip,零误伤。
7. 必做:每轮前脚本自动 cp 整章到 _backup_dedup/;若误改用"最新正确基线"还原(注意备份目录先后,_backup_semi 是 semi 轮之前、会撤销后续成果)。
校验标准: 旧昵称残留扫描——冷眼/冷眼客/冷面/夜行者等应=0;而硬骨头(成语啃穿)、孤狼(比喻)、海上漂(动词)、冷眼旁观(成语) 的非说话语境实例会正确保留,残留非0属正常,需区分"应保留成语"与"漏替昵称";主角团计数零变化。奇数引号行=0(见排版节)。
3. 排版规范化(scripts/format_text.py)
脚本自带自动备份 + 残留校验,且幂等可重跑(缩进检测 startswith(INDENT)、引号只认 ASCII ",重跑零副作用)。
调用:python format_text.py [章节目录] [--intro 作品介绍.md路径](默认=当前工作目录;处理前备份到 <目录>/_backup_fmt/)。
三项改动,全部可脚本化、可重跑:
| 项 | 做法 | 注意 |
|---|---|---|
| 首行空两格 | 正文每段首行加 \u3000\u3000(两个全角空格)。U+3000 在 HTML 不被折叠,缩进天然保留 |
【正文】 标签行本身不缩进;空行保留 |
| 全角中文引号 | ASCII 直引号 " → 按逐行奇偶交替转 "(开, U+201C)/"(闭, U+201D) |
逐行重置奇偶,因引号不跨行;校验"奇数引号行=0"即零错配 |
| 左对齐 | 阅读器 CSS:#content p{text-align:left};原 justify/center 一律改 left |
系统/频道提示行(p.note)也改 left |
为什么用全角空格而非 CSS text-indent: 直接改源 .md,源文件本身也规范(用户可能直接读 .md),且避免与 CSS 双重缩进。
作品介绍.md(含 #/##/- 结构)只转全角引号、不缩进。
4. 手机版 HTML 阅读器(scripts/build_reader.py)
调用:python build_reader.py [章节目录](默认=当前工作目录;封面需在 <目录>/cover/final/;跑后打印封面加载情况与 PASS/CHECK 校验)。
单文件、自包含、可离线、手机浏览器直接打开。
结构:
- 封面图用 PIL 降采样到 720 宽 JPEG → base64 内嵌(5 张:主封面 + 四卷分卷,或按你项目调整 COVER_FILES 映射)。
- 解析 100 章 【正文】 段为 <p>;匹配 ^【[^】]*】$ 的行作 <p class="note">(居中/左对齐的系统提示样式)。
- 作品介绍 md 轻量转 HTML(#→h2、##→h3、-→li)。
- 数据 const DATA = {...} 内嵌 JSON(ensure_ascii=False 保中文)。
阅读功能(纯前端 JS):
- 封面页 → 目录(分卷折叠 + 100 章跳章 + 作品介绍卡)→ 阅读页
- 左右滑动切章;底部上一章/下一章
- 翻页/滚屏双模式(flipMode 开关;翻页模式点右半屏下翻/左半屏上翻,带"屏 k/N"提示)
- 日间/夜间切换
- localStorage 记忆阅读进度(章节 + 滚动位置)
CSS 要点: 浅色书页底 #f3ece0、夜间 #14161a;正文 line-height:1.95; font-size:17px; padding:18px;body{overflow:hidden} + 各 view position:fixed; inset:0 实现三视图切换。
5. 发邮箱
python "C:/Users/xigua/.workbuddy/send_qqmail.py" \
--to 75536278@qq.com \
--subject "《书名》手机版阅读器(含封面+目录+百章)" \
--body "正文说明" \
--files "绝对路径/深渊方舟_手机版.html"
> 用户常驻规则:凡是我生成的任务文件,发一份到 75536278@qq.com。HTML 额外需手机版可看。
铁律(爽文连载守护,来自 novel-generator 角色 + 十读者试读反馈)
- 杀圣母不墨迹(用户硬要求)
- 已死角色不复活、不擅改已确立世界观
- 载具/实力只升不降、金手指有限制不无限开挂
- 频道人数递减、无"第X章"破壁(正文不出现"第N章"字样)
- 伏笔前后呼应、对话体现角色个性差异、反派不能太蠢
防重复写作铁律(十读者逐章试读沉淀,v1.4.0)
- POV转换检查:fix_pov.py的other_chars必须覆盖全部出场有名角色(含反派/龙套/势力名)。跑完后扫描反派名+主角"我"共现的段落,防止敌方动作误转为第一人称。
- 改字后联调排版:每跑完任何改字脚本(name_dedup/fix_pov/apply_optimizations)立即联调format_text.py。
- 频道角色具名化:每10章至少2-3个具名频道角色发声,用内置常驻人格(毒舌/乐子人/分析师/寻路人)替代随机匿名路人。
- 女性角色独立行动:每卷至少一次女性角色独立高光,不依赖主角推动(小雪/林姐/徐姐最敏感)。
- 合成挫折:每卷至少一次合成失败/过载/副作用——代价感使爽点更真实(赵伯/阿杰零容忍零代价)。
- 开篇技术密度控制:前5章技术术语穿插出现,每次配人话翻译或即时实用成果(小雨/徐姐是弃书高风险)。
- 人设立后少贴标签:杀圣母后避免后续频繁用"圣母/道德绑架"等关键词——靠行动维持,不靠重复口号。
- 主语精简:段落内首个"我"保留作锚点,后续连续句首的冗余"我"删除("我撑起身子"→"撑起身子")。跨角色段、对话引述、宾语位置的"我"保留。全章核验标准:连续三句以上句首都带"我"= 未通过。
- 长段拆分:正文段落超200字需按。切分,每段控制在150-200字,手机一屏不堆砌。分行触发场景/动作/视角切换。
- 行动定边界,不宣言"规矩":第一章"立规矩"是典型的宣言式爽文陷阱——读者喜欢的是"用行动让踩线的人后悔",不是"对十万人大喊你们都该守我的规矩"。把"只容规矩人/背刺者死"类命令式语句改为"这就是我的活法/合得来则友、合不来则分、踩上来则敌"的个人风格声明。人物形象通过推动事情来丰满,不过多重复描述已确立的人设特征。
- 生成后扫描 U+FFFD:每次跑完格式/排版流水线后扫描所有 .md 文件中是否含 U+FFFD 替换字符(这些是早期 AI 生成或编码转换时损坏的占位符,渲染为"豆腐方块")。一旦发现连续两个 FFFD 用","恢复,单个 FFFD 用"。",恢复。这是"生成后质量检查"必跑项,不能让读者看到豆腐方块。
- 生成后跑 AI 味扫描:完成所有修正后用 ai-flavor-remover 扫描全书。重点核验:感叹号连用=0、那一刻/这一瞬单章≤2、破折号中位≤10、重特征(填充词/泛泛结论/虚假范围)=0。注意区分"AI味"与"网文味"——战斗三连排比、角色口头禅、"据说"等角色视角传闻是正常表达,不要误删。
验证清单(每步后跑)
- [ ] 主语精简:抽查3-5章正文,连续句首"我"应≤2次/段
- [ ] 生成后 U+FFFD 扫描 = 0(替换字符=豆腐方块=损坏)
- [ ] POV 统一:剩余"他"应全在保护区或其他角色焦点段=0误改;剩余"林默"应全在【】系统消息内
- [ ] 五维优化:各维度关键词落位确认;跑后联调
format_text.py清理注入引入的引号残留 - [ ] 旧昵称残留扫描 = 0;主角团计数零变化
- [ ] ASCII 直引号残留 = 0;奇数引号行 = 0
- [ ] 正文所有内容行以
\u3000\u3000开头(仅标签行例外) - [ ] 重建 HTML 后 JSON 解析通过、章节数=预期、封面 base64 非空
- [ ] 邮件 SENT OK