novel_sentence_analysis
句式模式分析。逐句把正文分成九类句式并统计占比、转移、模板与段落结构,同时给出句长分布、情感曲线、细节密度、风格指纹与节奏建议。分析范围可以是全书,也可以是单章。全部为本地规则统计:情感词命中走词表加否定过滤与副词加权,语义隐性情感是在此之上的可选增强。结果写入书库缓存并同时落盘一份报告 JSON,可用 fresh 强制重算。
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
book | string | 是 | 书名。经 sanitizeSegment 过滤;对应目录内无章节文件时抛错。 |
chapter | string | 否 | 只分析该章(章号 / 文件名 / 标题子串)。省略则分析全书,scope 形如「全书 N 章」。 |
top | integer | 否 | 返回的高频句式模板条数。允许区间 1–50,默认 8;越界或非整数抛错(不静默钳制)。 |
maxSentences | integer | 否 | 采样句数上限。允许区间 100–100000,默认 20000;触顶后按前缀截断并在建议文本尾部标注。 |
curveSegments | integer | 否 | 情感曲线分段数。允许区间 1–50,默认 20;实际段数为 min(有效段落数, curveSegments)。 |
fresh | boolean | 否 | true 跳过缓存读取并强制重算;结果仍写回同一缓存文件。 |
brief | boolean | 否 | true 时返回 brief 单句摘要,渲染层只输出该句并提示「省略 brief 重新调用」。 |
root | string | 否 | 章节库根目录。省略时取插件 config.root,再省略取会话工作目录;MCP 形态下受启动参数 --root 约束。 |
输出结构
契约字段(output.schema.required):book、scope、enabled、totalChars、totalSentences。功能关闭时返回体只含这五项加 message,且 totalChars 与 totalSentences 均为 0。
| 字段 | 类型 | 内容 |
|---|---|---|
scope | string | 单章时为文件名;全书时为「全书 N 章」 |
enabled / message | boolean / string | 句式分析总开关状态;关闭时 message 给出开启方式 |
autoAnalyze | boolean | 生效的「分析作品时主动使用」开关状态 |
totalChars / totalSentences | integer | 全文字符数与实际参与统计的句数(受 maxSentences 影响) |
categories | array | 固定九项,顺序为 CATEGORY_ORDER:{type, label, count, ratio, avgLength, examples};examples 每类最多 3 条、每条超 42 字截断加「…」 |
transitions | array | 相邻句类型转移,{from, to, count},按次数降序,最多 12 条 |
motifs | array | 2 连句与 3 连句模板合并排序后取前 top 条,{pattern, count},模式用 → 连接 |
paragraphs | object | total / avgSentences / opening / closing / dialogueOnly / psychologyOnly / mixed / narrationOnly / exchanges;四类段落计数之和恒等于 total |
lengths | object | avg / median / shortRatio / mediumRatio / longRatio / distribution(四桶 1-10 / 11-24 / 25-40 / 41+) |
style | object | 对话、心理、环境、疑问(含反问)、感叹占比,短句与长句占比,subjectivityIndex(0–100 整数)、emotionDensity、avgSentenceLength、firstPersonDensity |
emotion | object | dominant、cleanDominant、confidence、caveat、aiAction、pollution、scores、cleanScores、intensity、topWords(≤12)、curve、quantification |
chapterPatterns | array | 全书分析时的分章压缩节奏序列,{chapter, sequence};单章分析为空数组 |
guidance | string | 节奏建议多行文本,末尾可能附前缀截断提示 |
density | object | actionVerbsPer1000 / actionChainRatio / actionChainExamples / objectNounsPer1000 / sensePer1000 / sense(五感官通道) |
fingerprint | string | 一维风格指纹串,格式与 novel_style_check 一致 |
reportFile / cache / cachedAt | string | 报告落盘路径;cache 枚举 hit / miss;cachedAt 为 ISO 时间 |
brief | string | brief:true 时的单句摘要 |
渲染层按 【句式分布】→【句式排列】→【句长与风格】→【主观情感】→【情感净化预警】→【情感量化】→【语义隐性情感】→【细节密度】→【节奏建议】→【风格指纹】→【AI 解读提示】 分段输出纯文本;情感净化预警段仅在 cleanDominant !== dominant 时出现,语义隐性情感段仅在 quantification.semanticImplicit.hits 非空时出现。
计算原理
1 · 分块、切句与九类判定
先分段落再切句,两者都做了降噪:
九类句式的判定是固定优先级瀑布,先命中先归类,不做打分:
祈使排在感叹之前,是为了让「闭嘴!」这类句归祈使,感叹只留给不含祈使词的叹句。反问分支已剔除「哪里」这一最常见的真疑问词,并把「谁 + 情态」收紧为固定格式(谁知道 / 谁说的 / 谁能想到…),避免把「谁会来参加明天的会议?」判成反问。
2 · 采样上限与密度分母
情感密度、主观性指数、细节密度、Valence 量化全部以 sampleText 为样本,与句式计数同源。此前存在的两套口径(截断时用句子拼接、未截断时用含空行与标题行的原文)会让密度类指标仅因是否触顶而不可比,现已统一:代价是未截断路径下空行与段落分隔符不再进分母,密度读数略高于旧版本。
3 · 排列规律:转移、模板、段落结构
四类段落计数互斥且穷尽,dialogueOnly + psychologyOnly + mixed + narrationOnly === paragraphs.total 恒成立——早先缺少 narrationOnly 桶时,全环境句段与全疑问句段会静默漏计。
4 · 句长分布
短 / 中 / 长三档与 distribution 四桶在 10 与 24 处对齐,按 distribution 复算 ratio 必然一致。
5 · 情感词扫描
三张表叠加使用:强情绪词 50 条(深层情感,进 clean)、弱情绪词 52 条(生理 / 爽感反应,只进 raw)、大连理工七类词表 27413 条(懒加载 gzip,兜底;只进 raw)。
「最长匹配 + 区间消费」消除了嵌套双计:「悲痛欲绝」不再被拆成「悲痛」+「悲痛欲绝」两分。DUTIR 兜底同样按最长优先扫描,但将上限由 4 字提到 8 字(词表最长条目 7 字),使 853 条 ≥5 字的成语级情感词恢复可命中;兜底词不做强弱分级,只进 scores 不进 cleanScores。
6 · 情绪污染源与置信度
命中污染阈值时 caveat 会写明「检测到高密度 X 描写…请勿直接采信,须 novel_read 抽查 2-3 段原文复核真实情感基调后再下结论」,同时给出 aiAction;仅强弱主导不一致时 caveat 以「剔除易污染的情绪词后,主导情感为…」表述;两者都不成立时 caveat 为空串。
7 · 情感曲线
曲线不做零强度段裁剪:段数由 min(段落数, curveSegments) 保证每段至少 1 个段落,不会产生空切片,因此强度为 0 的段是「真实的平缓段」而非伪段,跨章节曲线可按固定段数对齐。
8 · Valence 量化与隐性情感
explicitImplicitCompare 以显性效价均值 ±0.15 与隐性方向占比 0.6 判定「表里不一」,双向冲突(显正隐负、显负隐正)都会置位 explicitImplicitConflict。复合情感共现按段落内共现的情感类对统计,输出前 5 组并补中文标签(悲喜交加 / 又爱又恨 等)。
9 · 主观性指数、细节密度与指纹
10 · 缓存与报告落盘
缓存指纹用「每章前 8KB 的内容哈希 + 文件大小」而非 stat 的 mtime:内容同大小且 mtime 未变时旧指纹不会失效。命中路径只读每章前缀 8KB,不整章读盘。
约束与边界
- 两层开关,行为不同:工具级开关关闭时
assertToolEnabled抛错(「工具 novel_sentence_analysis…当前已在「写作助手功能」UI 中关闭」);句式分析总开关enabled关闭时不抛错,返回enabled:false、totalChars:0、totalSentences:0与message,且不读章节正文、不写缓存。调用方应据enabled而非totalSentences === 0判断可用性。 - 参数越界是错误而非钳制:
top/maxSentences/curveSegments由optionalInt校验,非整数或越界直接抛「参数 X 必须是 A 到 B 之间的整数」。内部analyzeText另有≥1与1–50的兜底钳制,供直接调用该函数的一方使用。 - 触顶即前缀采样:超过
maxSentences后只统计靠前部分,totalSentences等于上限而不是全书句数。这不是全量结果,提示只写在guidance末尾(不加顶层字段),调用方需要全量时应分章调用。 - 缓存键含内容指纹与全部参数:改一章内容、换一个
top或翻转enabled/autoAnalyze都会生成新缓存文件,旧文件不再命中;fresh:true跳过读取但仍写回同一文件。落盘目录按书最多保留 20 份报告,超出部分按修改时间淘汰,<book>-full.json与<book>-chapters-metrics.json是分析缓存、不匹配报告命名,不会被清理。 - 算法版本是硬失效条件:缓存内
ver与CACHE_VERSION(当前等于插件版本 5.0.0,见源码位置lib/core.js)不一致时视为过期并重新分析。这保证升级后不会复用旧口径的情感与句式结果。 - 语义隐性情感受独立门控:仅当
semanticImplicit(含semanticEmbedding总开关)开启且本地引擎可用时才会补充emotion.quantification.semanticImplicit。「用户明确关闭」与「引擎瞬时不可用」被区分对待——后者不会把缓存里已算好的semanticImplicit抹成空。单章分析时命中范围被约束在本章内,越界的旧缓存结果会被重算并写回。 - 情感字段可能被裁剪:
emotionCaveat关闭时emotion只保留dominant/scores/intensity/topWords/curve;emotionComplexity关闭时删除quantification。两个开关各自独立生效,关闭其一不会连带剥掉另一个。 - 静默降级点:DUTIR 词表加载失败时跳过兜底(不影响主词表结果);语义裁决与语义隐性情感失败不阻塞规则结果;缓存写盘失败不影响本次返回值;
cleanOutput统一消除-0、把NaN归一为null、剔除undefined键。 - 跨版本不可横比:v4.3.0 起,句末符口径统一(省略号单独成句)、密度分母统一为句子拼接样本、情感命中改为区间消费、Valence 的
adjVariance与deltaRobust改口径、隐性情感三项比率共用三分分母。同一本书在旧版本下产生的报告数值不可与新版本直接比较。
相关工具
- novel_style_report —— 本工具的
fingerprint、lengths、style、emotion是其「维度」聚合的原始来源。 - novel_style_check —— 复用本工具的九类占比、句长与主观性构造指纹向量,做单章对照判定。
- novel_semantic_search —— 与本工具的语义隐性情感共用同一个本地向量引擎、同一份索引缓存与同一套切块规则。
源码位置
- lib/index.js —— 工具注册与执行(
registerNovelSentenceAnalysis,第 403 行起;execute自第 590 行)、缓存键与pruneAnalysisReports(第 155 行)、cropEmotionByFeatures(第 83 行)、semanticGate(第 109 行) - lib/analysis.js —— 九类标签与顺序(
CATEGORY_LABELS第 23 行、CATEGORY_ORDER第 37 行、TYPE_CODE第 43 行)、词表(第 58–142 行)、splitBlocks(第 145 行)、splitSentences(第 183 行)、classifySentence(第 348 行)、scanEmotionHits(第 451 行)、emotionOf(第 500 行)、valenceSeries(第 896 行)、valenceStats(第 953 行)、implicitEmotionScan(第 763 行)、emotionComplexity(第 1095 行)、analyzeText(第 1174 行)、emotionCurve(第 1558 行)、buildFingerprint(第 1600 行)、densityOf(第 1678 行) - lib/core.js ——
buildBriefLine(第 1324 行)、formatSentenceAnalysis(第 1330 行)、enrichSemanticImplicit(第 793 行)、semanticFeatureEnabled(第 852 行)、cropEmotion(第 856 行)、cleanOutput(第 2308 行)、CACHE_VERSION(第 24 行) - lib/embedding.js ——
chunkText(第 520 行)、detectImplicitEmotions(第 292 行)、IMPLICIT_EMOTION_PROTOTYPES(第 232 行,共 29 条原型句)、fingerprint(第 338 行)