AI Agents Operations

AI智能体的上下文工程:上下文窗口里到底该放什么

Alejandro Rioja
Alejandro Rioja
1 分钟阅读
TL;DR

上下文工程是一门学科:在每一步决定哪些token值得占据智能体上下文窗口里的一席之地——系统指令、工具定义、检索数据和对话历史都在争夺同一块有限空间。提示词工程问的是我该怎么措辞;上下文工程问的是模型现在到底需要知道什么。常见的失败模式往往不是上下文太少,而是太多:陈旧的历史记录、不相关的工具schema,以及没人要求的检索文档,这些都在稀释信号、推高成本。我为每个类别设定固定预算,历史记录先于身份被削减,先做摘要再截断。

免费新闻通讯

每周三。28,400+ 读者。纯干货。

目录

2026年8月发布。

TL;DR: 上下文工程是一门学科:在每一步决定哪些token值得占据智能体上下文窗口里的一席之地——系统指令、工具定义、检索数据和对话历史都在争夺同一块有限空间。提示词工程问的是我该怎么措辞;上下文工程问的是模型现在到底需要知道什么。常见的失败模式往往不是上下文太少,而是太多:陈旧的历史记录、不相关的工具schema,以及没人要求的检索文档,这些都在稀释信号、推高成本。我为每个类别设定固定预算,历史记录先于身份被削减,先做摘要再截断。

操作者视角: 那些最难调试的智能体,出问题不是因为模型能力弱。它们出问题是因为我任由上下文窗口变成了一个杂物抽屉:任务根本用不到的六个工具schema,偏离原始请求40轮的对话历史,一份技术上相关、实际上毫无用处的检索文档。修改提示词没用。修改提示词前面的东西,才有用。

提示词工程给了你一个能跑起来的智能体。上下文工程则是让它在处理真实流量、真实历史和真实边缘情况时依然能跑下去的关键——如今我在这项技能上花的时间,已经超过了打磨提示词措辞。

提示词工程和上下文工程不是同一份工作

提示词是一条指令。上下文是模型在执行这条指令时所看到的一切:系统提示词、它能调用的工具、你检索或查询到的内容,以及你决定携带多少之前的对话或运行历史。提示词工程优化的是第一项的措辞。上下文工程优化的是全部四项的组合。

这个区别在实践中很重要,不只是术语上的区别。如果我写了一个精心打磨的提示词,却给智能体塞了五个不相关的工具schema和四十轮陈旧历史,措辞就不再重要了——模型正在一个大部分是噪音的上下文里进行推理。我那些从”演示里能跑”变成”凌晨三点遇到奇怪输入也能跑”的智能体,无一例外都是靠修理窗口里的内容做到的,而不是重新措辞窗口里的指令。

争夺空间的四样东西

每一轮,四个类别都在争夺同一块有限空间:

  1. 系统指令 ——身份、规则、输出格式。参见我用于系统提示词的五层结构——这是唯一应该保持近乎固定的类别,因为提示词缓存只有在前缀不变的情况下才划算。
  2. 工具定义 ——智能体这一轮可能会调用的每个工具的schema,不管它是否真的需要。
  3. 检索数据 ——从数据库、向量存储或API调用中取出的任何东西:记忆、文档、客户记录。
  4. 对话或运行历史 ——这次会话或这次运行中已经发生的事情。

这些都不是免费的。任何类别里的每一个token,都是模型在决定下一步该做什么时必须与其他所有token一起权衡的token,而且只要不是缓存命中,每一个token都是你要为每次请求付费的token。

出错的几乎总是太多,而不是太少

当智能体表现异常时,本能反应是添加更多上下文——更多指令、更多背景、更多”以防万一”的历史。以我的经验,情况往往恰恰相反。

工具schema太多。 我见过智能体调用错工具,不是因为缺少正确的工具,而是因为它被埋在那个任务根本不需要的另外六个工具后面。只发送与当前步骤相关的工具,而不是每次调用都带上整个工具箱。构建一个决定该暴露哪一部分工具的路由层成本很低,只要它避免了一次错误调用,就已经值回成本。

陈旧的对话历史。 一个拖着60轮历史、来自三个早已无关事件的支持智能体,并不是在”记住客户”——它是在用不相关的噪音稀释当前请求,偶尔还会基于已经不成立的事情采取行动。这正是带有边界窗口的情节记忆本应防止的失败模式,值得检查一下你的窗口是否真的有边界,还是已经悄悄无限增长了。

没人要求的检索文档。 语义检索返回前10个”最相似”的片段而不是前2个相关片段,会把答案埋在看似可信的干扰信息之下。更多检索上下文不等于更多信号——超过某个点之后,情况会主动变差,因为模型必须更费力才能找到真正重要的部分。

防御性重复的指令。 我在一些提示词里见过,同一条规则用四种不同方式重复,只因为智能体早期版本曾忽略过一次。这是一个信号,说明这条规则需要提前到提示词更靠前的位置,或者用结构方式强制执行(工具schema里的约束、一个校验步骤)——而不是用重复填满上下文的信号。

我实际使用的预算

在30多个生产智能体上,我在构建智能体之前就为每个类别设定明确的token预算,而不是等它开始表现异常之后:

类别预算方式空间紧张时最先削减的
系统指令固定、版本化,为缓存命中保持稳定最后才削减——这是身份,削减会改变行为
工具定义限定在当前步骤,而非整个工具箱任何从当前状态无法到达的工具
检索数据Top-k,k尽量小到任务能容忍的程度低于置信度阈值的低相关性结果
历史记录滑动窗口(最近N轮)或压缩摘要先削减最旧的原始轮次,替换为一行摘要

最后一列的顺序,就是真正的决策框架:先削历史记录,再削检索广度,再削工具范围,系统指令放在最后削。 历史记录是最容易在不损失正确性的前提下压缩的——一段两句话概括”1-30轮发生了什么”的摘要,通常能承载与完整记录相同的运营价值。削减系统指令是最危险的,因为智能体真正的行为就体现在那里。

先做摘要,再截断

截断——直接丢弃最旧的轮次——是这件事的粗糙版本。它能凑效,直到被丢弃的那一轮恰好包含了智能体需要的唯一事实。更好的模式是压缩:在丢弃原始历史之前,先把它折叠成一段简短的结构化摘要,捕捉其中的决定和事实,并且即使原始轮次消失之后,也永久保留这份摘要。

typescript
// workers/compact-history.ts

interface HistoryDigest {
  summary: string; // 2-3句话:已决定、已解决或仍未解决的事项
  keyFacts: Record<string, string>; // 值得逐字保留的稳定事实
  turnCount: number; // 这份摘要替代了多少原始轮次
}

async function compactIfNeeded(
  history: ConversationTurn[],
  env: Env
): Promise<{ digest: HistoryDigest | null; recent: ConversationTurn[] }> {
  const RECENT_WINDOW = 10;
  if (history.length <= RECENT_WINDOW) {
    return { digest: null, recent: history };
  }

  const toCompact = history.slice(0, -RECENT_WINDOW);
  const recent = history.slice(-RECENT_WINDOW);

  // 用便宜的模型做摘要,这一步几乎总是够用
  const digest = await summarizeTurns(toCompact, env);
  return { digest, recent };
}

这和评估框架把每一次生产故障都变成永久测试用例是同一个原理(参见我用来发布AI智能体的评估框架):不要丢弃信息,把它压缩成一种保存成本低、又依然有用的形式。原始轮次是可以丢弃的。但其中的事实通常不是。

检索:更少但更相关的结果胜过更多结果

同样的原则适用于从向量存储或数据库中提取的任何内容。人们很容易慷慨地检索——top-10、top-20——理由是更多上下文不会有坏处。但它会有坏处。每一个不相关的片段,都是模型必须阅读、权衡并舍弃的片段,足够大的一堆”几乎匹配”的结果,分量可能超过那个真正回答了问题的片段。

我的默认做法是从较小的k(2-4)开始,只有当我能用真实案例证明在这个宽度下答案确实缺失时才扩大它——而不是因为撒更大的网感觉更保险。如果检索质量不稳定,解决方法通常是更好的查询或一个重排序步骤,而不是更大的k。

把这一切与成本和正确性联系起来

上下文工程不只是一个质量问题——它是决定运行一个智能体成本高低的最大单一杠杆,因为在大多数智能体工作负载中,输入token的计费远高于输出token。膨胀的上下文窗口,在成为行为bug之前,首先是一张膨胀的账单。如果你还没看过在模型档位之间选择的成本计算,你所采用的上下文预算会直接改变那笔账:更小、边界更清晰的上下文,能让更便宜的模型胜任更多任务,因为模型不需要在一个不必要庞大的干草堆里找一根针。

我运行的每一个智能体都跑在Claude上,而模型档位的决策只有在上下文预算确定之后才有意义——在一个臃肿、没有边界的上下文上比较成本,根本说明不了任务真正需要什么。

而且,因为改变上下文窗口里的内容,和改变提示词一样会改变行为,每一次上下文改动都要通过和提示词改动一样的关卡:在发布之前,先拿由真实生产故障构建的评估集跑一遍。削减历史记录或收窄检索宽度,正是那种”显然安全”、却会在你不核实的情况下悄悄让某个边缘情况回退的改动。

操作者的结论

上下文工程就是在每一轮决定什么值得占据一个有限窗口里的位置——默认的失败方式是纳入太多,而不是太少。让系统指令保持稳定,并把它放在最后才削减。把工具定义限定在当前步骤。检索时收窄范围,只在有证据时才扩大。在丢弃历史记录之前先压缩成摘要,并优先削减最旧的原始轮次。然后针对你的评估集验证每一次改动,因为上下文改动对行为的影响和提示词改动完全一样——只是更容易假装它不是这样。

常见问题

什么是AI智能体的上下文工程?

这是一门学科,决定哪些token——系统指令、工具定义、检索数据和对话历史——在每一步进入智能体的上下文窗口,这与提示词工程不同,后者关注的是单条指令如何措辞。它在生产环境中最为重要,因为在那里,四个类别在每次请求中都争夺同一块有限空间。

上下文工程和提示词工程不一样吗?

是的。提示词工程优化的是一条指令的措辞。上下文工程优化的是模型在这条指令之外看到的一切——暴露了哪些工具、检索了什么内容,以及携带了多少历史记录。即便提示词措辞得很好,如果周围充斥着不相关的工具schema或陈旧的历史记录,它依然会失败。

AI智能体应该保留多少对话历史?

比你想的要少。一个有边界的滑动窗口(通常是最近10-20轮)加上对更早内容的压缩摘要,通常胜过完整的原始记录,因为它去除了噪音,却没有丢失重要的事实。在丢弃历史记录之前先压缩,而不是简单地截断。

更大的上下文窗口是否意味着我需要更少的上下文工程?

不是——它去掉了硬性的技术上限,但没有去掉成本或噪音问题。更大的窗口让粗放变得更便宜,但每一个不相关的token仍然会稀释模型必须处理的信号,并且只要不是缓存命中,每次请求依然要花钱。这门学科在20万token和8千token下同样重要。


相关: 如何写出不会在生产环境中失败的AI智能体系统提示词 · 如何为AI智能体添加记忆 · 提示词缓存:在不换模型的情况下降低Claude成本 · 我用来发布AI智能体的评估框架

需要帮助为你的用例设计智能体的上下文和记忆吗? 联系我 — 我为运营团队设计生产级智能体系统。

继续阅读

相关文章

继续阅读

将AI实战手册发送到您的邮箱

每周三。28,400+ 读者。纯干货。

↵ 查看全部结果 esc esc 关闭