跑了30天AI Agent自动化,我遇到了3种Token爆炸的场景(附解决方案)
我的 AI Agent 第一次"失忆",是运行到第 47 轮对话的时候。
当时 Agent 正在帮我处理一个持续了 2 小时的自动化任务——分析日志、修改配置、重启服务、再验证。前面一切正常。突然,它的输出开始变得莫名其妙:明明刚改过的文件路径,它又开始从头搜索;5 分钟前确认过的端口号,它又问了一遍。
我查了半天才发现:不是模型坏了,是上下文窗口满了。前面 46 轮的工具调用结果、系统提示、对话历史全塞在一起,新的工具输出根本塞不进去,模型只能看着被截断的历史"猜"。
如果你也在跑 AI Agent 自动化——不管是 Claude Code、OpenCode、还是自己搭的 LangChain Agent——这篇文章里的 3 种 Token 爆炸场景,你迟早会遇到。我先替你踩了一遍。
场景一:对话历史堆积——Agent 的"阿尔茨海默症"
怎么发生的
AI Agent 的核心工作模式是"思考→行动→观察→再思考"的循环。每一次循环,都会产生一段新内容:
- Assistant 的思考过程(think/reasoning)
- 工具调用的参数
- 工具返回的结果
问题在于,工具返回的结果经常远超你的预期。
举个例子:你的 Agent 执行了一次 grep 搜索,命令行返回了 800 行的匹配结果。Agent 看着这 800 行,从中挑出了 3 个关键文件路径——你只关心这 3 个路径。但那 800 行已经全部写入了上下文窗口,永久占据了 Token 配额。
这就是"对话历史堆积":Agent 跑得越久,上下文里无用的历史就越厚。30 轮之后,可能 70% 的 Token 都被早已过时的搜索结果和工具输出占用。
真实案例
我让 Agent 帮我排查一次部署失败。
它做了这些事:读日志文件(1200 行)→ grep 搜索错误关键词(600 行匹配)→ 读 Dockerfile(80 行)→ 读 docker-compose.yml(150 行)→ 检查端口占用(50 行)→ 读环境变量配置(40 行)→ 修改配置文件 → 重新构建。
到第 25 轮时,上下文中已经堆积了超过 2000 行的历史信息。而当 Agent 终于找到问题(端口冲突),它却已经"忘记"了最开始读到的那个报错信息,因为它被截断了。
最终这个本该 10 分钟解决的问题,Agent 绕了 35 分钟才搞定——其中有 20 分钟是在"重新发现"它曾经知道的信息。
解决方案:对话压缩 + 分级保留
核心思路很简单:不要把所有历史都留在上下文里,只保留"决策依据"。
# 伪代码:Agent 每轮的上下文管理策略
class ContextManager:
def __init__(self, max_tokens=80000):
self.max_tokens = max_tokens
self.critical_messages = [] # 始终保留
self.recent_turns = [] # 最近 N 轮
self.summaries = [] # 更早历史的摘要
def after_turn(self, messages, token_count):
if token_count < self.max_tokens * 0.7:
return # 还够用,不做处理
# 把最近 5 轮之前的对话压缩为摘要
old_turns = self.recent_turns[:–5]
if old_turns:
summary = self.summarize(old_turns)
self.summaries.append(summary)
self.recent_turns = self.recent_turns[–5:]
实际工程中,压缩可以用 LLM 做语义提炼:
“前 20 轮对话中,Agent 完成了以下关键操作:1) 确认了端口 8080 被占用;2) 将服务迁移到 8081;3) 修改了 3 个配置文件。关键结论:端口冲突已解决,服务正常运行。”
这一段可能只有 100 个 Token,但它保留了 20 轮对话的核心信息。压缩比通常能做到 10:1 到 20:1。
实践中我踩过的坑:不要用固定轮数触发压缩。有些任务 5 轮就解决了,压缩反而增加延迟。用 Token 使用率(比如超 70% 时触发)比用轮数靠谱得多。
场景二:大工具输出直接塞上下文——“一页纸把桌子压垮”
怎么发生的
这是最隐蔽也最致命的一种 Token 浪费。
你的 Agent 调用了一个 read_file 工具读了一个 2000 行的 Python 文件。这 2000 行全部出现在上下文里。然后 Agent 说:“我看了,只需要改第 847 行的端口号。”
2000 行输入,就为了定位一行。
再比如,Agent 用 browser_navigate 打开一个网页。页面的完整 DOM snapshot 可能有 5000 行——包括导航栏、侧边栏、footer 里所有无关元素的文本。Agent 只需要页面中间那一段表单内容。但 5000 行全进来了。
真实案例
我让 Agent 帮我对比两个 GitHub 仓库的 API 差异。它做了这些:
两页加起来 12000 行的 DOM snapshot,可能只有 200 行是 API 端点定义。10500+ 行是导航栏、广告、推荐阅读、评论区链接——对任务完全无用的噪声。
更糟的是,这些噪声不仅消耗 Token,还稀释了模型的注意力。当上下文中充斥着无关的 DOM 元素文本时,模型更容易"看走眼",从噪声里提取错误信息。
解决方案:工具端摘要 + 后处理管道
最好的策略是在工具输出进入上下文之前就做截断和摘要——而不是等上下文满了再事后补救。
# 工具输出的"双通道"模式
@tool
def read_file(path, mode="auto"):
content = open(path).read()
lines = content.split('\\n')
total_lines = len(lines)
if mode == "auto" and total_lines > 200:
# 返回摘要而非全文
return {
"summary": f"文件 {path},共 {total_lines} 行",
"head": lines[:30], # 前 30 行(import 等)
"tail": lines[–30:], # 尾 30 行
"hint": "使用 read_file(path, mode='full') 获取完整内容"
}
return {"content": content}
对于浏览器类工具,关键是不要直接把 DOM snapshot 给模型。做一层解析:
原始输出(6200 行 DOM) → 提取可交互元素 + 正文区域 → 压缩后输出(~300 行)
实践中我学到的最重要一课:工具的设计决定了 Agent 的 Token 效率。写工具的时候多花 10 分钟做输出过滤,比 Agent 跑崩了再排查省 2 小时。
场景三:系统提示 + 工具定义膨胀——“还没开始跑就已经输了”
怎么发生的
每个 AI Agent 都有一个 system prompt(系统提示),包含:
- Agent 的角色定义
- 行为规范
- 可用工具列表(每个工具都有名称、描述、参数 schema)
- 输出格式要求
- Memory / 上下文(如果注入了用户偏好等)
如果你的 Agent 有 15 个工具,每个工具的 JSON schema 平均 300 字符,仅工具定义就占 4500 字符——大约 1500 Token。
加上角色定义(2000 Token)、行为规范(1500 Token)、注入的 memory(1000 Token),你的 system prompt 可能已经占了 6000 Token。
而每轮对话中,这部分内容会被重复发送——除非你用的模型支持 prompt caching。
真实案例
我最初搭的一个 Agent 有 22 个工具。system prompt 光工具定义就占了 8000 Token。加上 Agent 的"persona"定义、长篇行为规范、几条 rules——system prompt 总共 12000 Token。
模型上下文窗口是 128K Token。听起来很大对吧?但除去 system prompt 的 12000,加上几轮工具调用产生的输出,跑到第 30 轮左右,上下文就接近 70% 了。Agent 开始"忘记"最早的指令。
更扎心的是:那 22 个工具里,平均每轮对话只用到了 4-5 个。另外 17 个工具的定义,每一轮都在白白消耗 Token。
解决方案:工具按需加载 + 分拆 Agent
方案 A:动态工具注册
不让所有工具同时在 system prompt 里躺着。根据任务类型,只加载相关的:
TOOLS_BY_DOMAIN = {
"code": ["read_file", "write_file", "search_code", "run_test"],
"deploy": ["docker_build", "docker_push", "k8s_apply"],
"web": ["browser_navigate", "browser_click", "web_search"],
}
def build_system_prompt(task_domain):
tools = TOOLS_BY_DOMAIN.get(task_domain, ALL_TOOLS)
return render_prompt(tools=tools)
这样 system prompt 从 12000 Token 可以降到 4000 Token。
方案 B:多 Agent 拆分
如果一个任务确实需要很多工具,把它拆成多个专用子 Agent。每个子 Agent 只带自己需要的工具:
- Planner Agent(5 个工具):负责拆解任务、分配子任务
- Coder Agent(6 个工具):负责读写文件、运行测试
- Deployment Agent(4 个工具):负责构建和部署
每个子 Agent 启动时,上下文是干净的、精简的。这比一个"超级 Agent"带 15+ 个工具的方案,Token 效率高 3-5 倍。
选型建议:三种场景怎么配方案
| 对话历史堆积 | 语义压缩 + 热缓存 | 10:1 ~ 20:1 | 中 |
| 大工具输出 | 工具端双通道输出 | 20:1 ~ 50:1 | 低 |
| 系统提示膨胀 | 工具按需加载 + Agent 拆分 | 3:1 ~ 5:1 | 中 |
如果你只能做一件事:先优化工具的返回格式。这是成本最低、收益最大的改动。一个 read_file 工具只返回文件摘要而非全文,能节省的 Token 可能比你费劲调 prompt 省下的多 10 倍。
如果你在搭一个长期运行的 Agent 系统,一定要有 Token 使用率监控。当单次会话的 Token 消耗超过窗口的 60% 时触发压缩——这是我跑了一个月后总结出的最佳实践。低于 60% 没必要压缩,高于 80% 可能来不及了。
你跑 AI Agent 的时候遇到过上下文溢出吗?用的什么方案解决的?留言聊聊,我还在持续优化这套系统。
📌 作者:Aliaoo 🚀 专注 AI 工具实战、云部署、自动化脚本。每篇都是亲测可跑的教程。

🖥️ 需要云服务器跑项目? 👉 CSDN 开发云常年折扣,新用户首单特惠
📬 觉得有用就点个赞,想追更就点个关注——下次搜到我不靠缘分。




