今天的 CSDN 文章:AI Agent 不是越“能聊”越好:真正落地时,第一道坎叫 Token 限制
一、先说结论:Token 限制不是“省钱”,而是 Agent 工程化的生死线
很多初学者理解 Token 限制,第一反应是:
“是不是为了少花点调用大模型的钱?”
是,但不只是。
在真实的 AI 应用里,限制 Token 至少有四个核心原因:
- 控制成本
- 避免超过模型上下文窗口
- 提高响应速度
- 降低模型被无关信息干扰的概率
尤其是做 Agent 的时候,Token 会爆炸得非常快。
比如一次智能运维 Agent 的调用流程可能是:
用户问题
+ 系统提示词 System Prompt
+ 多轮历史对话
+ RAG 检索出来的知识片段
+ Planner 生成的计划
+ Executor 调用工具的结果
+ Replanner 的重新规划内容
+ 最终回答要求
如果你不限制 Token,最后传给大模型的内容可能非常臃肿。
表面上看是“信息更多了”,实际上可能是:
上下文太长 → 模型抓不住重点 → 回答变慢 → 成本变高 → 甚至直接报错
所以,Token 管理本质上不是一个“小优化”,而是 AI Agent 从 Demo 走向工程化必须解决的问题。
二、Token 到底是什么?别只理解成“字数”
Token 可以简单理解成模型处理文本时的最小单位。
它不完全等于字,也不完全等于词。
比如中文里,一个汉字可能接近一个 Token,也可能多个字组成一个 Token;英文里,一个单词可能被拆成多个 Token。
举个简单例子:
我正在学习 Spring AI
这句话在人眼看来是几个词,但模型内部可能会拆成类似:
我 / 正在 / 学习 / Spring / AI
而下面这句话:
MessageChatMemoryAdvisor
虽然只是一个类名,但可能会被拆成多个 Token:
Message / Chat / Memory / Advisor
所以在 AI 项目中,不能简单用“字符数”判断 Token 数量。
更重要的是,Token 不只计算用户输入,还包括:
System Prompt
用户问题
历史对话
RAG 检索内容
工具调用结果
模型输出内容
也就是说,模型的一次调用,其实是一次完整上下文的 Token 消耗。
三、为什么做 RAG 之后 Token 特别容易爆?
RAG 的核心流程是:
用户问题 → 检索知识库 → 拼接相关文档 → 交给大模型生成回答
问题在于,很多人刚开始写 RAG 时,会有一个误区:
检索出来的内容越多,回答越准确。
这句话只对一半。
如果检索内容质量高,确实能提升回答准确性。
但如果你把一堆相关性一般、重复、噪声很大的 chunk 都塞进去,模型不一定更聪明,反而可能更迷糊。
比如用户问:
Spring AI 中 ChatMemory 的作用是什么?
你检索出了 10 个 chunk:
chunk1:ChatMemory 基础介绍
chunk2:MessageChatMemoryAdvisor 代码
chunk3:RedisChatMemoryRepository 配置
chunk4:Prompt 封装流程
chunk5:ChatClient Builder 使用
chunk6:MCP 工具注册
chunk7:RAG 检索流程
chunk8:SSE 流式响应
chunk9:模型参数配置
chunk10:Docker Compose 启动 Milvus
看起来都和 Spring AI 有点关系,但真正对问题有用的可能只有前 3 个。
如果全塞进去,就会出现:
有用信息少
无关信息多
Token 消耗大
模型注意力被稀释
这就是 RAG 中非常典型的 Token 浪费。
所以 RAG 不是“检索越多越好”,而是:
召回要够多,送给模型要够精。
也就是:
先多路召回 → 再过滤/重排 → 最后只拼接最有价值的上下文
四、Agent 场景下,Token 爆炸更严重
如果说 RAG 的 Token 爆炸来自“文档内容太多”,那 Agent 的 Token 爆炸就更复杂。
因为 Agent 不只是问答,它还会:
思考
规划
调用工具
读取工具结果
重新规划
继续执行
总结输出
比如一个运维 Agent 处理问题:
用户:为什么今天订单服务响应变慢了?
Agent 可能执行:
1. 分析用户问题
2. 判断需要查询监控指标
3. 调用 Prometheus 查询接口
4. 分析 CPU、内存、QPS、RT
5. 调用日志查询工具
6. 读取错误日志
7. 判断是否需要查询数据库慢 SQL
8. 调用数据库工具
9. 生成诊断报告
每一步都会产生上下文。
如果每一步都把完整历史带上,最后上下文可能变成这样:
系统提示词
+ 用户原始问题
+ 第一次思考
+ 第一次工具调用参数
+ 第一次工具返回结果
+ 第二次思考
+ 第二次工具调用参数
+ 第二次工具返回结果
+ 第三次思考
+ 第三次工具调用参数
+ 第三次工具返回结果
+ ...
这时候 Token 消耗会非常夸张。
更危险的是,工具返回结果通常很长。
比如日志工具返回 200 行日志,里面可能只有 5 行有用,但你如果全塞给模型,就会导致:
Token 暴涨
响应变慢
成本升高
关键异常被淹没
所以 Agent 工程里有一句很重要的话:
工具返回结果不能直接丢给模型,必须先做压缩、筛选和结构化。
五、Token 限制的核心思想:给上下文做“预算管理”
我们可以把一次模型调用看成一场考试。
模型的上下文窗口就像一张答题纸,空间有限。
你不能把所有资料都抄上去,而是要挑最有价值的内容。
一次请求的 Token 预算可以拆成:
总 Token 预算
= System Prompt 预算
+ 历史对话预算
+ RAG 知识预算
+ 工具结果预算
+ 用户问题预算
+ 模型输出预算
比如模型最大上下文是 32K Token,你不应该全部拿来塞输入。
因为还要给模型留输出空间。
可以设计成:
总上下文窗口:32000 tokens
System Prompt:2000
历史对话:6000
RAG 知识:12000
工具结果:6000
用户问题:1000
模型输出预留:5000
这就是上下文预算。
不是哪里内容多就塞哪里,而是每一类内容都有额度。
一旦超过,就要裁剪、压缩、摘要或者丢弃。
六、最常见的 5 种 Token 限制策略
1. 限制历史对话窗口
多轮对话中,历史消息不能无限保留。
最简单的方式是滑动窗口:
只保留最近 N 轮对话
比如只保留最近 10 条消息:
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.maxMessages(10)
.build();
这种方式简单直接,适合普通聊天场景。
但它的问题是:
早期重要信息可能被丢掉
比如用户一开始说:
我这个项目用的是 Spring AI Alibaba + Milvus
后面聊了很多轮,这条信息可能被挤掉。
所以只用滑动窗口还不够。
更好的做法是:
短期记忆:保留最近对话
长期记忆:把重要信息摘要后存储
2. 历史摘要压缩
当历史对话太长时,可以让模型先生成一个摘要。
原始历史可能是:
用户前面聊了 RAG、Milvus、Spring AI、ChatMemory、MCP、ToolCallback、SSE 等内容。
压缩后变成:
用户正在学习 Spring AI Alibaba,项目包含 RAG、ChatMemory、MCP 工具调用和 SSE 流式响应,目前重点关注 Agent 工程化落地。
这样就能把几千 Token 压缩成几百 Token。
工程上可以设计成:
最近 10 条消息:原文保留
更早的历史:摘要保存
最终拼接上下文:
System Prompt
+ 长期摘要
+ 最近消息窗口
+ 当前用户问题
这比单纯保留全部历史更合理。
3. 限制 RAG 检索内容数量
RAG 中常见参数是 TopK。
比如:
SearchRequest searchRequest = SearchRequest.builder()
.query(userQuestion)
.topK(5)
.build();
这里的 topK=5 就是限制最多返回 5 个知识片段。
但 TopK 不是越大越好。
如果 chunk 太大,TopK=10 可能已经非常长。
比如:
每个 chunk 800 tokens
TopK = 10
总计 = 8000 tokens
如果加上历史对话、系统提示词、工具结果,很容易超过预算。
所以 TopK 要结合 chunk 大小一起看。
一个比较稳的策略是:
chunkSize:500~800 tokens
TopK:3~5
必要时加 Rerank
也就是说,先多召回,再精挑细选,不要一股脑塞给模型。
4. 对工具返回结果做裁剪
Agent 中工具返回结果尤其要控制。
错误示范:
String logs = logTool.queryLogs(serviceName);
prompt.append(logs);
这等于把所有日志直接塞给模型。
更好的方式是先过滤:
List<String> errorLines = logs.stream()
.filter(line -> line.contains("ERROR") || line.contains("Exception"))
.limit(20)
.toList();
然后再结构化:
日志摘要:
1. 订单服务在 14:03 出现大量 TimeoutException
2. 错误集中在 createOrder 接口
3. 同时数据库连接池出现等待超时
4. 最近 10 分钟错误量从 5/min 上升到 230/min
模型真正需要的不是 500 行日志,而是有价值的异常摘要。
所以工具调用结果一定要经过:
过滤 → 聚合 → 摘要 → 再进入上下文
5. 限制模型输出长度
除了输入 Token,输出 Token 也要限制。
如果不限制输出,模型可能啰嗦很久。
在模型参数中通常可以设置:
maxTokens
maxOutputTokens
比如伪代码:
ChatOptions options = ChatOptions.builder()
.maxTokens(1000)
.temperature(0.7)
.build();
对于不同任务,可以设置不同输出长度。
比如:
普通问答:800 tokens
代码解释:1500 tokens
报告生成:3000 tokens
标题生成:200 tokens
不要所有场景都用同一个 maxTokens。
否则简单问题浪费 Token,复杂问题又不够用。
七、Token 限制不是简单截断,乱截会出问题
很多人一听限制 Token,就想到:
超过长度就 substring 截断
这在 AI 项目里是很危险的。
比如原文是:
数据库连接池耗尽的原因不是 MySQL 宕机,而是接口调用链中存在大量慢查询,导致连接长时间无法释放。
如果你粗暴截断成:
数据库连接池耗尽的原因不是 MySQL 宕机,而是……
模型可能误解,甚至产生完全相反的判断。
尤其是代码、日志、配置文件,不能随便从中间截。
错误示例:
spring:
ai:
dashscope:
api-key:
如果截断在这里,模型就不知道下面还有什么配置。
所以 Token 裁剪要遵循几个原则:
优先按语义块裁剪
优先保留标题、结论、异常、参数
不要从代码块中间截断
不要从 JSON/YAML 中间截断
不要截断关键因果关系
更好的方式是:
按段落切
按 chunk 切
按日志事件切
按函数/类切
按配置块切
八、RAG 中的 Chunk 设计,本质上也是 Token 管理
很多人以为 chunkSize 只是切片参数,其实它和 Token 控制关系非常大。
如果 chunk 太小:
优点:检索更精确
缺点:上下文不完整,模型可能看不懂
如果 chunk 太大:
优点:上下文完整
缺点:Token 消耗大,召回噪声多
比如一篇技术文档:
什么是 ChatMemory
MessageChatMemoryAdvisor 的作用
Redis 持久化记忆
SessionId 隔离
如果 chunk 太小,可能切成:
MessageChatMemoryAdvisor 会自动从 ChatMemory 中取出历史消息。
这句话单独看能理解,但缺少上下文。
如果 chunk 太大,可能把 ChatMemory、RAG、MCP 全放进一个 chunk,检索时容易带入噪声。
比较合理的做法是:
chunkSize:500~800 tokens
overlap:50~100 tokens
overlap 的作用是防止上下文被切断。
比如:
上一段结尾:MessageChatMemoryAdvisor 会在调用模型前……
下一段开头:把历史消息拼接到 Prompt 中。
如果没有 overlap,这个语义就断了。
所以 chunk 设计不是随便切文本,而是在做上下文预算规划。
九、Agent 项目里可以怎么设计 Token 管理模块?
如果项目复杂一点,不建议把 Token 限制逻辑散落在各个 Service 里。
可以抽象一个上下文管理器:
ContextManager
它负责:
1. 接收用户问题
2. 加载历史对话
3. 加载长期摘要
4. 加载 RAG 检索内容
5. 加载工具执行结果
6. 统一计算预算
7. 生成最终 Prompt
结构可以类似:
public class ContextManager {
private final int maxInputTokens = 24000;
private final int reservedOutputTokens = 4000;
public Prompt buildPrompt(UserQuery query,
List<Message> recentMessages,
String memorySummary,
List<Document> retrievedDocs,
List<ToolResult> toolResults) {
// 1. 添加系统提示词
// 2. 添加长期摘要
// 3. 添加最近对话
// 4. 添加 RAG 文档
// 5. 添加工具结果摘要
// 6. 超过预算时裁剪低优先级内容
return new Prompt(/* messages */);
}
}
这里有一个关键点:
不同上下文内容应该有优先级。
比如:
最高优先级:当前用户问题
高优先级:系统提示词、关键约束
中优先级:最近对话、RAG 高分文档
低优先级:较早历史、低相关文档、冗余工具结果
当 Token 超预算时,不是随机删,而是按照优先级删。
可以设计成:
1. 永远保留当前问题
2. 永远保留系统提示词核心规则
3. 优先删除低相关 RAG 文档
4. 再压缩工具结果
5. 再裁剪历史对话
6. 最后才压缩长期摘要
这才是工程化的 Token 管理。
十、一个更真实的 Prompt 拼接示例
错误写法:
你是一个智能助手。
历史对话:
{全部历史}
知识库:
{全部检索结果}
工具结果:
{完整日志}
用户问题:
{question}
这种写法最大的问题是没有预算,没有优先级,没有结构。
更好的写法:
你是一个面向 Java 后端与 AI Agent 项目的技术助手。
回答时优先基于给定上下文,不要编造不存在的信息。
【用户长期背景摘要】
{memory_summary}
【最近对话】
{recent_messages}
【相关知识片段】
只包含和当前问题最相关的 Top 5 文档:
{retrieved_docs}
【工具结果摘要】
只保留关键异常、指标变化和结论:
{tool_result_summary}
【当前问题】
{question}
【回答要求】
1. 先给结论
2. 再分层解释
3. 结合 Java/Spring AI 项目说明
4. 不要输出无关背景
这个 Prompt 看起来更长,但其实更省 Token。
因为它减少了废话和噪声,让模型更容易抓住重点。
十一、Token 限制和“模型幻觉”也有关系
很多人以为幻觉只和模型能力有关。
其实上下文设计不好,也会增加幻觉。
比如你给模型塞了太多无关内容:
ChatMemory
MCP
Docker Compose
Milvus
Redis
SSE
ToolCallback
用户只是问:
ChatMemory 和 RedisChatMemoryRepository 有什么关系?
但上下文里混入太多东西,模型可能会把 MCP、RAG、Redis、Session 全混在一起讲,最后回答变得很散。
这不是模型完全不会,而是你给它的信息太乱。
所以 Token 限制还有一个隐藏价值:
通过减少无关上下文,降低模型胡乱联想的空间。
RAG 里尤其明显。
高质量上下文应该是:
少而准
结构清晰
和问题强相关
带有明确来源
而不是:
多而杂
内容重复
相关性模糊
没有优先级
十二、不同场景下 Token 限制应该怎么配?
普通聊天场景
重点是历史对话控制。
推荐:
最近 10~20 条消息
长期摘要
输出限制 800~1500 tokens
适合:
学习助手
客服问答
个人助理
RAG 知识库问答
重点是控制检索内容。
推荐:
chunkSize:500~800 tokens
overlap:50~100 tokens
TopK:3~5
必要时 Rerank
输出限制:1000~2000 tokens
适合:
文档问答
企业知识库
项目资料查询
Agent 工具调用场景
重点是工具结果压缩。
推荐:
工具原始结果不直接进 Prompt
先做过滤、聚合、摘要
每个工具结果设置最大 Token
保留关键字段和结论
适合:
AIOps
数据分析 Agent
自动化运维 Agent
多工具协作 Agent
报告生成场景
重点是输出预算。
推荐:
输入内容先结构化
输出 maxTokens 适当放大
分段生成
必要时采用 map-reduce 总结
适合:
日报
诊断报告
技术总结
CSDN 草稿生成
十三、Token 管理的一个实战套路:分层压缩
对于复杂 Agent,不要指望一次模型调用解决所有问题。
可以采用分层压缩:
第一层:原始数据
第二层:局部摘要
第三层:全局摘要
第四层:最终回答
比如日志分析:
原始日志 10000 行
↓
按服务聚合异常
↓
每个服务生成摘要
↓
汇总成故障诊断上下文
↓
交给模型生成最终报告
这样做的好处是:
避免一次性塞爆上下文
减少无用 Token
让模型逐层聚焦
这其实也是很多 Agent Framework 背后的思想。
不是所有信息都直接进入最终 Prompt,而是先被处理成更适合模型理解的结构。
十四、在 Spring AI 项目中可以怎么落地?
在 Spring AI / Spring AI Alibaba 项目中,可以从几个地方入手。
1. ChatMemory 控制历史窗口
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.maxMessages(20)
.build();
这一步解决的是:
历史对话不能无限增长
2. RAG 检索控制 TopK
SearchRequest searchRequest = SearchRequest.builder()
.query(question)
.topK(5)
.build();
这一步解决的是:
不能把所有知识片段都塞给模型
3. 工具结果先摘要再返回
比如日志工具不要直接返回完整日志:
public ToolResult queryErrorLogs(String serviceName) {
List<String> logs = logService.query(serviceName);
List<String> errors = logs.stream()
.filter(line -> line.contains("ERROR") || line.contains("Exception"))
.limit(30)
.toList();
return ToolResult.builder()
.summary("发现 30 条关键异常日志,主要集中在订单创建接口。")
.details(errors)
.build();
}
这一步解决的是:
工具结果太长、太乱
4. Prompt 模板中明确上下文边界
【长期摘要】
{summary}
【最近对话】
{recentMessages}
【知识库内容】
{documents}
【工具结果摘要】
{toolSummary}
【当前问题】
{question}
这一步解决的是:
上下文结构混乱
5. 按任务类型设置不同输出长度
比如:
public ChatOptions buildOptions(TaskType taskType) {
return switch (taskType) {
case SIMPLE_QA -> ChatOptions.builder()
.maxTokens(800)
.temperature(0.5)
.build();
case CODE_EXPLAIN -> ChatOptions.builder()
.maxTokens(1800)
.temperature(0.4)
.build();
case REPORT -> ChatOptions.builder()
.maxTokens(3000)
.temperature(0.6)
.build();
};
}
这一步解决的是:
不同任务共用同一个 Token 配置
十五、Token 限制最容易踩的坑
坑 1:只限制输出,不限制输入
很多人只设置 maxTokens,以为就安全了。
但实际上,输入上下文一样可能超限。
输入太长 → 请求失败
输出太短 → 回答残缺
输入和输出都要控制。
坑 2:RAG TopK 设置过大
TopK 太大不一定更准。
尤其是 chunk 本身很长时,TopK=10 可能已经非常夸张。
RAG 要追求:
相关性,而不是数量。
坑 3:工具返回原始数据
日志、数据库查询结果、监控指标,不能原样全部进入 Prompt。
应该先变成:
异常摘要
趋势变化
关键字段
可能原因
坑 4:历史对话无限堆叠
多轮对话如果不做窗口和摘要,迟早会爆。
正确方式是:
最近消息窗口 + 长期摘要
坑 5:用字符数代替 Token 数
字符数只能粗略估算。
尤其是代码、英文类名、JSON、YAML、日志,很容易和真实 Token 数差距很大。
十六、我现在对 Token 限制的理解
之前我对 Token 的理解可能还停留在:
Token = 大模型计费单位
但现在结合 RAG、Agent、ChatMemory、工具调用之后,会发现 Token 更像是:
大模型的上下文资源
它决定了模型一次能看到多少信息。
而 Agent 工程化真正难的地方,不是简单调用一次模型,而是如何决定:
哪些信息该给模型
哪些信息不该给模型
哪些信息要原文保留
哪些信息要摘要压缩
哪些信息要完全丢弃
所以 Token 限制背后,其实是上下文工程。
一个好的 AI 应用,不是把所有东西都塞给模型,而是把最有价值的信息,以最清晰的结构,放到模型最容易理解的位置。
这才是 Token 管理真正的意义。
十七、最后总结
Token 限制不是一个简单的参数配置,而是一套完整的工程设计。
在 RAG 中,它决定知识片段怎么切、怎么召回、怎么拼接。
在 ChatMemory 中,它决定历史对话怎么保留、怎么摘要、怎么压缩。
在 Agent 中,它决定工具结果怎么过滤、怎么总结、怎么参与下一步推理。
如果不做 Token 管理,AI 应用很容易变成:
能跑 Demo
但慢、贵、不稳定、不准确
如果做好 Token 管理,系统才有可能变成:
上下文清晰
成本可控
响应稳定
回答更准
工程上可维护
所以限制 Token 不是限制模型能力,而是在帮模型聚焦。
真正成熟的 Agent,不是“什么都记住”,而是“知道什么时候该忘,什么时候该保留,什么时候该压缩”。
这也是 AI Agent 从玩具 Demo 走向真实项目时,必须补上的一课。
更多推荐


所有评论(0)