1. 从"模型会说话"到"模型懂你的业务":LLM 与 RAG 到底在解决什么问题
很多人第一次接触大模型,注意力都放在"它能不能写出一段通顺的话"上。但真正把大模型往业务里落地的人,很快会撞到另一堵墙:模型确实会说话,可它说的东西跟你的业务数据、内部文档、产品手册没有半点关系。你问它公司某个型号产品的保修条款,它要么一本正经地编,要么干脆说"我不知道"。这不是模型笨,而是它的知识边界就停在那里——训练数据截止到某个时间点,且不包含你私有的那部分内容。
LLM(Large Language Model,大语言模型)本质上是一个在海量文本上训练出来的概率模型,它做的事情是根据上文预测下一个 token。这个机制决定了它擅长语言组织、推理和泛化,但它并不天然拥有"查资料"的能力。RAG(Retrieval-Augmented Generation,检索增强生成)就是在这个缺口上补的一块板:在模型生成答案之前,先去一个外部知识库里把相关内容捞出来,塞进上下文,再让模型基于这些内容作答。
我习惯用一个类比来解释:LLM 像一位知识面很广但记不住细节的顾问,RAG 则是给他配了一个随叫随到的资料员。顾问负责组织语言、做推理判断,资料员负责在你说出问题后,第一时间把相关的几页材料翻出来递到他手上。顾问再聪明,没有资料员,也只能凭印象回答;资料员再勤快,没有顾问,翻出来的材料也没法变成一段人话。
这套组合解决的核心问题有三个。第一是知识时效性:模型训练有截止日期,RAG 让知识库随时可更新,改一条文档就生效,不用重新训练。第二是私有数据接入:企业内部的产品手册、工单记录、合同条款,这些不可能拿去训练公共模型,但可以通过 RAG 在推理时注入。第三是可溯源:模型给出的答案可以标注"来自哪份文档的哪一段",这在客服、法务、医疗等场景里几乎是刚需。
适合读这篇内容的人,大致分三类:一是刚入门、想搞清楚 LLM 和 RAG 各自定位的开发者;二是正在做企业知识库、智能客服、文档问答的产品或技术负责人;三是已经跑通了 Demo,但发现效果不稳定、想搞清楚底层原理再优化的实践者。不管你是哪一类,接下来的内容都会从原理讲到实操,尽量把"为什么这么做"讲透,而不是只丢一堆步骤。
2. LLM 的工作机制:为什么它既强大又不可靠
2.1 自回归生成:一个 token 一个 token 地"猜"
LLM 的生成过程叫自回归(autoregressive)。给定一段输入(prompt),模型输出第一个 token,然后把这个 token 拼回输入,再预测下一个,如此循环。每一步它输出的其实是一个在整个词表上的概率分布,采样策略决定最终选哪个词。
这里就引出一个常被问到的问题:temperature 到底在干什么。简单说,模型输出的原始分数叫 logits,经过 softmax 变成概率。temperature 是作用在 softmax 之前的一个缩放因子:温度趋近 0,分布变得尖锐,几乎总是选概率最高的词,输出稳定但可能死板;温度调高,分布被拉平,低概率词也有机会被选中,输出更多样但更容易跑偏。做 RAG 问答时我一般把 temperature 压到 0.1 到 0.3,因为这时候我们要的是"忠实于检索到的材料",不是"发挥创意"。
理解这一点很关键:模型没有"事实数据库",它只有概率。所谓"幻觉",本质上是模型在概率上选了一条语言上通顺、但事实上错误的路径。RAG 的价值就在于,把"事实"这部分从模型的参数记忆里剥离出来,交给外部检索来保证。
2.2 上下文窗口:RAG 的物理边界
每个 LLM 都有一个上下文窗口(context window),也就是一次能处理的 token 总量,输入加输出都算在内。早期模型只有 4K,现在主流模型动辄 32K、128K 甚至更长。这个数字直接决定了 RAG 能塞多少检索结果进去。
很多人以为上下文窗口越大越好,塞得越多越准。实测下来完全不是这么回事。有个现象叫"lost in the middle":当上下文很长时,模型对开头和结尾的信息利用得比较好,中间部分容易被忽略。所以 RAG 的关键不是"塞满",而是"塞准"。检索回来的十段内容里如果只有两段相关,剩下八段反而会稀释注意力,甚至引入矛盾信息让模型犯迷糊。
这就解释了为什么 RAG 系统里"检索质量"比"生成质量"更值得投入。生成那部分你基本只能靠选模型和调 prompt,但检索那部分,从切块策略到召回排序,每一步都有大量可优化的空间。
2.3 模型选型:不是越大越好
实际项目里选模型,我一般看四个维度:能力、成本、延迟、可控性。能力包括推理、指令遵循、多语言;成本按 token 计费,长文档问答场景下差异巨大;延迟直接影响用户体验;可控性则涉及能否本地部署、能否微调、数据是否出域。
一个常见的误区是"无脑上最强的模型"。如果你的场景是内部文档问答,问题相对固定,一个中等规模的模型配合好的检索,效果往往不输顶级模型,成本却低一个数量级。反过来,如果问题需要多步推理、跨文档综合,那模型能力就是瓶颈,省不得。
还有一点:模型是会迭代的。今天选定的模型,半年后可能被新版本替代。所以架构上要把模型调用抽象成一层接口,别把某个模型的特性写死在业务代码里。这一点在后面讲工程实现时会再展开。
3. RAG 的完整链路:从文档到答案中间发生了什么
3.1 索引阶段:切块是门手艺
RAG 的第一步是把知识库文档处理成可检索的形式。原始文档可能是 PDF、Word、网页、数据库记录,格式五花八门。处理流程通常是:解析出纯文本,按某种策略切成块(chunk),每块转成向量(embedding),存进向量数据库。
切块(chunking)这一步看似简单,实则最影响效果。切太大,一块里混了好几个主题,检索时噪声大;切太小,语义不完整,模型拿到半句话也没法用。常见的做法是按固定 token 数切,比如 512 或 1024,块之间留一点重叠(overlap),避免把一句话从中间劈开。
但固定长度切块对结构化文档很不友好。比如一份产品手册,按 512 token 硬切,很可能把"参数表"和"注意事项"切到一块里。我的经验是:优先按文档的自然结构切——按标题层级、按段落、按表格边界。如果文档本身有 Markdown 或 HTML 结构,就顺着结构走;如果是纯文本,至少按段落和句子边界切,别硬按字符数。
重叠部分设多少也有讲究。一般设块大小的 10% 到 20%。重叠太少,跨块的语义接不上;重叠太多,检索结果里全是重复内容,浪费上下文。我做过一个对比,同样一份技术文档,重叠从 0 调到 15%,问答准确率能提升七八个百分点,再往上加收益就很小了。
3.2 检索阶段:向量相似不等于语义相关
检索的核心是把用户问题也转成向量,然后在向量库里找最相似的块。相似度常用余弦相似度。这一步听起来很直接,但坑不少。
第一个坑是embedding 模型和生成模型要匹配语言和领域。如果你的文档是中文技术文档,用一个主要在中英文通用语料上训练的 embedding 模型,效果通常还行;但如果是法律、医疗这种专业领域,通用 embedding 可能抓不住术语之间的细微差别。这时候要么换领域 embedding,要么在检索后加一层重排(rerank)。
第二个坑是纯向量检索会漏掉关键词匹配。向量擅长语义相似,但对精确匹配不敏感。用户问"XX-2000 型号的保修期",向量检索可能返回一堆讲保修政策的段落,却没命中那个具体型号。解决办法是混合检索:向量检索加关键词检索(比如 BM25),两路结果融合。这个组合在实战里几乎是标配。
第三个坑是召回数量。召回太少,可能漏掉关键信息;召回太多,噪声大。我一般先召回 20 到 50 条,再用 rerank 模型精排到 3 到 8 条塞进上下文。rerank 模型比 embedding 模型重,但只对少量候选做精排,成本可控,效果提升明显。
3.3 生成阶段:prompt 决定了模型怎么用材料
检索回来的内容怎么交给模型,全靠 prompt 设计。一个基本的 RAG prompt 通常包含三部分:系统指令(你是谁、要遵守什么规则)、检索到的上下文、用户问题。
系统指令里最关键的一条是"只根据提供的上下文回答,如果上下文里没有相关信息,就说不知道"。这条能大幅降低幻觉。但光写这一条还不够,模型有时候会"脑补"上下文里没有的细节。我的做法是再加一条"回答时引用来源编号",逼模型把答案和具体材料对应起来,既方便溯源,也减少了自由发挥。
上下文怎么排列也有讲究。前面提到"lost in the middle",所以最相关的内容应该放在开头或结尾。如果检索结果有明确的相关性排序,就按"最相关放两头、次相关放中间"来排。另外,每段材料前面加上来源标识(比如文档名、章节),模型引用时会更准确。
还有一个细节:上下文里的材料之间如果互相矛盾,模型会怎么处理?实测下来它倾向于选先出现的那个,或者干脆把矛盾都列出来。所以检索阶段的相关性排序很重要,别把低质量内容排在高位。
4. 把 RAG 跑起来:一个可复现的最小实现
4.1 技术栈选择与理由
讲原理容易,落地是另一回事。这里给一个最小可跑的 RAG 实现思路,语言用 Python,因为生态最成熟。核心组件四个:文档加载、切块、embedding、向量库。
文档加载用unstructured或pypdf这类库,能处理 PDF、Word、HTML。切块用langchain-text-splitters里的RecursiveCharacterTextSplitter,它支持按分隔符优先级递归切分,比硬切字符数合理。embedding 可以用开源的sentence-transformers本地跑,也可以用云端 API。向量库本地开发用chroma或faiss就够了,生产环境再考虑milvus、qdrant这类。
选本地 embedding 还是云端 API,主要看两点:数据敏感性和成本。数据不能出域就本地跑,模型小一点、慢一点也能接受;数据不敏感、追求效果就上云端。我一般先用本地模型把流程跑通,效果不够再换云端对比。
4.2 核心代码骨架
下面是一个简化版的索引和查询流程,重点看结构,具体参数按你的数据调。
from langchain_text_splitters import RecursiveCharacterTextSplitter from sentence_transformers import SentenceTransformer import chromadb # 1. 切块 splitter = RecursiveCharacterTextSplitter( chunk_size=800, chunk_overlap=120, separators=["\n\n", "\n", "。", "!", "?", " ", ""] ) chunks = splitter.split_text(raw_text) # 2. 向量化 model = SentenceTransformer("BAAI/bge-small-zh-v1.5") embeddings = model.encode(chunks, normalize_embeddings=True) # 3. 入库 client = chromadb.PersistentClient(path="./rag_db") collection = client.get_or_create_collection("docs") collection.add( documents=chunks, embeddings=embeddings.tolist(), ids=[f"chunk_{i}" for i in range(len(chunks))] ) # 4. 查询 query = "XX-2000 型号的保修期是多久" q_vec = model.encode([query], normalize_embeddings=True) results = collection.query(query_embeddings=q_vec.tolist(), n_results=5) context = "\n\n".join(results["documents"][0])这段代码跑通不难,难的是效果调优。chunk_size和chunk_overlap这两个参数,我建议你拿一批真实问题做评测,别凭感觉设。评测方法很简单:准备 50 到 100 个问题,每个问题标注正确答案所在的文档段落,然后看检索能不能把那段召回回来。召回率上不去,后面生成再强也白搭。
4.3 从 Demo 到可用:必须补的三件事
第一件是重排。纯向量召回的前 5 条里,经常混着不相关的。加一个 rerank 模型(比如bge-reranker),对召回的 20 条重新打分排序,取前 5 条。这一步通常能把准确率拉高一大截,代价是几十到几百毫秒的延迟。
第二件是查询改写。用户的问题往往口语化、有指代、缺上下文。比如多轮对话里用户问"那它的价格呢",单看这句根本不知道"它"指什么。做法是用 LLM 把当前问题结合历史对话改写成独立完整的查询,再去检索。这一步对多轮问答场景几乎是必需的。
第三件是引用与兜底。答案里要能标出引用了哪几段材料,用户点开能看原文。同时要有兜底逻辑:检索结果的相关性分数低于某个阈值时,直接回复"知识库里没有找到相关内容",而不是硬让模型编。这个阈值需要根据你的 embedding 模型和相似度分布来定,一般取一个能过滤掉明显不相关结果的分数。
5. 那些文档里不会写的坑:RAG 实战中的真实问题
5.1 检索到了,但模型没用
这是最让人抓狂的情况:你明明看到检索结果里有正确答案,模型却答非所问。原因通常有三个。
一是上下文太长,关键信息被淹没。解决办法是减少召回数量、提高相关性,或者把最相关的内容放在上下文开头。
二是prompt 指令不够强。模型可能觉得自己的知识比上下文更可靠。这时候要在系统指令里明确"上下文是唯一事实来源",甚至可以要求模型先复述相关材料再作答。
三是材料格式混乱。如果检索回来的文本里夹杂大量表格符号、页眉页脚、乱码,模型理解起来会吃力。索引阶段做好清洗,比生成阶段调 prompt 更有效。
5.2 多轮对话里的指代和漂移
单轮问答好做,多轮就复杂了。用户第一句问"你们有哪些产品",第二句问"那个最贵的多少钱",第三句问"保修呢"。每一句都依赖前文,直接拿去检索必然失败。
我的做法是维护一个对话历史,每次检索前用 LLM 做一次"查询改写",把当前问题补全成独立查询。改写时把最近几轮对话作为上下文,让模型判断指代关系。改写完的查询再走检索流程。这样即使用户说话很省略,检索也能命中。
但改写也有风险:模型可能改错,把原本明确的问题改得偏离。所以改写后的查询最好也做一次相关性校验,或者保留原始查询做双路检索,取并集。
5.3 知识库更新与一致性
知识库不是一次建好就完事。文档会更新、会新增、会作废。如果只往向量库里加不删,旧版本的内容会和新版本一起被检索出来,模型拿到矛盾信息就懵了。
工程上要给每个块打上文档 ID 和版本号,更新文档时先按文档 ID 删掉旧块再插新块。向量库一般支持按 metadata 过滤删除,用起来不难,但要在流程里设计好,别等到线上出问题才补。
另外,文档更新后 embedding 要不要重算?如果只是文字微调,块内容变了就得重算,因为向量是基于内容生成的。这一步可以增量做,只处理变化的文档,不用全量重建。
5.4 成本与延迟的平衡
RAG 的每一步都在烧钱和时间:embedding 调用、向量检索、rerank、LLM 生成。线上系统要在效果和成本之间找平衡点。
几个实用的优化方向:embedding 可以缓存,相同查询不用重复算;rerank 只对 Top-N 做,N 别设太大;生成时控制上下文长度,别把无关材料塞进去;简单问题可以走小模型,复杂问题才路由到大模型。这些策略叠加起来,成本能降一半以上,体验基本不受影响。
6. RAG 之外:Agent、MCP 与知识库的边界
6.1 RAG 和 Agent 是什么关系
热词里经常出现 agent、agentic rag 这些词,很多人搞不清它们和 RAG 的区别。简单说,RAG 是一个"检索-生成"的固定流程,一问一答。Agent 则是一个能自主决策的系统:它可以根据任务需要,自己决定要不要检索、检索什么、检索几次,甚至调用外部工具。
Agentic RAG 就是把检索能力作为 Agent 的一个工具。Agent 面对复杂问题时,可能先检索一次,发现信息不够,再换个关键词检索,或者去查数据库、调 API,最后综合所有信息作答。这比固定流程灵活得多,但也更难控制,容易陷入循环或者调用过多工具导致延迟飙升。
我的建议是:如果你的场景是相对固定的文档问答,老老实实用标准 RAG,稳定可控。如果问题需要多步推理、跨数据源综合,再考虑 Agent 化,而且要设好最大步数和超时,别让它无限循环。
6.2 MCP 解决的是另一个问题
MCP(Model Context Protocol)常被拿来和 RAG 比较,其实它们不在一个层面。RAG 解决的是"怎么把外部知识喂给模型",MCP 解决的是"模型怎么标准化地调用外部工具和数据源"。一个是知识注入,一个是能力扩展。
打个比方:RAG 是给顾问配资料员,MCP 是给顾问配一套标准化的电话接口,让他能直接打给财务、法务、仓库问实时数据。两者可以共存,也可以独立使用。做企业知识库时,RAG 是主力;做需要实时数据的业务助手时,MCP 这类工具调用协议就更重要。
6.3 什么时候该考虑微调
RAG 不是万能的。有些场景 RAG 效果就是上不去,比如需要模型掌握特定的输出格式、特定的推理风格,或者领域术语极其密集。这时候微调(fine-tuning)可能是更好的选择。
但微调有代价:需要标注数据、需要算力、模型更新后要重新调。我的经验是,先用 RAG 把能解决的问题解决掉,把 RAG 解决不了的问题收集起来,看它们有没有共性。如果共性明显、数据量够,再考虑微调。很多时候,优化检索和 prompt 就能解决八成问题,剩下的两成再评估要不要微调。
7. 我踩过的几个具体坑和对应的解法
第一个坑是切块把表格切碎了。一份产品参数表,按固定长度切,参数名和参数值被分到不同块里,检索出来只有半张表,模型根本没法用。后来改成按表格整体切,表格作为一个块,问题就解决了。如果你的文档里表格多,一定要在切块阶段特殊处理。
第二个坑是embedding 模型选错语言。早期用了一个英文为主的模型处理中文文档,检索效果惨不忍睹。换成中文优化的模型后,同样的数据、同样的流程,召回率直接翻倍。选 embedding 模型时,先确认它的训练语料和你的文档语言匹配,这是最基本的要求。
第三个坑是相似度阈值设太高。一开始为了过滤噪声,把阈值设得很高,结果很多本该召回的内容被挡在外面,模型频繁说"不知道"。后来把阈值调低,配合 rerank 精排,效果好很多。阈值这个东西没有标准答案,要拿你的数据跑分布,看相关和不相关结果的分界在哪里。
第四个坑是忽略了对多轮对话的查询改写。上线初期用户反馈"聊两句就答非所问",排查后发现是检索时没带上下文。加上查询改写后,多轮场景的满意度明显提升。这个功能看起来简单,但对多轮体验的影响是决定性的。
第五个坑是没有做检索评测。一开始全靠人工试几个问题,觉得"差不多能用"就上线了。后来建了一个小评测集,才发现某些类型的问题召回率只有一半。有了评测集,每次调整参数都能量化对比,优化方向也清晰了。如果你只做一件事来提升 RAG 效果,我建议就是建评测集。
8. 给不同阶段实践者的建议
如果你刚入门,别急着上复杂框架。先用几十份文档、一个本地 embedding 模型、一个轻量向量库,把"切块-检索-生成"这条链路手动跑通。跑通之后你会发现,大部分问题都出在检索环节,而不是生成环节。理解这一点,后面的优化方向就不会跑偏。
如果你已经在做企业知识库,重点投入在检索质量上:混合检索、rerank、查询改写,这三样是性价比最高的优化。同时把评测体系建起来,没有评测就没有优化。生成那部分,选一个指令遵循好的模型,把 prompt 写清楚,基本就够了。
如果你在考虑 Agent 化,先问自己:标准 RAG 到底哪里不够用?是问题太复杂,还是数据源太多?如果只是效果不好,那大概率是检索没做好,Agent 化解决不了这个问题,反而增加复杂度。Agent 是给"需要自主决策"的场景用的,不是给"效果不好"的场景用的。
最后说一句关于模型选型的体会:别追新,追稳。生产系统里,一个你摸透了脾气、知道它在什么情况下会出错的模型,比一个刚发布、效果榜上第一但你完全不了解的模型更有价值。RAG 这套架构的好处就在于,模型是可以替换的零件,检索和知识库才是你真正的资产。把资产经营好,换什么模型都能跑得不错。