从0开始学AI智能体开发框架LangGraph----知识检索节点模块(含python具体实现代码)

大家好,我最近在整理 LangGraph 搭建的问答工作流中的一些知识点。本章主要介绍LangGraph 实现的知识检索节点,它是 RAG 链路核心执行单元,负责完成查询处理、权限过滤、文档检索与结果封装,为下游答案评估、内容生成节点提供标准结构化数据。这部分很重要,下面我仔细结合代码讲解一下这里,后面会连续更新这个系列。

0 LangGraph介绍

LangGraph是在LangChain之上构建的模块,它主要是用于更好地创建循环图。在之前的内容里面我们一直在用 LangChain 写链、写 Agent,今天开始介绍LangGraph ,LangGraph 可以独立使用,但它也能与任何 LangChain 产品无缝集成,为开发人员提供构建智能体所需的全套工具。LangGraph生态如下

  • LangSmith --- 有助于智能体评估和可观察性。调试性能不佳的 LLM 应用程序运行、评估智能体轨迹、在生产环境中获得可见性,并随着时间的推移提高性能。
  • LangGraph 平台 --- 使用专为长时间运行、有状态工作流设计的部署平台,轻松部署和扩展智能体。在团队之间发现、重用、配置和共享智能体 --- 并在LangGraph Studio中通过可视化原型快速迭代。
  • LangChain -- 提供集成和可组合组件,以简化 LLM 应用程序开发。

理解 LangGraph 最好的方式是先搞清楚它的三个基本概念:状态、节点和边。

  1. State:一个共享数据结构,表示应用程序的当前快照。它可以是任何 Python 类型,但通常是 TypedDict 或 Pydantic BaseModel
  2. Nodes:编码代理逻辑的 Python 函数。它们接收当前的 State 作为输入,执行一些计算或副作用,并返回一个更新的 State
  3. Edges:Python 函数,根据当前的 State 决定接下来执行哪个 Node。它们可以是条件分支或固定转换。

一、标准 RAG 执行流程(扫盲)

本节点调用统一检索流水线,完整执行行业通用六大 RAG 流程:

  1. Query Rewrite(查询改写) :优化用户原始语句,修正口语化表达,统一语义,提升检索匹配精度
  2. ACL(Access Control List 访问控制列表) :依据用户身份,过滤用户无访问权限的知识库内容
  3. Hybrid Retrieval(混合检索) :结合稠密向量检索稀疏关键词检索,批量召回相关文档
  4. RRF(Reciprocal Rank Fusion 倒数排名融合) :整合多路检索结果,完成去重并统一排序权重
  5. Rerank(结果重排) :对初筛文档二次相似度打分,筛选高关联有效文档
  6. Grounded Answer(事实锚定回答) :仅依托知识库原文生成答案,有效减少大模型幻觉问题

数据流交互

  • 上游输入:从全局状态state读取query(用户查询语句)user(用户身份对象)
  • 下游输出:将检索数据写入全局状态,供给grader_node(答案评估节点)generate_node(答案生成节点) 调用
  • 兼容设计:同时输出新版结构化数据与项目旧版数据格式,(注意:适配项目架构迭代过渡期,无需改动原有业务调用逻辑)

二、版本兼容说明

我的旧链路是这样的:knowledge_rag_node → run_rag_chain → KnowledgeRetriever → knowledge_service.search() 旧链路的问题:

1. 只有 dense 向量检索,没有 sparse/BM25

  • dense(向量检索) :把"年假怎么休"变成一串数字(向量),去向量库里找最接近的文档。适合语义匹配,比如"怎么休"能匹配到"休假制度"。
  • sparse/BM25(关键词检索) :像百度搜索一样,直接匹配关键词。适合精确查询,比如查"PRJ-001"这种编码。
  • 旧链路的问题:如果用户问了一个带精确编码的问题,向量检索可能"理解"错意思,搜不到。新链路两路一起查,互相兜底。

2. 没有 RRF 融合

  • RRF:把 dense 搜到的结果和 sparse 搜到的结果合并成一份,去掉重复的。
  • 旧链路的问题:旧链路只有 dense 一路结果,不需要合并。但这也意味着如果 dense 漏了,没有 sparse 来补。

3. 没有 rerank(重排序)

  • rerank:第一次检索可能返回 50 条,rerank 用更精细的打分模型把这 50 条重新排一遍,把最相关的放前面。
  • 旧链路的问题:直接拿 Milvus 返回的相似度分数排序,可能把不相关的东西排前面。新链路多了一步精排。

4. 没有 query rewrite、没有 ACL

  • query rewrite:用户说"请问一下年假怎么休",rewrite 把它改成"年假 休假 流程",更适合检索。
  • ACL :按用户身份过滤文档。比如 li.wei(技术部)看不到财务部的内部文档。
  • 旧链路的问题:直接拿用户原话去搜,口语前缀干扰检索;所有人看到的东西一样,没有权限隔离。

5. 没有 citations,只有简单的 sources

  • 旧 sources{"source_file": "休假制度.txt", "page_num": 1, "snippet": "..."} ------ 信息少,前端只能显示"来自休假制度.txt"。
  • 新 citations{"doc_id": "doc-2", "chunk_id": "doc-2-c1", "section_path": "总则", ...} ------ 前端以后可以做"点击跳转到原文第3章第2节"。

新链路增加了citations(结构化更强,带 chunk_id/section_path)和 retrieval_debug(排障信息),节点接入项目新版统一检索方法 run_retrieval_pipeline ,调用后返回 KnowledgeAnswerPayload(统一检索结果实体) ,内置引用信息、检索追踪日志;为保障存量业务稳定运行,代码内部完成数据格式自动转换 ,兼容项目原有 draft_answersourcesretrieved_docs 传统业务字段。

三、完整业务源码

我的完整项目工作流文件目录如下:

后面会逐一更新,下面介绍本文的源码:

ini 复制代码
"""知识检索节点。

from __future__ import annotations
from langchain_core.documents import Document
from app.chains.rag_chain import run_retrieval_pipeline
from app.core.security import CurrentUser

async def knowledge_rag_node(state: dict) -> dict:
   # 用户发起的检索提问,CurrentUser类型用户对象,用于解析访问权限,无参数则启用公开访问策略,返回封装检索文档、基础答案、来源引用、检索日志的状态字典
    
    # 1.提取全局状态内的查询内容与用户信息
    query = state["query"]
    user = state.get("user")

    # 2.校验用户对象类型,非法格式自动降级为匿名公开访问
    if user is not None and not isinstance(user, CurrentUser):
        user = None

    # 3.异步调用统一RAG检索流水线,执行完整检索逻辑
    payload = await run_retrieval_pipeline(query, user=user)

    # 4.转换适配旧版来源数据格式
    sources = [
        {
            "doc_id": c.doc_id,
            "source_file": c.source_file,
            "page_num": 1,
            "department": "",
            "score": 0.0,
            "snippet": c.snippet,
        }
        for c in payload.citations
    ]

    # 5.转换适配LangChain标准Document文档格式
    retrieved_docs = [
        Document(
            page_content=c.snippet,
            metadata={
                "doc_id": c.doc_id,
                "source_file": c.source_file,
                "page_num": 1,
                "department": "",
                "doc_type": "",
                "score": 0.0,
            },
        )
        for c in payload.citations
    ]

    # 6.统一返回多格式检索结果至工作流状态
    return {
        "retrieved_docs": retrieved_docs,
        "draft_answer": payload.answer,
        "sources": sources,
        "citations": payload.citations,
        "retrieval_debug": payload.retrieval_debug,
    }

四、代码核心逻辑解析

1. 依赖导入释义

  • Document:LangChain 官方标准文档实体,统一项目知识库文本存储格式
  • run_retrieval_pipeline:项目封装通用检索执行方法,整合全部 RAG 业务逻辑
  • CurrentUser:项目自定义用户实体类,存储用户角色、访问范围等权限信息

2. 用户权限处理逻辑

内置用户实体强类型校验 ,(注意:仅识别规范CurrentUser对象);用户信息为空、格式异常时,自动关闭 ACL 权限过滤,仅检索公开无限制知识库内容,避免权限异常引发检索失败。

3. 异步执行特性

async 异步关键字定义,支持多用户请求并行处理,dddd。

4. 数据格式转换逻辑

payload(新版检索结果实体) 中读取引用数据,循环组装为项目旧版字典格式、LangChain 标准文档格式,实现新检索能力无缝对接存量业务。

五、全局状态返回字段说明

字段名 英文释义 数据用途 使用场景
retrieved_docs 检索完成文档集合 存储结构化知识库文本 传统答案生成节点读取文本素材
draft_answer 草稿答案 检索后生成的基础回答文本 快速输出简易问答结果
sources 来源信息列表 轻量化文档出处数据 旧版业务完成基础内容溯源
citations 引用数据源 标准化结构化引用信息 新版业务实现前端精准原文定位
retrieval_debug 检索调试日志 记录全流程检索运行信息 开发排错、线上检索效果排查

举个🌰

json 复制代码
{
  "draft_answer": "员工年假按累计工龄计算...",
  "sources": [
    {
      "doc_id": "doc-2",
      "source_file": "休假制度.txt",
      "page_num": 1,
      "department": "",
      "score": 0.0,
      "snippet": "年假休假制度说明"
    }
  ],
  "retrieved_docs": [Document对象],
  "citations": [
    {
      "doc_id": "doc-2",
      "chunk_id": "doc-2-c1",
      "source_file": "休假制度.txt",
      "section_path": "总则",
      "snippet": "年假休假制度说明"
    }
  ],
  "retrieval_debug": {...}
}

六、开发实操注意要点

  1. 核心检索逻辑统一收口,节点只做适配 业务不参与这部分
    所有检索规则(召回数量、排序权重、RRF 融合策略)全部封装在run_retrieval_pipeline中,
    knowledge_rag_node只做「参数透传 + 结果格式适配」。
    (注意:避免把检索逻辑散落在节点里,后期调优、改策略时,不用修改工作流节点代码,只维护公共流水线即可)
  2. 预留业务拓展字段
    retrieved_docssources中的page_numscore目前用默认值占位,没有直接绑定业务逻辑。(注意:后续如果要做「文档分页召回」或「相似度排序展示」,可以直接从payload的检索结果里读取真实值赋值,不用重构节点结构)
  3. 权限校验做「容错兜底
    节点内仅做CurrentUser类型校验,非法 / 异常用户直接降级为公开访问策略,不抛出异常打断流程。(注意:和上游auth_check_node的权限校验分层,核心权限过滤逻辑在run_retrieval_pipeline里执行,这里只做兜底,避免节点越权)
  4. 新旧字段并行输出,分批升级
    节点同时返回draft_answer/sources/retrieved_docs旧格式和citations/retrieval_debug新格式,下游grader_nodegenerate_node不用改代码就能对接新链路。(注意:可以分批升级下游节点,不用一次性全量改造,大幅降低线上发布风险)
相关推荐
亚空间仓鼠14 小时前
Docker容器化高可用架构部署方案(十三)
docker·容器·架构
闵孚龙14 小时前
AI Agent 构建实战:Claude Code 模式迁移、Rust 代码审查 Agent、六层架构与工程闭环全解析
人工智能·架构
vivo互联网技术15 小时前
VAPD AgentKit:可组合 Agent 前端通用库实践
前端·ai·架构·agent
400分15 小时前
LangChain 最新版 Zero/One/Few-Shot 提示词模板实战详解
架构
贵慜_Derek15 小时前
《从零实现 Agent 系统》连载 03|控制循环:感知—决策—行动—反思
人工智能·设计模式·架构
Duang15 小时前
我把 Claude、Codex、Copilot、Gemini 拼成了一个工作流,接力写代码
人工智能·程序员·架构
白露与泡影15 小时前
轻量级微服务发布系统:Traefik + Nomad + Consul
微服务·架构·consul
humcomm16 小时前
如何利用AI进行智能监控
人工智能·架构
踩着两条虫16 小时前
AI 低代码引擎可视化设计器交互机制实战
前端·vue.js·人工智能·低代码·架构