2025年还没过完,圈子里就开始给RAG写悼词了。“RAG已死”、“下一步是上下文工程”,这类标题我刷到过不止一次,每次都能收获大量转发。作为一个实际把检索增强生成接到业务系统里、被检索漂移坑过无数回的一线程序员,我的态度很明确:RAG不会死,真正会被淘汰的是“只会把RAG当万能拼接器”的粗暴用法。2026年真正该补的课,是上下文工程——把有限的上下文窗口当成一项预算来管理,系统化地决定哪些信息进、哪些信息不进、以什么结构进、如何压缩与缓存。这篇文章不贩卖焦虑,只讲清楚两个概念的分工边界,并把小白能直接复现的搭建、调优和排错经验一并放出来。
1. RAG和上下文工程到底在争什么
1.1 先给RAG“验尸”:它到底死没死
RAG的全称是检索增强生成,核心思路非常朴素:模型记不住或者不该记住的知识,先放到外部存储里,用户提问时先检索相关片段,再把片段拼进提示词。打个比方,你让一个记忆力一般的人去开卷考试,他不需要背完整本教材,只需要知道去哪一章翻答案,然后把关键段落抄进答题卡。这套思路从诞生起就是为解决大模型的知识过期、编造和私有数据无法进入模型参数这三个问题,而且直到今天,这三个问题依然是应用落地的核心瓶颈。
如果“RAG已死”的意思是它要被某种新架构彻底取代,那它得先回答一个问题:在私有知识库问答、企业级客服工单处理、风控合规审查这类必须给溯源依据的场景里,不用RAG,你还能用什么?答案几乎是一个字都换不了。模型参数不会因为你花钱买了大窗口就自动更新,企业客户要的不是“好像知道”,而是“你有本事把哪份文档、哪一页、哪个条款列出来”。所以我不但不认为RAG死了,反而认为它在落地价值上比前两年更清晰。
那为什么会有“RAG已死”这种说法?因为很多人用RAG的时候,其实是把它理解成了“拼装检索结果”。前端把用户问题丢给一个向量库,取回一堆相似片段,再用模板一拼,完工。结果检索不准、片段互相矛盾、上下文塞过载导致生成质量下降,最后把这笔账全部算在RAG头上。实际上,这是典型的水管漏水赖水厂——检索出的是证据,但证据能不能被模型有效使用,取决于你怎么组织、筛选、压缩和路由这些证据,这已经属于上下文工程的范畴了。
1.2 上下文工程是个什么工程
上下文工程,英文叫Context Engineering,字面上看就是“对上下文做工程化治理”。它不是某一天突然冒出来的新架构,而是一个从提示词工程进化出来的系统工程。提示词工程解决的是“单个prompt怎么写更有效”,上下文工程解决的是“整个应用每次调用时,应该携带哪些信息、不带哪些信息,以及这些信息用什么顺序和结构进入模型”。
我更喜欢用“厨房配菜”来类比:提示词工程像菜谱,告诉你糖放几克、盐放几勺;上下文工程像后厨的备菜流程,决定今天店里来了客人之后,哪些食材送进炒锅、哪些进冷藏室、哪些直接丢垃圾桶,后厨整体能不能在高峰期把几十个订单稳定出菜。它关注的不只是最后那一勺调味,而是从食材采购(数据源接入)到预处理(清洗过滤)再到装盘顺序(上下文排版)的完整链路。
所以,上下文工程至少包含四件事:上下文的获取、筛选、压缩与编排;上下文的缓存与版本管理;上下文的来源跟踪和预算控制;以及上下文质量的可观测和可评测。这东西听起来确实比“写提示词”重得多,但只有把它当成一个工程系统来做,才能稳定地扛住线上流量和业务复杂度。
1.3 两者的顶层关系:不是替代,是分工
我的判断是:RAG和上下文工程不是竞争关系,而是局部与整体的关系。RAG管的是“知识从外部存储到模型输入”这一段,它是上下文工程的一个关键子模块。上下文工程管的是“模型输入层整个生命周期”,包括系统提示词、用户历史、工具返回结果、检索片段、模型内部记忆,甚至安全指令,全都要在被模型看到之前过一遍治理流程。
换个角度说,RAG产出的检索结果只是“半成品证据”,它能不能被有效使用,取决于上下文工程里的排序、压缩、去重、格式化和预算分配。很多团队只做RAG不做上下文工程,上线后效果像开盲盒:换一条query效果不错,换一条就幻觉百出。根因往往是检索没问题,但上下文拼装环节完全失控。理解了这层关系之后,再去定义2026年的技术路线就会清晰很多:不会写作能力、不会做路由的RAG,确实不够用;但把RAG嵌入到一个成熟的上下文工程体系里,它依然是当前性价比最高的知识注入方式。
2. 为什么2025年突然有人喊“RAG已死”
2.1 token窗口狂涨带来的幻觉
这两年各家大模型厂商都在疯狂推长上下文,一百万字、几百万token的窗口一个个冒出来。于是很多人产生了一个直接联想:既然窗口这么大,那我可以把整份产品文档、整本规章制度,甚至整个知识库的转储都塞进提示词,从此再也不需要做检索。这种思路听起来很解脱,但实际跑过的人基本都会在成本和效果两方面被打脸。
先说成本。长上下文对应的输入价格不会因为塞得多就便宜,每次请求都带上几十万token,传输耗时、排队耗时、模型首字延迟全部上涨,用户感受到的“响应慢”还不是最致命的,真正致命的是把几乎不可能稳定复用的长文本反复发送,单个用户日活带来的机器成本就能把项目的单位经济模型打穿。
再说效果。模型接收超长上下文时,注意力在这么多token之间分配得很不均。很多简单的长文本评测能过,但只要换成需要跨多个片段做多步推理的复杂任务,效果依然会明显退化。这就像开卷考试带了一整车书进去,翻找和定位的时间成本暴涨,最后答题时真正有效的信息密度反而降低了。超长窗口的真正价值不在于“替代检索”,而在于给上下文工程留出更大的缓存空间和更长的对话记忆。窗口再大,也不能成为不做信息筛选的借口。
2.2 RAG管线复杂之后的口碑滑坡
喊“RAG已死”的第二股力量,来自真实落地中的一堆败退体验。RAG管线的经典环节包括文档切分、向量化、索引、召回、重排序、提示词拼装,每一个环节都有不少“轻松翻车”的死角,这几年公开吐槽RAG效果差的声音越来越多,我总结了一下,常见的翻车集中在下面几类:
| 管线环节 | 常见问题 | 直接后果 |
|---|---|---|
| 文档切分 | 切块大小和重叠策略一刀切,语义被截断 | 召回结果答非所问 |
| 向量化 | 没有针对特定领域做适配,近义词理解差 | 检索召回率偏低 |
| 索引管理 | 元数据缺失,版本升级后旧文档没下线 | 新旧信息混着用,输出错得离谱 |
| 重排序 | 只看语义相关,不看时效和权威性 | 模型被错误证据带跑 |
| 提示词拼装 | 把海量检索片段不加整理全塞进去 | 上下文膨胀,模型无法聚焦 |
这些问题本质上不是“RAG方法错了”,而是“只做了RAG的壳,没做上下文的魂”。但站在旁观者和媒体视角,现象就是“RAG项目上线效果差”,于是标题越写越惊悚,慢慢传成了“RAG已死”。这两年我在几个社区和技术群里看到的现象是,那些把RAG稳定跑出业务价值的团队,几乎无一例外都花了大量时间在上下文工程相关的脏活累活上,比如切块边界分析、元数据设计、检索结果的压缩和排序规则。与其说这些团队靠RAG取胜,不如说他们靠的是上下文工程。
2.3 真正变化的不是技术,是问题定义方式
说到底,2025年最大的变化不是某个模型突然不行了,而是大家终于开始用“问题定义”的角度重新审视大模型应用。以前我们问:怎么把知识塞进模型?现在开始问:一次完整的对话或任务,它的上下文应该长什么样?哪些信息是可信的,哪些信息会引入噪声?预算怎么分配?来源怎么追溯?这几问,正好是上下文工程的核心盘面。
这个转变不是技术换代,而是工程视角的升级。就好比以前做饭关注的是“用什么锅”,现在关注的是“整个厨房的出餐流程”。RAG仍然是那把好锅,但光有好锅已经不足以撑起一家餐厅。当越来越多团队发现问题定义视角需要升级时,“RAG已死”这类话就成了这场升级的粗暴注脚。我看标题从来只看它背后的底层信号:大家不是在否定检索增强,而是在呼唤更系统的上下文治理能力。
3. 从零搭一个上下文工程工作流
3.1 先盘清楚你的“上下文资产”
我刚接触上下文工程时犯过一个错误:一上来就写了很多花哨的提示词模板,结果线上效果没有任何提升。后来想明白了,提示词只是上下文最后一公里,真正要处理的是一整批“上下文资产”。建议先花时间把应用里所有会进入模型输入的信息分门别类,我在实际项目里用的是下面这份分类清单:
| 资产类型 | 典型内容 | 进入上下文的方式 |
|---|---|---|
| 静态系统上下文 | 角色设定、任务目标、安全指令、输出格式 | 每次请求固定拼装 |
| 动态业务上下文 | 用户画像、权限标签、当前诉求、历史会话摘要 | 按用户和会话动态组装 |
| 检索上下文 | 知识库片段、工具返回结果、外部接口数据 | 由召回和重排序产出 |
| 记忆上下文 | 跨会话偏好、长期用户意图、业务常量 | 从缓存或记忆服务读取 |
盘完这四类资产之后,还有一个要立刻补上的动作:给每个资产块打上来源标记。我这边所有的上下文块统一带上doc_id、version、source_type、timestamp这些字段,别嫌麻烦,后面一旦出问题,没有来源标记你连排查的依据都找不到。
3.2 上下文打包与分层策略
资产盘清楚了,接下来是“怎么组装”。我会把最终交给模型的提示词分成四层:系统层、任务层、证据层、示例层。
系统层是固定不变的骨架,包含角色、任务定义、安全边界和思考规则,这一层要精简再精简。我见过不少团队的system prompt写得比论文还长,把一堆“你是一个强大的AI”“请认真回答”之类的废话全塞进去,除了稀释注意力之外毫无价值。任务层放的是用户当前这一轮的诉求,包括通过查询解析拆出来的关键参数、语境补充和明确目标。证据层放的是检索片段和工具结果,这是整个组装过程中最需要做预算控制的一层,对此我的经验是:并不是召回的片段越多越好,真正有效的信息密度才是核心指标。示例层放少量few-shot例子,好的示例比反复强调“请按JSON格式输出”这类重复指令有用得多,但示例务必短小精准,否则就是在给上下文增加无谓的开销。
组装的顺序有一个朴素原则:先放模型该优先看的东西,后放补充信息。很多应用把所有内容一股脑按某种固定顺序拼接,结果模型更关注中间段落而不是最后的结论,这本质上是上下文排版没做好。
3.3 用一份最简单的上下文模板跑通Demo
说再多理论,不如跑一个最小可复现的Demo。下面这个Python伪代码是我在搭建原型时用的基础模板,没有依赖任何特定框架,纯用标准库的思路演示“先拆资产、后组装、再压缩”的流程:
def build_context(query, profile, retrieved_chunks, examples, budget=3000): blocks = [] # 1. 系统层:固定,但控制长度 system_block = render_system_prompt(role=profile["role"], safety_rules=profile["safety"]) blocks.append(("system", system_block)) # 2. 证据层:按相关度排序,逐步分配预算 remaining = budget - estimate_tokens(system_block) for chunk in sorted(retrieved_chunks, key=lambda x: x["score"], reverse=True): block = "[证据|doc_id={}|version={}|time={}]\n{}".format( chunk["doc_id"], chunk["version"], chunk["timestamp"], chunk["text"] ) if estimate_tokens(block) <= remaining: blocks.append(("evidence", block)) remaining -= estimate_tokens(block) else: break # 3. 任务层:用户的真实诉求必须完整,放在证据之后 task_block = "[用户诉求]\n" + query if estimate_tokens(task_block) <= remaining: blocks.append(("task", task_block)) # 4. 示例层:最后放,预算还有富余才加 for example in examples: ex_block = "[示例]\n" + example if estimate_tokens(ex_block) <= remaining: blocks.append(("example", ex_block)) remaining -= estimate_tokens(ex_block) return assemble(blocks)注意顺序:系统层固定在前,证据层紧随其后,任务层放到证据层之后,示例层放最后。任务层在证据层之后可能让很多人意外,但实测下来,模型在读证据时已经在建立对问题的理解,最后看到任务层时能形成“带着证据看诉求”的效果,比把用户问题放最前面更稳定。
3.4 衡量上下文质量的三个指标
上下文做完之后不能拍脑袋说“感觉效果不错”,我强烈建议在项目里先定三条衡量标尺。第一是信息密度,指有效token占全部输入token的比例;同样一次请求,如果10个片段只有2个被模型真正用上,那就是密度太低。第二是覆盖率,指当前任务真正需要的关键事实是否出现在上下文中,漏了必输。第三是辨析率,指模型能不能从上下文中正确选出可信证据,尤其在存在多版本、多来源数据时,辨析率比覆盖率更关键。
实际操作里,我不太建议只看系统自动生成的指标,因为自动打分经常和用户体验脱节。更笨但有效的办法是:抽100条线上query,人工打开一次完整的请求日志,对比“上下文内容”和“答案质量”,给每条数据打一个“是否可从上下文推导出正确答案”的标记。这么抽出30分钟左右的时间,一定比跑十个评测集更能暴露问题。
4. 实战中的RAG该怎么跟上下文工程共存
4.1 检索不是目的,结构化才是
把RAG嵌入上下文工程的第一步,是改变对检索结果的心态。很多人把检索结果当成“原文片段”直接用,其实应该把检索结果当成“需要再次提炼的证据”。我在业务系统里,要求所有进入上下文的知识片段必须携带结构化元数据,并且显式地把元数据展现在提示词里。比如:
{ "doc_id": "KB-202601-001", "version": "2026.01", "department": "客户服务部", "updated_at": "2026-01-15", "trust_level": "official", "content": "针对订单未按时送达的情况,客服应先致歉并补偿10元优惠券。" }在提示词中显式带上doc_id和version这个动作,看似只多了几个字段,实际效果差异非常大。模型在这些字段的引导下会更倾向于把它当成事实依据,而不是自由发挥的语料。相反,如果只把原文文本扔进去,模型的“知识引用感”会弱很多,幻觉概率随之升高。
4.2 长文档处理的经典组合:预切分+重排序+压缩
处理长文档时,我用的组合套路是预切分、重排序、上下文压缩三段式。首先是预切分,块大小我一般控制在250到500个token之间,重叠量取50到80个token,这个参数是多次测试下来的平衡点。块太大,语义完整但噪声多;块太小,语义被切开但定位准。其次是重排序,初召回取回来前面30到50条,通过重排序模型筛到前10条左右。千万别把所有召回结果都交给模型,我给这个上限做的是硬截断。
最关键的是第三步:上下文压缩。这一步的核心是拿一个专门负责“提炼”的模型对每段证据做压缩,压缩率控制在原长度的一半以下,同时强制保留所有关键数字、结论以及引用编号。压缩前后的差距,我举一个常见案例:原本一份1800字的售后政策文档,被压到不足300字,仅保留退款时限、补偿金额和例外条件,模型回答的准确率反而明显提升。
压缩Prompt模板我在这里共享一个可用的版本:
你是上下文压缩器。请把下面的文档压缩到不超过原长度一半, 保留与用户可能问题直接相关的结论、数字、规则、例外条件。 输出必须包含原始文档编号和行号,不得编造任何原文不存在的信息。4.3 一个更稳健的混合架构
一套能上生产的架构,在我看来至少要有五个组件配合。查询路由器负责判断用户输入适合走检索、走纯生成还是走工具调用;索引层负责知识召回和版本过滤;上下文组装层负责分层打包和token预算控制;缓存层负责复用静态上下文和语义缓存,避免每次请求都从头跑全链路;校验链负责在模型输出前检查引用完整性、关键数值格式和越权口径。
| 组件 | 职责边界 | 关键设计点 |
|---|---|---|
| 查询路由器 | 判断用户意图类型 | 用轻量分类模型或规则,不要为了查一句话去跑大型RAG链路 |
| 索引层 | 知识召回、元数据过滤 | 强制版本、部门、权限字段的过滤前置 |
| 上下文组装层 | 分层拼装、预算分配 | 单层预算上限,超了就压缩,不硬塞 |
| 缓存层 | 静态模板缓存、语义缓存 | 缓存键必须包含模板版本号和关键业务参数 |
| 校验链 | 输出前引用与数值校验 | 引用doc_id必须存在于本次证据集,否则强制回归或重生成 |
这套架构跑下来,RAG并没有消失,它只是被压缩成了“索引层”这个角色。整个系统的稳定性,从依赖某一个环节的效果,变成了依赖多个环节的协同治理,这也正是上下文工程想表达的中心思想。
5. 线上事故复盘:一次“无效上下文”连环翻车
5.1 事故链路:从检索漂移到输出全错
前面讲了不少理论,来说一个我印象非常深的线上事故。当时我们运营的是一个内部知识库问答系统,就叫模拟项目X,知识库里有大量按季度更新的业务报表文档。有一次客服同事在系统里提问“上个季度的退货率是多少”,模型给了一个数字,客服直接引用了这个数字去答复客户,结果客户反馈“这个数据不对吧”,一查才知道,模型回答的是去年同季度的数字。
复盘下来,问题链路比我预想的更长。文档预切分时按固定窗口切,旧版本文档没有在索引里被标记下架;向量召回阶段,旧版本文档与用户提问的语义相似度和新版几乎持平;重排序模型只按语义相关度打分,根本不关心时间新旧;上下文压缩阶段在处理两段落时,把旧版和新版的关键数字拼到了同一个压缩片段里,还丢了版本标记。最终模型看到的上下文里,新旧数据是混在一起且无主次的,它自然倾向选那个更“顺眼”的数值。这条链路里没有任何一个环节是单独崩溃的,但它们串联之后,直接形成了一次不可接受的错误回答。
5.2 排查过程:先看召回还是先看上下文
事故发生后,团队按直觉先去查检索日志,折腾半天也没完全定位问题。后来我调整了排查顺序:先看模型最终收到的上下文,再倒推是哪个环节输入错了。这个顺序很关键,因为输出质量直接取决于最终上下文,很多时候召回结果没问题,问题出在拼装或压缩过程中。我们重新跑了一遍相同的用户query,把模型最终收到的完整上下文打印出来,发现里面既有新版文档的结论,也有旧版文档的数值,两者还被压缩层合成了一段“混合结论”。
为了把排查过程写得可复现,我整理出了当时的排查链路:
| 排查步骤 | 看到的现象 | 初步结论 |
|---|---|---|
| 打印模型完整输入 | 上下文中存在两个版本号,且压缩片段未标注版本 | 上下文组装层版本标记失效 |
| 检查重排序结果 | 新旧文档相关度分数相差不到1% | 重排序时间衰减因子缺失 |
| 检查索引库文档状态 | 旧版文本文档状态未置为不可检索 | 索引元数据更新流程有漏洞 |
| 检查压缩prompt | 未要求保留文档编号和版本 | 压缩模板设计不安全 |
所以,排查RAG类问题时,我建议把80%的精力放在“最终上下文长什么样”上,而不是一开始就怀疑向量库召回率不行。上下文是模型唯一消费的东西,它如果没有把准确的证据放在正确的位置,再好的召回模型也救不回来。
5.3 修复方案与验证结果
修复动作从索引层一路做到提示词层,从上到下补了四个口子。第一,所有文档写入索引之前强制校验版本号和生命周期状态,旧版状态的文档禁止进入检索结果,这个用索引过滤器就解决了。第二,重排序阶段加入时间衰减因子,同样语义相关度下,新近文档的得分加权更高,衰减强度我取了半衰期策略,避免一刀切杀死历史文档。第三,压缩prompt里强制要求“输出必须保留原文档ID与版本号,且禁止将不同版本的数值合并到同一结论”。第四,输出端加了一条引用校验规则,模型回答里出现的doc_id必须在本次输入上下文的证据集合里存在,如果引用了一个不存在的doc_id,就触发重新生成或返回兜底话术。
修复之后我拉了一批历史query重新验证,发现错误引用从几乎每周都有爆发,降到了连续四周为零,漏答率也从8.1%降到了1.2%左右。复盘结束后我最大的教训是:线上RAG系统出问题,通常不是单个环节坏了,而是上下文在链路里被悄悄污染了。没有来源健壮性,再聪明的模型都帮不了你。
6. 2026年做上下文工程的三个绕不开的坑
6.1 上下文从哪来:数据血缘与来源标注
第一个必须补的习惯是给每一条上下文记录建立数据血缘。所谓数据血缘,就是得能回答“这条结论是从哪个文档、哪个版本、哪个时间点来的”。企业知识库的问题比通用场景严重得多,同一套政策可能一年改三次,每次改版都会产生“新旧并存”的窗口期。如果不带版本标记,模型在这个窗口期里就很容易把旧内容当成当前有效内容。
具体落到操作上,入库前给每份文档生成唯一document_id,对该文档内容做hash存成指纹,所有引用片段在拼入上下文时都带上这两个字段。输出阶段再用引用校验规则去核对,一旦发生结果与上下文不一致,就能顺着doc_id迅速定位到具体来源。这套机制早期投入大约一到两个人日,但省下的排查时间可能是一个数量级。
6.2 上下文往哪放:缓存、路由与版本管理
第二个坑是很多人把上下文工程做成了每次请求都现拼现用。再漂亮的组装逻辑,只要每次都全量重跑,延迟和成本就下不来。正确做法是把上下文分层处理:完全静态的系统上下文,在产品发布时已经可以预编译好,直接走模板缓存;用户的基础画像和业务标签,按用户维度做短期缓存;检索召回结果,可以做语义缓存,让相同或近似的问题直接命中历史结果,这一步在客服类应用里收益尤其明显。
但缓存最容易被忽视的是版本管理。指令模板一旦调整,旧缓存全部要失效,否则用户会继续看到旧说法的答案。我给模板文件加了一个content hash,每次发布后,缓存键里带上这个hash,模板一改,缓存自动切新键。这个方法成本低且有效,强烈建议直接抄。
6.3 成本与延迟:token是预算,不是无限资源
第三个坑,也是2026年必然会被越来越多人关注的坑:token就是钱,也是延迟。我见过不少团队为了追求“信息全面”,把证据层塞得满满当当,结果响应速度和成本全部爆炸。上下文工程在一定程度上就是预算管理,每一层能拿到多少token,应当提前定好上限。比如系统层固定200token,任务层不超过300token,证据层最多1500token,超出部分要么压缩要么丢弃。
为了能在大流量下做实时控制,我写了一个极简的token预算器,原理不复杂,会在组装前先估算每个块的长度,超预算的直接跳过或交给压缩模块处理:
def estimate_tokens(text: str) -> int: # 中文场景下粗略估算:汉字约等于1个token,英文字符粗略折半 cjk_chars = sum(1 for c in text if "\u4e00" <= c <= "\u9fff") return int(cjk_chars + (len(text) - cjk_chars) / 2) + 16 def enforce_budget(blocks, budget=4000): total = 0 kept = [] for block in blocks: cost = estimate_tokens(block["payload"]) if total + cost > budget: continue kept.append(block) total += cost return kept这个预算器看似简单,但它是整个上下文工程稳定运行的最后一道保险。线上请求每秒进来几十个,人工肉眼检查token预算是不现实的,必须用代码做截断。
我踩过几次坑之后的体会是:上下文工程做得好不好,取决于你愿不愿意把它当成一份长期维护的预算与信任管理活来做,而不是靠一两个花哨模板一劳永逸。2026年如果你还在为RAG的效果发愁,不用急着换框架,先把自己系统里的上下文资产盘清楚,把来源标记、压缩规则、缓存失效这三件事补上,大概率比你想象的更有效。