同一个 AI,为啥有人用出花,有人用出屎?——聊聊上下文工程

0 阅读14分钟

同一个 AI,为啥有人用出花,有人用出屎?——聊聊上下文工程

上一篇,我们聊了 RAG 和向量检索:它让 AI 不用一口吞下百万行代码,而是先找出最该读的那几段。

但找到之后,新问题又来了:办公桌就这么大,捞回来的十段代码、三份文档、两条日志,到底谁先放上去,谁该被压缩,谁必须原样保留?

你八成见过这种场面。

同一个模型,同样的版本,你和隔壁工位那位大佬用出来的效果,差了两个次元。

你问它:

“帮我改一下这个 bug。”

它改出一坨你看不懂的东西,还顺手把别的地方搞崩了。

大佬问它:

“这个 bug 出在这个函数里,相关的调用方在这两个文件,之前类似的 case 我们是这么处理的,你按这个风格改,别动其他逻辑。”

它一次就改对了,连测试都顺手补上了。

于是你得出一个结论:

“大佬的 Prompt 写得好。”

对,但只对了一半。

真正的差距,不在那句话写得多漂亮,而在大佬控制了模型这次能看到什么、看不到什么、先看什么

这件事有个越来越常被提起的名字:

上下文工程(Context Engineering)。

如果说 Prompt 工程是“怎么把话说漂亮”,那上下文工程就是“往模型的办公桌上摆什么”。

开整。


第一章:模型没有记忆,它每次都是“失忆重开”

先纠正一个几乎所有人都有的错觉。

你以为你在和 AI “聊天”,它记得你们之前说过的一切。

其实不是。

大模型本身没有记忆

每一次请求,对它来说都是全新的、彻底失忆的一次开工

它能用的信息,只有一样东西:

你这一次请求,塞进上下文窗口里的所有内容。

所谓“它还记得我上一句说了什么”,真相是:

聊天软件每次都把之前的对话重新打包,连同你的新问题一起,再发一遍给模型。

flowchart LR
    A[历史对话] --> C[打包成上下文]
    B[你的新问题] --> C
    C --> D[发给模型]
    D --> E[模型只看这一坨作答]

模型不是记性好,是每次都被人重新喂了一遍剧情回顾

这个认知非常关键,因为它直接推出一个结论:

模型这次答得好不好,几乎完全取决于这次上下文里摆了什么。

模型的能力是上限,上下文是它这一次实际拿到的牌。

牌发烂了,再强的模型也打不出花来。

这就是为什么同一个模型,有人用出花,有人用出屎——

大部分人输在发牌,而不是输在模型。


第二章:办公桌就这么大,上下文窗口是稀缺资源

既然一切都靠“这次摆上桌的东西”,那第一个残酷现实就来了:

桌子是有限的。

上下文窗口(Context Window)就是模型这次能看的内容总量,单位是 Token。

你可以粗略理解成模型的“办公桌面积”。

桌子再大,也不是无限的。

而现实里,想往桌上堆的东西多得吓人:

  • 系统提示词(角色设定、规则、格式要求);
  • 历史对话;
  • 检索回来的代码片段;
  • 文件内容;
  • 工具返回的一大坨 JSON;
  • 报错日志;
  • 还有你真正想问的那句话。

它们都在抢同一张桌子。

很多人以为窗口越大越好,桌子越大越随便堆。

但堆满了会出三件事。

1. 成本和延迟一起飙

模型读得越多,通常算得越慢,花的钱也越多。

你为了问一行代码,把整个文件、整段历史、五百个检索片段全塞进去,等于每次点一杯咖啡都要求服务员先把整本菜单朗读一遍。

2. 关键信息被淹没,也就是“中间迷失”

这是个被反复验证过的现象:当上下文很长时,模型对开头和结尾的内容记得比较牢,对夹在正中间的内容往往会“视而不见”。

英文社区管它叫 Lost in the Middle。

你把最关键的那段代码,不偏不倚地塞在一大坨内容的正中央,模型很可能就这么滑过去了。

不是它读不到,是它注意力被稀释了。

3. 桌子满了,新东西就挤不进来

一旦窗口被历史和噪声占满,真正重要的新信息反而放不下,或者把旧信息挤掉了。

于是模型开始“断片”:前面说过的约束忘了,改着改着跑偏了。

所以上下文工程的第一条铁律是:

上下文窗口是稀缺资源,要像管预算一样管它。

不是能塞就塞,而是每一个 Token 都得配得上它占的那块桌面

flowchart TB
    subgraph DESK[上下文窗口 一张有限的办公桌]
        A[系统提示词]
        B[历史对话]
        C[检索片段]
        D[当前问题]
    end
    E[塞太多] --> F[成本涨 延迟高 中间迷失]

第三章:垃圾进垃圾出,噪声比空白更可怕

新手常犯一个错,觉得“多给点上下文总没坏处”。

给它整个文件、给它全部历史、给它所有搜索结果——信息越全,模型越聪明,对吧?

错。

无关的上下文不是中性的,它是有害的。

空着的桌面顶多是没帮上忙,堆满杂物的桌面会主动误导模型。

举个真实会发生的例子。

你让 AI 改 订单模块 的一个函数,顺手把整个仓库的代码都塞了进去,心想“信息全一点保险”。

结果仓库里还有一个两年前废弃的 订单模块_old,函数名几乎一样。

模型一看,好家伙,这里有个更“完整”的实现,参考它改吧——

于是它照着已经没人用的旧代码给你改了一版,逻辑自洽、语气自信、完全跑偏。

这不是模型蠢,是你把垃圾摆上了桌,它当成了资料。

上下文里塞了什么模型的反应
只有相关函数 + 明确约束稳,聚焦,改得准
相关函数 + 一堆无关文件开始在多个相似实现间摇摆
塞了废弃旧代码可能照着过期实现作答
塞了自相矛盾的历史对话前后约束打架,输出飘忽

所以“给全”从来不是目标。

上下文工程追求的是信噪比,不是信息量

一句话:

给模型看的不是“所有相关的”,而是“最相关的、且不打架的”。

宁可少给三段废话,也别混进一段会带偏它的旧代码。


第四章:上下文工程的四把刀——选、排、压、隔

那具体怎么“摆桌子”?

拆开看,无非四个动作:选什么、怎么排、放多少、什么时候清

我把它叫上下文工程的四把刀。

第一把刀:选(裁剪)

从海量信息里,只挑出这次真正用得上的。

  • RAG 捞回来的候选,只留 Top 几段;
  • 文件不整篇塞,只切出相关函数;
  • 工具返回的大 JSON,只提取关键字段;
  • 历史对话,只保留和当前任务相关的轮次。

这一步决定了信噪比的上限。

第二把刀:排(排序)

选出来之后,顺序也有讲究。

还记得第二章的“中间迷失”吗?

既然模型对开头和结尾更敏感,那就把最关键的信息放在两头,别让它埋在正中间。

系统约束放最前,当前问题放最后,参考资料垫中间——这是常见的排布思路。

第三把刀:压(压缩)

有些东西必须给,但可以不用原样给。

  • 一段几百行的日志,可以先摘要成“错误发生在 X,原因大概是 Y”;
  • 一长串历史对话,可以压成一段“目前进展总结”;
  • 一个大文件,可以只保留签名和关键分支。

压缩是在“完全丢弃”和“原样保留”之间找的第三条路。

但注意,压缩有损。摘要日志时把真正的报错行摘没了,就等于把线索弄丢了。

第四把刀:隔(隔离与清理)

任务推进过程中,桌上会不断堆东西。

上一步的中间结果、失败的尝试、过时的文件版本……如果不清,桌子迟早满。

所以要:

  • 及时丢弃已经用不上的中间产物;
  • 把跑偏的、过期的内容清出去;
  • 必要时给不同子任务分不同的“桌子”,别互相污染。
flowchart LR
    A[海量原始信息] --> B[选 只留相关]
    B --> C[排 关键信息放两头]
    C --> D[压 长内容摘要化]
    D --> E[隔 清理过期产物]
    E --> F[一张干净高信噪比的桌子]

看到这里你可能已经反应过来了:

前几篇聊的那些主角,其实全都是这四把刀的“刀具供应商”。

  • AST 与 Tree-sitter:让“选”和“压”能按完整函数切,而不是从中间劈开;
  • LSP 与符号索引:精准定位定义和引用,帮你“选”出真正相关的干货;
  • RAG 与向量检索:从百万行里先“选”出候选片段;
  • MCP:让 AI 能统一地去调这些能力,把料取回桌上。

它们各自解决一小步,但服务的是同一件事:

把最该看的东西,以最省的方式,摆到模型面前。

这就是上下文工程。


第五章:两个名场面,好桌子和坏桌子的差距

原理讲完,上对比。

同一个模型,同一个 bug,两种摆桌子的方式。

名场面一:改一个函数的 bug

任务:calculateDiscount 在某些订单上算出了负数折扣,修掉它。

坏桌子:一股脑全塞
上下文里塞了:
- 整个 order 目录 30 个文件
- 最近 50 轮和这个 bug 无关的闲聊
- 一份两年前的折扣规则文档 已废弃
- 你的问题: 帮我修一下折扣算错的问题

模型面对一坨杂物,开始在好几个相似函数间犹豫,还翻出废弃文档里的旧规则,改出一版似是而非的东西。

好桌子:选排压隔全上
上下文里只有:
- calculateDiscount 函数本体 由 AST 精确切出
- 它的两个调用方 由 LSP 定位
- 一个能复现负数的测试用例
- 明确约束: 只修负数场景 不动其他分支
- 你的问题放在最后

模型聚焦在这几段,一次就定位到边界条件没处理,补上判断,还顺手让测试通过了。

同一个模型,差距全在桌上摆了什么。

名场面二:多轮任务里的“越聊越蠢”

你和 AI 连续对话,推进一个稍复杂的重构。

不做上下文管理

每一轮都把全部历史原样带上。

聊到第 20 轮,窗口里塞着前 19 轮的所有细节:走过的弯路、改废的方案、临时的调试输出……

真正的当前目标,被埋在这一大坨正中间。

模型开始“断片”:把你三轮前否决的方案又提了一遍。

做了上下文管理

每隔几轮,把已经完成的部分压缩成一句进展总结,过期的调试输出清理掉,只保留:

  • 当前目标;
  • 最近一两轮的关键决定;
  • 一句“到目前为止已完成 X,正在做 Y”。

桌面始终清爽,模型始终知道自己在干嘛。

结果:不是模型变笨了,是桌子被你堆乱了。


第六章:把整个系列收进一个框架——“省 99% Token”到底省在哪

写到这,可以把前几篇散落的线索串成一根了。

这个系列里,几乎每一篇都出现过类似的说法:

“不用把整个文件塞进去。” “不用把整个仓库塞进去。” “不用把原始数据全塞回去。” “Token 省了 99%。”

当时它们看起来是各自领域的小技巧。

现在你应该看清了:它们本质上是同一件事的不同侧面。

它们全都在回答一个问题:

有限的上下文桌面,这一步到底该摆什么?

篇目那篇在“省”什么翻译成上下文工程
LSP不塞整个文件 只给跳转和诊断的干货用符号信息做精准的“选”
MCP不把工具返回的原始数据全塞回去对工具结果做“压”和“选”
AST不从中间劈开函数 按结构切让“选”和“压”切得完整
RAG不塞整个仓库 先检索候选从海量里做第一轮“选”
本篇统一调度这四把刀上下文工程本身

所以“省 99% Token”从来不是终点,它只是一个副产品。

真正的目标是:

让模型这次拿到的,是一张信噪比最高的桌子。

省下来的 Token 只是顺带的红利,答得更准才是目的。

上下文工程,就是给前面所有工具装上一个统一的大脑

决定在每一步,调哪把刀、摆哪些料、清哪些垃圾。

flowchart TB
    subgraph TOOLS[各司其职的工具]
        A[AST 切得准]
        B[LSP 连得清]
        C[RAG 捞得像]
        D[MCP 取得到]
    end
    subgraph BRAIN[上下文工程 统一调度]
        E[选]
        F[排]
        G[压]
        H[隔]
    end
    A --> BRAIN
    B --> BRAIN
    C --> BRAIN
    D --> BRAIN
    BRAIN --> I[一张高信噪比的桌子]
    I --> J[模型作答]

第七章:泼盆冷水,上下文工程也不是“越省越好”

讲了半天“别乱塞”,该防止你走到另一个极端了。

上下文工程的坑,恰恰有一半是省过头

坑一:压缩把关键证据压没了

为了省 Token,把报错日志摘要成一句“出错了”,把关键堆栈丢了。

模型拿到的是结论,不是线索,自然查不出根因。

压缩有损,别把真正的证据压进垃圾桶。

坑二:裁剪裁掉了必要的上下文

只给函数本体,不给它的调用方和类型定义。

模型不知道参数从哪来、返回值给谁用,只能靠猜。

“最相关的”不等于“最少的”,该给的关联信息一个都不能少。

坑三:过度依赖“大窗口”,以为不用管了

模型窗口越做越大,有人就觉得上下文工程没用了,反正随便塞。

但“中间迷失”和成本延迟不会因为窗口大就消失。

窗口大只是给了你更大的桌子,不等于你可以把桌子堆成垃圾场。

坑四:静态地摆一次桌,不管后续变化

很多任务是多步推进的。

第一步摆好的桌子,到第五步可能全是过期信息。

如果你只在开头摆一次,之后不再动态调整,桌面很快就脏了。

上下文不是摆一次的静态快照,而是需要一路维护的动态资源。

这最后一条,恰好戳到了一个新问题。

前面聊的都是“帮模型摆桌子”。

可现在的 AI 编程工具,比如 Claude Code,你只说一句话,它自己就跑了十几步:读文件、调工具、看结果、再决定下一步……

这一路上,桌子不再是你摆的,而是它自己在动态地加料、清桌、再加料。

它是怎么做到自己给自己摆桌子、还一步步逼近目标的?

这就是下一篇的主角:

Agent 循环。


总结:模型的上限是天赋,上下文才是它这次拿到的牌

回到开头那个问题:

同一个 AI,为啥有人用出花,有人用出屎?

现在答案很清楚了:

  1. 模型没有记忆,每次都靠这次塞进上下文的东西作答;
  2. 上下文窗口是有限的稀缺资源,塞满了会涨成本、丢关键、变迟钝;
  3. 无关上下文不是中性的,它会主动带偏模型;
  4. 上下文工程就是四把刀——选、排、压、隔;
  5. 前几篇的 AST、LSP、RAG、MCP,全是这四把刀的工具。

整篇最值得记住的一句:

模型的能力是上限,上下文是它这一次实际拿到的牌;发牌的水平,常常比模型本身更能决定结果。

所以下次再看到“同一个模型有人用出花有人用出屎”,你就知道了:

差的往往不是模型,是那张桌子。

最后留个问题:

你平时用 AI 编程,是习惯把整个文件甚至整个项目一股脑丢给它,还是会先想想“这一步它到底需要看哪几段”?

评论区聊聊。

如果这篇让你重新理解了“为什么大佬用得比你顺”,点个赞,让更多人看到这个系列。


P.S. 想直观感受上下文的威力,可以做个小实验:同一个问题问两次。第一次把整个文件粘给 AI,第二次只粘那个相关函数加一句明确约束。对比两次的回答,你大概率会发现——给得少、给得准的那次,反而答得更好。这不是玄学,这就是上下文工程。