1. 项目概述
最近在研究如何将传统药典知识库与现代AI技术结合,尝试用RAG(检索增强生成)技术构建了一个中药材知识检索系统。这个项目以《2020年药典一部》为数据源,通过语义检索方式实现了对药材信息的精准查询。相比传统的关键词匹配,这套系统能理解用户提问的深层含义,找到最相关的药典内容片段,再让大模型生成专业回答。
选择这个方向有几个考虑:首先,药典内容结构规范但专业性强,普通用户难以快速定位所需信息;其次,药材查询常涉及同义词和复杂表述(如"性状"可能被问为"长什么样"),需要语义理解能力;最后,现有搜索引擎在垂直领域效果有限,而RAG正好能弥补这一缺口。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与核心组件
2.1 RAG架构解析
RAG(Retrieval-Augmented Generation)的核心思想是将检索(Retrieval)与生成(Generation)结合。在本项目中,工作流程分为三个阶段:
- 数据预处理:将药典文档解析为结构化数据
- 检索阶段:通过向量相似度找到相关内容
- 生成阶段:基于检索结果生成最终回答
这种架构的优势在于:
- 知识更新只需修改文档,无需重新训练模型
- 生成内容严格受限于检索结果,避免大模型幻觉
- 特别适合专业性强、准确性要求高的领域
2.2 关键组件选型
2.2.1 向量模型:all-MiniLM-L6-v2
选择这个轻量级模型(仅80MB)基于以下考量:
- 多语言支持:能较好处理中文文本
- 性能平衡:在MTEB基准测试中表现优异,且推理速度快
- 维度适中:384维向量在精度和效率间取得平衡
实际测试中,该模型对药材专业术语的语义捕捉相当准确。例如"性状"、"显微特征"等专业表述都能正确映射到相近向量空间。
2.2.2 向量数据库:Qdrant
相比其他方案,Qdrant的优势在于:
- 高效的相似度搜索:支持余弦相似度、欧式距离等多种度量方式
- 丰富的过滤条件:可结合元数据(如药材名称、章节类型)进行混合检索
- 易用的API:提供Python客户端和REST接口
- 轻量部署:单机模式下用Docker即可快速启动
3. 实现细节与核心代码
3.1 数据预处理流程
药典文档为Word格式,结构如下:
code复制【药材名称】
【处方】...
【性状】...
【鉴别】...
解析时需注意:
- 标题通过字体大小(12pt)识别
- 章节通过【】标记划分
- 内容可能跨越多段落
关键代码实现:
python复制def read_docx(file_path):
doc = docx.Document(file_path)
content_dict = {}
current_section = []
for para in doc.paragraphs:
if is_title(para): # 判断是否为标题
if current_section:
content_dict[clean_title(current_section[0])] = current_section
current_section = []
current_section.append(para.text)
return content_dict
3.2 向量化与存储
数据存入Qdrant前需要:
- 将文本分块(本项目中按药材+章节划分)
- 生成向量表示
- 添加元数据便于过滤
核心代码:
python复制# 初始化模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 生成向量
def create_vector(text):
return model.encode(text).tolist()
# 构建存储结构
points = []
for herb, sections in content_dict.items():
for section in sections:
vector = create_vector(f"{herb} {section['type']} {section['content']}")
points.append(PointStruct(
id=generate_id(),
vector=vector,
payload={
"herb": herb,
"section_type": section['type'],
"content": section['content']
}
))
# 批量插入
client.upsert(collection_name="herbs", points=points)
3.3 检索优化技巧
为提高检索准确率,我们实现了:
- 问题分类:用LLM解析用户意图
python复制def classify_question(question):
prompt = """将问题分类为以下类型:
- 性状查询
- 鉴别方法
- 含量测定
- 功能主治
- 其他"""
# 调用LLM处理...
return category
- 混合检索:结合语义向量和元数据过滤
python复制results = client.search(
collection_name="herbs",
query_vector=vector,
query_filter=Filter(
must=[
FieldCondition(key="section_type", match=category)
]
),
limit=3
)
4. 系统效果与优化方向
4.1 典型查询示例
用户提问:"当归中阿魏酸的含量测定方法是什么?"
系统处理流程:
- 分类器识别为"含量测定"类问题
- 在"当归"条目下检索相关章节
- 找到HPLC测定方法的详细描述
- 生成结构化回答:
code复制当归中阿魏酸含量测定采用高效液相色谱法:
1. 色谱条件:C18柱,流动相为甲醇-0.1%磷酸溶液...
2. 对照品制备:精密称取阿魏酸对照品...
3. 供试品制备:取当归粉末约0.5g...
4. 含量限度:本品含阿魏酸不得少于0.050%
4.2 性能优化建议
-
预处理优化:
- 对长章节进行更细粒度分块(如按检测项目拆分)
- 添加同义词扩展(如"HPLC"↔"高效液相色谱")
-
检索优化:
- 实现多级缓存(查询缓存、结果缓存)
- 对高频查询建立倒排索引加速检索
-
生成优化:
- 设计更精细的prompt模板
- 添加引用溯源功能(标明答案出处)
5. 实践中的经验教训
5.1 遇到的典型问题
-
文档解析问题:
- Word中隐藏格式导致章节识别错误
- 解决方案:添加字体大小、样式等多特征判断
-
向量相似度偏差:
- 专业术语与日常用语距离过远
- 改进:在领域文本上对模型进行微调
-
LLM约束问题:
- 回答有时会超出检索内容范围
- 解决方法:在prompt中添加严格约束条件
5.2 推荐的工具链组合
经过实践验证的稳定组合:
- 文档解析:python-docx + 自定义规则
- 向量模型:all-MiniLM-L6-v2(轻量)或bge-small(更准)
- 向量数据库:Qdrant(推荐)或Chroma(更轻量)
- LLM:Qwen-7B(中文优)或Llama3-8B(通用性强)
对于想要尝试类似项目的开发者,建议先从单一药材的小规模测试开始,逐步扩展。特别注意数据清洗环节,这是影响最终效果的关键因素。
