基于 LangChain1.0 构建 RAG Agent 智能检索问答系统(完整可运行代码+详细注释)

45 阅读8分钟

✨ 本文代码均为可直接运行的完整代码,基于LangChain最新版API编写,无过时语法,放心食用~

✅ 一、安装项目依赖包

# 安装所有核心依赖,一行命令完成配置
pip install langchain langchain-text-splitters langchain-community bs4 python-dotenv

✅ 二、初始化三大核心组件(核心配置)

构建RAG Agent的核心三要素:对话大模型文本嵌入模型向量存储器

1. 配置大语言模型(LLM)- 对话生成核心

# 导入对话模型及环境变量配置依赖
from langchain_openai import ChatOpenAI
import os
from dotenv import load_dotenv

# 加载.env文件中的环境变量(私密密钥管理,规范开发)
load_dotenv()

# 配置通义千问大模型(qwen-max)参数
model_name = "qwen-max"
api_key = os.getenv("DASHSCOPE_API_KEY")
base_url = os.getenv("DASHSCOPE_BASE_URL")

# 初始化聊天模型:低随机性保证回答严谨,适配知识类问答场景
model = ChatOpenAI(
    model=model_name,
    api_key=api_key,
    base_url=base_url,
    temperature=0.1,  # 温度值越低,回答越精准、确定性越强
)

2. 配置文本嵌入模型 - 文本向量化核心

# 导入HuggingFace嵌入模型
from langchain_huggingface import HuggingFaceEmbeddings

# 初始化嵌入模型,行业主流轻量级高质量选型
# 特点:文本表征能力强、适配中文+英文,首次运行自动缓存至本地,无需重复下载
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")

3. 配置向量存储器 - 向量数据存储核心

# 导入内存级向量存储库
from langchain_core.vectorstores import InMemoryVectorStore

# 初始化内存向量库,轻量化无部署,无需持久化磁盘,适合快速开发/演示场景
# 绑定已初始化的嵌入模型,实现文本-向量的自动转换与匹配
vector_store = InMemoryVectorStore(embeddings)

✅ 三、文档处理全流程:加载 → 分片 → 入库(构建检索知识库)

RAG核心前置步骤:将非结构化的网页文本,处理为结构化的向量知识库,共分3步完成,流程标准化且可复用

1. 文档加载:精准爬取网页核心内容

# 导入网页加载器及网页解析依赖
import bs4
from langchain_community.document_loaders import WebBaseLoader

# 网页解析规则:只提取指定class的核心内容(标题/头部/正文),过滤冗余HTML标签,提升数据质量
bs4_strainer = bs4.SoupStrainer(class_=("post-title", "post-header", "post-content"))

# 初始化网页加载器,指定爬取路径+解析规则
loader = WebBaseLoader(
    web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),
    bs_kwargs={"parse_only": bs4_strainer},
)

# 加载网页文档至上下文
docs = loader.load()

# 校验加载结果:确保成功加载1篇文档
assert len(docs) == 1
# 输出文档总字符数,直观查看文本体量
print(f"✅ 文档加载完成,总字符数:{len(docs[0].page_content)}")

2. 文档分片:最优策略切割文本,保留上下文关联

# 导入递归字符分割器
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 初始化文本分割器,采用行业最优的递归分割策略
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,        # 单个文本块最大字符数,适配嵌入模型输入长度限制
    chunk_overlap=200,      # 相邻文本块重叠字符数,避免关键信息被截断,保留上下文关联性
    add_start_index=True,   # 记录文本块在原始文档中的起始索引,便于溯源匹配内容
)

# 对加载的文档进行分片处理
all_splits = text_splitter.split_documents(docs)

# 输出分片结果
print(f"✅ 文档分片完成,原始文档切分为 {len(all_splits)} 个子文档")

3. 向量入库:将分片文本存入向量库,完成知识库构建

# 将所有文本分片存入向量存储器,自动完成「文本→向量」转换+存储
document_ids = vector_store.add_documents(documents=all_splits)

# 预览前3个文本块的唯一标识ID
print(f"✅ 向量入库完成,文本块ID预览:{document_ids[:3]}")

✅ 四、核心实现:构建 RAG Agent 检索问答智能体(两种主流方案)

RAG的核心价值:让大模型结合外部知识库回答问题,解决大模型「知识过时、事实性错误、领域知识不足」的痛点;

以下提供两种工业界主流的RAG Agent实现方案,按需选择即可,均为可直接运行的最优写法

✅ 方案一:工具调用型 RAG Agent(推荐)

核心逻辑

自定义检索工具,让大模型自主决策是否调用检索工具,并基于检索到的上下文生成答案,具备「工具调用思维」,适配复杂多轮问答/多步骤查询场景,灵活性拉满。

# 导入工具装饰器,封装自定义检索工具
from langchain.tools import tool

# 封装检索工具:返回检索到的上下文内容+原始文档对象,指定返回格式规范
@tool(response_format="content_and_artifact")
def retrieve_context(query: str):
    """核心检索工具:根据用户查询语句,从向量库中检索相关上下文信息,辅助回答问题"""
    # 相似度检索:返回匹配度最高的2条文本内容(k值可按需调整)
    retrieved_docs = vector_store.similarity_search(query, k=2)
    # 格式化拼接检索结果:带上元数据+文本内容,提升大模型理解效率
    serialized_context = "\n\n".join(
        (f"Source: {doc.metadata}\nContent: {doc.page_content}")
        for doc in retrieved_docs
    )
    # 返回格式化文本 + 原始文档,兼顾可读性与溯源性
    return serialized_context, retrieved_docs

# 构建工具调用型智能体
from langchain.agents import create_agent

# 配置智能体核心参数:绑定大模型+检索工具+系统提示词
tools = [retrieve_context]
# 系统提示词:明确智能体能力边界与行为准则,精准引导大模型调用工具
system_prompt = (
    "你是一个专业的问答助手,你可以调用检索工具获取外部知识库的上下文信息。\n"
    "请务必使用检索工具辅助回答用户的查询问题,确保答案的准确性和事实性。"
)
# 初始化工具调用型RAG Agent
agent = create_agent(model, tools, system_prompt=system_prompt)

✅ 测试:复杂多步骤查询(智能体自主调用工具)

# 测试提问:多步骤复杂查询,考验智能体的工具调用能力与逻辑推理能力
query = (
    "What is the standard method for Task Decomposition?\n\n"
    "Once you get the answer, look up common extensions of that method."
)

# 流式输出回答结果,实时查看智能体的思考+回答过程(体验更佳)
print("===== 工具调用型RAG Agent 回答结果 =====")
for event in agent.stream(
    {"messages": [{"role": "user", "content": query}]},
    stream_mode="values",
):
    event["messages"][-1].pretty_print()

✅ 方案二:上下文注入型 RAG Agent(极简高效)

核心逻辑

通过中间件(middleware) 实现「全自动检索+上下文注入」,无需显式定义工具,智能体会在回答前自动检索并将上下文注入到系统提示词中,全程无感调用,代码极简、运行高效,适配简单单轮问答场景。

# 构建上下文注入型智能体
from langchain.agents.middleware import dynamic_prompt, ModelRequest

# 定义动态提示词中间件:自动检索+注入上下文,无感知完成检索逻辑
@dynamic_prompt
def prompt_with_context(request: ModelRequest) -> str:
    """核心中间件:从请求中提取用户最新问题,检索相关上下文并注入系统提示词"""
    # 提取用户最新的查询语句
    last_query = request.state["messages"][-1].text
    # 自动执行相似度检索,获取相关上下文
    retrieved_docs = vector_store.similarity_search(last_query)
    # 格式化拼接检索到的文本内容
    docs_content = "\n\n".join(doc.page_content for doc in retrieved_docs)
    # 构造带上下文的系统提示词,让大模型基于外部知识回答
    system_message = (
        "你是一个专业且乐于助人的问答助手,请严格基于下方提供的上下文信息回答用户问题:\n\n"
        f"{docs_content}"
    )
    return system_message

# 初始化上下文注入型RAG Agent:无需传入工具,中间件自动完成检索逻辑
agent = create_agent(model, tools=[], middleware=[prompt_with_context])

✅ 测试:基础单轮查询(极简高效)

# 测试提问:基础单轮查询,验证上下文注入效果
query = "What is task decomposition?"

# 流式输出回答结果
print("\n===== 上下文注入型RAG Agent 回答结果 =====")
for step in agent.stream(
    {"messages": [{"role": "user", "content": query}]},
    stream_mode="values",
):
    step["messages"][-1].pretty_print()

✨ 核心知识点补充

✅ 两种Agent方案对比 & 选型建议

  1. 工具调用型(方案一) :适合「复杂查询、多轮问答、多步骤推理」场景,大模型自主决策是否调用工具,灵活性强,是工业界主流方案;
  2. 上下文注入型(方案二) :适合「简单单轮问答、快速开发」场景,代码量少、无感知检索,开发效率高,但灵活性稍弱。

✅ 核心优化点说明

  1. temperature=0.1:低温度值保证回答的精准性和确定性,适配知识类问答场景,避免生成无关内容;
  2. chunk_overlap=200:文本块重叠设计,彻底解决「关键信息被截断」的问题,提升检索召回率;
  3. 流式输出stream():实时返回回答内容,提升用户体验,避免长时间等待;
  4. InMemoryVectorStore:轻量化向量库,无需部署、无需持久化,适合快速开发和演示,生产环境可替换为Chroma/Pinecone等持久化向量库。

✨ 总结

本文完整实现了基于LangChain的RAG Agent智能检索问答系统,从「依赖安装→组件配置→文档处理→智能体构建→测试验证」全流程闭环,代码可直接复制运行,两种主流方案全覆盖,适配不同业务场景。RAG作为LLM落地的核心技术之一,该套代码可无缝迁移至PDF/文档/本地知识库等场景,实用性拉满。

代码仓库

gitee:gitee.com/o_insist/la…

github:github.com/o-insist/la…

🔥 点赞+收藏,后续持续更新LangChain/RAG/Agent相关实战教程,干货满满~