【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今天的分享先到这了,下期再见~

相关推荐
dFObBIMmai30 分钟前
SQL复杂数据聚合_嵌套子查询与GROUP BY配合
jvm·数据库·python
用户43305141438131 分钟前
流程控制与并行工作
人工智能
云天AI实战派32 分钟前
ChatGPT/API 调用故障排查指南:Realtime 音频、智能体浏览器操作与 AI 编码代理全流程修复手册
人工智能·chatgpt·音视频
水上冰石35 分钟前
怎么查看olama是否用到了显卡加速
人工智能·显卡
码点滴38 分钟前
用自然语言指挥 K8s 集群:AI 运维 Agent 的架构原理与可运行原型
运维·人工智能·kubernetes
Wanderer X40 分钟前
【LLM】PPO
人工智能
霍夫曼vx_helloworld735241 分钟前
字符提取与字符识别
图像处理·人工智能·计算机视觉
小许同学记录成长41 分钟前
基于幅度形态与参数聚类的工作模式判别
python·算法·scikit-learn
Wang60742 分钟前
浅尝claude code记忆系统
人工智能
dinglu1030DL1 小时前
CSS Grid布局如何实现网格项目排序_使用order属性改变显示顺序
jvm·数据库·python