跳转至

LangChain RAG(检索增强生成)

RAG(Retrieval-Augmented Generation,检索增强生成)是目前最实用的 LLM 应用模式之一。它解决了一个核心问题:模型不知道你私有数据的内容

RAG 的思路很简单:先检索与问题相关的文档片段,再把这些片段作为上下文一起发给模型,让模型基于这些内容来回答。

1. RAG 的处理流程

[文档] → 加载 → 分割 → 向量化 → 存入向量库
[用户问题] → 向量化 → 在向量库中检索 → 相关片段 + 问题 → LLM → 回答

2. 安装依赖

pip install langchain langchain-openai langchain-community chromadb

chromadb 是一个轻量级的本地向量数据库,非常适合入门学习。

3. 完整 RAG 示例

下面用一段关于 Python 的文字来演示完整流程:

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma

load_dotenv()

# === 第一步:准备文档 ===
documents = [
    """Python 是一种解释型、面向对象的高级编程语言。
    它由 Guido van Rossum 于 1991 年创建,以简洁易读著称。
    Python 的设计哲学强调代码的可读性,使用缩进来表示代码块。""",

    """Python 广泛用于 Web 开发、数据科学、人工智能和自动化脚本。
    Django 和 Flask 是最流行的 Python Web 框架。
    在数据科学领域,NumPy、Pandas 和 Matplotlib 是核心工具。""",

    """Python 的版本历史:Python 2 于 2020 年正式停止维护。
    Python 3 是目前的主流版本,引入了很多改进,如类型提示、f-string 等。
    建议所有新项目都使用 Python 3.9 及以上版本。""",
]

# === 第二步:分割文档 ===
splitter = RecursiveCharacterTextSplitter(
    chunk_size=200,       # 每个片段最多 200 个字符
    chunk_overlap=20,     # 相邻片段重叠 20 个字符,避免语义断裂
)
chunks = splitter.create_documents(documents)
print(f"文档被分割为 {len(chunks)} 个片段")

# === 第三步:向量化并存入向量库 ===
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
)

# === 第四步:创建检索器 ===
retriever = vectorstore.as_retriever(
    search_kwargs={"k": 2},  # 每次检索最相似的 2 个片段
)

# === 第五步:构建 RAG 链 ===
llm = ChatOpenAI(model="gpt-4o-mini")
parser = StrOutputParser()

prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一位知识助手,请只根据以下提供的上下文来回答问题。
如果上下文中没有相关信息,就说"根据提供的资料,我无法回答这个问题"。

上下文:
{context}"""),
    ("human", "{question}"),
])

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = (
    {
        "context": retriever | format_docs,
        "question": RunnablePassthrough(),
    }
    | prompt
    | llm
    | parser
)

# === 第六步:提问 ===
print(rag_chain.invoke("Python 是谁创建的?"))
print(rag_chain.invoke("Python 常用在哪些领域?"))
print(rag_chain.invoke("Python 2 还能用吗?"))

4. 加载真实文档

实际项目中文档来自文件,LangChain 提供了多种文档加载器:

加载 PDF 文件:

pip install pypdf
from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("report.pdf")
docs = loader.load()
print(f"加载了 {len(docs)} 页")

加载网页:

pip install beautifulsoup4
from langchain_community.document_loaders import WebBaseLoader

loader = WebBaseLoader("https://python.org/about/")
docs = loader.load()

加载文本文件:

from langchain_community.document_loaders import TextLoader

loader = TextLoader("data.txt", encoding="utf-8")
docs = loader.load()

加载一个目录下的所有文件:

from langchain_community.document_loaders import DirectoryLoader

loader = DirectoryLoader("./docs/", glob="**/*.txt")
docs = loader.load()

5. 文本分割策略

RecursiveCharacterTextSplitter 是最常用的分割器,按照 \n\n\n、空格的优先级递归分割:

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,      # 片段最大长度(字符数)
    chunk_overlap=50,    # 相邻片段的重叠字符数
    length_function=len, # 计算长度的函数
)

chunk_size 和 chunk_overlap 如何选择:

  • chunk_size 太大:每个片段包含的信息太多,检索精度下降
  • chunk_size 太小:语义被截断,丢失上下文
  • chunk_overlap 建议为 chunk_size 的 10%~20%

6. 向量数据库持久化

上面的例子中,向量库在内存中,程序结束就消失了。生产环境需要持久化:

from langchain_community.vectorstores import Chroma

# 保存到本地目录
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./chroma_db",  # 指定存储路径
)

# 下次直接加载,无需重新向量化
vectorstore = Chroma(
    persist_directory="./chroma_db",
    embedding_function=embeddings,
)

7. 检索器进阶

as_retriever() 支持多种检索策略:

# 默认:相似度检索,返回最相似的 k 个
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# MMR(最大边际相关性):兼顾相似性和多样性,避免返回重复内容
retriever = vectorstore.as_retriever(
    search_type="mmr",
    search_kwargs={"k": 3, "fetch_k": 10},
)

# 相似度阈值:只返回相似度高于阈值的结果
retriever = vectorstore.as_retriever(
    search_type="similarity_score_threshold",
    search_kwargs={"score_threshold": 0.7},
)

总结

  • RAG = 检索 + 生成,解决模型不了解私有数据的问题
  • 流程:加载文档 → 分割 → 向量化 → 存库 → 检索 → 生成
  • RecursiveCharacterTextSplitter 是最常用的分割器
  • Chroma 适合本地开发,生产环境可用 Pinecone、Weaviate 等
  • 检索器支持相似度、MMR、阈值等多种策略

评论