← dsh-novel-writer 技术文档 / novel_fix_plan

novel_fix_plan

改稿台。把某一章的测量偏差翻译成按优先级排好序的待办清单:每项带稳定 id、类型、原文定位(lineStart / lineEnd / excerpt)、现值与目标区间、可照改的原著锚段、严重度与改写代价,以及一句改写方向action: "plan" 出清单,"verify" 复测,"mark" 标记单项。工具本身从不生成改写后的正文句子

为什么要有它。novel_style_check 返回的是数据——偏差清单、六维判定、原著锚段与一个 verdict 分级;它没有说「所以先改哪一句」。把数据组织成行动是模型的额外工作,而工作一旦靠自觉完成,就会时做时不做。本工具把同一批测量结果直接翻译成待办:排序由 severity / effort 决定,位置由行号给出,改完用 verify 复测,形成 plan → 改 → verify 的闭环。
关键边界:hint 只给方向,不给正文。每项的 hint 是一句改写方向(如「这句抽象词过密,改成具体动作或物件」),而不是改写后的句子。生成正文是宿主模型的事,插件不做——这是本插件的立身之本:零 token、正文不出本机。任何把 hint 当作「可直接粘贴的成稿」的用法都偏离了契约。

参数

参数类型必填说明
bookstring书名,即 novels/ 下的子目录名。空串或纯空白被拒绝,值经 sanitizeSegment 清洗。
chapterstring要改的章节标识:章号(1 / 01)、文件名(第01章.md)或标题子串,由 findChapter 解析;找不到则报错。与 novel_style_check 同一套解析规则(文件名优先于章号、纯数字不做子串兜底)。
actionstring枚举 "plan"(默认,产出待办清单) / "verify"(复测) / "mark"(标记单项)。省略或非枚举取值时按 plan 处理。
itemIdstringmark 必填mark 必需:目标待办项的 id(取自 plan 返回的 items[].id),必须已存在于该章已落盘的清单里。缺省或空白串直接报错
statestringmark 必填mark 必需:处置状态,枚举 "done"(已按 hint 改过) / "skip"(有意不改)。缺省或非枚举值直接报错,且两种情形文案不同(v5.0.0 修正):未传 → 「缺少 state 参数:只能是 "done"(已改)或 "skip"(跳过不改)」;传了非法值 → 「state 只能是 "done"(已改)或 "skip"(跳过不改),收到:"finished"」。(旧版两种情形都报「需要 state 参数」,传了非法值时容易被误判成漏传。)
rootstring章节库根目录。省略时按 config.root → 会话工作目录回退;MCP 形态下越界值被静默回退到启动参数 --root 指定的书库根。

参数对象声明 additionalProperties: false。本工具没有阈值类可调参数:严重度权重、类型判据与截断上限都是常量(截断上限恒为 12 条,v5.1.0 起由 40 收紧到 12)。测量入口 buildFixPlan / verifyFixPlan 自身接受 opts.tolerance / opts.maxItems / opts.plotStaleChapters 三个内部选项,但工具层固定传 {}不对调用方暴露

action 取值

取值语义前置要求返回体形态
plan重新测量本章并产出待办清单(默认),同时把清单原子写到该章的 fix-plan-*.json章节可解析;写作助手功能已开启(六维引擎门禁,与 novel_style_check 同口径)items[] + summary + planFile
verify对当前正文重算,并与上次落盘的清单逐项比对,给出三态结论。不写盘同上(引擎门禁);清单文件不存在时不报错,改为报告「按当前正文现算」checked[](逐项三态)+ summary
mark把单项标记为已处置(state: "done" 已改 / state: "skip" 有意不改)。只改落盘清单里的处置状态,不重新测量、不改正文,因此不受六维引擎门禁约束itemId state 齐备,且该章已有落盘清单、itemId 能在清单里找到ok + item(被更新的那条)
门禁口径:markplan / verify 不同。plan / verify 依赖六维引擎,执行前先读状态文件里的「写作助手功能」总开关,关闭时直接拒绝执行(与 novel_style_check 完全一致)。mark 在引擎门禁之前就分支返回——它只是读-改-写自己那份清单,即使总开关被关闭也能用来登记处置状态。三个动作都仍要过工具级开关 assertToolEnabled("novel_fix_plan")

输出结构

三个动作的返回体形状并不相同,且只有 plan 返回 planFile

action返回字段
planbookactionchapter{ file, number, title })、items[]summaryplanFile
verifybookactionchapter(优先取落盘清单里的 chapter,无清单时取本次现算的)、checked[]summary不返回 planFile(清单路径只在「找不到清单」时被写进 summary 文案)
markbookactionsummaryokitem(更新后的那条,含 state / stateAt

chapter 对象来自目录扫描:number 解析不出时为 null(键被整键丢弃)。契约必返字段是 book / action / summary

items[] 条目

实现里的 makeItem 严格只产出下列 9 个字段,条目 schema 为 additionalProperties: false——落盘文件里额外追加的 state / stateAt文件内部字段,不出现在返回体

字段类型内容
idstring稳定 id,形如 fix-<类型 slug>-<lineStart>-<sha1 前 8 位>。sha1 由 类型 | key | lineStart | lineEnd 派生(key 是指标名、word:词honor:词ritual:模式title:sisterbridge:候选类型plot:伏笔 id 等内部键)。同一章同一位置重新 plan 必得同一个 id,因此 mark 与跨次 verify 都能对齐同一项;正文改动导致行号变化时 id 随之变化。
typestring八类之一(中文,取自 METRIC_TYPE 与各 collect*Items):句式偏离 / 抽象度过高 / 留白异常 / 禁用词 / 语用不符 / 衔接缺失 / 伏笔未回收 / 情感过直。
locateobject{ lineStart, lineEnd, excerpt }:段落的绝对行号区间(1 起,含空行计数)与该段原文节选(截 80 字)。lineStart = lineEnd = 0 表示该问题属全章 / 跨章层面、定位不到具体行。
currentstring当前实测值或命中描述。指标类为「维度名 + 实测值」(如 complexity 0.61);禁用词为「命中「词」N 处(首个在第 N 行)」;语用项为「客套禁词「词」(第 N 行)」/「仪式禁式「样本」(第 N 行)」/「称谓「XX小姐」(第 N 行)」;情感过直为「情绪直陈 N 处(全章 M 处)」;衔接缺失为候选类型名;伏笔未回收为「未回收 N 章(最近提及:…)」。
targetstring目标区间或目标状态。指标类为基线带 low~high(如 0.38~0.52;基线 μ = 0 时为空串);禁用词为「改用「替代词」」或「避免使用该词」;衔接缺失为「补过渡/承接句(1 句即可)」;其余类别可能为空串。
anchorstring可照改的原著锚段(源自 buildStyleAnchorPackage 的抽样结果,与 novel_style_checkfixAnchors 同源)。只有指标类待办会带锚段,其余类别为空串。
severityinteger严重度 1–5(超出范围会被钳到边界)。指标类按 |σ| 分档:≥ 3σ → 5、≥ 2σ → 4、其余出带 → 3;退化成「全章层面」的指标项再降一档max(1, sev-1))。其余类别为固定值:禁用词 4、客套禁词 4、仪式禁式 4、称谓 3、情感过直 3、衔接缺失 2–4(时间硬跳 4 / 检查跳过 2 / 其余 3)、伏笔未回收 3–4(距离 ≥ 40 章或 priority: "high" → 4)。
effortinteger改写代价 1–3(超出范围会被钳到边界):1 = 局部替换即可(有登记替代词的禁用词 / 客套词;衔接缺失里的时间跳跃 / 时间过渡 / 钩子未接),2 = 需要重写该句或该段(指标类段落项、情感过直、仪式禁式、称谓;衔接缺失里的语义距离 / 人物断线),3 = 需要通读全章或跨章处理(指标类的全章层面项、伏笔未回收;衔接缺失里的检查跳过)。同严重度时先做省事的项。
hintstring规则拼接的改写方向(一句话,含实测值与基线的对照,指标类还会带上「本段 X」与方向建议),例如「抽象度偏离:全章 41(基线 22,偏差 +86%),本段 48。这句抽象词过密,改成具体动作或物件。只改这一段的写法,不要整章重写。」不含改写后的正文句子。
软化后缀(v5.1.0 新增,只有三类带)abstractDensity 抽象度 / gapIndex 留白指数 / hedgeDensity 不确定性三类指标的 hint,在原有前缀之后追加一句「(仅当这里读起来确实别扭才改;抒情、心理、留白段落属正常写法,可标记 skip 保留。)」。hint 前缀格式未变,只是句尾多这一句——它把这三维的默认处置从「改了」改成「先判断再决定」,抒情 / 心理 / 留白段落本来就该在这三维上偏离全书均值。

排序

sort(items) = severity 降序 → effort 升序 → lineStart 升序 → id 字典序

四级排序的用意依次是:先改影响文风判定最严重的;同严重度时先做便宜的(不让人卡在难改的一段上);同权重同代价时按正文顺序自上而下,便于一次通读改完;最后一级只用于让完全同键的项结果可复现lineStart = 0 的全章层面项排在同键项的最前(行号最小),调用方应显式分支处理,不要把它当作「第一段」。

渲染文本带 id(v5.0.0 修正)plan 的渲染在每条待办的「类型 / 严重度 / 难度 / 位置」行下方紧跟一行 id:fix-…。此前 id 只在返回体与落盘文件里,模型要 mark 得先自己去读 planFile 才能拿到——现在清单里直接可复制,少一次往返。

渲染文本带「处理建议」(v5.1.0 新增)plan 的渲染在 待办 N 条… 之后多一行处理建议,内容是分批改——同一段落 / 同一类型的问题合并成一次修改一轮只处理 3~5 条不必逐条 mark改完最多 verify 一次。这是给调用方的节奏约束,不是新的返回字段:返回体形状未变(items[] 仍是 9 字段,state / stateAt 仍只落盘、只由 mark 写入)。改稿一次只动几处、一次改完整段,比按清单从上到下逐条打磨更接近真实改稿,也能避免「改一处、测一次」的往返成本把注意力耗在数字上。清单上有一条不等于必须改那一条:只有偏离达到 2 倍容差以上、或那一处读起来确实别扭时才动笔——容差带本身取 1.5σ,六维里有一维出带是常见现象,为对齐数字改文只会把作者的写法磨平;确实属于正常写法的(抒情 / 心理 / 留白段落)用 mark state: "skip" 留下记录即可。

八类待办的判定来源

类型字符串是第一列这八个中文词,没有其它取值。判定统一遵守文件头的硬约束:判定不出就跳过该类检查,绝不退化成默认词表兜底。

type判定来源典型触发 / 门槛
句式偏离六维中四个「写法维度」的出带判定(METRIC_TYPEcomplexity / modifierDensity / actionDensity / hedgeDensity 统一映射到本类)该维相对偏差越出容差带,且绝对差值越过量级门槛complexity 0.3(每句小句数)、modifierDensity 8、actionDensity 8、hedgeDensity 4(每千字点数)。低于门槛按「无量级差异」忽略
抽象度过高六维「抽象度」abstractDensity 出带同上,量级门槛 8(每千字点数)
留白异常六维「留白指数」gapIndex 出带同上,量级门槛 12(每千字留白点数)
禁用词设定表 worldview.bannedWords 的正文逐行包含命中(取最近登记且非空的词表;显式登记的 bannedWords: [] 与「未登记」区分开)欧式背景作品出现「上香」一类用语冲突。每章每词最多一项,空串与重复词跳过
语用不符设定表 worldview.speechStyle三路honorBad(客套禁词,逐行精确包含)、ritualBadPatterns(仪式禁式,按正则匹配,模式长度须 ≤ 200 字符)、title仅当规范明确禁用「小姐」时才扫 XX小姐,每章只报第一处)客套表达与已登记的文化基准不符(如中式背景用「提点」)。speechStyle.tone 不参与本类判定
衔接缺失detectChapterBridge(上一章 → 本章) 的候选逐条转成待办(current 就是候选类型名);第一章没有上一章,整类跳过。检查本身抛错时也会产出一条 衔接·检查跳过 候选,不会静默等于「衔接正常」候选类型共 6 种:衔接·时间跳跃(硬跳词直接报)、衔接·时间过渡(软跳且上一章结尾无时间锚)、衔接·语义距离(本地 embedding 相似度 < 0.45,受语义开关与引擎可用性双重门控)、衔接·人物断线(上一章结尾出场人物在本章开头 800 字内全未出现)、衔接·钩子未接(上章钩子与本章开头字符命中率 < 25%)、衔接·检查跳过
伏笔未回收readPlotsstatus !== "done" 的条目:distance = 本章章号 − 最近提及章号distance ≥ 20每条产出一项埋设已超过 20 章仍未回收;章号解析不出的条目跳过(宁可漏报),本章章号也解析不出时整类跳过
情感过直纯文本判定,不依赖设定表:全章「程度副词 + 情绪词」紧邻命中(中间只允许 0–2 字,可带一个「的/地/得」)总数 ≥ 3 次才报,只取命中数最多的前 2 个段落定位词表示例:「非常 / 十分 / 极其 / 极为 / 无比 / 格外 …」×「难过 / 悲伤 / 愤怒 / 高兴 / 恐惧 …」。单次出现视为正常行文(如「她非常高兴地答应了」)不报
「伏笔未回收」的阈值是 20 章(PLOT_STALE_CHAPTERS)。低于该距离的未回收伏笔不产出待办——长篇里同时挂着的线本来就有十几条,全部塞进清单只会让清单失去优先级。该类别按条产出(同章可以有多条伏笔待办),每条都指向跨章层面的问题,因此定位恒为 0/0,并在 hint 里请调用方在本章或近 2 章内回收、或用 novel_plot update 登记新进展。测量入口接受 opts.plotStaleChapters 覆盖该阈值,但工具层不暴露。
指标类(前三类)的容差口径与 novel_style_check 不同。本工具用排除本章后的基线自身的推荐容差(recTol = 作者自身章节波动的 1.5σ),而不是「含本章的全书基线」——含本章时,一个故意写坏的章节会把 σ 抬到 90%+,导致它自己反而不出带,改稿台就哑了。基线章节数 < 3 时容差下限抬到 25%(小样本),基线章节数 < 2 时整类跳过

locate:段落级行号归因

现有分析引擎是段落级 / 句式级统计,没有行级归因。做法是先把正文切成段落并记下行号区间,逐段算指标,再把偏离最严重的若干段回映射成行号。三类问题的归因口径并不相同

// ① 行号索引:换行先归一到 \n,1 起编号(与 novel_read 同口径) lines = text.replace(/\r\n?/g, "\n").split("\n") paragraphs = 连续非空行合并为一段 → 记 [lineStart, lineEnd](空行视为段界) // ② 指标类(句式偏离 / 抽象度过高 / 留白异常)—— 按「段落是否同样出带」归因 for 每个章级出带的维度 v: if |v.value − v.mu| < 量级门槛 → 忽略该维度(不进清单) for 每个段落 p: (p.text.length ≥ 40 字才参与归因) if p 与全章「同向」偏离 且 judgeAgainstBaseline(测量(p)) 该维也出带: 收下 { p, 本段值, |σ| } 按 |σ| 降序取前 1 段(MAX_PARAGRAPH_ITEMS_PER_METRIC = 1) ← v5.1.0:同一维度只报最严重的这 1 段,其余同类段落不再逐段列出, 只在 summary 的计数里体现(旧版该常量为 3) 一段都没有 → 退化成一条「全章层面」项:lineStart = lineEnd = 0,severity 降一档、effort = 3 有段落项 → lineStart/lineEnd = 该段行区间,excerpt = 该段原文截 80 字,effort = 2 // ③ 字面类(禁用词 / 语用不符)—— 逐行精确包含,行号给到「首处命中行」 lineNo = 第一处包含该词的行的行号(firstLineContaining / 正则命中偏移 → lineOfOffset) excerpt = 该行原文 trim 后截 80 字;lineStart = lineEnd = lineNo // ④ 情感过直 —— 段级命中计数;⑤ 衔接缺失 —— 用候选详情里「…」引号内的词做精确行定位; // 引号内取不到词 → 0/0 并在 hint 里说明「没有可精确定位的行」 // ⑥ 伏笔未回收 —— 跨章问题,正文里没有「哪一行」可指,一律 0/0 定位不到时:lineStart = lineEnd = 0,并在 hint 里说明这是全章 / 跨章层面的问题
宁可粗,也不给错的行号。指标类给的是段落区间(约 40 字起)而不是某一行,且段落必须自身也出带才被归因——否则会出现「全章留白偏高,却指向一段根本不带省略号的文字」这种自相矛盾的定位。做出这个取舍是因为错误的行号比宽泛的区间更有害:模型会照着一个错位置去改,改坏的是本来没问题的句子。凡归因置信不足,一律退到整段区间;连段都定不到时给 0 并在 hint 里说明。调用方应对 lineStart = lineEnd = 0 做显式分支,不要把它当作「第 1 行」。

行号的坐标系是绝对行号(1 起,含空行与标题行),与 novel_readformatRead 一致,可直接把 lineStart 交给 novel_readoffset 读回上下文。换行先归一为 \n\r\n / \r 都算一个换行),因此「字符偏移 → 行号」与「行数组下标」永远在同一个坐标系里。

落盘与改稿闭环

路径 = <root>/.novel-writer/audits/fix-plan-<书名>-<归一化章键>.json 归一化章键 = sanitizeSegment(normalizeChapterKey(chapter)) ← 例:「1」「01」「第1章.md」都归一为「第01章」;与 novel_summary 的章号键同源 文件内容 = { tool: "novel_fix_plan", version: "5.0.0", book, chapter { file, number, title }, chapterKey, generatedAt, baselineScope, summary, notes[], items: [ { …9 个字段, state: "open"|"done"|"skip", stateAt: ISO|null } ] } plan → 重新测量 + 原子写(先写 .tmp-<pid>-<时间戳> 再 rename,走在 withFileTx 文件事务内; 与上一次同 id 的项保留其 done/skip 状态与 stateAt) verify → 读该文件 + 对当前正文重算,逐项给出三态(**本次不写盘**) mark → 读-改-写该文件,只把 itemId 指定项的 state / stateAt 更新,并刷新 updatedAt

必然需要落盘:markverify 要跨调用记得「这一项处理过了」。文件按书名 + 归一化章键命名,且章键取自解析后的章文件chapter="1" / "第01章.md" / "码头等船" 三种写法都归到同一份),避免同章不同写法各写一份、verify / mark 换个写法就「找不到清单」。落盘与 novel_continuity_check 的审计报告同处 audits/ 目录,文件名前缀 fix-plan- 与审计报告区分。plan 每次重新测量并整份覆盖该章的清单,但会按 id 把上一次的人工标记搬过来:N 项此前已人工标记 done/skip(状态已保留)会写进 summary。正文改动后 id 会随位置变化,旧 id 的标记自然失效(不会张冠李戴)。

落盘失败(目录只读 / 磁盘满 / 文件被占用)不抛错,本次结果仅内存返回;失败原因被追加进内部的 notes 数组。但要注意:summary 在落盘之前就已拼好,而 notes 不在 plan 的返回字段里buildFixPlan 只返回 book / chapter / items / summary / planFile),因此调用方看不到任何落盘失败的提示——返回体照常给出一个 planFile 路径,但文件并不存在。要间接发现只能靠随后的 mark:它会报「尚未生成改稿清单」。

verify 的三态

status含义detail 的形态
resolved(已解决)该条目在当前正文中已检不出——原来的问题项在本次重算的清单里既没有同 id 命中,也没有「同 type + 同 current」或「同 type」的兜底配对。可能是真改好了,也可能是这一段文本被改动后换了形态。「当前正文中已检不出该问题(已修复或该处文本已改动)」;若该条曾被人工 done / skip,尾部追加「(人工标记 done / skip)」
pending(仍待处理)问题还在。分两种命中路径:① id 精确命中(正文该处未动);② 同 type 兜底——行号或数值变了但同类问题仍存在(先找 current 文本也相同的,找不到再放宽到只要同 type),仍判 pending,并在 detail 里说明位置变化。①「问题仍存在:<current>」;②「问题仍存在(位置/数值已变化 → 现为第 N 行:<current>)」。人工标记不改变判定,只在尾部追加「(人工标记 done / skip)」——避免「标了完成但问题还在」被静默吞掉
new(本次新检出)本次重算清单里存在、但上一次落盘清单里没有配对上的条目——改稿最常见的副作用,必须显式报出来,否则「越改越偏」不会被发现。「本次新检出:<current>」

checked[] 的元素为 { id, type, status, detail },其中 idresolved / pending 保留旧清单的 id,对 new 是本次新生成的 id。summary 汇总三态计数(复测结果:已解决 X 项 / 仍待处理 Y 项 / 新检出 Z 项。)。清单文件不存在时不报错:所有本次检出的项都记为 newsummary 给出「未找到已落盘的改稿清单(<路径>)——本次按当前正文现算:…请先调用 buildFixPlan 生成清单。」

resolved 不等于「已回到带内」。判定口径是「这条待办在当前正文里还找不找得到」,而不是「该维度是否已落回基线带」。同一处改动可能让 A 项 resolved 的同时让 B 项变成 new;要确认整章是否回到带内,仍应跑一次 novel_style_check

不误报

正常文本不产出待办项:各类型只在指标实际出带、命中禁用词 / 语用禁式或钩子确实未承接时才生成条目;全部合格时 items 为空数组,这是一次成功调用而不是失败。为了不误报,实现里设了四道闸: 指标须同时越过相对容差带与绝对量级门槛 段落必须与全章同向偏离且自己也出带才被归因(同维最多 1 段,v5.1.0 起由 3 收紧到 1——同一维度只报最严重的那一段,其余同类段落由 summary 的计数体现,清单不再被同一维度的多段刷屏); 无基线、无 worldview、无伏笔、无法判定章号时整类跳过,不退化成默认词表; 章节正文短于 40 字时跳过全部指标类检查(空文本的六维全是 0,与基线一比必然「处处出带」)。

清单长度有上限:单次最多 12 条DEFAULT_MAX_ITEMS,v5.1.0 起由 40 收紧到 12),超出部分按排序截断,并在 summary 的备注里披露「待办超过 12 条,已按严重度从高到低截断 N 条(清单上限固定,先处理保留下来的高优先级项)」。上限收紧的用意是让清单可执行:一次抛出四十条,模型只会从上往下挑几条做,其余全部沦为空转;只留 12 条则每一条都是排序意义上最值得先动的,被截断的部分不是「漏掉了」,而是「这一轮先不管」——先按清单顺序处理保留下来的项,改完再跑一次 plan,原先被截断的项若仍成立会重新排进来(同一维度现在只报最严重的 1 段,因此回访时的清单也不会重新膨胀)。若清单接近或撞到这个上限,说明这一章的偏离是系统性的(通常该整段重写,而不是逐项修)。注意上限由调用方传参放宽:buildFixPlan 内部虽有 opts.maxItems,但工具层固定传 {},提示里不会再出现「可传 opts.maxItems」这类做不到的建议。

约束与边界

  • 不生成正文hint 只给改写方向,anchor 只给可参照的原著样例;返回体里没有任何「改写后的句子」。插件不调用在线模型,也从不代替作者写字。
  • 只读正文,只写自己的清单文件plan 不修改章节正文,落盘只发生在 <root>/.novel-writer/audits/ 下的 fix-plan-*.jsonverify 连清单都不写;mark 只改该文件里的处置状态。
  • 取消会抛错,不会伪装成空结果:调用被中止时(AbortError)三个动作一律向上抛,不落进任何降级分支——否则一次取消会返回「成功但清单为空」,被误读成「这一章没有问题」。
  • 行号可以被整段,但不会给错:指标类 locate 的最小粒度是段落;字面类(禁用词 / 语用)才精确到行;lineStart = lineEnd = 0 表示全章 / 跨章层面,需按分支处理。行号是绝对行号(与 novel_read 同口径),不随任何 offset 重编号。
  • id 与正文位置绑定:id 由「类型 + 内部 key + 行号区间」派生,因此正文改动会改变 id。跨次 verify 先按 id 精确配对,配不上再按「同 type(优先同 current)」兜底,因此挪了位置但问题同类的项仍判 pending;类型也变了或问题消失的项才会落到 resolved / new。这是「稳定 id」的边界:它跨次调用稳定,不跨正文改动稳定。
  • mark 的校验只有三条itemId 非空、state ∈ {done, skip}、该章已落盘清单里存在该 id。任一不满足即报错;它不重新测量、不校验正文是否真的改了,标记纯粹是给下一次 plan / verify 看的处置台账。
  • 依赖已有材料,质量随材料走:禁用词 / 语用项依赖 novel_settings 的设定表,衔接项依赖上一章可读,伏笔项依赖 novel_plot 的登记质量。材料缺失时相应类别不产出待办(而不是产出猜测),原因写进 notessummary,并随清单一起落盘。
  • 六维相关项可能整体缺失:需要至少 2 个基线章节(即全书除本章外至少 2 章、全书合计至少 3 章)才能估计作者自身波动;本章正文短于 40 字也跳过。此时句式偏离 / 抽象度过高 / 留白异常三类不产出,原因写进备注;style_check 的同一限制在此同样成立。
  • 首次调用需要测量本章:复用分章测量缓存(metricChaptersCached),已预热时成本很低;正文刚改过则要重算本章。
  • 优先级算法属经验值:severity 的分档阈值与 effort 的分级是规则给定值,不是从真实稿件学出来的;清单顺序应按实际改稿手感校准,而不是当作绝对权威。
  • 工具开关与引擎门禁是两道不同的闸:三个动作都要过 assertToolEnabled("novel_fix_plan")(工具级开关关闭时直接报错,可用 novel_sentence_config 重新开启);此外 plan / verify 还要过「写作助手功能」总开关(关闭时拒绝执行,文案引导去侧边栏开启),mark 不受第二道闸约束

相关工具

  • novel_style_check —— 同一次测量的「数据视图」:相似度、偏差清单、六维出带判定与 fixAnchors。本工具是它的「行动视图」。
  • novel_continuity_check —— 跨章审计(衔接 / OOC / 大纲走偏),其衔接检测与本工具的「衔接缺失」同类;审计报告与本工具的清单同处 audits/
  • novel_chapter_brief —— 写之前的材料装配(承接口、伏笔距离、基线、锚段),与本工具构成「写前 / 改后」两端。
  • novel_settings —— 禁用词与语用规范的数据源;要先补登记再跑本工具,否则禁用词 / 语用两类不会产出。
  • novel_read —— 按 locate.lineStart 读回该段上下文,核对是否真是要改的地方。

源码位置

  • lib/fixplan.js —— 改稿台主体:buildFixPlan / verifyFixPlan / markFixItem 三个导出,以及内部的 computeFixPlan(测量 → 六类收集 → 排序截断 → 落盘)、makeItem / makeItemId(9 字段与稳定 id)、buildParagraphIndex / lineOfOffset / firstLineContaining / lineExcerpt(段落索引与行号归因)、sortItems(四级排序)、METRIC_TYPE / TYPE_SLUG / METRIC_DIRECTION / METRIC_MIN_DELTA(类型映射与阈值常量)、collectMetricItems / collectBannedItems / collectPragmaticItems / collectEmotionItems / collectBridgeItems / collectPlotItems(六类判定)。
  • lib/index.js —— registerNovelFixPlan:工具注册、参数与输出 schema、三个动作分支、markitemId + state 校验(在引擎门禁之前分支)、plan / verify 的「写作助手功能」门禁、渲染文本。
  • lib/core.js —— withFileTx / atomicWriteJson(清单文件的原子写与串行事务)、normalizeChapterKey / sanitizeSegment(落盘文件名的章号归一与清洗)、readSettings(禁用词与语用规范)、readPlots(伏笔表)、detectChapterBridge(衔接检测)、metricChaptersCached / buildStyleAnchorPackage(分章测量缓存与锚包)、readSentenceState(引擎开关)。
  • lib/style-metrics.js —— measureStyleMetrics / computeBaselineFromPerChapter / judgeAgainstBaseline / METRIC_ORDER / METRIC_LABELS(六维测量、基线与出带判定)。
  • lib/core.js —— ALL_TOOLS / TOOL_LABELS 注册表条目(开关面板与关闭报错文案)。