【LangGraph】实战:基于 LangGraph 实现的智能文档问答系统

【LangGraph】本文主要内容: LangGraph真正上手,以及做一些简单的案例

    • 前言:
    • [1. 项目概述](#1. 项目概述)
        • [1.1. 系统组件](#1.1. 系统组件)
    • [2. 系统实现步骤](#2. 系统实现步骤)
        • [2.1 数据加载与处理](#2.1 数据加载与处理)
    • 接下来,将文本转为向量并存入向量数据库:
        • [2.2. 创建检索工具](#2.2. 创建检索工具)
        • [2.3. 构建工作流程](#2.3. 构建工作流程)
          • [2.3.1. 决策节点](#2.3.1. 决策节点)
          • [2.3.2. 重写问题节点](#2.3.2. 重写问题节点)
          • [2.3.3. 答案生成节点](#2.3.3. 答案生成节点)
        • [2.4. 定义工作流图](#2.4. 定义工作流图)
    • [3. 流程执行与调试](#3. 流程执行与调试)
    • [4. 总结与展望](#4. 总结与展望)

上一章内容在这-> 【LangGraph】实战:支持搜索的智能代理系统(Agent + Tool)

前言:

在现代人工智能应用中,问答系统已经成为一个非常重要的方向。传统的生成式模型虽然具备一定的知识能力,但其知识来源受限于训练数据,存在"过时"或"幻觉"的问题

为了解决这一问题,检索增强生成(Retrieval-Augmented Generation,RAG)技术逐渐成为主流方案。通过将"检索"与"生成"结合,模型可以在回答问题时动态访问外部知识库,从而显著提升答案的准确性与可靠性

本文将基于 LangGraph,结合 LangChain 生态,构建一个代理式 RAG 文档问答系统,实现从文档检索到答案生成的完整闭环流程


1. 项目概述

本项目基于 LangGraph 框架构建

采用代理式 RAG(检索增强生成)架构

引入"决策 + 检索 + 重写 + 生成"的多节点流程

系统的核心思路是:

  • 用户输入问题
  • 系统判断是否需要检索知识库
  • 若需要,则检索相关文档片段
  • 对问题进行必要优化(Rewrite)
  • 最终结合上下文生成答案

1.1. 系统组件

这个智能文档问答系统由以下几个核心组件组成:

  • 模型(ChatOpenai/ChatTongyi):用于生成回答的语言模型
  • 嵌入模型(OllamaEmbeddings):用于将文档内容转换为向量表示
  • 文档加载与处理(UnstructuredMarkdownLoader):用于加载和处理 Markdown 格式的文档
  • 向量存储(InMemoryVectorStore):用于存储和管理文档的向量表示,支持快速检索
  • 文本分割器(RecursiveCharacterTextSplitter):用于将文档内容切分为适合模型处理的小块
  • LangGraph:用于构建和管理工作流,包括节点和条件边

从整体来看,这是一套典型的:

复制代码
数据层(文档) → 向量层(Embedding) → 检索层(Retriever) → 决策层(Graph Agent)

2. 系统实现步骤

2.1 数据加载与处理

首先,定义数据加载流程,将原始文档转换为模型可处理的格式

本项目的数据源为 Markdown 文件,使用 UnstructuredMarkdownLoader 加载,并用 RecursiveCharacterTextSplitter 进行切分

python 复制代码
 # 加载所有 .md 文件
docs = [UnstructuredMarkdownLoader(path).load() for path in paths]

 #展平多层列表
docs_list = [item for sublist in docs for item in sublist]

# 定义文本分割器
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    encoding_name="cl100k_base", chunk_size=1000, chunk_overlap=50
)

# 切分文档
doc_splits = text_splitter.split_documents(docs_list)
接下来,使用 OllamaEmbeddings 将文档片段转化为向量,并存储在内存向量库中,以便后续快速检索。


# 使用内存向量存储
vectorstore = InMemoryVectorStore.from_documents(
    documents=doc_splits,
    embedding=embeddings,
)

这里的关键点在于:

  • chunk_size:控制每段文本长度
  • chunk_overlap:保证上下文连续性

接下来,将文本转为向量并存入向量数据库:

2.2. 创建检索工具

在构建问答系统时,检索工具的作用非常关键。系统需要从知识库中检索与用户问题相关的文档片段

我们通过 LangChain 的 create_retriever_tool 函数,结合 InMemoryVectorStore 来创建一个检索工具

python 复制代码
# 创建检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})


 创建检索工具

```python
retriever_tool = create_retriever_tool(
    retriever,
    "own_retrieve",
    "搜索并返回项目信息."
)

这里的意义在于:

  • 将检索能力"工具化"
  • 允许 LLM 自主决定是否调用
  • 为后续 Agent 决策提供基础

2.3. 构建工作流程

相比传统 LangChain Chain,LangGraph 更强调:

复制代码
状态 + 节点 + 条件流转

整个系统可以抽象为一个"有向状态图"。

2.3.1. 决策节点

决策节点的作用是根据当前状态决定是否需要使用检索工具。若用户的问题需要更详细的上下文信息,则触发检索;否则,直接回答用户的问题

python 复制代码
def generate_query_or_respond(state: MessagesState):
    """根据当前状态决定使用检索工具或直接响应用户"""
    result = model.bind_tools([retriever_tool]).invoke(state["messages"])
    return {
        "messages": [result]
    }

该节点的作用:

  • 判断是否需要调用检索工具
  • 本质是一个"工具选择器"

2.3.2. 重写问题节点

有时候,用户提出的问题可能不够清晰或者有歧义

为了提高问答质量,我们可以添加一个重写问题的步骤,尝试优化用户的问题,明确其意图

python 复制代码
def rewrite_question(state: MessagesState):
    """重写原始问题,优化表达"""
    question = state["messages"][0]
    prompt = REWRITE_PROMPT.format(question=question)
    result = model.invoke([HumanMessage(content=prompt)])
    return {
        "messages": [HumanMessage(content=result.content)]
    }

2.3.3. 答案生成节点

在重写问题之后,系统会调用生成答案的节点,结合检索到的上下文信息来生成最终的回答

python 复制代码
def generate_answer(state: MessagesState):
    """生成最终的答案"""
    question = state["messages"][0].content
    context = state["messages"][-1].content
    prompt = GENERATE_PROMPT.format(question=question, context=context)
    result = model.invoke([HumanMessage(content=prompt)])
    return {
        "messages": [result]
    }

2.4. 定义工作流图

完成各个节点的定义后,我们将它们组合成一个完整的工作流图

图中的边表示不同节点之间的流转关系,条件边则表示在某些条件下节点的跳转

python 复制代码
# 定义工作流图
agent_builder = StateGraph(MessagesState)

# 添加节点
agent_builder.add_node(generate_query_or_respond)
agent_builder.add_node("retrieve", retriever_node)
agent_builder.add_node(rewrite_question)
agent_builder.add_node(generate_answer)

# 添加普通边
agent_builder.add_edge(START, "generate_query_or_respond")

# 添加条件边
agent_builder.add_conditional_edges(
    "generate_query_or_respond", tools_condition, {"tools": "retrieve", "__end__": END}
)
agent_builder.add_conditional_edges(
    "retrieve", grade_documents, ["generate_answer", "rewrite_question"]
)

# 编译工作流图
graph = agent_builder.compile()

整个流程可以总结为:

复制代码
用户问题 → 是否检索 → 检索 → 质量判断 →(通过/重写)→ 生成答案

3. 流程执行与调试

完成图的定义后,系统可以流式执行,并根据用户输入生成对应的回答

我们可以模拟一个用户输入并查看系统的响应

css 复制代码
# 流式输出


for chunk in graph.stream(
    {"messages": [HumanMessage(content="技术栈都有什么")]}
):
    print(chunk)

LangGraph 支持流式执行,可以清晰看到:

  • 每一步节点执行情况
  • 状态如何变化
  • Agent 决策路径

4. 总结与展望

本文基于 LangGraph 实现了一个完整的代理式 RAG 文档问答系统,相比传统 RAG,具备以下优势:

  • 引入"决策能力",不再固定流程
  • 支持问题重写,提高检索质量
  • 增加结果评分机制,提升可靠性
  • 使用图结构,使流程更加清晰可控

从本质上看,该系统已经从"简单的问答系统"演进为"具备感知、决策与执行能力的轻量级智能体(Agent)


ok今天的分享先到这了,下期再见~

相关推荐
久违 °2 小时前
【AI-Agent】LangSmith 使用之Prompt(二)
人工智能·prompt
噜噜噜阿鲁~2 小时前
python学习笔记 | 6.3、函数-函数的参数
笔记·python·学习
AI算法沐枫2 小时前
计算机视觉需要哪些数学基础?常见问题全解析
人工智能·深度学习·线性代数·计算机视觉·自然语言处理
花千树-0102 小时前
LangGraph 与 ReAct Agent 调试技巧:从日志到可视化全解析
langchain·react·function call·ai agent·langgraph·mcp·j-langchain
2301_813599552 小时前
持久化存储如何适配不同浏览器?解决隐私模式下存储失败的指南
jvm·数据库·python
xiaotao1312 小时前
03-深度学习基础:RAG检索增强生成
人工智能·深度学习·rag·检索增强生成
2501_914245932 小时前
SQL如何高效提取大表前几行:分页查询与OFFSET优化
jvm·数据库·python
Ulyanov2 小时前
《玩转QT Designer Studio:从设计到实战》 QT Designer Studio状态机深度应用:智能待办事项管理系统
开发语言·python·qt·gui·雷达电子对抗系统仿真
AC赳赳老秦2 小时前
OpenClaw界面错乱、闪退问题,一键修复教程(附工具)
人工智能·python·职场和发展·django·tornado·deepseek·openclaw