LangChain进阶篇(一):RAG应用构建(向量数据库)

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

在大模型(LLM)应用开发中,RAG(检索增强生成) 已成为解决模型“知识幻觉”和“知识时效性”问题的标准范式。通过将私有数据或实时数据挂载到大模型上,我们可以构建出专业的知识库问答系统。

本文将深入剖析RAG的核心组件,并演示如何使用 LangChain、Chroma 向量数据库以及 DeepSeek 大模型构建一个完整的 RAG 应用。

📚 一、理论架构:RAG 的两大核心

一个典型的 RAG 应用程序由两个主要流程组成:索引(Indexing)检索与生成(Retrieval and Generation)

RAG 核心架构

索引阶段
Indexing

从源获取数据

长文本切片

向量化并入库

运行阶段
Retrieval & Gen

查找相关片段

LLM回答问题

1.1 索引阶段 (Indexing)

这是一个数据“摄取”的过程,通常在离线状态下完成,目的是为后续的搜索做好准备。

  1. 加载 (Load):通过 文档加载器 (Document Loaders) 将非结构化数据(如 PDF、网页、Markdown)加载为标准化的文档对象。
  2. 分割 (Split):使用 文本分割器 (Text Splitters) 将大型文档切分为较小的块(Chunk)。
    • 为什么要分割? 大块文本难以精确搜索,且容易超出 LLM 的上下文窗口限制。
  3. 存储 (Store):通过 嵌入模型 (Embedding Model) 将文本块转化为向量,并存储在 向量数据库 (VectorStore) 中。

Document Loader

Text Splitter

Embedding Model

写入

原始数据
Source Data

文档对象
Documents

文本块
Chunks

向量数据
Vectors

Vector Store
向量数据库

1.2 检索与生成阶段 (Retrieval & Generation)

这是 RAG 链在运行时与用户交互的实际过程。

  1. 检索 (Retrieve):根据用户的自然语言查询,检索器 (Retriever) 在向量数据库中计算相似度,找到最相关的 Top-K 个文本块。
  2. 生成 (Generate):将用户问题检索到的文本块合并,填入 Prompt 模板,发送给 LLM 生成最终答案。
DeepSeek模型 向量数据库 RAG链 用户 DeepSeek模型 向量数据库 RAG链 用户 提问: "什么是任务分解?" 语义搜索 (Embedding查询) 返回相关文档片段 (Chunks) 组装 Prompt: [背景资料] + [问题] 发送完整 Prompt 生成基于事实的回答 返回最终答案

💻 二、实战演练:构建 DeepSeek 文档助手

我们将使用 LangChain 构建一个 RAG 应用,该应用能够读取 Lilian Weng 的技术博客,并回答关于“AI Agent”的相关问题。

2.1 环境准备

请确保安装了以下核心依赖库:

pip install langchain langchain-chroma langchain-openai bs4

2.2 核心代码实现

以下是完整的源码。我们使用 ChatOpenAI 适配 DeepSeek 模型,使用 Chroma 作为本地向量数据库。

import bs4
from langchain import hub
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import ChatOpenAI
import os

# 1. 初始化 LLM (DeepSeek)
# DeepSeek 兼容 OpenAI SDK,只需配置 base_url 和 api_key
llm = ChatOpenAI(
    model_name="deepseek-chat",
    temperature=0.7,
    max_tokens=2000,
    api_key=os.environ["DS_AI_API_KEY"],
    base_url="https://api.deepseek.com/v1", 
)

# 2. 索引流程:加载 -> 分割 -> 向量化存储

# A. 加载 (Load): 使用 WebBaseLoader 抓取网页
# bs_kwargs 用于过滤网页噪音(如侧边栏、页脚),只保留正文
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()

print(">>> 加载的网页内容预览:")
print(docs[0].page_content[:500])
print("-" * 20)

# B. 分割 (Split): 使用递归字符分割器
# chunk_size=1000: 每块约1000字符
# chunk_overlap=200: 块之间重叠200字符,保证上下文连贯性
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)

# C. 存储 (Store): 使用 Chroma 向量库和 OpenAI Embeddings
# 注意:实际生产中建议使用本地 Embedding 模型或专门的 Embedding API
vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())

# 3. 检索与生成流程

# 将向量库转换为检索器对象
retriever = vectorstore.as_retriever()

# 从 LangChain Hub 拉取标准的 RAG 提示词模板
# 模板内容大致为: "Answer the question based only on the following context: {context} \n Question: {question}"
prompt = hub.pull("rlm/rag-prompt")

def format_docs(docs):
    """将检索到的多个文档块拼接成一个字符串"""
    return "\n\n".join(doc.page_content for doc in docs)

# 构建 LCEL (LangChain Expression Language) 链
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# 4. 执行提问
question = "What is Task Decomposition?"
print(f">>> 提问: {question}")
response = rag_chain.invoke(question)
print(">>> 回答结果:")
print(response)
print("-" * 20)

# 5. 查看检索到的来源(调试用)
print(">>> 生成所依据的来源文档片段:")
docs = retriever.get_relevant_documents(question)
for i, document in enumerate(docs):
    print(f"[Source {i+1}]: {document.page_content[:200]}...\n")

2.3 执行结果分析

运行上述代码后,控制台将输出以下内容。我们可以清晰地看到 RAG 的工作流:先加载了原始文章,然后根据问题“什么是任务分解”,模型给出了精准的定义。

>>> 加载的网页内容预览:
      LLM Powered Autonomous Agents
    
Date: June 23, 2023  |  Estimated Reading Time: 31 min  |  Author: Lilian Weng


Building agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.
Agent System Overview#
In
--------------------
>>> 提问: What is Task Decomposition?
>>> 回答结果:
Task decomposition is the process of breaking down a complex task into smaller, manageable steps or subgoals. Techniques like Chain of Thought (CoT) and Tree of Thoughts (ToT) use prompting to guide models in decomposing tasks step-by-step. It can also involve human inputs, task-specific instructions, or external tools like classical planners.
--------------------
>>> 生成所依据的来源文档片段:
[Source 1]: Component One: Planning#
A complicated task usually involves many steps. An agent needs to know what they are and plan ahead.
Task Decomposition#
Chain of thought (CoT; Wei et al. 2022) has become a s...

[Source 2]: Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outl...

[Source 3]: Illustration of how HuggingGPT works. (Image source: Shen et al. 2023)

The system comprises of 4 stages:
(1) Task planning: LLM works as the brain and parses the user requests into multiple tasks. Th...

[Source 4]: Resources:
1. Internet access for searches and information gathering.
2. Long Term memory management.
3. GPT-3.5 powered Agents for delegation of simple tasks.
4. File output.

Performance Evaluation:...

🔍 三、关键技术点解析

3.1 为什么需要 chunk_overlap

在代码中我们设置了 chunk_overlap=200

  • 问题:如果直接切分,可能会把一句话或一个完整的逻辑切断,导致 Chunk A 只有前半句,Chunk B 只有后半句。
  • 解决:重叠部分确保了语义的连续性。当检索器检索到边缘信息时,这部分重叠能让模型获得完整的上下文。

3.2 LCEL 链式调用

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

这是 LangChain 强大的表达式语言(LCEL):

  1. 并行处理:字典中的 contextquestion 是并行获取的。
    • context:调用 retriever 找文档 -> format_docs 拼成字符串。
    • questionRunnablePassthrough 直接传递用户的问题。
  2. Prompt 填充:将获取的上下文和问题填入 Prompt。
  3. 推理与解析:LLM 生成结果,StrOutputParser 将结果转为纯文本。

3.3 DeepSeek 的集成

LangChain 的灵活性在于其组件化。虽然我们使用的是 ChatOpenAI 类,但通过修改 base_url 指向 https://api.deepseek.com/v1,我们轻松地将后端大模型替换为了 DeepSeek,实现了更低成本、更高性能的生成体验。


本文演示了 RAG 应用最基础也最核心的形态:基于向量数据库的文档问答

  • 索引:让数据“入库”,变成机器可理解的向量。
  • 检索:让问题“寻址”,找到最相关的知识片段。
  • 生成:让模型“开卷考试”,基于参考资料生成准确答案。

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TracyCoder123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值