如果你亲手搭过一个能调 API、查数据库、发邮件的 Agent,你一定经历过这种“失忆”瞬间:前五轮还在好好聊,到第六轮模型突然忘了用户最初的诉求,一本正经地胡编。你第一反应是:“这个模型也太拉了。”但你打开 API 的 token 统计,会发现一个扎心的事实——每一轮请求里,System Prompt 和工具描述加起来吃掉的 token,比所有对话历史还多。你的 Agent 不是智商不够,而是它的“工作台”被杂物占满了。

这篇文章想说的,就是这个隐形问题:一个中等复杂度的 Agent,上下文窗口里有多少 token 是真正用在“干活”上的?我和身边做过 Agent 的朋友摸了一遍底,得到一个经验结论:System Prompt 大约占 20%,工具描述大约占 30%,真正留给执行和推理的只有 50%。这不是任何机构的官方测算,但它能解释你为什么总觉得 Agent“记性差”。
System Prompt 为什么吃掉 20%?因为它是一本每次进厂都要重读的员工手册
System Prompt 是模型对你这个 Agent 的全部“认识起点”。你要告诉它你是谁、要干嘛、按什么步骤来、不能做什么、输出是什么格式。一个稍微复杂的企业级 Agent,这些规则写满一两千 token 是家常便饭。Anthropic 的System Prompt 文档甚至建议把 system prompt 当代码一样反复精简,因为你每加一句规则,模型每轮都要多读一次。
注意,这里有个容易忽略的点:模型没有“记住”你的 system prompt,它只是每次都在输入里重新“看见”它。所以 System Prompt 写得再长,也只是一次性的引导成本,却成为每一轮的持续成本。这就像每做一个小任务,都要把《员工手册》从头读一遍——你也许觉得没什么,但模型的“阅读载荷”就那么大。
更微妙的是,System Prompt 里往往还有很多“防御性规则”。为了不让模型胡说,你写“如果不知道答案就说不知道”;为了输出稳定,你写“必须按 JSON 格式返回”。每一条都似乎必要,但彼此叠加,20% 就到了。
工具描述为什么吃掉 30?因为工具是模型的“可行动作”,但动作说明太肥了
在 Function Calling 的架构里,模型要“知道”它能调用哪些工具,才知道该选哪个。这不是魔法,它靠的是把每个工具的名字、用途、参数 schema 全部序列化成 token 塞进请求。我们来看一个最普通的天气查询工具:
{"name": "get_weather","description": "获取指定城市的当前天气信息","parameters": {"type": "object","properties": {"city": {"type": "string", "description": "城市名称"},"units": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位"}},"required": ["city", "units"]}}
这一个工具就占掉几十 token。如果你的 Agent 有 20 个工具,那就是一坨 1000~2000 token 的“工具说明书”。Anthropic 的 Tool Use 文档里专门有最佳实践,其中一个核心建议就是:把工具描述写短,让模型快速扫描,而不是精读。因为模型每走一步都要把这些描述完整过一遍,工具越复杂,它“选工具”的决策负担越重。
更气人的是,这些工具描述和你的对话内容可能完全无关。比如用户只是问“现在几点”,Agent 却要把“发送邮件”、“创建工单”、“更新数据库”的工具定义全都看一遍。这种浪费在每一轮都会发生。
真正留给执行的 50%,才是你 Agent 的“大脑活动区”
剩下那 50%,要装下用户请求、对话历史、工具返回结果,以及模型自己生成过程所需要依赖的上下文。这听起来不少,但现实很骨感,因为工具返回结果经常是巨无霸。你让 Agent 查个 SQL,返回 200 行数据,一轮就能把 50% 塞满;再往后,Agent 就只能在“最近一轮结果”和“用户最初需求”之间二选一。
长上下文模型解决这个问题了吗?没有。把窗口从 8k 换成 128k 后,开发者只会往里面塞更多工具和更多历史,结果你还是用同样比例把它填满。更麻烦的是,随着上下文变长,模型的注意力会迅速稀释。有一篇很著名的论文,Lost in the Middle,用实验证明:模型对长上下文中间部分的信息利用率最低。你可以把它理解成:即使你给了 128k 的豪华“工作台”,模型真正看得清楚的只有两头,中间全是高脚杯。
我最早也天真地以为,换更长上下文就能解决一切。把 128k 的 Agent 搭好后,演示时模型突然把一个关键需求搞错了——用户第一轮明明说过,但那条消息被埋在第 80 轮位置,模型根本“没在看”它。那一刻我才明白:上下文窗口的关键不是容量,而是预算分配。
怎么把 50% 的执行空间抢回来?五个能落地的思路
这 50% 不是不能改变。我踩完坑以后,总结出几条有效的方法,按投入产出比排序:
- 给 System Prompt 做减脂。删掉“你是一个很有帮助的AI助手”这种废话,也别把流程规范全部写进去。只保留真正影响行为的硬规则,把动态内容(比如用户信息、当前任务)放到调用时再注入。
- 给工具描述做压缩。工具说明里只写“什么时候用它、哪些参数必须传”,删掉形容词和客套话。一个工具的 description 应低于 15 个词。
- 把工具分组、按需注入。别一次把 30 个工具全给模型。先用一个小 Agent 或规则引擎判断场景,再只发送当前场景需要的 5 个工具。这是立竿见影的空间回血术。
- 历史对话离线化。不再把每一轮对话原样塞进上下文,而是每过几轮生成摘要,再把摘要和最近几轮原文放回去。关键事实存进外部记忆或检索系统,要用的时候才查。
- 用 prompt caching 降本。如果你跑在支持缓存的模型服务上(比如 Claude 的 Prompt Caching),System Prompt 和工具描述可以不再重复计费。但要知道,缓存省了钱,并没有省下上下文里的物理空间,该精简还是要精简。
下面这张表是我拿一个中复杂度 Agent 的 token 消耗估算出来的,对比几种策略的效果(示例数据,不是基准测试):
| 方案 | System Prompt | 工具描述 | 执行空间 | 有效利用率 |
|---|---|---|---|---|
| 默认裸配 | 1600 token(20%) | 2400 token(30%) | 4000 token(50%) | 50% |
| 精简 Prompt | 800(10%) | 2000(25%) | 5200(65%) | 65% |
| 精简 + 工具分组 | 800(11%) | 600(8%) | 5800(81%) | 81% |
| 再叠加历史压缩 | 800(12%) | 600(9%) | 5200(79%) | 79%* |
*历史压缩后,部分关键信息被挪到外部存储,上下文里的执行空间让给了当轮推理,有效利用率略有回落,但实际“可用记忆”其实变长了。
你可能想问:如果我把工具都描述得很短,模型会不会选错工具?其实不会。工具选择的准确率更多依赖工具名和 description 的组合是否语义清晰,而不是 description 的长短。而 Anthropic 的 Contextual Retrieval 博客也提到,让模型在信息进入上下文之前先做预处理和压缩,往往比把一切堆进去效果更好。
Agent 的天花板不在模型,而在上下文管理
回到标题那个比例:20% / 30% / 50%。这个比例不是一个宿命,而是一种诊断指标。当你调试 Agent 时,不要只盯正确率,看一眼 token 账单。如果平均每轮请求有超过 40% 的 token 花在 System Prompt 和工具描述上,那你的 Agent 是在“戴着镣铐奔跑”。
我认为,近一两年内 Agent 的竞争点根本不是模型智商,而是上下文管理能力。模型厂商当然在努力通过更大窗口、缓存和推理压缩来缓解,但只要 Agent 继续使用“把全部规则和工具塞给模型”这种激进架构,预算分配问题就会一直在。未来的成熟 Agent 大概率会把上下文当作一块临时的寄存器:只放当轮最重要的信息,其余都交给外部记忆。
下一次当你抱怨 Agent 记性差、行为不听话时,可以先打开 token breakdown 看看。可能你会发现,问题不是它笨——是它一出生,就有一半的桌面上都放着别人的东西。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/1306.html