Skip to content
Figo Blogs
Go back

Context Management:上下文选择、裁剪、压缩与缓存治理

Contents

Table of contents

Open Table of contents

1. 为什么 Context 是 Harness 的核心瓶颈

Agent 运行越久,Session 中积累的信息越多:

用户目标
历史对话
系统指令
项目规则
工具定义
文件内容
grep 结果
Shell 日志
Diff
测试输出
错误信息
Permission
Steering
Compaction Summary
Subagent Result

如果简单:

messages.append(...)

然后每轮全部发给模型,会很快出现:

Context Window 溢出
Token 成本上升
首 Token 延迟变长
Tool Result 噪声淹没目标
旧信息污染当前决策
模型注意力被稀释
Prompt Cache 复用下降
历史总结逐轮漂移

因此:

Context Management 本质上是 Agent Runtime 的“工作集管理”。

它和操作系统管理内存很像:

Session = 全量事实
Context = 当前工作集

2. Session ≠ Context

上一章已经建立:

Event Log = Source of Truth
Derived State = 当前 Runtime 状态
Context = Model-visible Projection

这里进一步明确:

Session 可以保存:

完整日志
完整 Tool Output
所有历史 Event
Trace
Permission Metadata
Sandbox Metadata

Context 只保留:

当前任务所需的最小充分信息
Important

Context 丢失信息并不可怕,Session 丢失事实才可怕。

Context 可以重建,Session 应尽量可追溯。


3. Context Management 的完整生命周期

Context Management 至少包含:

Selection
Prioritization
Ordering
Pruning
Truncation
Compaction
Retrieval
Retention
Instruction Hierarchy
Cache-friendly Prefix Construction
Budgeting

4. Context Budget:不能只看当前 Token 数

教学代码常写:

if used_tokens > context_window * 0.8:
    compact()

这个方法简单,但工业实现通常需要更完整的预算。

4.1 推荐预算模型

Context Window
-
Reserved Output Tokens
-
Tool Schema Tokens
-
Safety Margin
=
Usable Input Budget

例如:

usable_input_budget = (
    model_context_window
    - reserved_output_tokens
    - tool_schema_tokens
    - safety_margin
)

然后:

if estimated_input_tokens > usable_input_budget:
    reduce_context()

4.2 为什么必须预留 Output Budget

如果模型支持:

128K Context Window

不能把:

127K

全部用于输入。

因为还必须留出:

Reasoning
Text Output
Tool Calls
Structured Output
Patch

如果没有预留,模型可能在关键 Tool Call 或 Patch 生成中被截断。


5. Context Budget 不是一个单一池子

更实用的设计是逻辑分区。

例如:

System / Policy Budget
Project Instruction Budget
Conversation Budget
Working Memory Budget
Tool Result Budget
Retrieved Context Budget
Reserved Output Budget

可以理解成:

不一定需要硬编码固定比例,但应该有:

不同类型内容的预算意识。


6. Selection:模型到底应该看到什么

Context Builder 首先需要回答:

Session 中哪些信息和当前下一步决策相关?

可以把候选内容分成几类。

6.1 Must-Keep

通常必须保留:

System Instructions
Safety / Permission Policy
Current User Goal
Latest Steering
当前尚未完成的任务约束
关键 Tool Result
当前 Working Set

6.2 High-Value

通常优先保留:

最近读取的相关文件
当前 Diff
最近失败的测试
与当前问题直接相关的历史结论

6.3 Compressible

适合压缩:

较早对话
重复 Tool Output
多个相似错误
已完成步骤
长日志

6.4 Evictable

可以移出当前 Context:

已经解决的旧错误
无关文件内容
过时 Plan
重复确认信息
历史 Tool Metadata

7. Context Priority:不是“越新越重要”

很多实现采用:

最近消息优先

但工业 Agent 中并不够。

有些很早的信息比最近信息重要得多。

例如:

System Prompt
用户明确限制“绝对不要修改 production 配置”
Repository Policy
当前任务目标

即使它们出现得很早,也必须持续保留。

因此排序至少要考虑:

Instruction Priority
Task Relevance
Recency
Authority
Dependency
Unresolved Status

8. 推荐信息优先级模型

可以抽象为:

Tier 0:System / Safety / Policy
Tier 1:Current Goal / Latest Steering
Tier 2:Unresolved Task State
Tier 3:Current Working Set
Tier 4:Recent Tool Results
Tier 5:Historical Conversation
Tier 6:Low-value Metadata

Context 压力上升时:

先删 Tier 6
↓
再压缩 Tier 5
↓
再裁剪 Tier 4
↓
最后才动 Tier 2 / 3

而:

Tier 0 / Tier 1

原则上不应该因为 Token 压力被随意删除。


9. Ordering:同样的信息,顺序也会影响模型行为

Context Builder 不只负责“选什么”,还要决定“怎么排”。

推荐大体结构:

Stable System Instructions
↓
Stable Tool Definitions
↓
Project / Repository Instructions
↓
Current Task Goal
↓
Latest Steering / Constraints
↓
Compacted Historical Context
↓
Current Working Set
↓
Recent Tool Results
↓
Current User Turn

目的有两个:

  1. 维持 Instruction Hierarchy
  2. 提高稳定 Prefix 的概率

10. Prefix Cache:稳定前缀是一种 Runtime 优化

如果模型服务支持 Prompt / Prefix Caching,稳定前缀通常能降低:

成本
首 Token 延迟
重复计算

推荐原则:

高稳定、重复使用的信息靠前;高动态信息靠后。

例如:

System Prompt
Tool Schemas
Project Instructions
Stable Policies
↓
历史 / Summary
↓
Recent Tool Results
↓
Current User Input

避免在最前面插入:

Current Time
随机 Session ID
动态 Token Count
Trace ID

否则每轮 Prefix 都变化。

Warning

不要为了 Cache Hit 破坏正确的语义顺序。

正确性 > Cache Stability。


11. Pruning:先删除低价值信息,再做 Summary

很多系统一遇到 Context 压力就直接:

Summarize Everything

这是一个常见误区。

更合理的顺序:

原因:

能无损删除的内容,不要先用有损摘要。


12. Tool Result Retention:最容易打爆 Context 的部分

Tool Result 常常是 Token 消耗最大的来源。

典型:

grep
read_file
git diff
pytest
npm install
cargo build
日志
网页
数据库查询

不能简单全部长期保留。

12.1 Tool Result 生命周期

建议考虑:

Fresh
Relevant
Referenced
Superseded
Stale
Evictable

例如:

刚执行 pytest 的失败结果

在修复过程中很重要。

一旦:

下一轮 pytest 已成功

之前旧的失败输出通常可以降级或移除。


13. Tool-specific Retention Policy

不同 Tool 应该有不同策略。

ToolContext 中推荐保留
read_file当前相关范围,不保留整份大文件
grepTop-K 结果 + 总命中数量
Shell命令 + Exit Code + 关键首尾输出
Tests失败测试 + Error Summary
CompilerError Blocks / Diagnostics
git diff当前修改相关 Hunk
LSP当前 Diagnostics
Web / API结构化关键字段 + 来源标识

因此更好的架构是:

Tool
↓
Result Normalizer
↓
Retention Policy
↓
Context Projection

而不是所有 Tool Result 都直接 append。


14. Truncation:体积控制必须保留“缺失事实”

如果输出太长,截断后必须显式告诉模型:

内容不完整
原始长度
保留了哪些范围
是否可以继续读取

例如:

[OUTPUT TRUNCATED]
Original: 52,431 chars
Retained: first 6,000 + last 6,000
Use read_range(offset=...) for omitted content.

这比单纯:

output = output[:12000]

安全得多。

因为模型必须知道:

它看到的不是完整世界。


15. 大文件读取:Windowing 优于一次全读

对于 50K 行文件,不应:

read_file(all)

推荐:

metadata
↓
symbol / grep / index
↓
read relevant range
↓
expand around target

可以理解为:

这既降低 Token 成本,也减少无关内容干扰。


16. Compaction:什么时候才应该真正压缩

Compaction 适合处理:

较早历史
已完成步骤
重复观察
长期任务背景
大量细节但仍需保留语义

而不是所有东西都压缩。

典型触发方式:

Token Threshold
Step Count
Time / Session Length
Explicit User Request
Before Subtask Switch
Before Model Switch

17. Reactive Compaction 与 Proactive Compaction

17.1 Reactive

当:

Context 快满

才压缩。

优点:

简单
压缩次数少

缺点:

可能在关键步骤前被迫压缩
延迟突然增加
Summary 压力大

17.2 Proactive

在明确边界主动压缩:

完成一个子任务
切换工作阶段
完成一次大范围代码探索

优点:

Summary 语义边界更清晰
更容易保持任务结构

缺点:

可能过早丢失细节

更好的系统通常是:

Proactive + Reactive 混合。


18. Compaction 应压缩什么

一个好的 Summary 不应只是:

The user asked to fix the bug. We inspected files and made some changes.

更应该保留结构化状态:

Goal
Constraints
Completed Work
Current Working Hypothesis
Files Read
Files Modified
Important Symbols
Test Results
Unresolved Errors
Pending Decisions
User Steering
Next Recommended Step

19. 推荐 Compaction Schema

例如:

goal:
constraints:
completed:
working_set:
  files:
  symbols:
changes:
test_status:
failed_attempts:
open_questions:
latest_user_intent:
next_step:

结构化 Summary 的优势:

更容易稳定生成
更容易校验
更容易注入下一轮 Context
更适合 Eval

20. Summary Drift:长任务中的隐形风险

如果系统不断:

Summary 1
↓
Summary 2 基于 Summary 1
↓
Summary 3 基于 Summary 2
↓
...

就会出现类似多次转述后的信息漂移:

细节丢失
约束消失
错误事实被固化
任务目标变形

这就是 Summary Drift。


21. 如何降低 Summary Drift

推荐几种策略。

21.1 Stable Anchors

永远单独保留:

Original User Goal
Critical Constraints
Latest Steering
Repository Policy

不要让它们只存在于 Summary。


21.2 Summary + Raw Anchors

Context 可以是:

Stable Goal
Stable Constraints
Compacted History
Recent Raw Events

而不是:

Only Summary

21.3 Periodic Re-grounding

定期从原始 Session Events 重新生成 Summary,而不是永远 Summary-on-Summary。


21.4 Structured Verification

Compaction 后检查:

Goal 是否仍存在?
关键限制是否保留?
当前修改文件是否完整?
未解决错误是否丢失?

22. Retrieval:被移出 Context 不等于永久丢失

Context 不应该承担完整知识存储。

对于已经移出的信息,可以:

按需重新读取
重新 grep
从 Session Store 检索
从 Artifact Store 获取

因此:

Eviction ≠ Forgetting

更合理:

Context Eviction
↓
External Retention
↓
On-demand Retrieval

23. Working Set:Agent 当前真正“正在想”的东西

一个非常有用的概念是:

Working Set

例如 Coding Agent 当前正在修:

src/auth.ts
src/api.ts
tests/auth.test.ts

那么 Working Set 应该优先包括:

当前相关文件片段
当前 Diff
当前 Diagnostics
当前测试失败
最新用户限制

而不是整个 Repository 历史。

这和程序运行时的“工作集”非常类似。


24. Working Set 如何更新

可以通过事件动态更新:

Tool read_file(A)
↓
A 进入 Working Set

Tool edit_file(B)
↓
B 提升优先级

Tests 只涉及 C
↓
C 进入 Working Set

任务转向新模块
↓
旧文件逐渐衰减

可以给每项维护:

recency
relevance
reference_count
unresolved_dependency

25. Context Builder 推荐职责

可以设计成:

class ContextManager:
    def calculate_budget(...): ...
    def select(...): ...
    def rank(...): ...
    def prune(...): ...
    def truncate(...): ...
    def compact(...): ...
    def retrieve(...): ...
    def order(...): ...
    def build(...): ...

而不是:

def compact_messages():
    ...

ContextManager 的目标是:

构造下一次推理的最佳输入。

Compaction 只是其中一个策略。


26. 推荐 Context Item 数据模型

可以为每个候选信息维护:

@dataclass
class ContextItem:
    id: str
    type: str
    content: str

    token_estimate: int
    priority: int

    source_event_id: str | None
    created_at: float

    pinned: bool = False
    compressible: bool = True
    evictable: bool = True

进一步可以增加:

relevance_score
recency_score
authority
dependency_count

这样 Context Builder 才能显式决策。


27. Context 选择可以视为一个优化问题

抽象上:

Maximize:
    Expected Decision Value

Subject to:
    Token Budget

也就是:

在有限 Token 中
选择对当前下一步最有用的信息组合

并不一定需要真的运行复杂优化算法,但这种思维很重要。


28. Context Poisoning:旧事实可能变成错误上下文

Agent 长时间运行后,历史里可能存在:

已经失效的文件内容
旧测试结果
旧 Plan
被用户推翻的要求
错误推测

如果这些仍然在 Context 中,就会产生 Stale Context。

例如:

早期读到 foo() 有 2 个参数
↓
Agent 后来已经把它改成 3 个
↓
旧文件片段仍然存在 Context
↓
模型继续依据旧版本推理

因此 Context 不只是“压缩”,还要解决:

信息新鲜度与冲突。


29. Supersession:新事实覆盖旧事实

ContextManager 应识别:

New File Read supersedes old File Read
New Test Result supersedes old Test Result
Latest Steering supersedes previous plan
Latest Diff supersedes old Diff

可以给 Event / Context Item 增加:

supersedes
version
resource_id

帮助淘汰旧内容。


30. Conflicting Context:冲突不能静默存在

如果 Context 中同时出现:

用户要求:不要改 auth.py
旧 Plan:修改 auth.py

需要明确:

Latest User Steering > Old Plan

所以 Context Builder 需要尊重:

Authority
Recency
Explicit Override

而不是单纯按出现顺序拼接。


31. Context 与 Tool Schema 的关系

Tool Schema 也占 Context。

如果注册了:

100 个 Tool

每次都把全部 Schema 发给模型,会造成:

Token 浪费
选择难度上升
Cache 复杂度增加
误调用概率增加

因此 Harness 可以考虑:

Tool Selection
Dynamic Tool Exposure
Capability Groups
Lazy Tool Loading

也就是说:

Context Management 不只管理消息,也管理本轮暴露给模型的工具集合。


32. Dynamic Tool Exposure

例如当前任务只是代码阅读:

read
grep
glob
lsp

此时不一定需要暴露:

deploy
send_email
browser
database_write

当任务进入发布阶段再开放。

这同时改善:

安全
Token
Tool Selection Accuracy

33. Context 与 Subagent

未来进入 Subagent 后,要特别避免:

Parent Context 全量复制给所有 Subagent

更合理:

Parent Session
↓
Subtask Definition
↓
Scoped Context Projection
↓
Subagent

也就是说:

Subagent 应拿到任务相关 Context,而不是整个父 Session。

这也是为什么先学 Context Management,再学 Subagent。


34. Context 与 Model Routing

不同模型可能有不同:

Context Window
Tool Calling Format
Output Limit
Cache Behavior
Reasoning Budget

所以 ContextManager 不应完全脱离 Model Profile。

可以有:

budget = context_manager.calculate_budget(
    model_profile=model.profile
)

而不是默认所有模型统一 128K。


35. Context Failure Taxonomy

推荐把 Context 问题明确分类。

OVERFLOW
UNDER_CONTEXT
STALE_CONTEXT
CONFLICTING_CONTEXT
SUMMARY_DRIFT
TOOL_OUTPUT_DOMINANCE
INSTRUCTION_LOSS
PREFIX_INSTABILITY
IRRELEVANT_CONTEXT
MISSING_EVIDENCE

这样 Eval 时能分析:

Agent 是“不会做”,还是“没看到该看的信息”。


36. Context Overflow 与 Under-context 同样危险

很多人只关心:

Context 太多

其实:

Context 太少

同样会导致失败。

例如过度裁剪后:

丢掉用户限制
丢掉关键函数定义
丢掉之前失败原因

所以目标不是:

Minimum Tokens

而是:

Minimum Sufficient Context

37. Context Evaluation:怎么判断治理策略好不好

可以记录:

指标含义
Input Tokens / Step每轮上下文成本
Context Overflow Rate是否超窗口
Compaction Count压缩频率
Retrieval Count信息补回次数
Re-read Rate是否频繁重复读文件
Tool Output ShareTool Result 占上下文比例
Instruction Loss Rate是否丢失关键约束
Summary Drift Failure压缩导致的失败
Cache Hit / Reuse前缀复用情况
Task Success最终结果

38. 一个重要指标:Re-read Rate

如果 Context 管理不合理,Agent 会不断:

read_file(A)
↓
Context 淘汰
↓
read_file(A)
↓
Context 淘汰
↓
read_file(A)

这不一定是模型“笨”,可能是 Harness 的 Retention Policy 太激进。

因此:

Repeated Read Rate

是一个很有价值的 Context Eval 指标。


39. Context Policy 不应该只有一个策略

不同任务适合不同策略。

例如:

Coding

重点保留:

Working Set
Diff
Diagnostics
Tests

Deep Research

重点保留:

Claims
Evidence
Sources
Open Questions

Browser Agent

重点保留:

Current Page State
Recent Actions
Target
Critical DOM / Extracted Data

因此更合理:

ContextManager
↓
Task-specific Context Policy

40. 推荐工业级 Context Pipeline

这张图可以作为本章最终架构图。


41. 推荐实践:自己实现 ContextManager

实验 1:Token Budget

实现:

calculate_budget()

至少考虑:

context_window
reserved_output
tool_schema
safety_margin

实验 2:Priority Pruning

给 Context Item 设置:

priority
pinned
evictable

当超预算时:

自动删除最低优先级项

实验 3:Tool-specific Truncation

分别为:

bash
grep
read_file
tests

实现不同策略。

观察 Task Success 与 Token 使用变化。


实验 4:Working Set

维护:

active_files
active_symbols
recent_failures

只优先保留当前工作集。


实验 5:Compaction

生成结构化 Summary:

goal:
constraints:
completed:
working_set:
test_status:
open_questions:
next_step:

实验 6:Summary Drift

连续 Compact 5 次。

比较:

Summary-on-Summary

和:

Periodic Re-ground from Raw Events

是否丢失更多约束。


实验 7:Stale Context

让 Agent:

读取文件 A
修改文件 A
继续使用旧读取结果

验证 Supersession 机制是否能淘汰旧版本。


实验 8:Dynamic Tool Exposure

比较:

暴露 30 个 Tool

和:

只暴露当前任务需要的 5 个 Tool

在:

Token
误调用率
成功率

上的差异。


42. 推荐 ContextManager 伪代码

class ContextManager:
    def __init__(self, tokenizer, policies):
        self.tokenizer = tokenizer
        self.policies = policies

    async def build(
        self,
        session,
        model_profile,
        tool_registry,
    ):
        state = await session.current_state()

        budget = self.calculate_budget(
            model_profile=model_profile,
            tool_registry=tool_registry,
        )

        items = self.collect_candidates(
            session=session,
            state=state,
        )

        items = self.pin_critical(items)
        items = self.remove_superseded(items)
        items = self.rank(items, state=state)
        items = self.prune(items, budget=budget)
        items = self.truncate_tool_results(items)

        if self.token_size(items) > budget.input_limit:
            items = await self.compact(
                items=items,
                session=session,
                budget=budget,
            )

        if self.needs_retrieval(items, state):
            retrieved = await self.retrieve_missing_context(
                state=state,
                session=session,
            )
            items.extend(retrieved)

        items = self.order_for_model(items)

        selected_tools = self.select_tools(
            tool_registry=tool_registry,
            state=state,
        )

        self.validate_budget(
            items=items,
            tools=selected_tools,
            budget=budget,
        )

        return ModelContext(
            messages=self.project_messages(items),
            tools=selected_tools,
        )

这份伪代码的重点不是具体算法,而是职责顺序:

Collect
↓
Pin
↓
Remove Stale
↓
Rank
↓
Prune
↓
Truncate
↓
Compact
↓
Retrieve
↓
Order
↓
Validate

43. ContextManager 与 SessionStore 的边界

SessionStore 负责:

事实
事件
持久化
Replay

ContextManager 负责:

模型下一步看到什么

不要让 ContextManager:

修改原始历史
删除 Session Event
改变 Runtime 事实

Context 是 View,不是 Source of Truth。


44. ContextManager 与 Tool Runtime 的边界

Tool Runtime 负责:

执行 Tool
生成完整 Tool Result

ContextManager 负责:

决定 Tool Result 的哪些部分进入下一轮推理

例如:

Tool Runtime 保存 100K 日志
↓
Context Projection 只保留关键 5K

这样既能:

保留审计

又能:

控制 Token

45. 本章必答的 20 个问题

  • 为什么 Session 不应该等于 Context?
  • Context Management 为什么不等于 Compaction?
  • 为什么需要 Reserved Output Budget?
  • 为什么不能简单按“最近消息优先”?
  • 什么信息应该 Pinned?
  • 为什么 Pruning 应该早于 Compaction?
  • Tool Result 为什么需要独立 Retention Policy?
  • 截断时为什么必须告诉模型内容不完整?
  • 大文件为什么应该 Windowed Read?
  • Reactive 与 Proactive Compaction 有什么区别?
  • 好的 Compaction Summary 应该保存什么?
  • 什么是 Summary Drift?
  • 如何通过 Re-grounding 降低 Summary Drift?
  • 为什么 Context Eviction 不等于 Forgetting?
  • Working Set 是什么?
  • 什么是 Stale Context?
  • Supersession 解决什么问题?
  • 为什么 Tool Schema 本身也是 Context?
  • Dynamic Tool Exposure 有什么价值?
  • 为什么目标是 Minimum Sufficient Context,而不是 Minimum Tokens?

46. 与下一篇 Tool Runtime 的衔接

Context 决定:

模型看见什么。

接下来必须解决:

当模型决定采取 Action 后,这个 Action 如何安全、可靠地变成真实世界中的执行?

也就是:

Tool Call
↓
Tool Lookup
↓
Schema Validation
↓
Permission
↓
Scheduling
↓
Execution
↓
Timeout / Cancellation
↓
Sandbox
↓
Result Normalization
↓
Context Projection

下一篇:

Tool Runtime:从 Tool Call 到可靠执行

会重点讨论:

Tool Schema
Validation
Effect Metadata
Concurrency
Idempotency
Timeout
Cancellation
Result Normalization
Error Taxonomy
Execution Journal
Tool-specific Output Policy

47. 一句话总纲

Quote

工业级 Context Management 的目标不是“把历史塞进模型”,而是把完整 Session 中的事实持续重构成一个受预算约束、优先级明确、可裁剪、可压缩、可检索、可验证、对当前决策最有价值的模型工作集;Context 是 Runtime 动态生成的视图,而不是历史本身。


Share this post:

Previous Post
Tool Runtime:从 Tool Call 到可靠执行
Next Post
工业级 Agent Session:Event Log、State Reducer、Resume 与 Replay