fix(parser): PDF/DOCX 切片层级结构修复与 MinerU V2 解析增强
核心修复: - _post_process_chunks: 用 _buffer_has_body 标志替代 buffer.text_level=0, 标题+正文合并后保留 text_level 不置零,修复层级信息丢失 - heading_rules numeric_level2: 正则从 ^\d+\.\d+[\.、\s] 改为 ^\d+\.\d+(?!\.\d),修复无空格标题如"2.1运行调度"无法匹配 - V2 title handler: heading_rules 优先(模式匹配可靠),VLM 仅兜底 (VLM 常给所有标题 level=1) MinerU V2 解析增强: - 两轮 TOC 过滤:多行目录块检测 + 孤立标题/单字符残留清理 - 封面 logo 过滤、重复标题去重 - chart VLM 描述和 Markdown 数据表提取 - ChromaDB metadata 新增 text_level/bbox/table_type/sub_type 字段 配置调整: - CLUSTER_SECTION_PREFIX_LEVELS 1→2(两级 section 聚类更精确) - MINERU_LOCAL_BACKEND 默认改为 pipeline 文档: - RAG检索流程逻辑.md 更新 MinerUChunk 字段、ChromaDB metadata、 MinerU 解析策略等章节 - 新增 RAG引用跳转-优化计划.md、云端MinerU输出分析与优化方案.md
This commit is contained in:
@@ -106,11 +106,12 @@ DEFAULT_HEADING_RULES: List[HeadingRule] = [
|
||||
name="chinese_chapter",
|
||||
),
|
||||
# 2. 中文条款编号 -> h2
|
||||
# 匹配:第一条、第三款 等
|
||||
# 匹配:第一条、第三款 等(仅短标题,长正文段落不算标题)
|
||||
HeadingRule(
|
||||
pattern=re.compile(r'^第[一二三四五六七八九十百千万]+[条款]'),
|
||||
level=2,
|
||||
name="chinese_article",
|
||||
max_length=30,
|
||||
),
|
||||
# 3. 数字三级标题 -> h3(必须在二级之前匹配)
|
||||
# 匹配:1.1.1 背景、2.3.4 方案 等
|
||||
@@ -121,20 +122,23 @@ DEFAULT_HEADING_RULES: List[HeadingRule] = [
|
||||
max_length=100,
|
||||
),
|
||||
# 4. 数字二级标题 -> h2(必须在一级之前匹配)
|
||||
# 匹配:1.1 背景、2.3 方案 等
|
||||
# 匹配:1.1 背景、2.3 方案、2.1运行调度(无空格) 等
|
||||
# 使用负向前瞻排除三级标题(由 numeric_level3 处理)
|
||||
HeadingRule(
|
||||
pattern=re.compile(r'^\d+\.\d+[\.、\s]'),
|
||||
pattern=re.compile(r'^\d+\.\d+(?!\.\d)'),
|
||||
level=2,
|
||||
name="numeric_level2",
|
||||
max_length=80,
|
||||
),
|
||||
# 5. 数字一级标题 -> h1
|
||||
# 匹配:1. 概述、2、背景 等
|
||||
# 排除:以 ;;。,、: 结尾的文本(这些是编号列表项/子条目,不是独立标题)
|
||||
HeadingRule(
|
||||
pattern=re.compile(r'^\d+[\.、\s]'),
|
||||
level=1,
|
||||
name="numeric_level1",
|
||||
max_length=50,
|
||||
exclude_pattern=re.compile(r'[;;。,、::]$'),
|
||||
),
|
||||
# 6. 英文章节标题 -> h1
|
||||
# 匹配:Chapter 1、Section 2、Part 3 等
|
||||
@@ -201,6 +205,22 @@ class HeadingRuleEngine:
|
||||
import copy
|
||||
self.rules = copy.deepcopy(DEFAULT_HEADING_RULES)
|
||||
|
||||
def _validate_level(self, level: int, text: str, rule_name=None):
|
||||
"""各级别标题长度防护:超长文本不应作为标题,降为正文。
|
||||
H1 > 40字, H2 > 60字, H3 > 50字 → 降为正文。
|
||||
统一覆盖 v1 常规匹配、v2 style 匹配、bold_short_text 兜底所有返回路径。"""
|
||||
text_len = len(text)
|
||||
if level == 1 and text_len > 40:
|
||||
logger.debug(f"标题识别: '{text[:30]}...' H1 但超长({text_len}字),降为正文")
|
||||
return 0, None
|
||||
if level == 2 and text_len > 60:
|
||||
logger.debug(f"标题识别: '{text[:30]}...' H2 但超长({text_len}字),降为正文")
|
||||
return 0, None
|
||||
if level == 3 and text_len > 50:
|
||||
logger.debug(f"标题识别: '{text[:30]}...' H3 但超长({text_len}字),降为正文")
|
||||
return 0, None
|
||||
return level, rule_name
|
||||
|
||||
def detect(self, text: str, style: Optional[List[str]] = None) -> Tuple[int, Optional[str]]:
|
||||
"""
|
||||
检测文本的标题级别
|
||||
@@ -227,7 +247,7 @@ class HeadingRuleEngine:
|
||||
level = rule.match(text)
|
||||
if level > 0:
|
||||
logger.debug(f"标题识别(v2 style): '{text[:30]}' -> h{level} (规则: {rule.name})")
|
||||
return level, rule.name
|
||||
return self._validate_level(level, text, rule.name)
|
||||
|
||||
# 再检查是否匹配中文章节/条款等高优先级规则
|
||||
for rule in self.rules:
|
||||
@@ -236,9 +256,16 @@ class HeadingRuleEngine:
|
||||
level = rule.match(text)
|
||||
if level > 0:
|
||||
logger.debug(f"标题识别(v2 style): '{text[:30]}' -> h{level} (规则: {rule.name})")
|
||||
return level, rule.name
|
||||
return self._validate_level(level, text, rule.name)
|
||||
|
||||
# 最后兜底:加粗短文本 → h2
|
||||
# 但需先检查所有规则的 exclude_pattern,防止编号列表项被误判为标题
|
||||
# 例如 "3.完全满足品规。指..." 虽有 bold 样式,但属于列表项而非标题
|
||||
for rule in self.rules:
|
||||
if rule.enabled and rule.exclude_pattern and rule.exclude_pattern.search(text):
|
||||
logger.debug(f"标题识别(v2 style): '{text[:30]}' 被 {rule.name} 的 exclude_pattern 排除")
|
||||
return 0, None
|
||||
|
||||
# 否则作为加粗短文本 → h2(与 bold_short_text 规则对齐,但不依赖 **...** 标记)
|
||||
logger.debug(f"标题识别(v2 style): '{text[:30]}' -> h2 (规则: bold_short_text_via_style)")
|
||||
return 2, 'bold_short_text'
|
||||
|
||||
@@ -247,7 +274,7 @@ class HeadingRuleEngine:
|
||||
level = rule.match(text)
|
||||
if level > 0:
|
||||
logger.debug(f"标题识别: '{text[:30]}' -> h{level} (规则: {rule.name})")
|
||||
return level, rule.name
|
||||
return self._validate_level(level, text, rule.name)
|
||||
|
||||
return 0, None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user