资讯动态

RAG实战:从原理到代码,让大模型学会实时查资料

发布时间:2026/10/22 18:06:55 来源:尧图企业网站定制
前阵子一个做客服系统的朋友跑来问我说他想让智能助手学会查自家最新版产品手册但翻遍资料不是推荐微调就是让上重型组件看着就头大。我说你先别慌用RAGRetrieval-Augmented Generation检索增强生成就够了。翻译成大白话就是先让AI到资料库里“查资料”再把查到的内容当依据回答问题。很多人第一次听RAG会觉得玄乎其实它解决的事情特别朴素大模型训练完之后知识就凝固在参数里了你没法让它凭空知道昨天刚发布的新政策、新参数、新流程。RAG的思路就是绕开“重新训练”这条高成本路线在模型外面挂一个随时可更新的资料库每次回答前现场翻书。这篇内容适合刚接触大模型应用开发、想给AI接上私有知识、或者被“答非所问”和“一本正经胡说八道”折磨过的朋友们。我会从原理讲到代码最后把踩过的坑也一并整理出来跟着走一遍你也能给自己的AI装上“实时查资料”的能力。1. RAG到底解决了什么问题1.1 大模型的“死记硬背”困境大语言模型的本质是用海量文本训练出来的“概率预测器”。它把训练时见过的人类知识压缩进几十亿甚至上千亿个参数里回答问题时靠这些参数推断下一个字是什么。这个机制决定了两个致命特点第一知识截止于训练数据训练结束后的新信息它一概不知第二它天生就有“编造倾向”遇到不懂的问题宁可顺着语言习惯编一段起来也不说“我不知道”。这种一本正经的编造圈内给了一个专门名词叫“幻觉”。你可以把大模型想象成一个记忆力超强、但只看过特定教材的学霸。你问他教材里的公式、历史、代码他答得飞快但你要是问他公司上周刚发布的活动规则或者某产品最新版本的接口参数他就只能靠猜。猜对了是运气猜错了就是事故。尤其在企业场景里资料库每隔几天就变模型根本来不及重新训练。这时候你需要的不是一个更聪明的模型而是一个能“现查现答”的机制。1.2 RAG的核心思路先查资料再回答RAG的思路非常直白把“知识存储”和“知识调用”分开。模型不再负责记住所有细节它只负责读材料、组织语言。真正的知识放外部知识库用检索工具去捞。拿开卷考试打比方大模型是那个“记忆力超强但教材更新慢”的考生RAG就是允许他带一本随时能换新版教材进考场答题前先翻书再结合题目作答。翻书这步就是“检索”根据翻到的内容写答案就是“生成”。两条腿走路才是完整的RAG。相比微调Fine-tuningRAG有三个非常明显的优势资料更新成本低。新文档写好入库就行不用重训模型。答案可溯源。模型依据什么内容回答这些内容可以被展示、核对。幻觉大幅下降。模型不是凭空发挥而是有原文兜底。所以标题里说“实时查资料”本质上不是一个玄学概念而是指知识库独立于模型存在随时可以增删改查。模型还是那个模型但你给它换上了“新教材”。2. 小白也能懂的RAG实现拆解2.1 三大核心组件一个都不能少一个可运行的RAG系统拆开来看就三块知识库索引、检索器、生成模型。**知识库索引**不是简单地塞一堆txt文件进去而是要把文本切成合适的小块每一块用“嵌入模型”转成向量再把向量连同原始文本一起存起来。向量是啥你可以理解为高维空间里的一串数字专门用来表达“语义”。语义越接近的文本向量在空间里就越靠近。这一步相当于给图书馆里的每本书做分类编号编号越合理后面越容易找。检索器做的事就是你输入一个问题时把它也转成向量然后到知识库里去“找邻居”。找出语义最接近的top-k个文本块捞出来作为候选材料。这里的“邻居”不是按字面匹配而是按意思匹配。比如说你问“保修多久”文档里写的是“质保期”字面不搭边但语义很近照样能揪出来。生成模型就是普通的大语言模型。它负责把检索到的材料当成“参考资料”来读然后组织语言回答你的问题。三者分工明确缺一个都不行。没有知识库模型靠瞎猜没有检索器材料找不准没有生成模型前面都白费。2.2 完整工作流程离线建库在线问答RAG系统的工作流程可以分成两段离线阶段和在线阶段。离线阶段是做准备工作收集文档把产品手册、规章制度、运维日志等资料汇聚起来。文本清洗去掉页眉页脚、乱码、无意义的空行等噪音。文本切分把长文档切成几百字的小块避免整篇录入导致检索不精准。向量化每个文本块用嵌入模型转成向量。存储向量加原文一起落库。在线阶段是用户提问后的实时链路用户的提问进来先转成向量。拿提问向量去知识库做相似度检索取出top-k个最相关的文本块。把这几个文本块拼进一个提示模板里作为“参考资料”。把问题连同参考资料一起发给大模型。模型基于参考资料组织回答并输出结果。这套流程里最容易被忽略的是“拼上下文”这一步。很多新手直接问模型问题模型当然找不到答案你需要把检索到的内容明文塞进提示词里明确告诉它“只能依据这些内容作答”。上下文拼得好不好直接影响答案质量。这也是后面实操部分的核心。3. 动手实战从零搭一个“实时查资料”小助手3.1 技术选型新手最稳的组合我见过一些教程一上来就让你装向量数据库、部署重模型实际上小白很容易卡在环境这一步。这里给出一套门槛最低、但完全能体现RAG原理的组合嵌入模型用一个开源的中文嵌入模型加载方便无需付费API。知识库演示阶段不装专门向量库直接用本地的向量列表加NumPy做相似度检索跑通流程后再考虑换库。对话模型任何支持标准交互接口的大模型API都行通用格式换供应商只改一个地址。为什么这样搭配一是代码量最少核心逻辑一目了然二是方便你调试检索出了问题能直接打印向量和数据内容看不用被底层数据库的实现细节干扰三是迁移成本低等你想上生产环境时把“NumPy检索”那块换成专业向量库的查询接口就行其他代码不用动。3.2 环境准备十分钟跑通建库代码建议用Python的虚拟环境来跑避免依赖冲突。pip install sentence-transformers numpy requestssentence-transformers是加载嵌入模型常用的开源库numpy用来做向量运算requests用来调用对话模型接口。这三个就够不需要额外重型依赖。先写一个最简单的建库脚本。我假设你有一个文件夹叫knowledge_base里面放了几份txt文档。import os import numpy as np from sentence_transformers import SentenceTransformer # 1. 加载嵌入模型model_name请替换为你实际使用的中文嵌入模型标识 model SentenceTransformer(your_chinese_embedding_model) def split_text(text, chunk_size300, overlap50): 简单的文本切分策略 按字符长度切同时保留一部分重叠避免把一句话从中间断开。 chunks [] start 0 while start len(text): end start chunk_size chunks.append(text[start:end]) start end - overlap return chunks # 2. 扫描文档并切分 all_chunks [] all_ids [] for fname in os.listdir(knowledge_base): if not fname.endswith(.txt): continue with open(os.path.join(knowledge_base, fname), encodingutf-8) as f: text f.read() # 按段落清洗掉多余空行简单处理 text \n.join([line.strip() for line in text.splitlines() if line.strip()]) for i, chunk in enumerate(split_text(text)): all_chunks.append(chunk) all_ids.append(f{fname}_{i}) # 3. 全部向量化 all_embeddings model.encode(all_chunks) # 4. 存成一个字典结构演示阶段直接放内存 doc_index { ids: all_ids, texts: all_chunks, embeddings: all_embeddings.astype(np.float32), }这段代码里值得注意的有两个点一是切分策略我用了“固定长度加重叠”的写法保证文本块不会太长也不会把一句关键结论截得七零八落二是向量类型转成了float32能省将近一半内存查询速度也更快。接着是检索函数def search(question, doc_index, top_k3): q_vec model.encode(question).astype(np.float32) # 计算余弦相似度 embeddings doc_index[embeddings] scores embeddings q_vec / ( np.linalg.norm(embeddings, axis1) * np.linalg.norm(q_vec) 1e-9 ) top_indices np.argsort(scores)[-top_k:][::-1] results [] for idx in top_indices: results.append({ doc_id: doc_index[ids][idx], score: float(scores[idx]), text: doc_index[texts][idx], }) return results余弦相似度衡量的是两个向量方向的接近程度取值范围在-1到1之间越接近1越相似。这个实现虽然简单但足够让你理解检索的核心逻辑找“语义邻居”。3.3 完整问答Demo让AI学会翻新手册假设场景是一家公司要给客服做智能问答助手知识库里有个文档叫售后服务政策_v2.txt里面更新了一条内容“自2025年3月1日起全部产品的质保期由1年调整为2年。用户需在购买后30天内完成注册方可享受此政策。”用户提问“现在买产品保修几年”如果直接问没接RAG的模型它大概率会按自己训练时的知识乱答。接上RAG后我们先检索question 现在买产品保修几年 retrieved search(question, doc_index, top_k3) for r in retrieved: print(r[score], r[text][:80])检索结果里售后服务政策_v2.txt的那段内容分数最高。接下来把检索结果拼成上下文发给对话模型import requests def build_messages(question, context_chunks): context_block \n\n.join( [f[{i1}] {chunk[text]} for i, chunk in enumerate(context_chunks)] ) prompt ( 你是一个严谨的客服助手只能依据下方给出的参考资料来回答用户问题。\n 如果参考资料里没有足够信息请直接回答资料库中暂未找到相关信息。\n 严禁根据参考资料之外的内容编造答案。\n\n f参考资料\n{context_block}\n\n f用户问题{question} ) return prompt def chat_with_rag(question, retriever, doc_index, api_base, api_key, model_name): retrieved retriever(question, doc_index, top_k3) if not retrieved: return 没有检索到相关资料, [] prompt build_messages(question, retrieved) # 以下是通用的对话模型调用格式具体字段按你的服务商说明微调 resp requests.post( f{api_base}/chat/completions, headers{Authorization: fBearer {api_key}}, json{ model: model_name, messages: [{role: user, content: prompt}], }, timeout30, ) data resp.json() answer data[choices][0][message][content] return answer, retrieved调用后模型的回答会是根据最新的售后服务政策2025年3月1日起生效现在购买产品质保期为2年。需要注意用户需在购买后30天内完成注册才能享受这一政策。这个回答的关键在于模型不是在背答案而是根据我们塞进上下文的那段原文“翻译”出来的。哪怕模型本身完全没听过这个政策也不影响它回答正确。这就是RAG的魔力。4. 决定RAG效果的数据细节4.1 文本切分检索质量的第一道闸门很多人把RAG做出来之后发现效果差第一个怀疑的是模型不行其实八成问题出在文档切分上。切太大会怎样一个文本块包含好几个主题检索出来的材料只有最后一句话跟问题相关前面夹着一大堆无关信息模型很容易被带偏。切太小又会怎样一个完整的结论被拆成两半检索到一半语义不完整模型看不懂。我常用的经验是中文文档按300到500字切一块重叠50到100字。这个数值不是拍脑袋定的它对应着中文常见段落的信息密度——一个完整的小节或一个表格说明大约就在这个体量。重叠部分是为了防止切分线正好落在关键句子的中间。更聪明的做法是按结构切分。文档本身有标题就按标题切有段落就按段落切能保留语义边界就不要硬按字数切。一份产品规格表最好整表作为一块一个FAQ问答对最好问和答放一起。判断标准很简单单独拿一个文本块出来读如果仍然是一个语义完整的话切分就是合格的如果读着残缺就得调整。4.2 嵌入模型怎么选不踩坑嵌入模型决定了“语义理解”的上限。如果嵌入模型本身理解不了中文检索阶段就已经错了后面再好的大模型也救不回来。选择时优先考虑中英双语模型纯英文模型对中文支持往往粗糙。嵌入模型之间的差距体现在维度、推理速度、中文语义理解力上。高维度向量表达力强但存储和计算成本也高低维度模型跑得快但太笼统容易丢失细节。我的建议是从小规模测试入手不要凭感觉选。拷一批真实业务问题跑一遍检索人工看看返回的前三个结果是不是靠谱效果立竿见影。对了嵌入模型不需要跟对话模型来自同一家完全可以混搭。因为RAG系统里嵌入只负责“找材料”对话模型只负责“回答问题”两者独立。很多团队用着开源的嵌入模型接一个商用大模型的API效果一样很稳。4.3 向量数据库演示和生产的差距在哪前面演示用的是NumPy线性扫描把所有向量挨个算一遍相似度。数据量几千条时没问题但几百万条向量之后这种暴力检索会越来越慢。生产环境通常需要专业向量数据库它内部用近似最近邻算法用损失一点精度的代价换极高的查询速度。选型时建议关注三件事部署方式要一个轻量文件型库还是一个独立服务。相似度度量常见的有余弦距离、内积、欧氏距离演示用的余弦在多数场景下都够选了库之后不要频繁换度量方式。增量更新资料库每天都有新增看库支不支持直接追加或删除向量。影响范围上你的RAG系统未来要是从一个简单助手变成企业级知识中台向量库迟早要换所以当初建库的代码要留好抽象层别把检索逻辑和存储实现焊死在一起。5. 常见问题与排查技巧实录5.1 检索结果不相关先查这三件事检索结果不对最常见的原因有三个按排查顺序排列第一文本切分不合理。去打印出检出的文本块看看它是不是个语义完整的片段。如果切得残缺不全先调chunk_size和overlap。第二嵌入模型不适配。试着换一个中英双语或者领域更贴近的嵌入模型很多情况下更换模型后检索质量立竿见影。第三Top-K值太低。K1时恰好错过正确答案就翻车了。调大一点比如3到5把候选材料给足让大模型自己筛选。这不是在纵容检索不准而是给生成阶段更多容错空间。如果调了这些还不行再考虑混合检索方案关键词精确匹配加向量语义检索两种结果合并排序。对专有名词、型号编号这类词汇关键词匹配往往比向量检索更可靠。5.2 答案还在瞎编把“上下文”变成硬约束模型幻觉是个顽固问题但RAG系统里它是可以压制的。核心秘诀在提示词怎么写。我常用的模板里会明确写三件事只依据参考资料回答资料里没有的信息就坦然说不知道引用片段在末尾标注编号。这样模型会尽量在上下文中找信息。还有一种情况是“参考资料存在但答案是错的”这往往不是幻觉而是切分出来的文本块太多了模型被无关信息干扰。解决办法是限制上下文规模比如Top-K3且每块不超过500字总共不超过1500字足够回答大部分业务问题也不会给模型太多“自由发挥”的空间。另外把检索到的原始片段也返回给用户做成“底部引用来源”你会发现用户对答案的信任度明显提升。即使回答错了对方也能快速定位是哪份文档的问题。5.3 响应太慢、成本偏高怎么办RAG的延迟主要由两部分构成检索耗时和大模型推理耗时。检索通常不是瓶颈几毫秒到几十毫秒大模型输出才是大头。想优化先砍输入减少Top-K数量、缩短单块文本长度输入token少了首字延迟和费用都降。缓存策略也很值得做。同一个问题一周内被问了50遍没必要每次都到大模型走一圈。给检索结果加一层缓存命中就直接返回可以省掉接近一半的API费用。还可以用异步批处理。业务场景如果允许非实时反馈比如生成日报把多个问题分批送去大模型处理吞吐量能翻好几倍。实时客服场景则更适合用小模型快响应加事后质检的组合。5.4 如何用测试集评估RAG效果好坏不评估就上线等于闭眼开车。做RAG项目时我建议先攒50条左右真实业务问题涵盖常见问法和刁钻问法然后给每个问题标注期望答案来源。每次改完数据切分、换完嵌入模型或调完提示词就跑一遍测试集人工给答案打分。开局不用搞太复杂两个指标就够检索命中率期望的答案片段有没有出现在Top-K结果里。回答准确率最终回答是否抓住了核心信息有没有引入错误。第一次跑通后你会发现分数低的大多集中在数据层而不是模型层。这正是RAG的特质它把AI应用的重心从“调模型”拉回到“管好知识”这件事上。谁的数据整理得清楚谁的系统就更能打。就我个人做过的大大小小的项目来说前面兴奋地写完代码后面百分之八十的时间都在折腾数据清洗文档、调整切分、修检索措辞、看测试集反馈。这听起来不酷但确实是把RAG做扎实的必经之路。最后再分享一个实用心态别想着一步到位。先找一个几十页文档的小场景用今天这套代码把流程跑通再慢慢加数据、调参数。等你的资料库越来越规整你自然会发现原来让AI“实时查资料”这件事真的一点都不玄乎。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑