1. RAG到底是什么,为什么大家都在搞
最近半年,团队内部讨论最多的技术方向,除了大模型微调,就是RAG了。RAG全称Retrieval-Augmented Generation,检索增强生成。说白了,就是给大模型外挂一个知识库,在模型回答问题之前,先从知识库里检索出相关内容,再把这些内容作为上下文一起喂给模型,让模型基于这些检索到的信息来生成答案。
这套方案解决了一个非常实际的痛点:通用大模型虽然知识面广,但对特定领域、特定业务场景的问题,要么答非所问,要么一本正经地胡说八道。企业内部的规章制度、产品说明书、技术文档、客服话术,这些内容大模型根本没学过,硬问它就只能瞎编。RAG的思路很简单,不试图让模型记住你的私有知识,而是每次提问时把相关的知识片段找出来,放在模型的"眼前",让模型"看着资料回答"。这个思路本质上跟人查资料回答问题一模一样。
我接触RAG最早的场景,其实是帮一家公司做内部规章制度问答。几千页的员工手册、报销流程、行政规定,散落在各种Word和PDF里,员工问人事要看文档,人事翻半天。用RAG搭了一套问答系统之后,输入"出差住宿标准是多少",系统自动检索出对应的制度条款,给出带引用的答案。这个场景虽然简单,但把RAG的价值体现得很充分:资料不用重新整理,模型不用重新训练,直接挂上就能问答。后来做的项目多了,包括技术文档问答、售前方案问答、甚至法律合同条款问答,套路基本一致,但细节上的坑越踩越深。
这篇文章,我把从入门到优化的一整套实践经验整理出来,包括基础原理、环境搭建、核心流程、参数调优、效果评测、常见问题排查。内容偏实战,多数结论来自我用公开文档和开源工具做的模拟项目X,以及几个实际业务场景的落地复盘。如果你是刚接触RAG的开发者,按着步骤走一遍就能跑通基础链路;如果你已经在做RAG但效果不理想,直接跳到优化部分看,大概率能找到对应的问题解药。
2. RAG核心链路拆解与技术选型逻辑
2.1 一条完整链路里有哪些环节
RAG系统的标准流程,拆开来看其实就五个环节:文档加载、文本切片、向量化、向量检索、生成回答。前四个环节负责"找得准",最后一个环节负责"答得好"。想清楚这一点很重要,很多人一上来就调模型提示词,结果检索到的内容根本不对,模型再聪明也是巧妇难为无米之炊。
文档加载解决的是"把乱七八糟的格式变成纯文本",PDF要解析、Word要转换、HTML要抽正文。这个环节看起来没技术含量,实际上翻车率极高,尤其是扫描版PDF和复杂表格,解析出来全是乱码,后面所有环节都白费。文本切片解决的是"把长文档切成适合检索的片段",切太长定位不准,切太短语义不完整,切片粒度直接影响检索质量。向量化解决的是"把文本变成模型能算相似度的数字",通过嵌入模型把每个切片映射成一个高维向量,语义相近的文本向量距离也近。向量检索解决的是"从几万条切片里快速找到最相关的几条",本质上是一个最近邻搜索问题,靠向量数据库的索引来加速。最后一步,把检索到的切片拼到提示词里,交给大模型生成最终答案。
这五个环节,每个都有多种实现方案,不同方案选型组合出来的效果差异非常大。我见过有人用传统的关键词匹配做检索,遇到"内存不足"和"缓存满了怎么解决"这种语义相同但说法不同的query,效果就比较弱。向量检索的核心优势,就是能把"意思相近但字面不同"的内容也捞出来,这是RAG对比传统搜索引擎代的关键提升。
2.2 为什么是RAG而不是微调
很多刚接触这个领域的同学,第一个问题就是:既然要让模型学会我的业务知识,为什么不直接微调模型?
这里面的门道在于,微调和RAG解决的根本不是同一个问题。微调改变的是模型的参数,目标是让模型学会某种行为模式,比如"用简洁风格回答问题""遵循特定格式输出"。但模型的记忆容量是有限的,企图把几千份文档的知识全部灌进模型参数里,成本高、周期长、效果还不可控。而且业务文档天天在变,今天加一条新规定,微调过的模型明天就过时了,重新微调一次的成本是几天时间加几千块钱的算力。RAG不一样,文档更新了直接重新切片入库就行,分钟级生效,成本和风险都低得多。
另外RAG还有个隐形的优点:答案可溯源。微调模型回答问题时,你根本不知道它依据的是什么,出了错只能干瞪眼。RAG可以把引用原文附在答案后面,用户能看到"这个结论来自哪份文档的哪个章节",在合规要求高的场景里,这一点是刚需。我做过的一个合同条款问答项目,法务部门反复强调每个回答必须有原文出处,这种场景用微调根本无从下手。
当然RAG也不是万能的,如果你的目标是让模型掌握某种写作风格、学会特定推理方式,或者你只有少量高频更新的知识且对响应速度要求极高,那么微调甚至单独使用模型本身的能力可能更合适。实际项目中,RAG和微调常常配合使用:先微调让模型理解业务语言风格,再挂RAG补充实时知识,两者叠加效果最稳。
2.3 工具选型:向量数据库和LLM怎么挑
整个流程涉及的工具有不少,最核心的选型决策集中在两块:向量数据库和生成模型。
向量数据库这块,目前主流方案有开源自托管的FAISS、Chroma,还有云厂商托管的Milvus、pgvector等。我个人的经验是,刚起步做原型验证,用Chroma或者FAISS就够了,依赖轻、部署快、社区资料多。等数据量到了百万级、需要分布式和高并发,再考虑上Milvus或者云向量数据库。pgvector有个优势,如果你业务里本来就用PostgreSQL,加个扩展就能存向量,少维护一套组件,对中小团队很友好。选型时不要被benchmark上的数字迷惑,实测下来,几十万条数据的场景,FAISS的搜索速度完全够用,没必要一开始就把架构搞复杂。
生成模型的选择,核心看三点:效果、成本、部署方式。如果是企业内部工具,对数据安全敏感,可以用开源模型做私有化部署,比如基于Qwen系列或Llama系列的底座模型。如果只是搭个Demo,直接调闭源API最省事。我在模拟项目X里分别用开源模型和商业API各跑了一遍完整流程,结论是:基础问答场景差距不大,但在复杂多跳问答和长文本总结上,商用模型的理解能力确实领先一个档次。预算允许的情况下,业务落地优先商用API,持续跟踪开源模型的迭代,随时准备切换。
嵌入模型的选择同样关键。很多人在这一步偷懒,随便用一个通用嵌入模型凑合。实际上,嵌入模型决定的是"语义理解的天花板",如果你做的是中文场景,建议优先选中文语料训练过的嵌入模型,英文模型处理中文时,语义对齐的效果会有肉眼可见的差距。另外要注意,嵌入模型的输入长度是有限制的,通常是512个token,切片长度设计时要考虑到这个约束。
3. 知识库搭建全流程实操:从零到跑通
3.1 环境准备和基础依赖
开始动手之前,先把环境理清楚。我用的环境配置供参考:Python 3.10以上,一台8GB内存的普通开发机就够跑通全流程,不需要独立GPU。如果后面要本地跑生成模型,有显卡最好,没有就用CPU跑小模型或者直接调API。
基础依赖就三个:LangChain作为编排框架,一个向量数据库,一个嵌入模型。LangChain不是必须的,但用它能省掉不少胶水代码,尤其是文档加载器这部分,它内置了各种格式的解析器,省得自己造轮子。安装命令很简单:
pip install langchain langchain-community chromadb openai tiktoken pip install pymupdf pandas openpyxlpymupdf用来解析PDF,openpyxl用来解析Excel,pandas做数据处理,这几个是文档加载环节的常用件。如果你用FAISS替代Chroma,加一句pip install faiss-cpu就行。这里我多说一句,LangChain版本迭代很快,API经常变动,如果代码报AttributeError,大概率是版本接口变了,去查对应版本的文档比到处搜教程靠谱。
3.2 文档加载与清洗:最容易忽略的预处理环节
文档加载是整个链路里最枯燥却最关键的环节。我见过太多人把PDF直接丢给LangChain的PyPDFLoader就完事了,结果表格内容全乱、页眉页脚混入正文、扫描件全是乱码。预处理做不好,后面检索质量直接打折。
我的标准处理流程分四步。第一步,格式转换。所有文档统一转成纯文本或者Markdown,PDF用pymupdf提取文本和布局信息,扫描版PDF先做OCR再提取。第二步,清理噪声。去掉页眉、页脚、页码、目录重复条目,合并因为分页被切断的段落。第三步,伪标题处理。很多文档的小标题字体略大,提取出来就是普通文本行,需要根据上下文判断哪些是标题,在切片时保留标题层级信息,这对后续检索时的上下文理解很重要。第四步,统一编码。把全角半角、特殊符号统一规范化,避免同一个词因为编码差异被切开。
这一步踩过的坑我列几个典型的:Excel表格转成文本后,表头和表体如果不加分隔符,检索时会把两行内容黏在一起;Word文档中的图片注释和批注会被当成正文提取;PDF中的公式如果以图片形式存在,提取出来是空的。处理这些问题的核心原则只有一条:保证切片内容的语义完整性,宁可去掉一些冗余内容,不要让解析噪声污染后续流程。
3.3 切片策略:粒度与重叠的设计
切片是影响RAG检索效果最重要的单一因素之一,但偏偏是最容易被忽视的。切片的目标是让每个片段语义自洽:单看这一片能看懂,检索到它的时候它自己能说清楚一件事。切得太粗,一段超过2000字,里面混杂了好几个主题,检索一个细节问题时整段都命中,噪声太大;切得太细,每个片段只有一两句话,语义不完整,检索到了也不知道上下文。
切片策略上,我常用的基准参数是:chunk_size设为500到800个字符,chunk_overlap设为50到100个字符。重叠的作用是避免一个完整句子正好被切刀切断,核心实体(比如"报销流程")被拆到两片各自只剩一半,检索时两边都半死不活。重叠区域能保证被切断的内容在相邻切片中都有完整出现,无论是哪一片被检索到,信息都是完整的。
文档结构不同,切片方式也要跟着调整。Markdown格式的文档,可以按标题层级来切,每个二级标题下的内容作为一片,这样切出来的切片天然带有结构语义。纯文本合同类的文档,按段落和章节混合切。表格密集的文档,每个表格作为独立切片单元,表头和表体合并处理。我在一个模拟项目X的技术手册问答中对比过纯长度切和结构切的效果,结构切片的命中准确率明显更高,因为手册的每个章节主题相对独立,结构切片恰好对应了用户的提问粒度。
切片这一步还有个隐患:如果文档里有大量重复内容,比如每章末尾都有"参考第X章"这种话,切片后会造成检索结果高度相似,top-k里全是同一个章节的内容。遇到这种情况,入库前做一次去重,或者在检索阶段做多样性控制,后面优化部分细说。
3.4 向量化与入库:索引参数怎么调
切片完成之后,就是调用嵌入模型把每个切片转成向量。这里的核心操作是调用模型接口,传入文本,拿到向量,然后存入向量数据库。LangChain里直接封装好了这个过程:
from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma embedding_model = HuggingFaceEmbeddings( model_name="moka-ai/m3e-base" ) vector_store = Chroma.from_documents( documents=text_chunks, embedding=embedding_model, persist_directory="./vector_store" )这个例子里用的是M3E这个中文嵌入模型,效果中规中矩,胜在体积小、速度快。如果追求更好的语义效果,可以换更大型号,代价是embedding耗时变长。入库这一步需要关注的问题,一个是批量处理的速度问题,几万条切片一条条调接口会等很久,大批量入库时用批量接口,一次传几百条。另一个是去重问题,同一份文档如果重复入库,检索结果会被重复内容污染。
索引参数方面,向量数据库通常用HNSW算法做近似最近邻搜索。关键的参数是M和efConstruction:M控制每个节点的最大连接数,默认为16,加大能提高召回率但索引会更大、建索引更慢;efConstruction控制建索引时的动态候选集大小,加大能让索引质量更好。数据量不大时这些参数用默认值就行,数据量大了再逐个调。
3.5 检索问答链路:让模型学会"看资料答问题"
库搭建完毕,剩下的就是检索和生成了。检索阶段,先把用户的query做同样的向量化,然后在向量数据库里做相似度搜索,取top-k个相关切片。这里有个关键的细节:不要把top-k设得太大,5到8就足够了。检索回来的切片太多,上下文塞得太满,大模型的注意力会被无关内容稀释,反而答得更差。
拿到切片之后,进入提示词构建环节。这一步很多人也有误区,以为提示词写得越长越详细越好。实际上,清晰的结构比冗长的规则更有用。我常用的模板大致是这样:
你是企业内部的知识库助手。请严格依据以下资料内容回答问题。 如果资料中没有相关信息,请直接说明"资料库中未找到相关内容",不要编造答案。 参考资料: [1] 内容片段一 [2] 内容片段二 用户问题: {question}这个模板的精髓在于"不要编造"这四个字。RAG系统最大的风险就是检索到了无关内容,模型强行把不相干的信息组织成看似合理的答案。明确告诉模型"没有就说没有",能从根上减少幻觉的概率。另外,给每个切片编号,要求模型在回答中引用编号,这样做的好处是答案可溯源,用户点了编号就能跳回原文,对内部工具的信任度提升非常明显。
生成这步本身没什么黑科技,就是把检索到的内容和提示词一起发给大模型,拿到输出。有一个小技巧:如果对回答格式有要求,比如"务必先给结论再给依据"或者"用表格输出对比结果",直接写进提示词,大模型通常能稳定遵循。
4. 优化实战:八个影响成效的关键点在真实场景下的挣扎与解法
4.1 优化不等于调大模型参数量
RAG系统上线之后,效果不理想是常态。但"不理想"的表现各不相同:有的答非所问,检索结果压根不相关;有的定位到了相关内容但回答地很粗糙;有的是回答看起来全面但引用站不住脚。不同的问题对应的是链路上不同环节的缺陷,不能一上来就瞎调。
我在线上系统里总结出的经验是:先诊断问题出在哪个环节,再进行针对性优化。判断方法很简单,把检索到的切片内容直接打印出来看,如果切片本身就跟问题无关,那问题在切片或检索阶段;如果切片相关但回答混乱,那问题在提示词或模型选择上。这一招帮我省了大量调试时间,很多人热线调了半天提示词,最后发现是切片粒度太大导致检索定位不准,方向全错了。
优化这件事,要从全链条视角看。下面把我在实际项目中踩过的坑和有效的调整策略逐个列出,每个问题都对应可操作的参数或步骤。
4.2 检索效果差的三个主要对策
检索效果差,最直接的体现是召回的相关文档不对。第一个对策是调整检索策略。当前主流的进阶方案是混合检索:把向量检索和关键词匹配(BM25)结合起来,向量负责语义召回,关键词负责精确匹配,结果按权重融合,最后统一排序。这个方案在专业术语多的场景里效果尤其明显,比如医药文档中"阿莫西林克拉维酸钾",用户可能把它拆成"阿莫西林""克拉维酸钾"两个词问,纯向量检索容易把它当成整体语义来匹配,混合检索能直接用关键词捞出来。LangChain里有现成的EnsembleRetriever,权重默认各0.5,实测下来如果想强调精确匹配,可以把关键词权重调到0.6以上。
第二个对策是重排序(Rerank)。向量检索返回的top-k个结果,顺序是基于向量距离的,但向量距离跟"真正相关"之间并不等价。引入一个召回模型,对top-k的结果重新打分排序,效果提升非常明显。我用过多个开源的重排序模型做对比,在中文文档问答场景里,效果排序比较稳定。重排序的开销是有额外的推理时间,50到100条候选项重排一次大约耗时几十到几百毫秒,对交互式问答来说可以接受。建议先把向量检索的top-k调大到20到30,重排序之后再取前3到5条送进大模型,这样既能保证候选覆盖面,又不至于让上下文太长。
第三个对策是Query改写。用户的原始提问往往很口语化,甚至包含指代:"我刚才说的那个流程后面怎么走",这种query直接做向量化,效果很差。一个简单的方案是先用大模型把query改写为独立的自包含表述,再去做检索。LangChain里用LLMChain加一个改写prompt就能实现,改写之后再检索,命中率能提升不少。进阶一点的做法是做多路召回:对原始query和改写后的query分别检索,结果合并后再重排。我在实际项目里测试过,这个方案能把多轮对话中的追问场景从"完全答不对"提升到"基本能答对"。
4.3 切片优化、记忆机制与上下文管理的进阶技巧
切片策略的优化属于"没调好时感觉不到,调好之后被明显感知"的一类问题。一个非常实用的技巧是增补元数据。给每个切片打上文档标题、章节路径、页码、更新时间等标签,检索时先按标签做过滤,再在过滤结果里做相似度搜索。比如用户问"报销流程中发票要求",可以先用"财务制度"这个标签过滤掉非财务文档,检索精度立刻上一个台阶。实现上,Chroma支持metadata过滤,在query的时候加where条件就行。
另一个技巧是父子切片方案。父切片承载语义完整性,子切片承载精确性。入库时存两份切片:一份大粒度(比如整个小节)用于语义理解,一份小粒度(比如几句话)用于精确检索。检索时先用小切片找到精确位置,再把对应的父切片作为上下文喂给大模型。这段操作的精妙之处在于,既保证了答案的精准定位,又给了模型足够的上下文理解空间,回答质量和引用准确性同时提升。代价是实现复杂度略高,但编码量其实不大。
多轮对话场景下,还有一个不可忽视的机制:对话记忆管理。默认情况下,每轮对话独立检索,上一轮聊的"报销流程"到了下一轮问"发票呢",系统不知道"发票"指的是报销发票还是采购发票。处理方案有两种:一种是把最近几轮对话记录塞进检索上下文,但这会消耗大量token且让检索目标变得模糊;更好的做法是把历史对话压缩成一句话摘要,比如"用户正在咨询报销流程中的发票要求",然后用这个摘要作为检索的辅助上下文。实现时要注意控制历史对话的长度,通常保留最近3到5轮就够了,再老的对话信息对当前问题的帮助已经很小,还容易引入噪声。
4.4 评估体系建设:没有评测就没有优化
很多人做RAG优化靠感觉:问几个问题感觉差不多了就上线。这种做法在Demo阶段无所谓,在业务系统里就是灾难,因为你根本不知道改一个参数是变好还是变坏。我自己做了几十轮调优后才意识到,RAG的评测需要体系化,不然天天在瞎调。
一套好用的评测方案,我概括为"双指标+三维度"框架。双指标是召回准确率和答案质量满意度:前者评价"检索环节找得对不对",后者评价"生成环节答得好不好"。三维度是完整性、准确性、简洁性:完整性看答案是否遗漏了资料中的关键点,准确性看答案是否在事实上站得住脚,简洁性看答案是否废话连篇。评测集怎么造?从真实用户提问里收集100到300条问题,覆盖高频场景、长尾场景、边界场景,每条问题标注期望答案的来源文档章节。有了评测集,每次参数调整都跑一遍评测,看指标变化,优化方向就一目了然了。
评估集的维护同样重要。业务文档更新后,评测集里的期望答案也要同步更新。我建议把评测脚本和数据集放到代码仓库里管理,每次改动都跑一遍回归,防止改坏之前调好的效果。这一步本质上跟写单元测试是一个道理,只是测试的不是代码逻辑,而是整个RAG链路的效果。
5. 实战案例复盘:一个企业知识库问答系统的完整搭建记录
5.1 项目背景与需求分析
用一个具体的项目复盘来把前面的知识点串起来。模拟项目X的背景是:一家中型企业的内部技术部,需要搭建一个技术文档问答系统,覆盖内容包括产品使用手册、FAQ、故障排查指南、版本更新日志,文档总量大约2000份,以PDF和Markdown为主,核心使用者是客服团队,目标是把平均单次查询时间从10分钟压到1分钟以内。
需求拆解下来有三个关键点:文档格式杂(PDF表格多、Markdown层级深)、问题口语化明显("那个报错怎么弄""发货后几天能到")、答案需要可溯源(客服要把依据转述给用户)。这三个点对应到技术方案上,就是混合检索、query改写、引用标注这三个能力必须全上。
5.2 完整搭建步骤记录
按照前文的链路,整个搭建过程分六步完成,每一步的耗时和关键配置记录如下:
第一步,文档预处理。写了一个解析脚本,把PDF转文本、Markdown清洗、Excel转CSV,统一输出为结构化文档,耗时半天。这一步处理完大约产生12000个内容块,去掉页眉页脚和重复内容后剩约9800块。
第二步,切片。对Markdown文档按标题层级切,PDF文档按段落切,chunk_size设为600,overlap设为80。代码实现时用LangChain的RecursiveCharacterTextSplitter配合自定义分隔符,耗时约1小时。
第三步,向量化和入库。embedding模型选用M3E(中文场景,向量维度768),向量数据库用Chroma,本机持久化存储。1万个内容块入库耗时约几分钟,索引构建完毕。
第四步,实现检索。采用混合检索方案,向量检索和BM25各占权重0.5,top-k=30,然后接Rerank取前4个切片。这个环节的代码量不大,但调试费了些功夫,主要问题集中在LangChain版本接口变动上。
第五步,搭建问答链路。生成模型用商业API,提示词模板按章节3.5的框架,额外加了引用标注要求。整个链路封装成FastAPI服务,前端用一个简单的聊天窗口对接,前后端联调花了大半天。
第六步,效果评测。从客服部门收集了150条真实咨询问题,按难度分成三类:简单(直接命中FAQ)、中等(需要跨文档)、困难(口语化表达且涉及组合条件)。跑第一轮评测时,整体准确率大约75%,简单问题基本全对,困难和中等老出问题。
5.3 优化前后的效果差异
第一轮评测暴露出的问题集中在两类:一是困难类问题,用户口语化表达太强,直接检索到不相关内容;二是中等类问题,定位到了相关切片但答案组织地很粗糙,引用的准确性也不够。
针对口语化问题,加了一层query改写,用大模型先把"那个报错怎么弄"改写成"XX产品在安装过程中出现YY错误码,如何处理",再走检索链路。改完之后,困难类问题的召回率提升了20个百分点。针对回答粗糙的问题,主要是把切片方案升级成父子切片,检索到小切片后把父切片和兄弟切片一起作为上下文,模型能看到完整的章节内容,回答的完整性明显提升,引用准确率也上去了。
优化后的整体评测结果:简单问题准确率超过95%,中等问题约85%,困难问题约80%。这个效果对客服场景来说已经够用了,毕竟客服拿到答案还要人工转述,核心价值是把"找资料"的时间省掉了。叠加之前评估过单次检索平均耗时约1秒,远优于原先10分钟翻文档。
5.4 项目上线后的维护经验
上线只是开始,维护才是常态。文档更新是最高频的操作,我在维护阶段踩过最大的坑是:新增文档直接追加入库不带版本标记,结果旧文档内容和新文档内容混在一起,同样的问题有时候按旧规定答,有时候按新规定答。后来在元数据里加了版本号和生效日期,检索时按版本过滤,问题才算解决。
另一个维护经验是定期清理失效内容。企业文档经常有作废章节,如果不从库里移除,检索就会被过期信息污染。我的做法是每周跑一次同步脚本,对比文档源目录和向量库内的元数据,发现源文档已删除或更新的,自动移除或更新对应切片。
还有一项相对隐蔽但重要的经验:监控检索日志。在问答系统里记录每次query的检索结果top-k,定期抽样看有没有高频问题检索不到相关内容。这类问题单独测难以发现,但看汇聚数据很容易找到"用户问得很多、但召回率很低"的盲点,再针对盲点优化切片或补充文档。
6. 高频问题与排查路径速查
RAG系统的故障排查,难不在修,难在定位。我整理了一张实际问题排查表,对应的都是我在多个项目里真的遇到过的故障,每个问题都写了根因和排查路径。
先看文档加载类。症状是"入库后检索出来的内容乱码、缺字",重点检查扫描版PDF是否做了OCR、表格是否按行切碎、特殊字符是否被过滤。症状是"同一份文档入库两次,检索结果大量重复",根因是同步脚本没有幂等判断,按文件哈希去重可以解决。
再看检索类。症状是"问题明明在库里,但检索不到",优先检查切片是否有语义完整性问题,其次看embedding模型是否为中文优化版本,最后看query改写是否生效。症状是"检索结果相关但排序很靠后",优先加Rerank环节,其次是调大top-k再靠Rerank打分。症状是"不同用户对同一问题的检索结果差异巨大",检查metadata过滤条件是否生效,或者用户query本身语义歧义太强,需要拆分意图。
生成类问题也经常出现。症状是"答案跟材料无关,自己发挥",查看提示词是否明确写了"仅依据资料回答"以及是否要求"无资料时直接声明无法回答"。症状是"答案引用的编号对不上",查看切片编号是否在进入提示词前后保持一致,有些LangChain版本会在内部对Document对象排序导致编号错乱。症状是"回答太长没有重点",在提示词里加"先用一句话概括结论"的要求,并在生成参数里调整max_tokens和temperature,temperature降到0.1左右能显著减少发散。
最后是性能类问题。症状是"检索响应用了十几秒",优先查embedding接口调用是否是逐条串行,改成批量请求或者加缓存。症状是"并发一高就超时",把向量数据库和生成模型服务独立部署,加上连接池,生成模型这一步如果压力大就用异步调用排队,不要同步阻塞。
注意:排查问题的顺序比技巧更重要。我个人的习惯永远是"先看检索输出,再看生成输出"。拿到一个具体问题,先把检索到的原始切片打印出来,这片内容对不对已经能定位七成的问题。不要直接改提示词,不要直接换模型,多数情况下你省下的调试时间就藏在这一步。
7. 再透几个实操心得
整个过程走下来,简单聊聊我个人的体会,也算给刚开始做RAG的读者一些可参考的判断。
第一个心得:别追求一步到位。RAG系统的链条很长,每个环节都有优化的空间,但业务效果是短板效应决定的。把全链路跑通,产出一个"效果平庸但在进步"的版本,比憋一个月搞个"完美方案"再上线,价值大得多。上线之后有真实用户的提问数据,优化方向会越来越清晰,闭门造车很难判断什么才是用户真正关心的。
第二个心得:数据质量永远值得投入。做RAG之后我才彻底理解那句话——垃圾进,垃圾出。文档预处理多花一天,后面的检索和生成效果能救命。反过来,如果文档本身质量堪忧,换再强的模型、再多的Rerank都是白搭。我见过太多团队在模型调优上投入大量时间,却忽视了解析环节的无数噪声内容,这是本末倒置的。
第三个心得:评测集要趁早建。哪怕只有50条问题也要先建起来。没有评测体系,任何调整都等于盲人摸象。有了评测集,每次改动跑一遍,效果涨了就是涨了,跌了就是跌了,不靠感觉靠数据,这个习惯会帮你省掉大量无效调参时间。评测集本身也需要随业务演进持续扩充,把线上收集到的疑难问题不断加进去,这套系统就会越用越聪明。
第四个心得:架构选型别给未来挖坑。复用现有的中间件比新起炉灶更稳妥。回顾我自己的项目,选型的核心逻辑就是:小步快跑,能用开源绝不自研,能兼容现有技术栈就绝不多引入一套组件。真正决定RAG效果好坏的,永远是数据质量和提示词设计,而不是哪个数据库的索引速度更快。
最后送一句提醒:大模型发展很快,RAG的实现方式也在快速演化,但"检索-增强-生成"的核心思想不会变。把基础链路打通,把优化方法用熟,再迎接新工具新技术时,你的底座是稳稳的。这套能力,值得每个做应用开发的同行抽时间练一练。