RAG技术实战:从文本向量化到BGE-M3构建知识库问答

发布时间:2026/10/18 6:25:40
RAG技术实战:从文本向量化到BGE-M3构建知识库问答 身边不少朋友第一次接触“RAG”这个词都是从“给大模型加个知识库”这个需求开始的。但真正动手之后才发现RAG并不只是调一个接口那么简单它牵扯到信息检索、文本向量化、向量存储、切片策略、模型选型甚至还有一套独立的评测逻辑。这篇文章标题里写着“建议收藏”我就真按“可以反复翻出来对着做”的标准来写先讲清楚RAG解决什么问题再把整条技术链路拆开然后落到BGE-M3和一套可运行的最小Demo上最后把调参和排障的坑一个个填上。如果你正在做知识库问答、私有文档检索、企业内部助手或者单纯想把大模型的能力约束到自己的数据范围内这篇文章应该能帮你省掉不少自己摸索的时间。已经有经验的读者可以直接跳到第四章和第六章那里更贴近实战。1. 先搞清楚RAG解决的是哪类问题1.1 大模型的幻觉和知识边界从哪里来大模型之所以会一本正经地胡说八道根源在于它只记得训练时见过的内容。训练语料有截止时间模型无法自动获取新发生的事情也无法访问企业内部的私有文档、聊天记录、产品手册。更麻烦的是即使它“见过”某些知识也无法保证在回答时准确调用因为参数化记忆本身就有随机性和模糊性。这个问题的本质是大模型的知识全部被压缩在权重里我们无法控制它到底记住什么、忘掉什么。传统做法是把问题直接丢给模型让它从参数中“翻找”答案。但它的训练目标是生成下一个词而不是“从某个指定文档里提取事实”。两者天然不匹配。RAGRetrieval-Augmented Generation检索增强生成的思路完全不同不再逼模型回忆而是主动把相关资料先检索出来作为上下文一起交给模型让模型基于这份指定材料做总结和回答。这种方式等于给模型配了一本可随时翻看的参考书而不是让它靠记忆硬撑。1.2 RAG和微调不是二选一而是两个方向很多人会纠结一个问题遇到领域数据效果不好到底该做RAG还是微调我的经验是先别急着对立。微调和RAG解决的是不同层面的事情。微调改变的是模型本身的行为方式和表达风格它适合让模型学会某种回答格式、语气或者稳定输出特定结构。但微调很难做到“把一份新文档的事实塞进模型参数里”因为训练一个新知识点的成本高、更新慢、且可能污染原有能力。RAG则更适合“知识频繁更新、数据量大、要求可追溯”的场景。每次回答都可以指出引用来源数据变了只需要重新索引不需要重新训练模型。你完全可以先上一套RAG解决知识来源问题如果发现模型的回答风格不合适再做小规模的指令微调作为补充。1.3 什么人适合直接上手RAG三类人最容易从RAG中受益。第一类是做企业知识库的应用开发者。手里有大量操作手册、制度文档、历史工单目标是做一个能快速响应的问答助手这类需求几乎绕不开RAG。第二类是做内容检索类产品的技术负责人。以前用关键词搜索做站内检索用户体验越来越不能满足“问一句就出结果”的预期想升级成语义检索和智能问答。第三类是个人开发者想给本地笔记、电子书、论文库加一个“第二大脑”。RAG的技术栈已经足够轻量一台普通笔记本就能跑通完整流程非常适合学习和实验。需要提醒的是RAG不是银弹。如果你连文档本身都是乱的答案自然是乱的如果你的检索质量不行生成再强也补不回来。所以下面这部分我们先从整条链路来看RAG到底是怎么工作的。2. RAG全链路拆解从资料到答案要走几步2.1 上游文档接入与预处理RAG的第一步不是向量化而是把各种格式的文档处理成干净、可切分的文本。我见过太多项目在这里翻车PDF里有扫描图片需要OCRWord和Markdown里有表格和多级标题网页抓下来的内容夹杂着导航栏、广告和页脚。这些噪音如果不清理后面切分出来的chunk里会混入大量无效信息检索召回的质量随之下降。一份标准的预处理流程大致有四步格式解析根据文件类型选择解析工具把PDF、Word、HTML、Markdown统一转成纯文本。遇到扫描件需要先做OCR这一步不能省否则文字全是乱的。清洗过滤去掉乱码、多余空行、页眉页脚引用块、超链接标记等把表格尽量转成“列名值”的结构化描述。结构识别保留标题层级和段落边界为后面的切分提供锚点。比如一级标题、二级标题可以直接作为切分边界避免在一句话中间硬切。内容去重如果库里存在同一份文档的多版本要做查重否则检索时同一知识会反复出现干扰排序。预处理做到位后面的切分才有意义。很多人把RAG效果差归咎于模型不行其实数据源和切分才是第一道关口。2.2 中游文本向量化与索引构建预处理完成后要把切分好的文本送去向量化。这里的核心是把“自然语言文本”转换成“一组浮点数向量”让语义相近的文本在向量空间中距离更近。这个过程依赖嵌入模型Embedding Model也就是本文重点要讲的文本向量化环节。拿到向量之后还需要把向量存储到专门的地方。这里有一个很容易混淆的概念向量库和传统数据库不是一回事。传统数据库按行存数据、按条件查询向量库则是存储向量并提供按相似度检索的能力。常见的向量组件有FAISS、Milvus、Qdrant、ChromaDB等能力各有侧重但核心机制一致建立索引加速“找最近邻”的过程。索引构建这一步还有一个细节向量化时最好同步保存元数据比如来源文件、章节标题、原始文本。原因很简单检索出来的向量只是一个坐标最后真正要展示给用户看的还是原始文本和出处。没有元数据后面做引用追溯和前端展示都会很痛苦。2.3 下游检索、重排与生成检索阶段的核心逻辑是“把用户问题也向量化然后去向量库里找距离最近的向量”。但这里有一条很重要的经验不要只做一次粗糙的top-k召回。用户问题的表达千变万化“苹果的市场份额”和“水果行业营收情况”可能是同一个意思也可能不是同一个意思。初次召回往往混入大量不相关片段所以成熟系统都会在粗召回之后加一个重排Rerank环节。重排的常用做法是先用向量检索快速拿回比如50条候选再用一个更精确的排序模型或者基于交叉编码的匹配器逐条打分重新排序后只保留top-5或top-3。重排会增加一点延迟但能显著提升生成质量因为送给大模型的上下文更精准了。最后一步是生成。把检索到的相关片段作为参考上下文与用户问题一起组装成提示词交给大模型。这里要注意控制上下文长度和格式不要让参考片段淹没问题本身也不要让模型把参考内容当成事实原文直接照抄。生成阶段拼装的提示词质量直接决定了答案表达是否自然、引用是否清晰。3. 信息检索与文本向量化需要理解的底层原理3.1 传统信息检索的局限关键词不会“读心”在文本向量化成为主流之前信息检索靠的是倒排索引和关键词匹配。经典的BM25算法会计算一个词在文档里出现多少次、在整个库中多常见然后给文档打分。这个机制到今天依然有效尤其在精确匹配场景下非常可靠你搜“HTTPS状态码500”全文包含这几个字的文档就是正确答案。但关键词检索有明显的天花板。它无法理解同义词用户问“怎么降血压”文档里写的是“高血压的治疗方案”两个文本里没有共同关键词匹配就断了。它也无法理解抽象意图用户问“最近公司有哪些和AI相关的活动”文档里全是具体活动名称没有“AI相关”这个词排序就很难看。这就是传统检索的“词汇鸿沟”问题。信息检索从关键词走向向量化的本质是想让系统理解语义。“信息检索”和“文本向量化”在这里不是两个独立话题而是因果关系正是为了解决语义匹配问题才需要把文本转成向量。3.2 文本向量化的本质把文字变成数学文本向量化的核心目标是让机器找到一段文本的数学表示。最简单的理解方式一个模型把“猫在沙发上睡觉”和“猫咪躺在沙发里休息”映射到向量空间中两个向量应该距离很近而“冰箱里放着牛奶”距离它们就很远。早期的词嵌入模型是Word2Vec这类静态词向量。每个词只有一个固定向量无法解决一词多义比如“苹果”到底是水果还是品牌要看它周围的词才能判断。后来基于Transformer的预训练模型改进了这一点让每个词在具体句子中获取动态表示同一句话在不同语境下有不同的向量。而嵌入模型要做的不仅仅是词本身它可以把整个句子、段落甚至篇章压缩成一个向量。对于检索任务我们需要重点关注的不是某个词的向量而是整段文本被表达的语义方向。把几百字甚至几千字的文本压缩成几百到几千维的浮点数必然会有信息损失。这也是为什么“文本切片长度”和“模型支持的输入长度”会成为检索效果的重要变量。3.3 相似度计算余弦和内积怎么选文本变成向量之后用什么指标衡量“两个文本有多像”最常用的是余弦相似度公式就是向量夹角的余弦值取值在-1到1之间越接近1表示方向越接近。它的好处是对向量的长度不敏感适合比较不同长度的文本。内积则是直接做向量逐元素相乘再求和。如果想用内积作为相似度需要把向量做归一化处理让每个向量长度为1。归一化之后内积和余弦相似度在数学上是等价的但索引时的计算效率有差异。所以在实际使用里很多时候会调用内置的IP内积索引前提是编码时设置normalize_embeddingsTrue。这里有一个新手容易踩的坑向量库里的索引类型和嵌入模型是否归一化必须匹配。如果模型没有做长度归一化却用了内积索引那么返回的结果会偏向长度较大的文本语义相关性会被破坏。最简单的做法是让所有向量统一归一化再使用内积这样全局一致不容易出问题。3.4 混合检索为什么效果更好很多实践者最后会发现一个现象只靠稠密向量检索精确匹配能力会下降只靠关键词检索语义扩展能力又不足。于是出现了混合检索策略。混合检索的思路很简单同时用关键词检索比如BM25和向量检索稠密向量相似度各召回一批结果再把两个来源的分数做加权合并。好处很明显BM25能精确锁定标识符、型号、人名等专有名词向量检索能召回表达方式完全不同但语义相近的内容。两者互补整体召回率明显提升。BGE-M3这类模型之所以受到关注正是因为它在训练时同时优化了稠密向量、稀疏向量和多向量三种表示让混合检索的实现变得更简单。这正好引出了下一章的重点内容。4. BGE-M3实战模型特点、加载与使用4.1 抓住三个关键词多语言、多粒度、多功能BGE-M3这一系列模型的名字里M3代表三个“多”多语言、多粒度、多功能。多语言意味着它不仅支持中文和英文还支持更多语言及中英混合场景。做企业知识库时文档和用户问题经常是中英混杂的比如“请总结一下Serverless计费规则”这种输入对纯中文或纯英文嵌入模型都是挑战。BGE-M3对多语言环境的友好度让我在实测中明显感觉到召回稳定性的提升。多粒度指的是模型可以在词级别、短语级别、句子级别输出不同的表示。低粒度的表示适合精确定位文档中的关键实体高粒度的表示适合做段落和文档级别的召回。多功能则是它最突出的特点同一个模型可以同时输出稠密向量Dense、稀疏向量Sparse和多向量ColBERT表示。稠密向量负责语义概括稀疏向量负责精确词汇匹配多向量负责更细粒度的语义交互。三种表示组合起来就是上一章提到的混合检索方案而且不需要额外加载三套模型。4.2 环境准备与模型加载先把基础环境准备好。我这里以Python为例需要安装以下依赖pip install sentence-transformers faiss-cpu numpy如果你要处理PDF还可以加上pymupdf做文本提取。模型加载代码非常短from sentence_transformers import SentenceTransformer model SentenceTransformer(bge-m3)我第一次跑的时候用的是默认加载方式它会自动从公共模型仓库下载权重。如果你在公司内网或者网络下载不稳定建议先把模型权重文件下载到本地目录然后这样加载model SentenceTransformer(./models/bge-m3)使用本地路径看似多了一步但对后续部署很有帮助可以保证模型版本可控不被远端更新影响。代码里SentenceTransformer(bge-m3)是示意写法不同加载器对模型标识的写法略有差异。你在公共模型仓库页面里找到模型权重下载地址后把标识改成你实际下载的模型目录即可。4.3 单条与批量向量化实操模型加载完成后向量化操作本身很简单但有些细节值得注意。# 单条向量化 embedding model.encode(文本向量化是RAG中最核心的一步) print(embedding.shape) # 输出维度比如 (1024,) # 批量向量化 texts [ RAG是检索增强生成的简称。, BGE-M3支持稠密、稀疏和多向量三种表示。, 向量检索可以弥补关键词检索的语义鸿沟。, ] embeddings model.encode(texts, normalize_embeddingsTrue) print(embeddings.shape) # (3, 1024)normalize_embeddingsTrue不是可加可不加的它会把向量长度归一到1后续无论是用内积还是余弦相似度都比较稳定。如果你需要同时拿稀疏向量通常会使用模型底层接口返回一个包含稀疏权重映射的对象而不是普通数组。不同版本接口细节会有变化遇到问题时优先看模型目录下的使用说明。4.4 使用建议与资源占用模型加载到内存后占用的资源会明显高于传统的词向量模型。如果机器显存有限建议适当调小batch size避免一次编码太多文本导致溢出。另外BGE-M3虽然支持长文本但在RAG实践中依然建议把切片长度控制在合理范围。文本越长单个向量需要压入的信息越多检索时更倾向于召回“语义大概一致但不精准”的片段。经验值是单块文本控制在200到500字左右兼顾上下文完整性和语义聚焦度。如果一份文档非常长先按章节拆开再考虑是否需要进一步切分。5. 从零落一个可用的RAG系统完整代码走一遍5.1 准备最小数据集理论上RAG的代码链路不复杂切分、向量化、索引、检索、拼提示词、生成。但真正把链路串起来时每一步都会暴露出问题。我们先准备一个极简数据集用几条测试文档跑通流程再逐步替换成真实场景的大文档。documents [ 检索增强生成RAG是一种结合检索系统与生成模型的技术方案能有效缓解大模型的幻觉问题。, 文本向量化是指用神经网络把自然语言文本映射为向量表示语义相近的文本在向量空间中距离更近。, BGE-M3模型支持稠密检索和稀疏检索可以同时完成语义匹配与精确关键词匹配。, 向量数据库负责存储向量数据并提供基于相似度的最近邻检索能力。, 混合检索通常将关键词检索与向量检索的结果进行合并排序以兼顾准确率与召回率。, ]5.2 文档切分与向量化索引针对这个Demo文档已经足够短不需要再切分。但在真实项目里文档长度往往在几百到几千字。我习惯写一个简单的切分函数按字符长度切分并保留相邻片段的重叠减少语义断裂的影响def split_text(text, chunk_size300, overlap50): chunks [] start 0 while start len(text): end start chunk_size chunks.append(text[start:end]) start max(chunk_size - overlap, 1) return chunks接下来向量化并写入FAISS索引import numpy as np import faiss model SentenceTransformer(bge-m3) doc_vectors model.encode(documents, normalize_embeddingsTrue) dim doc_vectors.shape[1] index faiss.IndexFlatIP(dim) # 内积索引配合归一化向量 index.add(np.array(doc_vectors, dtypenp.float32))IndexFlatIP是暴力检索索引数据量小时最精准没有任何近似误差。数据量超过百万级以后再考虑IVF或者HNSW这类近似索引否则在线性检索精度足够的情况下没必要提前引入复杂度。5.3 检索阶段query处理与召回当用户提出问题我们做同样的向量化操作然后在索引里搜索def retrieve(query, top_k2): query_vec model.encode([query], normalize_embeddingsTrue) scores, ids index.search(np.array(query_vec, dtypenp.float32), ktop_k) results [] for score, idx in zip(scores[0], ids[0]): if idx 0: results.append((documents[idx], float(score))) return results retrieve(RAG是什么技术方案)这里返回的结果里score越大表示相关性越高。如果你发现同一批文档每次检索结果飘忽不定先检查一下是否对不同批次的文本用了不同的归一化设置或者观察一下模型是否在运行过程中被动更新过。5.4 生成阶段把上下文交给LLM检索拿到结果后把结果的原始文本拼接进提示词。以大模型API的标准格式为例可以这样组装请求import requests import os def generate_answer(query, retrieved): context \n\n.join([text for text, _ in retrieved]) prompt f请根据以下资料回答问题如果资料中没有相关信息请直接说明。 资料 {context} 问题{query} resp requests.post( os.getenv(LLM_API_URL), headers{Authorization: fBearer {os.getenv(LLM_API_KEY)}}, json{ model: os.getenv(LLM_MODEL), messages: [ {role: user, content: prompt} ], temperature: 0.3, }, timeout30, ) return resp.json()[choices][0][message][content]temperature设低一点是故意的。生成任务要的是忠实引用材料不是发散创作。如果你想要更稳的引用输出还可以在提示词里加上“每段回答后标注来自哪份资料”的指令。5.5 完整流程串起来把上面几段拼起来就是一个最小可用的RAG系统。真实使用时你还需要替换成自己的文档加载逻辑、向量库和生成服务。整个流程大致是加载文档清洗文本。切分文本为chunk。用BGE-M3把每个chunk转成向量。把向量写入向量索引保存原始文本与元数据。用户输入query转成向量后召回top-k。拼接提示词调用LLM生成答案。这套流程跑通不难但跑通之后你会发现效果远没有想象中那么惊艳。真正决定体验的是下一章讨论的调优问题。6. 效果评估与参数调优让RAG更好用的关键6.1 评估先于优化三个维度缺一不可很多RAG项目死于没有评估标准。你今天改了一版切分逻辑感觉“好像好了一点”但过两天又发现另一个问题来回折腾却不知道到底哪个改动是有效的。所以要先把评估维度定下来一般关注三件事。第一是检索召回率。比如你有100道测试问题每道题都有对应的标准答案文档系统有没有把正确的chunk捞回来。召回率是RAG的生死线因为如果正确内容压根没被检索到后面的生成阶段再强也无能为力。第二是命中内容的质量。即使正确文档被召回召回片段是否涵盖答案所需的关键信息如果答案分散在多个chunk里而top-k只捞到了一部分照样会漏答。第三是生成答案的质量。模型有没有忠实于材料、有没有遗漏关键条件、有没有把不相关的内容硬扯进来。这块主观性较强可以设计几个规则化的检查点回答是否包含答案关键词、是否出现“根据资料”这类引用标记、是否在有答案时还在说“资料中未提及”。6.2 几个影响最大的参数我整理了一张参数速查表方便你对照调整参数影响对象调整建议chunk_size检索命中精度越小越精准越大越完整常规200到500字overlap上下文连贯性为chunk_size的10%到20%防止跨段语义断裂top_k上下文信息量2到5比较常用过大容易引入噪音相似度阈值是否跳过无关内容根据实际得分分布设定避免无关信息进入提示词嵌入模型语义理解能力尽快换成支持多语言和多功能的模型是否有重排最终排序质量数据量大后建议加入重排环节temperature答案稳定性问答场景建议0.1到0.3这里有一个容易被忽略的组合关系top_k和chunk_size是联动的。当chunk很小比如只有200字那么top_k就要适当调大否则无法提供足够的上下文当chunk很大比如到1000字一个chunk可能就包含全部答案top_k设1都够用。调整时把两者当成一个变量组来看。6.3 建立标准测试集和调优闭环我的做法是建一个轻量测试集几十到上百条问题即可每条问题关联“期望命中的文档ID”和“期望答案要点”。每次改动之后固定跑同一批问题记录召回率和生成质量形成前后对比。调优顺序非常重要。先看检索召回率召回不行就优先调切分、模型和混合检索召回稳定了再看生成质量这时候才动提示词和重排。如果顺序反了比如先纠结提示词再回头调切分很容易白忙一场。我在实际项目里发现收益最大的往往是这三个动作把文档清洗干净、把chunk切得合理、把检索从单路改成混合。模型层面的提升虽然也很明显但数据准备粗糙的话再强的模型也发挥不出来。7. 经验避坑与问题排障7.1 答非所问先别怪生成模型当用户问“入职需要什么材料”系统回答“入职流程包含背景调查”但漏了“学历证明”时大部分人第一反应是修改提示词。但大概率问题出在检索正确答案的chunk根本没被召回或者被更高分的噪音chunk挤掉了。排查方法很简单把检索结果先打印出来看看。如果top-3里没有正确答案这就是检索问题跟生成模型无关。这时候去检查切分粒度、相似度阈值、索引类型和混合检索的权重配置。7.2 切分把上下文切碎了实体和结论分离有一种常见现象正确答案确实在库里但被切分逻辑拦腰截断。比如文档里先写了“该权限仅对HR角色开放”被切到了第一个chunk后面一段又写了“管理员默认拥有所有权限”被切到了第二个chunk。用户问“谁能看到薪资数据”时模型拿到的单个chunk信息不足自然回答不出来。解决思路有三个方向优先按章节和标题切分而不是纯按字数硬切设置overlap让相邻chunk保留上下文重叠必要时在检索阶段多召回几个chunk再做重排让模型看到更完整的上下文。7.3 向量维度不匹配或不同模型混用换嵌入模型时最容易碰到维度报错。比如之前的向量是1024维换成其他模型后变成768维向量库里的所有数据会冲突。更隐蔽的问题是项目跑着跑着有人顺手换了一个模型虽然输出维度一致但两个模型的向量空间完全不同旧数据的分布和新数据的分布根本不在一个空间里检索结果会变得莫名其妙。经验是把嵌入模型模型名连同版本号一起记入配置并将向量库中所有数据标记来源模型。如果模型升级宁可全量重新索引也不要混着用。7.4 性能问题编码慢、检索慢、接口超时RAG链路里的性能瓶颈通常有三个位置。文档批量索引时如果一次编码上千条长文本CPU会跑很久甚至内存爆掉建议分批编码并适当降低batch size。检索阶段如果用暴力索引且数据量很大单次检索可能超时需要切换HNSW或IVF索引并设置合适的查询参数。生成阶段如果上下文太长接口响应会变慢需要控制chunk数量和长度。日志也很重要。每次请求记录下检索耗时、召回数量、生成的token数性能问题出现时可以快速定位是哪个环节变慢了。7.5 一个小建议把检索结果存储下来调优时最怕“这次感觉效果好但不知道为什么”。把每次测试的检索结果和得分保存成日志方便复盘也方便后续调整阈值。这个操作听起来简单但做与不做的差距非常大。有日志你就能看到哪些问题始终召回失败、哪些query得分忽高忽低排障效率会高很多。8. 最后分享几点个人经验做了不少RAG相关的项目之后我最大的体会是RAG的瓶颈从来不在某一个模型上而在于整条链路的配合。BGE-M3确实让文本向量化这道工序变得更顺滑但如果没有好的文档处理、合理的切分和可靠的评估方法再好的嵌入模型也会被埋没。我个人的建议是刚开始做RAG时不要一上来就追求大而全的框架。先把最小链路跑通用固定测试集量化每个环节的效果然后再逐步加入混合检索、重排、缓存这些优化点。每加一个环节都重新跑一遍测试确认它带来了正向收益。这套方法看起来慢实际却是最快的一条路。最后再分享一个小技巧如果你要处理的是大量中英混合的技术文档建议把切分粒度往小里调一调比如300字左右重叠60字检索准确率通常会有可感知的提升。踩过几次坑之后你会发现RAG是一个数据工程和模型能力各占一半的领域先把数据这半边做好剩下的技术选型就有了明确的方向。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询