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

novel_chapter_brief

开写包。把「写这一章之前需要读的一切」装配成一次只读调用:上一章结尾原文(承接口)、上一章钩子、本章大纲方向行、按命中过滤的人物卡、未回收伏笔及其埋设距离、世界观禁用词与语用规范、全书六维基线、原著锚段与句式骨架、上一章风格自检结论,以及由此生成的三段式 plan 与本章禁用清单。chapter 取默认值 "next" 时指向「当前最大章号 + 1」,该文件可以尚未创建

为什么要有它。在 5.0.0 之前,「动笔前要读的东西」分散在 6~8 次工具调用里(基线、锚段、伏笔、设定、上一章正文、上一章钩子、大纲方向行…),全靠提示词叮嘱模型逐步执行。漏读没有任何人会察觉——而漏读正是文风漂移与设定矛盾的头号来源。本工具不新增任何测量能力,只是把已有材料装配成一次调用:材料出现在返回体里,就不再依赖模型记性。它是 novel_new_chapter 那套「基线 + 锚段 + 骨架」的抽离与扩容——建章节时附带的材料与本工具同源(同一测量缓存、同一锚包抽样),因此两处口径不会分叉。

参数

参数类型必填说明
bookstring书名,即 novels/ 下的子目录名。空串或纯空白被拒绝,值经 sanitizeSegment 清洗。书名目录不存在是本工具唯一会抛错的情形(其余材料缺失一律降级,见下文)。
chapterstring章节标识,四种形态:"next"(默认,= 当前最大章号 + 1,文件可能尚未创建)、章号(1 / 01)、文件名(第01章.md,可省略扩展名)或标题子串。解析规则与 novel_read 完全同源(findChapter:精确文件名优先于章号,纯数字不做子串兜底)。
budgetstring枚举 "compact"(默认) / "full",控制各字段的截断档位。只裁剪返回体,不改变计算量——见下文「截断档位」。
rootstring章节库根目录。省略时按 config.root → 会话工作目录回退;MCP 形态下越界值被静默回退到启动参数 --root 指定的书库根。

参数对象声明 additionalProperties: falsebudget 省略时按 compact 处理。

输出结构

返回体共 16 个键(13 个契约必返 + chapter / worldview / baseline 三个「取不到就整键省略」)。材料取不到时字段缺省或为空数组,同时在 degraded 里写清是哪些材料、为什么取不到;调用方应以 degraded 判断「这份材料是否完整」,而不是以字段是否存在来猜测。返回前经 dropNullDeep 处理——值为 null / undefined 的键整键丢弃(含数组内的 null 元素),而不是置 null

字段类型内容
bookstring净化后的书名(sanitizeSegment 的结果),可直接当目录名用。
chapterobject目标章标识:{ file, number, title }isNext = truefile推导出的预期文件名(见下文「目标章定位」),title 取「剧情大纲」里该章的方向行(截 60 字),未排到该章时该键省略。目标章无法唯一确定时(同章号多文件、找不到章节)chapter 整键省略,原因在 degraded
isNextboolean是否指向「文件尚不存在的章」。判定是 targetChapter === null && targetNumber !== nullchapter 取默认值或 "next" 时必为 true;此外传入一个尚未创建的章号(如 chapter: "5" 而磁盘上只有 1–4 章)也是 true——它同样按「将写此章」装配。
anchorstring承接口:上一章结尾原文。取 prevText.slice(-anchorChars).trim()compact 截 300 字符(默认)、full 截 600 字符;取的是结尾片段而非开头,正文据此续接语感与场景。无上一章时为空串。
previousHookstring上一章结尾钩子(创作资料「钩子记录.md」中该章的回填内容,按章号从「编号行」解析)。未回填时为空串并计入 degraded
outlineDirectionstring本章的大纲方向行(创作资料「剧情大纲.md」中对应章号的那一行)。无大纲或未排到该章时为空串并计入 degraded
charactersarray按命中过滤的人物卡(三源合并novel_settings 人物表 + 创作资料「主要人物设定.md」「次要人物设定.md」)。命中 = 人物名或其别名(长度均 ≥ 2 字)出现在上一章正文或本章大纲方向行中。compact 取前 5 条,full 不截断;无人物卡或全部未命中时为空数组。恒为数组(契约必返)。
openPlotsarray未回收伏笔(status !== "done"),每条附加 distance排序:优先级升档 → distance 降序 → id 字典序high > medium > lowpriority 缺失或非枚举值一律归一为 medium)。无伏笔表时为空数组并计入 degraded恒为数组
worldviewobject世界观用语规范:{ basis, bannedWords, recommended, speechStyle }。该键由设定表全部 worldview 条目合并而成:basis 拼入各条的 basis / description / ritual / notes(去重后以「;」连接),bannedWords / recommended / speechStyle 分别做并集与浅合并。四项全空时 worldview 整键省略并计入 degraded
baselineobject六维基线:每维 { mu, sigma, sigmaMeasured, sigmaUsed, sigmaClamped, low, high, recTol }recTol = 作者自身波动的 1.5σ 换算成百分比,取整到 5、下限 10、上限 100)。语料为 metricChaptersCached 的逐章指标,目标章已存在时先把它排除出基线语料(与 novel_style_check 同口径)。语料为空时 baseline 整键省略并计入 degraded
anchorsarray原著锚段,元素 { label, text }compact 取前 2 条、full 取前 5 条。抽样与分类规则与 novel_style_checkfixAnchors 同源。语料排除目标章自身(改稿场景下不能把「正在改的那一段」当原著范本),与 novel_fix_plan 的锚段口径一致;chapter="next" 时目标章不存在,等价于全用。
skeletonsarray句式骨架,元素 { type, text }compact 取前 3 条、fullslice(0, 8)。注意 buildStyleAnchorPackage 自身最多只产出 4 条,因此 full 的 8 只是「不额外截断」,实际条数上限仍是 4。语料同 anchors(排除目标章)。
lastVerdictstring上一章的六维对照结论,形如「第 N 章六维对照:<judge.summary>(由逐章指标缓存现算)」。它不是 novel_style_check 的落盘结论(插件从不落盘该结论),而是由缓存的逐章指标现算:以「除上一章外的其余章」为基线,容差由 core.buildTolerance 构造——novel_fix_plan 同一实现recTol 为主,基线不足 3 章时下限抬到 25%,可被用户的 styleTolerance 覆盖同名维度)。除上一章外不足 2 章可测时不给结论(单章基线估不出 σ);取不到时为空串并计入 degraded
avoidarray本章禁用清单三类来源合并,元素 { kind, detail }kind 取值为 已回收伏笔 / 禁词 / 已退场角色。三类都取不到时为空数组。恒为数组
planobject三段式行动框架:{ previousState, goal, checklist }checklist 为字符串数组),由上述材料生成,见下文「plan 的生成口径」。恒为对象
degradedarray降级说明,元素是字符串(不是对象),已按 Set 去重。空数组表示材料完整
chapter / worldview / baseline 是「有就返回、没有就整键省略」,其余 13 个键恒在。调用方读 worldview 前必须先判存在性;读 previousHook / outlineDirection / lastVerdict 这类必返字段时则只需判空串。判「材料为什么缺」应一律读 degraded

characters[] 条目

字段类型内容
name / role / descriptionstring人物名、角色档(设定表 / 主要 / 次要)与简介。同名人物会在三源之间合并:设定表条目给 role: "设定表",创作资料「主要人物设定.md」命中则升为 主要,仅出现在「次要人物设定.md」时为 次要
mentionsinteger人物名与全部别名在上一章正文中出现的次数合计(子串计数,非行数)。
sourcesstring[]命中来源,取值 上一章出场 / 大纲方向。至少命中一个才会出现在 characters 里。
traits / relationships / aliasstring / string[]仅当对应字段非空时出现(alias 只保留长度 ≥ 2 的别名)。
offstageboolean仅当为 true 时出现:设定表的 status / notes / description / traits 或创作资料人物简介命中「退场 / 已死 / 死亡 / 身亡 / 殉 / 不再出场 / 下线 / 殁」之一。渲染层会据此标注「已退场,勿写其出场」。

排序为:mentions 降序 → 「上一章出场」优先 → 名称中文拼音序。

openPlots 条目

字段类型内容
id / contentstring伏笔表的条目 id 与内容描述,原样透传。
type / prioritystring伏笔类型(剧情 / 设定 / 道具 / 人物 / 其他)与优先级(high / medium / low)。type 缺失时为空串;priority 缺失或非枚举值时归一为 medium(不是最低档)。
firstChapterinteger | 省略最早提及章号 = chapter / mentionedIn[] / lastMentioned 三处能解析出的章号取最小值;三处都解析不出时为 null该键被整键丢弃(同时 firstChapterKnown 出现且为 false)。
distanceinteger埋设距离 = 全书最大章号 − firstChapter(下限 0)。全书最大章号取「现有章节里能解析出章号的最大值」,不是章文件个数firstChapter 不可知时退化为全书最大章号(即「按全书起算」),并在 degraded 里点名。
note / payoffCondition / relatedCharacters / locations仅当伏笔表里登记过且非空时透传(后两项要求是非空数组)。

注意与 novel_plotentry.chapter 区分:本工具返回的是解析后的章号 firstChapter,不再返回原始的 chapter 文本字符串。

计算与装配

执行顺序:读创作资料(大纲 / 钩子)→ 定位目标章 → 定位上一章 → 逐章读正文 → 组装锚段与骨架 → 算六维基线 → 现算上一章结论 → 汇总伏笔 → 合并设定表与人物卡 → 生成本章禁用清单 → 生成 plan → 按 budget 截断。全程只读:除复用既有分析缓存(六维测量缓存文件由 metricChaptersCached 按内容哈希增量维护,这是既有行为)外不写任何文件——伏笔表也刻意不走 core.readPlots(它在「新位置缺失 + 旧位置存在」时会迁移写盘),改用只读版本按「新位置 → 旧位置」顺序自行解析。

1 · 目标章定位与 isNext

maxNumber = max( 现有章节的可解析章号 ) ← 无可解析章号时为 0 chapter 省略 或 === "next": number = maxNumber + 1 ← 无可解析章号时为 1 file = deriveChapterFileName(chapters, number) 优先沿用最后一章那种「纯章号文件名」的形态与数字宽度 第07章.md → 第08章.md 01.md → 02.md 形态不规则(带标题等)时回退 → "第" + padStart(number, 2, "0") + "章.md" isNext = true 否则 → findChapter 解析(文件名优先于章号;同章号多文件抛错→ 降级为「目标不确定」): 命中磁盘章节 → isNext = false 未命中但参数能解析成章号 → number = 该章号,file 同上推导,isNext = true 两者都不成立 → chapter 整键省略(degraded 说明)

isNext 的语义是「这一章还没写」而不是「参数是 next」:因此本工具不会因为目标章文件不存在而报错,反而正是它最有用的时刻——此时上一章就是全书最后一章,承接口与钩子都指向它。已有的 novel_style_check 无法对未创建的文件工作,本工具则可以先给材料。

上一章的取法:章号小于目标章号的最后一章;目标章号不确定时退回「全书最后一章」并在 degraded 里说明。全书没有任何更早的章节时,anchorpreviousHook 均为空串。

2 · 承接口 anchorpreviousHook

prev = 章号小于目标章号的最后一章(isNext 时即全书末章) anchor = prev 正文的结尾片段 compact → 末尾 300 字符 full → 末尾 600 字符 previousHook = 钩子记录.md 中 prev 那一行的回填内容 编号行形态:- 3 正文… / - 第3章 正文… / - 3、正文…(同章号只取第一条)

只给结尾而不给全章,是因为续写真正需要的接口是「上一句停在哪里、场景与语气是什么」;全章正文可通过 novel_read 按需补读,而全章塞进返回体只会挤占上下文。hook 未回填、或整份「钩子记录.md」都不存在时,两种情况在 degraded 里是不同的文案(「未初始化」与「没有该章的钩子」),便于区分「没建资料」与「忘了回填」。

3 · 人物卡过滤 characters

pool = 设定表人物表(name ≥ 2 字,含 alias 别名,别名同样需 ≥ 2 字) ∪ 创作资料「主要人物设定.md」(role = 主要) ∪ 创作资料「次要人物设定.md」(role = 次要) ← 同名以设定表为底,名称相同则合并 hit = 条目名或任一别名 出现在「上一章正文 ‖ 本章大纲方向行」中 输出 = hit 条目,compact 取前 5 条、full 不截断;无人物卡时为空数组 排序 = mentions 降序 → 「上一章出场」优先 → 名称中文拼音序

过滤器面向的是「这一章可能写到谁」,因此判据取上一章 + 本章大纲两份文本,而不是全书:全书命中会把整张人物表原样带出,等于没过滤。人物设定行的解析口径是 - 名字:简介没有冒号也认novel_outline action=character 只给名字不给简介时写出的就是 - 名字,旧正则会把插件自己写的人物卡静默丢掉);冒号后为空同样算一张卡(描述为空串)。会跳过「角色设定 / 世界观 / 不允许的事件 / 主线目的 / 题材偏好 / 额外要求 / 用户原创设定 / 用户角色设定」这些模板段标签——它们不是人名。设定表与创作资料都没有人物卡时为空数组,并计入 degraded

「已退场」判定是限定词表(退场 / 已死 / 死亡 / 身亡 / 殉 / 不再出场 / 下线 / 殁)正则命中设定表 status / notes / description / traits 或创作资料人物简介;命中即打上 offstage: true宁可漏报不可误报——误报会让模型把活人写死。

4 · 未回收伏笔与 distance

open = 伏笔表中所有 status !== "done" 的条目 ← 非 done 即视为未回收 firstChapter = min( 可解析章号 ),三处来源依次并入: entry.chapter (登记时的章号) entry.mentionedIn[] (scan 写入的章节文件名,逐个解析) entry.lastMentioned (最近一次命中的文件名) 三处都解析不出 → firstChapter 省略、firstChapterKnown = false distance = maxNumber − firstChapter ← maxNumber = 全书最大可解析章号 firstChapter 不可知时 → maxNumber(按全书起算,并在 degraded 点名) 结果为负时钳到 0 排序 = priority 升档(high → medium → low,脏值归一为 medium) → distance 降序 → id 字典序

distance 是「埋了多久没回收」的直接量化:它把静态的伏笔表变成带时间压力的清单,让调用方先处理埋得最久、优先级最高的线。已回收(done)的伏笔不进入 openPlots,而是从另一个方向进入 avoid(见下节)。

5 · 世界观 worldview 与禁用清单 avoid

worldview = 设定表 worldview 表全部条目合并后的 { basis, bannedWords, recommended, speechStyle } basis ← 各条的 basis / description / ritual / notes 去重后以「;」连接 bannedWords ← 各条并集(保序去重) recommended ← 各条 Object.assign 浅合并 speechStyle ← 数组字段并集、对象字段浅合并、标量取最后一个非空值 四项全空 → worldview 整键省略 avoid = ① 与本章方向行关键词有交集的已回收伏笔(kind = "已回收伏笔") 关键词 = plotKeywords(content) ∪ plotKeywordsAll(content) 长词(≥ 3 字)命中 1 个即算;2 字短词需命中 ≥ 2 个, 且排除「是已登记人名/别名的子串」的短词(只共享人名不算重埋) 最多 5 条 ② worldview.bannedWords 的前 10 条(kind = "禁词",有替代词时一并给出) ③ 标记为已退场的角色,最多 5 条(kind = "已退场角色") 三类合并为同一数组;都取不到时为空数组

第 ① 类的用意是防止「把已经用掉的线当作新钩子再埋一次」——这是长篇里最常见的自我重复。第 ② 类沿用设定表里已经登记好的禁用词,只取前 10 条是为了控制返回体体积:完整的禁用词表仍可用 novel_settings 查。第 ③ 类把「已退场角色」前置到清单里,避免与人物卡里的出场提示互相打架。

6 · 六维基线 baseline

perCh = metricChaptersCached( root, book, 全书章节文本 ) ← 自带缓存,不逐章重读 corpus = perCh 排除目标章(目标章尚不存在时 = perCh 全量) baseline = computeBaselineFromPerChapter( corpus ) → 每维 { mu, sigma, sigmaMeasured, sigmaUsed, sigmaClamped, low, high, recTol } → 语料为空时整键省略

基线与 novel_style_reportnovel_style_checknovel_new_chapter同一份分章测量缓存<root>/.novel-writer/analysis/<book>-chapters-metrics.json),缓存键按内容哈希且与章序无关,因此四个工具交替调用不会互相击穿缓存。只有正文 ≥ 40 字的章节才进入测量;性能:命中缓存时基线几乎零成本;长书首次调用仍需读全书算一遍分章指标(可能数秒),此后增量命中。

budget 不省计算。两档只决定返回体里各字段截到什么程度,测量与抽样步骤完全相同。书很长且已经预热过缓存时,用 compact 只是让上下文更省,不会让工具跑得更快;反过来,首次调用无论哪一档都要付全书测量的代价。

7 · 锚段与骨架 anchors / skeletons

anchors = buildStyleAnchorPackage( … ).anchors ← 产出上限 5 条 compact → 前 2 条 full → 前 5 条(即全部) skeletons = buildStyleAnchorPackage( … ).skeletons ← 产出上限 4 条 compact → 前 3 条 full → slice(0, 8)(实际仍受 4 条上限约束) 锚段抽样:全书文本按 /\n+/ 切段 → 每段 trim 后长度 ≥ 40 字才参选 对话桶(含引号且 ≤ 200 字)取 2 条、心理桶(含 想/觉得/心/怕/慌/记忆 且无引号)取 1 条、 描写桶(无引号且 80–200 字)取 2 条;三桶皆空时退化为「原文」桶取 3 条 每个桶内按全书等距抽样(非取开头 n 条),每条截 200 字 骨架抽样:全书文本按 [。!?…] 切句 → 去空白后长度 20–45 字才参选 → 去重后等距抽 4 条 按 对话 / 疑问 / 心理(想|觉得|仿佛|好像|似乎)/ 陈述 打标签

novel_new_chapternovel_style_check 共用同一个锚包构建函数,因此三处口径不会分叉。anchors 是「校准语感」的样例,不是造句模板——锚段读一遍对上味道即可,skeletons 只在写不出该类型句子时参考,不要照它造句、不要套句式模板;基线数字只是事后校验。两者都在返回体里,但用途不同。本书正文不满足抽样条件(所有段落都短于 40 字)时两个数组都为空,并计入 degraded

8 · 上一章结论 lastVerdict

base = computeBaselineFromPerChapter( 除上一章外的其余章 ) tol[key] = { low: -recTol, high: recTol } ← recTol 缺失时回退 15 若 opts.featState.styleTolerance[key] 同时有 low/high 数字 → 覆盖该维 judge = judgeAgainstBaseline( 上一章指标, base, tol ) lastVerdict = "第 N 章六维对照:" + judge.summary + "(由逐章指标缓存现算)"

这里刻意不做落盘:插件从不把 novel_style_check 的结论写进任何文件,因此「上一章自检结论」是每次由缓存的逐章指标现算的(口径与 novel_style_check 一致,并沿用侧边栏保存的自定义容差)。上一章正文不足 40 字、或除上一章外没有其它可测章节时为空串并计入 degraded

9 · plan 的生成口径

字段来源内容
previousStateprevChapter + previousHook / anchor「上一章停在哪里」的浓缩复述,三选一:有钩子 → 带上「结尾钩子:…」;无钩子但有正文 → 「正文已在 anchor(末 N 字),钩子未回填」;无正文 → 「正文为空」。全书开篇时为「无上一章(本章为全书开篇)」。
goaloutlineDirectionpreviousHook → 开篇本章目标,四级退化:有方向行 → 「按大纲方向行写:…」;无方向行但有钩子 → 「大纲未给方向,承接上一章钩子推进:…」;都没有但有上一章 → 「从上一章结尾(anchor)自然接续」;开篇 → 「按创作设定开篇」。
checklist上述各材料字符串数组,按固定顺序追加:读一遍锚段校准语感——不要照 skeletons 造句(固定首条,v5.1.0 改口径)→ 承接钩子 / 承接上一章结尾(有钩子取前者,否则取后者;全书开篇时两条都不加)→ 按方向行推进 → priority === "high" 的未回收伏笔(最多 3 条,带 distance)→ 别重复埋已回收伏笔 → 避免使用的禁词(前 5 条)→ 已退场角色 → 本章相关人物(最多 5 个,退场者带标注)→ 建议自检(novel_style_check单一维度轻微出带属正常波动,不要为对齐数字改文)→ 收尾按需,不必每章全套:确属新埋的伏笔才 novel_plot add;钩子与摘要在章节稳定后再回填(novel_outline action=hook number=N)。

plan材料的索引,不是新信息:它不引入任何未在其它字段出现过的判断,只把散落的材料组织成「动笔前看一眼」的三段式;不编造剧情,所有文本都从已有字段派生(超长片段统一按 clip 截断加省略号)。字段本身不随 budget 截断。

截断档位 budget

字段compact(默认)full
anchor末尾 300 字符末尾 600 字符
anchors前 2 条前 5 条(= 构建函数的产出上限,即全部)
skeletons前 3 条slice(0, 8)——构建函数最多只产出 4 条,故实际仍是 4
characters前 5 条不截断
其余字段两档一致(openPlots / worldview / baseline / avoid / plan / degraded 不做档位裁剪)

默认 compact 是因为本工具的目标场景是接着写下一章:承接口 300 字符足以承接语气,锚段 2 条 + 骨架 3 条足以定调。追求更完整的锚段材料(锚段 5 条、承接口 600 字符)时应显式传 full,或改用 novel_style_checkfixAnchors(写完之后按偏差取锚段,比写之前更准)。budget 只接受 "full" 一个特殊值,传入任何其它值(含 "compact" 与未声明值)都按 compact 执行。

优雅降级

以下情形一律不报错:相应字段缺省或为空数组,同时在 degraded 里说明材料名与原因。这是刻意的设计——「书还没建大纲」不是调用错误,不应该让整个流程中断。

情形返回
没有创作资料(未 initpreviousHook / outlineDirection / chapter.title 为空,plan.goal 退化;degraded 分别给出「创作资料未初始化」与「无钩子记录」两条说明
没有伏笔表openPlots = []avoid 只保留禁词与已退场角色,degraded 说明
没有设定表worldview 整键省略、characters 为空数组(除非创作资料里有人物设定),avoid 只保留已回收伏笔一类
全书只有 1 章无「上一章」→ anchor / previousHook 为空、lastVerdict 为空;除目标章外无其他可测章节 → baseline 整键省略
书太短算不出基线baseline 整键省略(不输出 μ=0 的假基线),degraded 区分「全书没有 ≥ 40 字的章节」与「除目标章外没有可测章节(书只有 1 章)」两种原因
目标章无法唯一确定chapter 整键省略、isNextnumber 是否可解析而定,degraded 说明「同章号多文件」或「找不到章节」
锚包构建失败 / 无合格段落anchors / skeletons 为空数组,degraded 说明「抽样要求段落 ≥ 40 字」
单章读取失败(编码 / 权限 / 被占用)该章按空正文处理,degraded 逐章点名(不影响其余材料)
上一章没有可对照的指标lastVerdict 为空串,degraded 分三种情形:「上一章无六维指标」「除上一章外没有可测章节」「除上一章外不足 2 章可测(估不出 σ,与 novel_fix_plan 同门槛)」
书名目录不存在抛错(唯一例外:无书可开写,返回空材料没有意义)

取消信号(exec.signal / AbortError不属于降级:它会原样向上传播,不被吞掉。

约束与边界

  • 只读工具:除复用既有分析缓存(metricChaptersCached 维护的分章测量缓存,属既有行为)外不写任何文件——不建章节、不改伏笔状态、不落盘报告。伏笔表刻意不走会在旧位置迁移写盘的 core.readPlots,而是用本模块的只读版本。需要真正创建章节时用 novel_new_chapter
  • 唯一抛错路径是书名目录不存在;材料缺失、缓存不可用、锚包失败、书太短都走降级。调用方不应把空字段读成「这本书没有伏笔」——必须读 degraded 区分「确实没有」与「取不到」。
  • chapternovel_read 共用解析规则:精确文件名优先于章号;只给章号且命中多个同号文件时会报错并列出候选(本工具把它降级为「目标不确定」而不是中断)。纯数字参数不做子串兜底。
  • isNext 不等于「参数是 next」:它表示目标章文件尚不存在,因此传入一个未来的章号同样为 true。此时返回的 file 是预期文件名而非磁盘事实(可用 novel_chapters 核对)。
  • avoid 只取禁用词前 10 条:这是返回体预算的取舍,不是「只禁这 10 个词」。完整词表在设定表里,语用规范(称谓 / 客套 / 仪式禁式)通过 worldview.speechStyle 整体给出。
  • distance 依赖章号可解析chapter / mentionedIn / lastMentioned 三处都解析不出章号时,firstChapter 省略、firstChapterKnown = falsedistance 退化为「全书最大章号」(相当于按全书起算),并在 degraded 里点名前 5 条。排序时这类条目按 priority 归一为 medium、再按已退化的 distance 参与比较——不会因此被排到最后,这是刻意的:距离未知不等于不重要。
  • 人物命中判定是纯字符串包含,不做语义推理:别名登记不全时会漏命中,同名不同人(如「小周」既指人物又指地名)时会误命中;名字与别名短于 2 字的一律跳过。这是零 token 本地实现的必然边界,命中结果应视为候选
  • 首次调用可能较慢:长书且缓存未预热时需读全书算分章指标;这一点与 budget 无关(见上文)。
  • 工具开关:执行前经 assertToolEnabled 校验状态文件,被关闭时直接报错(可用 novel_sentence_config 重新开启)。本工具不受「写作助手功能」总开关约束——它与 plan / verify 那类引擎门禁无关。

相关工具

  • novel_new_chapter —— 真正创建章节文件的工具;其附带的 baseline / anchors / skeletons 与本工具同源,本工具是那套材料的抽离与扩容(多了钩子、大纲方向行、人物卡、伏笔距离、禁用清单与 plan)。
  • novel_style_check —— 写完之后的对照判定(建议跑一次,偏离明显时才据此修正);其 fixAnchorslastVerdict 分别为本工具锚段的同源材料与上一章结论的来源。
  • novel_outline —— 维护本工具读取的创作资料(钩子记录、剧情大纲方向行)。
  • novel_plot —— 伏笔登记表;action: "graph" 的结构视图给出跨章的伏笔生命周期,其 plotLifecycle.distance 与本工具的 openPlots.distance 是同族口径(都按「全书最大章号 − 最早提及章号」),但本工具的 firstChapter 只从登记字段(chapter / mentionedIn / lastMentioned)推导,而结构视图还会用伏笔内容的关键词去全书正文里找首次命中章,两者结果可能不同。
  • novel_settings —— 人物表与世界观用语规范(禁用词 / 推荐替代 / 语用规范)的维护入口。
  • novel_read —— 需要上一章全文(而非仅结尾 300 字)时按行号补读。

源码位置

  • lib/brief.js —— 开写包主体:buildChapterBrief(唯一的对外导出)与其内部各步——BRIEF_BUDGETS(两档预算常量)、readPlotsReadOnly(只读伏笔读取)、readCreationFile / parseNumberedLines(创作资料读取与编号行解析)、deriveChapterFileName(预期文件名推导)、plotFirstChapter(最早提及章号)、mergeWorldview(worldview 多条目合并)、parseCharacterLines / OFFSTAGE_RE / countMentions(人物卡三源合并与命中统计)、plotKeywordsAll(不截断的关键词版本,供「已回收伏笔 ∩ 方向行」使用)、clip / isAbort
  • lib/index.js —— registerNovelChapterBrief:工具注册、参数校验(budget 归一)、输出 schema 与渲染文本。
  • lib/core.js —— metricChaptersCached(六维分章测量缓存)、buildStyleAnchorPackage(锚段与骨架的抽样与上限)、readSettings(设定表)、plotsFile / legacyPlotsFile / normalizePlotEntry / plotKeywords(伏笔表路径与条目归一)、creationDir / CREATION_FILES(创作资料路径)、scanChapters / findChapter / readTextFile / parseChapterNumber(章节定位、读取与章号解析)。
  • lib/style-metrics.js —— computeBaselineFromPerChapter(基线的 μ / σ / recTol 口径)/ judgeAgainstBaseline(上一章六维判定)/ METRIC_ORDER / METRIC_LABELS
  • lib/core.js —— ALL_TOOLS / TOOL_LABELS 注册表条目(开关面板与关闭报错文案)。