Просмотр исходного кода

feat: add dim 33 outline adherence detection (fixes #22)

Writer prompt now treats volume_outline as a hard constraint instead of
soft guidance. PRE_WRITE_CHECK requires outline anchor before writing.
Auditor reads volume_outline and checks for plot node skipping or
premature consumption. Updated docs from 32 to 33 dimensions.
Ma 7 месяцев назад
Родитель
Сommit
f5d7bc1e2a

+ 6 - 6
README.md

@@ -90,7 +90,7 @@ inkos style import 参考小说.txt 吞天魔帝 --name 某作者  # 导入文
 
 ### 其他 v0.4 变更
 
-- 审计维度从 26 扩展到 32(+4 番外维度 + dim 27 敏感词 + dim 32 读者期待管理)
+- 审计维度从 26 扩展到 33(+4 番外维度 + dim 27 敏感词 + dim 32 读者期待管理 + dim 33 大纲偏离检测)
 - 审计员联网搜索:年代考据题材可联网核实真实事件/人物/地理(原生搜索能力)
 - 调度器重写:AI 节奏(默认 15 分钟一轮)、并行书处理、立即重试、每日上限
 - 修订者新增 `spot-fix` 模式(定点修复)
@@ -191,13 +191,13 @@ fatigueWordsOverride: ["瞳孔骤缩", "不可置信"]   # 覆盖题材默认
 
 主角人设锁定、数值上限、自定义禁令、疲劳词覆盖——每本书的规则独立调整,不影响题材模板。
 
-### 32 维度审计
+### 33 维度审计
 
-审计细化为 32 个维度,按题材自动启用对应的子集:
+审计细化为 33 个维度,按题材自动启用对应的子集:
 
-OOC检查、时间线、设定冲突、战力崩坏、数值检查、伏笔、节奏、文风、信息越界、词汇疲劳、利益链断裂、年代考据、配角降智、配角工具人化、爽点虚化、台词失真、流水账、知识库污染、视角一致性、段落等长、套话密度、公式化转折、列表式结构、支线停滞、弧线平坦、节奏单调、敏感词检查、正传事件冲突、未来信息泄露、世界规则跨书一致性、番外伏笔隔离、读者期待管理
+OOC检查、时间线、设定冲突、战力崩坏、数值检查、伏笔、节奏、文风、信息越界、词汇疲劳、利益链断裂、年代考据、配角降智、配角工具人化、爽点虚化、台词失真、流水账、知识库污染、视角一致性、段落等长、套话密度、公式化转折、列表式结构、支线停滞、弧线平坦、节奏单调、敏感词检查、正传事件冲突、未来信息泄露、世界规则跨书一致性、番外伏笔隔离、读者期待管理、大纲偏离检测
 
-dim 20-23(AI 痕迹)+ dim 27(敏感词)由纯规则引擎检测,不消耗 LLM 调用。dim 28-31(番外维度)检测到 `parent_canon.md` 自动激活。dim 32(读者期待管理)始终开启。
+dim 20-23(AI 痕迹)+ dim 27(敏感词)由纯规则引擎检测,不消耗 LLM 调用。dim 28-31(番外维度)检测到 `parent_canon.md` 自动激活。dim 32(读者期待管理)、dim 33(大纲偏离检测)始终开启。
 
 ### 去 AI 味
 
@@ -483,7 +483,7 @@ TypeScript 单仓库,pnpm workspaces 管理。
 - [x] 可插拔雷达(RadarSource 接口)
 - [x] 外部 Agent 集成(OpenClaw 等)
 - [x] 题材自定义 + 单本书规则(genre CLI + book_rules.md)
-- [x] 32 维度连续性审计(含 AI 痕迹检测 + 番外维度)
+- [x] 33 维度连续性审计(含 AI 痕迹检测 + 番外维度 + 大纲偏离检测)
 - [x] 去 AI 味铁律 + 文风指纹注入
 - [x] 多 LLM provider(OpenAI + Anthropic + 兼容接口)
 - [x] AIGC 检测 + 反检测改写管线

+ 12 - 2
packages/core/src/agents/continuity.ts

@@ -52,6 +52,7 @@ const DIMENSION_MAP: Record<number, string> = {
   30: "世界规则跨书一致性",
   31: "番外伏笔隔离",
   32: "读者期待管理",
+  33: "大纲偏离检测",
 };
 
 function buildDimensionList(
@@ -92,6 +93,7 @@ function buildDimensionList(
 
   // Always-active dimensions
   activeIds.add(32); // 读者期待管理 — universal
+  activeIds.add(33); // 大纲偏离检测 — universal
 
   // Conditional overrides
   if (gp.eraResearch || bookRules?.eraConstraints?.enabled) {
@@ -154,6 +156,9 @@ function buildDimensionList(
     if (id === 32) {
       note = "检查:章尾是否有钩子?最近3-5章内是否有爽点落地?是否存在超过3章的情绪压制无释放?读者的情绪缺口是否在积累或被满足?";
     }
+    if (id === 33) {
+      note = "对照 volume_outline:本章内容是否对应卷纲中当前章节范围的剧情节点?是否跳过了节点或提前消耗了后续节点?剧情推进速度是否与卷纲规划的章节跨度匹配?如果卷纲规划某段剧情跨N章但实际1-2章就讲完→critical";
+    }
 
     dims.push({ id, name, note });
   }
@@ -173,7 +178,7 @@ export class ContinuityAuditor extends BaseAgent {
     genre?: string,
     options?: { temperature?: number },
   ): Promise<AuditResult> {
-    const [currentState, ledger, hooks, styleGuideRaw, subplotBoard, emotionalArcs, characterMatrix, chapterSummaries, parentCanon] =
+    const [currentState, ledger, hooks, styleGuideRaw, subplotBoard, emotionalArcs, characterMatrix, chapterSummaries, parentCanon, volumeOutline] =
       await Promise.all([
         this.readFileSafe(join(bookDir, "story/current_state.md")),
         this.readFileSafe(join(bookDir, "story/particle_ledger.md")),
@@ -184,6 +189,7 @@ export class ContinuityAuditor extends BaseAgent {
         this.readFileSafe(join(bookDir, "story/character_matrix.md")),
         this.readFileSafe(join(bookDir, "story/chapter_summaries.md")),
         this.readFileSafe(join(bookDir, "story/parent_canon.md")),
+        this.readFileSafe(join(bookDir, "story/volume_outline.md")),
       ]);
 
     const hasParentCanon = parentCanon !== "(文件不存在)";
@@ -254,6 +260,10 @@ ${dimList}
       ? `\n## 正传正典参照(番外审查专用)\n${parentCanon}\n`
       : "";
 
+    const outlineBlock = volumeOutline !== "(文件不存在)"
+      ? `\n## 卷纲(用于大纲偏离检测)\n${volumeOutline}\n`
+      : "";
+
     const userPrompt = `请审查第${chapterNumber}章。
 
 ## 当前状态卡
@@ -261,7 +271,7 @@ ${currentState}
 ${ledgerBlock}
 ## 伏笔池
 ${hooks}
-${subplotBlock}${emotionalBlock}${matrixBlock}${summariesBlock}${canonBlock}
+${subplotBlock}${emotionalBlock}${matrixBlock}${summariesBlock}${canonBlock}${outlineBlock}
 ## 文风指南
 ${styleGuide}
 

+ 2 - 0
packages/core/src/agents/writer-prompts.ts

@@ -396,6 +396,7 @@ function buildPreWriteChecklist(book: BookConfig, gp: GenreProfile): string {
   const lines = [
     "## 动笔前必须自问",
     "",
+    `${idx++}. 【大纲锚定】本章对应卷纲中的哪个节点/阶段?本章必须推进该节点的剧情,不得跳过或提前消耗后续节点。如果卷纲指定了章节范围,严格遵守节奏。`,
     `${idx++}. 主角此刻利益最大化的选择是什么?`,
     `${idx++}. 这场冲突是谁先动手,为什么非做不可?`,
     `${idx++}. 配角/反派是否有明确诉求、恐惧和反制?行为是否由"过往经历+当前利益+性格底色"驱动?`,
@@ -433,6 +434,7 @@ function buildOutputFormat(book: BookConfig, gp: GenreProfile): string {
 (必须输出Markdown表格)
 | 检查项 | 本章记录 | 备注 |
 |--------|----------|------|
+| 大纲锚定 | 当前卷名/阶段 + 本章应推进的具体节点 | 严禁跳过节点或提前消耗后续剧情 |
 | 上下文范围 | 第X章至第Y章 / 状态卡 / 设定文件 | |
 | 当前锚点 | 地点 / 对手 / 收益目标 | 锚点必须具体 |
 ${resourceRow}| 待回收伏笔 | Hook-A / Hook-B | 与伏笔池一致 |

+ 7 - 1
packages/core/src/agents/writer.ts

@@ -245,9 +245,15 @@ ${params.recentChapters || "(这是第一章,无前文)"}
 ## 世界观设定
 ${params.storyBible}
 
-## 卷纲
+## 卷纲(硬约束——必须遵守)
 ${params.volumeOutline}
 
+【卷纲遵守规则】
+- 本章内容必须对应卷纲中当前章节范围内的剧情节点,严禁跳过或提前消耗后续节点
+- 如果卷纲指定了某个事件/转折发生在第N章,不得提前到本章完成
+- 剧情推进速度必须与卷纲规划的章节跨度匹配:如果卷纲规划某段剧情跨5章,不得在1-2章内讲完
+- PRE_WRITE_CHECK中必须明确标注本章对应的卷纲节点
+
 要求:
 - 正文不少于${params.wordCount}字
 - 写完后更新状态卡${params.ledger ? "、资源账本" : ""}、伏笔池、章节摘要、支线进度板、情感弧线、角色交互矩阵