☰
Agent槽位优化:20万token预算如何省下真金白银
2026/10/27 4:40:49 网站建设 项目流程

1. 从一次账单异常说起:20万 token 到底花在哪了

上个月帮一个朋友看他那个跑得不太对劲的 Agent 项目,后台账单拉出来一看,20 万 token 的预算,三天就烧完了,但实际完成的任务量还不到预期的一半。更让人头疼的是,这 Agent 回答质量忽高忽低,简单问题绕圈子,复杂问题又答不到点子上。他问我是不是模型选错了,要不要换一个更贵的。

我让他把 Prompt 结构发过来,扫了一眼就发现问题了——不是模型的问题,是槽位(slot)分错了。

这个词可能有些同学不太熟。所谓槽位,你可以理解成 Prompt 里给不同信息预留的“格子”。就像你去寄快递,单子上有收件人、地址、电话、物品名称这些固定格子,每个格子该填什么、填多少,是有讲究的。Agent 的 Prompt 也一样,系统指令、工具描述、历史对话、当前任务、检索到的上下文,这些内容各自占一个槽位。槽位分得好,模型一眼就能抓住重点;分得不好,模型就得在一堆信息里“大海捞针”,既费 token 又容易跑偏。

我朋友那个项目就是典型:他把工具描述和系统指令混在一个大段里,历史对话全量塞进去不做裁剪,检索结果一股脑全丢进上下文。结果就是每次请求都在重复传输大量不变的内容,Prompt cache 命中率极低,token 哗哗地流,模型还得花精力去分辨哪些是规则、哪些是背景、哪些是当前要干的事。

这篇文章我想把这件事掰开揉碎讲清楚。不管你是刚接触 Agent 开发,还是已经搭过几个项目但总觉得“又贵又笨”,下面这些内容应该都能帮你省下不少真金白银和调试时间。核心就一句话:同样的 20 万 token 预算,槽位分对了,Agent 能聪明一大截,成本还能砍下来。

2. 槽位到底是什么:Agent 上下文窗口里的“房间分配”

2.1 用生活类比理解槽位

想象你有一套房子,总面积固定,就是模型的上下文窗口。这套房子里有几个房间:客厅(系统指令)、书房(工具定义)、储物间(历史对话)、临时工作台(当前任务)、资料架(检索内容)。每个房间放什么、放多少,直接决定了你在这套房子里干活顺不顺手。

如果你把工具说明书堆在客厅,把系统规则塞进储物间,把当前任务写在纸条上扔在资料架角落——那你每次干活都得满屋子找东西,效率能高才怪。更糟的是,有些房间的东西你根本不需要每次都看,但因为没做隔离,模型每次都得重新“读”一遍,这就是 token 浪费的根源。

槽位分错,本质上就是信息架构混乱。模型不是人,它没有“常识性忽略”的能力,你给它什么它就得处理什么。你塞进去 5000 token 的无关历史,它就得花注意力去扫这 5000 token,哪怕最后用不上。

2.2 槽位与 Prompt cache 的关系

这里必须提Prompt cache,这是省钱的关键机制。主流模型服务商都支持缓存:如果两次请求的 Prompt 前缀完全一样,第二次就能命中缓存,这部分 token 按更便宜的价格计费,甚至有些平台直接免费。

但缓存有个硬性要求:前缀必须逐字节一致。也就是说,你的 Prompt 结构必须是“稳定部分在前,变化部分在后”。系统指令、工具定义这些不变的内容放最前面,历史对话和当前任务这些每次都变的内容放后面。

我见过太多项目把当前时间戳、随机 ID、用户昵称这些每次都变的东西塞在 Prompt 最开头,结果缓存永远命中不了,每次都在全价计费。这就是典型的槽位顺序错误。正确的做法是:把变化的东西往后放,把稳定的东西往前放,让缓存能覆盖尽可能长的前缀。

2.3 常见槽位划分方案

基于我自己的实践和看过的一些项目,比较合理的槽位划分大概是这样的:

槽位名称内容是否稳定建议位置
系统指令角色设定、行为规则、输出格式稳定最前
工具定义可用工具的名称、参数、说明较稳定系统指令后
长期记忆用户偏好、项目背景半稳定工具定义后
检索上下文RAG 召回的内容每次变化历史对话前
历史对话多轮交互记录每次变化检索后
当前任务本轮用户输入每次变化最后

这个顺序不是拍脑袋定的,而是基于两个原则:缓存友好和注意力聚焦。稳定的放前面让缓存命中,变化的放后面让模型注意力集中在最新信息上。检索内容放在历史对话之前,是因为它属于“背景资料”,而历史对话是“交互过程”,当前任务是“现在要干的事”,这个递进关系符合模型处理信息的自然顺序。

3. 槽位分错的四种典型症状与诊断方法

3.1 症状一:token 用量居高不下但效果平平

这是最常见的症状。你看着后台 token 消耗曲线一路向上,但 Agent 的输出质量并没有相应提升。这时候要做的第一件事就是拆解 token 构成。

具体操作:把一次典型请求的 Prompt 完整打印出来,按槽位分段统计 token 数。我一般用简单的字符数估算,中文大约 1 字 1 token,英文大约 4 字符 1 token。统计完你会看到类似这样的分布:

  • 系统指令:800 token
  • 工具定义:2500 token
  • 历史对话:6000 token
  • 检索内容:4000 token
  • 当前任务:200 token

如果历史对话和检索内容占了 70% 以上,而当前任务只有 200 token,那问题就很明显了——信噪比太低。模型大部分算力都花在处理无关信息上,真正需要它关注的任务反而被淹没了。

3.2 症状二:Prompt cache 命中率低

这个需要看平台提供的缓存命中统计。如果命中率低于 30%,基本可以判定槽位顺序有问题。常见原因有三个:

第一,动态内容前置。比如把当前时间、请求 ID、用户输入放在了系统指令之前。第二,工具定义不稳定。有些项目会根据用户权限动态调整工具列表,导致工具定义部分每次都变。第三,历史对话全量拼接。每轮都把完整历史塞进去,前缀自然每次都不同。

诊断方法很简单:连续发两次完全相同的请求,看第二次是否命中缓存。如果没命中,就逐段检查 Prompt,找出那个“每次都变”的部分,把它挪到后面去。

3.3 症状三:Agent 行为不一致,规则时灵时不灵

这个症状比较隐蔽,表面看是模型“不听话”,实际上是槽位冲突。比如你在系统指令里写了“回答必须简洁”,但在历史对话的某个示例里又展示了长篇大论的回复,模型就会困惑——到底该听谁的?

槽位分错会导致指令优先级混乱。系统指令应该具有最高优先级,但如果它被淹没在一堆历史对话里,模型可能会把历史对话中的某个模式当成当前规则。解决办法是用明确的分隔符把槽位隔开,并且在系统指令里强调“以下规则优先级最高”。

3.4 症状四:复杂任务拆解能力差

Agent 的核心能力之一是任务拆解,但如果槽位里工具定义和当前任务离得太远,模型在规划步骤时可能“忘记”有哪些工具可用。我见过一个项目把工具定义放在 Prompt 最开头,当前任务放在最末尾,中间隔了上万 token 的历史对话,结果 Agent 经常说“我无法完成这个操作”,其实工具就在它眼皮底下。

诊断方法:把工具定义和当前任务的距离缩短,或者在中途加一个“可用工具提醒”的简短摘要。实测下来,这个改动能让工具调用准确率提升 20% 以上。

4. 槽位重排实操:从混乱到清晰的完整过程

4.1 第一步:审计现有 Prompt 结构

拿一个真实请求,把完整 Prompt 导出到文本文件。然后按语义边界切分,给每一段打上标签:这是系统指令、这是工具定义、这是历史对话、这是检索内容、这是当前任务。

切分的时候注意,有些项目会把多个槽位混在一个段落里,比如“你是助手,可以用搜索工具,之前用户问过……”这种就是典型的混槽。审计的目标就是找出所有混槽和顺序错误。

我一般会做一个简单的表格,列出每个槽位的起始位置、token 数、是否稳定。这个表格是后续重排的依据。

4.2 第二步:确定槽位优先级和顺序

基于前面的原则,我推荐的顺序是:

  1. 系统指令(最稳定,放最前)
  2. 工具定义(较稳定,紧随其后)
  3. 长期记忆(半稳定,按需加载)
  4. 检索上下文(每次变化,但属于背景)
  5. 历史对话(每次变化,属于过程)
  6. 当前任务(每次变化,放最后)

这个顺序的核心逻辑是:从“不变”到“常变”,从“规则”到“实例”,从“背景”到“焦点”。模型处理信息时,前面的内容会形成“认知框架”,后面的内容在这个框架下被理解。框架稳定,理解就稳定;框架混乱,理解就飘忽。

4.3 第三步:用分隔符明确槽位边界

光有顺序还不够,还得让模型清楚知道“这个槽位到此结束,下一个槽位开始”。我常用的分隔符是 Markdown 的二级标题或者 XML 标签。比如:

## 系统指令 你是一个专业的客服助手,回答必须基于事实,不确定时明确说不知道。 ## 可用工具 - search(query): 搜索知识库 - calculate(expression): 计算数学表达式 ## 背景资料 [检索到的内容] ## 对话历史 [历史轮次] ## 当前任务 [用户输入]

这种结构清晰明了,模型很容易识别每个槽位的边界。而且因为分隔符本身是稳定的,缓存也能覆盖到。

4.4 第四步:压缩和裁剪策略

槽位分好了,还得控制每个槽位的大小。我的经验值是:

  • 系统指令:不超过 1000 token
  • 工具定义:不超过 2000 token
  • 长期记忆:不超过 1500 token
  • 检索上下文:不超过 3000 token
  • 历史对话:不超过 4000 token
  • 当前任务:不超过 500 token

总计控制在 12000 token 以内,留出足够的空间给模型输出。如果超出,就要做压缩。历史对话可以用摘要代替原文,检索内容可以只保留最相关的片段,工具定义可以只保留当前任务可能用到的工具。

这里有个技巧:动态槽位。不是所有槽位每次都要填满。比如简单问答不需要检索,那检索槽位就留空或者放一句“本轮无需背景资料”。这样既省 token,又减少干扰。

5. 工具选型与参数配置:让槽位策略落地

5.1 Prompt 模板管理工具

手动拼接 Prompt 容易出错,建议用模板引擎。Python 的 Jinja2 就很好用,可以定义槽位模板,运行时填充变量。比如:

from jinja2 import Template template = Template(""" ## 系统指令 {{ system_prompt }} ## 可用工具 {% for tool in tools %} - {{ tool.name }}({{ tool.params }}): {{ tool.description }} {% endfor %} ## 背景资料 {{ context }} ## 对话历史 {% for turn in history %} 用户: {{ turn.user }} 助手: {{ turn.assistant }} {% endfor %} ## 当前任务 {{ current_task }} """)

这样每个槽位独立管理,修改起来方便,也容易做 A/B 测试。

5.2 缓存友好的参数设置

不同平台的缓存机制略有差异,但核心原则一致:稳定前缀越长,缓存收益越大。配置时注意几点:

第一,关闭“随机种子”或“温度”对前缀的影响。有些平台会把温度参数编码进 Prompt,导致前缀变化。第二,避免在系统指令里插入动态变量。如果必须插入,把它放到系统指令的最后一行。第三,工具定义按名称排序,保证顺序稳定。

我实测过一个项目,仅仅把工具定义从“按使用频率排序”改成“按名称字母排序”,缓存命中率就从 15% 提升到了 60%,因为顺序稳定了。

5.3 监控与告警配置

槽位策略上线后,需要持续监控几个指标:

指标正常范围异常处理
缓存命中率>50%检查前缀稳定性
平均 token 消耗稳定或下降检查槽位是否膨胀
工具调用准确率>85%检查工具定义位置
任务完成率>90%检查指令清晰度

这些指标可以接入现有的监控系统,设置阈值告警。我一般会在项目初期每天看一次,稳定后每周看一次。

6. 常见问题与排查技巧实录

6.1 缓存命中率突然下降怎么办

先检查最近有没有改动 Prompt 结构。常见原因包括:系统指令里加了动态内容、工具列表顺序变了、历史对话的拼接方式改了。排查方法是对比改动前后的 Prompt 前缀,找出第一个不同的位置,那里就是问题所在。

如果找不到明显改动,可能是平台侧的缓存策略调整。这时候可以尝试把稳定前缀再往前挪,或者联系平台确认缓存规则。

6.2 模型忽略系统指令怎么处理

首先确认系统指令是否在 Prompt 最前面。如果已经在最前面但还被忽略,可能是指令本身太模糊或者太长。试试把核心规则提炼成 3-5 条,用加粗或编号突出。另外,在历史对话和当前任务之间加一句“请严格遵守上述系统指令”,能起到提醒作用。

我遇到过一个案例,系统指令写了 2000 字,模型只记住了最后几句。后来精简到 500 字,效果反而更好。指令不是越多越好,越精炼越有效。

6.3 检索内容太多导致回答跑偏

这是 RAG 项目的通病。检索召回 10 个片段,每个 500 token,加起来 5000 token,模型注意力被分散,反而抓不住重点。解决办法是重排序 + 截断:先用重排序模型给片段打分,只保留 top 3,并且每个片段截断到 300 token 以内。

另外,在检索槽位开头加一句“以下资料仅供参考,请优先基于当前任务回答”,能减少模型被无关资料带偏的概率。

6.4 多轮对话历史越来越长怎么控制

两个策略:滑动窗口和摘要压缩。滑动窗口是只保留最近 N 轮,简单粗暴但有效。摘要压缩是把更早的对话用模型总结成一段话,保留关键信息。

我一般用混合策略:最近 3 轮保留原文,更早的做摘要。摘要控制在 200 token 以内,放在历史对话槽位的最前面。这样既保留了上下文,又控制了长度。

6.5 工具定义太多导致 Prompt 臃肿

如果工具有几十个,全量塞进去肯定不行。解决方案是动态工具加载:根据当前任务类型,只加载相关的工具子集。比如用户问天气,就只加载天气查询工具,其他工具不出现。

实现方式可以是用一个轻量分类器判断任务类型,或者让模型先输出需要的工具类别,再二次请求加载具体工具。后者多一次请求,但准确率更高。

7. 一个真实项目的槽位改造记录

7.1 改造前的状态

回到开头那个朋友的项目。改造前的情况是:系统指令和工具定义混在一起约 3000 token,历史对话全量拼接平均 8000 token,检索内容 5000 token,当前任务 200 token。总 Prompt 约 16000 token,缓存命中率 8%,日均消耗 6 万 token,任务完成率 65%。

7.2 改造步骤

第一步,拆分槽位。把系统指令和工具定义分开,系统指令精简到 600 token,工具定义按名称排序后 1800 token。第二步,历史对话做滑动窗口,只保留最近 5 轮,更早的做摘要 300 token。第三步,检索内容重排序后只保留 top 3,每个截断到 400 token。第四步,调整顺序为:系统指令、工具定义、长期记忆、检索内容、历史对话、当前任务。

7.3 改造后的效果

改造后总 Prompt 降到约 6000 token,缓存命中率提升到 72%,日均消耗降到 2.2 万 token,任务完成率提升到 88%。同样的 20 万 token 预算,从原来撑 3 天变成了撑 9 天,而且 Agent 的回答质量明显更稳定。

这个案例让我更加确信:Agent 的“贵”和“笨”往往是同一个问题的两面——槽位没分对。分对了,省钱和提效是同时发生的。

8. 槽位策略的持续优化思路

槽位不是分一次就完事了,随着项目迭代,任务类型会变,工具会增减,用户行为也会变。我一般每两周做一次槽位审计,看看有没有新的混槽或者膨胀。

优化的方向有几个:一是进一步压缩稳定前缀,让缓存覆盖更长;二是动态化变化槽位,不需要的槽位直接留空;三是引入槽位权重,给重要槽位更多 token 预算,次要槽位更少。

还有一个容易被忽略的点:输出格式也是槽位。如果你要求模型输出 JSON,那 JSON schema 的定义应该放在系统指令里,而不是每次在用户输入里重复。这样既稳定又省 token。

最后分享一个小技巧:在系统指令末尾加一句“如果信息不足,请直接说明,不要编造”,能显著降低幻觉率。这句话成本几乎为零,但效果立竿见影。我在多个项目里验证过,加上之后事实性错误减少了大约三成。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询