在问答应用程序中,让用户看到生成答案所使用的来源信息常常非常重要。实现这一功能的最简单方法是让生成链返回每次调用时检索到的文档。在本文中,我将基于Lilian Weng的博客“LLM Powered Autonomous Agents”以及RAG(Retrieve and Generate)教程中的应用,讨论如何在RAG应用中返回来源信息。
技术背景介绍
在许多问答应用中,返回答案的同时显示答案生成的来源可以增加用户的信任度和透明度。尤其是在信息敏感或需要验证的场景中,标记答案的出处显得尤其重要。
核心原理解析
我们将介绍两种方法:
- 使用内置的
create_retrieval_chain
函数,该函数默认返回来源。 - 使用简单的LCEL(List-Chain-Execute-List)实现,示例化操作原理。
这两种方法都可以帮助我们将来源信息结构化到模型响应中,使得模型可以报告在生成其答案时使用了哪些特定的来源。
代码实现演示
依赖安装
在本教程中,我们将使用OpenAI的嵌入和Chroma向量存储,但这里展示的所有内容都可以与任何Embeddings
、VectorStore
或Retriever
配合使用。
%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()
使用create_retrieval_chain
首先,我们选择一个LLM(大语言模型):
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
这里是基于LLM的问答应用,其中构建了一个RAG链:
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
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?"})
自定义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
)
chain.invoke({"input": "What is Task Decomposition"})
应用场景分析
- 学术研究:在学术问答场景中,展示来源可以提高答案的可信度。
- 企业智库:在企业内部系统的知识检索中,可能需要对每个答案标记出处以进行信息溯源。
- 媒体报道:在新闻或信息传播应用中,回答问题时引用来源可以提高报道的可信度。
实践建议
- 结合使用多种技术:结合使用语言模型与检索模型,保证答案的准确性和可解释性。
- 优化性能:确保每一步的计算和检索高效,避免回答延迟。
- 持续监控和调整:在应用环境中不断迭代和改进RAG应用,实现更好的用户体验。
如果遇到问题欢迎在评论区交流。
—END—