ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RAG技术原理与本地搭建实战:从检索增强到企业落地

RAG技术原理与本地搭建实战:从检索增强到企业落地 1. 为什么偏爱RAG从大模型记性差说起大模型这两年火成这样但用过的同学几乎都会遇到同一个尴尬场景你跟它聊公司内部报销制度它一本正经地告诉你一个完全过时的流程你问它上季度某条业务线的具体数据它直接开口编。根本原因就一句话——大模型的知识停留在训练截止的那一刻之后发生的事它一概不知而且它天生有一本正经胡说八道的毛病术语叫幻觉。RAG检索增强生成就是冲着这两个痛点去的。思路特别朴素模型不知道答案没关系我先从外部知识库里把相关材料捞出来把材料作为上下文喂给它让它基于材料作答。相当于给模型配了一个随身携带的资料员模型负责读资料员负责找。你问它公司报销制度它不再凭记忆瞎猜而是先翻出你上传的《费用报销管理办法》照着文件内容回答来源可溯、口径统一。这项技术尤其适合三类场景。第一类是知识密集型问答比如企业内部的制度咨询、产品资料问答、售后FAQ第二类是内容实时性要求高的场景比如新闻资讯总结、竞品动态追踪第三类是垂直领域专业问答比如医疗、法律、金融这些对准确率极其挑剔的行业。如果你恰好在这类场景里做AI应用RAG几乎是目前性价比最高的方案不用重新训练模型不用昂贵的GPU用一个Embedding模型加一个向量数据库就能搭出可用的系统。这篇文章我会把RAG的原理拆开讲透然后进入企业落地部分聊架构设计、权限隔离、图片怎么处理这些实际避不开的问题最后给一份从零到一的本地RAG搭建实录再加上我实际运维中踩过的一堆坑。全文尽量用大白话看完你至少能把RAG的完整链路讲明白也能知道自己在搭建时选型该往哪个方向走。提示本文不涉及任何需要特殊网络环境才能访问的资源所有工具均使用公开可用的开源项目。2. 拆解RAG原理检索生成两个引擎如何协同工作很多教程上来就画一堆流程图实际上RAG的整个流程拆成离线准备和在线问答两个阶段就完全清楚了。离线阶段负责把资料切块、向量化、建立索引在线阶段负责把问题向量化、检索、拼上下文、让大模型生成回答。理解清楚这两个阶段后面遇到任何问题你都能定位到具体环节。2.1 离线准备文本切块与向量化入库RAG的第一个关键动作是切。你上传的PDF、Word、Markdown不可能整篇塞给模型模型上下文有限而且整篇塞进去检索精度极低。合理做法是把文档切成一个个语义完整的块Chunk每块几百字块与块之间保留少量重叠防止语义被拦腰截断。接着是向量化。这一块是新手最容易懵的地方——为什么要把文本变成向量简单说机器没法直接比较两段话像不像但可以把每段话映射成一组几百维的数字这组数字叫向量。语义相近的两段话它们的向量在空间里的距离就更近。比如怎么申请年假和年假申请流程这两句话向量距离就很近而怎么申请年假和今天天气不错的向量距离就非常远。具体实现中每个文本块经过Embedding模型编码变成一串浮点数再连同原文、文件路径、元数据一起写入向量数据库。常用的开源Embedding模型有BGE系列的bge-large-zh-v1.5、bge-m3英文场景有OpenAI的text-embedding-3-small需要API Key等。如果做中文知识库BGE系列的m3模型表现非常稳多语言和中文效果都不错。这里有两个细节特别影响效果。第一是切块大小。切得太小比如每块只有一句话可能丢失上下文语境切得太大比如一次切一整页检索出来的内容不够精准。我实际测试下来中文场景块大小设在200到500字之间重叠50到100字效果比较均衡。但这只是起点具体要根据文档类型调。第二是Metadata设计。入库时一定要保留原始文件信息、页码、章节标题这些在最后生成回答时能用来做引用溯源企业应用里没有来源的回答约等于不可用。2.2 在线问答查询改写、检索与增强生成用户提问到达系统后第一步不是直接检索而是先做查询预处理。很多人忽略这一步直接拿用户的原始问题去向量库捞数据。用户的提问往往很口语化比如咱们加班费咋算的直接检索容易捞不回精准内容。企业级系统一般会先用大模型把问题改写一遍加班费怎么计算标准是什么然后再做向量检索召回效果立刻不一样。检索环节也有讲究。现在的成熟方案普遍采用双路召回一路走Embedding向量相似度负责召回语义相关的块另一路走BM25关键词匹配负责召回那些语义模型可能漏掉的精确术语比如合同编号、产品型号、法条序号。两路的结果做融合排序再进Rerank模型精排。Rerank模型会逐条评估这个问题和这段材料到底有多相关把真正有用的块排到前面过滤掉噪声。这一环节对最终答案质量的影响非常大我在企业项目里实测同一套知识库加不加Rerank答案准确率能差20个百分点。最后生成的环节最容易被低估。你以为把检索结果往Prompt里一拼丢给大模型就完事了实际远没有这么简单。Prompt里必须明确告诉模型以下是从知识库检索到的参考资料请严格基于资料内容回答如果资料中没有相关内容明确回答知识库中未找到相关信息严禁编造。同时要给模型注入引用要求让它回答时标注答案来自哪一份资料的哪一段这样你才能做溯源校验。还有一个实践细节检索回来的TopK块通常取3到5个就够了取太多反而会把不相关的内容塞进上下文干扰模型判断。2.3 向量检索的原理以及为什么相似度不能只看数字聊到向量检索就绕不开相似度计算。最常用的指标是余弦相似度公式不复杂理解起来也很直观把两个向量想象成从原点出发的两条箭头余弦相似度就是看这两个箭头的夹角有多大。夹角越小说明方向越一致相似度越高。这一招在普通文本场景够用但有两种情况会翻车。第一种是短文本和长文本比较。用户的提问往往三四十个字而被切分的文本块可能有四五百字向量维度都归一化之后短文本容易被长文本吞掉信号导致相关但不精确的块被召回。第二种是领域术语问题。止损在金融场景和游戏场景完全是两个意思纯向量相似度无法区分语境。这也是为什么我后面要强调企业级RAG一定要做查询改写混合检索Rerank的组合拳单靠一个向量相似度打天下在真实场景里一定不够用。聊到这里你可以顺带记住一个结论RAG的核心难点从来不在生成而在检索到的内容到底准不准。很多时候你觉得模型答得不好真不是模型笨是上游检索根本没把对的资料捞上来。3. 企业级RAG落地架构设计、权限隔离与数据处理从原理到落地中间隔着一道巨大的鸿沟。个人玩RAG搭个本地demo能跑就行企业级RAG要面对的是权限、安全、合规、高并发、数据更新这五座大山。这一章我把企业落地中最关键的几个决策点逐一说明。3.1 企业级架构为什么不能只靠一个向量数据库打天下不少团队一开始图省事所有文档灌进同一个向量库就完事。这种做法在几十份文档的场景还能应付一旦到了几千份甚至几万份文档问题会集中爆发检索慢、噪声多、权限没法隔离、数据更新牵一发动全身。企业级架构至少要拆分出四层接入层、处理层、存储层、服务层。接入层负责对接各种来源包括NAS共享盘、飞书文档、Confluence、数据库等处理层做格式解析、清洗、切块、向量化存储层是核心向量库、关系型数据库、对象存储三者配合使用——向量库存向量和元数据关系库存业务映射关系对象存储存原始文件服务层对外提供统一的问答API和后台管理界面。这样分层的好处是每一层都能独立扩展和替换。比如后期检索性能不够可以只替换向量数据库而不用动前面的处理流程数据源要新增一个系统也只需要在接入层加一个适配器。这套架构很多团队一开始不愿意搭觉得重但一旦数据量上来重新返工的代价远大于一开始就分层。3.2 权限隔离企业知识库最大的隐形坑我见过太多企业RAG项目栽在权限上。一个全员可访问的文件共享盘里混着HR的薪酬资料、销售部的报价单、法务部的合同全灌进知识库后员工随便一问就能问出不该看的机密。这在国内企业合规要求日益严格的环境下风险极高不是说你有意泄露而是系统架构天然没有做隔离一旦出事追责都没法追。解决方案通常是向量库分区元数据过滤的组合。在文档入库的时候给每个文档打上权限标签比如部门、密级、可见范围查询的时候先根据当前用户身份计算出可见的文档ID列表再带着这个列表去检索向量库本身只做分区不对内容做判断。这样做的前提是你必须在入库阶段就能确定每个文档的权限归属这个工作没法自动化只能依靠业务部门在文档上传时指定。如果你想省事一点可以按数据源做物理隔离不同部门的资料放进不同的向量库集合查询时路由到对应的集合。但这样会牺牲跨部门检索的能力部门之间一旦需要协同查阅资料你又要去做跨库检索和结果合并复杂度反而更高。我的建议是权限体系从第一天就设计不要抱等出事了再补的侥幸心理。3.3 混合数据PDF、表格、扫描件怎么统一处理企业里的资料从来不是整洁的纯文本PDF表格、PPT、扫描件、Excel报表什么妖魔鬼怪都有。先说结论格式是小事关键是先把文本从格式里干净地抽出来。PDF分两种文字版PDF直接用PyMuPDF或pdfplumber提取精度很高扫描版PDF必须先做OCR通常用PaddleOCR中文表格识别效果不错。扫描件不OCR就入库检索结果几乎为零这个坑我见过太多人踩。接下来有个很多人没意识到的问题表格数据要不要向量化拿我自己的经验来说大段复杂的表格直接向量化效果很差。因为表格的信息密度太高切块时容易把表头和数据行拆散向量检索时根本找不回完整体。更好的方案是表格转描述入库前先把表格转化为自然语言描述比如2024年第一季度销售收入为1200万元同比增长15%再去做切块和向量化。这样看起来丢了一些结构化信息但检索准确率会明显提升生成的回答也更自然。图片要不要入库这是很多人在搜rag知识库能存储图片嘛时真正想问的。答案是可以但要区分用途。如果是带文字的截图、票据、合影里的铭牌本质上属于OCR问题把图片里的文字抽出来后按文本处理如果是需要看图理解的产品图片、医疗影像等那要走多模态模型的路子把图片交给视觉模型去embedding成向量再入库检索。个人和企业搭建初期别一上来就上多模态先把纯文本链路跑通图片需求压后再处理。3.4 数据更新与索引治理知识库不能一劳永逸企业知识库最要命的不是搭建那一刻而是日后的持续更新。文件每周都在变有些被修改了有些被删了有些合并了。如果索引不跟着更新系统回答的内容就会逐渐过时而没人察觉。我做过一个项目知识库里存的是旧版流程文件结果员工问了半年之后才发现答案和实际流程已经对不上了信任感一落千丈。索引更新的常规做法分两种全量重建和增量更新。全量重建最省心每天凌晨把当天有变动的文档全部重新切块、重新向量化再把旧的向量删除。增量更新则是在文档被修改时通过监听文件变动事件只更新变动的那部分文档索引。前者实现简单但消耗计算资源后者效率高但容易漏事件。个人项目我推荐定时全量重建企业项目如果文件量达到几万份、全量重建耗时太长才需要考虑事件驱动的增量更新。不管用哪种方式有一个动作必须做定期人工抽检。每两周随机抽几十条问题去验证答案质量对比知识库更新前后的表现。这一步没有工具能替代毕竟大模型输出的质量波动是看不见摸不着的只能靠人肉抽查兜底。4. 从零搭建本地RAGOllama开源框架实操实录说了这么多原理和架构是时候实操一把了。这一章给出一份零基础可以照着做的本地RAG搭建过程工具链选的是Ollama本地向量库开源Embedding模型全程不依赖任何付费服务也不依赖云端API个人电脑就能跑。4.1 为什么选Ollama作为运行底座Ollama是目前本地跑大模型最省心的工具它把模型下载、依赖环境、启动服务全部封装好了一条命令就能跑起Qwen2、Llama3这些开源模型。RAG链路里的生成模型本地可选的方案并不多Ollama算是最成熟的那个。再配合它的Python/Java生态做一个本地知识库问答demo半天就能跑通。需要说明的是Ollama只是生成模型底座RAG的完整链路还有检索、切块、向量化等环节。实践中很多人会用LangChain或LlamaIndex这种框架来把整个流程串起来。LangChain的接口封装全、社区资料多但版本更新快、API变动频繁入门时容易踩包版本不兼容的坑我建议锁定版本后再开发。LlamaIndex更偏向检索场景如果你只做纯RAG不整复杂AgentLlamaIndex更顺手。另一个热词是langchain4j easy rag这是Java生态下的LangChain实现适合中间件团队用Java统一技术栈的场景。如果你的团队全是Java后端不用强行引入PythonLangChain4j的Easy RAG模式代码量很少十来分钟能出一个demo。我团队里有位朋友用Java栈做过一次内部工具评价是比想象中成熟但中文社区资料相对少排坑要靠文档摸索。4.2 环境准备装好Ollama和两个开源模型先下载安装Ollama支持Windows、macOS、Linux三大平台官网直接下载安装包就好。装完之后打开终端拉取两个模型一个是Embedding模型用于向量化一个是生成模型用于最终回答。# 拉取Embedding模型这里选用BGE的长文本版本 ollama pull bge-large-zh-v1.5 # 拉取生成模型Qwen2是中文场景综合体验较好的开源模型 ollama pull qwen2:7b模型下载需要一点时间取决于网速。拉完之后可以顺手验证一下模型是否正常响应ollama list # 应该能看到 bge-large-zh-v1.5 和 qwen2:7b 两条记录这里有个容易被忽略的点Embedding模型并不只能在Ollama里跑也可以用HuggingFace直接加载。我之所以推荐Ollama统一管理是为了把模型运行这件事交给一个工具免得你本地Python环境里既要安装PyTorch又要处理CUDA版本冲突麻烦得要命。如果你的机器没有NVIDIA显卡纯CPU也能跑只是速度慢一点个人demo完全够用。4.3 构建知识索引选文本拆解工具还是自己写切块回答热搜里的有没有本地的rag文本拆解工具其实切块这个动作本身就是文本拆解的核心。如果你只想快速试效果可以直接用LangChain的RecursiveCharacterTextSplitter它按段落、句号、逗号逐级切分用法如下from langchain_text_splitters import RecursiveCharacterTextSplitter splitter RecursiveCharacterTextSplitter( chunk_size300, # 每块300字符 chunk_overlap80, # 块间重叠80字符 separators[\n\n, \n, 。, , , , , , ] ) text 你的原始文本内容…… chunks splitter.split_text(text) for i, chunk in enumerate(chunks): print(f第{i1}块: {chunk[:50]}……)注意separators的顺序中文文本一定要先把句末标点放进去否则切分可能硬生生把一句话从中间切断。如果你要处理的是Markdown、PDF或者HTMLLangChain里也有对应的MarkdownTextSplitter、RecursiveCharacterTextSplitter能识别格式结构。总之拆解工具不需要额外安装什么神器常规的文本切分器就够用。切完之后就是向量化入库。我选Chroma作为本地向量数据库因为它轻量、零配置、数据落在本地磁盘个人项目最合适。把每个块嵌入成向量连同原始文本、文件路径一起写入Chromaimport chromadb from chromadb.utils import embedding_functions client chromadb.PersistentClient(path./rag_demo_db) collection client.get_or_create_collection( namemy_kb, embedding_functionembedding_functions.OllamaEmbeddingFunction( urlhttp://localhost:11434/api/embeddings, model_namebge-large-zh-v1.5, ), ) ids [fchunk_{i} for i in range(len(chunks))] documents chunks # 切块后的文本 collection.add( idsids, documentsdocuments, metadatas[{source: example.txt} for _ in chunks], )到这一步知识索引就建好了。你可以多跑几个文件试试把不同话题的文档都塞进去方便后面测试检索效果。4.4 检索与问答写一个最简单的RAG查询循环索引建好后问答部分就是一个检索拼接生成的循环。下面这段代码把完整流程串起来先把提问向量化检索取回TopK个块拼进Prompt再抛给Qwen2生成答案from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores import Chroma from langchain_community.llms import Ollama embed_model OllamaEmbeddings(modelbge-large-zh-v1.5) vectordb Chroma(persist_directory./rag_demo_db, embedding_functionembed_model) llm Ollama(modelqwen2:7b, temperature0.1) query 我们公司的年假制度是怎样的 retriever vectordb.as_retriever(search_kwargs{k: 4}) docs retriever.get_relevant_documents(query) context \n\n.join([doc.page_content for doc in docs]) prompt f请基于以下资料回答问题。如果资料中没有相关信息请直接回复“知识库中未找到相关信息”不要编造。 【资料】 {context} 【问题】 {query} answer llm.invoke(prompt) print(answer)跑通这个循环之后一个最小可用的本地RAG就成型了。后续你可以替换成更专业的向量库比如Milvus的本地模式、加上Rerank模型、做混合检索、接上API服务化。但核心链路就是这三步——检索、拼装、生成。这里给你三个参数建议k值召回块数建议起始设3到5太少可能漏内容太多会塞入噪声。temperature生成参数要调低设0.1到0.2之间减少模型自由发挥的空间避免幻觉。如果回答质量不稳定优先检查检索结果的相关性而不是怀疑生成模型。在Prompt里让模型先把检索内容复述一遍能直观看出问题出在哪一环。5. 常见问题排查实录性能瓶颈、幻觉与图片存储迷思最后这部分我专门把过去一年多实打实遇到的问题挑最有代表性的记录下来。这些问题不是你可能会遇到而是几乎每个人都会遇到早看到就能少走弯路。5.1 检索不到的正确答案问题多半在切块或查询改写现象你明确知道知识库里有某段内容但提问就是检索不到。这种问题有两大元凶。第一是切块把关键句从中腰斩断了——比如一块里开头提到了年假结尾才说了15天中间还混着考勤调休各种话题导致这块向量离哪个问题都不够近检索时被排到后面。解决方式适当增大块与块之间的重叠并尝试把切块大小调整到更贴合文档主题粒度。第二是口语化查询与正式文本之间差异太大。你问怎么算加班费资料里写的是加班工资的计算基数为……向量距离可能不够近。解决方式给查询前加一步改写让大模型先把口语转为书面检索式语句。5.2 回答了但引用了错误内容加一层校验有一种情况比检索不到更隐蔽检索回来的内容是有的但回答引用的那部分其实不对。比如用户问A产品的退货政策检索器召回了退货政策和A产品介绍两块结果模型把A产品的质保政策当成退货政策来回答。要堵住这个问题一方面靠Rerank过滤无关召回块另一方面要在Prompt中要求模型回答时带上引用来源比如根据《A产品售后服务政策》第2.3节……。这样哪怕答案有偏差至少你能追溯可以快速修正。5.3 RAG知识库存图片把存和理解分开看rag知识库能存储图片嘛这个热搜词背后其实是两类需求混在一起。第一类是我的PDF里有插图插图上的信息也要能被搜到处理方式就是前面说的OCR抽文字后按文本处理第二类是用户上传一张产品实拍图想让我对图进行知识问答那必须上多模态模型用图文向量模型比如CLIP类模型把图片变成向量检索时与文本分开处理。个人项目阶段先做OCR文字抽取就够用了别一上来就上视觉大模型成本和复杂度都会成倍上升。5.4 回答太慢先分清瓶颈在检索还是生成很多团队遇到性能问题就想着换更快的模型实际瓶颈经常不在生成。检索慢的原因通常是向量库没有索引优化或者每次请求都重复加载模型生成慢的原因则多半是模型太大、显存不够或者并发请求没有做排队。排查思路很简单先统计单次问答的总耗时再分别统计检索耗时和生成耗时哪个环节占比高就优化哪个。本地个人项目最常见的问题是Embedding模型和生成模型同时加载挤爆内存解决方式是用Ollama的keep-alive机制让模型常驻或者给两个模型分配不同的设备。5.5 RAG的终极瓶颈不要神化把它定位为工具最后聊聊rag瓶颈这个热词。RAG的瓶颈从技术层面看主要是两个一个是检索精度的天花板切块再精细、排序再合理也无法保证100%的召回率另一个是生成模型对上下文的理解上限如果检索回来的多个块之间存在矛盾或表述不一致模型可能无所适从。但从我在项目里的实际体感来看RAG更大的瓶颈是工程化的耐心问题——把原始数据处理好、把切块调好、把检索链路测好这些脏活累活占整个项目的八成精力技术本身反而只占两成。还有一点要泼冷水RAG不是万能的它解决的是外部知识如何引入的问题解决不了模型推理能力不足的问题。如果你的场景需要复杂的多步推理、跨文档归纳总结光靠一个简单的RAG循环是远远不够的那就需要往GraphRAG、Agent工作流这个方向去升级——这也是ontology ragwiki和rag这些热词背后真正指向的方向。6. 几点实操下来最想说的话写到最后分享几条只有真正把RAG用起来才会懂的经验。第一条一定要建立评估集再上线。找业务同事帮忙整理三五十条有标准答案的问答对每次改动切块参数、换Embedding模型、调检索逻辑都用这套评估集跑一遍量化对比准确率。没有评估集你所有的感觉变好了都是玄学。这是我从实际项目中吃过大亏之后才养成的习惯。第二条切块参数不是一次调好的而是一个持续迭代的过程。我见过有人花一周时间死磕切块大小其实更聪明的做法是先跑通全流程再针对具体回答差的问题去回溯到底是哪个环节出了问题对症下药。先能跑再调优不要一上来就追求完美。第三条无论你用什么框架一定要保留原始文本来源。知识库里的每一块内容都要能追溯到原始文件和位置否则出了问题没法复盘。这不仅是工程素养更是企业合规的基础要求。如果看完这些你准备动手了建议先搭一个最小闭环跑通把数据准备好再去研究各种高级特性。RAG的难度不在搭起来而在调好。希望这篇长文能让你在自己动手的时候少踩几个我已经替你踩过的坑。
返回列表