欢迎光临
我们一直在努力

做RAG别再死磕大模型!90%的坑都栽在检索和上下文工程

文章目录

      • 前言
      • 1. 先搞懂底层逻辑:为啥大模型必然幻觉,RAG凭啥能治?
        • 1.1 大模型的本质,就是“猜下一个词”
        • 1.2 RAG的核心:把证据塞进去,逼它复述
      • 2. 切块切坏了,检索直接崩——Chunking是流水线命门
        • 2.1 检索的最小单位,不是文档,是chunk
        • 2.2 最经典的反模式:固定大小一刀切
        • 2.3 正解:父子Chunk,检索用针,生成用线团
      • 3. Embedding不是魔法——两条铁律,破一条就翻车
        • 3.1 第一条铁律:入库和检索,必须同模型同版本
        • 3.2 第二条铁律:垃圾进,垃圾出
        • 3.3 正确操作:写死模型,复用函数
      • 4. 纯向量检索还不够——混合检索+RRF,解决精确词痛点
        • 4.1 稠密向量的软肋:抓不住精确词
        • 4.2 2026年标准答案:混合检索+RRF融合
        • 4.3 别踩坑:召回越多≠越好,小心Lost in the Middle
        • 4.4 正解:over-fetch + rerank
      • 5. 重排是单点ROI最高的优化,生成端必须留拒答退路
        • 5.1 加个重排器,质量直接抬一截
        • 5.2 生成端没拒答,前面全白搭
      • 6. 朴素RAG早就过时了?聊聊三个新范式
        • 6.1 “RAG已死”是伪命题
        • 6.2 GraphRAG:多跳推理的神
        • 6.3 Adaptive RAG:省钱又快的自适应检索
        • 6.4 Agentic RAG:听起来很美,但还早
      • 7. 上生产必看:8个踩坑清单,逐条打勾
        • 7.1 第一类:数据&切块层
        • 7.2 第二类:检索&重排层
        • 7.3 第三类:安全&治理层

在这里插入图片描述
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,
传送门https://blog.csdn.net/HHX_01

前言

做LLM应用的朋友,我赌五毛,你一定踩过RAG的坑。

本地调试的时候多顺啊——FAISS一搭,embedding一调,similarity_search(top_k=5)一跑,20条样本答得有模有样。你信心满满拍板上线,觉得RAG不就是接个向量库嘛,有手就行。

结果一周之后,三连暴击直接给你干到怀疑人生。

老板堵你工位问“报销政策改了哪几条”,它张嘴就背去年的旧版——合着索引从建好那天起就躺平了,从来不更新是吧。

法务同事的大名,赫然出现在给销售客户的回答里——多租户数据隔离?不存在的,A公司的文档直接喂给B公司的人了。

用户甩过来一个精确编号“SR-2026-088”,它答得天马行空——纯向量检索根本抓不住这种精确字符串,全靠语义瞎蒙。

这时候你第一反应是不是:模型不行!换大的!embedding不够强!换更贵的!

我以前也这德行。直到踩了一圈坑、被老板骂了三回才明白:RAG的坑,九成在检索与上下文工程,根本不在模型。你换再大的模型,救不了稀烂的检索。

1. 先搞懂底层逻辑:为啥大模型必然幻觉,RAG凭啥能治?

1.1 大模型的本质,就是“猜下一个词”

很多人没想明白,大模型自回归生成的每一步,都是在前面文本的基础上,从词表里挑概率最高的那个词往下接。

它的训练目标是“像人话”,不是“说真话”。概率分布里根本没有“真/假”这个维度,它根本不知道自己“不知道”。遇到超出预训练语料的内容——新知识、私域文档、刚改的政策——它只能用“最像正确答案的token”给你凑,这就是幻觉的来源,纯纯机制性的,改不了。

1.2 RAG的核心:把证据塞进去,逼它复述

RAG厉害在哪?它不动模型一丁点儿参数,就是把答案的证据在推理的时候动态注入prompt,引导模型去“复述证据”,而不是“凭空生成”。

效果有多猛?给大家上点实测数据:

腾讯云测的,RAG相对纯生成,幻觉直接减40%到71%;

麦肯锡的报告,事实性错误率能降92%;

Mayo Clinic的案例更狠,RAG准确率92.7%,微调才78.4%,但成本只有微调的5%到8%——尤其是财务规则这种每季度都更的场景,微调根本不划算。

说白了,新知识、私域数据、高频变更的场景,RAG比微调又便宜又准。

给大家看个最基础的grounded prompt写法,核心就是强制模型只基于资料作答,答不上来就说不知道:

你是一个严谨的问答助手。只能依据下面【资料】中的内容回答,
【资料】之外的信息一律视为未知。
若资料不足以回答,必须回复"根据现有资料无法回答"。
回答中每个事实后用 [1][2] 标注出处。

【资料】
[1] 2026 Q3 报销政策:差旅报销上限提升至 800 元/天……
[2] 2026 Q3 报销政策:发票须在发生后 30 日内提交……

2. 切块切坏了,检索直接崩——Chunking是流水线命门

2.1 检索的最小单位,不是文档,是chunk

太多人忽略这一步了。你把一句话从中间砍断,语义直接碎成渣,后面句向量算得再准,也召回不到正确的块。

切块不是随便切切就行,主流的有五种策略,各有各的适用场景:

固定大小:每200-500 token一刀切,重叠10%-20%。好处是通用、最快,坏处是容易切坏句子。

语义切分:按句号、段落在语义边界断。适合长文、论述类内容。

递归结构:按#、##标题层级递归下钻。适合Markdown、代码文档。

文档感知:保留标题层级、表格、列表结构。适合技术手册、API文档。

父子Chunk:小块(~128 token)用于检索,父大块(512-1024 token)喂LLM。既要准度又要上下文完整,选它。

2.2 最经典的反模式:固定大小一刀切

我见过太多图省事的,直接固定大小切到底。比如“报销上限800元,发票须在30日内提交”这句话,正好从中间劈成两半。

召回的时候只命中半句,模型拿到的是“报销上限800元”的半截,或者“发票30日内提交”的半截,能答对标了才怪。

2.3 正解:父子Chunk,检索用针,生成用线团

父子Chunk的思路特别妙:检索用小粒度的“针”,精准命中目标;返回的时候带回它所属的大粒度“线团”,保证上下文完整。

给大家看LangChain风格的最小实现:

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.retrievers import ParentDocumentRetriever

# 父文档:大块,喂给 LLM
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100)

# 子文档:小块,用于向量检索
child_splitter = RecursiveCharacterTextSplitter(chunk_size=128, chunk_overlap=16)

retriever = ParentDocumentRetriever(
vectorstore=vs, # 用 child 块建索引
docstore=store, # 用 parent 块存原文
child_splitter=child_splitter,
parent_splitter=parent_splitter,
)

hits = retriever.get_relevant_documents("Q3 报销上限是多少?")
# 召回的是 128-token 小子块,但返回的是它所属的 1024-token 父块

3. Embedding不是魔法——两条铁律,破一条就翻车

3.1 第一条铁律:入库和检索,必须同模型同版本

向量化不是什么“文字变魔法数字”的黑科技。两个向量能算余弦相似度的前提,是它们在同一个向量空间里。

入库用bge-m3(1024维),检索用text-embedding-3(3072维),维度都不一样,算出来的相似度有个屁意义?召回全是随机的,纯靠运气。

这是数学前提,不是调参能救的。别整那些“我调调温度试试”的没用操作。

3.2 第二条铁律:垃圾进,垃圾出

还有人直接把PDF整页丢进去embed?页眉页脚、页码、表格乱码、水印,全给你编码进向量里了。

embedding可不会帮你过滤噪声,它忠实的把所有内容都编码成向量。你喂进去的是垃圾,检索出来的自然也是垃圾。

3.3 正确操作:写死模型,复用函数

把模型名和版本号直接写死在代码里,别用什么latest,哪天偷偷更了版本你都不知道。入库和检索两端复用同一个embed函数,绝不另起炉灶。

# 入库与检索必须共用同一个模型 + 同一个版本
EMBED_MODEL = "BAAI/bge-m3" # 写死,别用 "latest"

def embed(texts: list[str]) > list[list[float]]:
# 入库 cleansing:去页眉页脚、抽表格为文字、按 chunk 切
cleaned = [clean_for_embed(t) for t in texts]
return client.encode(cleaned, model=EMBED_MODEL, normalize_embeddings=True)

# 检索端 reuse 同一函数,绝不另起一个模型
query_vec = embed(["Q3 报销上限是多少?"])[0]

4. 纯向量检索还不够——混合检索+RRF,解决精确词痛点

4.1 稠密向量的软肋:抓不住精确词

稠密向量擅长找“语义相近”的,但对精确编号、专有名词、报错码这种东西,直接抓瞎。

你搜“SR-2026-088”,它可能给你召回一堆“服务请求处理规范”,就是找不到精准的那一条。纯靠语义,就是这德行。

4.2 2026年标准答案:混合检索+RRF融合

现在行业默认的玩法,是稠密向量+BM25稀疏检索,再用Reciprocal Rank Fusion(RRF)做融合。

BM25负责抓精确词,编号、报错码一抓一个准;向量负责语义近义。两者各召回top-k,然后按排名融合,不依赖各自的分数尺度,特别好用。

4.3 别踩坑:召回越多≠越好,小心Lost in the Middle

很多人觉得,top_k设大点,召回多了总没错吧?大错特错。

斯坦福、哈佛、OpenAI联合测过:给模型10段资料,关键信息放在第5-7位的时候,准确率比放在第1-3位低约22%。这就是Lost in the Middle——你塞得越多,关键信息越容易被埋在中间,模型直接视而不见。

把top-20不分青红皂白全塞进prompt,纯属费力不讨好。

4.4 正解:over-fetch + rerank

正确的做法是:先宽召回top-20,保证召回率;再用重排器精排,取top-3到5喂给模型。既保召回,又压噪音,性价比拉满。

给大家个RRF的最小实现:

import math

def rrf(lists_of_ranks, k=60):
"""lists_of_ranks: [向量topk文档id, BM25topk文档id, …]"""
score = {}
for ranks in lists_of_ranks:
for i, doc_id in enumerate(ranks):
score[doc_id] = score.get(doc_id, 0.0) + 1.0 / (k + i + 1)
return sorted(score, key=score.get, reverse=True) # 融合后重排

# 向量 top20 + BM25 top20 → RRF 融合 → 送 reranker 取 top5
fused = rrf([dense_topk(20), bm25_topk(20)])
top5 = cross_encoder_rerank(query, fused[:20])[:5]

5. 重排是单点ROI最高的优化,生成端必须留拒答退路

5.1 加个重排器,质量直接抬一截

整条RAG流水线里,加一个Cross-Encoder重排器,是投入产出比最高的一步,没有之一。

为啥?Bi-encoder(也就是embedding)为了能离线建索引,query和doc各自编码,互不交互,精度天生有天花板。

Cross-Encoder不一样,它把query和doc拼在一起过一遍模型,能捕捉特别细微的相关性,准度高很多。缺点是慢,所以只能用在召回后的少量候选上。

标准打法就是:embedding宽召回top-20 → Cross-Encoder重排 → 取top-3~5。就这一步,答案质量直接上一个台阶。

5.2 生成端没拒答,前面全白搭

检索做得再好,生成端如果没有“我不知”的退路,照样前功尽弃。

没有拒答路径,只要资料一缺,模型立刻回到“按概率编”的老路子,幻觉直接回潮。你前面花那么多功夫优化检索,全打水漂。

生成端必须补两件事:

第一,每个事实标注出处[1][2],可追溯、可审计,出了问题能定位到源文档;

第二,强制拒答,资料不足就老老实实说“根据现有资料无法回答”,别硬撑。

给大家看重排器的调用示例,以bge-reranker为例:

from sentence_transformers import CrossEncoder

reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")
pairs = [(query, doc) for doc in retrieved_docs[:20]]
scores = reranker.predict(pairs)
ranked = [d for _, d in sorted(zip(scores, retrieved_docs[:20]), reverse=True)]
context = ranked[:5] # 只取 top5 喂给 LLM

6. 朴素RAG早就过时了?聊聊三个新范式

6.1 “RAG已死”是伪命题

最近总有人说“RAG已死”,纯纯扯犊子。死的是朴素RAG——也就是检索→拼prompt→生成那种原始玩法,RAG本身一直在进化。

而且RAG和长上下文根本不是替代关系,是互补的。说白了就是“RAG负责找,长上下文负责读”。现在业内讲的“上下文工程”,本质上RAG正在变成上下文管理这门大学科的一个子集。

6.2 GraphRAG:多跳推理的神

第一个要说的就是微软的GraphRAG,24年中开源的。

思路是让LLM从文档里抽取实体和关系,建知识图谱,再做社区检测和层级摘要。最擅长的就是多跳推理,比如“谁参与了项目X,用了哪笔预算,对应哪条政策”这种跨好几个文档的问题,朴素向量检索根本答不了。

实测数据也狠:多跳QA的Recall@5从73.4%涨到87.8%,直接涨了近20个点;复杂问题回答率89%,传统的才47%。

缺点是索引成本高,不过后来出的LazyGraphRAG把成本压到了约0.1%,预算紧也能上。

6.3 Adaptive RAG:省钱又快的自适应检索

第二个是Adaptive RAG,自适应检索。

思路也很简单:先用一个轻量分类器判断查询类型,再路由。简单问题直接答,普通问题单跳检索,复杂问题多跳检索。

实测检索准确率+40%,延迟-40%,API成本直接砍30%到50%,性价比拉满,生产环境特别实用。

6.4 Agentic RAG:听起来很美,但还早

第三个是Agentic RAG,把检索本身做成ReAct行动循环。模型自己决定“再查一次”“换关键词”“查哪个数据源”。

听起来确实很智能,但Gartner2026年初估计,也就约30%进入生产,整体还处于早期阶段,尝鲜可以,大规模落地谨慎。

7. 上生产必看:8个踩坑清单,逐条打勾

前面讲的都是底层机制,这里直接给大家上可落地的避坑清单。每条都是我实打实踩过的,症状+解法都给你,上线前对着逐条打勾就行。

7.1 第一类:数据&切块层

1. 切块切坏句子

症状:检索经常命中半句,答非所问。

解法:用语义、递归或者父子chunk;核心文档直接上父子Chunk,小块检索、父块生成。

2. Embedding脏数据(垃圾进垃圾出)

症状:相似度算出来没意义,召回全随机。

解法:入库/检索同模型同版本写死;PDF先清洗,去页眉页脚、表格转文字,再做向量化。

7.2 第二类:检索&重排层

3. 纯向量检索漏精确词

症状:编号、报错码这类精确字符串答不上来。

解法:上混合检索(稠密+BM25+RRF),精确匹配交给稀疏检索搞定。

4. Top-K静态截断,上下文灌爆

症状:top_k从5调到20,成本翻3倍,质量反而下降。

解法:over-fetch top-20 → Cross-Encoder重排 → 取top-3~5;别无脑堆k值。

5. Lost in the Middle

症状:关键信息在资料中段时,答案质量明显跳水(约降22%)。

解法:通过重排把关键证据顶到前几位;控制上下文长度,别一锅烩。

7.3 第三类:安全&治理层

6. 多租户数据泄露

症状:A公司的文档出现在B公司的回答里。

解法:ACL在chunk级或库级做隔离;检索前必须按租户过滤,绝不跨租户召回。

7. 无拒答路径,幻觉回潮

症状:资料不足时模型照样瞎编。

解法:grounded prompt强制“资料不足即拒答”,配合引用标注实现可追溯。

8. 索引陈旧/检索回音壁

症状:政策改了三个月,答案还是旧版;或者系统越用,检索结果越窄。

解法:定时重建索引;主动注入多样性,监控召回分布。别觉得检索系统一劳永逸,Google DeepMind都证实了,2000-5000次查询内,系统就会收敛到窄子集,变成“检索回音壁”。还有外部语料别全信,做好内容校验,CMU测过检索投毒成功率能到63%。

最后和大家掏三句心窝子:

第一,RAG的瓶颈在检索与上下文,不在模型。换更大的模型,救不了烂检索。

第二,“接个向量库就完事”是最危险的起点。向量库只是个存储层,真正的功夫全在它前后——切块怎么切、模型同不同、召回够不够、重排有没有、ACL做没做、索引新不新。

第三,RAG是一门上下文管理学科,不是数据库查询。它的目标永远是:在正确的时刻,把正确的证据,以正确的密度,喂给模型。

对了,你们做RAG的时候踩过最离谱的坑是什么?评论区聊聊,咱们一起按这8条定位定位。

P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/HHX_01

赞(0)
未经允许不得转载:171主机测评 » 做RAG别再死磕大模型!90%的坑都栽在检索和上下文工程
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址