共计 1588 个字符,预计需要花费 4 分钟才能阅读完成。
1. 为什么我们需要关注 token 消耗?
最近在开发 LLM 应用时,我发现 token 消耗是个隐形杀手。尤其是在 agent 场景下,一次对话可能轻松消耗上千 token。以 GPT-4-32k 为例,按官方定价每 1000 个 token 约 0.06 美元计算,一个包含 10 轮对话流程的 agent 每天处理 1000 次请求,月成本就高达 1800 美元——其中 30%-50% 其实是可以通过优化节省的。

典型的 token 浪费场景包括:
- 冗长的系统消息重复传输
- 包含无用信息的对话历史
- 自由格式响应中的冗余描述
- 未压缩的上下文数据
2. 结构化提示词设计三板斧
2.1 系统消息瘦身术
传统系统消息喜欢事无巨细地描述角色设定,比如:
你是一个专业、友善的客服助手,需要遵守以下规则:1. 始终用中文回答...(200+ 字)
优化方案:
- 使用「角色标签」替代长描述
[角色 = 客服助手][语言 = 中文][风格 = 专业且友善] - 将固定规则移入 Few-shot 示例
- 动态加载非核心约束条件
2.2 响应模板标准化
自由格式响应不仅难解析,还浪费 token。推荐使用 JSON Schema 约束输出结构:
from langchain.prompts import PromptTemplate
response_template = PromptTemplate.from_template("""
请按以下格式响应:```json
{{
"action": "对话类型",
"content": "核心内容",
"need_human": true/false
}}
```""")
2.3 对话历史压缩
通过两种方式精简上下文:
- 自动摘要:每 3 轮对话生成一次摘要
- 关键信息提取:使用 NER 识别实体保留
3. 实战代码示例
from langchain.llms import OpenAI
from transformers import GPT2Tokenizer
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
def count_tokens(text):
return len(tokenizer.encode(text))
# 优化后的系统消息
optimized_system_msg = """
[角色 = 旅行规划助手][输出格式 =JSON][记忆窗口 = 3 轮]
```json
{{"query":"用户问题", "response":{{"plan":[], "tips":""}}}}
```"""print(f" 优化前 token 数: {count_tokens(old_system_msg)}")
print(f"优化后 token 数: {count_tokens(optimized_system_msg)}")
4. 效果对比数据
| 优化项 | 原始 token 数 | 优化后 token 数 | 降幅 |
|---|---|---|---|
| 系统消息 | 217 | 58 | 73% |
| 单次响应 | 189±25 | 92±12 | 51% |
| 10 轮对话上下文 | 2843 | 1276 | 55% |
5. 新手避坑指南
5.1 不要过度压缩
曾有个失败案例:把用户请求摘要成「订酒店」,导致模型遗漏了「带婴儿」的关键需求。建议保留:
- 数量信息(人数、天数)
- 绝对否定词(不要、禁止)
- 特殊需求(无障碍、过敏源)
5.2 模型差异注意
- GPT-3.5 对格式指令更敏感
- Claude 系列适合长消息但价格高
- 本地模型可能需要调整分隔符
5.3 流式响应处理
当使用 streaming 时:
- 提前约定数据分块标记
- 避免在中间状态做完整性校验
- 设置超时熔断机制
6. 还能怎么优化?
三个进阶方向供探索:
- 动态上下文窗口:根据对话阶段调整历史长度
- 语义缓存:对相似 query 复用历史响应
- 模型蒸馏:训练小模型处理简单请求
写在最后
经过这些优化,我们的客服 agent 月成本从 $1800 降到了 $900 左右。最关键的是培养了对 token 的敏感度——就像程序员要关注时间复杂度一样,LLM 开发者需要建立「token 复杂度」意识。下次设计 prompt 时,不妨先问自己:这段话真的需要这么多 token 吗?
正文完
