RAG 每日一技(十一):只检索还不够爽?迭代式文档精炼(Refine)了解一下!

前情回顾

在上一篇文章中,我们用混合搜索解决了纯向量检索对关键词不敏感的问题,让我们的“召回”能力又上了一个台阶。现在,我们手上已经有了一个经过“粗排+精排”或者“混合搜索”得到的高质量相关文档列表。

接下来我们该怎么做?

按照我们之前在第八篇里学到的方法,我们会把这些文档块(Chunks)一股脑儿地塞进一个Prompt模板里,然后交给LLM去生成答案。

这种“一波流”的做法简单直接,但它有几个明显的缺点:

  1. 上下文窗口限制:如果召回的文档太多、太长,很容易就会超出LLM的上下文窗口限制,导致信息被截断。
  2. “迷失在中间”问题:研究表明,当LLM处理一个很长的上下文时,它对开头和结尾部分的信息最敏感,中间部分的信息很容易被“忽略”。
  3. 信息综合能力差:简单地将一堆文档块堆砌在一起,可能会让LLM难以理清头绪,特别是当答案需要综合多个文档块的信息时。

有没有一种更像“人”的思考方式?我们人类在写报告时,通常是先看第一份资料,写个初稿;再看第二份资料,在初稿的基础上补充和修改;再看第三份……不断地迭代和精炼。

今天,我们就来学习这种模拟人类思考过程的高级RAG策略——迭代式精炼(Refine)

什么是Refine策略?

Refine策略是一种串行处理多个文档的RAG工作流。它不再是一次性地将所有上下文都抛给LLM,而是逐一“喂”给LLM,并让LLM在每一步都对之前的答案进行“反思和改进”。

它的具体流程如下:

  1. 从检索器获取一个有序的文档列表(比如Top 3:[Doc1, Doc2, Doc3])。
  2. 第一步:使用一个“初始问答Prompt”,将第一个文档(Doc1)和用户问题结合,生成一个初步答案
  3. 第二步:使用一个“精炼答案Prompt”,将第二个文档(Doc2)、用户问题、以及上一步生成的初步答案结合,让LLM在已有答案的基础上,利用新文档的信息来优化和完善答案
  4. 第三步:继续使用“精炼答案Prompt”,将第三个文档(Doc3)、用户问题、以及第二步生成的精炼答案结合,再次进行优化。
  5. …循环往复,直到处理完所有文档,得到最终答案。
核心:两种不同的Prompt模板

实现Refine策略的关键,在于我们需要两个不同的Prompt模板。

1. 初始问答模板 (Initial Prompt)
这个模板用于处理第一个文档,和我们之前学的标准RAG Prompt很像。

INITIAL_PROMPT_TEMPLATE = """
根据下面的上下文信息,回答问题。
---
上下文:
{context}
---
问题:
{question}
---
答案:
"""

2. 精炼答案模板 (Refine Prompt)
这个模板是Refine策略的灵魂,它告诉LLM要扮演一个“编辑”的角色,在已有答案的基础上进行改进。

REFINE_PROMPT_TEMPLATE = """
原始问题是:{question}
我们已经有了一个初步的答案:{existing_answer}
现在我们有了一些额外的上下文信息,希望能进一步完善这个答案。
---
额外上下文:
{context}
---
请结合新的上下文,改进原有的答案。如果新的上下文没有提供有用的信息,就直接返回原有的答案。
完善后的答案:
"""
上手实战:用代码实现Refine循环

让我们通过一个场景来感受Refine的威力。假设用户问“RAG的优缺点是什么?”,而我们召回的文档中,一个讲了优点,另一个讲了缺点。

# 这是一个模拟的LLM调用函数
def call_llm(prompt):
    print("--- 正在调用LLM ---")
    print(f"Prompt: {prompt[:200]}...") # 只打印部分prompt
    # 手动模拟LLM的回答
    if "优点" in prompt and "缺点" not in prompt:
        return "RAG的优点是能够利用外部知识,减少模型幻觉,并提高答案的实时性。"
    elif "缺点" in prompt:
        return "RAG的优点是能够利用外部知识,减少模型幻觉,并提高答案的实时性。其缺点则包括检索质量对最终效果影响大,以及系统延迟较高等问题。"
    else:
        return "模拟回答..."

# --- 1. 准备工作 ---
# 用户问题
question = "RAG的优缺点是什么?"

# 召回的有序文档列表
documents = [
    "RAG,即检索增强生成,其主要优点是能将实时、动态的外部知识库引入到大型语言模型中,从而显著减少模型产生事实性错误的‘幻觉’现象。", # 文档1: 只讲优点
    "尽管RAG很强大,但它也有缺点。系统的最终效果高度依赖于检索模块的质量,如果检索出错,生成环节也无能为力。此外,检索、生成两阶段的流程也引入了额外的系统延迟。" # 文档2: 只讲缺点
]

# --- 2. 实现Refine循环 ---
# 处理第一个文档
initial_prompt = INITIAL_PROMPT_TEMPLATE.format(context=documents[0], question=question)
current_answer = call_llm(initial_prompt)
print(f"--- 初步答案 (基于文档1) ---\n{current_answer}\n")

# 循环处理剩余的文档
for doc in documents[1:]:
    refine_prompt = REFINE_PROMPT_TEMPLATE.format(
        question=question,
        existing_answer=current_answer,
        context=doc
    )
    current_answer = call_llm(refine_prompt)
    print(f"--- 精炼后答案 (结合新文档) ---\n{current_answer}\n")

final_answer = current_answer
print(f"--- 最终答案 ---\n{final_answer}")

结果分析:
通过观察打印出的流程,你会清晰地看到答案是如何一步步“成长”的:

  1. 第一次调用后,答案只包含了RAG的优点
  2. 第二次调用时,LLM接收到了包含“缺点”信息的新上下文,以及上一步的答案。在REFINE_PROMPT_TEMPLATE的指导下,它将新旧信息整合,输出了一个同时包含优点和缺点的、更全面的答案。
Refine策略的优缺点
  • 优点
    • 能处理大量文档:可以突破单次Prompt的上下文窗口限制。
    • 答案更全面:通过迭代综合,能生成质量更高、信息更完整的答案。
    • 减轻“迷失”问题:每次只聚焦一小块新信息,LLM的注意力更集中。
  • 缺点
    • 高延迟:需要进行N次串行的LLM调用(N是文档数量),耗时很长,不适用于实时问答场景。
    • 顺序敏感:文档的顺序可能会影响最终结果。
总结与预告

今日小结:

  • Refine(迭代式精炼) 是一种通过串行处理多个文档,逐步完善答案的高级RAG策略。
  • 它依赖于“初始问答”和“精炼答案”两种不同的Prompt模板来指导LLM的行为。
  • Refine能生成更全面的高质量答案,但代价是极高的延迟,适用于对答案质量要求苛刻的离线任务。

Refine策略的串行调用机制决定了它的速度瓶颈。那么,有没有一种可以并行处理多个文档,最后再汇总结果的策略呢?这样不是能大大节省时间吗?

明天预告:RAG 每日一技(十二):多路并进,融会贯通!聊聊Map-Reduce RAG策略

明天,我们将学习另一种源自大数据处理思想的经典RAG策略:Map-Reduce。我们将看看它是如何通过“分而治之”再“合并归一”的方式,来高效地处理海量文档的。敬请期待!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值