做RAG智能体最典型的死法是这样的:Demo跑通了,演示效果惊艳,一放到真实业务里就翻车——用户问的问题查不到,答案看着专业其实是编的,多轮对话三句之后就开始胡说。我过去复盘了大量类似项目,结论都指向同一件事:问题几乎不在大模型选型,而在于RAG链路和智能体编排没有被当成一个完整系统来设计。这篇文章就是一套RAG智能体全栈开发的完整技术体系归档,从数据工程、检索链路、智能体编排、框架选型、评测体系到生产避坑,一条线拉通。适合三类人:正在从零搭建RAG知识库的开发者、准备把RAG接入业务系统的后端工程师、需要做技术方案评审的负责人。这份归档的价值是长期可查,项目立项和排障时反复翻阅。
1. RAG与智能体如何咬合成一个完整系统:从知识边界说起
1.1 LLM的知识边界是RAG存在的根本理由
大语言模型本质上是一个参数化知识库,它知道的全部内容都固化在训练时的权重里。这意味着两件事:第一,它有知识截止日期,训练完之后新发生的事它一概不知;第二,它的知识是通用的,你企业内部那些制度文档、项目周报、合同条款、技术规范,它根本没机会见过。
有人会想,那我把所有文档都塞进上下文里让它读不就行了?超长上下文确实一直在变长,但实际工程里不能这么干。一是成本随token数线性上涨,二是相关性稀释,塞进去的内容越多,模型越难从噪音里抓住重点。更关键的是权限问题,一个租户或一个部门的数据不应该被另一个部门的人通过模型调用看到,你如果把整个知识库都倒进上下文,权限边界就形同虚设。
一个贴切的类比:人类专家并不是脑子里装着所有资料才成为专家的,而是掌握了检索、阅读、理解、判断这些方法论,遇到问题知道去哪查、怎么查、查到之后怎么用。RAG就是给LLM安装这个"查阅外部资料"的能力。它把知识从模型参数中解放出来,放到一个可持续更新、可精细控制权限的外部索引里,让模型的每一次回答都基于最新、最相关的证据。
1.2 朴素RAG:检索一次、拼装一次、生成一次
最朴素的RAG流程就是一条直线:用户问题进来,用embedding模型把问题变成向量,去向量库做相似度检索,取出最相近的Top-K个文本块,把它们拼进prompt,再让LLM基于这些资料生成答案。
这个流程能解决基础问题,但三道坎它绕不过去。
第一,它只会检索一次。用户问"今年Q3营收和去年同期相比变化如何",朴素RAG很可能只检索到今年的数据,根本不会想到需要再找去年的数据来对比,最后答案里只有半边信息。
第二,它不判断检索结果好不好。向量相似度是一个数值,但数值高不等于内容真的有用。如果Top-K里全是无关文本,LLM会硬着头皮基于这些垃圾编答案,或者老老实实说"资料里没有",而不会想"我换个说法重新查一次"。
第三,它无法选择数据源。一个稍微复杂点的企业知识库往往有多个子库,技术文档、销售手册、制度流程分开存。朴素RAG从头到尾只在一个库里找,跨库检索基本靠撞运气。
这三道坎决定了朴素RAG适合做单轮问答、FAQ应答、固定知识库查询这类简单场景,但撑不起真正的业务复杂问题。
1.3 从RAG到Agentic RAG:检索从"步骤"变成"工具"
Agentic RAG是这两年智能体开发里非常重要的演进方向。它改变的不是检索算法本身,而是把"检索"这个动作从一条固定流水线上的步骤,升级成智能体可以自由调用的工具。LLM不再被动地"被安排去检索",而是自己判断:这个问题需不需要查资料?需要的话查哪个库?查一次不够就查两次,结果不满意就换个查询语句再查。
业界一般把Agentic RAG分成几个递进层级。路由式RAG负责在多个数据源之间做意图判断,比如用户问"今年的年假政策",它知道该去人力资源库而不是技术库查。规划式RAG把复杂问题拆成多个子步骤,每一步可能对应一次独立检索。反思式RAG在生成答案之后会做一个自我校验,如果发现证据不足或者答案与资料矛盾,会触发新一轮检索来修正。
下面这个对比能看得更清楚:
| 维度 | 朴素RAG | Agentic RAG |
|---|---|---|
| 决策单元 | 无LLM决策,固定流程 | LLM自主判断检索策略 |
| 检索次数 | 通常1次 | 可多次,根据结果动态调整 |
| 数据源选择 | 单一固定库 | 可路由到多个库 |
| 自我纠错 | 不判断结果好坏 | 生成后校验,低置信度触发再检索 |
| 适用场景 | FAQ、单文档问答 | 多库查询、对比分析、复杂业务推理 |
一句话概括:RAG负责给模型提供"资料",智能体负责决定资料"怎么用、用几次、不够怎么办"。两者咬合在一起,才是今年行业里一直提的RAG智能体的完整形态。
1.4 为什么这轮行业热点是"全栈开发"
既然RAG智能体是"资料+策略+模型"三件事的结合,那就必然涉及一整条技术栈:数据怎么清洗解析、文本怎么切分、向量怎么存储、检索怎么做、重排怎么做、Agent怎么编排、效果怎么评估、线上怎么观测。任何一个环节掉链子,整个系统都会跟着失灵。
这也是为什么很多团队觉得RAG"做起来不难,做好太难"——单独看每个环节都有现成教程,但把它们串成一个能扛住真实业务流量的系统,需要的是全栈视野。下面开始拆这条链路的每一层。
2. 全栈架构地图:从文档入库到答案生成的五层链路
2.1 五层总览:每一层干什么、用什么、看什么指标
在动手写代码之前,脑子里必须先有一张完整的分层地图。我把一套RAG智能体系统拆成五层,每一层职责独立、可单独测试、可单独替换。
| 层次 | 核心职责 | 代表工具/组件 | 关键指标 |
|---|---|---|---|
| 数据接入层 | 文档解析、内容清洗、格式归一 | Unstructured、PyMuPDF、PaddleOCR | 解析成功率、清洗耗时 |
| 索引存储层 | 文本分块、向量化、向量存储 | LangChain、LlamaIndex、FAISS、Milvus、pgvector、Chroma | 入库耗时、存储成本、向量维度 |
| 检索增强层 | 多路召回、重排、查询改写 | BM25、Elasticsearch、bge-reranker、CrossEncoder | hit rate、MRR、nDCG |
| 智能体编排层 | 路由、规划、记忆、反思 | LangGraph、Dify、自研Agent框架 | 任务完成率、平均检索次数 |
| 应用与评测层 | API服务、流式输出、评测、观测 | RAGAS、LangSmith、FastAPI | 端到端延迟、忠实性、可用性 |
这五层不是简单的先后顺序,而是一个可以反复迭代的闭环。比如检索层指标不行,你可能会回溯到索引层去换分块策略,甚至回溯到数据接入层去重新解析文档。各层解耦最大的好处就在这里:每一层都能独立做A/B测试,不然出了问题你根本不知道该调谁。
2.2 一次完整的数据流转:从入库到出答案
我把这条链路用纯文字走一遍,这样你脑子里会有清晰的流程感。
文档进入系统后,先经过数据接入层:PDF、Word、扫描件、Excel各走各的解析通道,把非结构化内容转成纯文本或Markdown,顺手做掉敏感信息脱敏和无关噪音剔除。然后进入索引存储层:清洗后的文本按策略切成固定大小的块,每个块调用embedding模型转成向量,向量连同原文和元数据一起写入向量库。
用户提问时,请求先到智能体编排层。智能体判断这个问题是应该直接回答、查知识库还是调用外部工具。如果决定走RAG,它会做一次query改写,把口语化问题转成更适合检索的表达,然后触发检索增强层——通常是BM25和向量检索双路召回,把两路结果合并后送进重排模型,从中挑出最相关的几个文本块。最后这些文本块连同用户的原始问题、必要的对话上下文,一起拼进prompt交给LLM生成答案。生成结果还可能过一道反思节点,检查证据是否充分,再决定直接输出还是重新检索。
这一步走完,你就知道一个RAG智能体项目该分成几个模块来规划了。后面三章分别对应这条链路的三个最关键的战场:数据工程、检索链路、智能体编排。
2.3 分层设计最容易踩的坑
分层听起来简单,实际落地时最常见的错误是"层内过度耦合"。我见过一个项目,解析文档时顺便把chunk大小也定死了,后面想调整分块策略只能回去重新解析全部文档,白白浪费大量时间。正确做法是每一层的输出都做成标准中间格式:解析层输出统一Markdown文本,索引层只管把文本变成向量,检索层只管把query变成命中结果。这样任何一层升级,其他层完全不用动。
第二个常见错误是跳层排障。上线后用户说检索结果变差了,运维直接去调embedding模型,结果查了半天发现是上游加了一批新文档,解析清洗环节出了问题,没有到索引层就已经污染了。我现在的习惯是排障时必须从上往下逐层验证,先看解析产物,再看向量库内容,最后才看检索参数,一层一层缩小范围。
3. 数据工程是隐形胜负手:解析、分块与索引结构
3.1 文档解析:PDF表格和扫描件是重灾区
很多RAG项目在原型阶段用的是纯文本Markdown,一切都很美好。一上真实数据就崩了,因为真实文档长这样:扫描版PDF需要OCR、Excel里塞着合并单元格、PPT的文字分散在文本框里、合同条款是半结构化段落。解析环节不过关,后面所有环节都会被拖累。
我的建议是分文件类型定方案。文字型PDF用PyMuPDF这类库直接抽文本,速度快、保真度高;扫描件必须接入OCR,PaddleOCR这类中文识别效果已经比较成熟;表格类PDF先用版面分析转成Markdown表格,因为LLM对结构化表格的理解远好过一行乱序文字。
举一个我实际见过的案例:某团队把合同PDF直接按文本流切块,表格里的内容在转换后完全错位,用户问"2024年的预算金额"时,向量库里的相关内容是碎成渣的乱码,检索命中率自然惨不忍睹。所以解析完一定要留一道质检环节——随机抽一批文档,人工看一眼转换结果,确认文本顺序、表格结构、特殊符号都没问题再进索引。这一步看着土,但比之后花一周调检索参数有效得多。
3.2 分块策略选择:没有万能参数,只有合适策略
文本分块是RAG里既基础又容易翻车的地方。块太大,单个块里噪音多,检索精度被稀释,而且超出embedding模型的有效长度后信息会打折;块太小,单个块装不下完整语义,检索到的内容经常是半句话,答案拼不出来。
几种主流策略我用一张表整理清楚:
| 分块策略 | 原理 | 适用场景 | 典型参数 |
|---|---|---|---|
| 固定大小切分 | 按token数硬切,设置overlap避免语义断裂 | 日志、新闻正文等结构化不强的文本 | chunk_size 300-500,overlap 30-50 |
| 递归字符切分 | 按段落、句子、字符的优先级逐级切分 | 通用企业文档,第一版最佳起点 | chunk_size 500,overlap 50 |
| 语义分块 | 用embedding相似度在语义断点处切分 | 主题切换明显的长文,如产品手册 | 相似度阈值按数据分布调 |
| 父子分块 | 检索命中小子块,返回其所属父级完整文档 | 需要大背景上下文的问答 | 子块200-300,父块1000-2000 |
| 小chunk大窗口 | 检索用小chunk,生成时取小chunk所在完整章节 | 兼顾检索精度和上下文完整度 | 检索chunk 200,窗口带完整段落 |
我的实战建议很朴素:第一版先用递归字符切分,chunk_size 500、overlap 50跑通全链路,之后再用评测数据判断问题方向。如果发现检索结果"像但不够全",往大块方向调;如果发现"不够像",往小块方向调。一次只改一个变量,不要同时动分块又动embedding,否则指标变化了你根本不知道是谁的功劳。
3.3 索引结构增强:GraphRAG与Ontology RAG解决知识割裂
普通RAG有一个天然短板:它把所有知识都切成了碎片,碎片与碎片之间的关联信息在切分时就丢掉了。我称之为"知识割裂"。最典型的表现是跨文档问题,比如"这套产品的历史版本里,哪些版本不支持IPv6?"——这个答案分散在若干个版本的发布说明里,单文档检索只会给你个别零碎句子,拼不出一份完整回答。
GraphRAG就是针对这个问题的解法。它用LLM把文档里的实体和关系抽取出来,构建成一张知识图谱,再把图谱按社区结构聚类生成摘要索引。用户问的是全局性问题时,走的不是文档碎片检索,而是图谱社区摘要检索。这样一来,"知识割裂"就被图谱结构补上了,答案不再是碎片拼凑,而是有逻辑脉络的归纳。
Ontology RAG则更侧重语义约束。本体是一套概念和关系的显式定义,比如"苹果(公司)"和"苹果(水果)"在业务库里是两个不同的实体。有了本体约束,检索时就不会把词面相同但语义不同的内容混为一谈。这在家居、医疗、法务这类术语歧义严重的行业特别有用。
不过我必须泼一盆冷水:GraphRAG和Ontology RAG的构建成本都不低,实体抽取、关系建模、本体维护都需要持续投入。80%的业务场景,混合检索加一个重排就够用了。先别追求技术堆砌,你的数据规模和问题复杂度到了那一步,再上图谱和本体不迟。
3.4 元数据与权限隔离:检索之前必须过滤
RAG智能体一旦接入企业业务,权限就是一条不能让步的红线。正确的做法是把权限过滤推进到检索阶段,而不是生成阶段。每个文本块入库时,metadata里带上tenant_id、所属部门、文档密级、生效时间;用户查询时,检索请求直接附加过滤条件,让向量库只返回对当前用户可见的内容。
千万别靠prompt让LLM"注意不要泄露他人信息",这既慢又不可靠。检索里没过滤的内容,一旦被召回,LLM是有可能把它拼进回答里的。权限必须在数据源头就切死。
更新策略同样不能省。源文档有改动时,需要按source_id做upsert覆盖;文档下架时,要同步从向量库里删除对应向量。常被忽略的是"已删除文件在检索里仍然出现"这类事故,根因就是向量库和源库不同步。这属于生产事故级别的问题,需要在设计文档阶段就安排好同步机制。
4. 检索链路实录:从向量召回、混合检索到重排
4.1 Embedding模型选型:中文场景的务实建议
embedding是整个检索链路的地基。选型要先看你的语言场景。中文业务场景里,BGE系列(比如bge-m3)是绕不开的选择,它对中文语义的理解成熟度高,还支持稠密向量和稀疏向量混合输出,后面接混合检索很方便。如果是英文为主、中文为辅,OpenAI的text-embedding-3-small性价比不错,量级不大时large也值得考虑;如果是纯内部中文知识库,bge-m3我实测下来命中率普遍更好。
维度方面,bge-m3输出1024维,text-embedding-3-small是1536维,大模型主打的3-large是3072维。维度越高能编码的信息越丰富,但存储成本和检索延迟也跟着涨。绝大多数业务场景1024维已经够用,不要盲目追高。
一个容易被忽略的细节:query和文档必须用同一个embedding模型,而且要保证文本预处理逻辑一致——文档里的签名、页眉页脚、特殊编码先去干净再转向量,否则你会看到一批漂移的低质量向量一直拖累检索结果。
4.2 混合检索:BM25和向量各管一段
纯向量检索有一个经典翻车场景:用户拿产品型号、政策文号、合同编号这些精确值来查。向量检索擅长语义相似,对这种词面完全一致的精确匹配反而没有优势,因为embedding把它泛化到了语义空间,细微的编号差异就会被忽略。而BM25这类稀疏检索,在精确术语匹配上表现反而稳定。
所以我的标准方案是混合检索:BM25负责精确匹配,向量负责语义兜底,两路结果按权重合并取并集。以LangChain为例,可以用EnsembleRetriever把两个retriever包起来,权重先按0.3/0.7给到一个初始值,之后通过评测调整。
具体到代码层面长这样(以LangChain 0.3系为例):
from langchain.retrievers import EnsembleRetriever from langchain_community.retrievers import BM25Retriever bm25_retriever = BM25Retriever.from_documents(chunks, k=50) vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 50}) hybrid_retriever = EnsembleRetriever( retrievers=[bm25_retriever, vector_retriever], weights=[0.3, 0.7] ) candidates = hybrid_retriever.invoke(query)这里面有一个操作细节:两路retriever的k都要设得偏大,比如50,因为这只是召回阶段,后面还有重排来精筛。召回阶段的目标是"宁可多捞,不要漏掉",精度交给下一步。
4.3 重排:把Top 50缩成Top 5的必要一步
重排是经常被新手跳过但效果提升最明显的一步。原理很简单:向量检索和BM25用的是双塔结构,query和文档各自编码成向量再算相似度,速度快但精度有限;重排模型用的是交叉编码器,把query和文档拼在一起输入模型做深度交互,精度高但速度慢。
工程上的经典组合是"双塔召回 + 交叉重排":先用便宜的召回模型把候选从Top 50放大到Top 100,用交叉编码器对候选集逐个打分,最后留Top 3到Top 5进prompt。一句话,召回负责圈定范围,重排负责精准定位。
代码片段参考:
from sentence_transformers import CrossEncoder reranker = CrossEncoder("BAAI/bge-reranker-base") pairs = [(query, doc.page_content) for doc in candidates] scores = reranker.predict(pairs) top_docs = [doc for _, doc in sorted(zip(scores, candidates), reverse=True)[:5]]重排我踩过一个坑:明明加了reranker,效果却没有提升。后来排查发现是召回阶段只取了Top 5,相关文档根本不在候选集合里,重排再准也没用。所以顺序一定不能错,先扩大召回范围,再精排。
4.4 查询改写与HyDE:别把用户的原话直接丢进检索
用户提问往往是口语化的,直接拿去和文档做向量匹配,效果通常一般。比如用户问"今年三季度的数发给我们了吗",文档里写的是"2024年第三季度数据交付状态报告",两者的词面重叠度很低。正确的做法是先让LLM做一次query改写,把口语转成适合文档检索的表达,比如转成"2024年第三季度数据交付状态"。
HyDE是另一个实用技巧:让LLM先根据问题生成一个假设答案,再用这个假设答案的embedding去做检索。因为假设答案在语义上更接近目标文档的表述方式,有时候能明显提升召回率。但它也不是万能的,如果假设答案本身方向偏了,检索结果会跟着偏,所以需要做小批量实验确认。
还有一个容易忽略的点:多条件复杂问题要先拆解再检索。"去年到今年预算变化的原因"这个问题,需要拆成三个子查询——去年预算文档、今年预算文档、变更说明文档。这一步应该在query改写阶段就完成,而不是靠后面生成阶段硬猜。
5. 智能体编排层:路由、规划、记忆与反思
5.1 路由:决定一次请求走哪条路
RAG智能体不是所有问题都该走检索。一个"什么是增值税"的常识问题直接回答最快,一个"查询所有服务器IP"的问题应该调CMDB接口,只有企业内部知识问答才需要走RAG。智能体编排层的第一个动作,就是做意图路由。
路由的实现方式可以很简单:用一个prompt让LLM判断请求类型,也可以专门训练一个小模型做分类,根据你的并发和成本要求来。判断依据一般包括:问题里有没有明确的私域知识关键词、是否需要实时数据、是否要求调用业务系统。我建议路由规则里一定要有"不确定就走RAG"的兜底策略,宁可多查也不错答。
给一个路由判断的prompt思路参考:你是一个请求分类器,输入用户问题,输出三类标签(DIRECT_ANSWER / KNOWLEDGE_RETRIEVAL / TOOL_CALL),同时输出置信度。低于阈值的自动归入KNOWLEDGE_RETRIEVAL,让检索链路去兜底。
5.2 规划与多跳检索:复杂问题的拆解能力
普通RAG只能处理单跳问题,也就是"回答里需要的所有信息都藏在同一个文本块里"。但真实业务里大量问题都是多跳的。比如"对比去年和今年的预算差异并说明原因",这个回答需要的信息分散在至少三份文档里,单次检索根本凑不齐。
Agentic RAG的规划层会把这类问题拆成子目标序列:先查去年预算文档,再查今年预算文档,然后查中间的变更记录,最后把三次检索到的证据汇总交给LLM生成对比结论。每一步都是一次独立检索,且下一步要根据上一步的结果动态调整。
实现这种能力,业界主流是用LangGraph这类框架编排Agent的节点和边。每个节点是一个Action(检索、调用工具、生成),每条边是LLM判断后的转移条件。需要特别注意的是,多跳检索不是无限制跳,一定要设置最大步数,比如最多5步,防止Agent在检索链路上转圈烧token。
5.3 记忆管理:别把注意力全放在聊天历史上
记忆是智能体编排层的高频话题,但很多RAG项目误以为"记忆=聊天历史缓存",把注意力全放在怎么存对话上。我的看法是:在RAG场景里,工作记忆(当前任务里已经检索到并确认有用的证据)远比闲聊历史重要。
工作记忆的作用是防止重复检索和证据自相矛盾。一个多跳问题执行到第二步时,Agent应该记得第一步已经查到了什么,避免再查一遍,同时知道已有证据和当前问题之间的缺口在哪,决定下一步查什么。
聊天历史则是另一个维度。它需要分短期和长期:短期会话记忆用滑动窗口加摘要控制长度,防止上下文爆炸;长期事实记忆可以存进向量库,比如"用户上次提到他们团队正在做数据迁移",下次回答时可以把相关历史作为背景。实现原则很简单:能不放的不放,能压缩的绝不全文塞进prompt。
5.4 反思与自我修正:Agentic RAG的闭环
我在1.3节提到的反思式RAG,是让RAG智能体真正变智能的关键一步。它的循环流程是:检索、生成、自我评分,如果评分不合格,就触发再检索,修正答案后重新评分,直到通过或达到重试上限。
这和"让LLM道歉"不是一个概念。反思的核心是让模型评估证据是否足够——我拼出来的答案,每一条关键论断都能在检索到的文档里找到对应出处吗?如果找不到,说明检索没查到位,应该换一种query再查,或者换个数据源,或者老老实实告诉用户"资料里没有相关内容"。
用LangGraph实现这个闭环时,生成节点后面接一个反思判断节点,判断结果通过就直接输出,不通过就回到检索节点重新走一遍。我在项目里实测下来,这个机制对"答案看着合理但细节对不上"这类问题的改善非常明显。唯一要注意的是重试次数必须有限,别让它无限循环消耗成本。
5.5 多智能体协作:按需引入,别为噱头铺开
多智能体架构在复杂工单场景确实有用,比如一个Planner负责任务分解,若干Executor分头检索,一个Reviewer负责汇总验证。任务本身够复杂、数据源又分散时,这种分工能明显提升效率。
但多智能体也意味着多倍的工程复杂度:Agent之间要通信、要同步状态、要做超时重试、要处理部分失败。我的原则是:单个Agent能解决的事,坚决不上多智能体;先把单Agent做到极致,再在真正需要的地方引入第二个角色。很多团队一上来就铺多智能体,结果成本翻了几倍,效果反而因为协调开销而下降。
6. 框架选型与本地落地:LangChain、LlamaIndex、Dify与Ollama零基础方案
6.1 主流框架怎么选:先问团队属性和定制深度
RAG智能体开发框架选择,市面上消息很杂,我把主流几条路线按"谁适合用什么"整理出来。
| 框架 | 定位 | 适合人群 | 主要优势 | 常见坑 |
|---|---|---|---|---|
| LangChain/LangGraph | 编程式全流程编排 | 后端开发团队 | 集成多、可控性强、生态大 | API更新快,学完版本就换了 |
| LlamaIndex | 数据管道与索引强 | 数据工程背景团队 | 文档加载、索引策略丰富 | 生态相对LangChain窄 |
| LangChain4j | Java生态RAG方案 | Java技术栈团队 | 语言亲和、打包部署平滑 | 组件库比Python版少 |
| Dify | 可视化平台 | 业务侧快速交付 | 拖拽搭建、上线快、有现成Agent编排 | 深度定制场景受限 |
| 自研 | 完全可控 | 重度定制场景 | 无框架约束、细节可控 | 维护成本最高 |
我的选型经验是:先做一道选择题,你的团队是写代码多还是配置界面多?如果是代码团队,又有重度定制需求,LangGraph或LlamaIndex起步;如果业务方想快速验证,Dify这类可视化平台效率高得多。不少企业用的是混合路线——Dify搭基础流程,关键模块用自定义代码集成进去。这方案性价比往往最高。
6.2 零基础本地RAG知识库:Ollama加简易方案的完整步骤
如果你只是想在自己电脑上跑通一个本地知识库,不想依赖云端API,最省事的组合是Ollama加LangChain加Chroma。整个过程约半小时,我把步骤写全。
第一步,安装Ollama并拉取两个模型,一个推理模型,一个embedding模型,终端里执行:
ollama pull qwen2.5:7b ollama pull bge-m3第二步,创建Python虚拟环境并安装依赖:
pip install langchain langchain-community chromadb ollama pypdf第三步,写一个脚本,完成从文档加载到问答的完整流程。以一份PDF为例:
from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores import Chroma from langchain_community.llms import Ollama loader = PyPDFLoader("docs/sample.pdf") docs = loader.load() splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) chunks = splitter.split_documents(docs) embeddings = OllamaEmbeddings(model="bge-m3") vectorstore = Chroma.from_documents(chunks, embeddings, persist_directory="./chroma_db") retriever = vectorstore.as_retriever(search_kwargs={"k": 5}) question = "这份文档里关于数据备份的策略是什么?" contexts = retriever.invoke(question) context_text = "\n\n".join([c.page_content for c in contexts]) llm = Ollama(model="qwen2.5:7b") prompt = f"请基于以下资料回答问题:\n\n{context_text}\n\n问题:{question}" answer = llm(prompt) print(answer)这套代码跑通之后,你就有了一个完整的本地RAG最小系统。之后再往里面加混合检索、重排、Agent编排,都是在这个底盘上逐步扩展。
6.3 从本地脚本到企业级服务的工程化要点
本地脚本能跑通,离能上线还差一段工程化距离。我总结四点必须做的事。
第一是API服务化,把问答逻辑封装成FastAPI接口,对外提供HTTP调用,同时支持SSE或WebSocket流式输出,不然大模型回答要等好几秒才一次性吐出,用户等不住。
第二是缓存与限流。相同或高度相似的query,如果走缓存直接返回,能省下大量重复检索和模型调用成本。缓存的key要同时包含query和用户权限标识,避免不同权限的人读到同一份错误的缓存结果。限流也必须做,LLM调用成本高,不限制并发很可能一夜之间账单爆炸。
第三是可观测性。检索链路的每一步都值得打trace:query改写花了多久、BM25召回了多少、向量召回了多少、重排后选了几条、LLM生成耗时多少。出了问题,你必须有办法定位到具体是哪一层。
第四是安全和合规。向量库里存的是企业真实数据,接口层面需要有鉴权、限流、审计日志。文档入库前该做的敏感信息识别和脱敏,不能因为搭了RAG就跳过。
7. 评测与调优:没有指标体系的RAG项目走不远
7.1 检索质量指标:hit rate、MRR、nDCG的出处与含义
我见过太多RAG项目上线后完全靠"手感"调优,换了个embedding模型也不知道效果是变好还是变坏。这属于给自己挖坑。RAG的检索质量有三个基础指标,必须建立起来。
hit rate(召回命中率)是最直观的指标,它计算的是:在所有测试问题中,有多少比例的问题能在检索结果里至少找到一条相关文档。比如你准备100个测试问题,检索Top 5里包含相关文档的问题有80个,hit rate就是0.8。这个指标回答的是"系统到底能不能把该找的东西找回来"。
MRR(平均倒数排名)关心的是"相关文档排在第几位"。同样一题,相关文档排在第一位得1分,排在第二位得0.5分,排在第三位得0.33分,取所有问题的平均。两个系统hit rate可能都是0.9,但一个相关文档永远排第一,另一个永远排第四,MRR会如实反映差距——前者能省掉用户很多翻找时间。
nDCG(归一化折损累计增益)更进一步,它不只看是否命中,还看排序质量,并且对排位靠后下降的惩罚是渐进的。业务里如果要强调"相关文档位置越靠前越好",nDCG就是最合适的指标。
这三个指标从不同角度度量同一件事。我建议项目建设初期至少盯住hit rate,因为它直接反映最要命的"查不到"问题;进入调优阶段再看MRR和nDCG。
7.2 生成质量指标:忠实性、相关性、完整性
检索指标再好看,最终用户看到的是生成的答案。生成质量单独有一套评价维度。
忠实性(faithfulness)衡量的是答案内容是否都可以由检索到的资料支撑。这是对抗幻觉的核心指标,一条答案只要有一句论断在资料里找不到出处,就算不忠实。相关性(answer relevance)衡量的是答案是否回答了用户真正问的问题——有时候模型生成了一大段正确但跑题的内容,照样算失败。完整性(completeness)则看答案有没有覆盖用户问题的所有子项,比如用户问了原因和影响两个点,答案只说了一个,就不完整。
这三项指标都能用RAGAS这类开源评估框架自动计算。RAGAS里的faithfulness、answer_relevancy、context_precision、context_recall四个分数覆盖了我上面讲的内容。但自动化评估本质上依赖大模型打分,所以我会同时保留人工抽检机制:每个迭代周期抽20条真实问答,让人工判断质量,和自动分数做交叉验证。
7.3 构建评测集与自动化回归:把优化从手感变成科学
评测集的构建是整个评测体系的底座。我建议第一个版本先做50到100条golden set,每条包含三要素:一个真实用户问题、期望检索到的文档片段或标准答案、备注这属于哪类query(单跳、多跳、精确匹配、口语查询等)。
这个集成的价值有两个:一是覆盖高频问题,防止优化A问题搞坏了B问题;二是覆盖边界情况,比如权限隔离问题、知识更新问题、跨文档聚合问题。评测集建好后,每次改动——换embedding、改分块策略、调检索参数、改prompt——都跑一遍回归,对比指标变化。这才是"调优"的正确打开方式。没有评测集的所谓调优,基本是盲人摸象,把运气当能力。
7.4 调优路线图:先数据后模型再编排
RAG性能出问题时,不少人第一反应是换更强的大模型,这通常是最没必要的一步。我按优先级整理一条调优路线图,按顺序走。
先看数据工程:文档解析是否完整、分块策略是否合理、文本块是否干净。这一层是地基,解析出来的乱码谁也救不了。再看检索链路:embedding模型有没有选对、混合检索有没有做、重排加没加、top_k和权重设得对不对。随后看生成侧:prompt是否约束了忠实性要求、是否有引用来源。最后才看Agent编排:路由准不准、反思机制要不要加。
整个调优过程坚持一次只动一个变量。今天改取样策略,明天换embedding,后天调重排权重,但要确保每次只有一项变化装在评测集里跑,指标变化才能真正归因。顺便说一句,行业公开数据里,代码质量这类专业场景,已经能看到企业级智能体把缺陷召回率做到91%以上,这种数字背后靠的不是某一个炫技模块,而是整套评测驱动、层层调优的打磨。
8. 生产环境避坑清单:十个高频问题与2026年工程化方向
8.1 十个高频坑:现象、根因、对策
我直接把项目里反复见过的坑整理成一张排查表,遇到问题时先对着找。
| 现象 | 根因 | 对策 |
|---|---|---|
| 检索结果文不对题 | embedding和chunk不匹配数据特征 | 先测分块策略,再测embedding选型 |
| 答案看似专业实则编造 | 没有忠实性约束,也没有反思环节 | prompt加"只依据资料回答",引入反思评分 |
| 知识更新不及时 | 索引与源库不同步 | 建立Upsert和删除同步机制 |
| 权限内容越权返回 | 检索阶段未过滤权限 | 在metadata过滤条件里提前处理 |
| 高并发时延迟飙升 | 无缓存、无异步、检索链路串行 | 加query缓存,重排和外部调用并行化 |
| 中文精确检索翻车 | 没有BM25,或analyzer未做中文适配 | 混合检索,配置中文分词器 |
| 上下文过长导致质量下降 | chunk过大、聊天历史全文塞入 | 小chunk大窗口,历史改用摘要 |
| 改动后效果不可控 | 没有评测集和回归机制 | 搭建golden set,每次改动跑回归 |
| 文档内恶意指令被模型执行 | 缺乏prompt注入防护 | 检索内容与用户指令分离,增加注入检测 |
| 上线后问题定位无门 | 链路没埋点 | 检索全链路加trace和日志 |
这张表看起来都是老生常谈,但每个坑我都见过真实项目栽进去,而且一栽就是一两周。建议把它贴在团队wiki上,做方案评审时逐条对照。
8.2 从概念演示到工程化落地:2026年的三个方向
行业里现在越来越形成一个共识:2026年是智能体从概念演示走向工程化落地的分水岭。这不是说某天有个爆炸性技术出现,而是前两年的Demo潮沉淀下来的经验开始标准化了。我观察到三个明确方向。
第一个方向是Agentic RAG的闭环化。单轮问答已经被做透了,2026年的重点是让RAG智能体处理真正复杂的任务:多数据源联动、自我反思修正、可解释的证据链。简单说,就是从"问一句答一句"进化成"把一个活儿干完"。
第二个方向是数据治理和评测标准化。更多企业会意识到,RAG效果的天花板在数据质量,而不在模型能力。文档解析规范、分块参数标准、评测集建设、自动化回归流程,会逐渐变成RAG项目的标配而不是加分项。
第三个方向是多模态与实时知识。扫描件里的图表、产品图、音视频里的信息会越来越多地进入检索体系;知识更新的时效要求也会从"按天同步"推进到"分钟级实时"。这个方向对数据管道的压力会明显增大。
8.3 最后分享一点个人体会
做了几年RAG相关项目,我越来越确认一个判断:凡是把RAG智能体当成"调一个模型接口"的项目,基本都走不远;凡是把它当成一个系统工程来对待的项目,即使初期效果一般,也因为建立了评测和迭代机制,越做越稳。失败项目的共同点其实不是技术选型失误,而是没有人对整条链路指标负责——解析工程师只管解析,算法工程师只管embedding,后端工程师只管API,出了问题互相甩锅。
这份归档文档重点不在代码,而在帮你在脑袋里搭起这张全链路地图。项目立项时对着它明确每一层该谁负责、该看什么指标;方案评审时对着它检查有没有漏掉权限、评测、观测这些最容易翻车的环节;线上排障时按着分层思路一层层缩小问题范围。如果只记一句话,我建议记住:RAG智能体是全栈工程,不是模型调用。把这句话刻在项目文档的第一页,能帮你省下大半年的弯路。