Agent上下文管理的四道防线:从压缩策略到Workspace隔离的全链路实践
Agent上下文管理的四道防线:从压缩策略到Workspace隔离的全链路实践
当Prompt写得足够清楚,Agent依然不稳定时,问题出在上下文管理上。本文从检索、压缩、记忆、隔离四个维度,系统拆解Agent上下文工程的全链路实践。
引言:为什么Prompt工程救不了你的Agent?
“指令明明写对了,Agent为什么还会做错?”
这是AI Agent团队最常遇到的困惑。一个典型场景是:合同审核Agent的系统提示写得非常清楚——“任何对外发送动作都必须先由用户确认”。测试前几轮一切正常,到了第18轮,Agent却把未经确认的审核意见直接发给了供应商。
复盘时发现,问题不出在Prompt本身,而出在模型推理前“看见”的全部信息:工具列表里塞着二十多个相似动作;检索结果带进来一份过期流程,写着“审核完成后自动通知”;对话历史经过压缩,摘要保留了合同结论,却漏掉了“对外发送前再次确认”这条临时约束。
这就是上下文工程的本质问题:Agent每执行一步,上下文都在变化。如果不对上下文进行系统化管理,Prompt写得再漂亮,也会被其他信息的干扰“淹没”。
本文将从四道防线——检索、压缩、隔离、记忆——系统拆解Agent上下文管理的全链路实践。
第一道防线:检索——按需加载,不让噪声进入窗口
1.1 问题本质:不是“信息不够”,而是“信息太多”
很多团队遇到Agent回答不准确,第一反应是“多塞点资料”。但Manus团队分享的经验恰恰相反:把过多的工具和资料塞进上下文,反而会让模型“分心”——工具太多导致选错工具、检索结果互相矛盾导致决策错误。
核心原则:默认不进入,能写出触发条件才升级为按需,能证明每轮都需要且出错代价高才升级为常驻。
1.2 三层动作空间设计
Manus团队将工具调用组织成三层抽象,按需加载:
L1:函数调用层(Function Calling)
- 标准化、Schema安全
- 工具变更时缓存重建
- 数量控制,避免上下文混乱
L2:沙箱工具层(Sandbox Utilities)
- 会话在VM沙箱中运行
- 模型直接调用Shell命令
- 大输出直接写入文件,不挤占上下文
L3:脚本/包&APIs层(Packages & APIs)
- Agent写Python脚本调用预授权API
- 适合数据量大、多步调用的复杂流程
- 运行时内存处理大量数据,只返回关键信息
- 保持上下文干净,留给“推理”而非“搬运数据”
1.3 代码实践:工具输出的检索与截断
class ToolOutputManager:
"""
工具输出管理:大输出落盘,只留摘要
参考:Manus的Offload Context策略
"""
def __init__(self, workspace_dir: str):
self.workspace_dir = workspace_dir
self._output_cache = {}
def handle_tool_output(self, tool_name: str, output: str, max_inline_tokens: int = 500) -> dict:
"""
处理工具输出:大结果写文件,小结果直接返回
"""
estimated_tokens = len(output) // 4
if estimated_tokens <= max_inline_tokens:
# 小结果:直接返回
return {
"type": "inline",
"content": output
}
else:
# 大结果:写入文件,返回摘要+引用
file_path = f"{self.workspace_dir}/tool_outputs/{tool_name}_{timestamp()}.txt"
write_to_file(file_path, output)
# 生成摘要(用cheap模型或截取前500字)
summary = output[:500] + "...[truncated]"
return {
"type": "reference",
"summary": summary,
"file_path": file_path,
"instruction": f"完整结果已写入{file_path},如需详细信息请使用read_file工具读取"
}
关键点:把大结果“卸载”到文件系统,而不是塞进对话历史。
第二道防线:压缩——保住关键状态,而不是“删掉旧消息”
2.1 两种压缩策略
从Manus和Hermes等系统的实践中,可以提炼出两种核心压缩思路:
| 策略 | 做法 | 适用场景 |
|---|---|---|
| 卸载(Offload) | 工具结果写文件,上下文只留摘要+文件引用 | 大结果、长输出 |
| 压缩(Compress) | 对历史对话做结构化摘要,保留关键状态 | 长对话、多轮迭代 |
2.2 三区压缩模型
Hermes Agent采用的“三区压缩模型”是目前生产环境中验证有效的方案:
┌──────────────────────────────────────────────────┐
│ HEAD(锚定区) │
│ System Prompt + 首轮对话 │
│ → 永远不压缩,保证Agent人格和目标不动摇 │
├──────────────────────────────────────────────────┤
│ MIDDLE(归档区) │
│ 历史对话中被压缩的部分 │
│ → 用LLM生成结构化摘要,保留关键状态 │
├──────────────────────────────────────────────────┤
│ TAIL(活跃区) │
│ 最近对话(约~20k tokens) │
│ → 完整保留,保证当前任务连续性 │
└──────────────────────────────────────────────────┘
触发阈值:上下文达到85%时自动触发归档,将MIDDLE区替换为摘要。
2.3 压缩时必须保留的三类信息
仅靠“总结”是不够的。Anthropic的实践表明,摘要一旦漏掉关键信息,后续执行会出错。压缩模板必须强制保留三类内容:
class CompactionPreserver:
"""
压缩时必须保留的关键信息
参考:Anthropic长任务实践 + Manus结构化总结
"""
@staticmethod
def extract_preserved_state(messages: list) -> dict:
return {
# 1. 已经做出的决定
"decisions": [
"确认使用PostgreSQL作为主数据库",
"决定将缓存层迁移到Redis"
],
# 2. 尚未解决的事项
"pending_items": [
"待确认API限流策略",
"等待法务回复合规审核"
],
# 3. 任务的关键约束
"constraints": [
"测试环境数据库不能删除",
"所有API变更需先code review",
"审批规则:对外发送前必须用户确认" # ← 这条经常被漏掉!
]
}
2.4 代码实践:渐进式压缩
class ContextCompressor:
"""
渐进式上下文压缩
参考:AgentScope Harness + ContextCompressor npm包
"""
def __init__(self, max_tokens: int = 60000, threshold: float = 0.85):
self.max_tokens = max_tokens
self.threshold = threshold
self.summarizer = CheapLLMSummarizer() # 使用cheap模型做摘要
def should_compress(self, current_tokens: int) -> bool:
return current_tokens > self.max_tokens * self.threshold
def compress(self, messages: list, config: dict) -> list:
"""
压缩历史消息
保护:HEAD区(System + 首轮)+ TAIL区(最近消息)
压缩:MIDDLE区(中间消息)
"""
head_size = config.get("protect_first_n", 3) # System + 首轮对话
tail_size = config.get("protect_last_n", 10) # 最近N条消息
# 分割三区
head = messages[:head_size]
middle = messages[head_size:-tail_size]
tail = messages[-tail_size:] if tail_size > 0 else []
if not middle:
return messages
# 从middle中提取关键状态
preserved = self._extract_preserved_state(middle)
# 用cheap模型压缩middle
summary = self.summarizer.summarize(middle, preserved)
# 重新组装:HEAD + 摘要 + TAIL
compressed = head + [{
"role": "system",
"content": f"[历史摘要] {summary}",
"preserved_state": preserved
}] + tail
return compressed
关键原则:压缩不是删消息,是把“对后续行动有影响”的状态提取出来,用结构化方式保留。
第三道防线:隔离——让每个Agent拥有独立的上下文边界
3.1 隔离的核心问题
当Agent系统从单体扩展到多Agent协作时,共享上下文会成为最大的故障源。OpenClaw团队的实践表明,“上下文污染”是单体AI助理最典型的痛点:写文章时夹带代码术语、把上一个客户的需求当成当前需求的前置条件、不同风格的输出混杂在一起。
3.2 隔离的三层边界
Polynoia系统提出了“per-agent隐私模型”——每个Agent只看到自己参与过的对话,不同Agent之间的上下文完全隔离:
内容类型 谁能看到
─────────────────────────────────────────
自身身份 自己
项目档案 所有项目成员
跨对话文本 只看到自己参与过的对话
代码变更 同一workspace下所有成员
当前对话历史 本对话成员
关键规则:Agent不在的对话完全不可见(包括别人之间的1v1);两个不同人格共用同一模型(如Claude-Fast和Claude-Hardcore)也各自独立。
3.3 代码实践:Workspace隔离
AgentScope Java 1.1.0的Harness模块提供了完整的Workspace隔离方案:
// AgentScope Harness:隔离的Workspace配置
WorkspaceSpec docker = WorkspaceSpec.docker()
.isolationScope(IsolationScope.SESSION) // 会话级隔离
.sandboxBackend(SandboxBackend.DOCKER) // 沙箱执行
.workspacePath("/workspace");
// 每个Agent拥有独立的workspace
HarnessAgent agent = HarnessAgent.builder()
.name("content-writer")
.workspace(new WorkspaceConfig()
.scope(IsolationScope.USER)
.backend(FileSystemBackend.REDIS) // 分布式共享
)
.build();
IsolationScope提供了四个隔离粒度:
| 粒度 | 适用场景 |
|---|---|
SESSION |
单次会话隔离,最细粒度 |
USER |
用户级隔离,跨会话共享工作区 |
AGENT |
Agent级隔离 |
GLOBAL |
公共工具型Agent |
3.4 子Agent隔离原则
Claude Code等系统在多Agent协作中验证了三条关键隔离原则:
原则1:零继承是默认 —— 子Agent只接收明确的任务Prompt,不继承父Agent的对话历史、上下文和会话状态。
原则2:Full-inheritance Fork必须单层 —— 当子Agent确实需要完整上下文时(Fork模式),强制单层边界:分叉出来的Agent不能再分叉。否则递归分叉会产生指数级上下文成本。
原则3:文件系统隔离 —— 子Agent修改文件时,需要自己的工作副本(通过worktree或临时目录),避免文件级竞态条件。
第四道防线:记忆——跨会话的知识沉淀与状态恢复
4.1 双层记忆架构
AgentScope Harness提出了双层记忆机制:
短期记忆(对话内)
└── 当前会话的完整对话历史
└── 存在于session文件中
长期记忆(跨会话)
└── 自动提炼的事实沉淀到MEMORY.md
└── 后台周期性合并流水账为精炼记忆
└── 下次启动时自动加载到System Prompt
4.2 记忆的四个操作语义
Manus团队的经验表明,记忆的核心难点不在“存”,而在“什么有资格写入、发生变化时如何更新、过期后怎样退出”。至少需要四个操作语义:
class MemoryEntry:
"""
长期记忆条目的规范
参考:Manus显式记忆机制 + Anthropic上下文工程
"""
def __init__(self, content: str, source: str, confidence: float):
self.content = content
self.source = source # 来源:user_confirm / system_verify / model_infer
self.confidence = confidence
self.created_at = now()
self.expires_at = None # 需显式设置过期时间
self.version = 1 # 支持版本更新
def should_persist(self) -> bool:
# 只有用户明确确认或系统验证的事实才能写入长期记忆
return self.source in ["user_confirm", "system_verify"]
4.3 代码实践:Workspace驱动的状态恢复
OpenClaw和Hermes的实践表明,Workspace是Agent状态的“唯一事实来源”:
workspace/
├── AGENTS.md # Agent人格定义(what am I)
├── SOUL.md # Agent的行为规则(how I act)
├── MEMORY.md # 跨会话长期记忆
├── knowledge/ # 领域知识
├── skills/ # 可复用技能
├── sessions/ # 会话历史
│ └── <conv-id>/
│ └── session.jsonl
└── agents/<agentId>/ # 每个Agent的独立状态
每次运行自动从Workspace加载上下文,结束后自动回写记忆,Agent的能力随时间持续演化。
总结:上下文管理的四道防线
| 防线 | 核心目标 | 关键实践 |
|---|---|---|
| 检索(Retrieve) | 不让噪声进入窗口 | 按需加载工具和资料;三层动作空间(函数/沙箱/脚本) |
| 压缩(Compress) | 保住关键状态 | 三区压缩(HEAD/MIDDLE/TAIL);强制保留决定/未决项/约束 |
| 隔离(Isolate) | 不让上下文互相干扰 | Workspace隔离;per-agent隐私边界;子Agent单层Fork |
| 记忆(Memory) | 跨会话知识复用 | 双层记忆(短期+长期);显式写入门禁;Workspace驱动恢复 |
这些防线不是独立运作的,而是层层递进的关系:检索决定信息是否进入窗口,压缩决定窗口内信息的优先级和生命周期,隔离决定信息在Agent间的边界,记忆决定信息是否跨会话留存。
当Agent出错时,排查顺序应该是:
- 还原 —— 把错误时模型收到的完整输入拉出来,包括系统提示、工具定义、检索片段、压缩摘要、记忆
- 对症 —— 资料没进来?查检索;资料过多互相冲突?查压缩;上下文串线?查隔离;跨会话误用旧知识?查记忆
- 单变量回归 —— 一次只改一个来源或一条策略,用同一任务重跑
做到这四道防线,Prompt工程才真正有了“地基”——模型看见的信息是可控的、干净的、有序的,而不是一锅大杂烩。
参考文献:
- Polynoia上下文系统设计,GitHub,2026
- AgentScope Java Harness Framework,GitHub,2025
- 上下文管理 - From Manus分享,微信公众平台,2026
- 上下文工程观止,今日头条,2026
- OpenClaw“养虾”指南,腾讯云开发者社区,2026
- memo-agent Context Compression,NPM,2026
- Context Isolation for Delegated Work,GitHub,2026
更多推荐

所有评论(0)