1. 先把RAG这件事说清楚:为什么它突然这么火
这两年大模型圈子里,RAG(Retrieval-Augmented Generation,检索增强生成)几乎成了必聊话题。你随便打开一个技术社区,都能看到“RAG实战”“RAG教程”“RAG瓶颈”这些词。但说实话,很多人聊RAG聊得挺玄乎,绕来绕去就是“给大模型加个外挂知识库”。我换个更直白的说法:RAG就是让大模型学会开卷考试。
在没有RAG之前,大模型回答问题靠的是训练时“背”下来的知识。问题来了,训练数据有截止日期,新知识它不知道;内部细节它没练过,就只能靠“编”。你让它回答公司内部的报销流程、某台设备的维修手册,它大概率会一本正经地胡说八道。RAG解决的就是这件事——先从一个外部知识库里检索出和问题相关的资料片段,再把这些片段拼到提示词里,让大模型基于这些资料来回答。相当于考试时给它一张小抄,只不过这张小抄是系统现场从资料库里翻出来的。
适合读这篇东西的人,我觉得有这几类:想在公司内部落地知识库问答的工程师;正在对比RAG和微调方案的算法同学;以及对“RAG知识库能存图片吗”“RAG和知识图谱到底啥关系”这类话题有疑惑的产品经理。我会按照“架构拆解、核心细节、实操流程、问题排查、进阶扩展”这条线往下讲,尽量把所有环节讲透。
这里先抛结论:RAG不是银弹,但它确实是当前让大模型“接上地气”成本最低、见效最快的方式。下面我们就从它的设计逻辑开始。
2. 核心架构拆解:索引、检索、生成,三个环节一个都不能少
2.1 RAG的三个阶段,其实是在模拟人查资料的过程
你可以把RAG理解成一个三段式流程:索引阶段、检索阶段、生成阶段。
索引阶段做的是“把资料整理好放进档案柜”。原始资料可能是PDF、Word、网页、Markdown,甚至是一堆散落的数据库记录。这些内容不可能直接交给大模型,因为大模型一次能读的字数有限,而且它对杂乱的格式很头疼。所以索引阶段要做这么几件事:先把文档切成一段一段的文本块,再把每一段转换成一个向量(一串能代表语义的数字),最后把这些向量连同原始文本存进一个专门的数据库里。
检索阶段做的是“根据问题找到最相关的几段资料”。用户输入问题后,系统先把问题也转成向量,然后去向量数据库里找“距离最近”的几段文本。这个过程很像你在图书馆查书——先搜关键词,再翻目录,最后锁定几页相关的内容。RAG的检索一般有两种风格:基于关键词的稀疏检索,和基于语义向量的稠密检索。现在主流的做法是两者结合,先用关键词召回一批候选,再用向量语义召回一批候选,最后合并去重。
生成阶段就很好理解了:系统把检索到的资料片段和用户的原始问题拼成一段完整的提示词,交给大模型,让模型“参考以下资料回答问题”。这也是为什么我说RAG像开卷考试——资料是现场给的,答案是在资料基础上组织出来的,不是凭空编的。
这三个阶段看起来简单,但每一步都有大量可优化的空间。我见过不少团队在向量数据库里丢了一堆文档就不管了,结果检索出来的片段驴唇不对马嘴,生成的质量自然一塌糊涂。检索的质量直接决定了生成的天花板,这个道理很多教程不会明说,但实际操作几次你就会深有感触。
2.2 为什么不是上微调,而是用RAG
既然有RAG,就必然有人问:为什么不用微调(Fine-tuning)?这俩到底什么区别?我用一个很生活化的例子给你讲明白。
微调就像请一个老师傅专门培训你三个月,让你把某些知识“内化”进脑子里。效果很好,但成本很高——你得准备大量标注数据,得有GPU资源,还得花时间反复训练,而且一旦知识更新,又得重新训一遍。RAG不一样,它更像在你办公桌上放一套完整的工具书,遇到问题随手翻开那一页查。知识库里的资料可以随时增删修改,不需要动模型本身,成本低、更新快、可控性强。
举个具体场景:一家制造企业想把设备故障排除手册做成智能问答系统。手册有200页,每周还会更新。如果用微调,意味着每次改版都要重新训练模型,光是数据清洗就够呛。用RAG就舒服多了——直接把新版PDF丢进知识库,旧的删掉,查询时自动就能检索到最新内容。
那RAG是不是完全替代微调?也不是。如果某类任务对输出格式有非常固定的要求,比如必须输出三段式报告,或者必须用到某种特定术语体系,微调可以把这些“表达习惯”教给模型。RAG解决“知识从哪来”,微调解决“话怎么说”,两者其实可以搭配使用。但从投入产出比看,绝大多数知识问答场景,先用RAG都是更明智的选择。
2.3 一个RAG系统到底用到哪些组件
知道原理之后,我们再盘点一下一个完整的RAG系统包含哪些组件。这能帮你快速建立起全局观。
| 组件 | 作用 | 常见选型 |
|---|---|---|
| 文档解析器 | 从PDF、Word、网页中提取纯文本 | PyMuPDF、Unstructured、BeautifulSoup |
| 文本切分器 | 把长文档切成合适的文本块 | LangChain的TextSplitter、LlamaIndex的NodeParser |
| 向量化模型 | 把文本变成向量 | OpenAI Embeddings、BGE、M3E、text-embedding-ada-002 |
| 向量数据库 | 存储向量并支持相似度检索 | Chroma、Milvus、Weaviate、Pinecone、Qdrant |
| 重排序模型 | 对检回的片段进行精细化排序 | BGE-reranker、Cross-Encoder |
| 大模型 | 基于资料生成最终答案 | GPT系列、Claude、Qwen、DeepSeek |
这里多说一句,很多人一上来就堆重型组件,觉得组件越高级越好。我的经验是:前期先用轻量级方案跑通闭环。比如向量数据库先用Chroma这种内嵌式的,赶上原型验证阶段完全够用,等数据量到了百万级再迁到Milvus或者Qdrant不迟。追求一步到位,往往会被系统工程细节拖垮进度。
3. 关键细节深挖:切分、向量化、检索策略,每一步都有坑
3.1 文本切分:这个环节决定了检索质量的上限
文本切分是整个RAG里最容易被忽视、但对效果影响最大的环节之一。你想想,如果一段文档被切成几百个碎片,语义被打断,检索时匹配到的片段就是残缺的。你要是切得太碎,比如每50个字一小段,语义信息就不完整;切得太长,比如每2000字一大段,检索到的片段里有效信息占比低,大模型容易被无关内容干扰。
怎么切比较合理?我的经验是分两步走:先按文档结构切,再按固定窗口微调。比如一篇PDF手册,先按章节、按段落边界去切,尽量保证每个块在语义上是相对完整的;然后再设一个最大长度上限,比如512个token或800个汉字,超了就再切。还要注意同一个段落里如果包含表格、列表,最好单独处理,因为表格结构用纯文本表示容易散架。
另一个容易踩的坑是“上下文丢失”。比如你在第100页提到“该设备”指的是第95页介绍的一款型号,如果你切割时没有保留足够的上下文,检索出来的文本块里只有“该设备”这个词,模型根本不知道它代指什么。针对这种情况,我常用的一个技巧是:切片时给每个块带上文档标题、章节路径等元数据,这样模型至少知道这段内容来自哪个章节、讲的是什么主题。
3.2 向量化模型的选型:别盲目追求大模型,要选对路子
向量化就是把文本“翻译”成一串数值的过程。选什么样的向量化模型,直接影响检索的召回质量。市面上选择很多,OpenAI有text-embedding-ada-002,开源的有BGE系列(BAAI General Embedding)、M3E(Moka Massive Multilingual Embedding)等。
这里有个很实际的建议:如果内容以中文为主,可以考虑BGE或M3E系列,它们在中文语义上的表现往往比英文原生的模型更出色;如果业务涉及中英混合,OpenAI的Embedding模型通用性也不错,但需要注意API调用成本。我一直觉得,用开源模型跑本地向量化没有多大事儿,特别是数据量在百万级以下时,BGE-large的表现足够满足绝大多数场景。
还有一个容易踩的坑:不要把不同模型生成的向量混在一个库里面。不同模型的向量空间不一样,混着存会导致检索效果急剧下降。我见过不少团队因为中途换了Embedding模型,向量库里新旧向量混杂,结果检索结果完全失控。换模型可以,但必须全部重新向量化一遍。
3.3 检索策略:单纯靠向量检索不够,要做好“混合检索+重排序”
很多人第一版RAG就是向量检索+TopK召回,结果发现效果不稳定——有时候挺好的,有时候答非所问。问题往往出在向量检索本身的缺陷上:它擅长处理语义相近的表达,但在处理精确关键词、产品型号、编号这类信息时,效果就很一般。
举个场景:用户问“派克液压泵PV140的密封件型号”,如果知识库里原文写的是“PV140 泵密封组件 型号 P3042”,向量相似度可能找得到,但如果你用BM25这类关键词检索,能更精准地命中的“PV140”“P3042”这些硬编码。这就是为什么我建议用混合检索——把稀疏检索(BM25)和稠密向量检索结合起来,分头召回,再做融合和去重。
融合之后还要过一道“重排序”(Rerank)环节。重排序模型会基于用户问题,对召回的候选片段做更精细的“配对打分”,把最相关的三到五段排在前面,交给大模型。这一步对最终答案质量的提升非常明显,我实测下来,加了重排序之后答案的准确率和连贯性都有肉眼可见的提升。
那具体到实现层面,你可以看一下LangChain里BM25Retriever加向量Retriever的EnsembleRetriever,或者LlamaIndex里的QueryFusion,都是比较成熟的方案。重排序方面,BGE-reranker是开源里表现不错的选手,模型体积也不大,部署成本可控。
3.4 向量数据库怎么选:本地轻量还是生产级集群
向量数据库的选择经常被问起。我先给个大体判断:数据量在百万级以内,Chroma、FAISS、Qdrant这些都能跑得挺舒服;数据量到了千万级,或者需要分布式部署、高并发查询,再考虑Milvus、Weaviate或者Pinecone这类企业级产品。
以Mac本地开发为例,Chroma可以说是最友好的选择——它是一个嵌入式的向量数据库,不需要单独启动服务,直接通过Python API读写就行,特别适合做原型验证和本地调试。步骤大概是:
# 安装Chroma pip install chromadbimport chromadb from chromadb.utils import embedding_functions # 指定嵌入模型 embedding_func = embedding_functions.SentenceTransformerEmbeddingFunction(model_name="BAAI/bge-small-zh-v1.5") # 创建客户端(本地持久化) client = chromadb.PersistentClient(path="./chroma_db") # 创建集合 collection = client.get_or_create_collection( name="knowledge_base", embedding_function=embedding_func ) # 写入文档 collection.add( documents=["RAG全称Retrieval-Augmented Generation,是一种检索增强生成技术..."], ids=["doc_001"] ) # 查询(返回top 3相似片段) results = collection.query(query_texts=["RAG是什么"], n_results=3) for doc in results["documents"][0]: print(doc)这套代码在Mac上跑起来没有任何压力,也是我平时做原型最快的方式。等后面数据量大了,再迁移到Docker里跑的Qdrant或者云端的Pinecone,架构上做一层抽象,切换成本并不高。
4. 手把手落地:在Mac上从零搭建一个RAG知识库问答系统
4.1 环境准备与依赖安装
下面我带你完整走一遍在Mac上搭建RAG知识库的过程。假设你已经有Python 3.9+环境,并且装好了Anaconda或者Miniconda。Mac的Apple Silicon芯片在运行本地模型时性能还行,特别是能利用MPS加速的部分,但大模型生成环节还是建议通过API来调用,本地重点跑Embedding和检索这几块。
先创建项目目录和虚拟环境:
mkdir rag_demo && cd rag_demo python3 -m venv .venv source .venv/bin/activate然后安装必要依赖:
pip install langchain langchain-openai chromadb sentence-transformers pypdf bm25用到的库各司其职:LangChain负责把RAG流程串联起来;Chroma做向量存储;sentence-transformers提供本地Embedding模型;pypdf用来解析PDF;bm25提供关键词召回。
4.2 文档导入与切分
这一步的核心是“把PDF变成可检索的文本片段”。我准备了一份模拟的设备手册PDF作为示例,实际操作时你可以换成你自己的文档。
from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter loader = PyPDFLoader("./device_manual.pdf") documents = loader.load() # RecursiveCharacterTextSplitter会优先按段落切分,保持语义完整性 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=80, separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""] ) docs = text_splitter.split_documents(documents) print(f"切分为 {len(docs)} 个文本块")chunk_overlap这个参数我重点说一下。它让相邻文本块之间保留80个字符的重叠区域,目的是避免一句话正好被切分边界截成两半,导致语义撕裂。工程里很多人为了省存储把overlap设成0,结果检索recall掉得厉害,这属于典型的省小钱亏大钱。
4.3 向量化与写入向量库
有了文本块之后,要把它转成向量存到Chroma里。这里建议在Mac上直接用开源的BGE模型,不需要调用API,隐私性也更好。
from chromadb.utils import embedding_functions # 用本地BGE模型生成向量 embedding_func = embedding_functions.SentenceTransformerEmbeddingFunction(model_name="BAAI/bge-small-zh-v1.5") import chromadb client = chromadb.PersistentClient(path="./chroma_db") collection = client.get_or_create_collection(name="manual_qa", embedding_function=embedding_func) # 批量写入向量库 collection.add( documents=[doc.page_content for doc in docs], ids=[f"doc_{i}" for i in range(len(docs))], metadatas=[{"source": doc.metadata.get("source", "")} for doc in docs] ) print(f"成功写入 {collection.count()} 条向量数据")这一步执行完之后,你的Mac上就有一个可查询的知识库了。可以简单验证一下:
result = collection.query(query_texts=["设备报警代码E02是什么含义"], n_results=3) for doc in result["documents"][0]: print(doc)4.4 构建检索问答链路
接下来把检索和生成串成一个完整的问答链路。假设你使用OpenAI的GPT或者兼容的API接口作为生成模型,代码可以这样组织:
from langchain_openai import ChatOpenAI from langchain.chains import RetrievalQA from langchain.vectorstores import Chroma # 初始化向量库 vectorstore = Chroma( collection_name="manual_qa", persist_directory="./chroma_db", embedding_function=embedding_func ) # 初始化生成模型 llm = ChatOpenAI( model="gpt-4o-mini", temperature=0, api_key="your_api_key" ) # 构建检索问答链 qa_chain = RetrievalQA.from_chain_type( llm=llm, retriever=vectorstore.as_retriever(search_kwargs={"k": 4}), return_source_documents=True ) # 测试问答 response = qa_chain.invoke({"query": "设备报警代码E02是什么含义,应该怎么处理?"}) print(response["result"])如果你不想用OpenAI的API,Mac本地其实也有替代方案——通过Ollama跑Qwen系列模型,LangChain也支持Ollama接口,只需要把ChatOpenAI替换成ChatOllama,并配置好模型名称即可。这样整套系统完全本地化运行,数据不出内网,适合有隐私要求的场景。
4.5 加上混合检索和重排序,效果再上一个台阶
刚才这版是最基础的“向量检索 + LLM生成”流程,跑通没问题,但离“好用”还有一段路。我建议你在此基础上加上BM25关键词召回和重排序这两个模块。
from langchain.retrievers import BM25Retriever, EnsembleRetriever from langchain_community.embeddings import HuggingFaceEmbeddings from langchain.retrievers.document_compressors import CrossEncoderReranker from langchain.retrievers import ContextualCompressionRetriever # 创建BM25检索器(基于原始文本) bm25_retriever = BM25Retriever.from_texts([doc.page_content for doc in docs]) bm25_retriever.k = 4 # 创建向量检索器 embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5") vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 4}) # 混合检索:权重各占一半 ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever, vector_retriever], weights=[0.5, 0.5] ) # 引入重排序模型 reranker = CrossEncoderReranker(model_name="BAAI/bge-reranker-base") compression_retriever = ContextualCompressionRetriever( base_compressor=reranker, base_retriever=ensemble_retriever ) # 用新的检索器替换原来的retriever,其余问答链路不变这一步做完之后你再测几个刁钻问题,会发现回答的精准度有明显提升,尤其是涉及型号、编号、具体步骤这类需要“抠字眼”的问题。
5. 常见问题与排查实录:我把踩过的坑都列在这里
5.1 检索到的内容不相关?先从“切分”和“召回”找原因
这是咨询最多的问题。答案不准,十有八九是检索阶段没有把真正有用的片段找出来。你可以用一个很简单的办法排查:把检索到的片段原文打印出来,自己读一遍。看看这些片段是不是真的和问题相关。
如果检索结果本身就跑偏了,那大概率出现在这几个地方:
- 切分粒度不合适,有效信息被切散或者混入了太多噪音;
- Embedding模型和文档语言不匹配,中文文档用了英文模型;
- TopK值取得太小,真正相关的片段排在第五名以后被截掉了;
- 没有做混合检索,硬编码型号类信息通过向量检索找不准。
排查的策略也很直接——分步验证。固定其他环节,单独调整切分的chunk_size和overlap;再单独比较不同Embedding模型在同一批数据上的召回效果。这种逐变量排查的方式最有效,不要眉毛胡子一把抓。
5.2 知识库能不能存图片?这个问题要分两个层面看
热搜里“RAG知识库能存储图片嘛”这个话题很有意思,我从实操角度给你拆一下。传统的RAG知识库主要存的是文本和向量,图片本身是不能被大模型直接“阅读”的。如果你往知识库里塞一张JPEG图片,检索流程根本没法处理。
但这不代表图片内容没办法纳入RAG。常见的解法有两种:
第一种,把图片转成描述文字。你可以用多模态模型(比如GPT-4V、Qwen-VL或者开源的CogVLM)对图片生成一段文字描述,再把这段描述存入知识库。这样用户问“那张设备连接示意图里说了什么”,系统就能通过检索这段文字描述来间接回答问题。
第二种,使用多模态向量模型。CLIP这类模型可以把图片和文本映射到同一个向量空间,图片可以不经过文字转换直接参与向量检索。但要注意,多模态向量检索在工程实现上比纯文本复杂不少,需要额外的图片预处理流程。
我给的建议很务实:如果你的场景里图片比例不高,先用“图片转文字描述”的方式把图纳入知识库,成本低、效果好。等图片数量很大,而且检索需求确实需要“以图找图”时,再上多模态向量方案。
5.3 “RAG瓶颈”到底在哪里?如何针对性优化
“RAG瓶颈”这个词最近频繁出现。我观察下来,RAG在实际落地中真正卡脖子的地方主要有这么几个。
首先是文档解析的瓶颈。很多真实业务文档是扫描件、表格、多栏排版,解析器一上手就乱码、丢内容。这个瓶颈不在RAG框架本身,而在“进库”环节。我常用的思路是在解析阶段多花功夫——表格用专门的表格解析工具,扫描件先走OCR(光学字符识别)流程。预处理做得越细,后面检索越省心。
其次是检索质量的瓶颈。向量检索有天花板,它对复杂意图和模糊指令的理解能力有限。比如“我想找那个设备在高温环境下的运行注意事项”,这里“高温环境”可能文档里写的是“环境温度超过50℃”——靠向量相似度就很难命中,需要检索策略更聪明,比如引入同义词扩展、查询改写。
再就是系统集成的瓶颈。一个真正好用的RAG系统,往往需要对接权限系统、文档管理系统、日志分析系统,不是一个Python脚本能搞定的。这也是不少项目从Demo到落地之间最大的鸿沟。
5.4 常见问题速查表
| 症状 | 可能原因 | 建议处理 |
|---|---|---|
| 检索结果和问题无关 | 切分粒度不当;Embedding模型不匹配 | 调整chunk_size/overlap;换用中文语义模型 |
| 答案内容正确但过于笼统 | TopK值太小,上下文不足 | 适当调大k值,比如从3调整到5 |
| 遇到型号/编号就答错 | 向量检索不擅长精确匹配 | 加入BM25关键词召回,改用混合检索 |
| 同一问题每次答案不一致 | 生成模型temperature过高;检索结果不稳定 | temperature设为0;检查向量库是否有重复数据 |
| 知识库更新后效果变差 | 向量库中旧数据没有清理 | 更新文档时同步删除旧向量,避免新旧数据混杂 |
| 存储图片后检索不到 | 图片无法直接被文本检索 | 用多模态模型生成图片描述再入库 |
5.5 优化效果的一些实测心得
我自己在一次次调优里最大的体会是:不要一上来就想着换更强的模型、上更复杂的框架。先把基础环节打磨好,比什么都管用。
比如我测试过一个客户文档问答项目,一开始准确率只有65%左右。我做了三件事:把chunk_size从1000改到400,加了80字符的overlap;把单向量检索改成BM25+向量混合检索;加了一个BGE-reranker重排序。三天时间,准确率从65%干到了88%。这三件事没有一个涉及大模型本身的改动,但变化就是这么明显。
还有一个容易被忽略的因素是提示词设计。同样的检索结果,给模型的提示词措辞不同,输出质量也有明显差别。你可以在系统提示词里写明:“请严格基于以下参考文档回答问题,不要使用文档之外的知识;如果文档中没有相关信息,请直接告知无法回答。”这种约束性提示词能有效缓解模型“自由发挥”的毛病。
6. 进阶方向:RAG和知识图谱、Ontology的结合,以及多模态扩展
6.1 结构化知识库 vs RAG知识库:两条不同路线
“RAG知识库和结构化知识库怎么区分?各自什么应用场景?”这是个好问题。先说结论:RAG知识库适合非结构化文本的语义检索,结构化知识库(比如知识图谱或Ontology)适合精确关系的逻辑查询。
RAG知识库存储的是文档切片和向量,它的优势是“你不需要提前设计数据结构,扔一堆文档进去就能用”。缺点是它对关系型问题比较吃力。比如问“A部门的张经理和B部门的技术负责人是不是同一个项目的成员”,如果资料分散在多份文档里,RAG每次都只能检索到零散片段,回答时很难组织出完整的逻辑关系。
结构化知识库就不一样。它先把实体、关系抽取出来,比如“张三—任职于—技术部”“技术部—负责—ERP项目”,然后用图数据库查询。这种模式回答关系问题非常精准,但代价是前期必须做大量的数据建模、抽取、清洗工作。
所以最佳实践不是二选一,而是把两者结合起来。非结构化的长文本走RAG检索,结构化的实体关系走图数据库查询,两条路径的结果一起交给大模型整合。这也是“GraphRAG”思路能火起来的原因。
6.2 Ontology RAG是怎么回事
最近“ontology RAG”这个词热度也在涨。Ontology(本体)本质是一套对领域概念和关系的显式定义,比知识图谱更偏“逻辑层”。我理解Ontology RAG的核心思路是:让RAG在检索时能理解领域里的概念层级和关系,而不是全靠向量相似度去猜。
举个例子,在医疗场景里,“高血压”和“血压升高”在文本里可能是两个不同的表达,但Ontology定义了它们之间的等价和从属关系。传统RAG靠向量也许能模糊匹配到,但有了Ontology,系统可以在检索前先做一次概念扩张——把用户问题映射到概念体系里,再把相关概念对应的资料片段都召回出来。这种方式对专业领域的精准召回帮助非常大。
不过Ontology RAG的落地门槛也不低——得先把领域本体建立起来,这本身就需要领域专家深度参与。对我来说,现阶段这更像“锦上添花”的能力,把基础RAG做好了,再考虑是否引入Ontology提效。
6.3 多模态RAG:除了文字,还有图、表、音频
顺着“图片能不能存”的问题往下聊——多模态RAG是目前比较热的扩展方向。核心是让知识库不仅能检索文本,还能检索图片、表格、甚至音视频内容。
表格的处理是个特例。表格本质上是结构化信息,直接转成文本再切分,会丢失行列关系。我测试过一些解析工具,把表格转成Markdown格式再入库,比纯文本提取效果明显更好,因为Markdown保留了表格的结构语义,向量模型对这类文本的理解准确度更高。
图片的处理前面已经提过——用多模态模型生成图片描述,再把描述作为文本入库。音频视频也是类似思路,先转写文字再进知识库。所以“多模态RAG”目前的工程范式并没有那么玄妙:统一用多模态模型把非文本内容“翻译”成文本,然后走标准的RAG流程。
6.4 从Demo到生产:值得关注的几个实践建议
如果这套RAG系统要真正上生产环境,我会建议你重点考虑这几件事:
第一,做好数据更新机制。知识库不能只加不减,文档改版时要能识别出哪些旧版本需要下线,哪些新版本需要入库。我见过太多知识库越来越“脏”,最终检索失控。
第二,加一层查询改写(Query Rewrite)。用户的原始问法往往不够规范,可以先让大模型把口语化问题改写成语义更明确的查询,再去知识库检索。这一步能显著提升召回准确率。
第三,建立评估体系。准备一组标准测试集,每次调整完策略都跑一遍,用召回率和答案准确率来衡量变化。没有评估体系的优化,都是“感觉主义”。我在后面会再具体展开。
6.5 怎么评估你的RAG系统好不好用
最后聊一个常被忽略但极其重要的话题——评估。RAG系统的效果评估不能靠“我随手问了几个问题感觉还行”来验证。
评估分两个层面:检索评估和生成评估。
检索评估用Recall@K、Precision@K这些指标,测量系统是否把相关片段召回了。做法是准备一组“问题-正确答案片段”的数据集,跑完检索后看正确答案片段有没有出现在TopK里。
生成评估更复杂。你可以用公开的RAG评测基准(比如RGB、RAGAS),也可以自己攒一批领域问题,让大模型打分或者人工打分。关键是评估要和优化闭环跑起来——改一个参数,跑一遍评估,对比分数,再决定要不要采纳这次改动。我以前犯过的错误是“凭感觉调参”,浪费了大量时间,直到建立了评估集之后,整个优化过程才变得可度量、可复现。
我最后再分享一个实操技巧:把检索到的原文片段和最终回答一起展示给用户。这样用户能自己判断回答是基于资料生成的还是模型瞎编的,也方便你排查问题。这个小改动对信任度的提升非常明显,很多商业产品里都把这个作为标配功能。如果你在做知识库问答,不妨第一时间加上。