上下文越长越好吗?Agent 如何选择、压缩与预算
内容摘要:模型每次只能看到你放进上下文窗口的东西。上下文工程(Context Engineering) 就是在有限 Token 预算内,决定"放什么、怎么压、丢什么、缓什么"。它比"写更长的 Prompt"更能决定 Agent 的成败。 难度:进阶 | 前置知识:01 基础、03 模型 API
常见误区
把全部聊天记录、检索文档和工具结果都塞进上下文,模型掌握的信息就会更多、答案也会更准。
先给结论
上下文工程(Context Engineering)的目标是在有限 Token 预算内装配当前步骤真正需要的信息。过长上下文会增加成本、延迟、干扰与泄露面,必须进行选择、压缩、分区、截断保护和缓存。
带着这三个问题阅读:
- 一次 Agent 调用的上下文由哪些来源组成,优先级如何确定?
- 摘要压缩和直接截断分别可能丢失什么关键事实?
- 如何用预算器让系统在超限前显式降级,而不是静默丢指令?
一、概念与用途
Agent 的每一次模型调用,上下文通常由多个来源拼装而成:
核心矛盾:来源很多,预算有限。放太多 → 超窗被截断、成本高、"大海捞针"式遗漏;放太少 → 模型缺关键信息、幻觉。上下文工程就是在这两者间做受控取舍。
不适用场景:如果任务极短、上下文远小于窗口,过度工程化反而增加复杂度。先测量,再优化。
二、上下文预算:先算账,再装配
把窗口当成预算来分配。给每类内容设上限,超出就触发压缩或丢弃策略,而不是任由拼接:
from dataclasses import dataclass
@dataclass
class ContextBudget:
"""按来源分配 Token 预算(示例值,应按模型窗口调整)。"""
total: int = 8000
reserve_output: int = 1500 # 为输出预留
system: int = 500
tools: int = 800
rag: int = 2500
history: int = 2000
# 剩余给当前输入与状态
def input_budget(self) -> int:
return self.total - self.reserve_output
def rough_tokens(text: str) -> int:
ascii_n = sum(1 for c in text if ord(c) < 128)
return round(ascii_n / 4 + (len(text) - ascii_n) / 1.5)关键:一定要为输出预留(
reserve_output)。只算输入不算输出,是超窗与截断的常见原因。
三、选择:只放"这一步需要的"
不是所有历史、所有检索结果都要进上下文。选择策略:
- 相关性优先:RAG 结果按相似度取 Top-K,而非全塞(11)。
- 近因优先:会话历史保留最近若干轮,早期轮次摘要化。
- 任务相关:只注入与当前子任务有关的状态字段(09)。
- 工具按需:不要把几十个工具声明全给模型,按场景路由可用工具子集(08)。
四、压缩:把长历史变短
当历史超预算,用"滚动摘要(rolling summary)"压缩早期内容,保留近期原文:
def compress_history(history: list[dict], budget: int,
summarize) -> list[dict]:
"""保留最近消息原文;更早的用 summarize() 压成一条摘要。
summarize 是注入的函数(可用一次便宜的模型调用实现)。"""
kept: list[dict] = []
used = 0
# 从最近往前保留原文
for msg in reversed(history):
t = rough_tokens(msg["content"])
if used + t > budget:
break
kept.append(msg)
used += t
kept.reverse()
older = history[: len(history) - len(kept)]
if older:
summary = summarize(older) # [非确定性] 摘要由模型生成
kept.insert(0, {"role": "system", "content": f"[早期对话摘要] {summary}"})
return kept权衡:摘要会丢细节、可能引入摘要幻觉。关键事实(如用户确认过的决定、幂等键)应存入状态/记忆(09、10),不要只依赖摘要。
五、截断:最危险的"静默失败"
超窗时若简单地"砍掉最前面的内容",可能把系统指令或关键约束砍掉,导致 Agent 突然"失忆"、越界。
正确做法:
- 系统指令与安全约束固定保留,永不参与截断。
- 截断发生时记录日志/告警(可观测性,17),不要静默。
- 优先压缩可再生内容(历史、检索结果),保护不可再生内容(状态、幂等键)。
六、缓存:省钱又提速
- 提示缓存(Prompt Caching):部分厂商支持缓存长而稳定的前缀(如系统指令、长工具声明),命中后更便宜更快。把稳定内容放前面、变动内容放后面以提高命中率。
- 检索缓存:相同查询的 RAG 结果可短期缓存。
- 语义缓存:对高度相似的请求复用答案(需谨慎,避免返回过期/串号结果)。
[待验证]:提示缓存的可用性、命中规则、计费方式各厂商不同,使用前查官方文档(
verified_at: 2026-09-07)。
七、失败路径
- 只算输入不算输出 → 输出被截断成半截 JSON。
- 历史无限增长 → 逐渐超窗,模型"忘记"早期约束。
- RAG 全量注入 → 相关信息被噪声淹没("lost in the middle")。
- 摘要丢关键事实 → Agent 重复已完成的动作。
八、测试与验证
- 对典型会话构造"超长历史"用例,断言压缩后系统指令仍在、Token 在预算内。
- 对截断路径写测试:确认关键约束不被砍、且有日志。
- 度量:上下文 Token 分布、超窗率、截断触发率(17)。
九、安全与权限
十、性能与成本
- 上下文长度直接决定成本与延迟。压缩、选择、缓存是 Agent 降本的三大杠杆(18)。
十一、框架差异
- LangChain 1.0 用"中间件"(如
SummarizationMiddleware)做上下文压缩;其他框架各有机制。原则一致:预算化、选择、压缩、保护关键内容。
十二、关联章节
面试复述
上下文不是越长越好,而是要与当前决策相关、可信且在预算内。系统应先保留指令与任务状态,再选择检索证据和历史,最后用可观测的压缩或降级处理超限。
复述时先给结论,再补机制、适用边界和一个可验证指标;不要只罗列框架名称或产品功能。
迁移练习
为一个包含系统指令、20 轮历史、5 段检索结果和3个工具返回的请求制定 Token 预算表,并明确超限时的保留顺序与降级动作。
检索关键词
上下文工程 Context Engineering 上下文预算 滚动摘要压缩 截断风险 提示缓存 Prompt Caching
官方来源
- 各模型厂商"context window / prompt caching"文档;RAG"lost in the middle"相关研究(
verified_at: 2026-09-07)。
