# All files / modules/book-generator/utils content-consistency.ts

0% Statements 0/95
100% Branches 1/1
100% Functions 1/1
0% Lines 0/95

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168

     /**
     * 内容一致性跟踪器（事中预防）
     *
     * 解决 issue：质量检查是"事后纠错"，问题发现得晚，rewrite 成本高。
     *
     * 在内容生成过程中维护一个运行中的"全书术语表/关键概念档案"，
     * 每章生成后自动校验关键术语和逻辑是否一致，发现问题可即时触发修正。
     *
     * 核心思路：
     *   - 逐章/逐节生成时，提取并缓存关键术语、定义、角色设定
     *   - 后续章节生成后，快速比对缓存中的术语定义是否一致
     *   - 发现冲突立即记录，生成完成后汇总报告
     */

    /** 术语定义记录 */
    export interface TermRecord {
      term: string;           // 术语/概念名
      definition: string;     // 定义/描述
      firstChapter: number;   // 首次出现的章节号
      chapterOccurrences: number[]; // 出现的章节号列表
    }

    /** 一致性检查结果 */
    export interface ConsistencyIssue {
      type: 'conflicting_definition' | 'missing_definition' | 'inconsistent_naming' | 'logical_contradiction';
      severity: 'high' | 'medium' | 'low';
      term: string;
      chapterA: number;
      chapterB?: number;
      description: string;
    }

    /** 一致性报告 */
    export interface ConsistencyReport {
      terms: TermRecord[];
      issues: ConsistencyIssue[];
      hasConflicts: boolean;
    }

    export class BookConsistencyTracker {
      private terms: Map<string, TermRecord> = new Map();
      private chapterContentCache: Map<number, string> = new Map();
      private issues: ConsistencyIssue[] = [];

      /**
       * 记录一个新生成的章节内容，提取关键术语。
       */
      registerChapter(
        chapterNumber: number,
        chapterTitle: string,
        content: string
      ): void {
        this.chapterContentCache.set(chapterNumber, content);

        // 简单正则提取可能的术语定义模式（中文常见格式）
        // 模式1：xxx 是指 / xxx 指的是 / xxx 定义为
        const definitionPatterns = [
          /([^\s，,。.；;]{2,15})[（(]([^）)]{2,20})[）)]/g,           // 术语（英文/缩写）
          /([^\s，,。.；;]{2,20})(?:是指|指的是|定义为|即|也就是|意为)\s*([^\s，,。.；;]{2,30})/g, // 术语是指xxx
        ];

        for (const pattern of definitionPatterns) {
          let match: RegExpExecArray | null;
          while ((match = pattern.exec(content)) !== null) {
            const term = match[1].trim();
            const definition = match[2].trim();

            // 过滤太短的噪声
            if (term.length < 2 || definition.length < 1) continue;

            const existing = this.terms.get(term);
            if (existing) {
              existing.chapterOccurrences.push(chapterNumber);
              // 检查定义是否冲突
              if (existing.definition !== definition &&
                  this.levenshteinSimilarity(existing.definition, definition) < 0.6) {
                this.issues.push({
                  type: 'conflicting_definition',
                  severity: 'high',
                  term,
                  chapterA: existing.firstChapter,
                  chapterB: chapterNumber,
                  description: `术语"${term}"在第${existing.firstChapter}章定义为"${existing.definition}"，第${chapterNumber}章定义为"${definition}"，差异较大`,
                });
              }
            } else {
              this.terms.set(term, {
                term,
                definition,
                firstChapter: chapterNumber,
                chapterOccurrences: [chapterNumber],
              });
            }
          }
        }
      }

      /**
       * 获取全书术语摘要，可嵌入后续章节的提示词中
       * @param maxTerms 最多返回的术语数
       * @param currentChapter 当前正在生成的章节号（排除该章自身的术语）
       */
      getTermContext(maxTerms: number = 20, currentChapter?: number): string {
        const entries = Array.from(this.terms.values())
          .filter(t => currentChapter === undefined || !t.chapterOccurrences.includes(currentChapter))
          .slice(0, maxTerms);

        if (entries.length === 0) return '';

        const lines = entries.map(t =>
          `- ${t.term}：${t.definition}（见第${t.firstChapter}章）`
        );
        return `\n## 已建立的关键概念/术语（请保持一致定义）\n${lines.join('\n')}`;
      }

      /**
       * 获取所有发现的一致性问题
       */
      getIssues(): ConsistencyIssue[] {
        return [...this.issues];
      }

      /**
       * 生成完整的术语一致性报告
       */
      generateReport(): ConsistencyReport {
        return {
          terms: Array.from(this.terms.values()),
          issues: [...this.issues],
          hasConflicts: this.issues.length > 0,
        };
      }

      /**
       * 简单字符串相似度（Levenshtein 距离比）
       */
      private levenshteinSimilarity(a: string, b: string): number {
        const maxLen = Math.max(a.length, b.length);
        if (maxLen === 0) return 1;
        const distance = this.levenshteinDistance(a, b);
        return 1 - distance / maxLen;
      }

      private levenshteinDistance(a: string, b: string): number {
        const matrix: number[][] = [];
        for (let i = 0; i <= a.length; i++) {
          matrix[i] = [i];
        }
        for (let j = 0; j <= b.length; j++) {
          matrix[0][j] = j;
        }
        for (let i = 1; i <= a.length; i++) {
          for (let j = 1; j <= b.length; j++) {
            if (a[i - 1] === b[j - 1]) {
              matrix[i][j] = matrix[i - 1][j - 1];
            } else {
              matrix[i][j] = Math.min(
                matrix[i - 1][j] + 1,
                matrix[i][j - 1] + 1,
                matrix[i - 1][j - 1] + 1
              );
            }
          }
        }
        return matrix[a.length][b.length];
      }
    }