novel_chapter_brief
开写包。把「写这一章之前需要读的一切」装配成一次只读调用:上一章结尾原文(承接口)、上一章钩子、本章大纲方向行、按命中过滤的人物卡、未回收伏笔及其埋设距离、世界观禁用词与语用规范、全书六维基线、原著锚段与句式骨架、上一章风格自检结论,以及由此生成的三段式 plan 与本章禁用清单。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 处理。
输出结构
返回体共 16 个键(13 个契约必返 + chapter / worldview / baseline 三个「取不到就整键省略」)。材料取不到时字段缺省或为空数组,同时在 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 }。该键由设定表全部 worldview 条目合并而成:basis 拼入各条的 basis / description / ritual / notes(去重后以「;」连接),bannedWords / recommended / speechStyle 分别做并集与浅合并。四项全空时 worldview 整键省略并计入 degraded。 |
baseline | object | 六维基线:每维 { mu, sigma, sigmaMeasured, sigmaUsed, sigmaClamped, low, high, recTol }(recTol = 作者自身波动的 1.5σ 换算成百分比,取整到 5、下限 10、上限 100)。语料为 metricChaptersCached 的逐章指标,目标章已存在时先把它排除出基线语料(与 novel_style_check 同口径)。语料为空时 baseline 整键省略并计入 degraded。 |
anchors | array | 原著锚段,元素 { label, text }:compact 取前 2 条、full 取前 5 条。抽样与分类规则与 novel_style_check 的 fixAnchors 同源。语料排除目标章自身(改稿场景下不能把「正在改的那一段」当原著范本),与 novel_fix_plan 的锚段口径一致;chapter="next" 时目标章不存在,等价于全用。 |
skeletons | array | 句式骨架,元素 { type, text }:compact 取前 3 条、full 取 slice(0, 8)。注意 buildStyleAnchorPackage 自身最多只产出 4 条,因此 full 的 8 只是「不额外截断」,实际条数上限仍是 4。语料同 anchors(排除目标章)。 |
lastVerdict | string | 上一章的六维对照结论,形如「第 N 章六维对照:<judge.summary>(由逐章指标缓存现算)」。它不是 novel_style_check 的落盘结论(插件从不落盘该结论),而是由缓存的逐章指标现算:以「除上一章外的其余章」为基线,容差由 core.buildTolerance 构造——与 novel_fix_plan 同一实现(recTol 为主,基线不足 3 章时下限抬到 25%,可被用户的 styleTolerance 覆盖同名维度)。除上一章外不足 2 章可测时不给结论(单章基线估不出 σ);取不到时为空串并计入 degraded。 |
avoid | array | 本章禁用清单,三类来源合并,元素 { kind, detail },kind 取值为 已回收伏笔 / 禁词 / 已退场角色。三类都取不到时为空数组。恒为数组。 |
plan | object | 三段式行动框架:{ previousState, goal, checklist }(checklist 为字符串数组),由上述材料生成,见下文「plan 的生成口径」。恒为对象。 |
degraded | array | 降级说明,元素是字符串(不是对象),已按 Set 去重。空数组表示材料完整。 |
chapter / worldview / baseline 是「有就返回、没有就整键省略」,其余 13 个键恒在。调用方读 worldview 前必须先判存在性;读 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)。全书最大章号取「现有章节里能解析出章号的最大值」,不是章文件个数。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。
「已退场」判定是限定词表(退场 / 已死 / 死亡 / 身亡 / 殉 / 不再出场 / 下线 / 殁)正则命中设定表 status / notes / description / traits 或创作资料人物简介;命中即打上 offstage: true。宁可漏报不可误报——误报会让模型把活人写死。
4 · 未回收伏笔与 distance
distance 是「埋了多久没回收」的直接量化:它把静态的伏笔表变成带时间压力的清单,让调用方先处理埋得最久、优先级最高的线。已回收(done)的伏笔不进入 openPlots,而是从另一个方向进入 avoid(见下节)。
5 · 世界观 worldview 与禁用清单 avoid
第 ① 类的用意是防止「把已经用掉的线当作新钩子再埋一次」——这是长篇里最常见的自我重复。第 ② 类沿用设定表里已经登记好的禁用词,只取前 10 条是为了控制返回体体积:完整的禁用词表仍可用 novel_settings 查。第 ③ 类把「已退场角色」前置到清单里,避免与人物卡里的出场提示互相打架。
6 · 六维基线 baseline
基线与 novel_style_report、novel_style_check、novel_new_chapter 走同一份分章测量缓存(<root>/.novel-writer/analysis/<book>-chapters-metrics.json),缓存键按内容哈希且与章序无关,因此四个工具交替调用不会互相击穿缓存。只有正文 ≥ 40 字的章节才进入测量;性能:命中缓存时基线几乎零成本;长书首次调用仍需读全书算一遍分章指标(可能数秒),此后增量命中。
budget 不省计算。两档只决定返回体里各字段截到什么程度,测量与抽样步骤完全相同。书很长且已经预热过缓存时,用 compact 只是让上下文更省,不会让工具跑得更快;反过来,首次调用无论哪一档都要付全书测量的代价。
7 · 锚段与骨架 anchors / skeletons
与 novel_new_chapter、novel_style_check 共用同一个锚包构建函数,因此三处口径不会分叉。anchors 是「校准语感」的样例,不是造句模板——锚段读一遍对上味道即可,skeletons 只在写不出该类型句子时参考,不要照它造句、不要套句式模板;基线数字只是事后校验。两者都在返回体里,但用途不同。本书正文不满足抽样条件(所有段落都短于 40 字)时两个数组都为空,并计入 degraded。
8 · 上一章结论 lastVerdict
这里刻意不做落盘:插件从不把 novel_style_check 的结论写进任何文件,因此「上一章自检结论」是每次由缓存的逐章指标现算的(口径与 novel_style_check 一致,并沿用侧边栏保存的自定义容差)。上一章正文不足 40 字、或除上一章外没有其它可测章节时为空串并计入 degraded。
9 · plan 的生成口径
| 字段 | 来源 | 内容 |
|---|---|---|
previousState | prevChapter + previousHook / anchor | 「上一章停在哪里」的浓缩复述,三选一:有钩子 → 带上「结尾钩子:…」;无钩子但有正文 → 「正文已在 anchor(末 N 字),钩子未回填」;无正文 → 「正文为空」。全书开篇时为「无上一章(本章为全书开篇)」。 |
goal | outlineDirection → previousHook → 开篇 | 本章目标,四级退化:有方向行 → 「按大纲方向行写:…」;无方向行但有钩子 → 「大纲未给方向,承接上一章钩子推进:…」;都没有但有上一章 → 「从上一章结尾(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_check 的 fixAnchors(写完之后按偏差取锚段,比写之前更准)。budget 只接受 "full" 一个特殊值,传入任何其它值(含 "compact" 与未声明值)都按 compact 执行。
优雅降级
以下情形一律不报错:相应字段缺省或为空数组,同时在 degraded 里说明材料名与原因。这是刻意的设计——「书还没建大纲」不是调用错误,不应该让整个流程中断。
| 情形 | 返回 |
|---|---|
没有创作资料(未 init) | previousHook / outlineDirection / chapter.title 为空,plan.goal 退化;degraded 分别给出「创作资料未初始化」与「无钩子记录」两条说明 |
| 没有伏笔表 | openPlots = [],avoid 只保留禁词与已退场角色,degraded 说明 |
| 没有设定表 | worldview 整键省略、characters 为空数组(除非创作资料里有人物设定),avoid 只保留已回收伏笔一类 |
| 全书只有 1 章 | 无「上一章」→ anchor / previousHook 为空、lastVerdict 为空;除目标章外无其他可测章节 → baseline 整键省略 |
| 书太短算不出基线 | baseline 整键省略(不输出 μ=0 的假基线),degraded 区分「全书没有 ≥ 40 字的章节」与「除目标章外没有可测章节(书只有 1 章)」两种原因 |
| 目标章无法唯一确定 | chapter 整键省略、isNext 视 number 是否可解析而定,degraded 说明「同章号多文件」或「找不到章节」 |
| 锚包构建失败 / 无合格段落 | anchors / skeletons 为空数组,degraded 说明「抽样要求段落 ≥ 40 字」 |
| 单章读取失败(编码 / 权限 / 被占用) | 该章按空正文处理,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只取禁用词前 10 条:这是返回体预算的取舍,不是「只禁这 10 个词」。完整词表在设定表里,语用规范(称谓 / 客套 / 仪式禁式)通过worldview.speechStyle整体给出。distance依赖章号可解析:chapter/mentionedIn/lastMentioned三处都解析不出章号时,firstChapter省略、firstChapterKnown = false,distance退化为「全书最大章号」(相当于按全书起算),并在degraded里点名前 5 条。排序时这类条目按priority归一为medium、再按已退化的distance参与比较——不会因此被排到最后,这是刻意的:距离未知不等于不重要。- 人物命中判定是纯字符串包含,不做语义推理:别名登记不全时会漏命中,同名不同人(如「小周」既指人物又指地名)时会误命中;名字与别名短于 2 字的一律跳过。这是零 token 本地实现的必然边界,命中结果应视为候选。
- 首次调用可能较慢:长书且缓存未预热时需读全书算分章指标;这一点与
budget无关(见上文)。 - 工具开关:执行前经
assertToolEnabled校验状态文件,被关闭时直接报错(可用 novel_sentence_config 重新开启)。本工具不受「写作助手功能」总开关约束——它与plan/verify那类引擎门禁无关。
相关工具
- novel_new_chapter —— 真正创建章节文件的工具;其附带的 baseline / anchors / skeletons 与本工具同源,本工具是那套材料的抽离与扩容(多了钩子、大纲方向行、人物卡、伏笔距离、禁用清单与 plan)。
- novel_style_check —— 写完之后的对照判定(建议跑一次,偏离明显时才据此修正);其
fixAnchors与lastVerdict分别为本工具锚段的同源材料与上一章结论的来源。 - 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注册表条目(开关面板与关闭报错文案)。