PARSING_ANALYSIS.md 17 KB

中国药典 AI 解析系统 — 文字解析问题分析报告

版本: v1.0
日期: 2026-07-17
范围: data-pipeline/ 全部解析模块 + backend-python/app/rag/ 检索链路
主线版本: 2025 年版药典(主要解析目标)
补充版本: 2020 年版药典(历史数据兼容)


一、项目解析架构总览

数据源 解析层 入库层

药典 PDF (2020/2025) -> pharmacopoeia.py -> ingest.py 药典 DOCX (2025) -> docx_ingest.py -> docx_ingest.py (内置入库) 维基百科 -> wiki_spider.py -> import_from_openclaw.py NMPA 公开数据 -> nmpa_spider.py -> ingest.py 考试大纲 -> exam_outline.py -> knowledge_chunker.py

当前状态: 仅 DOCX 解析链路可用,PDF 解析器为空壳,考试模块未实现。


二、2025 版药典解析(主线)

2.1 DOCX 解析模块 — docx_ingest.py

文件位置: data-pipeline/docx_ingest.py
当前状态: 可用,但存在多处健壮性问题

2.1.1 解析流程

DOCX 文件 | 文件名 -> 药品名(display_name) | 段落遍历(doc.paragraphs) | Heading 1 -> 跳过(与文件名重复) 拼音行 -> 提取 pinyin 短行(<=10字符)-> section 标题匹配 【xxx】格式 -> 行内 section 标题 其他 -> 归入当前 section | 凡例/纲要 -> 按编号拆分(一、二、三...) | 输出结构化 dict

2.1.2 存在的问题

编号 问题 严重程度 说明
D-01 表格内容完全丢失 严重 只遍历 doc.paragraphs,未处理 doc.tables。药典中含量测定限度表、鉴别反应表等关键信息以表格形式存在
D-02 section 标题长度硬编码 中等 len(text) <= 10 阈值过小,较长标题会被漏掉
D-03 拼音识别可能误判 中等 is_pinyin_line() 可能将英文药品名或化学分子式误判为拼音
D-04 凡例/纲要拆分逻辑脆弱 中等 中文数字正则 {1,2} 无法匹配二十一等复合数字;模式优先级可能导致错误匹配
D-05 文件名依赖 轻微 药品名直接取文件名,不规范的文件名(含空格、编号前缀)会导致解析错误
D-06 旧版去重逻辑风险 中等 dedup_old_versions() 将所有非 2025 版同名药品标记为 inactive,可能误删 2020 版独有的药品信息

2.1.3 section 标题映射表

SECTION_HEADERS = OrderedDict([

(处方, 处方), (制法, 制法), (性状, 性状),
(鉴别, 鉴别), (检查, 检查), (浸出物, 浸出物),
(含量测定, 含量测定), (含量, 含量测定),
(功能与主治, 功能主治), (功能, 功能主治), (主治, 功能主治),
(用法与用量, 用法用量), (用法, 用法用量), (用量, 用法用量),
(注意, 注意事项), (注意事项, 注意事项),
(规格, 规格), (贮藏, 贮藏), (类别, 类别),
(制剂, 制剂), (附注, 附注),

])

缺失的映射:

  • 包装(部分条目)
  • 有效期(部分条目)
  • 核准日期(部分条目)
  • 修订日期(部分条目)

2.1.4 2025 版四部目录结构

根据 extract_catalog.py 提取的目录:

中国药典 2025 年版 四部 |-- 凡例 |-- 通则 | |-- 制剂通则 | |-- 通用检测方法 | +-- 指导原则 |-- 药用辅料 +-- 通用技术要求


2.2 PDF 解析模块 — pharmacopoeia.py

文件位置: data-pipeline/crawlers/pharmacopoeia.py
当前状态: 完全未实现

2.2.1 设计目标(注释中描述)

  1. PyMuPDF 提取文本 + 坐标
  2. 基于字体大小/位置识别标题层级
  3. 正则匹配药品条目边界
  4. 按条目切分 -> 结构化输出

2.2.2 未实现的方法

方法 预期功能 当前返回
parse() 完整解析流程 []
_extract_text_with_position() 提取文本块及坐标 []
_detect_entry_boundaries() 检测条目边界 []
_parse_entry_sections() 解析各栏目 空字典

2.2.3 2020 版 PDF 解析的特殊挑战

挑战 说明
排版差异 2020 版 PDF 与 2025 版排版规则不同,字体大小、缩进量需要分别适配
双栏布局 部分页面采用双栏布局,文本提取时可能出现跨栏混排
化学结构式 含量测定等章节包含化学结构式图片,纯文本提取会丢失
页码不连续 前言、目录、凡例的页码与正文不连续,需要分别处理

2.3 PDF 目录提取 — extract_catalog.py

文件位置: data-pipeline/crawlers/extract_catalog.py
当前状态: 部分可用

2.3.1 存在的问题

编号 问题 说明
C-01 层级判断靠坐标硬编码 x < 50 为部级、40 <= x < 100 为章级、x >= 80 为节级,不同 PDF 版本排版不同时会失效
C-02 层级范围重叠 章级 40-100 与节级 >=80 重叠,x=90 同时满足两个条件
C-03 目录页定位不稳定 依赖目录关键词匹配,如果目录页格式不同会定位错误

三、2020 版药典解析(补充)

3.1 数据来源

来源 格式 状态
data/pharmacopoeia_2020_volume1_toc.pdf PDF 无法解析(解析器未实现)
data-pipeline/data/catalog_volume1.json JSON 已提取目录结构
data-pipeline/data/catalog_volume2.json JSON 已提取目录结构
data-pipeline/data/catalog_volume3.json JSON 已提取目录结构
data-pipeline/data/catalog_volume4.json JSON 已提取目录结构

3.2 2020 版与 2025 版的差异

差异项 2020 版 2025 版
主要数据格式 PDF DOCX
药品品种数量 约 5911 种 新增 + 修订
凡例内容 2020 版凡例 2025 版凡例(有更新)
通则编号 旧编号体系 可能有调整
检测方法 2020 版方法 新增/修订方法

3.3 2020 版数据去重策略

当前 docx_ingest.py 中的去重逻辑:

UPDATE drugs SET is_active = FALSE WHERE is_active = TRUE AND source_version != 2025年版 AND name IN (

  SELECT name FROM drugs
  WHERE source_version = 2025年版 AND is_active = TRUE

)

问题:

  • 同名药品在 2020 版和 2025 版可能有不同的检查标准,直接标记旧版为 inactive 会丢失历史标准信息
  • 建议改为版本共存模式,查询时优先返回 2025 版,但保留 2020 版可查

四、文本清洗模块 — cleaner.py

文件位置: data-pipeline/processors/cleaner.py
当前状态: 功能薄弱

4.1 清洗规则冲突

self.replacements = [

(r(?<!\n)\n(?!\n), ),           # 规则1: 移除单换行
(r\x00, ),                         # 规则2: 移除空字符
(r([a-z])\n([a-z]), r\1\2),     # 规则3: 英文断行连接
(r(\d)\n(\d), r\1\2),         # 规则4: 数字断行连接
(r\n{3,}, \n\n),                # 规则5: 多换行压缩
(r\t,  ),                         # 规则6: 制表符替换
(r {2,},  ),                       # 规则7: 多空格压缩

]

冲突分析:

  • 规则 1 先执行 -> 所有单换行被删除 -> 规则 3、4 永远无法匹配
  • 正确顺序应该是:先处理断行连接(规则 3、4),再移除不当换行(规则 1)

4.2 OCR 纠错字典不足

ocr_fixes = {

鍜?: 鍟?,
鍞?: 鍞?,
鑼剁⒈: 鑼剁⒈,

}

问题:

  • 仅 3 条规则,远远不够覆盖药典 PDF 的 OCR 错误
  • 部分映射本身看起来是编码问题产生的乱码
  • 缺少药学专业术语的纠错(如苷与甙、馏与溜等)

4.3 缺失的清洗能力

缺失项 说明
全角/半角规范化 中文逗号 vs 英文逗号、中文句号 vs 英文句号、中文括号 vs 英文括号
化学分子式处理 上下标丢失问题(如 H2O 应为 H 下标 2 O)
中药拉丁名规范化 拉丁学名斜体标记丢失
特殊符号处理 摄氏度、微克、毫升等单位符号的规范化
段落合并逻辑 跨页段落的智能合并

五、智能切片模块 — chunker.py

文件位置: data-pipeline/processors/chunker.py
当前状态: 部分实现

5.1 已实现:药品条目切片

def chunk_drug_entry(self, drug_entry: dict) -> list[Chunk]:

# 按 section 切分,保持每个 section 完整
# 若单个 section 过长,再按段落细分

问题:

  • chunk_overlap 参数未使用,切片之间没有重叠
  • 长 section 切分后,最后一段可能低于 min_chunk_size 但仍被加入

5.2 未实现:凡例/通则切片

def chunk_regulation(self, text: str, metadata: dict) -> list[Chunk]:

return []  # 空实现

5.3 未实现:考试知识点切片

def chunk_exam_knowledge(self, knowledge_point: dict) -> list[Chunk]:

return []  # 空实现

5.4 切片内容格式问题

content=f銆恵drug_name} - {section_key}銆慭n{section_text}

  • 銆恵 和 銆慭n 是编码乱码(应为 【 和 】换行)
  • 说明 f-string 中的中文括号在某个环节被损坏

六、考试知识点切片 — knowledge_chunker.py

文件位置: data-pipeline/processors/knowledge_chunker.py
当前状态: 完全未实现

class KnowledgeChunker:

def process(self, knowledge_point: dict) -> list[dict]:
    return []

def build_chapter_tree(self, knowledge_points: list[dict]) -> dict:
    return {}

影响: 考试模块的数据管道完全断裂,无法支持:

  • 章节化知识点学习
  • AI 自动出题
  • 刷题/错题集/模拟考试

七、向量检索模块 — retriever.py

文件位置: backend-python/app/rag/retriever.py
当前状态: 部分实现

7.1 意图分类问题

def classify_intent(query: str) -> str:

# 基于关键词匹配的简单分类
编号 问题 说明
I-01 关键词冲突 过敏同时出现在 safety_sections 和 symptom_keywords 中,优先级判断导致错误分类
I-02 无否定语义处理 这不是感冒会匹配到感冒关键词
I-03 无置信度判断 所有查询都被强制分到某个类别,没有不确定的兜底
I-04 无上下文理解 无法理解多轮对话中的指代关系

7.2 BM25 关键词检索未实现

注释写着混合检索器:pgvector 向量检索 + BM25 关键词检索,但实际只有向量检索。

影响:

  • 精确药品名查询(如阿莫西林)效果不如精确匹配
  • 专业术语查询(如HPLC)向量检索可能不够精准

7.3 数据库连接管理

engine = create_async_engine(DB_URL) try:

# ...

finally:

await engine.dispose()

每次查询都创建新的 engine 并销毁,应该使用连接池复用。


八、Reranker 模块 — reranker.py

文件位置: backend-python/app/rag/reranker.py
当前状态: 形同虚设

class Reranker:

def _load_model(self):
    pass  # 空实现

def rerank(self, query, documents, top_k=5):
    sorted_docs = sorted(documents, key=lambda d: d.get(score, 0), reverse=True)
    return sorted_docs[:top_k]  # 只是按原始分数排序

设计目标: BGE-Reranker-v2-m3 Cross-Encoder 精排
实际效果: 仅按向量相似度分数排序取 Top-K,无精排能力


九、Embedder 模块 — embedder.py

文件位置: data-pipeline/processors/embedder.py
当前状态: 本地模型未实现

def encode(self, texts: list[str], batch_size: int = 32) -> list[list[float]]:

return [[0.0] * settings.embedding_dim] * len(texts)  # 返回全零向量

实际情况: 入库时走 DashScope API(ingest.py 中),本地 BGE-M3 模型未加载。


十、Wiki 爬虫解析 — wiki_spider.py

文件位置: data-pipeline/crawlers/wiki_spider.py
当前状态: 部分可用

10.1 内容截断问题

sections[matched] = .join(current_text)[:3000]

每个 section 最多 3000 字符,超出部分直接截断,可能丢失关键的不良反应、禁忌等信息。

10.2 分类推断过于简单

def infer_category(name, sections):

if any(k in name for k in [草, 花, 叶, 根, 皮, 中, 散, 汤, 丸]):
    return 中药

仅靠药名中是否含特定汉字来判断,误判率高(如花旗参匹配花)。


十一、数据 Schema 定义

11.1 药品条目 Schema

{ drug_id: string, name: string, name_en: string, name_pinyin: string, category: 化学药 | 中药 | 生物制品 | 辅料, subcategory: string, sections: {

性状: string,
鉴别: string,
检查: string,
含量测定: string,
类别: string,
贮藏: string,
制剂: string,
用法与用量: string,
禁忌: string,
不良反应: string,
注意事项: string

}, source: {

version: 2020年版 | 2025年版,
volume: 一部 | 二部 | 三部 | 四部,
page: string

} }

11.2 知识点 Schema

{ point_id: KP-{subject}-{chapter}-{seq}, subject: yao1 | yao2 | fagui | zonghe, chapter_id: string, title: string, content: string, difficulty: 1-5, frequency: 高频 | 中频 | 低频 | 未考, related_drugs: [drug_id], key_points: [string] }

11.3 题目 Schema

{ question_id: Q{yyyy}{mm}{seq}, question_type: A | B | X, subject: yao1 | yao2 | fagui | zonghe, content: string, options: [A. xxx, B. xxx, C. xxx, D. xxx], answer: A | AB | ABC, explanation: string, knowledge_point_ids: [point_id], frequency: 高频 | 中频 | 低频 | 未考 }


十二、问题汇总与优先级

12.1 致命问题(阻塞核心功能)

编号 问题 模块 影响
F-01 PDF 解析器完全未实现 pharmacopoeia.py 2020 版药典 PDF 无法解析
F-02 考试知识点切片为空 knowledge_chunker.py 考试模块数据管道断裂
F-03 Reranker 未加载模型 reranker.py 检索结果无精排能力
F-04 BM25 关键词检索未实现 retriever.py 精确查询效果差

12.2 严重问题(影响数据质量)

编号 问题 模块 影响
S-01 DOCX 解析不处理表格 docx_ingest.py 含量测定限度表等丢失
S-02 文本清洗规则冲突 cleaner.py 英文/数字断行连接失效
S-03 OCR 纠错字典不足 cleaner.py PDF 提取文本错误多
S-04 切片无 overlap chunker.py 上下文断裂
S-05 凡例/通则切片为空 chunker.py 通则类内容无法检索

12.3 中等问题(影响准确性)

编号 问题 模块 影响
M-01 意图分类关键词冲突 retriever.py 用户意图误判
M-02 section 标题长度硬编码 docx_ingest.py 部分标题漏识别
M-03 凡例/纲要拆分逻辑脆弱 docx_ingest.py 编号拆分错误
M-04 PDF 目录层级坐标硬编码 extract_catalog.py 不同 PDF 版本失效
M-05 旧版去重可能误删 docx_ingest.py 2020 版独有信息丢失

12.4 轻微问题(影响体验)

编号 问题 模块 影响
L-01 Wiki 内容截断 wiki_spider.py 长内容不完整
L-02 Wiki 分类推断粗糙 wiki_spider.py 分类误判
L-03 文件名依赖 docx_ingest.py 不规范文件名出错
L-04 数据库连接不复用 retriever.py 性能浪费

十三、改进建议

13.1 短期(Phase 2 内)

  1. 实现 PDF 解析器

    • 使用 PyMuPDF 提取文本 + 坐标
    • 基于字体大小识别标题层级
    • 针对 2020 版和 2025 版分别适配
  2. 修复文本清洗规则顺序

    正确顺序

    self.replacements = [ (r([a-z])\n([a-z]), r\1\2), # 先处理断行 (r(\d)\n(\d), r\1\2), (r(?<!\n)\n(?!\n), ), # 再移除不当换行 ... ]

  3. 实现表格解析

    • 遍历 doc.tables 提取表格内容
    • 将表格转换为结构化文本或 JSON
  4. 实现 Reranker 模型加载

    • 加载 BGE-Reranker-v2-m3
    • 实现 Cross-Encoder 精排

13.2 中期(Phase 3)

  1. 实现 BM25 混合检索

    • 使用 pg_trgm 或 Elasticsearch
    • 向量检索 + 关键词检索融合排序
  2. 优化意图分类

    • 使用小模型(如 BERT)进行意图分类
    • 解决关键词冲突问题
  3. 实现考试知识点切片

    • 按大纲章节组织知识点
    • 生成可向量化的 chunk

13.3 长期(Phase 4+)

  1. 多版本数据共存

    • 2020 版和 2025 版数据共存
    • 查询时优先返回最新版,但可切换版本
  2. 增量更新机制

    • 药典更新时自动检测变更
    • 增量解析和入库
  3. 质量评估体系

    • 解析准确率评测
    • 检索召回率评测
    • AI 回答准确率评测(目标 >95%)

十四、附录

14.1 药典版本对照表

版本 部数 主要内容 数据格式
2020 年版 四部 中药、化学药、生物制品、通则 PDF
2025 年版 四部 新增 + 修订品种 DOCX

14.2 考试科目代码

代码 科目名称
yao1 药学专业知识(一)
yao2 药学专业知识(二)
fagui 药事管理与法规
zonghe 药学综合知识与技能

14.3 题型说明

题型 说明
A 型题 单项选择题
B 型题 配伍选择题
X 型题 多项选择题

文档结束