知识库里有:“企业差旅报销制度 V3.0.pdf”,里面详细规定了打车费用的上限。
用户搜:“打车钱”。
结果:搜不到,或者相关度极低。
原因在于:用户的查询和文档在语义空间中并不重叠。用户的输入是口语化的、短的;而文档是正式的、长的。
查询扩展的核心逻辑是:不要直接搜用户输入的词,而是搜“用户心里真正想找的内容”。
以下是三种最主流且高效的查询扩展技术方案:
一、 方案 A:LLM 多路改写
这是最直观的方法。利用大模型(LLM)将用户的短语扩充成多个不同角度的完整句子,然后并行搜索。
1. 原理
用户输入往往存在“词不达意”。通过 Prompt 让 LLM 充当“翻译官”,将一个模糊查询翻译成 3-5 个不同风格的精确查询。
2. 流程
用户输入:“连不上网”
LLM 改写指令:“请将上述问题改写为 3 个不同维度的搜索查询,包括技术术语、同义词变体。”
生成结果:
执行:将这 3 个句子分别做 Vector Search,然后对结果进行去重(Deduplication)和重排序(Rerank)。
3. 优势与劣势
优点:极大地提高了召回率(Recall),解决了因关键词不匹配导致的漏搜。
缺点:增加了延迟(Latency),因为要调用一次 LLM,且后续要进行多次向量检索。
二、 方案 B:HyDE(假设文档嵌入)—— 这一招非常强
HyDE是目前向量检索领域的“杀手锏”。它的思路是逆向的:不扩充问题,而是直接“伪造”答案。
1. 痛点
向量模型通常在计算“问题”和“答案”的相似度时表现一般,但在计算“答案”和“答案”的相似度时表现极好。
2. 原理
在检索之前,先让 LLM 针对用户的问题写一篇幻觉答案(Hypothetical Document)。这个答案内容可能是不准确的,但它的句式、语气、关键词与真实文档库里的内容是高度相似的。
3. 流程
用户输入:“如何退货?”
LLM 生成假设文档:“如果您需要退货,请登录个人中心,点击订单详情,选择申请售后。由于商品属于...(注意:这是LLM瞎编的,但包含了'个人中心'、'售后'等关键语义)”
编码(Encoding):将这段“瞎编的答案”转化为向量。
检索:用这个向量去数据库里搜。
结果:因为“瞎编答案”和“真实政策文档”在语义上非常接近,检索精准度大幅提升。
4. 适用场景
非常适合跨域检索或用户问题极其简短的场景。
三、 方案 C:思维链/子问题分解
针对复杂、多跳的模糊问题。
1. 场景
用户问:“对比一下 iPhone 15 和 华为 Mate 60 的屏幕参数。”
如果直接搜这句话,可能只能搜到一些泛泛而谈的新闻。
2. 原理
利用 LLM 将复杂问题拆解为多个独立的原子问题。
3. 流程
拆解:
并行检索:分别搜索这两个子问题,获取精准的数据片段。
汇总:将两边的检索结果喂给 LLM,生成最终的对比回答。
四、 实战中的技术选型策略
并不是所有场景都要上最复杂的 HyDE。以下是推荐的组合拳:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
五、 核心代码逻辑示例 (Python 伪代码)
如果你在使用 LangChain 或 LlamaIndex,逻辑大致如下:
def query_expansion_search(user_query):1. 调用 LLM 生成 3 个相关问题augmented_queries = llm.generate([f"将 '{user_query}' 改写为3个更专业、详细的搜索查询。" ])# 加上原始查询all_queries = [user_query] + augmented_queriessearch_results = []# 2. 并行执行向量检索for q in all_queries:vector = embedding_model.encode(q)results = vector_db.search(vector, k=5)search_results.extend(results)# 3. 结果去重 (基于文档ID)unique_results = deduplicate(search_results)# 4. (可选) 重排序 Rerank# 使用 Cross-Encoder 对最终结果精排final_results = reranker.rank(user_query, unique_results)return final_results
总结
用户查询太模糊,本质是“意图"和“语料”之间的鸿沟。
如果是词汇鸿沟(不知道专业术语),用LLM 改写。
如果是语义鸿沟(问题和答案长得不像),用HyDE。
不要指望用户学会“提示词工程”,查询扩展就是系统主动帮用户完成提示词优化的过程。


