RAG检索结果不准如何解决?

RAG检索结果不准如何解决?

高级RAG优化手册:3招解决检索不准和查询模糊

1. 实际案例

用户问“布洛芬能不能和感冒药一起吃?”但回答不相关

最初系统返回的检索内容是:

  • 布洛芬的退烧作用
  • 感冒的症状介绍
  • 药品剂量说明

检索不准
没有召回“药物相互作用”相关内容,导致大模型回答很泛,甚至出现模糊建议。

2. 排查过程

① 文档切分方式

发现说明书被固定长度切分,导致:

  • 药物相互作用”段落被拆散
  • 语义完整性丢失
  • 向量表达不准确

② 查询语义问题

用户说“感冒药”,但知识库中是:

  • 复方氨酚烷胺
  • 对乙酰氨基酚
  • 抗组胺类

口语表达与医学术语不一致

③ 检索排序问题

只做了向量相似度排序,没有:

  • 关键词过滤
  • 领域优先级控制

导致高频“药品介绍”被优先召回。

3. 解决方案

1️⃣ 语义切分优化

  • 说明书结构切分(适应症 / 用法 / 相互作用)
  • 保证每个 chunk 语义完整

提高向量表达质量

2️⃣ 查询增强(Query Rewrite)

对用户问题进行结构化增强,例如:

原问题:

布洛芬能不能和感冒药一起吃

增强后检索 query:

布洛芬 药物相互作用 联合用药 禁忌

👉 让检索更偏医学语义

3️⃣ 检索结果过滤与重排

  • 设定领域优先级:相互作用 > 功效介绍
  • 向量召回 + 关键词匹配双重筛选
  • 限制 topK 并设置相关度阈值

👉 降低无关内容进入上下文

4. 优化后:

  • 能稳定召回“联合用药风险”相关内容
  • 医疗问答准确性明显提升
  • 幻觉回答明显减少

5. 具体解决方案

医疗说明书结构切分

正确切分方式应该是:

chunk 内容 metadata
1 适应症段落 type=indication
2 用法用量 type=dose
3 不良反应 type=adverse
4 相互作用 type=interaction

自定义结构切分器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MedicalDocumentSplitter implements TextSplitter {

@Override
public List<Document> split(Document document) {
String content = document.getContent();
List<Document> result = new ArrayList<>();

// 按结构标题切分
Map<String, String> sections = parseMedicalSections(content);

for (Map.Entry<String, String> entry : sections.entrySet()) {
Map<String, Object> metadata = new HashMap<>();
metadata.put("sectionType", entry.getKey());

result.add(new Document(entry.getValue(), metadata));
}

return result;
}

private Map<String, String> parseMedicalSections(String text) {
Map<String, String> map = new HashMap<>();

map.put("indication", extract(text, "适应症"));
map.put("dose", extract(text, "用法用量"));
map.put("interaction", extract(text, "药物相互作用"));

return map;
}
}

👉 把一整篇医疗文档按医学结构拆开
👉 给每一块打上语义标签(metadata)
👉 输出适合做向量化的知识块

结合 TokenTextSplitter 做二次切分

有些段落仍然很长,可以:

1️⃣ 先结构切分
2️⃣ 再限制 token 长度

1
2
3
4
5
6
7
TokenTextSplitter tokenSplitter = new TokenTextSplitter(300, 50);

List<Document> finalDocs = new ArrayList<>();

for (Document doc : structuredDocs) {
finalDocs.addAll(tokenSplitter.split(doc));
}

利用metadata 参与检索

例如存储:

1
2
3
4
5
6
7
{
"content": "...避免与抗凝药同时使用...",
"metadata": {
"drug": "布洛芬",
"sectionType": "interaction"
}
}

查询时可以:

只检索 interaction 类别
✔ 提高联合用药问题召回率

Spring AI 查询示例思路:

1
2
3
SearchRequest request = SearchRequest.query(queryText)
.withFilterExpression("sectionType == 'interaction'")
.withTopK(5);

在 RAG 入库阶段,我没有采用默认的固定长度切分,而是根据医疗说明书的结构进行语义切分,比如适应症、用法用量和药物相互作用分别作为独立知识块,并为每个 chunk 添加结构化 metadata。
这样在向量检索时可以结合语义相似度和结构过滤,提高特定问题场景下的召回准确率。对于较长段落,我会再结合 token 切分控制上下文长度,然后统一写入 Milvus 向量库。