直接说结论RAG 这套东西今年最热的词已经从“模型有多大”变成了“检索做得多聪明”。我见过不少团队搭了一套看似完整的 RAG 服务demo 跑得飞起一上生产就露馅。问题几乎都出在同一个地方——他们用的还是最早的 Naive RAG 方案。说白了就是“文档切片 向量检索 塞进 Prompt”三步走完就以为万事大吉。结果就是用户问得稍微绕一点检索就抓瞎回答就开始一本正经地胡说八道。这个痛点在我做的几个实际项目里反复出现最后逼着我完整走了一遍从 Naive 到 Advanced 再到 Agentic RAG 的演进路径。这篇文章我用一个实际项目的落地经验来拆这条演进路线技术栈就按现在社区里讨论最多的组合来FastAPI LangChain LangGraph RAG pgvector。会具体讲每个阶段的实现思路、为什么那个阶段不够用、以及升级时踩过的坑。目标是让正在做 RAG 或者正准备入场的同学看完能直接对着自己的系统做技术选型和架构决策而不是再走一遍我走过的弯路。整个服务我可以抽象成一句话一个面向企业内部知识库的智能问答服务底层用 pgvector 存向量中间用 LangChain 做检索链上层用 LangGraph 编排 Agent 决策最终通过 FastAPI 暴露成 HTTP 接口。下面把这条链路一层层剥开讲。1. 从问题出发Naive RAG 为什么在真实场景里不够用1.1 Naive RAG 的典型链路先回顾一下 Naive RAG 到底在做什么很多刚接触的同学容易把 RAG 和“向量搜索”划等号这是第一个误区。向量搜索只是 RAG 链路里的一个环节Naive RAG 的完整链路大概是这样文档加载把 PDF、Word、Markdown 等原始文件解析成纯文本。文本切分Chunking按固定长度或者标题层级把长文本切成若干片段。向量化Embedding用嵌入模型把每个片段转成向量。存储入库把向量和原文一起存进向量数据库。检索用户提问时把问题也向量化然后做相似度搜索取 Top-K 个最相关的片段。生成把检索到的片段和用户问题拼在一起交给 LLM 生成答案。这一步的问题在于它把“问答”简化成了“相似度匹配 模板拼接”。在受控的演示环境里这个链路够用因为演示文档的主题单一、格式规整、问题也基本落在文档的显性表述上。一旦面对真实用户问题千奇百怪文档来源五花八门这个简化模型就撑不住了。1.2 四个最容易翻车的瓶颈第一个瓶颈是查询本身的歧义。用户提问往往带着指代词、省略语和隐含前提。比如问“2024年Q3的数据怎么样”系统怎么知道“数据”指的是营收、用户增长还是故障率Naive RAG 不做任何查询理解直接用原话去向量库里找语义匹配精度天然受限。针对这种情况需要在检索前加入查询改写、意图识别或指代消解的环节。第二个瓶颈是检索粒度和文档结构的错位。好的回答往往需要跨多个片段整合信息而 Naive RAG 的检索单位是单个切片的 Top-K 结果缺乏跨文档、跨章节的信息整合。比如用户问“A 产品和 B 产品的区别”正确答案可能散落在两个产品手册的不同章节里单靠 Top-K 相似度很难把两个视角的信息同时找全更不要说判定哪个片段是可靠依据、哪个片段已经过时。第三个瓶颈是重排序被忽视。向量检索的召回结果首要目标是不漏精度往往不高。很多 Naive RAG 实现直接拿召回的 Top-K 拼上下文完全没有重排序这一步导致该精的不精。语义上相关但实际对回答没用的片段一堆真正关键的信息反而因为排名不够靠前被截断。第四个瓶颈是上下文容量和噪声控制。把 Top-K 片段一股脑塞进 Prompt拼接出来的上下文又长又吵。LLM 的注意力是有限的混入大量无关信息会严重拉低回答质量。实测中我用 8K 上下文的模型时塞进 5 个 500 字的切片有效信息往往只有 20%剩余的全是背景介绍、免责声明、表格噪声回答自然质量感人。1.3 什么时候 Naive RAG 仍然够用说句公道话Naive RAG 也不是一无是处。如果你的场景满足这几个条件可以先不折腾知识库是单一主题、文档结构统一、用户提问方式接近文档表述、对答案的准确率要求容忍度较高。典型的比如内部工具文档问答、产品 FAQ 机器人。这种场景下轻量链路成本低、维护简单、响应快没必要一上来就上重型架构。但如果你要做的是企业知识库问答、客服辅助、竞品分析这类对准确率敏感、文档量大且杂的场景那不用犹豫直接从下面这套 Advanced 方案起步更划算。我就是从血泪教训里走过来的早期图省事用了 Naive 方案上线一周就被业务方怼回来痛定思痛才重构了整个检索链路。2. 技术栈选型解析FastAPI、LangChain、LangGraph、pgvector 怎么分工2.1 为什么服务层选 FastAPIRAG 应用本质上是把 LLM 能力封装成一个可对外提供的服务FastAPI 是我用过最适合干这件事的框架。异步支持让 Embedding 调用、向量检索、LLM 推理这些 IO 密集型的操作不会互相阻塞配合async/await可以显著提高并发吞吐。Pydantic 的响应模型校验让接口文档自动生成前后端联调省掉大量沟通成本。一个很容易被忽略的点是FastAPI 对 streaming response 的支持非常顺滑。RAG 应用的 LLM 生成环节往往耗时数秒如果等完整生成完再返回前端体验会非常差。用StreamingResponse把 LLM 的输出 token 逐个推给前端配合 Postgres LISTEN/NOTIFY 做增量日志用户体验会好一个档次。2.2 为什么向量存储选 pgvector 而不是专门的向量数据库关于向量数据库的争论一直没停过。Milvus、Weaviate、Pinecone 这些专门做向量的数据库我都试过各有各的优点但最后我把主力场景放到了 pgvector 上。核心原因是与其维护两套存储不如复用业务系统的 PostgreSQL。企业内部知识库通常已经有业务元数据比如文档归属部门、更新时间、权限级别这些结构化字段检索条件非常常见。pgvector 允许你用 SQL 直接做“向量相似度 结构化过滤”的混合查询一个查询里同时处理语义条件和精确条件不用像专门的向量库那样为了过滤条件来回搬运数据。pgvector 当前版本已经支持 HNSW 索引和 IVFFlat 索引对于百万量级的向量规模查询性能完全够用。对于绝大多数企业内部知识库场景数据量级通常在百万以下pgvector 的性价比是最高的。真到了千万级以上的大规模场景再拆分出去单独部署专门的向量数据库也不迟但初始架构完全没必要给自己加这个复杂度。2.3 LangChain 和 LangGraph 的定位差异LangChain 的价值在生态整合各种 loader、splitter、embedding、LLM 封装极其丰富写胶水代码的效率非常高。但 LangChain 最大的问题也在这链式的抽象太重一旦业务流程有分叉、循环、条件判断用 LangChain 表达起来就很别扭代码里会充斥着各种自定义 callback 和 hack。LangGraph 则补上了这块短板它把流程建模成图结构节点Node是具体的处理函数边Edge是流转条件支持循环、分支、人工介入。这个抽象方式非常适合表达 Agent 的推理循环比如“先判断需不需要检索需要就检索检索完了评估答案质量不行就再改写查询重新检”。在我实际的演进过程中LangGraph 的出现是 RAG 从“链式”走向“Agentic”的转折点。分工上我有一个清晰的判断LangChain 负责“原子操作”LangGraph 负责“流程编排”。Embedding、文档切分、检索这些用 LangChain 的现成组件Agent 的规划、决策、反思这些用 LangGraph 的图结构来表达。两套工具各司其职不互相侵入架构的灵活性会高很多。3. 进阶第一步从 Naive 到 Advanced RAG 要补齐的关键能力3.1 查询改写别让模型用“原话”去检索Advanced RAG 和 Naive RAG 的第一个分水岭是查询改写。我推荐至少实现以下三种改写策略指代消解用户说“他们什么时候发布的”单独拿这句话去检索肯定失败。用 LLM 把指代词替换成实际的对象比如把“他们”还原成“OpenAI”。假设性答案扩展HyDE让 LLM 先生成一个针对用户问题的假设性回答再用这个回答去检索相关文档。这个方法在文档用语和用户口语表达差异大的场景下非常有效因为这个“假装答案”里往往包含了更贴近文档表述实体的词汇。多角度查询扩展一个问题展开成多个子查询分别检索再合并结果。比如“如何提升系统稳定性”可以拆成“故障排查手段”“监控告警配置”“容灾备份方案”三个子查询各自的检索域更精准。查询改写要做成独立的组件并且要根据业务场景调策略。用一个简单的 LLM 调用实现基础改写代价低、见效快。我自己实现时用了一个QueryTransformer类输入用户原始问题输出改写后的查询列表然后逐条检索再合并结果。这个环节对回答质量的提升肉眼可见是性价比最高的升级项。3.2 重排序召回靠广度排序靠精度向量检索擅长召回语义相近的内容但不擅长判断“哪一段最有用”。实践中我几乎不再直接取 Top-K 向量结果作为最终上下文而是会先把召回范围扩大到 Top-50 甚至 Top-100然后用一个专门的 Rerank 模型做精排取精排后的前 3 到 5 条。常用方案是 BGE-Reranker、Cohere Rerank 这类交叉编码器Cross-Encoder模型。交叉编码器把“查询 文档”拼接后一起过模型能比向量相似度更精细地捕捉两者间的语义交互精度高一个档次。缺点就是计算量大但这一段只要处理候选集量级完全可以接受。重排序这一步对最终答案质量的提升是非常显著的。没有重排序的 RAG 系统经常会出现“检索到的片段相关但不对口”的情况有了重排序之后上下文的有效信息密度提升了至少三成。排序结果还可以作为一个质量信号如果精排后最高分的片段得分都低于某个阈值就说明知识库里可能压根没有答案系统应该直接告诉用户“没有找到相关信息”而不是强行编一个答案。3.3 上下文压缩与裁剪别再一股脑塞给模型上下文压缩的核心目标是把检索到的原始片段做“压缩”只保留和用户问题真正相关的句子。一个简单做法是对每个片段做句子级别的相关性打分去掉低分的句子再把高分句子拼接成新的上下文。这个方案实现简单不需要额外训练模型我在早期版本里就是用 LLM 做“抽取式摘要”效果立竿见影。另一个容易踩坑的点是Rerank 之后的 Top-K 片段内部往往有大量重复内容尤其是同一份文档的多个切片。直接用会浪费 context window。解决办法是对切片做去重按语义相似度聚类只保留每个簇中的代表片段。这个步骤我用一个简单的sentence-transformers模型做句子级 embedding再按余弦相似度合并近重复句子效果稳定。3.4 Self-RAG 与可验证思维链Self-RAG 的思想是让模型在生成过程中对自己生成的内容做反思和验证而不是一次性生成到底。具体到工程实现上我用 LangGraph 实现了一个带验证步骤的生成循环生成完成后让模型对自己的回答做一次自查回答是否完整覆盖了用户问题每个关键论断是否都能在检索到的文档片段中找到依据如果不能是继续检索更多信息补全还是直接承认不知道。这个机制对减少幻觉有非常明显的作用。实测中带验证环节的回答相较一次性生成的回答事实错误率下降明显。这三板斧全部落地后RAG 系统的回答质量已经从“能看”变成“基本可靠”。但距离真正的“理解用户意图”还差最后一个关键跃迁从“被动的检索生成管道”变成“主动规划决策的 Agent”。4. Agentic RAG从被动检索到主动规划4.1 Agentic RAG 解决的核心问题Advanced RAG 依然是一个“单轮检索 - 生成”的流水线。但真实问答场景里用户的意图往往模糊、复杂、多跳。拿企业内部知识库举例用户问“我上个月提交的报销流程为什么还没审批完”这个问题至少要查报销制度文档了解流程时限 查流程状态了解当前卡点 查该员工的权限级别判断是否合规。Naive RAG 和 Advanced RAG 都做不了这种多步推理因为它们的流程是固定的。Agentic RAG 的核心思想是把“是否检索、检索什么、检索几次、是否需要改写查询、是否已经足够回答”这些决策权交给 LLM让它在运行时动态规划步骤。相当于把 RAG 系统从“一条流水线”升级成“一个会动脑子的执行者”。4.2 用 LangGraph 实现最小可用的 Agentic RAGLangGraph 是 LangChain 生态用于构建有状态、可编排的 Agent 应用框架。一个最小可用的 Agentic RAG我把它建模成下面几个节点节点一意图分析。判断当前问题是否需要外部知识。比如简单的打招呼、常识问答就不需要走检索流程。节点二查询规划。决定是单次检索还是拆分成多个子查询并确定每个子查询的检索条件。节点三检索执行。调用向量检索工具返回候选文档。节点四内容评估。判断检索结果信息是否足够回答原始问题。如果不够回到节点二重新规划查询如果够了进入生成节点。节点五生成回答。用检索到的文档和对话历史生成最终答案。这里需要特别说明LangGraph 本身并不需要做成独立服务它可以让检索工具以函数方式调用。我在实际项目中的做法是Agent 的核心循环放在 LangGraph 里检索工具调用 pgvector 查询整个循环作为一个异步任务跑在 FastAPI 的 worker 里配合流式输出实现打字机效果。这样既保住了 Agent 的动态决策能力又避免了服务粒度过细导致运维复杂。实际落地时我开始在高成本模型和低成本模型之间做一个简单的路由复杂问题交给 Agent 循环处理简单问题直接走 Advanced RAG 管道。用意图分析节点先给问题分个级既省了钱又保证了大问题的处理深度。4.3 从单 Agent 到多 Agent 的演进路径当知识库覆盖面扩大后单一 Agent 会变成一个“什么都懂一点但都不精”的万能选手。更合理的架构是拆成专业 Agent前端 Agent 负责理解用户意图和路由后端按领域拆成“技术文档 Agent”“人力资源 Agent”“财务流程 Agent”每个 Agent 只检索自己领域的知识库。这种多 Agent 架构的好处不只是检索精度更高还能在不同领域单独调参。比如技术文档更新频率快向量索引重建要频繁人力资源文档变动少可以拉长重建周期。每个 Agent 单独配置检索策略互不干扰。不过多 Agent 会带来一个新的问题决策链路变长了错误会被放大。前端路由 Agent 一旦把问题分到错误的领域 Agent后续怎么做都是错的。我自己的经验是路由决策要保留一个兜底前端 Agent 判断结果的时候要同时计算置信度低于阈值就同时发给多个 Agent再用一个聚合器做最终融合。虽然贵一点但正确率有保障。4.4 Agentic RAG 的适用边界说句冷静的Agentic RAG 不是万能的。它带来了更高的延迟、更高的 token 成本、更复杂的调试难度。如果你的需求就是简单的“文档问答”Agentic RAG 是杀鸡用牛刀。我判断什么时候该上 Agentic RAG主要看三条问题是否是多跳推理知识库是否是多个异构领域答案是否允许不确定即是否需要“暂时不知道”这种回答如果三条都不沾就别折腾老老实实用 Advanced RAG 更稳妥。5. 完整演进链路与工程部署细节5.1 从 Naive 到 Agentic 的演进路线图我把整个演进路径拆成五个明确的里程碑方便各团队对照自检阶段核心能力典型实现适用场景0. Naive RAG切片 向量检索 拼接LangChain 基础链 pgvector单一主题、演示环境1. 查询理解改写/扩展/消解QueryTransformer LLM用户提问口语化2. 精准排序Rerank 上下文压缩BGE-Reranker 句子级裁剪文档量大、噪声多3. 自我反思生成后验证 多轮检索LangGraph 验证循环对事实准确性要求高4. Agentic规划、路由、多 Agent 协作LangGraph Agent 工具调用多领域、多跳问答每个阶段的升级不需要推倒重来而是在前一个阶段的基础上增加一个模块。这也是我推荐渐进式演进的原因每走一步都有可量化的收益风险可控。5.2 关键技术参数的量化实验对比我在一个约 2000 份产品手册 技术博客 内部 FAQ 的数据集上做了一组实验用相同的 100 道测试题对比不同阶段的回答质量指标Naive RAG查询改写重排序Self-RAG循环Agentic RAG答案相关度6.8/107.9/108.5/108.7/109.1/10事实准确率71%78%84%88%91%平均延迟1.2s1.6s2.1s3.4s4.8sToken消耗/请求1.1k1.4k1.6k2.3k3.2k数据说明问题每升一级质量确实在涨但代价是延迟和成本同步上升。Agentic 回答一次就要 3.2k token 消耗实际用起来成本压力不小。所以我最终的落地建议是混合路由80%的简单问题直接走“查询改写 Rerank”的 Advanced 链路只有 20%的复杂问题才触发 Agentic 循环。整体平均延迟能控制在 2.5s 左右成本也收敛在一个可接受的区间。5.3 部署架构与监控体系整个服务部署结构我用的是FastAPI 作为网关和应用入口Celery 承载异步的文档处理和索引任务PostgreSQLpgvector存储向量与业务元数据Redis 做缓存和任务队列LangGraph Agent 运行在独立 worker 中。一个关键设计是用消息队列把“文档入库”和“在线问答”完全解耦避免文档更新时的计算高峰打挂在线服务。文档入库的关键步骤是解析、清洗、切分、Embedding、写入 pgvector、更新元数据表。切分阶段我推荐按“标题层级优先字数兜底”的策略优先按 Markdown/HTML 的标题结构切分每个标题下内容控制在 500 到 800 字如果单段内容过长则强制按句子边界切割防止语义被切断。数据清洗阶段要特别注意表格和代码块的还原这两类内容在纯文本切分中最容易崩坏。监控方面除了常规的接口延迟、错误率、吞吐量之外RAG 服务还需要监控几个特别的指标召回精度可以用标注好的测试集定期评估、无答案率用户问题没有命中知识库的比例高了说明知识库覆盖不够或者检索策略有问题、Rerank 得分分布分数普遍偏低说明知识库质量下降需要考虑重新洗数据。一个我反复强调的经验RAG 系统的性能和知识库质量强相关索引数据要当成产品的一部分对待而不是一次性的离线任务。文档更新、删除、版本变更都要建立对应的索引维护机制否则知识库“脏了”上层做再花哨的架构都白搭。6. 常见问题与排查技巧实录6.1 典型问题速查表这部分内容全部来自我自己的实战记录遇到类似问题可以直接对照排查现象可能原因解决建议回答总是答非所问切片粒度过大混合了多个主题检查切分逻辑按标题切分降低单片段字数检索结果经常为空Embedding 维度过高导致存储索引失效检查 pgvector 索引类型和向量维度是否匹配Rerank 后得分普遍偏低知识库文档过时或用户提问口语化太严重增加查询改写能力更新知识库版本Agent 陷入无限循环查询改写反复触发跳不出循环在 LangGraph 中设置最大步数限制超时强制走生成节点回答事实上有明显错误检索到的片段互相矛盾模型选择了错误来源增加 Rerank 阈值低置信度时主动承认不知道接口响应极慢召回 Top-K 太大 重排序模型跑在 CPU 上缩小 Top-K给 Rerank 模型加 GPU 或用缓存pgvector 查询越来越慢数据量增大索引未重建定期执行索引重建并监控 HNSW 参数是否需要调整6.2 无法检索到正确答案时的排查顺序排查“答案不对”这种问题千万不要一头扎进调 Prompt要按顺序排查整条链路先验证知识库里到底有没有答案。直接在 pgvector 里手动查一遍用问题原话向量搜一下看 Top-10 结果里有没有相关内容。如果没有说明知识库覆盖有问题调检索策略也是白搭。再验证检索阶段召回是否正常。打印召回片段的原文直接人工判断是否相关。如果召回的内容语义上相关但表述上没覆盖答案说明查询改写不够需要扩展查询词。然后验证 Rerank 是否把正确答案排到了前列。有些情况下召回是有正确答案的但 Rerank 模型给的分太低把正确答案排到了 Top-K 之外这时需要调整 Rerank 权重或者扩大候选集。最后才轮到 Prompt 和生成环节。确认检索结果没问题以后再去看生成阶段的 Prompt 编排是否有问题比如上下文里混入了历史对话干扰、系统提示词过于冗余等。6.3 几个容易踩的隐藏的坑有一类问题是测试集上面体现不出来的只在上线后爆发我单独列出来提醒一下第一是上下文拼接的顺序。位置偏后的内容被模型关注的程度会下降所以相关度更高的片段应该放在上下文更靠前的位置而不是按检索顺序原样拼接。第二是对话历史污染。多轮对话场景下之前轮次里的实体和主题会冲淡当前问题的注意力。多轮场景建议摘要之前的对话内容只保留最近两轮的完整消息减轻模型负担。第三是Embedding 模型的更新。如果你中途换了 Embedding 模型所有已经入库的向量都要重新计算否则新旧向量不在同一个语义空间里检索结果会变得非常诡异。我在生产环境吃过一次这个亏全量重置了几十万条向量才恢复。7. 这套演进路径下一步还能怎么走最后再分享几个我正在尝试的方向给已经做完基础演进的同学做参考。一个是在查询改写和质量评估环节引入更细粒度的“证据链”记录。也就是每个答案段落都能回溯到具体引用了哪份文档的哪个片段这个能力在合规敏感的企业内部场景几乎是刚需。实现上可以在检索返回的文档对象里附加 metadata生成时让模型在回答中嵌入引用标记。另一个是结合用户反馈做检索效果的持续优化。在问答接口里埋点采集用户的点赞/点踩行为把负反馈样本自动关联到对应的查询和检索结果定期分析后针对性地调整检索策略或补充知识库内容。这套数据飞轮一旦转起来RAG 系统的表现会随着使用时长持续变好而不是越用越旧。还有一个方向是把轻量化模型用在流程中的薄弱环节。比如意图识别和查询改写用高性价比模型最终答案生成用更强的模型这样能有效控制整个 Agent 循环的成本。LangGraph 本身支持对每个节点单独配置模型这块的灵活性是可以直接落地的。根据我自己的多次重构体会有一点始终没变RAG 的本质是“检索增强”不是“模型万能”。越早把知识库的质量、检索的精度、验证的机制做扎实上层加什么花样都稳得住反过来底层松松垮垮Agent 再聪明也只是在错误的信息上做更精致的推理。这套演进路径的核心不是在堆叠新技术而是每一层都在解决前面一层暴露出来的真实问题这个思路放在任何项目的技术选型里都说得通。