novel_style_report
风格画像报告。对全书逐章测量六个文笔维度,汇总为基线带 μ±σ,并聚合文风指纹、高频词、题材流派、情感量化、氛围 12 维与语义风格距离,作为「测量数据」交给调用方模型做风格判断。插件只报数、不下结论:模型的判断可通过 aiJudgment 回传存盘,供后续续写时复用。
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
book | string | 是 | 书名,即 novels/ 下的子目录名。经 sanitizeSegment 过滤,拒绝路径分隔符与上跳。 |
root | string | 否 | 章节库根目录。MCP 形态下必须落在启动参数 --root 指定范围内,越界会被静默回退。 |
action | string | 否 | report(默认)生成测量报告;get 只读取已保存的 AI 判断,不重算。 |
brief | boolean | 否 | true 返回一句话精简结论,用于省 token 的场景。 |
aiJudgment | string | 否 | 模型给出的风格气质判断。传入后与测量数据一同写入 style-reports/<book>.json。 |
输出结构
契约字段(output.schema):book 与 report 为必返;其余按需出现。渲染层会在正文之后追加「风格锚·原著段落」与「句式骨架」两段,供生成端照样例写,而不是只盯数字。
| 字段 | 类型 | 内容 |
|---|---|---|
report | string | 人类可读的完整报告文本 |
baseline | object | 六维基线与每章测量值,见下文「基线带」 |
baselineChapterCount | integer | 实际参与基线统计的章数(已剔除过短章节) |
dimensions | object | 指纹 / 词汇 / 题材 / 情感 / 氛围 12 维 / 语义距离 |
anchors / skeletons | array | 原著代表性段落与句式骨架样例 |
brief | string | 精简模式下的单句结论 |
savedJudgment | string | action:"get" 时返回的历史判断 |
六个维度的定义与公式
全部维度零依赖、纯规则统计,不做模型推理。所有计数先按千字归一,量纲统一:
测量前先剥离 Markdown 标题行(^#{1,6}[ \t]+[^\n]*),章节标题不参与任何统计。
1 · 句法复杂度 complexity
切句用与 analysis.js 共用的句末符集合(。!?…!?),正则为「句末符后紧跟非句末符处切分,或遇换行切分」;句末符后的闭合引号会被吞掉(她说:「走吧。」 不产生孤儿引号句),纯标点残片(!!!)丢弃。小句按 [,,;;::] 切分后计非空段。该维度是「平均每句由几个小句构成」,近似嵌套深度。
2 · 修饰密度 modifierDensity
「的」作为词尾(真的 / 似的 / 目的 / 有的 / 是的)或词首(的确 / 的士)时都不是修饰助词,用两张停用词表按命中位置的 2 字窗口排除;刻意不收录「中的 / 的话」——它们在「梦中的场景」「他的话很多」里是真修饰语。X地 侧按「地」的位置分两张名词表:以「地」结尾的(土地 / 原地 / 目的地…)用后缀判定,以「地」开头的(地上 / 地下 / 地方…)用命中之后的 2 字窗口判定。
3 · 抽象度 abstractDensity
采用「最长匹配 + 区间消费」而非正则前瞻:旧写法要求抽象词后必须是非汉字,导致「她的感情很深。」计 0 而「她的感情,很深。」计 1——同词只因后随标点不同而结果相反。已知取舍:相邻两个抽象词若被一个 4 字窗口跨越(如「感情和理性」),只计 1 个。
4 · 动作密度 actionDensity
词表本身是文档:单字表无法区分「分(分开)」与「十分」、「行(行走)」与「行李」,因此用 2 字黑名单在命中处兜底,窗口取命中字的前一字与后一字两种组合。第 ③ 条修的是越过文本末位与句末动词的漏检。
5 · 不确定性 hedgeDensity
最长优先 + 占位符是为了避免重叠计数:像是要 不再被算成 像是 两次加 像是要 两次。
6 · 留白指数 gapIndex
两项都按千字归一是硬性要求:早期版本把千字密度与「未完句百分比」(0–100 量级)直接相加,15 字文本能算出 222.22,量纲不可比。未完句定位用顺序游标推进,重复文本不会误判。
基线带 μ±σ 的计算
逐章测量后汇总,跳过正文长度不足 40 字的章节(不足则不参与统计,也不计入 baselineChapterCount):
minSigmaRatio 默认 0.15,即 σ 下限为均值的 15%。推荐容差取「作者自身章节波动的 1.5 倍 σ」,四舍五入到 5 的倍数,下限 10%、上限 100%;μ=0 时回退 15 以避免除零。
sigmaMeasured(原始样本标准差)、sigmaUsed(参与 low/high/recTol 的钳制值)与 sigmaClamped(是否发生钳制)。早期版本只返回钳制后的值,展示层把 0.15μ 的下限当成「作者自身波动」念给用户——实测真实 σ=0.0084 被报成 0.15,差 18 倍。
偏离判定
给新章指标 v 与基线 μ:
tol 默认取 { low: −recTol, high: +recTol },也接受调用方逐维自定义。容差语义有明确区分:显式 0 表示零容差(任何偏差都判 out),只有缺失 / null / 空串 / 非有限值才回退推荐值——早期用 Number(x) || -recT 把 null 也当 0,单侧缺失时那一侧会退化成零容差。
约束与边界(对调用方模型)
- book 必填,且经
sanitizeSegment过滤;root在 MCP 形态下必须落在启动时的--root之内——越界时由 MCP 层静默回退到--root并只写 stderr 日志,调用方不会收到错误,因此不要把root当作可靠的越权探测手段。 - 测量与判断分离:本工具不返回风格结论。需要「这本书的文风是什么气质」这类判断时,由模型阅读报告后给出,并通过
aiJudgment回传,插件负责存盘与复用。 - 过短章节被排除:正文 < 40 字的章节不参与基线;
baselineChapterCount会小于实际章数,这是预期行为。 - 维度可能被跳过:当某维基线 μ=0 且 σ=0 时无法做相对判定,该维进入
skippedDims,summary会写明「另有 N 个维度基线均值为 0、未做相对判定」。调用方不应把「无维度超出容差带」读成「全部维度合格」。 - 数值不可跨版本横比:v4.3.0 统一了句末符口径(省略号单独成句)、清洗了动作词表(去重 29 + 剔除 11 个非动词)、抽象度改为最长匹配、修饰密度增加停用词表——同一本书在新旧版本下的报告数值不可直接比较。
- 落盘位置:报告写入
<root>/.novel-writer/style-reports/<book>.json;action:"get"只读取该文件,不重算。
相关工具
- novel_style_check —— 用同一套维度做「新章 vs 全书基线」的对照判定;基线会先剔除目标章再计算。
- novel_sentence_analysis —— 九类句式分布、情感曲线与风格指纹,是本报告「维度」部分的原始来源。
- novel_semantic_search —— 语义风格距离所依赖的本地向量引擎。
源码位置
- lib/style-metrics.js —— 六维测量、基线计算、偏离判定(
measureStyleMetrics/computeBaselineFromPerChapter/judgeAgainstBaseline) - lib/analysis.js ——
TERMINATORS句末符常量、句式与情感分析 - lib/index.js —— 工具注册:参数、输出契约与
execute实现 - lib/vibe.js —— 氛围光谱 12 维