YhazrinJournal
← 所有文章
AI工程随笔

上下文管理

Agent 上下文的选择、压缩与组织。

每次模型上下文窗口从 128K 涨到 1M,我做的第一件事都不是往里塞更多东西,而是重新算一遍:这次任务真正需要被模型「看到」的到底是什么。

答案通常比我以为的要少一个数量级。

塞满上下文是最容易的失败方式

上下文窗口是一个上限,不是目标。我见过太多这样的写法:把整个代码库附上,把全部历史对话拼进去,把十几篇文档接在一起,然后问一句「帮我修一下这个 bug」。

输出质量不会随输入线性上升,它会在某个点之后断崖下跌。原因并不神秘:注意力是有限的预算,塞得越多,每一份被认真读到的比例就越低。

更麻烦的是信噪比。一段 2000 行的代码里真正出问题的是第 3 行,但你把它和另外 1999 行平等地放进了上下文。模型要自己在噪声里定位信号,而这个定位过程本身就在消耗它读其他部分的注意力。

上下文的价值不取决于它有多少,取决于里面有多少是模型非读不可的。

真正的做法:先做减法,再做加法

我现在的工作流是反过来的。

第一步,先明确结束条件。 这次任务的「完成」长什么样?如果我说不出来,那不管塞多少上下文都是错的——模型会朝着一个模糊的方向堆砌看起来合理的东西。

第二步,只放决策必需的原始材料。 不是整个文件,而是出问题的那几个函数加上它的直接调用方;不是全部文档,而是和这个 bug 直接相关的那两段。

第三步,把判断结果固化下来,而不是保留推理过程。 这是压缩幅度最大的一次。如果探索阶段跑了 20 次工具调用,其中 18 次是走弯路,我不需要保留这 18 次的完整输出,我需要的是「已确认 X」这个结论,加上支撑它的证据。

ts
// 不要这样:把思考过程和结论一起塞回去
messages.push(...allToolCalls);
messages.push(userMessage);

// 这样:结论 + 证据
messages.push({
  role: "assistant",
  content: `已确认根因:${rootCause}\n关键证据:${evidence.join("\n")}`,
});

上下文预算是一种工程纪律

我把它类比成团队的时间预算。你当然想让工程师了解所有背景,但如果你把他八小时里七小时花在读上下文上,剩一小时写代码,他交付的东西一定不好。

所以我现在会在 prompt 里显式写出边界:

  • 必读:出问题的那几个函数、相关的类型定义
  • 不用读:其他目录、构建产物、历史实现
  • 已经确认:前面几轮已经排除的可能

这三条边界,比多给 50K token 有用得多。

什么时候反而该给更多

不是永远做减法。有两种情况反而需要主动加。

跨文件的隐式约束。 比如一个全局的错误处理约定,代码里没有注释,但三十个文件都遵守它。这时把约定显式写出来,比让模型从三十个文件里归纳要可靠得多。

需要判断品味的情况。 比如「这个交互设计对不对」,把几个「对」的参照实现一起给它,比讲十条原则有用。设计判断靠样例,不靠描述。

这两种情况有个共同点:你给的每一样东西都是经过筛选的,不是「整个项目」。

一个可以马上用的自检

在发送一个复杂的 Agent 任务前,问自己三个问题:

  1. 如果我是那个接手的人,我需要读什么才能做完?
  2. 我给的东西里,有多少是我已经知道「可以不看」的?
  3. 我能说清楚任务完成的判据吗?

三个都能答上来,任务大概就能跑。答不上来的那个,通常才是真正的问题所在。

上下文工程的核心不是「给模型更多」,是替模型做决定。它本质上是在替一个非常聪明但没有上下文的人,整理一份刚好够用的材料。