# 章节切分泄漏问题根因分析报告 ## 问题描述 **现象**: 第十章"其他资料"的内容(计算书、相关图纸等)被错误地合并到第九章的最后一个 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 专项施工方案 ``` 这是**页眉**内容,根本不包含"第十章"字符串! **结论**: 存在**位置计算 bug**,`current_pos` 的累加逻辑或 `_find_pattern_in_line` 的返回值计算有误。 ### 3. 代码分析 **可疑代码段** (`title_matcher.py`): ```python 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: 修复位置计算逻辑(推荐) 统一使用原始文本进行位置计算,避免清理前后的位置映射问题: ```python 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: 增加页眉过滤 在标题查找前,先过滤掉页眉页脚内容,避免干扰: ```python def _filter_headers_footers(self, pages_content: List[Dict]) -> str: """过滤每页的页眉页脚后再进行标题查找""" # 实现页眉页脚检测和过滤逻辑 ``` ### 方案 3: 基于 TOC 的标题定位增强 利用 PDF 目录中的页码信息,限制标题搜索范围: ```python 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 **状态**: 待修复