前言:什么是RAG?
在构建问答Agent时,我们常常希望它能基于我们提供的特定文档(如产品手册、公司规定、学习笔记等)来回答问题,而不是仅仅依赖其训练数据中的通用知识。实现这一目标的核心技术就是RAG(Retrieval-Augmented Generation,检索增强生成)。
RAG的工作流程可以概括为两步:
- 检索(Retrieval):当用户提出问题时,系统首先从你的知识库(文档)中检索出与问题最相关的片段。
- 生成(Generation):系统将用户的问题和检索到的相关文段一起发送给大语言模型(LLM),让LLM基于这些上下文信息,生成精准的回答。
LangChain为实现RAG流程提供了强大的支持。本教程将带你一步步构建一个完整的RAG问答Agent。
准备工作
在开始之前,请确保你已经安装了必要的Python库。我们将使用langchain来构建核心逻辑,ollama来本地运行大模型,chromadb作为向量数据库,以及bs4来解析HTML文档。
pip install langchain langchain_community ollama chromadb beautifulsoup4同时,请确保你已经通过Ollama在本地运行了一个大语言模型(如llama3)。
步骤一:加载文档
首先,我们需要一个数据源。这里我们以加载一个网页内容为例。LangChain提供了丰富的DocumentLoader,可以加载各种类型的数据。
# 1. 加载文档from langchain_community.document_loaders import WebBaseLoader
loader = WebBaseLoader( web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",), bs_kwargs=dict( parse_only=("article",), ),)docs = loader.load()步骤二:分割文档
直接将整篇长文档交给LLM处理,效率低且效果差。我们需要将其分割成更小的、语义完整的文本块(Chunks)。
# 2. 分割文档from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)splits = text_splitter.split_documents(docs)步骤三:创建并存储Embeddings
为了让计算机能够理解文本并判断相关性,我们需要将文本块转化为向量(Embeddings)。然后,将这些向量存储在向量数据库(Vector Store)中,以便后续快速检索。
# 3. 创建并存储Embeddingsfrom langchain_community.embeddings import OllamaEmbeddingsfrom langchain_community.vectorstores import Chroma
# 创建Embeddings模型embeddings = OllamaEmbeddings(model="llama3")
# 创建向量数据库并存入分割后的文档vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings)步骤四:创建检索器
向量数据库本身只是一个存储。我们需要创建一个retriever(检索器),它定义了如何根据用户查询从数据库中检索相关文档。
# 4. 创建检索器retriever = vectorstore.as_retriever()步骤五:创建并运行检索链(Retrieval Chain)
现在,万事俱备。我们可以创建一个“检索链”,将前面所有的步骤串联起来:接收用户问题 -> 格式化Prompt -> 调用检索器获取相关文档 -> 将问题和文档传给LLM -> 生成最终答案。
LangChain的表达式语言(LCEL)让这个过程变得异常简单。
# 5. 创建并运行检索链from langchain.chains import create_retrieval_chainfrom langchain.chains.combine_documents import create_stuff_documents_chainfrom langchain_core.prompts import ChatPromptTemplatefrom langchain_community.llms import Ollama
# 定义LLMllm = Ollama(model="llama3")
# 定义Prompt模板prompt = ChatPromptTemplate.from_template("""Answer the following question based only on the provided context:
<context>{context}</context>
Question: {input}""")
# 创建文档处理链document_chain = create_stuff_documents_chain(llm, prompt)
# 创建检索链retrieval_chain = create_retrieval_chain(retriever, document_chain)
# 发起调用response = retrieval_chain.invoke({"input": "What is Task Decomposition?"})print(response["answer"])运行代码后,你将看到LLM基于你提供的网页内容,给出了关于“Task Decomposition”的精准回答。
总结
恭喜你!你已经成功构建了一个功能完备的知识库问答Agent。通过本教程,你掌握了RAG的核心流程,并学会了如何使用LangChain将文档加载、分割、向量化、检索和生成等步骤有机地结合起来。
这只是一个开始。你可以尝试替换不同的文档加载器、分割策略、向量数据库和LLM模型,来满足更复杂的业务需求。在下一篇文章中,我们将更深入地探讨Agent的核心机制:ReAct框架与工具调用。
参考资源: