欢迎光临
我们一直在努力

跑了30天AI Agent自动化,我遇到了3种Token爆炸的场景(附解决方案)

跑了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 差异。它做了这些:

  • 搜索到 repo A 的 API 文档页 → DOM snapshot 6200 行
  • 搜索到 repo B 的 API 文档页 → DOM snapshot 5800 行
  • 分别提取关键信息
  • 两页加起来 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开发云

    🖥️ 需要云服务器跑项目? 👉 CSDN 开发云常年折扣,新用户首单特惠

    📬 觉得有用就点个赞,想追更就点个关注——下次搜到我不靠缘分。

    赞(0)
    未经允许不得转载:171主机测评 » 跑了30天AI Agent自动化,我遇到了3种Token爆炸的场景(附解决方案)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址