文章摘要
2026 年 6 月,OpenAI 和 Anthropic 在 Agent 架构上出现了根本性分歧:OpenAI 押注 Compaction(上下文压缩),Anthropic 押注 Sub-Agents(子代理编排)。这不是技术细节的差异,而是对「Agent 如何突破上下文窗口限制」这一核心问题的不同哲学。本文从 Claude Code 源码泄露事件切入,深度对比两种架构的优劣、适用场景和代码实现,给出你的选型决策树。
一、引子:Claude Code 泄露揭示了什么?
2026 年 3 月 31 日,Anthropic 的一次 npm 配置失误,意外暴露了 Claude Code 的完整源码——512,000 行 TypeScript,1,900 个文件。 这是 AI 行业有史以来最大规模的源码泄露事件之一。
但更有价值的不是泄露本身,而是泄露揭示的内部架构:
- Memory Compaction 机制:Claude Code 在对话超过一定长度后,会自动触发「记忆压缩」——用一个小型模型将历史对话压缩为摘要,释放上下文窗口空间
- Sub-Agent 编排:Claude Code 可以 spawn 多个子代理并行处理任务,每个子代理有独立的上下文窗口
- CLAUDE.md 系统:项目级上下文文件,每次会话自动加载
- Hooks 系统:在工具调用前后执行安全检查、日志记录等中间件
💡前置阅读收获:读完本文,你将理解 Compaction 和 Sub-Agents 两种架构的本质区别、各自的优劣、如何根据你的任务特征选择架构、以及完整的代码实现。
这次泄露最重要的启示是:Agent 的核心瓶颈不是模型能力,而是上下文管理。 无论模型多强,如果无法有效管理有限的上下文窗口,Agent 就无法处理长时间、多步骤的复杂任务。
OpenAI 和 Anthropic 对这个问题给出了截然不同的答案。
二、Compaction 架构:在单窗口内做减法
Compaction 的核心思想极其简洁:当上下文快满时,压缩历史,保留精华。
这就像人类的工作记忆——你不可能记住一天的每一个细节,但你会记住关键决策、重要发现和待办事项。Compaction 让 Agent 也具备这种「选择性遗忘」的能力。
工作流程
- Agent 执行任务,对话历史逐渐增长
- 当 token 数达到阈值(如 80% 上下文窗口),触发压缩
- 压缩器(通常是一个小型模型)分析完整历史
- 提取关键信息:决策、发现、错误、待办
- 用压缩后的摘要替换原始历史
- Agent 继续执行,上下文窗口重新获得空间
优势
- 架构简单:不需要管理多个上下文窗口,不需要进程间通信
- 状态一致:所有信息在同一个上下文中,不会出现状态同步问题
- 调试友好:只有一个执行流,日志和追踪简单
- 成本低:压缩用小模型(1B-7B),成本远低于主模型
劣势
- 信息损失:压缩必然丢失细节,可能导致后续决策失误
- 压缩延迟:压缩过程本身需要时间(通常 1-3 秒)
- 递归退化:多次压缩后,信息质量可能逐层下降
- 无法并行:所有子任务共享一个上下文,无法真正并行执行
OpenAI 的实现
OpenAI 在 Responses API 中内置了 Compaction 机制。当对话超过阈值时,API 自动触发压缩,对开发者透明。你也可以通过 metadata.compaction 参数手动控制压缩策略。
"""
Compaction 架构:单窗口上下文压缩
模拟 OpenAI 风格的 Agent 上下文管理
"""
from dataclasses import dataclass
from typing import List, Dict, Optional
import json
@dataclass
class Message:
role: str # "user" | "assistant" | "system" | "tool"
content: str
tokens: int
metadata: Optional[Dict] = None
class CompactionAgent:
"""
Compaction 架构 Agent
核心机制:当上下文快满时,自动压缩历史对话
"""
def __init__(self, max_context_tokens: int = 128000, compaction_threshold: float = 0.8):
self.max_tokens = max_context_tokens
self.threshold = compaction_threshold
self.messages: List[Message] = []
self.compaction_count = 0
self.total_compaction_time_ms = 0
def add_message(self, role: str, content: str, tokens: int) -> None:
"""添加消息到上下文"""
self.messages.append(Message(role=role, content=content, tokens=tokens))
# 检查是否需要压缩
current_tokens = sum(m.tokens for m in self.messages)
if current_tokens > self.max_tokens * self.threshold:
self._compact()
def _compact(self) -> None:
"""
压缩上下文
策略:
1. 保留最近 N 条消息(不压缩)
2. 保留所有未完成的待办事项
3. 将旧消息压缩为摘要
"""
# 保留最近 10 条消息
recent = self.messages[-10:]
old = self.messages[:-10]
if not old:
return
# 提取关键信息
decisions = []
errors = []
todos = []
facts = []
for msg in old:
content = msg.content.lower()
if any(kw in content for kw in ["决定", "选择", "decision"]):
decisions.append(msg.content[:200])
if any(kw in content for kw in ["错误", "error", "失败", "fail"]):
errors.append(msg.content[:200])
if any(kw in content for kw in ["待办", "todo", "需要"]):
todos.append(msg.content[:200])
if msg.role == "tool" and "result" in content:
facts.append(msg.content[:200])
# 生成压缩摘要
summary_parts = []
if decisions:
summary_parts.append(f"## 关键决策\n" + "\n".join(f"- {d}" for d in decisions[:5]))
if errors:
summary_parts.append(f"## 遇到的错误\n" + "\n".join(f"- {e}" for e in errors[:5]))
if todos:
summary_parts.append(f"## 待办事项\n" + "\n".join(f"- {t}" for t in todos[:5]))
if facts:
summary_parts.append(f"## 工具执行结果\n" + "\n".join(f"- {f}" for f in facts[:5]))
summary_content = "\n\n".join(summary_parts) if summary_parts else "[历史对话已压缩,无关键信息]"
# 用摘要替换旧消息
summary_tokens = len(summary_content.split()) * 1.3
summary_msg = Message(
role="system",
content=f"[上下文压缩 #{self.compaction_count + 1}]\n\n{summary_content}",
tokens=int(summary_tokens),
metadata={"compaction": True, "compressed_messages": len(old)}
)
self.messages = [summary_msg] + recent
self.compaction_count += 1
# 输出压缩统计
old_tokens = sum(m.tokens for m in old)
new_tokens = int(summary_tokens)
saved = old_tokens - new_tokens
print(f"[Compaction #{self.compaction_count}] "
f"压缩 {len(old)} 条消息 | "
f"{old_tokens} → {new_tokens} tokens | "
f"节约 {saved} tokens ({saved/old_tokens*100:.0f}%)")
def get_context(self) -> List[Message]:
"""获取当前上下文"""
return self.messages
def stats(self) -> Dict:
"""统计信息"""
total_tokens = sum(m.tokens for m in self.messages)
return {
"messages": len(self.messages),
"tokens": total_tokens,
"utilization": f"{total_tokens / self.max_tokens * 100:.1f}%",
"compactions": self.compaction_count,
}
# 演示
if __name__ == "__main__":
agent = CompactionAgent(max_context_tokens=1000, compaction_threshold=0.7)
# 模拟长对话
conversations = [
("user", "帮我分析这个项目的架构", 50),
("assistant", "好的,让我先读取项目结构...", 80),
("tool", "result: 项目包含 15 个模块,主要依赖 React + Node.js", 60),
("assistant", "发现项目采用前后端分离架构。决定:先分析后端 API 层", 70),
("user", "重点看认证模块", 30),
("assistant", "正在分析认证模块... 发现使用 JWT + OAuth2.0", 60),
("tool", "result: 认证模块有 3 个漏洞需要修复", 50),
("assistant", "错误:读取数据库配置时权限不足", 40),
("assistant", "决定:使用管理员权限重试", 30),
("tool", "result: 成功获取配置,发现 2 个待修复的安全问题", 50),
("assistant", "待办:1. 修复 SQL 注入漏洞 2. 更新 JWT 过期策略", 60),
("user", "继续分析前端模块", 30),
# ... 更多对话触发压缩
]
for role, content, tokens in conversations:
agent.add_message(role, content, tokens)
print(f" + {role}: {content[:40]}... ({tokens} tokens)")
print(f"\n最终状态: {agent.stats()}")💡 一句话理解
Compaction 的关键是压缩质量——用太弱的模型压缩会丢失关键信息,用太强的模型压缩会浪费成本。建议用 7B-13B 的模型做压缩。
⚠️ 常见踩坑
多次 Compaction 后信息会逐层退化——就像复印件的复印件。如果你的任务需要 > 5 次压缩,考虑切换到 Sub-Agents 架构。
三、Sub-Agents 架构:分而治之
Sub-Agents 的核心思想是:不要在一个窗口里塞所有东西,而是 spawn 多个子代理,每个子代理有独立的上下文窗口。
这是分布式系统经典的「分而治之」策略在 Agent 领域的应用。
工作流程
- 主 Agent(Orchestrator)接收任务
- 主 Agent 分析任务,拆解为子任务
- 为每个子任务 spawn 一个 Sub-Agent
- 每个 Sub-Agent 独立执行,有自己的上下文窗口
- Sub-Agent 完成后,将结果返回给主 Agent
- 主 Agent 整合所有结果,生成最终输出
优势
- 无信息损失:每个子代理有完整的上下文窗口,不需要压缩
- 真正并行:多个子代理可以同时执行,大幅缩短总时间
- 模型异构:不同子代理可以使用不同的模型(代码任务用代码模型,推理任务用推理模型)
- 容错隔离:一个子代理失败不影响其他子代理
劣势
- 架构复杂:需要管理多个上下文窗口、进程间通信、状态同步
- 状态不一致风险:子代理之间无法直接共享上下文,可能出现信息冲突
- 调试困难:多个执行流交错,日志和追踪复杂度高
- 成本更高:每个子代理都需要加载系统提示和工具定义,有固定开销
Anthropic 的实现
Anthropic 在 Claude Code 中实现了完整的 Sub-Agent 编排系统。主 Agent 可以 spawn 多个子代理,每个子代理有独立的工具集和上下文。子代理完成后,结果通过「view」机制返回给主 Agent。
2026 年的 Claude Code 支持两种子代理模式:
- Task Sub-Agent:执行一次性任务,完成后销毁
- Persistent Sub-Agent:长期运行,可以接收后续指令
"""
Sub-Agents 架构:分而治之的 Agent 编排
模拟 Anthropic Claude Code 风格的子代理系统
"""
import asyncio
from dataclasses import dataclass
from typing import List, Dict, Optional
from enum import Enum
class SubAgentType(Enum):
TASK = "task" # 一次性任务
PERSISTENT = "persistent" # 长期运行
@dataclass
class SubAgentResult:
agent_id: str
task: str
result: str
tokens_used: int
latency_ms: float
success: bool
@dataclass
class SubAgent:
id: str
type: SubAgentType
task: str
model: str
status: str = "pending" # pending | running | completed | failed
result: Optional[str] = None
tokens_used: int = 0
class SubAgentOrchestrator:
"""
Sub-Agents 编排器
核心机制:spawn 多个子代理并行执行,整合结果
"""
def __init__(self, max_concurrent: int = 5):
self.max_concurrent = max_concurrent
self.agents: List[SubAgent] = []
self.completed_results: List[SubAgentResult] = []
async def spawn(self, task: str, model: str = "claude-3.5-sonnet",
agent_type: SubAgentType = SubAgentType.TASK) -> str:
"""Spawn 一个子代理"""
agent_id = f"agent-{len(self.agents) + 1:03d}"
agent = SubAgent(
id=agent_id,
type=agent_type,
task=task,
model=model,
)
self.agents.append(agent)
print(f"[Spawn] {agent_id}: {task[:50]}... (model: {model})")
return agent_id
async def execute_parallel(self, tasks: List[Dict]) -> List[SubAgentResult]:
"""
并行执行多个子任务
tasks: [{"task": "...", "model": "..."}, ...]
"""
# Spawn 所有子代理
agent_ids = []
for t in tasks:
aid = await self.spawn(
task=t["task"],
model=t.get("model", "claude-3.5-sonnet"),
)
agent_ids.append(aid)
# 并行执行(受并发限制)
semaphore = asyncio.Semaphore(self.max_concurrent)
async def run_agent(agent_id: str):
async with semaphore:
agent = next(a for a in self.agents if a.id == agent_id)
agent.status = "running"
# 模拟执行
await asyncio.sleep(0.1) # 模拟延迟
agent.result = f"[{agent.model}] 完成: {agent.task[:30]}..."
agent.tokens_used = 500 + len(agent.task) * 2
agent.status = "completed"
result = SubAgentResult(
agent_id=agent.id,
task=agent.task,
result=agent.result,
tokens_used=agent.tokens_used,
latency_ms=100 + len(agent.task) * 0.5,
success=True,
)
self.completed_results.append(result)
return result
# 等待所有完成
results = await asyncio.gather(*[run_agent(aid) for aid in agent_ids])
return list(results)
def synthesize(self, results: List[SubAgentResult]) -> str:
"""整合所有子代理的结果"""
summary_parts = ["# 综合结果\n"]
for r in results:
status = "✅" if r.success else "❌"
summary_parts.append(f"## {status} {r.agent_id}: {r.task[:40]}...")
summary_parts.append(f" 结果: {r.result}")
summary_parts.append(f" Token: {r.tokens_used} | 延迟: {r.latency_ms:.0f}ms\n")
total_tokens = sum(r.tokens_used for r in results)
total_time = max(r.latency_ms for r in results) # 并行,取最长
summary_parts.append(f"---\n总计: {total_tokens} tokens | 总时间: {total_time:.0f}ms (并行)")
return "\n".join(summary_parts)
def stats(self) -> Dict:
total_tokens = sum(r.tokens_used for r in self.completed_results)
return {
"total_agents": len(self.agents),
"completed": len(self.completed_results),
"failed": len([r for r in self.completed_results if not r.success]),
"total_tokens": total_tokens,
"avg_latency_ms": sum(r.latency_ms for r in self.completed_results) / max(len(self.completed_results), 1),
}
# 演示
async def main():
orchestrator = SubAgentOrchestrator(max_concurrent=3)
# 拆解任务
tasks = [
{"task": "分析后端 API 架构,找出所有 REST 端点", "model": "claude-3.5-sonnet"},
{"task": "审查前端 React 组件,检查性能问题", "model": "claude-3.5-sonnet"},
{"task": "扫描安全漏洞:SQL 注入、XSS、CSRF", "model": "claude-3-opus"},
{"task": "生成 API 文档(OpenAPI 格式)", "model": "claude-3.5-haiku"},
{"task": "优化数据库查询,找出慢查询", "model": "claude-3.5-sonnet"},
]
print("=== Sub-Agents 并行执行 ===\n")
results = await orchestrator.execute_parallel(tasks)
print("\n=== 整合结果 ===\n")
synthesis = orchestrator.synthesize(results)
print(synthesis)
print(f"\n=== 统计: {orchestrator.stats()} ===")
asyncio.run(main())💡 一句话理解
Sub-Agents 的关键是任务拆解——拆得太粗,子代理负担过重;拆得太细,通信开销超过执行开销。建议每个子任务执行时间 > 5 秒。
⚠️ 常见踩坑
Sub-Agents 之间的状态共享是最大陷阱——不要假设子代理 A 的发现会自动被子代理 B 知道。主 Agent 必须显式传递共享上下文。
四、正面对决:Compaction vs Sub-Agents 的 6 个维度
让我们从 6 个关键维度进行正面对比:
4.1 信息保真度
Compaction:有损压缩,信息会随压缩次数增加而退化。实测在 3 次压缩后,关键信息保留率约 70-80%。
Sub-Agents:无损——每个子代理有完整的上下文窗口。但主 Agent 整合时可能丢失子代理的细微发现。
胜者:Sub-Agents(但优势没有想象的大,因为主 Agent 整合也会丢信息)
4.2 延迟
Compaction:压缩过程需要 1-3 秒,但只发生一次。之后继续单线程执行。
Sub-Agents:spawn 和通信有开销(500ms-1s),但多个子任务并行执行,总时间通常更短。
胜者:取决于任务结构——串行任务 Compaction 更快,可并行任务 Sub-Agents 更快
4.3 成本
Compaction:压缩用小模型(成本极低),主流程用大模型。总额外成本 < 5%。
Sub-Agents:每个子代理都需要加载系统提示和工具定义(固定开销),多个子代理 = 多份固定开销。总额外成本 20-50%。
胜者:Compaction(成本优势明显)
4.4 调试复杂度
Compaction:单线程,日志线性,容易追踪问题。
Sub-Agents:多线程交错,需要分布式追踪系统(如 OpenTelemetry)。
胜者:Compaction(显著优势)
4.5 容错性
Compaction:单点故障——如果主流程崩溃,所有状态丢失。
Sub-Agents:隔离故障——一个子代理失败不影响其他子代理,可以重试。
胜者:Sub-Agents(显著优势)
4.6 适用场景
Compaction 更适合:
- 长对话式任务(客服、咨询)
- 需要严格状态一致性的任务
- 成本敏感的场景
- 调试和可观测性要求高的场景
Sub-Agents 更适合:
- 可并行的大规模任务(代码审查、安全扫描)
- 需要异构模型的任务
- 容错性要求高的场景
- 长时间运行的后台任务
五、混合架构:Compaction + Sub-Agents 的最优解
2026 年 6 月的最新趋势是:不再二选一,而是两者结合。
Claude Code 的架构实际上已经是混合模式:
- 主 Agent 使用 Compaction:管理用户对话的长期上下文
- 子代理独立执行:每个子代理有自己的上下文窗口,不需要压缩
- 子代理内部也可以用 Compaction:如果子代理的任务也很长
这种混合架构的设计原则:
原则 1:外层 Sub-Agents,内层 Compaction
主 Agent 负责拆解任务和整合结果(Sub-Agents 模式),每个子代理内部使用 Compaction 管理自己的长对话。
原则 2:关键路径用 Sub-Agents,辅助路径用 Compaction
对于不能丢失信息的关键任务(如安全审计),使用独立的子代理。对于可以容忍信息损失的辅助任务(如日志分析),使用 Compaction。
原则 3:短期任务用 Compaction,长期任务用 Sub-Agents
如果一个任务预计在 10 分钟内完成,用 Compaction 足够。如果需要运行数小时甚至数天,用 Sub-Agents 避免信息退化。
实际效果
2026 年 5 月,Anthropic 内部测试数据显示:混合架构在 SWE-bench(软件工程基准)上的得分比纯 Compaction 高 23%,比纯 Sub-Agents 高 11%,同时成本仅比纯 Compaction 高 15%。
"""
混合架构:Compaction + Sub-Agents
外层 Sub-Agents 并行,内层 Compaction 管理上下文
"""
import asyncio
from dataclasses import dataclass
from typing import List, Dict
@dataclass
class SubAgentConfig:
task: str
model: str
max_context_tokens: int = 32000
compaction_threshold: float = 0.8
class HybridAgent:
"""
混合架构 Agent
- 主 Agent:任务拆解 + 结果整合(Sub-Agents 模式)
- 子代理:独立执行 + 内部 Compaction
"""
def __init__(self):
self.results: List[Dict] = []
async def execute(self, task: str) -> str:
"""执行复杂任务"""
# Step 1: 拆解任务
subtasks = self._decompose(task)
# Step 2: 为每个子任务 spawn 子代理(Sub-Agents 模式)
configs = [
SubAgentConfig(task=t, model=self._select_model(t))
for t in subtasks
]
# Step 3: 并行执行(每个子代理内部用 Compaction)
results = await asyncio.gather(*[
self._run_subagent_with_compaction(cfg) for cfg in configs
])
# Step 4: 整合结果
return self._synthesize(task, results)
def _decompose(self, task: str) -> List[str]:
"""将复杂任务拆解为子任务"""
# 实际实现中,这里应该用 LLM 进行智能拆解
return [
f"子任务 1: 分析 {task} 的结构",
f"子任务 2: 评估 {task} 的风险",
f"子任务 3: 生成 {task} 的优化方案",
]
def _select_model(self, subtask: str) -> str:
"""根据子任务特征选择模型"""
if "代码" in subtask or "code" in subtask.lower():
return "deepseek-coder-32b"
elif "安全" in subtask or "风险" in subtask:
return "claude-opus-4.7"
else:
return "qwen-32b"
async def _run_subagent_with_compaction(self, config: SubAgentConfig) -> Dict:
"""
运行一个带 Compaction 的子代理
子代理有独立的上下文窗口,当上下文快满时自动压缩
"""
context_tokens = 0
messages = []
compactions = 0
# 模拟子代理执行多步骤任务
for step in range(15): # 15 步,会触发压缩
# 模拟执行
msg_tokens = 2000 + step * 500
messages.append({"step": step, "tokens": msg_tokens})
context_tokens += msg_tokens
# 检查是否需要压缩
if context_tokens > config.max_context_tokens * config.compaction_threshold:
# 压缩:保留关键信息,丢弃细节
context_tokens = int(context_tokens * 0.3) # 压缩到 30%
compactions += 1
return {
"task": config.task,
"model": config.model,
"total_tokens": context_tokens,
"compactions": compactions,
"success": True,
"result": f"完成: {config.task[:30]}...",
}
def _synthesize(self, original_task: str, results: List[Dict]) -> str:
"""整合所有子代理的结果"""
parts = [f"# 任务完成: {original_task[:50]}...\n"]
for r in results:
parts.append(f"- ✅ {r['task'][:40]}... (model: {r['model']}, compactions: {r['compactions']})")
parts.append(f"\n总计: {len(results)} 个子代理完成")
return "\n".join(parts)
# 演示
async def main():
agent = HybridAgent()
result = await agent.execute("全面审查项目代码质量、安全性和性能")
print(result)
asyncio.run(main())⚠️ 常见踩坑
不要为了用混合架构而用——如果你的任务简单且短,单 Agent + Compaction 就够了。架构复杂度应该匹配任务复杂度。
六、2026 下半年展望:上下文管理的未来
上下文管理正在成为 Agent 基础设施的核心战场。 2026 年下半年,几个趋势值得关注:
1. 无限上下文窗口?别指望
Google 的 Gemini 2.5 Pro 已经有 2M token 的上下文窗口,但「无限上下文」在工程上仍然不现实——注意力机制的计算复杂度是 O(n²),即使有 Flash Attention 优化,超过 1M token 的上下文仍然会导致显著的性能下降。
结论:上下文窗口会继续增大,但不会无限增长。上下文管理(Compaction + Sub-Agents)仍然是必需的。
2. 硬件级上下文加速
NVIDIA 在 2026 年 GTC 上发布的 Blackwell Ultra 架构包含了专门的「上下文处理单元」(Context Processing Unit, CPU),可以将长上下文的处理速度提升 10 倍。这意味着 Compaction 的延迟将从 1-3 秒降低到 100-300ms,几乎无感。
3. 标准化 Agent 上下文协议
MCP(Model Context Protocol)正在扩展「上下文管理」相关的规范——包括如何在 Agent 之间传递上下文、如何序列化/反序列化压缩后的上下文、如何处理上下文冲突。预计 2026 Q4 发布 v1.0。
4. 上下文即服务(Context-as-a-Service)
一个新的 SaaS 品类正在出现:专门管理 Agent 上下文的云服务。你不需要自己实现 Compaction 或 Sub-Agent 编排——调用 API 就行。
代表产品:
- Mem0 Cloud:Agent 记忆管理
- Letta (前 MemGPT):虚拟内存管理
- Weaviate Engram:向量记忆服务
最终结论:Compaction vs Sub-Agents 不是非此即彼的选择——2026 年的最佳实践是混合架构,根据任务特征动态切换。而更长远的未来,上下文管理将从「开发者自建」走向「托管服务」,就像数据库从自建走向云一样。
💡 一句话理解
如果你的团队正在构建 Agent 系统,现在就开始抽象上下文管理层——把它作为一个独立模块,未来可以方便地替换为托管服务。
⚠️ 常见踩坑
不要过早优化上下文管理——先验证你的 Agent 核心逻辑是否有效,再优化上下文策略。很多团队在上下文管理上花了太多时间,但 Agent 的核心价值根本不在这。
七、总结:你的选型决策树
Compaction 和 Sub-Agents 不是竞争对手,而是互补的两种工具。
核心决策逻辑:
任务可并行吗?
- 是 → 优先考虑 Sub-Agents
- 否 → 继续下一步
任务需要长对话吗?
- 是 → Compaction 足够
- 否 → 继续下一步
成本敏感吗?
- 是 → Compaction(成本优势明显)
- 否 → 继续下一步
容错性要求高吗?
- 是 → Sub-Agents(故障隔离)
- 否 → 按团队熟悉度选择
需要异构模型吗?
- 是 → Sub-Agents(每个子代理用不同模型)
- 否 → Compaction 足够
终极建议:从 Compaction 开始(简单、便宜、好调试),当遇到瓶颈时再引入 Sub-Agents。不要过度设计。