基于LLM的查询重写和HyDE

本文详细介绍了两个RAG改进——基于LLM的查询重写和HyDE——以优化RAG的检索召回。

基于LLM的查询重写和HyDE

在本系列的第4部分《从零开始构建RAG CLI聊天机器人》,我构建了一个功能性的CLI RAG聊天机器人,可以搜索文档并提供上下文相关的答案。虽然聊天机器人表现良好,但我从最初的规划阶段就知道,一个关键的改进领域是使用一个单一的多功能提示,同时用于向量数据库搜索和LLM的响应生成。我当时的主要目标是快速建立一个可工作的系统,并将此优化留到以后进行。

本文详细介绍了两个渐进的改进——基于LLM的查询重写和HyDE——以解决这一限制。对于那些喜欢调整和优化AI系统的读者来说,这篇文章提供了增强我们的RAG聊天机器人的实用下一步。

1、单一用途提示的问题

让我们回顾一下第4部分中聊天机器人的一个典型用户输入:

“@knowledgebase search on vibe coding, then summarize, list pros and cons”

发送到向量数据库和LLM的提示:

  • 向量搜索查询: "search on vibe coding, then summarize, list pros and cons"
  • LLM生成提示(系统提示 + 上下文 + 清理后的用户输入):
根据以下知识库中的上下文,请回答以下问题:

上下文:
{context}
问题:search on vibe coding, then summarize, list pros and cons

请基于上述上下文提供有帮助的答案。
如果上下文不足以回答问题,请明确说明。

1.1 为什么这很重要

这种方法造成了根本性的低效率并损害了准确性。我意识到,向量数据库的优势在于其能够根据核心概念的语义相似性查找文档,而不是处理指令。当我的搜索查询包含“search”、“summarize”或“list pros and cons”这样的动作导向术语时,它引入了噪音,使焦点偏离了实际主题——“vibe coding”。因此,数据库检索到的文档与用户的实际信息需求不太精确,因为它试图匹配整个复杂的指令。

同样,大型语言模型(LLM)在清晰、明确的指令下表现最佳。当LLM接收到原始用户查询时,它被要求“search”一个已经作为上下文提供的知识库。在RAG系统中,LLM的真正作用是从给定的信息中综合并生成响应。迫使它从上下文中解释用户的原始查询是一个不必要的且容易出错的步骤。这可能导致更不准确的摘要、潜在的误解以及模型生成的响应可能没有完全基于提供的文档的风险。

本质上,这种方法混淆了检索(向量数据库的任务)和生成(LLM的任务)的不同职责。一个理想的RAG系统会将这两个方面分开,为每个组件提供专门针对其功能的查询或提示。这种分离是构建更高效、准确和强大的聊天机器人的关键。

2、解决方案:查询重写器

本节的代码可以通过切换到项目仓库的 0.0.2 标签来访问。

git checkout 0.0.2

2.1 转换过程

解决方案是引入一个 QueryRewriter,它进行一次额外的LLM调用,以将用户意图分为两个优化后的提示。这个新的两步过程允许系统向向量数据库发送干净、专注的查询,并向最终的LLM发送清晰的指令提示。

带有查询重写器的序列图

2.2 LLM文本分析的力量

我发现使用LLM进行意图分离非常有效。传统的NLP技术在解析复杂的用户请求时存在困难,但通过适当的提示,LLM可以:

  • 从冗长的指令中提取核心主题
  • 一致地生成结构化的JSON输出
  • 处理对话上下文和后续问题
  • 区分要搜索的内容和对结果的处理方式

注意: 在本部分中,我改用 gemini-1.5-flash 模型进行LLM调用,因为发现Llama 3.2在生成结构化JSON输出时并不总是能遵循指令。

3、重写器的工作原理

QueryRewriter 向LLM发送特定的系统提示。然后将 llm_query 结果添加到系统提示中,再发送到最终的LLM。这大大提高了结果,因为LLM的生成任务现在变得干净而直接。

重写器的系统提示:

你必须以包含以下字段的JSON对象进行回复:
- "search_rag": boolean - 如果查询包含 "@knowledgebase" 则为 true
- "embedding_source_text": string - 仅包含核心主题关键词,忽略指令词
- "llm_query": string - 适用于最终LLM的清晰指令,并包含适当的上下文引用

输入: "@knowledgebase search on vibe coding, then summarize, list pros and cons"

LLM输出:

{
  "search_rag": true,
  "embedding_source_text": "vibe coding programming approach",
  "llm_query": "Based on the provided context, explain what vibe coding is, including its pros and cons, and cite sources."
}

这种分离使得向量搜索可以专注于寻找关于“vibe coding”的文档,同时给LLM清晰的指令来处理这些文档。

4、进一步优化:HyDE策略

本节的代码可以通过切换到项目仓库的 0.0.3 标签来访问。

git checkout 0.0.3

4.1 什么是HyDE?

HyDE(假设文档嵌入)进一步优化了查询,利用生成型LLM来改善检索。不同于仅仅从用户的问题中提取关键词,HyDE生成一个“假设文档”——即对一个好答案可能是什么样子的猜测。这个假设答案随后用于向量搜索。

传统方法: 搜索包含“vibe coding”的文档

HyDE方法: 搜索与“Vibe coding is a programming approach that emphasizes writing code based on intuition and flow state…”相似的文档

4.2 为什么HyDE能提高匹配度

假设文档嵌入捕捉了全面答案应包含的语义含义,而不仅仅是查询本身的关键词匹配。这种方法可以找到概念上相关的文档,这些文档可能不包含确切的搜索词,但讨论相同的概念。LLM生成一个合理、上下文丰富的文档,弥合查询和知识库之间的差距,从而实现更相关的结果,特别是对于模糊或域外查询。

4.3 实现优势

现有的 QueryRewriter 架构已经非常适合 HyDE。相同的 JSON 结构和集成点完美工作——只需更新系统提示即可:

系统提示更改:

- "embedding_source_text": string - 仅包含核心主题关键词,忽略指令词  
+ "embedding_source_text": string - 一个聚焦的2-4句假设文档,回答核心主题

4.3 HyDE的实际应用

系统现在使用LLM生成一个假设答案,然后用于向量搜索。这导致了更好的文档检索。

用户: "@knowledgebase search on vibe coding, then summarize, list pros and cons"

LLM输出(HyDE策略):

{
  "search_rag": true,
  "embedding_source_text": "Vibe coding is a programming approach that emphasizes writing code based on intuition, flow state, and personal rhythm rather than strict methodologies. This coding style prioritizes developer comfort, creativity, and maintaining a natural coding rhythm. Practitioners focus on writing code that feels right and maintains consistent energy levels during development sessions.",
  "llm_query": "Based on the provided context, explain what vibe coding is, including its pros and cons, and cite sources."
}

现在系统搜索与这个假设解释语义相似的文档,可能会找到比简单的关键词方法更相关的内容。

注意: 你可以看到,即使使用相同的用户输入,llm_query 也与之前的输出不同。这是因为我对提示进行了进一步的调整,以更好地定制LLM生成的指令。虽然我的博客文章提供了“vibe coding”的上下文,但成功的HyDE实施需要LLM有一些先验知识来生成高质量的假设答案。对于这个特定的例子,像Gemini或其他具有最新数据的模型训练的强大的、最新的LLM是必不可少的,因为像Llama 3.2这样的模型可能没有关于较新术语“vibe coding”的知识,无法生成有用的假设文档。

5、初步的检索分数观察

虽然真正的科学基准需要更大、更多样化的数据集,但我们可以通过我们单个测试案例的检索分数来看一些有趣的初步结果。

让我们比较“vibe coding”查询在三个版本中的前五个检索分数:

  • 0.0.1(基础版): 用于两种操作的单一提示
  • 0.0.2(重写器): 将搜索关键词与生成指令分开
  • 0.0.3(HyDE): 使用假设文档进行语义匹配
0.0.1(基础)返回的文档及相应分数
0.0.2(重写器)返回的文档及相应分数
0.0.3(HyDE)返回的文档及相应分数

你会注意到 Rewrite 策略(0.0.2)的分数与 Basic 方法(0.0.1)的分数惊人地相似,尽管在这个特定情况下略有下降。这可能是由于嵌入模型已经足够智能,可以在嵌入过程中有效地过滤掉“summarize”和“search”等“噪声”词。

然而,最显著的结果是 HyDE 策略(0.0.3)的分数大幅上升。这强烈表明,生成一个假设文档可以实现更精确的语义匹配,因为假设文档的嵌入是比简单关键词查询更精确的目标信息表示。

需要注意的是,这些分数只衡量查询嵌入与检索文档之间的相似性。我们还没有正式测量最终LLM生成响应的质量。然而,更好的文档匹配为系统提供了更强的基础,使其更有可能让LLM拥有高质量的上下文,从而提供更优的回答。

6、实验与未来改进

配置切换使得实验变得简单,可以轻松比较哪种策略更适合你的特定文档和查询模式。

这些查询转换技术也为更复杂的策略做好了准备:

  • 结合向量和元数据过滤的混合搜索
  • 查询扩展和多查询生成
  • 自动触发RAG而不使用显式短语

模块化设计意味着可以通过实现新的系统提示并更新配置选项来添加新的策略。

7、结束语

查询转换解决了基本RAG系统中的一个根本性低效问题:使用一个提示用于两个不同的目的。通过将搜索优化与生成指令分开,我们实现了:

  • 更专注的向量数据库搜索,使用相关关键词或假设文档
  • 更清晰的LLM指令,产生更好的响应
  • 一种灵活的基础,用于尝试不同的检索策略
  • 文档相关性和响应质量的可衡量提升

这些渐进的改进展示了如何通过模块化设计逐步增强RAG系统,构建更强大和可靠的基础,为未来的技巧做准备。


原文链接:Part 5: Advanced RAG Techniques — LLM-Based Query Rewriting and HyDE

汇智网翻译整理,转载请标明出处