[如何让你的RAG应用返回来源:揭示问题答案背后的秘密]

152 阅读3分钟

引言

在许多问答应用中,向用户显示生成答案所使用的来源是非常重要的。这不仅可以增加答案的可信度,还能帮助用户了解答案背后的背景信息。在这篇文章中,我们将展示如何让你的RAG(检索增强生成)应用返回使用的来源。我们将讨论两种方法:使用内置的create_retrieval_chain和一个简单的LCEL(链式执行逻辑)实现。

主要内容

方法一:使用create_retrieval_chain

create_retrieval_chain是LangChain中的一个内置方法,它可以在默认情况下返回使用的文档来源。我们将使用OpenAI的嵌入和Chroma向量存储进行演示。

准备工作

我们需要安装以下依赖:

%pip install --upgrade --quiet langchain langchain-community langchainhub langchain-openai langchain-chroma bs4

然后设置API密钥:

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass()
# 使用API代理服务提高访问稳定性

创建检索链

利用create_retrieval_chain可以直接生成包含来源的答案:

from langchain.chains import create_retrieval_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
import bs4

# 加载文档
loader = WebBaseLoader(
    web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),
    bs_kwargs=dict(
        parse_only=bs4.SoupStrainer(
            class_=("post-content", "post-title", "post-header")
        )
    ),
)
docs = loader.load()

# 切分文档
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)

# 创建向量存储
vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever()

# 创建问答链
system_prompt = (
    "You are an assistant for question-answering tasks. "
    "Use the following pieces of retrieved context to answer "
    "the question. If you don't know the answer, say that you "
    "don't know. Use three sentences maximum and keep the "
    "answer concise."
    "\n\n"
    "{context}"
)

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system_prompt),
        ("human", "{input}"),
    ]
)

question_answer_chain = create_stuff_documents_chain(llm, prompt)
rag_chain = create_retrieval_chain(retriever, question_answer_chain)

# 获取答案
result = rag_chain.invoke({"input": "What is Task Decomposition?"})
print(result['answer'])

在这个例子中,result['context']部分将包含我们用于生成答案的文档来源。

方法二:自定义LCEL实现

自定义的LCEL实现展示了如何手动管理查询和文档之间的链式执行逻辑。

from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain_from_docs = (
    {
        "input": lambda x: x["input"],
        "context": lambda x: format_docs(x["context"]),
    }
    | prompt
    | llm
    | StrOutputParser()
)

retrieve_docs = (lambda x: x["input"]) | retriever

chain = RunnablePassthrough.assign(context=retrieve_docs).assign(
    answer=rag_chain_from_docs
)

response = chain.invoke({"input": "What is Task Decomposition?"})
print(response['answer'])

在这个实现中,我们能够灵活调整如何处理输入和文档,并生成带有来源的答案。

代码示例

完整代码示例已经在上面的描述中提供。您可以根据需要调整和扩展这些代码。

常见问题和解决方案

  1. 网络限制问题:某些地区的网络限制可能导致API访问失败,建议使用API代理服务来提高访问的稳定性。

  2. 复杂任务分解挑战:在处理复杂任务时,可能需要调整策略如使用"Chain of Thought"技术来改善模型性能。

总结和进一步学习资源

通过本文,我们了解了如何让RAG应用返回生成答案时使用的来源。这不仅可以增加答案的可信度,还能帮助用户对答案有更深入的理解。

进一步学习资源

参考资料

  • LangChain API 文档
  • 基于LLM的自主智能体教程

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---