novel_chapter_brief
actionChains(原著典型颗粒度的动作链)、clauseFrames(本章大纲子句框架)、prevChapterIssues(上一章遗留问题)、symptomPrescriptions(本章症状处方)、referenceProvenance(参照集来源)。五者受新增总预算 enhanceChars 约束,装填优先级为「上一章遗留 & 症状处方 > 子句框架 > 动作链」。plan.checklist 另新增一条恒定项:章末必须停在具体动作、突发异响或一句没答完的话上,不写总结陈词与未来展望。baselineScope(文案)与 baselineMode(all / custom)披露,并写进模型可见的渲染文本。
开写包。把「写这一章之前需要读的一切」装配成一次只读调用:上一章结尾原文(承接口)、上一章钩子、本章大纲方向行、按命中过滤的人物卡、未回收伏笔及其埋设距离、世界观禁用词与语用规范、必用词硬清单(lexicon)、六维基线(语料按面板「基线范围」选取,默认全书)、原著锚段与句式骨架、上一章风格自检结论,以及由此生成的三段式 plan 与本章禁用清单;v7.0.0 起另附五项近距护航材料(原著动作颗粒度、本章大纲子句框架、上一章遗留问题、本章症状处方、参照集来源)。chapter 取默认值 "next" 时指向「当前最大章号 + 1」,该文件可以尚未创建。
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
book | string | 是 | 书名,即 novels/ 下的子目录名。空串或纯空白被拒绝,值经 sanitizeSegment 清洗。书名目录不存在是本工具唯一会抛错的情形(其余材料缺失一律降级,见下文)。 |
chapter | string | 否 | 章节标识,四种形态:"next"(默认,= 当前最大章号 + 1,文件可能尚未创建)、章号(1 / 01)、文件名(第01章.md,可省略扩展名)或标题子串。解析规则与 novel_read 完全同源(findChapter:精确文件名优先于章号,纯数字不做子串兜底)。 |
budget | string | 否 | 枚举 "compact"(默认) / "full",控制各字段的截断档位。只裁剪返回体,不改变计算量——见下文「截断档位」。 |
root | string | 否 | 章节库根目录。省略时按 config.root → 会话工作目录回退;MCP 形态下越界值被静默回退到启动参数 --root 指定的书库根。 |
参数对象声明 additionalProperties: false;budget 省略时按 compact 处理。
输出结构
返回体共 24 个键(13 个契约必返 + baselineScope / baselineMode 两个恒定出现的范围披露字段 + worldview + chapter / lexicon / baseline 三个「取不到就整键省略」+ v7.0.0 新增的 5 个近距护航键)。v6.9.0 另有一个条件键 rawMode(仅非净化模式开启时出现,见下表,不计入上面的 24);worldview 自 v6.9.5 起恒在(未登记设定时按文化基准注入默认禁用词表)。材料取不到时字段缺省或为空数组,同时在 degraded 里写清是哪些材料、为什么取不到;调用方应以 degraded 判断「这份材料是否完整」,而不是以字段是否存在来猜测。返回前经 dropNullDeep 处理——值为 null / undefined 的键整键丢弃(含数组内的 null 元素),而不是置 null。
| 字段 | 类型 | 内容 |
|---|---|---|
book | string | 净化后的书名(sanitizeSegment 的结果),可直接当目录名用。 |
chapter | object | 目标章标识:{ file, number, title }。isNext = true 时 file 为推导出的预期文件名(见下文「目标章定位」),title 取「剧情大纲」里该章的方向行(截 60 字),未排到该章时该键省略。目标章无法唯一确定时(同章号多文件、找不到章节)chapter 整键省略,原因在 degraded。 |
isNext | boolean | 是否指向「文件尚不存在的章」。判定是 targetChapter === null && targetNumber !== null:chapter 取默认值或 "next" 时必为 true;此外传入一个尚未创建的章号(如 chapter: "5" 而磁盘上只有 1–4 章)也是 true——它同样按「将写此章」装配。 |
anchor | string | 承接口:上一章结尾原文。取 prevText.slice(-anchorChars).trim(),compact 截 300 字符(默认)、full 截 600 字符;取的是结尾片段而非开头,正文据此续接语感与场景。无上一章时为空串。 |
previousHook | string | 上一章结尾钩子(创作资料「钩子记录.md」中该章的回填内容,按章号从「编号行」解析)。未回填时为空串并计入 degraded。 |
outlineDirection | string | 本章的大纲方向行(创作资料「剧情大纲.md」中对应章号的那一行)。无大纲或未排到该章时为空串并计入 degraded。 |
characters | array | 按命中过滤的人物卡(三源合并:novel_settings 人物表 + 创作资料「主要人物设定.md」「次要人物设定.md」)。命中 = 人物名或其别名(长度均 ≥ 2 字)出现在上一章正文或本章大纲方向行中。compact 取前 5 条,full 不截断;无人物卡或全部未命中时为空数组。恒为数组(契约必返)。 |
openPlots | array | 未回收伏笔(status !== "done"),每条附加 distance。排序:优先级升档 → distance 降序 → id 字典序(high > medium > low;priority 缺失或非枚举值一律归一为 medium)。无伏笔表时为空数组并计入 degraded。恒为数组。 |
worldview | object | 世界观用语规范:{ basis, bannedWords, recommended, speechStyle, culture, cultureName, cultureSource }。basis 由设定表全部 worldview 条目的 basis / description / ritual / notes 去重后以「;」连接;bannedWords / recommended / speechStyle 一律取自 lib/core.js 的唯一裁决点 resolveStyleRule(settings, 章节正文)——与 novel_continuity_check 的用语扫描同源(v6.4.0 前开写包只读用户登记表、没有默认表兜底,两处词源不一致是本轮缺陷的根因)。因此 bannedWords = 去重(用户登记 ∪ 文化基准默认表),显式登记 bannedWords: [] 表示「不要默认表」;culture / cultureName / cultureSource(worldview / detected / default)说明这套规范的文化基准与来源。v6.9.5 起:即使未登记任何 worldview 条目,worldview 也照常返回——按文化基准(正文检测 → 默认欧式)注入默认禁用词表,并在 degraded 写明「已按文化基准「…」注入默认禁用词表(共 N 条)」;此前该键整键省略,于是"开写包不给禁词、写完审计却报 69 条"。 |
lexicon | object | 必用词硬清单(v6.2.0 新增):{ enabled, total, shown, truncated, groups, counts, files },groups 为 [{ kind, terms: [{ term, avoid[], scene?, note?, priority?, scope? }] }]。数据源为 novel_lexicon 的合并两层词表(同名以书级为准,层开关关闭者不参与),且仅当功能开关「必用词优先」(lexiconFirst)开启、词表非空时出现;其余情形整键省略(开关关闭、有层被关闭、读取失败都会在 degraded 里写明)。compact 注入 60 条、full 注入 200 条,每条保留 2 / 3 个 avoid 通用替身词。 |
baseline | object | 六维基线:每维 { mu, sigma, sigmaMeasured, sigmaUsed, sigmaClamped, low, high, recTol }(recTol = 作者自身波动的 1.5σ 换算成百分比,取整到 5、下限 10、上限 100)。语料为 metricChaptersCached 的逐章指标,先按面板「基线范围」筛出可选章、再排除目标章(v6.5.5:按全书 默认=旧行为,按部分 只用勾选的章——是「只用」,不是「不够再补」;目标章永远排除,即使它被勾选;与 novel_style_check 同口径、同走 core.resolveBaselineScope)。实际基线章少于 3 章时推荐容差抬到至少 ±25%(单章也能当基线,不会因 σ=0 把一切都报成偏差);勾选的章若取不到(已删除 / 正文过短)则被跳过并如实计数(文案写作「…,其中 K 章未纳入基线(已删除、或正文过短无法测量)」——本路递的是逐章指标,短章本就不在其中);一章都取不到则回退 按全书(本键只是测量值;范围文案见同级的 baselineScope)。语料为空时 baseline 整键省略并计入 degraded。 |
baselineScope | string | 取样范围披露(v6.5.5,恒定出现、不在 required 契约里):写明 baseline 与 anchors / skeletons 的共同取样范围。由 core.resolveBaselineScope 统一给出——按全书 时为「全书除本章外的 N 章」(只有 1 章可用时直接给章名),按部分 时为「自定义基线 N 章(选中 M 章)」(有取不到的章时括注为「(选中 M 章,其中 K 章未纳入基线(已删除、或正文过短无法测量))」;N = 本路语料池里实际入选的章数——开写包递的是逐章指标,故 N 就是真正进 μ/σ 的可测章数);一章都取不到而回退时按原因给两种文案:「(自定义选择已失效,已回退全书)」或「(选中的章只有本章自身,不能作为自己的基线,已回退全书)」。渲染层把它写进模型可见的文本:【风格基线】…(取样范围:<文案>)——不写出来,模型会把「你勾的那几章」当成整本书来校准语感,那恰好是这个功能想避免的事。 |
baselineMode | string | 范围档位(v6.5.5,恒定出现):all = 按全书(默认) / custom = 按面板选中的部分章。回退发生后取值是 all,要区分「本来就是按全书」与「回退」,读 baselineScope 文案里的后缀——按原因两种:「(自定义选择已失效,已回退全书)」/「(选中的章只有本章自身,不能作为自己的基线,已回退全书)」。渲染层据它切换锚段标签:custom 时「【原著锚段·照这个味道写】」改为「【锚段·照这个味道写】(取样范围:…)」——按部分时那些章未必是「原著」(用户可能正是勾了他自己满意的续写章)。 |
anchors | array | 原著锚段,元素 { label, text }:compact 取前 2 条、full 取前 5 条。抽样与分类规则与 novel_style_check 的 fixAnchors 同源。语料先按面板「基线范围」选取、再排除目标章自身(v6.5.5;按部分 时锚段只出自勾选的章——否则锚段会逐代变成 AI 自己的文风),与 novel_fix_plan 的锚段口径一致;chapter="next" 时目标章不存在,等价于只做范围筛选。渲染层标签随 baselineMode 变:all → 「【原著锚段·照这个味道写】」,custom → 「【锚段·照这个味道写】(取样范围:…)」(按部分时那些章未必是「原著」)。 |
skeletons | array | 句式骨架,元素 { type, text }:compact 取前 3 条、full 取 slice(0, 8)。注意 buildStyleAnchorPackage 自身最多只产出 4 条,因此 full 的 8 只是「不额外截断」,实际条数上限仍是 4。语料同 anchors(按「基线范围」筛选并排除目标章)。 |
actionChains | array | 原著典型颗粒度动作链(v7.0.0,可空数组):字符串数组,只收参照集正文里的完整句(15~70 字、≥2 个动作动词、≥1 个物件名词、不含引号;不截断)。取样分两步:全部候选按「动作动词数 → 物件数 → 句长」排序后只取 p60~p85 分位那一段(专挑最密的打斗句,示范就会永远比作者自己写得更密);再从池子里按目标章号确定性轮换(起点 = 章号 × 7 mod 池长)取最多 3 条,相邻章天然不重叠。池子 ≤ 3 条时全量返回,一条都没有时为空数组并在 degraded 说明。用途是判断「动作有没有连起来」,不是造句模板——不要照抄原句。 |
clauseFrames | array | 本章大纲子句框架(v7.0.0,可空数组):元素 { clause, category, label, format }。clause 是从大纲方向行切出的原句片段(按 ,;。、→ 与换行切分,最多 8 条;只切出一段时再按「并且 / 并 / 又 / 随后 / 接着 / 却 / 不料 / 为了 / 然后」二次切分),category / label 是 7 类动词元类之一——move 位移入场 / fight 交锋对抗 / trade 交易获取 / discover 发现窥探 / talk 交谈博弈 / endure 静态承受 / introspect 内省推演(连动句取末位动词,一条都没命中的兜底 introspect)。format 是固定填空格式「需指定:1 个可触碰器物 + 1 个肢体动作 + 1 个落点(这一幕停在哪)」——插件不生成问句、也不指定物象,填什么由模型在 novel_new_chapter 的 plan 里写。 |
prevChapterIssues | array | 上一章遗留问题(v7.0.0,最多 3 条,可空数组):元素 { key, severity, title, detail },与 novel_new_chapter 的写盘体检调用同一个诊断函数(lib/chapter-diagnose.js),因此同一章不会出现「写盘说合格、开写包说有问题」。只有当上一章正是诊断集里最近的那一章时才给出;用途是「先修再写」——上一章的毛病换章之后就没人管了。 |
symptomPrescriptions | array | 本章症状处方(v7.0.0,每章最多 3 条,可空数组):元素 { key, severity, text },key 取值 ending_preach / bare_dialogue / slop / abstract / action_thin / sense_mono / object_thin,text 是可执行的方向而不是句子。三个防「用错药」的机制:相关性门控(本章大纲里没有交谈 / 交易类子句就不发「防站桩对话」的处方)、3 章 2 中滞回(最近 3 章里 ≥2 章出现才激活,或上一章单次严重到 severity ≥ 4 立刻激活)、上限 3 条并按严重度排序。新书前 3 章(参照集不足 3 章可测)走固定 3 条极简底线:ending_preach / object_thin / abstract。 |
referenceProvenance | string | 参照集来源(v7.0.0,恒定出现):custom = 用户在面板勾了标杆章;all-unverified = 按全书取样、可能含 AI 续写章。后者会在 degraded 里多一句提示,建议去侧边栏「风格基线范围」只勾原著或最满意的标杆章,防止动作链与相对分位随续写漂移(参照章少于 4 章时不打扰)。它与诊断集是两回事:参照集「向原著看齐」,诊断集只看目标章之前最近 1~3 章。 |
lastVerdict | string | 上一章的六维对照结论,形如「第 N 章六维对照:<judge.summary>(由逐章指标缓存现算)」。它不是 novel_style_check 的落盘结论(插件从不落盘该结论),而是由缓存的逐章指标现算:以「除上一章外的其余章」为基线(v6.5.5 起这份语料同样先按面板「基线范围」筛选,按部分 时只取勾选的章,上一章仍被排除),容差由 core.buildTolerance 构造——与 novel_fix_plan 同一实现(recTol 为主,实际基线章不足 3 章时下限抬到 25%,可被用户的 styleTolerance 覆盖同名维度)。除上一章外不足 2 章可测时不给结论(单章基线估不出 σ);取不到时为空串并计入 degraded。 |
avoid | array | 本章禁用清单,元素 { kind, detail },来源合并为同一数组:已回收伏笔 / 禁词 / 已退场角色,以及 v6.4.0 起的语用规范四类 语用规范·称谓 / 语用规范·客套 / 语用规范·仪式 / 语用规范·语气(speechStyle 里未覆盖的键回落为 语用规范·<键名>)。全部取不到时为空数组。恒为数组。 |
plan | object | 三段式行动框架:{ previousState, goal, checklist }(checklist 为字符串数组),由上述材料生成,见下文「plan 的生成口径」。恒为对象。 |
degraded | array | 降级说明,元素是字符串(不是对象),已按 Set 去重。空数组表示材料完整。 |
rawMode | object | v6.9.0,仅非净化模式(features.rawWriting)开启时出现:{ on, hint, sample:{chapters,chars}, baseline:{softeningPer1000,dialogueRatio}, target, checklist }。baseline.softeningPer1000 是原著里「把场面绕过去」的写法密度(省略号整段 / 明示跳过 / 时间跳 / 代称替代 / 跳出场景的评论,启发式、纯规则),取样取自目标章之前的最近 ≤60 章、正文 ≥40 万字时截断;target 是本章要求;同一条硬约束还会进 plan.checklist,渲染文本里出现【非净化模式·目标带】。开关关闭时该键不存在(不是空对象)。 |
chapter / lexicon / baseline 是「有就返回、没有就整键省略」,其余键恒在(不再写死"19 个"这种数字——键数每加一个字段就得改一次,历史上已经因此错过一次;worldview 自 v6.9.5 起也归入「恒在」:无设定时按文化基准注入默认词表;v7.0.0 新增的 5 个键恒在但可能是空数组)。调用方读 lexicon 前必须先判存在性;读 previousHook / outlineDirection / lastVerdict 这类必返字段时则只需判空串。判「材料为什么缺」应一律读 degraded。
characters[] 条目
| 字段 | 类型 | 内容 |
|---|---|---|
name / role / description | string | 人物名、角色档(设定表 / 主要 / 次要)与简介。同名人物会在三源之间合并:设定表条目给 role: "设定表",创作资料「主要人物设定.md」命中则升为 主要,仅出现在「次要人物设定.md」时为 次要。 |
mentions | integer | 人物名与全部别名在上一章正文中出现的次数合计(子串计数,非行数)。 |
sources | string[] | 命中来源,取值 上一章出场 / 大纲方向。至少命中一个才会出现在 characters 里。 |
traits / relationships / alias | string / string[] | 仅当对应字段非空时出现(alias 只保留长度 ≥ 2 的别名)。 |
offstage | boolean | 仅当为 true 时出现:设定表的 status / notes / description / traits 或创作资料人物简介命中「退场 / 已死 / 死亡 / 身亡 / 殉 / 不再出场 / 下线 / 殁」之一。渲染层会据此标注「已退场,勿写其出场」。 |
排序为:mentions 降序 → 「上一章出场」优先 → 名称中文拼音序。
openPlots 条目
| 字段 | 类型 | 内容 |
|---|---|---|
id / content | string | 伏笔表的条目 id 与内容描述,原样透传。 |
type / priority | string | 伏笔类型(剧情 / 设定 / 道具 / 人物 / 其他)与优先级(high / medium / low)。type 缺失时为空串;priority 缺失或非枚举值时归一为 medium(不是最低档)。 |
firstChapter | integer | 省略 | 最早提及章号 = chapter / mentionedIn[] / lastMentioned 三处能解析出的章号取最小值;三处都解析不出时为 null,该键被整键丢弃(同时 firstChapterKnown 出现且为 false)。 |
distance | integer | 埋设距离 = 本章目标章号 − firstChapter(下限 0)。v6.2.0(缺陷 #64)起基准是目标章号——开写包支持改稿已存在的章、也支持补写中间章,用「全书最大章号」会把目标章之后才埋的伏笔记成「已埋 N 章」,等于让模型提前泄露一条还不存在的线;目标章号不可知时才退回全书最大章号,并在 degraded 与清单里写明基准。全书最大章号取「现有章节里能解析出章号的最大值」,不是章文件个数。firstChapter 不可知时退化为该基准值(即「按全书起算」),并在 degraded 里点名。 |
note / payoffCondition / relatedCharacters / locations | — | 仅当伏笔表里登记过且非空时透传(后两项要求是非空数组)。 |
注意与 novel_plot 的 entry.chapter 区分:本工具返回的是解析后的章号 firstChapter,不再返回原始的 chapter 文本字符串。
计算与装配
执行顺序:读创作资料(大纲 / 钩子)→ 定位目标章 → 定位上一章 → 逐章读正文 → 组装锚段与骨架 → 算六维基线 → 现算上一章结论 → 算参照集分位 / 动作链 / 子句框架 / 诊断集与症状处方 → 汇总伏笔 → 合并设定表与人物卡 → 生成本章禁用清单 → 生成 plan → 按 budget 截断。全程只读:除复用既有分析缓存(六维测量缓存文件由 metricChaptersCached 按内容哈希增量维护,这是既有行为)外不写任何文件——伏笔表也刻意不走 core.readPlots(它在「新位置缺失 + 旧位置存在」时会迁移写盘),改用只读版本按「新位置 → 旧位置」顺序自行解析。
1 · 目标章定位与 isNext
isNext 的语义是「这一章还没写」而不是「参数是 next」:因此本工具不会因为目标章文件不存在而报错,反而正是它最有用的时刻——此时上一章就是全书最后一章,承接口与钩子都指向它。已有的 novel_style_check 无法对未创建的文件工作,本工具则可以先给材料。
上一章的取法:章号小于目标章号的最后一章;目标章号不确定时退回「全书最后一章」并在 degraded 里说明。全书没有任何更早的章节时,anchor 与 previousHook 均为空串。
2 · 承接口 anchor 与 previousHook
只给结尾而不给全章,是因为续写真正需要的接口是「上一句停在哪里、场景与语气是什么」;全章正文可通过 novel_read 按需补读,而全章塞进返回体只会挤占上下文。hook 未回填、或整份「钩子记录.md」都不存在时,两种情况在 degraded 里是不同的文案(「未初始化」与「没有该章的钩子」),便于区分「没建资料」与「忘了回填」。
3 · 人物卡过滤 characters
过滤器面向的是「这一章可能写到谁」,因此判据取上一章 + 本章大纲两份文本,而不是全书:全书命中会把整张人物表原样带出,等于没过滤。人物设定行的解析口径是 - 名字:简介,没有冒号也认(novel_outline action=character 只给名字不给简介时写出的就是 - 名字,旧正则会把插件自己写的人物卡静默丢掉);冒号后为空同样算一张卡(描述为空串)。会跳过「角色设定 / 世界观 / 不允许的事件 / 主线目的 / 题材偏好 / 额外要求 / 用户原创设定 / 用户角色设定」这些模板段标签——它们不是人名。设定表与创作资料都没有人物卡时为空数组,并计入 degraded。
「已退场」只识别明确的结构化状态,或句首直接描述该角色的退场/死亡陈述;人物简介中提及他人的死亡、否定或假设不直接触发。比如“调查父亲死亡真相”不会设置 offstage:true。这是保守规则,复杂叙述仍应由作者核对。
4 · 未回收伏笔与 distance
distance 是「埋了多久没回收」的直接量化:它把静态的伏笔表变成带时间压力的清单,让调用方先处理埋得最久、优先级最高的线。已回收(done)的伏笔不进入 openPlots,而是从另一个方向进入 avoid(见下节)。
5 · 世界观 worldview 与禁用清单 avoid
degraded 里写明「哪套基准、共多少条」,不再整键省略)
recommended ← rule.recommended(用户登记优先覆盖默认表)
speechStyle ← rule.speechStyle(合并全部 worldview 条目的语用规范)
culture / cultureName / cultureSource ← 文化基准与来源(worldview / detected / default)
avoid = ① 与本章方向行关键词有交集的已回收伏笔(kind = "已回收伏笔")
关键词 = plotKeywords(content) ∪ plotKeywordsAll(content)
长词(≥ 3 字)命中 1 个即算;2 字短词需命中 ≥ 2 个,
且排除「是已登记人名/别名的子串」的短词(只共享人名不算重埋)
最多 5 条
② worldview.bannedWords 的全部词(kind = "禁词")
每 12 条一组,detail 写明「第 i/n 组,共 N 条」,替代词内联为「词→替词」
③ 语用规范 worldview.speechStyle(kind = "语用规范·称谓" / "·客套" / "·仪式" / "·语气")
称谓 = title
客套 = honorBad(有 honorGood 时内联「词(改用:替词)」,每 12 条一组)
仪式 = ritualBadPatterns(按匹配式给出)+ ritualGoodNote
语气 = tone
④ 标记为已退场的角色,最多 5 条(kind = "已退场角色")
四类合并为同一数组;都取不到时为空数组第 ① 类的用意是防止「把已经用掉的线当作新钩子再埋一次」——这是长篇里最常见的自我重复。第 ② 类不再截断(v6.4.0 前只取前 10 条,而合并默认表后动辄 40+ 词,模型最多只看得到 10 个且看不到总数),改为按 12 条一组紧凑渲染,不静默丢词。
v6.4.0 修复(P0-1):语用规范此前从未到达模型。speechStyle(称谓 / 禁客套语 / 仪式禁式 / 语气)一直被合并进返回体的 worldview,但全文件没有任何消费点——它既没进 avoid 也没进 plan.checklist,于是用户精心登记的「称谓用先生/阁下」「禁提点/承蒙」「仪式禁烧香磕头」「语气禁文言四字客套」一个字都没到模型。第 ③ 类就是补上的消费点:逐项渲染成模型可见的条目,plan.checklist 里再点名一次。这套规范正是为防中式语用渗透而设计的,缺了它,欧式设定下写出的「多谢提点」没有任何一道防线会提醒模型。
6 · 必用词 lexicon(硬清单)
avoid 前 N 个(compact 2 / full 3),再按 kind 分组
渲染(render)新增分区:
【必用词·优先使用】凡涉及以下概念,必须用表内词写成;标了「勿用」的通用说法一个都不要出现:
〔类型〕
- 词条★|(勿用:替身1/替身2)|场景:标签|备注
(共 N 条,本章只注入 M 条;完整表用 novel_lexicon action=list)
plan.checklist 追加一条硬指令(前 12 个词条列名,超出以「等 N 个」收尾)v6.3.0 修复:这段分区此前从未渲染。lib/brief.js 一直在产出 lexicon 载荷、也一直在 plan.checklist 里写下「完整表见上方【必用词·优先使用】」,但 render 里没有对应块——模型可见的文本只来自 render,于是那句硬指令指向了一个不存在的分区,avoid(勿用替身词)一条都到不了模型。现已补上;lexicon 字段本身一直在 JSON 返回体里,词表的登记与审计不受该缺口影响。
与世界观禁用词的分工:novel_settings 的 worldview.bannedWords 管「不要写什么」(进 avoid 清单,kind = "禁词"),本分区管「必须写什么」;两者可以指向同一件事的两面(禁「铜灯」+ 必用「青铜灯」),但数据、开关与消费路径互不重叠。完整语义与九个 action 见 novel_lexicon。
7 · 六维基线 baseline
基线与 novel_style_report、novel_style_check、novel_new_chapter 走同一份分章测量缓存(<root>/.novel-writer/analysis/<book>-chapters-metrics.json),缓存键按内容哈希且与章序无关,因此四个工具交替调用不会互相击穿缓存。只有正文 ≥ 40 字的章节才进入测量;性能:命中缓存时基线几乎零成本;长书首次调用仍需读全书算一遍分章指标(可能数秒),此后增量命中。
<root>/.novel-writer/baseline/<书名>.json({ mode, chapters, updatedAt };mode 只认 all / custom,其余回落 all;单条文件名截 220 字、上限 2000 章),文件不存在即等于 按全书;面板里的容差(styleTolerance)则是全局一份、所有书共用。它要解决的问题是:模型续写每章只参考上一章,从第 2 章起参考的就是自己的产物,而基线原本从全书取样(含 AI 写的章)、锚点自己也在漂——把标杆钉在作者指定的章上,插件才能回答「相对这几章,这章偏了多少」。它不判定「你是否漂移了」。范围是会披露的(v6.5.5):返回体新增 baselineScope(文案)与 baselineMode(all / custom),渲染文本也带上——【风格基线】…(取样范围:<文案>),custom 时锚段标签同时改为「【锚段·照这个味道写】(取样范围:…)」。按全书 与回退两种情况都沿用同一套措辞(回退时文案里按原因带「(自定义选择已失效,已回退全书)」或「(选中的章只有本章自身,不能作为自己的基线,已回退全书)」)。baseline 只是测量值,范围说明读 baselineScope。
budget 不省计算。两档只决定返回体里各字段截到什么程度,测量与抽样步骤完全相同。书很长且已经预热过缓存时,用 compact 只是让上下文更省,不会让工具跑得更快;反过来,首次调用无论哪一档都要付全书测量的代价。
8 · 锚段与骨架 anchors / skeletons
与 novel_new_chapter、novel_style_check 共用同一个锚包构建函数,因此三处口径不会分叉。anchors 是「校准语感」的样例,不是造句模板——锚段读一遍对上味道即可,skeletons 只在写不出该类型句子时参考,不要照它造句、不要套句式模板;基线数字只是事后校验。两者都在返回体里,但用途不同。本书正文不满足抽样条件(所有段落都短于 40 字)时两个数组都为空,并计入 degraded;锚包"生成失败"是另一回事(v6.6.0 起两者文案分开,见下表)。
9 · 上一章结论 lastVerdict
这里刻意不做落盘:插件从不把 novel_style_check 的结论写进任何文件,因此「上一章自检结论」是每次由缓存的逐章指标现算的(口径与 novel_style_check 一致,并沿用侧边栏保存的自定义容差)。上一章正文不足 40 字、或除上一章外没有其它可测章节时为空串并计入 degraded。
10 · plan 的生成口径
| 字段 | 来源 | 内容 |
|---|---|---|
previousState | prevChapter + previousHook / anchor | 「上一章停在哪里」的浓缩复述,三选一:有钩子 → 带上「结尾钩子:…」;无钩子但有正文 → 「正文已在 anchor(末 N 字),钩子未回填」;无正文 → 「正文为空」。全书开篇时为「无上一章(本章为全书开篇)」。 |
goal | outlineDirection → previousHook → 开篇 | 本章目标,四级退化:有方向行 → 「按大纲方向行写:…」;无方向行但有钩子 → 「大纲未给方向,承接上一章钩子推进:…」;都没有但有上一章 → 「从上一章结尾(anchor)自然接续」;开篇 → 「按创作设定开篇」。 |
checklist | 上述各材料 | 字符串数组,按固定顺序追加:读一遍锚段校准语感——不要照 skeletons 造句(固定首条,v5.1.0 改口径)→ 承接钩子 / 承接上一章结尾(有钩子取前者,否则取后者;全书开篇时两条都不加)→ 按方向行推进 → priority === "high" 的未回收伏笔(最多 3 条,带 distance)→ 别重复埋已回收伏笔 → 避免使用世界观禁用词(前 8 个列名 + 「共 N 条,完整清单见 avoid 的「禁词」」)→ 称呼、客套、仪式与语气按语用规范写(仅当 worldview.speechStyle 存在时追加)→ 必用词优先(硬约束,完整表见上方【必用词·优先使用】;前 12 个词条列名,超出以「等 N 个」收尾)→ 已退场角色 → 本章相关人物(最多 5 个,退场者带标注)→ 建议自检(novel_style_check;单一维度轻微出带属正常波动,不要为对齐数字改文)→ 收尾按需,不必每章全套:确属新埋的伏笔才 novel_plot add;钩子与摘要在章节稳定后再回填(novel_outline action=hook number=N)。 |
plan 是材料的索引,不是新信息:它不引入任何未在其它字段出现过的判断,只把散落的材料组织成「动笔前看一眼」的三段式;不编造剧情,所有文本都从已有字段派生(超长片段统一按 clip 截断加省略号)。字段本身不随 budget 截断。
v7.0.0 起 checklist 恒定多一条「章末收束闸门」:「本章必须停在具体动作、突发异响或一句没答完的话上;不写总结陈词与未来展望。」它排在自检建议之后、收尾提醒之前,是动笔时还来得及改的一条;写完之后同一件事由 novel_new_chapter 的 postWriteAudit 以「章末升华」再核一次(只告警、不阻断保存)。
11 · 增强包:颗粒度 / 子句框架 / 处方(v7.0.0)
为什么要一个总额:这几块各自只加一两百字,叠起来足够把弱模型的注意力稀释掉。优先级把最近的事放前面——上一章遗留和本章处方是「现在就得处理」的,动作链只是语感参照。四项全部可空数组;空数组只说明这次没有可用材料,具体原因看 degraded(例如参照集里找不出符合 15~70 字、≥2 个动作动词、≥1 个物件的完整句)。
clauseFrames.length === 1)多一行三步引导。大纲只切出 1 条子句时,渲染文本在「本章欠账·逐条具象兑现」段里、该子句行之前追加一行:
单句大纲请拆成三步写入 plan:① 入场接触的器物与环境 → ② 核心动作交锋或阻碍 → ③ 章末停在哪个未完动作或异响。
常量
SINGLE_CLAUSE_STEP_HINT(lib/obligations.js),K > 1 时不追加。它仍然是填空说明、不是问句:三步只划定「必须出现哪几类要素」,具体器物、动作与落点由模型在 novel_new_chapter 的 plan 里填,插件不生成任何正文。clauseFrames 条目的 format 行(「需指定:1 个可触碰器物 + 1 个肢体动作 + 1 个落点」)照常输出——三步引导是在它前面追加的一行,不是替换掉单条子句的填空格式。
sense_mono)改千字密度口径。参照集分位里 senseChannels.<通道> 存的是次数(p10 / p50 / p85 / p95),次数随章长线性增长——参照集全是万字长章时 p10 可能是 5 次,2000 字的正常章永远达不到;反过来 tactile / olfactory / temperature 的 p10 常为 0,旧判据 hits < 0 恒假、这三个通道永不出处方。现在两边都换算成每千字多少次再比:
diagnoseChapterSymptoms 的每个样本都带 chars(该章正文 text.length),所以第 ① 条路恒可用、走的是密度对密度;拿不到参照集章长(或该通道地板恰为 0)时才退化回次数判定,而不是拿本章长度去折算参照集的次数(那正是「参照集万字长章把 2000 字正常章压死」那个误报的翻版)。某通道的四个分位全为 0 时整条通道跳过(书太短、该通道恒为 0,不冤报)。severity 不变:本章该通道 0 命中记 4,其余记 3。
referenceProvenance);上一章遗留与症状处方从诊断集取样(只看目标章之前最近 1~3 章,自定义参照集时会剔掉被勾成标杆的原著章)。两者混用久了,「原著颗粒度」会逐渐变成 AI 自己写出来的味道。
enhanceChars 总额(且在 lib/brief.js 的 BRIEF_BUDGETS 里,属于源码常量、面板不暴露)。也就是说:如果你只想看「承接口 + 锚段 + 骨架 + 清单」这类基准材料、不想要处方与欠账,目前没有开关可以关掉它们——只能忽略这几段,或改源码常量。之所以先不加开关:功能开关每多一个,面板就多一行、多一条「为什么这次没出现」的解释成本;而在真实使用里这四项是同一套「动笔前护航」,拆开用的场景还没有被验证过。
截断档位 budget
| 字段 | compact(默认) | full |
|---|---|---|
anchor | 末尾 300 字符 | 末尾 600 字符 |
anchors | 前 2 条 | 前 5 条(= 构建函数的产出上限,即全部) |
skeletons | 前 3 条 | slice(0, 8)——构建函数最多只产出 4 条,故实际仍是 4 |
characters | 前 5 条 | 不截断 |
lexicon | 注入 60 条,每条保留 2 个 avoid | 注入 200 条,每条保留 3 个 avoid |
| 增强包四项(v7.0.0) | 合计 450 字符 | 合计 900 字符 |
| 其余字段 | 两档一致(openPlots / worldview / baseline / avoid / plan / degraded / referenceProvenance 不做档位裁剪) | |
默认 compact 是因为本工具的目标场景是接着写下一章:承接口 300 字符足以承接语气,锚段 2 条 + 骨架 3 条足以定调。追求更完整的锚段材料(锚段 5 条、承接口 600 字符)时应显式传 full,或改用 novel_style_check 的 fixAnchors(写完之后按偏差取锚段,比写之前更准)。budget 只接受 "full" 一个特殊值,传入任何其它值(含 "compact" 与未声明值)都按 compact 执行。v7.0.0 的增强包按 enhanceChars 整包计量(不是每项各截一刀),所以四项可能被截在不同的位置——被截掉的条数写在 degraded 里。
优雅降级
以下情形一律不报错:相应字段缺省或为空数组,同时在 degraded 里说明材料名与原因。这是刻意的设计——「书还没建大纲」不是调用错误,不应该让整个流程中断。
| 情形 | 返回 |
|---|---|
没有创作资料(未 init) | previousHook / outlineDirection / chapter.title 为空,plan.goal 退化;degraded 分别给出「创作资料未初始化」与「无钩子记录」两条说明 |
| 没有伏笔表 | openPlots = [],avoid 只保留禁词 / 语用规范 / 已退场角色,degraded 说明 |
| 没有设定表 | v6.9.5 起:worldview 不再整键省略——按正文自动检测(或默认欧式)的文化基准注入默认禁用词表,degraded 写明「设定表无 worldview 条目:已按文化基准「…」注入默认禁用词表(共 N 条)」;characters 为空数组(除非创作资料里有人物设定),avoid 只保留该禁用词表与已回收伏笔 |
| 登记了 worldview 但没填 basis / 禁词 / 语用规范 | 仍按文化基准(登记文本 → 正文检测 → 默认欧式)注入默认禁用词表,worldview.bannedWords 为合并结果;degraded 写「仅按文化基准「…」注入默认禁用词表(共 N 条)」,避免调用方误以为「没有规范」 |
| 没有必用词表 / 开关关闭 | lexicon 整键省略并静默(lexiconFirst 关闭且词表已有条目时,degraded 明确写「已登记 N 条,但开关当前为关闭」) |
| 词表某一层被关闭 | 该层不参与合并;degraded 写「必用词表有层被关闭:本书层已关闭(N 条)」,并指路侧边栏「必用词表」页 |
| 词表条目超出注入上限 | lexicon.truncated = true(shown < total),degraded 写「共 N 条,本章只注入 M 条(场景命中与高优先者先占位)」 |
| 词表读取失败(文件损坏等) | lexicon 整键省略,degraded 写「必用词表读取失败:…」(不中断整份开写包) |
| 全书只有 1 章 | 无「上一章」→ anchor / previousHook 为空、lastVerdict 为空;除目标章外无其他可测章节 → baseline 整键省略 |
| 书太短算不出基线 | baseline 整键省略(不输出 μ=0 的假基线),degraded 区分「全书没有 ≥ 40 字的章节」与「除目标章外没有可测章节(书只有 1 章)」两种原因 |
| 基线范围选的章「不够用」(v6.5.5) | 实际参与统计的章(已剔除目标章)少于 3 章时照常出基线,但推荐容差抬到至少 ±25%(不降级、不报缺);只有 1 章可用时 μ 由单章得出、σ 不可估计,调用方不要把它当精确判据——实际用了几章看 baselineScope 里的章数 |
| 基线范围选的章都取不到(v6.5.5) | 取不到的章(已删除 / 正文过短)跳过并如实计数(文案「…,其中 K 章未纳入基线(已删除、或正文过短无法测量)」);一章都取不到时回退 按全书,不返回空基线,并把回退按原因写进 baselineScope:「…(自定义选择已失效,已回退全书)」或「…(选中的章只有本章自身,不能作为自己的基线,已回退全书)」 |
| 目标章无法唯一确定 | chapter 整键省略、isNext 视 number 是否可解析而定,degraded 说明「同章号多文件」或「找不到章节」 |
| 锚包生成失败(内部错误) | anchors / skeletons 为空数组,degraded 写「原著锚段/句式骨架生成失败(内部错误,本次抽样未完成)——与「正文段落不足 40 字」不是一回事,请查 novel_diagnose 的事件日志或重试」。旧版把这种系统失败也说成「抽样要求段落 ≥ 40 字、本书正文不满足」——把内部错误讲成"你的正文太短",v6.6.0 已分开 |
| 锚包无合格段落(正文确实太短) | anchors / skeletons 为空数组,degraded 说明「抽样要求段落 ≥ 40 字」——只有真的抽不出段落时才有这句 |
| 增强包超出本章预算(v7.0.0) | 四项按「上一章遗留 & 症状处方 > 子句框架 > 动作链」的顺序装填,装不进的那几条不进返回体(数组变短,不是算错),degraded 写「增强包超出本章预算(N 字),已截断 K 条低优先内容」 |
| 参照集里取不到动作链 / 诊断集为空(v7.0.0) | actionChains 为空数组时 degraded 说明「参照集没有符合条件的完整动作句(15~70 字、≥2 个动作动词、≥1 个物件)」;诊断集没有可测章节(目标章之前不足 1 章、或章章正文过短)时 prevChapterIssues / symptomPrescriptions 为空数组或退回新书底线,不报错 |
参照集按全书(all-unverified,v7.0.0) | 不降级,但 degraded 多一句提示:参照集含已写续写章,建议在「风格基线范围」只勾标杆章(参照章少于 4 章时不提示——新书本来就没几章) |
| 单章读取失败(编码 / 权限 / 被占用) | 该章按空正文处理,degraded 逐章点名(不影响其余材料) |
| 上一章没有可对照的指标 | lastVerdict 为空串,degraded 分三种情形:「上一章无六维指标」「除上一章外没有可测章节」「除上一章外不足 2 章可测(估不出 σ,与 novel_fix_plan 同门槛)」 |
| 书名目录不存在 | 抛错(唯一例外:无书可开写,返回空材料没有意义) |
取消信号(exec.signal / AbortError)不属于降级:它会原样向上传播,不被吞掉。
约束与边界
- 只读工具:除复用既有分析缓存(
metricChaptersCached维护的分章测量缓存,属既有行为)外不写任何文件——不建章节、不改伏笔状态、不落盘报告。伏笔表刻意不走会在旧位置迁移写盘的core.readPlots,而是用本模块的只读版本。需要真正创建章节时用 novel_new_chapter。 - 唯一抛错路径是书名目录不存在;材料缺失、缓存不可用、锚包失败、书太短都走降级。调用方不应把空字段读成「这本书没有伏笔」——必须读
degraded区分「确实没有」与「取不到」。 chapter与novel_read共用解析规则:精确文件名优先于章号;只给章号且命中多个同号文件时会报错并列出候选(本工具把它降级为「目标不确定」而不是中断)。纯数字参数不做子串兜底。isNext不等于「参数是 next」:它表示目标章文件尚不存在,因此传入一个未来的章号同样为true。此时返回的file是预期文件名而非磁盘事实(可用 novel_chapters 核对)。avoid的词表不截断(v6.4.0 起):禁用词按 12 条一组紧凑渲染、每组写明「共 N 条」,语用规范(称谓 / 客套 / 仪式禁式 / 语气)也逐条给出,因此不存在「只禁前 10 个词」式的静默丢弃。词源是resolveStyleRule的合并结果(用户登记 ∪ 文化基准默认表),与novel_continuity_check的用语扫描一致。未登记任何 worldview 条目时也会注入默认表(worldview自 v6.9.5 起恒在:按文化基准给默认禁用词,并在basis与degraded里写明「按哪套基准、共多少条」)——想指定基准时用 novel_settings 的detect/add登记一条 worldview。distance依赖章号可解析:chapter/mentionedIn/lastMentioned三处都解析不出章号时,firstChapter省略、firstChapterKnown = false,distance退化为「全书最大章号」(相当于按全书起算),并在degraded里点名前 5 条。排序时这类条目按priority归一为medium、再按已退化的distance参与比较——不会因此被排到最后,这是刻意的:距离未知不等于不重要。- 人物命中判定是纯字符串包含,不做语义推理:别名登记不全时会漏命中,同名不同人(如「小周」既指人物又指地名)时会误命中;名字与别名短于 2 字的一律跳过。这是零 token 本地实现的必然边界,命中结果应视为候选。
- 首次调用可能较慢:长书且缓存未预热时需读全书算分章指标;这一点与
budget无关(见上文)。 lexicon只取「装得进预算」的部分:compact 60 / full 200 条、每条保留 2 / 3 个avoid是返回体预算的取舍,不是「词表只有这么多」;完整词表用 novel_lexicon 的list/export取,截断只影响注入、不动词表数据。lexicon是硬指令,不是新测量:本工具不判断「用没用对」,只把表内词与「勿用」替身词摆到模型面前;判断发生在写完之后,由novel_lexicon action="audit"按行号给出(只读、不改正文)。- 工具开关:执行前经
assertToolEnabled校验状态文件,被关闭时直接报错(可用 novel_sentence_config 重新开启)。本工具不受「写作助手功能」总开关约束——它与plan/verify那类引擎门禁无关。
相关工具
- novel_new_chapter —— 真正创建章节文件的工具;其附带的 baseline / anchors / skeletons 与本工具同源,本工具是那套材料的抽离与扩容(多了钩子、大纲方向行、人物卡、伏笔距离、禁用清单与 plan,v7.0.0 起又多了动作链、子句框架、上一章遗留、症状处方与参照集来源);它的
plan参数正是用来兑现本工具的clauseFrames,postWriteAudit与本工具的prevChapterIssues同源。 - novel_style_check —— 写完之后的对照判定(建议跑一次,偏离明显时才据此修正);其
fixAnchors与lastVerdict分别为本工具锚段的同源材料与上一章结论的来源。 - novel_outline —— 维护本工具读取的创作资料(钩子记录、剧情大纲方向行)。
- novel_plot —— 伏笔登记表;
action: "graph"的结构视图给出跨章的伏笔生命周期,其plotLifecycle.distance与本工具的openPlots.distance是同族口径(都按「全书最大章号 − 最早提及章号」),但本工具的firstChapter只从登记字段(chapter/mentionedIn/lastMentioned)推导,而结构视图还会用伏笔内容的关键词去全书正文里找首次命中章,两者结果可能不同。 - novel_settings —— 人物表与世界观用语规范(禁用词 / 推荐替代 / 语用规范)的维护入口。注意分工:
worldview.bannedWords管「不要写什么」(进本工具的avoid清单),novel_lexicon 管「必须写什么」(进本工具的lexicon分区)。 - novel_lexicon —— 必用词表:本工具
lexicon分区与plan.checklist硬指令的数据源(合并两层、同名以书级为准,受功能开关lexiconFirst控制);写完一章用它的action: "audit"复核覆盖率与「通用词顶替」的行号。 - novel_read —— 需要上一章全文(而非仅结尾 300 字)时按行号补读。
源码位置
- lib/brief.js —— 开写包主体:
buildChapterBrief(唯一的对外导出)与其内部各步——BRIEF_BUDGETS(两档预算常量,含lexicon/avoidPerTerm)、readPlotsReadOnly(只读伏笔读取)、readCreationFile/parseNumberedLines(创作资料读取与编号行解析)、deriveChapterFileName(预期文件名推导)、plotFirstChapter(最早提及章号)、mergeWorldviewBasis(worldview 多条目的 basis 文本拼接,v6.4.0 起只管 basis——词源交给resolveStyleRule)、pushBannedWordAvoids/pushSpeechStyleAvoids(禁用词与语用规范的分组渲染,v6.4.0 新增,后者修复「speechStyle 到不了模型」)、parseCharacterLines/OFFSTAGE_RE/countMentions(人物卡三源合并与命中统计)、plotKeywordsAll(不截断的关键词版本,供「已回收伏笔 ∩ 方向行」使用)、必用词注入(readLexicon→selectLexiconForBrief→groupLexiconByKind,含lexiconFirst门禁与三条degraded文案)、clip/isAbort。 - lib/index.js ——
registerNovelChapterBrief:工具注册、参数校验(budget归一)、输出 schema 与渲染文本(含【必用词·优先使用】分区——v6.3.0 起才真正渲染)。 - 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/obligations.js / lib/chapter-diagnose.js / lib/lexicons/slop.js —— v7.0.0 新增的三个确定性模块:
buildClauseFrames/ENDING_GATE_TEXT(子句框架与章末闸门)、diagnoseChapterSymptoms/checkPlanFulfillment(单章症状与 plan 兑现,与 novel_new_chapter 的写盘体检共用同一实现)、scanSlop(8 类套话库)。参照集与动作链的裁决在 lib/core.js:resolveReferenceSet/selectDiagnosisChapters/percentile/buildReferenceStats/buildActionChains,本模块的fitEnhance负责按enhanceChars整包装填。 - lib/core.js ——
ALL_TOOLS/TOOL_LABELS注册表条目(开关面板与关闭报错文案)。