基于 LangChain 1.0 的检索增强生成(RAG)实战

基于 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 操作导出所有的提问和引用记录,以不断优化本地知识库。

运行示例

相关推荐
大力财经4 小时前
纳米漫剧流水线接入满血版Seedance 2.0 实现工业级AI漫剧确定性交付
大数据·人工智能
咚咚王者4 小时前
人工智能之语音领域 语音处理 第六章 语音处理技术发展趋势与未来展望
人工智能·语音识别
ipython_harley4 小时前
【AGI】OpenAI核心贡献者翁家翌:修Infra的人,正在定义GPT-5
人工智能·gpt·ai·agi
幻风_huanfeng4 小时前
人工智能之数学基础:什么是凸优化问题?
人工智能·算法·机器学习·凸优化
愚公搬代码4 小时前
【愚公系列】《剪映+DeepSeek+即梦:短视频制作》046-转场:短视频一气呵成的秘密(什么是转场)
人工智能·音视频
周周爱喝粥呀4 小时前
词元ID是如何转为嵌入向量? 位置嵌入的作用是什么?
人工智能·ai
_李小白4 小时前
【OSG学习笔记】Day 31: 渲染到纹理(RTT)
笔记·数码相机·学习
RopenYuan4 小时前
FastAPI -API Router的应用
前端·网络·python