← dsh-novel-writer 技术文档

技术文档

当前版本 7.3.4 · 更新记录见 CHANGELOG(下方「版本说明」)

dsh-novel-writer 的 22 个工具全部在本地运行,不使用在线大模型:所有指标由规则统计与本地 ONNX 推理得出。本组页面面向二次开发与集成方,逐工具说明参数契约、计算原理、约束边界与源码位置。

阅读约定

每个工具页固定按以下顺序组织,可以跳读:

  • 参数:名称、类型、是否必填、语义与校验方式;
  • 输出结构:返回体字段与渲染层行为;
  • 计算原理:公式、词表、阈值与取舍,尽量给出可直接对照源码的表达式;
  • 约束与边界:对调用方模型的限制、被静默跳过的情形,以及跨版本不可比之处;
  • 源码位置:lib/*.js 中的对应实现。
关于数值。从 v4.3.0 起,本插件做了一轮口径整修(统一句末符、清洗动作词表、抽象度改最长匹配、修饰密度增加停用词、六维基线 μ=0 改绝对尺度判定等)。同一本书在新旧版本下的报告数值不可直接比较,涉及具体差异的页面会单独标注。

版本说明

每条一行,展开可读完整说明;最新一版默认展开。完整历史见 CHANGELOG。

  • 7.3.4 · 2026-11-14 收干净 7.3.3 之后又核出的一轮:3 条上一轮漏修(诊断读数上限文案、连贯性审计的大纲方向行判据、MCP 死分支)+ 4 条 7.3.3 自己带出来的(BODY_TOO_LARGE 死导入导出、只删空目录时 remaining/skipped/审计日志不如实、空目录已消失时文案自相矛盾、面板冷启动拿已删除的书名去要数据)

    没有新功能,全是「说错的话」和「没说的话」。最实际的一条是连贯性审计的大纲对照: 它仍写死 /^- \d+ /,而 7.3.3 已经把钩子承接与 novel_outline 统一到 OUTLINE_LINE_RE——于是用 * 12 … / ## 12 … / - 第12章 … / 行首带缩进写大纲的作者会被误报「没有方向行」。诊断报告的 「读数上限 2000 条」也是无条件写的,而明细模式实际只读 200 条。 另有一批是 7.3.3 自己带出来的:把 #6/#7 挪回 core 单点实现后, BODY_TOO_LARGE 的导入悬空了(连带两处说明变成假话);「只删空目录」的提前返回分支 把 remaining 写死成 0、不标 partial、还绕开了 delete_apply 审计日志;空目录在预览后已消失时回「未能移除(见失败项)」而 failed 是空数组; 面板冷启动会拿一本 localStorage 里记住、其实已被删除的书名去要数据,且落定后不会纠正。

    7.3.3 · 2026-11-13 按外部源码审计报告修完 34 条 + 它漏掉的 16 条:GET 请求能删书、删书路由跟随局域网开关、POST /state 写错书库根、设定表读失败仍拿默认词表出待办、取消被记成数据故障、裸台词行号退化成 0、scanSlop 不归一换行(工具数仍 22)

    这一版来自一份外部源码审计报告(36 条)。它的准确度比上一份高:34 条有真问题, 只有 2 条我判「影响不成立」。最该先看的是三条数据安全:GET 请求真的会删书 (?action=apply 移进回收站、?action=purge 不可恢复,而文档写着「破坏性动作一律走 POST」); 删书路由跟随 allowLanState(文档说「仅支持回环」,实际一开局域网访问, 不带 Origin 头的脚本就能删);POST /state 用了过期的书库根 (另两处都走 resolveUiRootInfo,唯独它自己直取 lastRoot,实测把创作资料写进了过期路径)。 另有一批「静默失效」:设定表读失败后仍用默认词表产出禁用词待办、最小宿主下插件加载中断(22 个工具已注册、 提示词分区一条没注册)、三处把「用户取消」当成读取失败并写进事件日志、词表改名报假成功还暗地停用条目。 语义写反的典型是开写包对「后文才埋的伏笔」一处说「本章尚未埋下、不要透露」、另一处渲染成「本章将首次埋下」。 数量口径有两处变化,跨版本不可直接比较:scanSlop 现在归一换行(纯 CR 不再让整章变 1 行), 句末 ASCII ... 从此计入未完句(gapIndex 会上升)。 同时把新加的门禁补齐——ui-audit 的 R9 此前自称「双向」其实缺了 CSS→JS 方向, 补上后立刻又抓出 15 个死 CSS 类。

    7.3.2 · 2026-11-12 修 7.3.1 自己引入的 4 个回归(开写包假降级 / 重试流泄漏 / 纯本地阈值存不进 / 回收站加载态),清 22 条残留,并更正上版日志里 3 处不实说法(工具数仍 22)

    只修 bug。上一版修完 79 条之后,独立复审发现修复本身基本属实(75/83 真修好、7 条刻意保留且依据充分), 但我在修的过程中新引入了 4 个问题,最重的一条是:修「读失败被谎报成未初始化」时把披露条件写反了 (!read.missing 成功时也为真),于是任何创作资料已初始化的书,每次开写包都会多出 2~4 条假降级。 这个 bug 能过测,是因为当时只测了「坏了要报」、没测「好了不能报」——本版补了 test/brief-degrade-test.mjs 做三态对照(套件 36→37),并验证过它在旧条件上会红。 另三条:重试的 LLM 流在用户取消时不释放、纯本地模式下「提问节奏」永远存不进、回收站「加载中」会卡死。 同时更正上一版更新日志里 3 处与源码不符的说法(一个被列为「已清理」其实被保留的组件、 只改了一半的 8~70 注释、声称覆盖但实际没覆盖的建章自动章号)。

    7.3.1 · 2026-11-12 修第三方源码审计里 79 条能举证的缺陷:删除/恢复越界、审计渲染返回类型、面板阈值显示、草稿镜像防护恒失效、推理型模型兜底失效(工具数仍 22)

    这一版只修 bug,不加功能。起点是一份 88 条的外部审计清单:79 条成立、3 条误报(清单自己已标出)、 6 条「代码属实但影响不成立」。三条最该知道的:① 书名里写 ../.. 时删除目标能逃出 novels/, 而restore 的反向通道同样不校验(两条都加了测试,且是先写测试证明会失败再修); ② 对抗性审计的三个动作 render 返回的是字符串,会让下一轮请求失败(不是当轮报错); ③ 面板拿不到宿主下发的 chatThreshold,把 150 显示成 400,再点保存就把真实值覆盖掉。 另有一批「静默失效」:草稿镜像不得落进正文目录的防护因缺两个 import 恒返回 false、 推理型模型(先出 reasoning-delta)的非净化兜底永久不触发、必用词表损坏时开写包完全静默。 5 处口径变化已单列(actionChainRatio 修正、氛围链路取最强、标题截断、戏谑分流、日志上限文案), 新旧数值不可直接比较;工具数仍 22,无迁移步骤。

    7.3.0 · 2026-11-10 实时聊天写作:逐段写、程序按字数阈值让你确认,草稿进独立目录(工具数 21 → 22)

    新增第 22 个工具 novel_draft(草稿镜像):AI 一段段地写,每写完一段就把这一段正文交给它,由程序追加进独立目录 .novel-writer/drafts/<书>/ 并自行累计字数——只追加,AI 不读、不改、不清;正式章节在用户确认前一个字节都不动。它不生成正文,也不判断该不该问人:action 只有 commit,返回值里的 nextAction(continue / ask_user)由程序按字数阈值判定(默认 400,可在面板「提问节奏」填 100~5000),够阈值才把本批合并后的正文(combinedText)与固定文案的 question 一起交出去;计数存在该章的 <章键>__state.json 里,账本丢失时按镜像里的 ## 第N段 段头重建。为什么必须另起目录:插件所有章节扫描都按 novels/<书>/ 枚举并把每个 .md 当一章参与字数、六维基线与语义索引,半成品放进去会污染这些数字,而界面上只表现为「数字怪怪的」——工具层用 isInsideBookDir 硬断言拦死,路径落进正文目录直接拒绝。章键归一(能解析出章号就统一成 第NN章,并认领同章号的既有文件,避免升级后同一章多出一份)与纪律(开关 features.chatWriting,默认关;打开后系统提示注入八条硬约束,开写包多一个 chatWriting 分区;novel_new_chapter 定稿时由插件自己追加一条「定稿落盘」记录,已有逐段内容只写标记、一个字都没有才补正文)。面板:诊断报告页新增只读的「草稿镜像」卡片(选书 → 章节留空列文件、填章号看逐段正文),数据来自 GET /api/dsh-novel-writer/draft?book=&chapter=,不提供修改/清理。按书删除把 drafts/ 单独登记成 data:drafts(整目录移入回收站)——它不是扁平文件白名单的一员(放进去会永远命中 0 个文件而被跳过,留下孤儿目录)。工具数 21 → 22,参数契约只加这一个工具,书库格式不变;详见 novel_draft。

  • 7.2.0 · 2026-11-07 对抗性审计:改稿台新增严格对账、独立 AI 任务书与证据复核(编造的原文判废),工具数仍 21

    新入口挂在 novel_fix_plan 上,不新增工具(仍为 21 个):action="audit" 严格对账——只跑八条可机器判定的契约(必用词 / 替身词、世界观禁用词、套话、原地打转、大纲兑现、章末收束、连续裸台词、伏笔登记),每条违约要么带 1-based 行号与原句片段、要么明确标成全章层面,契约没跑会写明原因(ran: false)而不是「查过没问题」;action="audit_brief" 生成给另一个没有写作上下文的 AI 的审计任务书(判据、取证 JSON 格式、「举不出证据一条都不许写」的硬约束,末尾附正文);action="audit_verify" 逐条复核它交回的 claims——行号错但原文确实在文中 → corrected(自动修正行号),全文找不到 → rejected(疑似编造证据),并给出 score(可信证据占比)。明确不做文学评论:抽象度 / 物件偏少 / 感官单通道这类风格症状仍在改稿台与开写包里作建议,但不进审计;审计永不修改正文,三个动作都不落盘。参数契约只加一个 claims,书库格式不变,详见 novel_fix_plan 的「严格对账的 8 条契约」与「为什么这不是文学评论」两节。

  • 7.0.0 · 2026-11-01 修尺子:细节密度词表去噪,动作链示例不再恒取开头三句、不再截断

    这一版只做一件事:修尺子。细节密度的几张词表(动作 / 物件 / 五个感官通道)此前全是单字正则——「一点」贡献 2 个"动作动词",「根本 / 要被」贡献"物件名词",「一样 / 里面」贡献"视觉",「痛苦 / 语气 / 力气」贡献"嗅觉",「干净 / 麻烦」贡献"触觉",「温柔」贡献"温度"。也就是说,过去基于这些数字得出的"感官偏科""动作干瘪"结论,测的是词表,不是你的正文。
    ① 加上下文豁免层:新增 lib/lexicons/collocation-guards.js(193 条搭配,覆盖 7 个通道),这些虚词搭配命中时不再计数;实测同一章细节密度下降约 15%~20%。② 动作链示例修两个旧 bug:旧版 actionChains.length < 3 填满即止、且遍历从全书开头开始,于是每一章拿到的都是本书第 1 章开头那三句(所谓"按章轮换"根本无从谈起);旧版还按 40 个码点硬截断,放进开写包等于教模型写残句——现在只取 15~70 字的完整句并等距抽样。③ 面板可见:「诊断报告」页新增度量口径区,显示当前口径、还有几个缓存来自旧口径,并提供「重建分析缓存」按钮(只删 analysis/*-chapters-metrics.json 派生缓存;你在「基线范围」里选的章节存在 baseline/ 下,不会被删)。
    质量门禁:新增 test/density-precision-test.mjs——陷阱句必须为 0、真信号必须仍被计入、固定语料计数锁定、豁免表不许有"死条目"(登记了却根本不命中该通道词表)。
    升级须知:细节密度(动作 / 物件 / 五维感官)在多数书上会下降,这是修掉虚词灌水、不是能力退化,同一章的新旧数值不可直接比较;六维基线(动作密度 / 抽象度 / 修饰密度 / 虚化 / 留白 / 复杂度)本次未改,口径不变。缓存随版本号失效,首次测量会重建。工具数仍为 21,参数契约与书库格式不变。
    Phase 0 / 1A / 1B(同一版本内继续补完,不另起版本号):Phase 0 修尺子(细节密度上下文豁免、动作链只取完整句,见上);Phase 1A novel_chapter_brief 新增 5 个字段——actionChains(原著典型颗粒度动作链,p60~p85 分位取样 + 按目标章号确定性轮换)、clauseFrames(大纲子句框架,7 类动词元类 + 填空格式,插件不生成问句)、prevChapterIssues(上一章遗留问题)、symptomPrescriptions(症状处方,相关性门控 + 3 章 2 中滞回 + 每章最多 3 条)、referenceProvenance(参照集来源),受新增总预算 enhanceChars(compact 450 / full 900 字,装填优先级「上一章遗留 & 症状处方 > 子句框架 > 动作链」)约束,开写清单另加一条恒定项「章末收束闸门」;Phase 1B novel_new_chapter 新增可选参数 plan(不写入正文,只用于写盘后核对兑现)与返回字段 postWriteAudit(最多 5 条,按严重度降序:未兑现的 plan 子句 / 章末升华 / 连续站桩对话 / 高频套话;只告警、不阻断保存),新增套话库 lib/lexicons/slop.js(8 类 / 73 条 pattern),novel_fix_plan 据此新增一类待办 AI套话(按类别归并、给命中处数与行号区间、target「0 处」,不罗列套话原文),并把参照集与诊断集分开。Phase 2 novel_fix_plan 接入本地向量审计(lib/vector-audit.js,纯本地 ONNX、零外网、不需要任何 API key):新增两类待办 原地打转(连续 ≥3 块语义雷同且没有新信息:章内 z ≥ 2.0、绝对余弦 ≥ 0.80、新二字组占比 < 0.12)与 大纲漏写(大纲子句「关键词命中 || 向量相似 ≥ 0.42」,都未命中才报);只在改稿台跑(单块约 47ms,进开写包/建章/常规自检会拖垮高频路径),受语义嵌入开关与引擎可用性双重门控,不可用时明确跳过并披露。测试套件 18 → 23 套,工具数仍为 21,版本号仍为 7.0.0。

  • 6.9.5 · 2026-10-29 词表体系集中整修:修掉情绪虚高与六维失真,修「先生 / 蜡烛」误伤中式日常语境

    这一版只处理审查中已确认的问题,不发新功能。三处影响最大的修复:① 愤怒情绪虚高——DUTIR_TO_EMOTION 此前把大连理工词典的「恶」类(10,262 词,含无聊 / 尴尬 / 愚蠢 / 虚伪 / 啰嗦等贬义评价)全部映射成「愤怒」,导致表面主导情绪偏向 anger 并连带推高氛围轴;现在「恶」不再计入五类主情绪,只由效价词库承担负向。② 开写包与审计口径分裂——未登记 worldview 时开写包此前直接返回 worldview: null(不给禁词),写完后审计却按默认表报错;现在两边统一走同一裁决点,开写包按文化基准注入默认表并在降级说明里写明「哪套基准、共多少条」。③ 东方语用误伤——从东方禁用称谓移除「先生」(教书先生 / 账房先生 / 说书先生 属正常用法),并把「点烛 / 蜡烛 / 礼拜」三条硬规则收窄为「祭坛点烛 / 祭坛蜡烛·祷告蜡烛·献祭蜡烛 / 做礼拜·礼拜日·主日礼拜」,此前「点起蜡烛」「礼拜佛祖」会被判硬错误。
    其余修复:单字否定词(没 / 莫 / 无 / 未)误吞角色名后的情绪词(「苏莫愤怒」「林未恐惧」);修饰密度误杀「天真的 / 认真的 / 所有的 / 原有的」;动作密度混入 70 余个非动词与性质形容词导致虚高,且二字程度副词(十分 / 非常 / 格外)未豁免;抽象度按任意 2~4 字切片(「他很想」「天空」「认真」「她醒来」全部命中);必用词审计在专名与替身词互相包含时(「魔导步枪」vs「步枪」)误报;关键词提取的停用字条件写反(|| 应为 &&)导致「她的 / 着她」霸榜;大纲解析漏认「节」;幽默度扫描只看前 6000 字;对话占比把 ASCII 直引号数了两次;文化基准检测与「文化基准存疑」提示未接子串碰撞守卫。
    词表扩充:西幻标记词补 28 个高信号词(子爵 / 大公 / 纹章 / 骑士团 / 告解室 / 魅魔 / 地下城 / 羊皮纸 / 先令 等),中式标记词补齐与禁用词表一致的 27 个市井与制度词,非净化模式的淡化特征库补时间跳跃 / 委婉总结 / 元评论三类,意象载荷补五组微表情与肢体反应。另为「失敬 / 银子 / 银两 / 铜钱 / 包袱」补上前后字守卫与实证反例。
    升级须知:缓存随版本号失效,首次分析会重建;动作密度、抽象度、修饰密度与愤怒占比在多数书上会下降(修掉虚高,不是退化),跨版本对照请用本版重取基线;词表扩张会让既有书新出现一些用语候选(覆盖变宽,不是新增误报)。工具数仍为 21,参数契约与书库格式不变。

  • 6.9.0 · 2026-10-22 非净化模式重做:动笔前给目标带、动笔时兜底、写完可核对(新增「直白度」度量)

    起因是用户反馈「非净化模式开了跟没开一样」。核查结论:旧版只往系统提示里加 5 行抽象要求,动笔那一刻模型拿到的材料里一个字都没有——问题不在力度,在可执行性与位置。本版改成三层:① 动笔前——novel_chapter_brief 新增 rawMode 分区(原著「把场面绕过去」的写法密度、处/千字,启发式;本章目标带;一条硬约束同时进 plan.checklist),建章返回也带同一提示;② 动笔时——新增 llm/stream 兜底层:三层闸门(开关 / 系统提示里的非净化模式标记 / 输出开头的自我刹车特征)全中时,丢弃本次输出只重试一次,重试只在系统消息尾部追加写作指令(不改用户消息,不用「你已获授权」这类话术),事件记 raw 日志且不记录任何正文;③ 写完后——新增直白度度量(lib/explicit.js,纯规则、零 token):六类淡化写法密度 + 对话段落占比,novel_style_check 在开关打开时给出「本章 X 处/千字 vs 原著 Y 处/千字」与一句结论(clean / new / similar / softer / much-softer / more-explicit)。与同类「破甲」插件的取舍:只借「注入位置 + 拒绝检测重试」的工程结构,不借去人格 / 宿主补丁 / 「永不拒绝」那套——那套会改变整个 agent 的行为边界,超出本插件职责。已知边界:直白度是启发式(只数明确的淡化写法,不数比喻多、句子长这类风格差异),不作为质量评分;重试只一次且只认三类自我刹车特征;兜底依赖宿主 llm/stream,缺失时安静跳过并留痕;效果取决于模型本身。工具数 21、参数契约与书库格式不变。

  • 6.8.0 · 2026-10-22 按书删除与回收站:删一本书不必再翻 12 个位置(issue #2)

    回应 issue #2「删不干净」:一本书的资料散在 12 个位置——正文、创作资料、数据目录 9 个按书子目录与 state.json 的设定库条目,另有 v0.8.0 前留在数据目录根上的旧位置伏笔表。本版新增第 21 个工具 novel_delete_book(按书删除):一次列出全部待清理位置、文件数、体积与样例,默认只预览,action:"apply" 才执行;keys/面板勾选框可只清其中几类(例如只删报告历史与缓存,正文留着)。匹配改为前缀 + 分隔符边界(书名后必须紧跟 . / - / _ 或到此结束,另加 fix-plan-<书>- 与旧位置两条规则),因此删《孤舟》不会碰到《孤舟子》;只删 <书>.json 必然漏掉 <书>-<hash>.json、<书>__style_2.json 这类文件。执行时内容移入回收站(.novel-writer/trash/<书>-<时间戳>/)并写 manifest.json,restore 原样移回(目标已存在则逐文件跳过、绝不覆盖),purge 才彻底删除;书名撞上 _global / 创作资料 / 点号目录时直接拒绝。面板同样可用:首页第一个分区新增一行「删除整本书」,点进去是独立页面(选书 → 预览清单 → 逐处勾选确认,页面下方常驻回收站,恢复 / 彻底删除都在同一页),书库概况里的每本书旁也保留快捷删除按钮;「报告历史」里还能逐份删除报告(同样进回收站,条目名「(报告历史)」);另有宿主路由 POST /api/dsh-novel-writer/delete-book(仅回环)与 POST /api/dsh-novel-writer/reports {action:"forget"},与工具共用同一套回收站。已知边界:移入回收站不释放磁盘空间,trash/ 不自动清理(面板数据目录体积会把回收站算进去);恢复按文件粒度、不做合并改写,同名文件一律跳过。工具数 20 → 21,参数契约与书库格式其余部分不变。

  • 6.7.0 · 2026-10-20 面板视觉全面改版:只改长相与说法,功能面不动

    起因是三条用户反馈:① 不好看 ② 太杂、功能难找 ③ 描述看得半懂不懂。本版只改网页端面板——lib/client.js 的渲染层、tools/client-src/V6-components.css(4.6 KB → 19.4 KB 的视觉系统)、图标源,以及同步更新断言的 test/client-test.mjs。功能面完全不变:8 个视图一个不少(features / tools / baseline / creation / reports / model / lexicon / diagnose),ALL_TOOLS 仍为 20,controller 的 api.* 语义与后端路由零改动。五条设计原则:① 一个页面只讲一件事(标题告诉你在哪,正文只放这一页的内容,不重复全局状态);② 安静的默认态(开着的开关不再整行变蓝、也不再贴「已开启」——开关本身就是状态;颜色只留给「需要注意」与唯一主操作);③ 说人话(描述写「对你有什么用」,工具 ID novel_xxx、参数 action=audit 与内部术语不进主文案,最多收进「说明」展开区或 tooltip);④ 一种卡片、一种行、一种分区标题(全面板只用这三种积木,不再出现虚线框、无阴影框、蓝色胶囊标题等第 N 种容器);⑤ 路径与维护信息往下沉(文件路径、数据目录、state.json 统一收进页面底部的「存储位置」折叠区,一个页面只出现一次)。具体变化:删掉每页的大 Hero 卡与子页的蓝色胶囊标题,主页只在标题下留一行副标题,分区标题改为 12px 灰色小标签;首页按「写作资料 / AI 行为 / 功能与工具 / 记录与维护 / 高级」分区,原来独立的「书库统计」卡并进「写作资料」卡底部的一行书库概况(「书库 N 部 · M 章 · X 字」,点开是每本书一行,含「7 天未动」);工具行统一由 PreferenceRow 承载,导航行行尾是「计数 + ›」;标题、按钮与分组名不再使用彩色 emoji,改为线性图标(图标源 52 个键、每个工具行一枚专属图标);阈值行的 slider 辅助输入一并移除,每维只留「低于 / 高于」两个百分比输入框。升级须知:无需迁移——不新增配置文件、不改书库格式、不改任何工具的参数契约;面板布局与导航有调整,如果你依赖旧的操作路径,按新界面的中文标签找一次即可。所有既有行为断言(剪贴板三条降级路径、reqId 过期丢弃、破坏性操作二次确认、hook 稳定性)一条没删。设计规格全文见仓库内 tools/UI-REDESIGN.md(16 KB:工作纪律、设计原则、视觉系统、信息架构、文案风格)。

  • 6.6.5 · 2026-10-19 情感复杂度与置信度联动:不可采信时不再给顶格结论

    起因是一句用户反馈:「写作插件用情感曲线测试我的某些小说时会直接爆掉」「第一章的情感复杂度会直接爆掉」。核查结论是不是崩溃,是输出自相矛盾:同一份返回体里一边写着 confidence: "low" 与「检测到高密度成人向内容 / 感官描写…请勿采信」的 caveat,另一边照样给出 complexity: { score: 0.6, level: "high" }——下游只要读 level 就会得出「情感复杂度高」,而这个结论已被工具自己否定。实测(书库里的《棍妹》第一、二章,成人向内容密度 8.99‰ / 9.77‰):两章都判 level: "high",而两章的 confidence 都是 "low"。本版让 emotionComplexity(perChapter, reliability) 接收可靠性信号({ polluted, pollutedBy, noData },由 analyzeText 在算完污染与零命中判定后传入,经 emotionalQuantification 透传):level 降级为 "unknown"(新增取值,此前只有 low / medium / high),并新增 reliable(boolean)与 reliabilityNote(固定说明,可采信时为空串);score 数值保留,但 reliable: false 已标明它的地位。触发条件三类:污染(成人向内容 / 感官描写 ≥ 1.5‰、或战斗/爽文 ≥ 3‰、或恐怖/疯狂 ≥ 3‰,阈值与词表都是既有实现、本版未改)、无数据(全文零情感词命中)与无 clean 数据(stats.length === 0,覆盖「只命中弱情绪词、clean 一个强词都没有」的情形——这一条由文档负责人独立实测抓出后于本版内一并修掉,同一早退分支不再给 low + reliable: true);三条的 score 0 都表示「没测到」而不是「情感简单」。真凶之一在渲染层:lib/core.js 的【情感量化】段此前打印 JSON.stringify(q.complexity).slice(0, 120),而 reliable / reliabilityNote 排在对象末尾、必然被 120 字符截掉,于是「本项不可采信」永远到不了渲染文本;现在改成可读的一行(- 复杂度: <score>(<level>)熵 X/Y,多样性 N,主次 A↔B(k),章间漂移 有/无(基准 …)),并在 reliable === false 时单独起一行写出 reliabilityNote;novel_style_report 的渲染此前完全没有这一项,本版一并补上。如实交代:本版只做了 P0(可信度联动 + 渲染补齐),原计划的 P1「用 clean 计数重算复杂度」经核查已经满足(perChapter 传的本来就是 cleanScoresOf 聚合的 clean 计数,实测第二章 complexity.dominant = anger 与 cleanDominant 一致、而 raw dominant 是 joy),因此不写成「本版新增」;成人向内容 / 感官描写密集的文本的情感维度本来就不适用,本版做的是「别在不适用的语料上硬给一个高置信度的数字」、不是「让插件准确判断 成人向内容 的情感」,这类文本建议改用节奏 / 爽点 / 对话密度等维度;阈值(1.5‰ / 3‰)未调;情感复杂度没有「正确值」(score 是熵归一化 0.5 + 多样性 0.25 + 主次冲突 0.25 的启发式聚合),本版只是让它在不可采信时不再冒充结论。工具数与参数契约不变(20 个)。另:CACHE_VERSION = PLUGIN_VERSION,分析缓存跟着插件版本号走,所以升级会自动失效旧口径的结果、不需要手动清缓存。详见 novel_sentence_analysis。

  • 6.6.0 · 2026-10-15 可观测性与诊断:本地事件日志 + 第 20 个工具 novel_diagnose

    起因是一句用户反馈:「有些错误光看提示看不出来,能不能加日志」。本版加了本地事件日志与诊断报告,但刻意不做成「给作者看日志文件」——作者不会去读 .jsonl,真正的消费者是替他操作的 AI。新增第 20 个工具 novel_diagnose:读日志 + 做五处体检——书库根、书库内容(书数/章数/字数/解析不出章号的文件/过短章,只统计不读正文)、插件数据目录(应有子目录、JSON 能否解析、zstd 压缩缓存、0 字节、超大与读不出的文件)、语义引擎(惰性探测,不加载模型)、开关与状态快照(哪些工具/功能被关了、总开关在哪一档、lastRoot 是否失效)——把近期调用与降级失败、「工具 × 书名」反复出问题的组合、高频失败类型、可执行建议与「本次覆盖与未覆盖」聚合成一份中文报告(report / events / clear;days 默认 7、上限 90)。工具调用审计在注册层统一包一层,20 个工具一处覆盖(含诊断自身,不散落埋点),只记工具名、安全参数摘要(书名/章号/action 等定位字段 + content/text 的长度)、耗时、状态与错误 code。静默失败的留痕是选择性的:6 个服务端模块里 catch 点 163(块级 } catch 161 + 链式 .catch( 2;旧稿的 164 把 core.js 注释里引用的写法也算成了代码,属口径过计),另有 84 处 logEvent( 调用点——这两个是不同总体:84 处里 77 处落在 catch 体内(=有留痕的 catch 点),其余 7 处根本不在 catch 里(书库根轨迹 resolveRoot / noteRoot 共 6 处 + 工具调用审计 installToolAudit 1 处,它在 finally 里),所以静默的 catch 点 = 163 − 77 = 86(旧稿的「静默 80」是拿 84 减 164 得来的,会少算 6 处;分 7 类,理由是"不存在/空是常态、已如实披露、重抛由审计层记、纯清理、允许的探测、有意不记的每项分支、无法区分 ENOENT");根解析另加一条只记「可疑决策」的留痕。本版内另修三处契约(都由实测触发):参数摘要的长度/计数派生键(contentChars / textChars / srcChars / filesCount / chaptersCount / termsCount)与 name 此前漏登记在白名单外被整批丢掉——「只记长度」当时并不成立,现补齐;分享层改为 fail-closed(readLogEvents 只有显式 forShare === false 才返回本地层,其余一律脱敏);分享层的 redacted: true 由条件性改为恒带(层级标志,不是"有内容"标志)。另有一组 P0 修复:「读不到」不再被说成「没有」——只有 ENOENT 才说「尚未初始化 / 未登记 / 尚未保存 / 无创作资料」,文件存在却读不出来时只说「该文件存在,但读不出来」并给出路径与原因(novel_outline 的 read、开写包的锚包、novel_style_report 的 action=get、连贯性审计的大纲对照四处)。覆盖口径如实交代:四处里三处(创作资料读取 / 风格判断读取 / 大纲对照)端到端生效;锚包那一处在真实工具路径上不可达(内部 try 里全是预读文本上的字符串操作,唯一会抛的 assert 在 try 之外),该分支只有函数级与源码级断言、没有端到端覆盖。另有一条残留同类缺陷(下版处理):把某章文件换成同名目录后,scanChapters 的 if (!entry.isFile()) continue 会静默跳过它——该章从章表消失、诊断的「读不出来的文件」计数为 0、开写包 degraded 里也没有「读取章节失败」,即这一条路径上「读不到」仍被讲成「没有」。日志落在状态目录旁(~/.dsh/dsh-novel-writer/logs/events.jsonl,512 KB 轮转、留最近 3 份),不在书库根——理由很实在:最常见的故障就是「书库根不对」,那时 .novel-writer/ 根本不存在或不可写。隐私是两级设计(口径在本版定稿时改准):本地日志靠字段白名单以元数据为主(时间 / 工具名 / 书名 / 章号 / 状态 / 耗时 / 计数 / 路径 / 错误 code),但自由文本字段(错误消息等)经收敛后至多保留一句(≤160 字)——所以本机日志里可能出现被错误消息回显的输入片段(实测:novel_lexicon 找不到词条时会把传入的词条名回显进 detail),日志文件不出本机;而任何离开本机的内容(诊断报告、面板「复制诊断包」、诊断路由)一律走分享层,自由文本整体剔除,报告层再把错误一律归类成固定 code(JSON.parse 的报错会回显文件内容片段,而 summaries/plots/settings 正是由正文派生的)——因为值层面的长度 / 标点启发式从原理上无法保证自由文本里不含正文,真正的保证是「离开本机的东西不含自由文本」。报 bug 请用面板「复制诊断包」或 novel_diagnose 的输出,不要直接附本机日志文件。面板新增「诊断」入口(选统计窗口 + 一键复制诊断包,用于贴进 issue);新增 GET /api/dsh-novel-writer/diagnose 路由,与工具同一实现。本工具不判定对错、也不减少 bug,只让定位变快:静默失败只接了有诊断价值的那部分(「文件不存在」这类正常分支刻意不记),并发/性能只给已记录到的耗时、不做压测,各清单有条数与读取预算上限。工具数 19 → 20,参数契约与书库格式不变。

  • 6.5.5 · 2026-10-11 风格基线可指定范围:按全书 / 按部分,锚点不再跟着 AI 一起漂

    回应 issue #1「风格漂移」:AI 续写每章只参考上一章,从第 2 章起它参考的就是自己的产物;而「六维基线」与「原著锚段」原本都从全书取样——书越长 AI 占比越高,锚点自己也在漂(既测不出对原著的偏移,锚段也逐代变成 AI 的文风)。本版给侧栏「风格基线」面板加上基线范围:按全书(默认,=旧行为,存量书零迁移)/ 按部分(展开章节选择器,只用勾选的章——是「只用」,不是「优先 + 不够再补」)。正在检查的那一章永远排除(即使它在选中列表里):拿自己跟自己比必然「完美符合」,比不检查更糟。勾中的章(剔除目标章后)少于 3 章时推荐容差自动放宽到至少 ±25%,所以单章也能当基线;勾中的章若取不到(已改名 / 删除,或正文过短无法测量)会被跳过并如实计数,一章都取不到时回退「按全书」并在返回体里写明原因——两种文案:选择都失效 → 「(自定义选择已失效,已回退全书)」;只选中了本章自身 → 「(选中的章只有本章自身,不能作为自己的基线,已回退全书)」。范围按书保存(<书库根>/.novel-writer/baseline/<书名>.json),与全局的容差设置(styleTolerance)不同层。风格自检 / 开写包 / 改稿台 / 风格画像 / 建章五路同源,baselineScope 的文案随之反映用户的选择(开写包与建章还把范围写进模型可见的渲染文本)。插件不判定「你是否漂移了」(文风本就允许演变,那不可判定),只回答「相对你指定的标杆,这一章偏了多少」。工具数与调用参数不变(19 个),五路返回体各新增两个只读字段:baselineScope(文案)与 baselineMode(all / custom)。

  • 6.5.0 · 2026-10-09 用语审计误报修复:子串守卫、行号定位与分档,并堵住正则回溯

    用语审计此前是纯子串匹配,而禁用词 93% 是 2 字词,于是「里正」会在「这里正是」上误报(实测 19 句无辜句子报出 15 条,其中 14 条是误报)。本版:新增子串碰撞守卫表(紧邻字/序列有实证才挡)、候选改为行号区间定位(不再贴原文,按需回读)、按词长分硬命中 / 待判 / 提示三档(2 字词交给模型读那一行自行判断,明确允许判"不是问题")、中式基准下禁词与客套词去重(旧版每词报两遍)。安全侧:登记的正则新增静态校验(拒绝嵌套量词与无界通配——实测同类模式在 29 字符上要跑 40 秒)并改为逐句扫描,被拒绝的模式会显式报出而不是静默跳过。新增逐词静音(worldview.allowWords,仅本书生效)。工具数与参数契约不变(19 个)。

  • 6.4.0 · 2026-10-09 用语规范接线:欧式设定下的中式市井词拦截链全线接通

    开写包现在会渲染「语用规范」(称谓、客套、仪式、语气),禁用词表改为与用户登记合并而非整份替换,并取消了 10 条截断。连贯性审计的章节模式同时返回用语命中。工具数与参数不变(19 个)。

  • 6.3.0 · 2026-10-06 审计修复版:必用词分区、词表损坏保护与面板修复
    v6.3.0:本地测试版。第 19 个工具 novel_lexicon(必用词表,v6.2.0 新增)在本版修掉一批缺陷并对齐界面:开写包补上 【必用词·优先使用】 分区(此前 plan.checklist 的硬指令指向一个不存在的分区,avoid 勿用词一条都到不了模型)、词表文件损坏后按层开关会把整层条目静默清空(现改为拒写并明确报「词表文件损坏」,原条目不再丢失)、面板「合并 / 全局」两个视图加载失败,均已修复;词表面板三层视图改用官方分段控件,改名撞车改为明确报错。工具数与参数契约不变(19 个)。
  • 6.1.0 章节查找、复测、文本统计、语义短段落与 MCP/界面修复
    v6.1.0:修复章节查找、复测、文本统计、语义短段落与 MCP/界面问题;当前规则以下列页面为准。历史版本的报告数值可能因计分修正而变化。
  • 6.0.0 界面全面重构(工具与状态文件零变化)
    v6.0.0 界面全面重构(工具与状态文件零变化)。本版把面板外观按 DSH 官方设计体系统一(--dsw-alias-* 语义 token / 0.5px 发丝线 / 官方圆角尺度 / 官方 UI primitives),随后又按「官网式重构」收敛:强调色统一为 DeepSeek 蓝(state-business-primary)、主面板改「品牌光晕头部 + 分区 kicker + 图标卡 + 状态 pill」、内容卡由描边改为软阴影与悬停抬升、二级页标题与按钮家族统一、各级页面宽度恒定一致。若你是从旧版升级,工具数量、参数与返回结构均未变动,无需改任何调用。详解见 novel_sentence_config 的「界面为什么『看起来像 DSH』(v5.5.0)」一节。
    v6.0.0 再进一层:源码分块 + 构建链装配、6 个实际使用的统一组件(单一行模式 PreferenceRow / 分组卡 GroupCard)、19+5 枚线图标、阈值 slider 辅助输入、页面级 push-pop 过渡动画;工具 / 参数 / 状态文件依旧零变化。
  • 5.0.0 新增开写包与改稿台;系统提示词增加「档位 × 场景」
    v5.0.0 新增两个工具。novel_chapter_brief(开写包)把动笔前要读的材料装配成一次只读调用;novel_fix_plan(改稿台)把风格自检的测量数据翻译成按优先级排序的待办清单。工具数 16 → 18,另为 novel_plot 增加 action: "graph" 结构视图(不新增工具,跨章计算并入既有伏笔工具)。两个新工具均不生成正文,也不调用在线模型。
    v5.0.0 另有系统提示词的「档位 × 场景」两个正交维度。档位(systemPromptMode:off / brief / full)决定注入多少,场景(promptScene:general / writing / revising / auditing / setup)决定注入什么;场景仅在 full 档下生效,且只改提示词内容、不改任何工具能力。老状态文件没有 promptScene 键时落到 general(即 4.x 的通用工作流,本版仅在其中补入 7 行新工具入口指引),既有工作流不变。两字段的读写入口与完整语义见 novel_sentence_config。

工具总览

章节与文稿

分析与测量

设定、剧情与创作资料

审计与配置

该调哪个工具

工具数到 22 之后,「选对工具」本身也有了成本。下表按你想回答的问题给出口径;其中三个分析类工具名字最像,一并说明区别。

你想回答的问题用哪个主要输出
这本书整体是怎么写的(写作手法)novel_sentence_analysis九类句式分布与排列规律、句式模板、段落结构、句长节奏、情感曲线、风格指纹与节奏建议
我这一章写得像不像原著novel_style_check单章 vs 全书其余章的指纹相似度与 high / medium / low 分级、偏差清单、六维出带判定、原著锚段
这本书是什么风格novel_style_report六维测量与 μ±σ 基线、词汇、题材与流派、情感、氛围 12 轴、语义距离、锚包与 AI 风格判断
动笔前我该读哪些材料novel_chapter_brief承接口、上一章钩子、大纲方向行、命中人物卡、未回收伏笔及埋设距离、世界观禁用词、六维基线、锚段与骨架、禁用清单与三段式 plan
这一章要改哪里、改完没有novel_fix_plan按 severity → effort 排序的待办清单(带段落行号与改写方向,不给改写后的正文)+ verify 三态复测
全书有没有断线 / 丢人 / 忘伏笔novel_plot(action: "graph")伏笔生命周期与埋设距离、人物出场矩阵与连续缺席(≥ 5 章)、线索活跃度与最大空档、时间线顺序核对、大纲方向行对照
我想让 AI 必须用我的专有名词 / 惯用词novel_lexicon开写包注入的硬清单(lexicon 分组词条 + plan.checklist 硬指令)、成稿 audit 的覆盖率与未使用清单、以及「通用词替身顶替」的行号与原句(只读)
出错了但看不出原因,或面板列不出书novel_diagnose近期调用 / 降级 / 失败聚合、高频失败类型、「工具 × 书名」反复出问题的组合,五处体检(书库根 / 书库内容 / 数据目录损坏 / 语义引擎 / 开关快照)与可执行建议,以及本次覆盖与未覆盖清单(只含元数据,可安全贴进 issue)
三个分析工具已共享底层引擎与缓存,重叠在实现层、不在接口层。novel_sentence_analysis / novel_style_check / novel_style_report 用的是同一套句式与情感引擎,读写的也是同一批缓存文件(<book>-chapters-metrics.json 分章六维测量、<book>-full.json 全书分析、analysis/<book>-<sha1>.json 句式报告历史、style-reports/<book>.json 风格判断)——分章测量的指纹按文件名排序后再哈希,因此三者交替调用命中同一份缓存,不会互相击穿。它们不合并,是因为回答的问题互不替代(怎么写 / 像不像 / 是什么风格),输出形状也各不相同;合并会把三份 output.schema 并成一份、并让三个稳定的工具名消失(MCP 客户端配置、文档页与提示词一起失效),代价大于收益。这里用「意图 → 工具」的对照表做消歧,而不是减少工具数。

共同约定

书库根目录与路径

所有工具接受可选的 root(含 novels 子目录的目录)。解析优先级:调用参数 root → 插件配置项 → MCP 启动参数 --root → 环境变量 DSH_NOVEL_WRITER_ROOT → 当前工作目录。MCP 形态下,参数里的 root 必须落在启动时 --root 指定范围内;越界时由 MCP 层静默回退到 --root,只写入 stderr 日志,调用方不会收到错误(插件层 resolveRoot 本身不做范围校验,范围校验只在 MCP 层 isInsideRoot 进行)。书名、分类等片段经 sanitizeSegment 过滤,防止路径穿越。

数据落盘

所有派生数据集中写在 <书库根>/.novel-writer/ 下:plots(伏笔)、settings(设定)、lexicon(必用词表)、summaries(摘要)、analysis(分析报告)、audits(审计)、embedding(语义索引)、style-reports(风格画像)。写盘采用临时文件 + 原子替换;失败会抛出 ENOVELWRITEFAIL 而不是静默丢弃。

唯一的例外是 v6.6.0 的事件日志:它写在状态目录旁——~/.dsh/dsh-novel-writer/logs/events.jsonl(受 DSH_NOVEL_WRITER_STATE 覆盖;512 KB 轮转、保留 3 份历史),不在 <书库根>/.novel-writer/。理由很实在:最常见的故障就是「书库根不对」,那时 .novel-writer/ 根本不存在或不可写,偏偏最需要留下证据。日志以元数据为主(工具名 / 书名 / 章号 / 状态 / 耗时 / 计数 / 路径 / 错误 code),但自由文本字段(错误消息等)经收敛后至多保留一句(≤160 字)——所以本机日志里可能出现被错误消息回显的输入片段,该文件不出本机;聚合查看与对外分享见 novel_diagnose(报告与面板「复制诊断包」走分享层,自由文本整体剔除)。

语义引擎的可用性

语义检索、语义风格距离与语义隐性情感依赖随包分发的 bge-small-zh-v1.5 量化模型(约 24MB)与 onnxruntime-web(WASM)。引擎不可用时这些能力自动降级为纯规则模式,其余功能不受影响;返回体会以 skippedDims 或专用文案说明降级原因,而非返回空结果。引擎相关的降级同样会记入事件日志,可由 novel_diagnose 聚合查看(本工具只聚合已记录到的事件,不会主动跑一次推理去探测)。

MCP 形态的边界

包内 mcp/server.mjs 把同样 22 个工具暴露为 stdio MCP 服务器(可用 Streamable HTTP 传输)。协议侧:未知工具返回 -32602(Protocol Error);支持 2025-11-25 / 2025-06-18 / 2025-03-26 / 2024-11-05 版本协商并原样回显;JSON-RPC 批量请求自 2025-06-18 起按规范拒绝;单行报文上限 4 MiB;novel_import 的 src 默认限制在书库根内,需显式加 --allow-external-src 才能放开。