RAG 每日一技(十三):检索一次不够?学习查询改写与迭代式检索!

前情回顾

到目前为止,我们构建的RAG系统,其检索流程本质上是"一锤子买卖":用户提问 -> 系统检索 -> 返回结果。如果第一次检索返回的都是不相关的垃圾信息,整个流程就宣告失败。

这就像一个经验不足的侦探,跟丢了第一条线索后,就直接放弃了整个案件。而一个经验丰富的老侦探,则会重新审视案情,分析失败原因,然后从一个新的、更巧妙的角度再次切入。

我们能让我们的RAG系统也成为一名"老侦探"吗?答案是肯定的。核心思想就是:不要把用户的原始问题当成一成不变的"圣旨",而是要授权给RAG系统,让它在检索前或检索失败后,对原始问题进行"改写"和"优化"。

这项技术,统称为查询转换(Query Transformation)

为什么要改写用户的查询?

用户的原始查询可能存在以下问题:

  1. 过于宽泛或模糊:"请谈谈人工智能。"
  2. 包含多个子问题:"对比一下Refine和Map-Reduce策略的优缺点。"
  3. 语言风格与文档库不匹配:用户用口语提问,而文档库是专业术语。

对这些查询进行改写,可以大大提高检索的精准度。下面我们介绍两种强大且常用的查询改写技术。

技术一:查询分解 (Query Decomposition)

当一个问题包含多个方面时,一次性检索很难找到一个"完美"的文档能覆盖所有方面。查询分解的思想就是将一个复杂问题,拆解成多个独立的、更简单的子问题,然后对每个子问题分别进行检索,最后将所有结果汇总。

  • 原始问题:"对比一下Refine和Map-Reduce策略的优缺点。"
  • 分解后的子问题
    1. "RAG中的Refine策略有什么优点?"
    2. "RAG中的Refine策略有什么缺点?"
    3. "RAG中的Map-Reduce策略有什么优点?"
    4. "RAG中的Map-Reduce策略有什么缺点?"

我们可以用一个LLM来自动完成这个分解任务。

分解Prompt模板示例:

python 复制代码
DECOMPOSITION_PROMPT_TEMPLATE = """
你是一个查询分析专家。请将用户提出的复杂问题,分解成一系列更简单的、可以独立回答的子问题。
请直接返回一个Python列表(List)格式的字符串。

复杂问题: "{question}"

分解后的子问题列表:
"""

工作流程: 复杂问题 -> LLM(分解) -> [子问题1, 子问题2, ...] -> 分别检索 -> 汇总结果 -> LLM(最终生成)

技术二:假设性文档嵌入 (HyDE)

HyDE (Hypothetical Document Embeddings) 是一种脑洞大开、但效果惊人的技术。

我们之前的思路是:计算"问题"和"文档"的向量相似度。但问题通常很短,而文档很长,这种不对称性有时会影响相似度计算的准确性。

HyDE反其道而行之,它的流程是:

  1. 拿到用户的问题后,不直接去检索
  2. 而是先让一个LLM凭空想象并生成一个"假设性的、完美的"答案。这个答案是模型"猜"出来的,很可能是错的。
  3. 我们不关心 这个答案内容的对错,而是将这个长长的、包含丰富上下文的"假设性答案"进行向量化(Embedding)
  4. 用这个"假设性答案的向量"去向量数据库里进行搜索。

核心思想 :一个"完美的答案"的向量,在向量空间中,理应与存储着"真实答案"的文档块的向量,极为接近。我们通过生成一个虚构的答案,来创造一个更优质的"查询探针"。

HyDE Prompt模板示例:

python 复制代码
HYDE_PROMPT_TEMPLATE = """
请根据以下问题,生成一个理想中的、详细的回答。请注意,这个回答是用于后续检索的,不需要保证事实的绝对正确性,但需要包含可能的相关信息和关键词。

问题: "{question}"

生成的理想回答:
"""

工作流程: 问题 -> LLM(生成假设性答案) -> Embedding(假设性答案) -> 用新向量进行检索 -> LLM(用真实文档生成最终答案)

上手实战:两种改写策略的代码逻辑

我们用代码逻辑来展示这两种策略如何实现。

python 复制代码
# 模拟函数
def call_llm(prompt):
    print("--- 调用LLM ---")
    print(f"Prompt: {prompt[:150]}...")
    if "分解" in prompt:
        return "['RAG中Refine策略的优缺点是什么?', 'RAG中Map-Reduce策略的优缺点是什么?']"
    if "理想回答" in prompt:
        return "RAG(检索增强生成)是一种将大语言模型与外部知识库结合的技术。它通过检索相关文档来为生成提供依据,从而减少幻觉并提高答案的准确性和时效性。这在问答、内容创作等领域有广泛应用。"
    return "..."

def retrieve(text_for_embedding):
    print(f"--- 正在对文本进行Embedding并检索 ---")
    print(f"文本: {text_for_embedding[:100]}...")
    return ["检索到的文档1", "检索到的文档2"]

# --- 策略一:查询分解 ---
def run_decomposition(question):
    print("\n=== 运行查询分解策略 ===")
    prompt = DECOMPOSITION_PROMPT_TEMPLATE.format(question=question)
    sub_questions_str = call_llm(prompt)
    sub_questions = eval(sub_questions_str) # 将字符串格式的列表转为真实的列表

    all_retrieved_docs = []
    for sub_q in sub_questions:
        docs = retrieve(sub_q)
        all_retrieved_docs.extend(docs)
    
    print("--- 所有子问题检索到的文档 ---")
    print(list(set(all_retrieved_docs))) # 去重

# --- 策略二:HyDE ---
def run_hyde(question):
    print("\n=== 运行HyDE策略 ===")
    prompt = HYDE_PROMPT_TEMPLATE.format(question=question)
    hypothetical_answer = call_llm(prompt)
    
    retrieved_docs = retrieve(hypothetical_answer)
    print("--- HyDE检索到的文档 ---")
    print(retrieved_docs)


complex_question = "对比一下Refine和Map-Reduce策略的优缺点,并简述RAG是什么。"
run_decomposition(complex_question)
run_hyde(complex_question)

总结与预告

今日小结:

  • 优秀的RAG系统不应满足于"一次性"检索,而应具备查询改写迭代检索的能力。
  • 查询分解:将复杂问题拆分为简单的子问题,分别检索,再汇总结果,适合处理多方面的问题。
  • HyDE:通过生成一个"假设性答案"来创造一个更优质的查询向量,适合处理较短或较模糊的问题。
  • 这些技术让RAG系统变得更"聪明",能从失败的检索中自我恢复,并主动优化检索路径。

经过这几天的学习,我们已经探索了混合搜索、Refine、Map-Reduce、查询改写等多种高级组件和策略。现在,我们的工具箱里已经装满了各种强大的"乐高积木"。

问题来了:我们该如何高效、优雅地将这些复杂的组件和逻辑流(比如先做查询改写,再做混合搜索,最后用Refine策略生成答案)串联起来,构建成一个稳定、可维护的应用呢?手动编写大量的"胶水代码"显然是一场噩梦。

明天预告:RAG 每日一技(十四):化繁为简,统揽全局------用LangChain构建高级RAG流程

明天,我们将把目光从"造零件"转向"搭框架"。我们将正式介绍大名鼎鼎的AI应用开发框架LangChain,学习它是如何通过"链(Chain)"的概念,将我们学过的所有RAG组件和策略,像搭乐高一样轻松地编排和组合在一起的。这将是提升你RAG开发效率的革命性一步!

相关推荐
江畔柳前堤2 小时前
roLabelImg 详细安装教程
开发语言·人工智能·后端·云原生
阿里云大数据AI技术2 小时前
分链路差异化设计的DSP准实时数仓|钛动科技基于阿里云实时计算 Flink 版 + DLF Paimon + EMR Serverless StarRocks 的实践
人工智能·flink
陕西企来客2 小时前
2026年7月AI智能搜索曝光趋势研判
大数据·人工智能·机器学习·ai智能搜索曝光
阿里云大数据AI技术3 小时前
从算力到智能体,面向 Agentic AI 的基础设施演进
人工智能·agent
hangyuekejiGEO3 小时前
GEO技术服务选型指南
大数据·人工智能·python
阿里云大数据AI技术4 小时前
EMR Serverless Spark AI Function 的双维降本实践
人工智能·sql·spark
陈随易4 小时前
moon,apt和yum之外linux系统命令安装新选择
前端·后端·程序员
维基框架4 小时前
GitHub源码处理提速 一趟扫描反而更慢
人工智能·github
冬奇Lab4 小时前
代码库知识库系列(05):向量检索 vs 知识图谱——加了调用图并没有变更好
人工智能
AKAMAI4 小时前
你的源服务器可能是你做出的最昂贵决定
运维·人工智能·云计算