用LangGraph写一个小RAG
最小RAG的链式写法大致这样用户问题 - 检索资料 - 拼Prompt - 调模型回答用LangGraph把最小RAG拆成一张小图START ↓ retrieve_docs ├─ 有足够相关资料 - build_context - answer - END └─ 无足够相关资料 - fallback - END图结构接下里主要是看下graph里一般怎么接入rag实现不复杂。准备知识库和测试问题先准备一份很小的客服知识库包含退款、发票、会员续费、商品质量问题等内容然后测试三个问题1、未发货订单能退款吗 2、电子发票在哪里下载 3、积分可以提现吗预期效果应该是前两个问题应该能检索到资料并回答第三个问题不在知识库范围内应该走fallback。定义State和Runtime ContextState保存这次问答过程中产生的数据class RagDocument(TypedDict): source: str chunk_index: int start_index: int content: str similarity: float class RagState(TypedDict, totalFalse): question: str retrieved_docs: list[RagDocument] context: str route: Literal[answer, fallback] answer: str稍微解释下question是用户问题retrieved_docs是结构化检索结果context是给模型看的上下文这里后续会包含从向量哭里检索出来的文档片段route记录走了正常回答还是fallbackanswer保存最终回答。Runtime Context保存运行配置和外部依赖内容包括class RagContext(TypedDict): knowledge_base_path: str top_k: int min_similarity: float embedding_model: str answer_model: str ollama_base_url: str vectorstore: InMemoryVectorStoretop_k是每次检索最多返回几个相关片段min_similarity是最低相似度阈值低于这个分数的资料不进入回答流程embedding_model使用的Embedding模型vectorstore已经构建好的向量库对象用它根据用户问题检索相关资料两份存储的区别1、问答过程中产生的东西放State2、运行时需要的配置和依赖放Runtime Context启动时先建向量库启动时建一次向量库后面多个问题都可以复用写入到内存暂时不用本地存储def build_vectorstore( knowledge_base_path: str, embedding_model: str, ollama_base_url: str, ) - InMemoryVectorStore: docs load_support_docs(knowledge_base_path) embeddings OllamaEmbeddings( modelembedding_model, base_urlollama_base_url, ) return InMemoryVectorStore.from_documents(docs, embeddings)文本切分用RecursiveCharacterTextSplittersplitter RecursiveCharacterTextSplitter( chunk_size160, chunk_overlap30, separators[\n\n, \n, 。, , , ], add_start_indexTrue, ) docs splitter.split_documents(raw_docs)add_start_indexTrue能保留chunk在原文里的起始位置后面展示参考资料时有用。写检索节点retrieve_docs检索节点做的四件事读取问题、查询向量库、过滤低相关资料、写回State。def retrieve_docs(state: RagState, runtime: Runtime[RagContext]) - RagState: context runtime.context question get_question(state) results context[vectorstore].similarity_search_with_score( question, kcontext[top_k], ) retrieved_docs: list[RagDocument] [] for doc, similarity in results: if similarity context[min_similarity]: continue retrieved_docs.append( { source: Path(str(doc.metadata.get(source, unknown))).name, chunk_index: int(doc.metadata.get(chunk_index, -1)), start_index: int(doc.metadata.get(start_index, -1)), content: doc.page_content, similarity: similarity, } ) return {retrieved_docs: retrieved_docs}这里的min_similarity0.60只是当前知识库和embedding模型下的实验阈值不是通用标准。换知识库或模型后要重新观察和选择你要使用啥分数写条件边有资料才回答检索后不要直接回答先判断有没有足够相关资料def route_after_retrieve(state): if state.get(retrieved_docs): return build_context return fallback有资料才回答没资料就fallback这里也体现了langchain与langgraph环境下使用rag的实现区别。写build_context、answer和fallbackbuild_context把结构化检索结果整理成模型可读的上下文def build_context(state: RagState) - RagState: context_parts [] for index, doc in enumerate(state.get(retrieved_docs, []), start1): context_parts.append( f资料 {index}{doc[source]}#chunk-{doc[chunk_index]}\n f相似度{doc[similarity]:.3f}\n f起始位置{doc[start_index]}\n f正文{doc[content]} ) return {context: \n\n.join(context_parts), route: answer}answer只根据用户问题和检索上下文回答model ChatOllama( modelcontext[answer_model], base_urlcontext[ollama_base_url], temperature0, )但是Prompt里要写清楚只根据给定资料回答不要编造资料中没有的信息这个约束还是挺重要的不然模型会放飞自己。fallback不调用模型直接兜底就行。def fallback(state: RagState) - RagState: return { route: fallback, context: , answer: 当前知识库里没有找到足够相关的资料建议转人工客服或补充更多问题信息。, }RAG的底线没有依据时不要不懂装懂。把节点连成图图本身不大graph_builder StateGraph(RagState, context_schemaRagContext) graph_builder.add_node(retrieve_docs, retrieve_docs) graph_builder.add_node(build_context, build_context) graph_builder.add_node(answer, answer) graph_builder.add_node(fallback, fallback) graph_builder.add_edge(START, retrieve_docs) graph_builder.add_conditional_edges( retrieve_docs, route_after_retrieve, { build_context: build_context, fallback: fallback, }, ) graph_builder.add_edge(build_context, answer) graph_builder.add_edge(answer, END) graph_builder.add_edge(fallback, END) graph graph_builder.compile()到这里一个最小LangGraph RAG就搭好了。运行并观察三条路径启动时配置知识库、模型和向量库knowledge_base_path support_policy_retriever.txt embedding_model qwen3-embedding:latest answer_model qwen3-coder:30b ollama_base_url http://localhost:11434 vectorstore build_vectorstore( knowledge_base_pathknowledge_base_path, embedding_modelembedding_model, ollama_base_urlollama_base_url, ) runtime_context { knowledge_base_path: knowledge_base_path, top_k: 2, min_similarity: 0.60, embedding_model: embedding_model, answer_model: answer_model, ollama_base_url: ollama_base_url, vectorstore: vectorstore, }每次调用时初始State只放问题initial_state {”question”: question} final_state graph.invoke(initial_state, contextruntime_context)看下关键输出的效果问题 1未发货订单能退款吗 路径answer 最终回答未发货订单可以申请退款系统会自动拦截发货流程退款通常在1-3个工作日内原路退回。 参考资料[1] support_policy_retriever.txt#chunk-10(0.730); [2] support_policy_retriever.txt#chunk-2109(0.626) 问题 2电子发票在哪里下载 路径answer 最终回答电子发票可在订单详情页下载。进入”我的订单 - 订单详情 - 发票信息”查找发票入口。 参考资料[1] support_policy_retriever.txt#chunk-3208(0.732) 问题 3积分可以提现吗 路径fallback 最终回答当前知识库里没有找到足够相关的资料建议转人工客服或补充更多问题信息。 参考资料无基本符合预期。‘9. 五条核心边界1、State保存用户问题、检索结果、上下文和最终回答。2、Runtime Context保存知识库、模型、检索参数和向量库。3、向量库启动时构建一次不要每个问题重复建索引。4、检索节点只负责找资料。5、没有足够相关资料就fallback不让模型硬答。实验细节GitHub 仓库 https://github.com/yauld/ai-forge 完整实验文章 labs/langgraph/foundations/24 | LangGraph RAG把最小问答链路接入图.md 实验代码 labs/langgraph/foundations/experiments/24_minimal_rag_graph/学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

相关新闻

LangGraph 状态快照与回滚|Agent 故障自愈、流程回溯

LangGraph 状态快照与回滚|Agent 故障自愈、流程回溯

每执行完一个图节点,LangGraph 自动保存一份当前完整状态,就是快照。每条快照包含:全量状态、父快照 ID、时间戳、自定义业务元数据。 Checkpointer(存储器) 快照存放载体,分三类: MemorySav…

2026/9/20 13:21:12 阅读更多 →
HDMI视频寄存器配置实战:从同步时序到色彩空间转换详解

HDMI视频寄存器配置实战:从同步时序到色彩空间转换详解

1. 项目概述与核心价值在嵌入式视频系统开发,尤其是涉及HDMI、DisplayPort这类高速数字接口的领域,工程师们常常会遇到一个看似简单实则棘手的问题:如何让一块屏幕准确地、稳定地显示出我们想要的图像?这背后远不止是发送像素数据…

2026/9/23 13:41:04 阅读更多 →
TMS320C28x FPU64软件流水线优化:从延迟槽到指令级并行实战

TMS320C28x FPU64软件流水线优化:从延迟槽到指令级并行实战

1. 项目概述与核心价值在嵌入式数字信号处理器(DSP)的开发中,尤其是面向电机控制、数字电源、新能源逆变器等对实时性要求极高的领域,每一纳秒的指令执行时间都至关重要。TMS320C28x系列DSP,凭借其强大的定点运算能力和…

2026/9/23 21:44:49 阅读更多 →

最新新闻

Flutter数值映射库num_remap在鸿蒙开发中的应用与优化

Flutter数值映射库num_remap在鸿蒙开发中的应用与优化

1. Flutter 三方库 num_remap 鸿蒙适配实战指南在 OpenHarmony 生态中开发动态交互应用时,数值范围映射是个高频需求场景。无论是处理传感器数据、手势操作还是动画效果,都需要将原始数据转换为适合 UI 展示的数值范围。传统的手写映射代码不仅冗长难维护…

2026/9/24 0:01:25 阅读更多 →
Lss-bev IndexPut插件:前端高效索引操作实践

Lss-bev IndexPut插件:前端高效索引操作实践

1. 项目背景与核心价值Lss-bev系列插件作为现代前端工程化体系中的重要组成部分,其IndexPut模块的部署实践直接影响着数据索引操作的性能表现。在实际项目中,我们经常遇到需要高效处理大规模索引更新的场景,而传统方案往往面临以下痛点&#…

2026/9/24 0:01:25 阅读更多 →
JSP+JDBC+MySQL+Servlet图书管理系统实战:从源码部署到性能优化

JSP+JDBC+MySQL+Servlet图书管理系统实战:从源码部署到性能优化

简介:面向Java Web初学者,这份图书管理项目源码以图书信息增删改查为主线,完整整合了JSP、JDBC、MySQL与Servlet技术栈,演示了从页面展示、请求处理到数据库读写的基本路径,适合用来理解MVC分层与原生Web开发流程。压缩…

2026/9/24 0:01:25 阅读更多 →
Lombok与JDK版本冲突引发NoSuchFieldError:根因排查与修复指南

Lombok与JDK版本冲突引发NoSuchFieldError:根因排查与修复指南

如果你在某个平平无奇的下午执行mvn clean package,看到编译进度条卡在注解处理阶段,随之蹦出这么一行:java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field ...基本可以确认一件事&…

2026/9/24 0:01:25 阅读更多 →
面向对象综合训练:从图书管理系统掌握封装、继承与多态

面向对象综合训练:从图书管理系统掌握封装、继承与多态

面向对象学完语法之后,最尴尬的阶段就是“懂的都懂,一写就懵”。day09这个综合训练,说白了就是把前面封装、继承、多态、抽象这些概念,从“背概念”切换到“用概念”。这篇我把自己的练习过程完整拆开,从选题思路到代码…

2026/9/24 0:01:25 阅读更多 →
Windows系统安装全指南:从U盘启动盘制作到UEFI/GPT分区方案

Windows系统安装全指南:从U盘启动盘制作到UEFI/GPT分区方案

不管是给老电脑续命,还是给新装的机器做首次引导,Windows系统的安装都属于那种“看着简单,做起来全是细节”的活儿。我前前后后帮同事、朋友装了不下几十台机器,自己也因为手贱删错分区、改了引导方式导致安装失败过好多次&#x…

2026/9/24 0:00:20 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →