LLM 面试备考
首页/Agent 架构

上下文工程 Context Engineering

所属:01-fundamentals / 04-agent-architecture
面试权重:★★★
来源:通用领域知识(B站「AI Agent 八股」合集高频);未做一手考证
相关项目:Writing Copilot —— 检索内容的筛选链与压缩;Race Bot —— schema 召回与 few-shot 注入 → 见 02-interview/projects/writing-copilot.md

一句话#

上下文工程是有意识地决定「每一轮往上下文窗口里放什么、放多少、按什么顺序放」——它是 prompt engineering 的超集:prompt 管的是「怎么措辞」,context 管的是「窗口里到底有什么」。

为什么重要(面试角度)#

这是 2025–2026 年 agent 领域最热的话题之一,而且它比大多数 topic 更能区分「做过 demo」和「做过生产」:

  • 你会不会把上下文当预算来管理(有账、有配额、有取舍),而不是「能塞多少塞多少」
  • 你能不能解释长上下文为什么退化(有名义窗口 ≠ 有有效窗口)
  • 你知不知道指令放在哪里会影响结果(首尾效应、lost in the middle)
  • 你会不会做压缩和摘要,以及知道压缩的代价必须可评测
  • 你理解子 agent 的上下文隔离是上下文工程的一种手段(把污染性的工作挡在主上下文之外)

Writing Copilot 里的检索筛选链(粗筛 → 精排 → 压缩 → 注入)就是一次完整的上下文工程实践,这题必须结合它答。

核心内容#

上下文预算:把它当账来算#

一个 agent 每轮的上下文窗口被这些部分瓜分:

组成 性质 归属
系统提示 / 指令 常驻 固定成本,每轮都付费
工具定义(name + description + schema) 常驻 随工具数线性增长,常被低估
记忆(用户画像、长期事实) 常驻或按需 见 memory.md
检索内容 按需 通常是最大且最不可控的一块
对话历史 累积 随时间线性增长
当前任务输入 按需 —
输出预留 — 必须留,否则会被截断
推理余量 — 上下文太满会挤压「思考空间」

核心动作是显式分配预算:例如给检索内容设上限(不超过窗口的 X%),超出就压缩或丢弃;给工具定义设上限,超了就做工具检索或拆子 agent。

最常被忽略的两项:一是工具定义,几十个工具就是每轮上万 token 的固定开销;二是输出预留,忘记预留会导致长回答被截断。

另一个工程约束:稳定的前缀有利于 prompt caching(缓存公共前缀的 KV,降低重复系统提示的成本与首 token 延迟)。这意味着上下文的顺序应该稳定 —— 把不变的东西放前面(系统提示、工具定义),变化的东西放后面。把动态内容插到前面会反复击穿缓存,成本会明显上升。

放进去 vs 去检索#

这是上下文工程最基本的决策:

放进去(常驻) 去检索(按需)
内容少、稳定、每轮都要用 内容多、稀疏相关、只有部分场景用
例:输出格式约束、核心业务规则、语气要求 例:知识库文档、历史工单、用户过往记录
成本可预测,收益是模型「一直记得」 成本随用量变化,风险是该召回的没召回

判据:如果某信息「不用会导致错误」,且它小而稳定 —— 放进去;如果它大、且只有一部分请求需要 —— 去检索。介于两者之间(中等大小、高频需要)的,可以放摘要版本常驻、细节按需检索。

反模式:把整个知识库塞进系统提示(成本爆炸、注意力稀释);或者反过来,把核心格式约束也做成检索项(该命中时没命中,模型就自由发挥了)。

指令位置与「lost in the middle」#

模型对上下文的注意力不是均匀的:

  • 首尾效应:开头和结尾的信息更容易被利用,中间部分容易被忽略。已有研究(常被引用的是 "Lost in the Middle" 这一类发现)观察到:把关键信息放在长上下文中间时,模型检索到并使用它的比例明显低于放在开头或结尾。
  • 实践含义:
    • 关键约束放开头(系统提示),并在任务末尾复述一次
    • 长文档做检索/问答时,把最相关的片段放在靠近问题的位置,而不是按原始文档顺序堆
    • 不要指望「提醒一句很重要」能起作用 —— 位置比措辞更重要

注意这条结论的边界:具体退化幅度依模型、长度与任务类型而变,且新模型不断在改善长上下文能力。所以面试时应该说「这是一个被反复观察到的现象,方向和机制清楚,具体幅度依模型而定」,而不是当成固定规律引用。

长上下文的退化#

名义窗口 ≠ 有效窗口。即使模型声称支持很长的窗口:

  • 注意力稀释:内容越多,单个信息分到的注意力越少,关键细节被淹没
  • 指令遗忘:早期设定的约束在后文被稀释,模型开始「不守规矩」
  • 干扰:不相关内容会主动误导模型(尤其是检索回来的看似相关但实际无关的片段)
  • 性能与成本:上下文越长,首 token 延迟越高、显存占用越大(KV cache 线性增长,见 01-llm-core/kv-cache.md)

业内有「context rot」这类说法描述「上下文越长、模型对其中信息的利用越不可靠」的现象 —— 它是一个经验性、被广泛观察到的工程共识,而非严格的定量定律。衰减发生的具体长度与阈值依模型和任务而变,不要抛具体数字。

结论:上下文工程的目标不是「塞得下」,而是「塞得准」。 更少但更相关的上下文,通常优于更多但杂乱的上下文。

压缩与摘要#

当内容超出预算时:

手段 做法 代价
截断 丢弃最旧的历史 / 只保留最近 N 轮 简单粗暴,早期约束彻底丢失
滑动窗口 保留最近 N 轮 + 常驻系统提示 同上,但对闲聊类任务够用
滚动摘要 定期把历史压缩成一段摘要,替换掉原始轮次 有信息损失,且摘要会累积误差
结构化摘要 摘要时保留决策、约束、未完成事项,丢弃过程性内容 效果好,但需要设计摘要的 schema
工具输出裁剪 在工具层截断/分页/只取字段 见 tool-use.md,这是性价比最高的一环
检索式历史 不常驻历史,按需检索相关轮次 多一次检索开销,且可能漏

三个必须说清的点:

  1. 摘要是有损的,而且损失不可逆。原文一旦被替换成摘要,信息就再也回不来了。
  2. 摘要本身会引入错误。用一个模型去压缩,压缩过程可能把「不确定」压成「确定」,把细节压错。
  3. 压缩策略必须可评测。要能验证「压缩之后,回答所需的信息是否还在上下文里」—— Writing Copilot 的做法就是先粗排砍掉大量低分块,再对保留的块做细粒度抽取,并配专门的评测验证压缩后答案质量没有退化。不能凭感觉调压缩率。

子 agent 的上下文隔离#

把「上下文污染性」最强的工作放到子 agent 里做,只把结论带回主上下文:

主 agent 的上下文:任务 + 计划 + 各子任务的结论(干净、可控)
      ↑ 只回传结论
子 agent 的上下文:检索原始内容 / 试错过程 / 大量工具输出(脏、但被隔离)
                                    ↓ 用完全部丢弃

这是多智能体的核心收益(见 multi-agent.md),也是上下文工程最有力的手段之一 —— 它把「如何压缩」变成了「根本不让它进来」。代价是信息损失不可控:子 agent 决定回传什么,就等于替你做了压缩决策,所以回传内容要有明确 schema(结论 + 依据 + 置信度)。

上下文的评测#

上下文策略是必须 A/B 评测的工程参数,不能靠直觉:

  • 压缩率调高调低,端到端质量怎么变?(画一条权衡曲线)
  • 关键信息是否还在注入的上下文里?(对「答案所需信息的存在性」做专门验证)
  • 不同上下文组织方式(顺序、位置)对同一批问题的效果差异
  • 成本与延迟的联动变化(token 数、TTFT)

面试里这一点很加分:它说明你把上下文当成可度量的工程变量,而不是「prompt 玄学」。

常见面试问法#

Q:什么是上下文工程?和 prompt engineering 什么关系? A:prompt engineering 关注措辞与指令写法;上下文工程关注窗口里放什么、放多少、什么顺序 —— 检索内容、工具定义、记忆、历史、压缩策略、子 agent 隔离都算。它是 prompt engineering 的超集:措辞再好,如果关键信息没进上下文,模型也无从作答。

Q:长上下文模型是不是就不用做上下文工程了? A:不是。名义窗口不等于有效窗口:内容越多,注意力越稀释,关键信息越容易被淹没,早期指令容易被遗忘,且不相关内容会主动干扰。同时长上下文带来延迟与成本上升(KV cache 随长度线性增长)。所以窗口变大降低的是「放不下」的焦虑,不解决「放得准」的问题。目标是更少但更相关。

Q:指令应该放在哪里? A:放在开头(系统提示)并在任务末尾复述一次 —— 这是利用首尾效应。长上下文中间的信息更容易被忽略,这是被反复观察到的现象("lost in the middle" 一类发现),所以关键约束不要埋在中间。注意具体退化幅度依模型与任务而变,不是一个固定阈值。

Q:上下文满了怎么办? A:分层处理:先把工具输出裁剪或分页(性价比最高);再对历史做滚动摘要,摘要时保留决策、约束与未完成事项,丢弃过程性内容(结构化摘要优于自由摘要);再考虑把污染性强的工作放到子 agent 里做隔离,只回传结论。三条原则:摘要是有损且不可逆的、摘要本身会引入错误、以及压缩策略必须配评测 —— 不能凭感觉调压缩率。

Q:你在 Writing Copilot 里怎么做上下文控制? A:一条筛选链:爬取页面 → 分块 → 相关性打分 → 重排序 → 细粒度内容筛选与压缩 → 才注入 LLM。核心是先粗筛再精排再压缩,逐级降低候选量,最后只把高质量片段喂给模型 —— 推理成本、延迟、噪声同时下降。关键点是压缩必须可评测,否则调压缩率时没有依据。

易错点#

  • ❌ 把上下文工程等同于「prompt 写得好一点」。
  • ❌ 认为窗口大就不需要管理上下文 ——「塞得下」不等于「用得上」。
  • ❌ 忘记工具定义是常驻成本,几十个工具就是每轮上万 token。
  • ❌ 忘记给输出预留空间,导致回答被截断。
  • ❌ 把动态内容放在上下文最前面,破坏 prompt caching 的稳定前缀。
  • ❌ 认为摘要无损,或者不评测就调压缩率。
  • ❌ 抛「第 N 个 token 之后开始遗忘」这类精确阈值 —— 现象方向明确,幅度依模型而变。
  • ❌ 把关键约束埋在长上下文的中间位置,然后抱怨模型「不听指令」。

自测#

1. 「名义上下文窗口」和「有效上下文窗口」有什么区别?为什么这个区别重要?

答案

名义窗口是模型技术上能接受的最大 token 数;有效窗口是模型能可靠利用的信息量。两者不等:随着上下文变长,注意力被稀释,关键信息更容易被淹没,早期设定的指令容易被忽略,不相关内容会主动干扰,模型对中间位置信息的利用率明显低于首尾("lost in the middle" 一类被反复观察到的现象)。重要性在于:它意味着「放得下」不等于「用得上」,所以上下文工程的目标是更少但更相关,窗口变大并没有取消管理的必要性。同时长上下文还会带来延迟与成本上升(KV cache 随长度线性增长)。注意具体退化幅度依模型和任务而变,不宜当作固定阈值使用。

2. 一个信息,你怎么决定是放进系统提示(常驻)还是做成检索项(按需)?

答案

看两个维度:规模与必要性。如果信息小巧稳定、且每轮都需要(例如输出格式约束、核心业务规则、语气要求),放进去 —— 成本可预测,且保证模型一直记得;如果信息量大且只有部分请求需要(知识库文档、历史工单、用户过往记录),去检索 —— 否则成本爆炸且注意力被稀释。中间地带(中等大小、高频需要)的常见做法是:常驻一个摘要版本,细节按需检索。反模式有两个:把整个知识库塞进系统提示,或者把核心格式约束也做成检索项(该命中时没命中,模型就自由发挥了)。

3. 上下文超预算了,你会按什么顺序做压缩?每一步的代价是什么?

答案

顺序建议:① 裁剪工具输出(在工具层截断、分页、只保留需要的字段)—— 代价最低,因为工具返回里通常大量是噪声;② 滚动摘要对话历史 —— 保留决策、约束、未完成事项,丢弃过程性内容;代价是有损且不可逆,摘要本身还可能引入错误;③ 子 agent 隔离 —— 把检索、试错这类污染性工作放到子 agent,只把结论带回主上下文;代价是子 agent 替你做了压缩决策,回传内容要有 schema 才可控;④ 最后才是截断/滑动窗口 —— 简单但会彻底丢失早期信息。无论哪一步,压缩策略都必须配评测(验证「回答所需信息是否仍在上下文里」),不能凭感觉调压缩率。

4. 为什么说「子 agent 上下文隔离」是上下文工程的手段?它的代价是什么?

答案

因为它把「如何压缩」这个问题变成了「根本不让它进来」:让子 agent 在它自己的上下文里消耗大量检索原文与试错过程,用完之后整体丢弃,只把提炼后的结论回传主上下文。主上下文因此始终干净,指令不被稀释,工具集也被隔离。代价有两层:一是信息损失不可控 —— 回传什么由子 agent 决定,等于它替你做了压缩决策,它丢掉的可能正是下游需要的;二是子 agent 自己也有成本(重复的系统提示与工具定义)。缓解办法是让回传走结构化 schema(结论 + 依据 + 置信度 + 缺失项),并在关键节点做校验。

5. 为什么上下文的「顺序」在工程上很重要?至少给两个理由。

答案

① 注意力分布不均:开头和结尾的信息更容易被模型利用,中间部分容易被忽略,所以关键约束应放在系统提示并在任务末尾复述,检索到的最相关片段应靠近问题而不是按原文档顺序堆放。② prompt caching:缓存以公共前缀为单位,把稳定内容(系统提示、工具定义)放前面、动态内容放后面,才能让缓存命中;把动态内容插到前面会反复击穿缓存,成本和首 token 延迟都会明显上升。此外稳定的顺序也让调试更容易 —— 上下文布局变化本身就是一个需要控制的变量。