基于 LangChain 1.0 的检索增强生成(RAG)实战
文章目录
- [基于 LangChain 1.0 的检索增强生成(RAG)实战](#基于 LangChain 1.0 的检索增强生成(RAG)实战)
-
- [1. RAG 核心流程概览](#1. RAG 核心流程概览)
- [2. 环境与依赖准备](#2. 环境与依赖准备)
- [3. 核心模块代码实现](#3. 核心模块代码实现)
-
- [3.1 文档加载 (Load)](#3.1 文档加载 (Load))
- [3.2 文本分块 (Chunk)](#3.2 文本分块 (Chunk))
- [3.3 向量化与存储 (Embed & FAISS)](#3.3 向量化与存储 (Embed & FAISS))
- [3.4 检索与 RAG Chain 组装 (Retrieve & Answer)](#3.4 检索与 RAG Chain 组装 (Retrieve & Answer))
- [4. 企业知识库系统设计与扩展思路](#4. 企业知识库系统设计与扩展思路)
-
- [4.1 交互性与模块化封装](#4.1 交互性与模块化封装)
- [4.2 查询日志与溯源 (Log & Sources)](#4.2 查询日志与溯源 (Log & Sources))
🔗 查看完整专栏(LangChain 1.0 与 LangGraph 学习笔记)
特别说明:
本文为个人学习笔记,内容仅供学习与交流使用,禁止转载或用于商业用途。笔记为个人理解与总结,可能存在疏漏或偏差,欢迎读者参考并自行甄别。

大语言模型虽然强大,但通常缺乏私有数据(如企业内部文档)的知识,且容易产生"幻觉"。为了解决这个问题,检索增强生成(Retrieval-Augmented Generation, 简称 RAG) 成为了目前最主流的落地架构。
本篇笔记将基于 LangChain 1.0 和 FAISS 向量数据库,从零手写一个完整的 RAG 系统。
1. RAG 核心流程概览
在编写代码之前,我们需要先在脑海中建立 RAG 的标准数据流转模型。整个过程可以分为**数据准备(离线)和检索问答(在线)**两个阶段。
完整的数据流水线如下:
shell
原始文档 (Document)
→ 1. 加载 (Load)
→ 2. 文本分块 (Chunk)
→ 3. 向量嵌入 (Embed)
→ 4. 存入向量库 (FAISS Vector Store)
--- 以上为准备阶段,以下为问答阶段 ---
→ 5. 用户提问检索 (Retrieve)
→ 6. 拼接上下文交由大模型 (LLM)
→ 7. 生成精准答案 (Answer)
系统通过计算用户问题与文档块的向量相似度,精准召回最相关的文档片段,并将其作为"参考资料"(Context)喂给 LLM,从而生成有理有据的回答。
2. 环境与依赖准备
本项目需要处理文档解析和向量计算,除了核心的 LangChain 包之外,还需要额外安装 FAISS 引擎。
py
# 核心环境依赖
pip install langchain langchain-community langchain-openai python-dotenv
# 安装 FAISS 向量数据库(CPU版本即可满足本地测试需求)
pip install faiss-cpu
3. 核心模块代码实现
接下来,我们将上述 RAG 的 7 个步骤拆解,利用 LangChain 1.0 提供的组件逐一实现。
3.1 文档加载 (Load)
LangChain 社区提供了丰富的 Document Loaders。我们可以轻松加载 PDF、Word、TXT,甚至直接批量加载整个目录下的文件。
py
from langchain_community.document_loaders import PyPDFLoader, DirectoryLoader, TextLoader
# 示例 1:加载单个 PDF 文件
pdf_loader = PyPDFLoader("data/RAG.pdf")
pdf_docs = pdf_loader.load()
# 示例 2:批量加载知识库目录下的所有 txt 文件
directory_loader = DirectoryLoader(
"data/knowledgebase/docs/",
glob="**/*.txt",
loader_cls=TextLoader,
loader_kwargs={"encoding": "utf-8"}
)
all_docs = directory_loader.load()
print(f"共加载了 {len(all_docs)} 个文件")
3.2 文本分块 (Chunk)
整篇文档通常会超出 LLM 的上下文窗口,我们需要将其切片。对于中文文本,使用 RecursiveCharacterTextSplitter 并自定义中文分隔符效果最佳。
py
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 针对中文优化的递归字符分块器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 每块最大字符数
chunk_overlap=50, # 相邻块之间的重叠字符数,防止语义被生硬切断
separators=[
"\n\n", "\n", "。", "!", "?", ";", ",", " ", "" # 按中文标点优先级分割
],
length_function=len
)
chunks = text_splitter.split_documents(all_docs)
print(f"分块完成,共 {len(chunks)} 个块")
3.3 向量化与存储 (Embed & FAISS)
接下来,将这些文本块通过 Embeddings 模型转化为密集向量,并存储在本地 FAISS 数据库中。
py
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
# 1. 初始化 Embedding 模型 (这里使用 OpenAI 兼容接口)
embeddings = OpenAIEmbeddings(
base_url="YOUR_BASE_URL",
api_key="YOUR_API_KEY",
model="text-embedding-3-small"
)
# 2. 构建 FAISS 向量数据库
vector_store = FAISS.from_documents(chunks, embeddings)
# 3. 持久化保存到本地目录
vector_store.save_local("data/knowledgebase/index")
print("向量数据库创建并保存成功!")
# 附加:需要时可以通过以下方式加载已有索引
# loaded_vector_store = FAISS.load_local("data/knowledgebase/index", embeddings, allow_dangerous_deserialization=True)
3.4 检索与 RAG Chain 组装 (Retrieve & Answer)
最后,我们将检索器、提示词模板、大模型组装成一条 LCEL (LangChain Expression Language) 处理链。
py
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain.chat_models import init_chat_model
# 1. 将向量库转换为检索器 (召回相似度最高的 Top-3 文档块)
retriever = vector_store.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
# 2. 构建严格约束的系统提示词
prompt = ChatPromptTemplate.from_template("""
## 角色:你是一位从事人工智能领域的知识问答助手,请基于上下文来回答问题。
## 上下文:{context}
## 问题:{question}
## 要求:
1. 仅基于上下文来回答,不要编造和查询额外信息;
2. 如果上下文没有相关信息,请直接回答"我不知道";
3. 请用中文回答,准确且简洁。
""")
# 3. 初始化 LLM 模型
model = init_chat_model(
model="your-model-name",
model_provider="openai",
base_url="YOUR_BASE_URL",
api_key="YOUR_API_KEY",
temperature=0.0 # RAG 场景通常将温度设为 0 以保证严谨性
)
# 4. 辅助函数:将召回的多个 Document 对象格式化为长字符串
def format_docs(docs):
return "\n\n".join([doc.page_content for doc in docs])
# 5. 组装 RAG Chain (LCEL 语法)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
# 执行提问测试
question = "公司的核心价值观是什么?"
result = rag_chain.invoke(question)
print(f"【回答】: {result}")
4. 企业知识库系统设计与扩展思路
在掌握了核心代码之后,如果我们想将其封装为一个可用的"企业知识库问答系统",除了上述的 Pipeline,通常还需要在业务层补充以下设计:
4.1 交互性与模块化封装
可以将流程封装进一个 KnowledgeBase 类中,提供 build_index()、load_index() 和 query() 方法。通过 CLI(命令行界面)使用 while True 循环实现交互式持续问答。
4.2 查询日志与溯源 (Log & Sources)
在企业级应用中,建立信任感的核心是答案可追溯。我们在执行 RAG 链时,除了获取最终的 Answer,还应当记录检索出来的原文档路径并保存日志。
py
# 截取召回文档的元数据作为溯源依据
sanitized_docs = []
for doc in retrieved_docs:
sanitized_metadata = {}
if "source" in doc.metadata:
# 仅保留文件名,避免暴露服务器物理路径
sanitized_metadata["source"] = Path(doc.metadata["source"]).name
sanitized_docs.append(sanitized_metadata)
# 构建标准的审计日志
log_entry = {
"question": question,
"answer": result,
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"sources": sanitized_docs
}
# 后续可以将 log_entry 追加写入 JSON 日志文件
通过这种日志追踪机制,系统不仅能提供精准的回复,管理员还能通过 export 操作导出所有的提问和引用记录,以不断优化本地知识库。
运行示例
