1. 为什么ClaudeAgent必须要做内存管理
先说一个很多人在构建Agent时的痛点:单轮对话模型能力再强,一旦进入“多轮工具调用+长日程执行”的真实业务场景,上下文窗口就变成了最卡脖子的资源。市面上讨论“C语言内存管理”“Linux内存管理”的帖子满天飞,但LLM应用的“内存”往往被忽略。这里的“内存”不是进程地址空间里的堆和栈,而是模型一次能看到的Token数量——你可以把Token窗口理解成Agent的工作记忆,它决定了Agent能记住多少历史、决策依据和工具返回结果。窗口不够用,轻则被迫截断历史导致失忆,重则直接报错、任务中断。
在做ClaudeAgent的内存管理时,“上下文压缩”是最实用、也最容易被低估的一环。很多人的第一反应是“把旧消息删掉不就完了”,但真正落地时你会发现:直接删可能导致关键信息丢失,不删又放不下,而粗暴拼接摘要会导致Agent在后续步骤里丢失细节。我自己的经验是,一套可靠的上下文压缩方案需要同时解决三个问题:什么时候触发压缩、压缩成什么形态、压缩后如何保证任务的连续性。
这篇文章我会用完整的思考和可运行的代码,带你把上下文压缩这一层真正落到实际Agent里。适合已经在用Claude API做Agent开发、但被长对话中的“上下文爆炸”困扰的开发者。即使你用的是其他大模型API,这套思路和代码结构也可以直接迁移。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 内存管理的设计拆解:不是“压缩”而是“取舍”
2.1 先认清上下文爆炸的三个来源
在做压缩之前,必须搞清楚Token被谁吃掉了。根据我在真实业务里的统计,Agent对话的Token消耗通常来自三块:
第一块是多轮历史消息。用户每说一句,Agent每回复一句,都会完整留存。如果Agent还要在循环里调用工具,每一次工具结果都会作为一条消息进入上下文,一轮推理就可能新增几千Token。
第二块是工具返回的原始数据。例如查询数据库返回100行记录,搜索接口返回大段JSON,这些内容占用的Token常常是历史对话的数十倍。这些数据往往只对当前这一步决策有用,下一步可能就不再需要。
第三块是Agent自身的思考和中间输出。在Claude的Agent循环里,模型会先作规划,再调用工具,再根据结果作下一步计划。如果把这些中间步骤全部塞进上下文,Alias会迅速膨胀。
理解了这三个来源,你就会明白:盲目保留“完整历史”其实是在浪费Token,而上下文压缩的本质,是对信息做分层——哪些要完整保留,哪些要提炼后保留,哪些直接丢弃。
2.2 压缩策略选型:摘要式、结构化、裁剪式
我在项目里试过三种压缩思路,分别适用于不同场景,这里直接给出选型结论。
摘要式压缩是最通用的一种。它把较旧的历史对话交给模型生成一条精炼摘要,然后将摘要替代原始消息。好处是保留了语义主干,坏处是细节会丢失。适合“目标明确、但过程较长”的Agent任务,比如多轮搜索、数据整理。
结构化压缩更适合流程型任务,例如“先查库存、再下单、最后通知”。这类任务的每一个步骤都有固定的状态和字段,如果全部塞入对话历史,浪费严重。正确做法是把历史信息提炼成结构化记录,例如“库存查询结果:SKU-1001剩余12件,SKU-1002已缺货;下单结果:订单号SO-2024-001已创建”。
裁剪式压缩就是硬删。只保留最近N轮消息和永久必要的系统级信息,更早的原始消息直接移除。它不产生摘要,逻辑最简单,代价是Agent会遗忘较早的约束条件。这里有一个很容易踩的坑:如果你把用户最初提出的硬性要求也一并裁剪掉,后续步骤就会“跑偏”。
2.3 为什么“无脑截断”不可行
很多人最开始想着:“既然窗口不够,我就只保留最后10轮消息,前面全删掉。”这个方案在简单Demo里可能能跑通,但放到真实业务里几乎必然翻车。
举个真实例子。我做过一个销售线索分析Agent,用户的初始指令是“只分析华南地区的客户,排除竞品相关的合同”。如果我简单截断早期消息,两轮之后Agent就会开始把华东客户也分析进去,因为它已经“忘记”了区域限制。这就是典型的上下文丢失。
所以我的结论是:压缩本身是一种“有损操作”,关键不是避免损失,而是有意识地决定损失哪些信息。你需要在触发压缩时,把用户约束、目标状态、决策依据等“不可丢”的信息显式保留下来,哪怕是以摘要的形式。
3. 核心实现:一个可落地的上下文压缩器
3.1 整体设计与消息结构约定
我在项目里维护了一个名为 Compactor 的类,它的核心职责是:持续跟踪当前对话窗口的Token占用,一旦超过阈值,就对旧消息执行压缩,并把压缩结果重新塞回消息列表里。
为了让这段代码能直接跑起来,我先把消息结构做一个简单约定:所有消息都是字典,格式与Claude API的Messages接口一致,包含 role 和 content 两个字段。例如:
python复制{"role": "user", "content": "查询Q1销售额"}
{"role": "assistant", "content": "我来查询数据库。"}
{"role": "tool_result", "content": "Q1销售额为1200万。"}
如果你用的是其他模型,只需将消息格式稍作映射即可。
3.2 Compactor类的骨架代码
先给出一个完整的类实现,后面的小节会对
