基于 Python 的自然语言处理系列(62):Rewrite-Retrieve-Read

介绍

        Rewrite-Retrieve-Read (重写-检索-阅读) 是一种改进 RAG(Retrieve-and-Read)流程的方法,最早由论文 Query Rewriting for Retrieval-Augmented Large Language Models 提出。

        在现实世界中,用户的原始查询往往并不适合直接用于检索,尤其是在 LLM 任务中。为了解决这个问题,该方法首先利用 LLM 重写查询,使其更加适合检索,进而提升最终回答的准确性。

        本篇文章将展示如何使用 LangChain Expression Language 来实现 Rewrite-Retrieve-Read

基准 RAG 方法

        基准 RAG (Retrieve-and-Read) 方法的实现如下:

from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
from langchain.utilities import DuckDuckGoSearchAPIWrapper

# 定义 Prompt 模板
template = """Answer the user's question based only on the following context:

<context>
{context}
</context>

Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)

model = ChatOpenAI(temperature=0)
search = DuckDuckGoSearchAPIWrapper()

def retriever(query):
    return search.run(query)

# 构建检索-阅读流水线
chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)

# 示例查询
simple_query = "what is langchain?"
chain.invoke(simple_query)

问题分析

        上述方法对于格式良好的查询表现良好,但如果查询包含无关内容(如带有情绪或背景信息的长句),检索效果可能会大幅下降。例如:

distracted_query = "man that sam bankman fried trial was crazy! what is langchain?"
chain.invoke(distracted_query)

        由于检索器不能有效处理这些"分散注意力的查询",它可能会返回不相关的搜索结果。

Rewrite-Retrieve-Read 实现

        为了解决上述问题,我们可以先使用 LLM 重写查询,然后再进行检索。具体实现如下。

查询重写模块

from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser

# 定义查询重写 Prompt
template = """Provide a better search query for \
web search engine to answer the given question, end \
the queries with ’**’. Question: \
{x} Answer:"""
rewrite_prompt = ChatPromptTemplate.from_template(template)

# 也可以使用 LangChain Hub 中的现成 Prompt
from langchain import hub
rewrite_prompt = hub.pull("langchain-ai/rewrite")

# 解析查询
def _parse(text):
    return text.strip("**")

rewriter = rewrite_prompt | ChatOpenAI(temperature=0) | StrOutputParser() | _parse

# 示例测试
rewriter.invoke({"x": distracted_query})

Rewrite-Retrieve-Read 流水线

rewrite_retrieve_read_chain = (
    {
        "context": {"x": RunnablePassthrough()} | rewriter | retriever,
        "question": RunnablePassthrough(),
    }
    | prompt
    | model
    | StrOutputParser()
)

# 执行查询
rewrite_retrieve_read_chain.invoke(distracted_query)

结论

        Rewrite-Retrieve-Read 方法通过引入 查询重写 (Query Rewrite),使检索更加精准,提升了最终 LLM 生成回答的质量。相比于传统 Retrieve-and-Read 方法,它能够更好地应对用户提供的非标准化查询,适用于 搜索引擎增强生成 (RAG)、问答系统、法律/医学文档检索 等应用场景。

        如果你对这个方法感兴趣,欢迎在评论区交流你的想法!

如果你觉得这篇博文对你有帮助,请点赞、收藏、关注我,并且可以打赏支持我!

欢迎关注我的后续博文,我将分享更多关于人工智能、自然语言处理和计算机视觉的精彩内容。

谢谢大家的支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的Anthony

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值