「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 原创,首发于掘金/知乎/微信公众号。转载请联系作者。