为什么 RAG 搜不到正确答案?Query Rewriting 的 3 种策略

24 阅读4分钟

「CNC 机器震动得厉害怎么回事?」——你问 A,知识库写的是 B。Query Rewriting 就是在检索前把 A 翻译成 B。


问题不在检索器,在问题本身

RAG 检索的输入是用户的原始提问。但用户不按知识库的术语说话:

用户的问法知识库的写法
「CNC 机器震动得厉害」「电机振动超标(>4.5mm/s)」
「轴承那块太热了」「轴承温度超过70°C时」
「传送带老是跑歪」「传送带跑偏调整方法」

同一个意思,两种表达。Embedding 模型不够智能到能跨术语做精确匹配——这是 Query Rewriting 要解决的问题。


策略 1:HyDE — 用假设答案去搜

HyDE(Hypothetical Document Embeddings)的思路很巧妙:

不要拿问题去搜文档,拿「假设的答案」去搜文档。

为什么有效? 假设答案和真实文档的用词风格更接近。你问「CNC 机器震动得厉害」,LLM 生成的假设答案是「振动值 4.8mm/s,超出正常范围,可能原因是轴承磨损或转子不平衡」——这段话的用词和知识库里的维修手册几乎一样。

// QueryRewriter 中的 HyDE 实现
String hydePrompt = """
    你是工业设备专家。请用维修手册的风格简单回答以下问题。
    问题:%s
    假设答案:""".formatted(query);

String hypothesis = chatModel.generate(hydePrompt);
// 用 hypothesis 做检索,而不是原始 query

代价:每次检索多一次 LLM 调用。但这次调用很短(只需生成 2-3 句话),延迟增加约 1-2 秒。

适用场景:专业术语密集的知识库(工业维修、医疗、法律)。


策略 2:Multi-Query — 多角度围攻

从一个问题生成 3 个不同角度的问题,分别检索,结果去重合并:

原始问题:「CNC-001 振动异常怎么排查?」

→ Query 1:「CNC机床振动超标的故障诊断方法」
→ Query 2:「机械设备振动异常的常见原因和排查步骤」
→ Query 3:「数控机床主轴振动测试和检修流程」
List<String> queries = queryRewriter.multiQueryRewrite(query);
// 每个 query 分别检索,结果合并去重
for (String q : queries) {
    results.addAll(embeddingStore.search(q));
}

3 个查询中只要有一个命中,检索就成功。 相比单一查询,命中率通常能提升 10-15%。

适用场景:复杂问题、开放式问题、用户描述不精确。


策略 3:Step-Back — 退一步看全局

有时用户的问题太具体了,检索不到精确答案。但退一步问一个更通用的问题,反而能找到相关背景知识。

原始问题:「CNC-001 主轴转速 12000rpm 时振动 4.8mm/s 什么原因?」
    ↓ Step-Back
通用问题:「高速主轴振动超标的一般原因和排查方法」
String stepBack = queryRewriter.stepBackRewrite(query);
// 用更通用的背景问题检索

适用场景:具体设备 + 具体数值的问题;知识库覆盖了通用原理但没覆盖具体型号。


三种策略怎么选?

策略额外开销最佳场景
HyDE+1 次 LLM 调用(短)术语密集、文档风格统一
Multi-Query+1 次 LLM 调用(中)问题复杂、多角度
Step-Back+1 次 LLM 调用(短)问题太具体、知识库偏原理

三种都加一次 LLM 调用——所以不建议全用。按场景选一个。

industrial-agent-long 这个项目,我先上了 HyDE——工业维修知识的术语密度高,假设答案和真实文档的用词重合度大,效果最好。


Query Rewriting 的 API 端点

# HyDE
curl -X POST http://localhost:8080/api/rag/rewrite/hyde \
  -H "Content-Type: application/json" \
  -d '{"query":"CNC轴承太热了怎么回事"}'

# Multi-Query
curl -X POST http://localhost:8080/api/rag/rewrite/multi-query \
  -H "Content-Type: application/json" \
  -d '{"query":"传送带老是跑偏"}'

# Step-Back
curl -X POST http://localhost:8080/api/rag/rewrite/step-back \
  -H "Content-Type: application/json" \
  -d '{"query":"CNC-001 主轴12000rpm时振动4.8"}'

一句话总结

Query Rewriting 不是在优化检索器,是在优化问题本身。让问题「像文档一样说话」,检索就自然能找到。不是检索器不够好,是你喂给它的 query 太「用户」了。


本文由 LaoLiang 原创,首发于掘金/知乎/微信公众号。转载请联系作者。