dsh-novel-writer 6.6.0 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -85,7 +85,7 @@ Then ask, in order (this is one complete chapter):
85
85
  3. **"check chapter 7"** → `novel_style_check` returns similarity, a deviation list and the six-metric comparison
86
86
  4. **"turn chapter 7 into a fix list"** → `novel_fix_plan` returns the prioritised to-dos; run `verify` afterwards to re-check the three states
87
87
 
88
- > Want the baseline to follow **the chapters you wrote yourself** instead of the whole book? In the panel's **Style baseline** view, set **Baseline scope** to **selected chapters** and tick them: the check, the chapter brief, the fix plan and the style report all switch to those chapters as the baseline (**per book**; the default **whole book** is the old behaviour — leave it alone and nothing changes). Creating a chapter (`novel_new_chapter`) uses the same scope for the μ summary and the anchor pack it attaches.
88
+ > Want the baseline to follow **the chapters you wrote yourself** instead of the whole book? In the panel's **Style baseline** view set **Range** to **Selected**, tick your yardstick chapters under **Pick chapters**, then hit **Save range**: the check, the chapter brief, the fix plan and the style report all switch to those chapters as the baseline (**per book**; the default **Whole book** is the old behaviour — leave it alone and nothing changes). Creating a chapter (`novel_new_chapter`) uses the same scope for the μ summary and the anchor pack it attaches.
89
89
 
90
90
  To just look at the baseline first, ask **"run novel_style_report on my novel"** and you get:
91
91
 
@@ -156,7 +156,7 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
156
156
  | Cost | **0 (local inference)** | Billed per token | Self-hosted |
157
157
  | Purpose-built for Chinese fiction | **Yes** | Generic | No |
158
158
  | Style baseline (μ±σ) | **Yes** | Rare | No |
159
- | DSH integration | **20 tools + official seats & website-style UI** | None | None |
159
+ | DSH integration | **20 tools + official seats & a unified panel** | None | None |
160
160
  | Usable without DSH | **Yes (MCP)** | Yes | You wrap it yourself |
161
161
 
162
162
  > **A note for non-Chinese users**: this plugin is designed for analysing and writing Chinese fiction — the sentence-pattern,
@@ -173,14 +173,14 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
173
173
  3. **12-axis vibe spectrum**: nightmare / angst / heartwarming / fluff / tearjerker / dark / mystery / blaze / absurd / lonesome / aesthetic / sensual — with traceable evidence, 0 token.
174
174
  4. **Local semantic engine**: bge-small-zh Chinese model (24MB, shipped with the plugin) local CPU inference — `novel_semantic_search` finds semantically related passages with natural language (with chapter location), semantic style comparison, semantic implicit emotion; lazy loading + graceful fallback.
175
175
  5. **Sentence-pattern analysis**: 9 categories, arrangement patterns, rhythm, emotion curve, style fingerprint + guidance, with cache & report export.
176
- 6. **Emotion purification & quantification**: strong/weak emotion-word grading, pollution detection, caveat warning + AI re-verification; Valence sliding window → variance V / delta Δ / conflict index C + implicit imagery carriers.
176
+ 6. **Emotion purification & quantification**: strong/weak emotion-word grading, pollution detection, caveat warning + AI re-verification; Valence sliding window → variance V / delta Δ / conflict index C + implicit imagery carriers.<br>**Emotion complexity now follows confidence (v6.6.5)**: when the conclusion is not trustworthy (a pollution threshold is hit, the text contains no emotion words at all, or no usable clean counts remain) the tool no longer hands out a top-tier verdict — `complexity.level` is reported as `unknown`, together with `reliable: false` and a `reliabilityNote` giving the reason, and the rendered text prints that warning on its own line; `score` keeps its numeric value for cross-chapter comparison. The emotion dimension is simply not applicable to R18 / high-physiological prose — use rhythm, payoff and dialogue density instead.
177
177
  7. **Worldview & pragmatics detection**: auto cultural-baseline detection with confidence; speechStyle title/honorifics/rituals/tone norms; genre & theme + webnovel signals.
178
178
  8. **Writing toolkit**: plot tracking / five settings tables (characters·locations·items·timeline·worldview) / chapter summaries / continuity audit / batch import / style check / continuation writing.
179
- 9. **Per-tool UI toggles**: a 「写作助手功能」 ("Writing Assistant") entry in the left sidebar plus a main-column panel (**official DSH 0.2.0 seats**; hosts older than 0.2.0 fall back automatically) — master switch + grouped tool toggles + feature toggles + prompt mode/scene/**lean workflow**, plain-language labels, data-dir usage & semantic-engine status. All colors go through `--dsw-alias-*` semantic tokens (three themes in sync with the host), controls use the official UI primitives, DeepSeek-blue accents, a glow hero header with section kickers, and equal-width pages across every level.<br>**The UI goes further (Plan A)**: source split + build chain (assert-failed builds never write, `--check` verifies source==artifact), 6 unified components with a single row pattern (`PreferenceRow`), group cards across every switch page, 19+5 line icons, a slider as auxiliary input on the tolerance rows, and page-level push-pop transition animations (zero state / zero hook).
180
- 10. **Style Baseline**: Six writing metrics (syntactic complexity / modifier density / abstraction / action density / hedging / gap index) + per-chapter μ±σ baseline band; `novel_style_report` outputs the band, `novel_style_check` compares new chapters (in-band ✓ / out-of-band ⚠); per-metric ±% tolerance configurable in the panel's **Six-metric baseline** view (**recommended = 1.5× σ of the book's chapter variance**, rounded, clamped to ±10%~100%; leave blank to use recommended) — free theme, writing style kept inside the band.<br>**Baseline scope (v6.5.5, configured per book)**: the baseline does not have to be the whole book — in the panel's **Style baseline** view choose **whole book** (default, = the old behaviour) or **selected chapters**, then tick the chapters you treat as the yardstick; the baseline then uses **only** those (it means "only these", not "prefer these and top up when short"). This exists for "the AI keeps writing and ends up sounding like the AI": from chapter 2 onward the model is really reading its own previous chapter for reference, and **a baseline sampled from the whole book drifts along with it** (the longer the book, the higher the AI share). Nail the yardstick to the chapters you wrote yourself and the plugin can answer "**relative to those chapters**, how far has this chapter moved" — it never judges whether you have drifted, which is not decidable in the first place. The chapter being checked is **always excluded** (otherwise it would be comparing itself with itself); when fewer than 3 chapters are ticked the recommended tolerance widens to **at least ±25%**, so even a single chapter can serve as the baseline; chapters that cannot be used (renamed / deleted, or **too short to measure**) are skipped and reported honestly, and if not one of them can be used it falls back to **whole book** and states the reason in the returned baseline description. The scope is stored per book (`<library root>/.novel-writer/baseline/<book>.json`), so books do not affect each other.
179
+ 9. **Per-tool UI toggles**: a 「写作助手功能」 ("Writing Assistant") entry in the left sidebar plus a main-column panel (**official DSH 0.2.0 seats**; hosts older than 0.2.0 fall back automatically) — the home page is grouped into Writing resources / AI behavior / Features & tools / History & maintenance / Advanced, one thing per page; tool rows all go through the same `PreferenceRow`, the switch itself is the state (no more blue full-row highlight, no 「已开启」 badge), and copy says what it does for you rather than how it works (tool ids and parameters stay out of the main text). Colors use `--dsw-alias-*` semantic tokens (three themes in sync with the host), controls use the official UI primitives, DeepSeek blue is reserved for the single primary action and for things that need attention, and every page shares one width.<br>**v6.7.0 panel visual redesign**: the render layer in `lib/client.js` was rewritten, the visual-system CSS grew from 4.6 KB to 19.4 KB, there are 52 line icons (every tool row has its own), file paths / data-dir usage / `state.json` all moved into a single "Storage locations" disclosure at the bottom of each page, and the "Semantic model" row under Features & tools now shows engine status; the functional surface is unchanged (8 views / 20 tools / backend routes untouched). The full spec is `tools/UI-REDESIGN.md` in the repo.<br>**UI foundation (Plan A)**: source split + build chain (assert-failed builds never write, `--check` verifies source==artifact), unified components (`PreferenceRow` single row pattern, `GroupCard`, `StorageDisclosure` for storage paths, etc.), and page-level push-pop transition animations (zero state / zero hook).
180
+ 10. **Style Baseline**: Six writing metrics (syntactic complexity / modifier density / abstraction / action density / hedging / gap index) + per-chapter μ±σ baseline band; `novel_style_report` outputs the band, `novel_style_check` compares new chapters (in-band ✓ / out-of-band ⚠); per-metric ±% tolerance configurable in the panel's **Style baseline** → **Six style measures** section (one row per metric with "below" and "above" percentage inputs; **recommended = 1.5× σ of the book's chapter variance**, rounded, clamped to ±10%~100%; leave blank to use recommended) — free theme, writing style kept inside the band.<br>**Baseline scope (v6.5.5, configured per book)**: the baseline does not have to be the whole book — in the panel's **Style baseline** view choose **Whole book** (default, = the old behaviour) or **Selected**, then tick the chapters you treat as the yardstick; the baseline then uses **only** those (it means "only these", not "prefer these and top up when short"). This exists for "the AI keeps writing and ends up sounding like the AI": from chapter 2 onward the model is really reading its own previous chapter for reference, and **a baseline sampled from the whole book drifts along with it** (the longer the book, the higher the AI share). Nail the yardstick to the chapters you wrote yourself and the plugin can answer "**relative to those chapters**, how far has this chapter moved" — it never judges whether you have drifted, which is not decidable in the first place. The chapter being checked is **always excluded** (otherwise it would be comparing itself with itself); when fewer than 3 chapters are ticked the recommended tolerance widens to **at least ±25%**, so even a single chapter can serve as the baseline; chapters that cannot be used (renamed / deleted, or **too short to measure**) are skipped and reported honestly, and if not one of them can be used it falls back to **whole book** and states the reason in the returned baseline description. The scope is stored per book (`<library root>/.novel-writer/baseline/<book>.json`), so books do not affect each other.
181
181
  11. **Writing sentinels**: `novel_continuity_check` extended — ①**bridge check** (`chapter`: time jumps / semantic distance / character continuity / hook handoff, with quoted evidence) ②**OOC check** (`ooc`: per-character emotion baseline deviation) ③**outline drift** (`outline`: direction vs body keyword overlap); **brief mode** for report tools.
182
- 12. **Original mode & creation files**: fill in creation settings in the panel's **Original mode** view (worldview/characters/forbidden/main conflict/genre/extras, blank = model decides, per-book profile library); novel_outline maintains creation files (bible/characters/outline/hooks/status), enforcing the bible → outline → hook chain with dynamic batches (10→20→30 chapters) to prevent plot jumps and OOC.
183
- 13. **Experience & stats**: main panel **library stats** (per-book chapters/total chars/7-day active chars, 🔥 green), **🎬 demo** (built-in sample, no files, runs the 6-dim baseline), **📊 report history** (analysis/style-reports browsing), **🩺 diagnostics** (diagnostic report + statistics window + **one-click copy of the diagnostic bundle** for pasting into an issue); actionable error hints; slimmer tool descriptions.
182
+ 12. **Original mode & creation files**: fill in creation settings in the panel's **Creation profile** view (worldview / characters / forbidden events / main conflict / genre / extras, blank = model decides, per-book profile library); novel_outline maintains creation files (bible/characters/outline/hooks/status), enforcing the bible → outline → hook chain with dynamic batches (10→20→30 chapters) to prevent plot jumps and OOC.
183
+ 13. **Experience & stats**: the home page's Writing resources card ends with one line of **library summary** ("Library: 3 books · 13 chapters · 50k characters"); expand it for each book's chapters / characters / `7 days idle` — the old standalone library-stats card is now this one line. History & maintenance holds the **demo** (built-in sample, nothing saved, runs the six style measures), **report history** (saved sentence analysis and style reports, grouped by book) and **diagnostics** (statistics window + **one-click copy of the diagnostic bundle** for pasting into an issue); actionable error hints; slimmer tool descriptions.
184
184
 
185
185
  ---
186
186
 
package/README.md CHANGED
@@ -82,7 +82,7 @@ mkdir -p novels/我的小说 # 把章节文件放进去(第01章.md、第0
82
82
  3. **「自检第 7 章」** → `novel_style_check` 给出相似度、偏差清单与六维对照
83
83
  4. **「把第 7 章排成改稿清单」** → `novel_fix_plan` 给出按优先级排好的待办;改完再 `verify` 复测三态
84
84
 
85
- > 想让基线对着**你亲手写的那些章**、而不是全书?面板「风格基线」→ **基线范围** 选 **按部分** 并勾章即可:上面这条链上的自检、开写包、改稿台与风格画像都会改用这几章当基线(**按书生效**;默认「按全书」=旧行为,不动它就跟以前完全一样)。建章(`novel_new_chapter`)时附的 μ 摘要与锚段用的是同一份范围。
85
+ > 想让基线对着**你亲手写的那些章**、而不是全书?面板「风格基线」里把「范围」切到 **按部分**、在「挑选章节」里勾出标杆章,再点「保存范围」即可:上面这条链上的自检、开写包、改稿台与风格画像都会改用这几章当基线(**按书生效**;默认「全书」=旧行为,不动它就跟以前完全一样)。建章(`novel_new_chapter`)时附的 μ 摘要与锚段用的是同一份范围。
86
86
 
87
87
  只想先看看家底,就说 **「用 novel_style_report 给我的小说做一次风格画像」**:
88
88
 
@@ -155,7 +155,7 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
155
155
  | 花费 | **0(本地推理)** | 按 token 计费 | 自建 |
156
156
  | 中文小说专用 | **是** | 通用 | 否 |
157
157
  | 风格基线(μ±σ) | **有** | 少见 | 无 |
158
- | 与 DSH 集成 | **20 工具 + 官方席位与官网式界面** | 无 | 无 |
158
+ | 与 DSH 集成 | **20 个工具 + 官方席位与统一面板** | 无 | 无 |
159
159
  | 非 DSH 用户可用 | **可以(MCP)** | 可以 | 需自己封装 |
160
160
 
161
161
  > **致非中文用户**:本插件为中文小说分析写作而设计——句式、情感、意象等核心能力以及内置的语义模型,全部针对中文语料构建与调优。在深耕中文的同时兼顾英文等其他语言,确实超出了我目前的能力范围。若因此给您带来不便,我深感抱歉,恳请谅解。
@@ -169,14 +169,14 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
169
169
  3. **氛围光谱 12 轴**:噩梦感 / 焦虑压抑 / 温馨治愈 / 甜宠日常 / 催泪虐心 / 黑暗残酷 / 悬疑神秘 / 热血激昂 / 荒诞无厘头 / 孤独疏离 / 文艺唯美 / 情欲暧昧——证据链可追溯,0 token。
170
170
  4. **本地语义引擎**:bge-small-zh 中文模型(24MB 随插件分发)本地 CPU 推理——`novel_semantic_search` 自然语言搜全书语义相关段落(带章节定位),语义级风格对比、语义隐性情感,懒加载 + 自动回退。
171
171
  5. **句式模式分析**:九类句式分布、排列规律、句长节奏、情感曲线、风格指纹与节奏建议,带缓存与报告导出。
172
- 6. **情感净化 + 量化**:强/弱情绪词分级、污染源检测、caveat 预警 + AI 复核;Valence 滑动窗口 → 方差 V / 斜率 Δ / 矛盾指数 C + 隐性意象载体。
172
+ 6. **情感净化 + 量化**:强/弱情绪词分级、污染源检测、caveat 预警 + AI 复核;Valence 滑动窗口 → 方差 V / 斜率 Δ / 矛盾指数 C + 隐性意象载体。<br>**情感复杂度与置信度联动(v6.6.5)**:结论不可采信时(污染命中阈值、全文零情感词命中,或没有可用的 clean 情感计数)不再给顶格结论——`complexity.level` 标为 `unknown`,并附 `reliable: false` 与 `reliabilityNote` 说明原因,渲染文本里也会单独一行写出这句警告;`score` 保留数值便于跨章对比。R18 / 高生理描写文本的情感维度本就不适用,建议改用节奏、爽点、对话密度等维度。
173
173
  7. **世界观与语用检测**:文化基准自动判断(西/东/混合)+ 置信度;speechStyle 称谓/客套/仪式/语气规范;题材流派 + 网文信号。
174
174
  8. **写作辅助全家桶**:伏笔登记表 / 设定五张表(人物·地点·道具·时间线·世界观)/ 章节摘要 / 连贯性审计 / 批量导入 / 风格自检 / 续写辅助。
175
- 9. **全工具 UI 开关**:左栏「写作助手功能」入口 + 主栏面板(**DSH 0.2.0 官方席位**;老宿主 < 0.2.0 自动回退旧挂载)——总开关 + 工具开关分组 + 功能开关 + 提示词档位/场景/**精简工作流**,大白话文案,显示数据目录占用与语义引擎状态。颜色全走 `--dsw-alias-*` 语义 token(三主题与官方同源)、控件用官方 UI primitives、品牌蓝强调、光晕头部与分区 kicker、各级页面等宽。<br>**界面再进一步(方案 A)**:源码分块 + 构建链装配(构建失败不写盘、`--check` 校验源与产物一致)、6 个统一组件与单一行模式(`PreferenceRow`)、分组卡片贯穿全部开关页、19+5 枚线图标、阈值 slider 辅助输入、页面级 push-pop 过渡动画(零 state / 零 hook)。
176
- 10. **风格基线**:文笔六维测量(句法复杂度/修饰密度/抽象度/动作密度/不确定性/留白指数)+ 按章节 μ±σ 基线带;`novel_style_report` 输出基线带,`novel_style_check` 对照新章偏差(带内 ✓ / 出带 ⚠);面板「六维基线」视图可自定义每维 ±% 容差(**推荐值 = 原著章节波动的 1.5 倍 σ**,自动取整、限 ±10%~100%;输入框留空即用推荐)——原创/续写时主题自由、写法保持在基线带内。<br>**基线范围(v6.5.5,按书选择)**:基线不必永远是全书——面板「风格基线」里选**按全书**(默认,等于旧行为)/ **按部分**,再用章节选择器勾出你认作标杆的章,基线就**只用**这几章(是「只用」,不是「不够再补」)。这是给「AI 续写越写越像 AI 自己」准备的:从第 2 章续写起,模型参考的其实是它自己写的上一章,而**从全书取样的基线自己也在漂**(书越长 AI 占比越高)。把标杆钉死在你亲手写的那些章上,插件就能回答「**相对这几章**,新章偏了多少」——它不判断你有没有漂移,那一项本就不可判定。正在检查的那一章**永远排除**(否则等于拿自己跟自己比);勾中的章少于 3 章时推荐容差自动放宽到**至少 ±25%**,所以单章也能当基线;取不到的章(已改名 / 已删除,或**正文过短无法测量**)会被跳过并如实计数,**一章都取不到**时自动回退「按全书」并在返回体里写明原因。范围按书保存(`<书库根>/.novel-writer/baseline/<书名>.json`),换书互不影响。
175
+ 9. **全工具 UI 开关**:左栏「写作助手功能」入口 + 主栏面板(**DSH 0.2.0 官方席位**;老宿主 < 0.2.0 自动回退旧挂载)——首页按「写作资料 / AI 行为 / 功能与工具 / 记录与维护 / 高级」分区,**一个页面只讲一件事**;工具行统一由 `PreferenceRow` 承载,**开关本身就是状态**(开着的开关不再整行变蓝、也不再贴「已开启」),描述只写「对你有什么用」,工具 ID 与参数不进主文案。颜色全走 `--dsw-alias-*` 语义 token(三主题与官方同源)、控件用官方 UI primitives、DeepSeek 蓝只留给「唯一主操作」与需要注意的状态,各级页面等宽。<br>**v6.7.0 面板视觉改版**:`lib/client.js` 渲染层重写、视觉系统 CSS 4.6 KB → 19.4 KB、52 枚线图标(每个工具行一枚专属图标),文件路径 / 数据目录占用 / `state.json` 统一收进页面底部的「存储位置」折叠区,首页「功能与工具」的「语义模型」一行显示引擎状态;功能面完全不变(8 个视图 / 20 个工具 / 后端路由零改动),设计规格见仓库内 `tools/UI-REDESIGN.md`。<br>**界面底座(方案 A)**:源码分块 + 构建链装配(构建失败不写盘、`--check` 校验源与产物一致)、统一组件(`PreferenceRow` 单一行模式、`GroupCard` 分组卡、`StorageDisclosure` 存储位置折叠区等)、页面级 push-pop 过渡动画(零 state / 零 hook)。
176
+ 10. **风格基线**:文笔六维测量(句法复杂度/修饰密度/抽象度/动作密度/不确定性/留白指数)+ 按章节 μ±σ 基线带;`novel_style_report` 输出基线带,`novel_style_check` 对照新章偏差(带内 ✓ / 出带 ⚠);面板「风格基线」页的「文风六项指标」分区可自定义每维 ±% 容差(每维一行:指标名 + 「低于」「高于」两个百分比输入框;**推荐值 = 原著章节波动的 1.5 倍 σ**,自动取整、限 ±10%~100%;输入框留空即用推荐)——原创/续写时主题自由、写法保持在基线带内。<br>**基线范围(v6.5.5,按书选择)**:基线不必永远是全书——面板「风格基线」里选**全书**(默认,等于旧行为)/ **按部分**,再用章节选择器勾出你认作标杆的章,基线就**只用**这几章(是「只用」,不是「不够再补」)。这是给「AI 续写越写越像 AI 自己」准备的:从第 2 章续写起,模型参考的其实是它自己写的上一章,而**从全书取样的基线自己也在漂**(书越长 AI 占比越高)。把标杆钉死在你亲手写的那些章上,插件就能回答「**相对这几章**,新章偏了多少」——它不判断你有没有漂移,那一项本就不可判定。正在检查的那一章**永远排除**(否则等于拿自己跟自己比);勾中的章少于 3 章时推荐容差自动放宽到**至少 ±25%**,所以单章也能当基线;取不到的章(已改名 / 已删除,或**正文过短无法测量**)会被跳过并如实计数,**一章都取不到**时自动回退「按全书」并在返回体里写明原因。范围按书保存(`<书库根>/.novel-writer/baseline/<书名>.json`),换书互不影响。
177
177
  11. **写作哨兵三件套**:`novel_continuity_check` 扩展——①**衔接检查**(chapter 参数:时间硬跳/语义距离/人物延续/钩子承接四路检测,带原文引用)②**OOC 检测**(ooc 参数:角色情绪基线偏离)③**大纲走偏**(outline 参数:方向行 vs 正文关键词重合);报告工具支持 **brief 精简模式**。
178
- 12. **原创模式与创作资料**:面板「原创模式」填写创作设定(世界观/角色/禁忌/主线/题材/额外要求,留空=模型自定,多书独立设定库);novel_outline 维护创作资料(创作设定/人物/剧情大纲/钩子记录/创作状态卡),原创强制「设定书→大纲→钩子」链,动态批次(10→20→30 章)防剧情跳跃与角色 OOC。
179
- 13. **体验与统计**:主面板**书库统计卡**(每本章数/总字数/近 7 天活跃字数,活跃🔥标绿)、**🎬 体验演示**(内置示例不落盘跑六维基线)、**📊 报告历史**(analysis/style-reports 列表浏览)、**🩺 诊断**(诊断报告 + 统计窗口 + **一键复制诊断包**,用于贴进 issue);错误提示带解决步骤;工具说明压缩省 token。
178
+ 12. **原创模式与创作资料**:面板「原创设定」填写创作设定(世界观 / 角色设定 / 不允许的事件 / 主线目的 / 题材偏好 / 额外要求,留空=模型自定,多书独立设定库);novel_outline 维护创作资料(创作设定/人物/剧情大纲/钩子记录/创作状态卡),原创强制「设定书→大纲→钩子」链,动态批次(10→20→30 章)防剧情跳跃与角色 OOC。
179
+ 13. **体验与统计**:首页「写作资料」卡底部一行**书库概况**(「书库 3 部 · 13 章 · 5.0 万字」,点开是每本书的章数 / 字数 / `7 天未动`)——原来独立的「书库统计」卡已并入这一行;「记录与维护」里有**体验演示**(内置示例不落盘跑六项文风指标,不保存任何内容)、**报告历史**(已保存的句式分析与风格报告,按书分组)与**诊断报告**(统计窗口 + **一键复制诊断包**,用于贴进 issue);错误提示带解决步骤;工具说明压缩省 token。
180
180
 
181
181
  ---
182
182
 
package/lib/analysis.js CHANGED
@@ -1165,20 +1165,57 @@ export function chapterEmotionStats(counts) {
1165
1165
  };
1166
1166
  }
1167
1167
 
1168
- /** v1.6.0 全书聚合:复杂度评分 0-1(熵归一化0.5 + 多样性0.25 + 主次冲突0.25)+ 章间漂移。 */
1169
- export function emotionComplexity(perChapter) {
1168
+ /**
1169
+ * v1.6.0 全书聚合:复杂度评分 0-1(熵归一化0.5 + 多样性0.25 + 主次冲突0.25)+ 章间漂移。
1170
+ *
1171
+ * v6.6.5 修正(用户反馈「第一章的情感复杂度会直接爆掉」):
1172
+ * 复杂度此前**与置信度完全脱钩** ✗ —— 同一份输出里一边写着 `confidence: "low"` + 「检测到高密度
1173
+ * R18/生理描写,dominant 可能来自生理/爽感词而非真实情感,请勿采信」,另一边**照样**给出
1174
+ * `complexity: { score: 0.6, level: "high" }`。下游 AI 只要读 `level` 就会得出「情感复杂度高」的
1175
+ * 结论,**而这个结论已被工具自己否定**。实测(棍妹 第一/二章,R18 密度 8.99‰ / 9.77‰):
1176
+ * 两章都判 `level: "high"`,而两章的 `confidence` 都是 `"low"`。
1177
+ *
1178
+ * 现在接收 `reliability`(由调用方把污染/无数据判定传进来),当结论不可采信时:
1179
+ * · `level` 降级为 `"unknown"`(**不再给顶格的 high**)
1180
+ * · 加 `reliable: false` + `reliabilityNote`(固定说明)—— 让"不可采信"出现在**结论字段本身**,
1181
+ * 而不是只写在旁边的 caveat 里(那正是误导的来源)
1182
+ * · `score` 数值保留(便于对比),但 `reliable: false` 已标明其地位
1183
+ *
1184
+ * @param perChapter 分章计数(**clean 计数**,调用方已用 cleanScoresOf 聚合)
1185
+ * @param reliability 可选:{ polluted?: boolean, pollutedBy?: string[], noData?: boolean }
1186
+ */
1187
+ export function emotionComplexity(perChapter, reliability) {
1188
+ const rel = reliability && typeof reliability === "object" ? reliability : null;
1189
+ const polluted = rel?.polluted === true;
1190
+ const noData = rel?.noData === true;
1191
+ const unreliable = polluted || noData;
1192
+ const reliabilityNote = polluted
1193
+ ? "⚠️ 本复杂度基于已被污染的情感计数(" + ((rel?.pollutedBy ?? []).join("、") || "高密度场景性用词") + "),不可采信:请勿据此判断文本的情感复杂度,须 novel_read 抽查原文后自行判断。"
1194
+ : noData
1195
+ ? "ℹ️ 未检出任何情感词,本复杂度无数据可依(score 0 表示「没测到」,不是「情感简单」),请勿据此下结论。"
1196
+ : "";
1197
+ const levelOf = (raw) => (unreliable ? "unknown" : raw);
1170
1198
  // v4.0.0 修正:c.counts 缺失时不再抛 TypeError(与其余 ?? 0 的防御风格一致)
1171
1199
  const list = Array.isArray(perChapter) ? perChapter : [];
1172
1200
  const stats = list.map((c) => ({ chapter: c?.chapter, ...(chapterEmotionStats(c?.counts) ?? {}) })).filter((s) => s.entropy !== void 0);
1173
1201
  if (stats.length === 0) {
1174
- // 无情感词命中:不复杂(low),恒有值供模型读取
1202
+ // v6.6.5 修正(**由文档负责人在实测中独立发现的同类残留**,与本次要修的是同一类问题):
1203
+ // 走到这里说明 **clean 情感计数为空**(stats 正是从 clean 计数算出来的)—— 也就是复杂度
1204
+ // **无数据可依** ✗。而此前只在 `noData`(**raw** 计数为零)时才标不可采信,于是
1205
+ // 「raw 命中了弱词、clean 一个强词都没有」这类文本(实测 `她笑了笑,眼泪掉了下来。`)
1206
+ // 会得到 `level: "low"` + `reliable: true`,**而 `reliabilityNote` 却写着「无数据可依」** ✗
1207
+ // —— 结论与说明自相矛盾,正是本版要消灭的形态。现在统一:**没有 clean 数据 ⇒ 不可采信、level 给 unknown**。
1175
1208
  return {
1176
- score: 0, level: "low", entropy: 0, maxEntropy: Math.round(EMOTION_MAX_ENTROPY * 1000) / 1000,
1209
+ score: 0, level: "unknown", entropy: 0, maxEntropy: Math.round(EMOTION_MAX_ENTROPY * 1000) / 1000,
1177
1210
  // v4.3.0(清单第 35 条):无次要情感时 secondary 统一为空串(旧版此处"neutral"、
1178
1211
  // 上方非空分支却给"joy"这类 0 占比情感,两处口径不同)。
1179
1212
  diversity: 0, dominant: "neutral", dominantRatio: 0, secondary: "", secondaryRatio: 0,
1180
1213
  conflict: "", conflictStrength: 0,
1181
- chapterDrift: { meanEntropy: 0, entropyVariance: 0, swinging: false }
1214
+ chapterDrift: { meanEntropy: 0, entropyVariance: 0, swinging: false },
1215
+ reliable: false,
1216
+ reliabilityNote: polluted
1217
+ ? reliabilityNote
1218
+ : "ℹ️ 未检出可用的情感计数(clean 计数为空),本复杂度无数据可依:score 0 表示「没测到」,不是「情感简单」,请勿据此下结论。"
1182
1219
  };
1183
1220
  }
1184
1221
  const totalCounts = {};
@@ -1196,7 +1233,8 @@ export function emotionComplexity(perChapter) {
1196
1233
  const level = score >= 0.6 ? "high" : score >= 0.4 ? "medium" : "low";
1197
1234
  return {
1198
1235
  score: Math.round(score * 100) / 100,
1199
- level,
1236
+ // v6.6.5:不可采信时降级为 "unknown" —— 见函数头说明(原先照样给 high,与 confidence:"low" 自相矛盾)
1237
+ level: levelOf(level),
1200
1238
  entropy: global.entropy,
1201
1239
  maxEntropy: Math.round(EMOTION_MAX_ENTROPY * 1000) / 1000,
1202
1240
  diversity: global.diversity,
@@ -1212,14 +1250,19 @@ export function emotionComplexity(perChapter) {
1212
1250
  meanEntropy: Math.round(meanEntropy * 1000) / 1000,
1213
1251
  entropyVariance: Math.round(entropyVariance * 10000) / 10000,
1214
1252
  swinging: entropyVariance > 0.03
1215
- }
1253
+ },
1254
+ reliable: !unreliable,
1255
+ reliabilityNote
1216
1256
  };
1217
1257
  }
1218
1258
 
1219
1259
  /**
1220
1260
  * v1.6.0 情感量化入口:三指标 + 显隐对比 + 复杂度 + 复合共现(纯规则 0 token)。
1261
+ *
1262
+ * v6.6.5:新增第 4 个可选参数 `reliability`,透传给 `emotionComplexity`——
1263
+ * 让复杂度知道"这次的情感计数是否被污染/是否根本没数据",从而不再给出与 `confidence` 矛盾的顶格结论。
1221
1264
  */
1222
- export function emotionalQuantification(text, perChapter, blocks) {
1265
+ export function emotionalQuantification(text, perChapter, blocks, reliability) {
1223
1266
  // v4.3.0(清单第 33 条):入参兜底——旧版 emotionalQuantification("x", [{chapter,counts}])
1224
1267
  // 抛 "blocks is not iterable",而它下面的 fallback 分支本意就是支持"无分章数据"这一输入。
1225
1268
  const src = String(text ?? "");
@@ -1242,7 +1285,7 @@ export function emotionalQuantification(text, perChapter, blocks) {
1242
1285
  stats,
1243
1286
  implicit,
1244
1287
  compare: explicitImplicitCompare(meanValence, implicit),
1245
- complexity: emotionComplexity(perChapter),
1288
+ complexity: emotionComplexity(perChapter, reliability),
1246
1289
  composites: compositeEmotionPairs(blockList)
1247
1290
  };
1248
1291
  }
@@ -1537,7 +1580,13 @@ export function analyzeText(text, options = {}) {
1537
1580
  }).filter((m) => Object.values(m.counts).some((v) => v > 0))
1538
1581
  };
1539
1582
  })();
1540
- const quantification = emotionalQuantification(sampleText, perChapterGroups.entries, blockMeta.map((m) => m.sentences).filter((s) => s.length > 0));
1583
+ const quantification = emotionalQuantification(sampleText, perChapterGroups.entries, blockMeta.map((m) => m.sentences).filter((s) => s.length > 0), {
1584
+ // v6.6.5:把"这次的情感计数可不可信"传给复杂度,避免出现
1585
+ // 「confidence: low(勿采信)」与「complexity.level: high」并存的矛盾输出。
1586
+ polluted,
1587
+ pollutedBy,
1588
+ noData: totalEmotion === 0
1589
+ });
1541
1590
  if (quantification?.complexity?.chapterDrift) quantification.complexity.chapterDrift.basis = perChapterGroups.basis;
1542
1591
  const emotion = {
1543
1592
  dominant: dominantEmotion,