章节切分泄漏根因分析.md 5.2 KB

章节切分泄漏问题根因分析报告

问题描述

现象: 第十章"其他资料"的内容(计算书、相关图纸等)被错误地合并到第九章的最后一个 chunk 中。

验证结果: 通过 verify_leak.py 脚本验证,确认这是真实泄漏,非误报。

  • 泄漏内容:2359 字符的第十章内容
  • 影响范围:特定测试文件 测试模版-四川路桥专项施工方案框架以及编制说明(2025修订第三版)- v0.2.pdf
  • 批量测试结果:7 个文件中 6 个通过,仅该文件失败

根因分析

1. 标题位置查找异常

涉及代码: core/construction_review/component/doc_worker/utils/title_matcher.py

发现的问题:

  • _find_full_title_positions("第十章 其他资料", full_text) 正确找到两个位置:[4524, 43321]
    • 4524: 目录中的"第十章 其他资料"
    • 43321: 正文中真实的"第十章 其他资料"标题
  • _select_best_position 最终返回的位置是:32460(错误!)

2. 位置 32460 的真相

实际内容: 位置 32460 所在的文本是:

XXXX 公司 XXX 专项施工方案

这是页眉内容,根本不包含"第十章"字符串!

结论: 存在位置计算 bugcurrent_pos 的累加逻辑或 _find_pattern_in_line 的返回值计算有误。

3. 代码分析

可疑代码段 (title_matcher.py):

def _find_full_title_positions(self, title: str, text: str) -> List[int]:
    positions = []
    lines = text.split('\n')
    current_pos = 0

    for line in lines:
        line_clean = self._remove_escape_chars(line)
        line_normalized = self._normalize_title(line_clean)

        if title_normalized in line_normalized:
            pos_in_line = line_normalized.find(title_normalized)
            line_pos = self._find_pattern_in_line(
                title_normalized, line, pos_in_line
            )
            if line_pos >= 0:
                found_pos = current_pos + line_pos  # <-- 可能的问题
                positions.append(found_pos)

        current_pos += len(line) + 1  # +1 for newline <-- 可能的问题

可能的根本原因:

  1. _find_pattern_in_line 返回的 line_pos 是在原始行中的位置,但 current_pos 是基于清理前行计算的
  2. 字符编码问题导致位置偏移(如中文标点、特殊空白字符)
  3. _remove_escape_chars 前后的长度差异未正确处理

影响评估

维度 评估
严重性 高 - 导致章节内容错位,影响完整性审查准确性
范围 特定文件触发,可能与页眉格式有关
频率 批量测试 7 个文件中出现 1 次,约 14% 触发率

修复建议

方案 1: 修复位置计算逻辑(推荐)

统一使用原始文本进行位置计算,避免清理前后的位置映射问题:

def _find_full_title_positions(self, title: str, text: str) -> List[int]:
    # 使用原始文本直接搜索,避免位置映射复杂性
    import re

    # 构建兼容空格变体的正则模式
    title_pattern = title.replace(' ', r'\s+')
    positions = []

    for m in re.finditer(title_pattern, text):
        # 验证这是否是真正的标题行(行首/独立)
        line_start = text.rfind('\n', 0, m.start()) + 1
        line_end = text.find('\n', m.start())
        line_text = text[line_start:line_end].strip()

        if self._is_valid_title_line(line_text, title):
            positions.append(m.start())

    return positions

方案 2: 增加页眉过滤

在标题查找前,先过滤掉页眉页脚内容,避免干扰:

def _filter_headers_footers(self, pages_content: List[Dict]) -> str:
    """过滤每页的页眉页脚后再进行标题查找"""
    # 实现页眉页脚检测和过滤逻辑

方案 3: 基于 TOC 的标题定位增强

利用 PDF 目录中的页码信息,限制标题搜索范围:

def find_title_positions(self, items, full_text, pages_content, toc_pages):
    # 对于每个标题,使用 TOC 中的页码信息缩小搜索范围
    # 只在 TOC 指示的页面范围内搜索对应标题

相关提交

  • commit 93bcdb9: fix(doc_worker): 修复章节标题定位错误导致的跨章节内容吞并
    • 该提交添加了 _get_toc_boundary_position 方法用于 TOC 边界保护
    • 但本问题的根因在 _find_full_title_positions 的位置计算,该提交未修复

调试脚本

以下脚本可用于复现和验证问题:

  1. utils_test/Chunk_Split_Test/verify_leak.py - 验证泄漏是否存在
  2. utils_test/Chunk_Split_Test/debug_split.py - 跟踪 _get_toc_boundary_position 调用
  3. utils_test/Chunk_Split_Test/debug_split2.py - 跟踪 found_titles 列表
  4. utils_test/Chunk_Split_Test/debug_actual_text.py - 分析实际 PDF 文本
  5. utils_test/Chunk_Split_Test/debug_find_positions.py - 手动跟踪位置查找逻辑

附录:关键位置数据

标题: "第十章 其他资料"

正确位置:
- 4524: 目录中出现
- 43321: 正文中真实标题位置

错误返回:
- 32460: 页眉文本中(不包含"第十章")

位置 32460 所在行内容:
'XXXX 公司 XXX 专项施工方案'

分析时间: 2026-03-30 分析人: Claude Code 状态: 待修复