大模型调用,如何优化 Token 消耗?
·
1. 引言:为什么需要优化 Token 消耗?
在大模型(LLM)应用开发中,Token 是计算成本与 API 费用的核心计量单位。无论是 OpenAI GPT、Claude、文心一言还是通义千问,其定价通常基于输入与输出的 Token 数量。一次不经优化的调用可能消耗数千甚至数万 Token,导致:
- 成本飙升:高频调用下,费用可能远超预期。
- 响应延迟:过长的输入/输出会增加网络传输与模型计算时间。
- 上下文窗口受限:模型有最大上下文长度限制(如 128K),低效的 Token 使用会更快触及上限,影响长文档处理能力。
因此,掌握 Token 优化技巧是构建高效、经济大模型应用的关键。本文将系统介绍从提示工程、数据预处理到架构设计的全方位优化策略。
2. 理解 Token:计数规则与成本构成
2.1 什么是 Token?
Token 是模型处理文本的基本单位。在英文中,一个 Token 大约对应 0.75 个单词;在中文中,一个汉字通常为 1-2 个 Token。标点符号、空格也可能被计为 Token。
示例:
"Hello, world!"→ 可能被分词为["Hello", ",", " world", "!"],计为 4个 Token。"你好,世界!"→ 可能被分词为["你", "好", ",", "世", "界", "!"],计为 6个 Token。
2.2 成本公式
单次调用成本 ≈ (输入Token数 + 输出Token数) × 单价
优化需同时关注 输入(Prompt) 与 输出(Completion) 两部分。
3. 核心优化策略
3.1 提示工程优化(减少输入 Token)
这是最直接、最有效的优化手段。
| 策略 | 具体做法 | 预期效果 |
|---|---|---|
| 精简指令 | 删除冗余客套话、重复说明。使用清晰、简洁的指令。 | 减少 10%-30% 的 Prompt Token。 |
| 使用系统提示(System Prompt) | 将固定的角色设定、行为准则放在 system 消息中,它通常比 user 消息计价更低(取决于平台)。 | 优化成本结构。 |
| 结构化输入 | 使用 JSON、YAML、XML 等格式传递结构化数据,而非冗长的自然语言描述。 | 提高信息密度,减少解释性文字。 |
| 示例少而精(Few-Shot) | 提供 1-2 个最具代表性的示例,而非堆砌大量例子。 | 显著减少示例部分的 Token 消耗。 |
| 利用函数调用(Function Calling) | 让模型返回结构化数据(如 JSON)而非长文本,再由代码解析执行。 | 精准控制输出长度,避免模型“自由发挥”产生冗余。 |
优化前:
用户:你好,AI助手。我希望你能帮我总结一下下面这篇文章的核心内容。这篇文章可能有点长,请你耐心阅读。总结时请抓住重点,不要太啰嗦,但也要全面。文章内容是:...
优化后:
系统:你是一个专业的文本总结助手。
用户:总结以下文章:...
3.2 输出控制优化(减少输出 Token)
控制模型的“表达欲”,引导其产出精炼内容。
| 策略 | 具体做法 | 预期效果 |
|---|---|---|
设置 max_tokens 参数 | 明确限制生成文本的最大长度,避免生成无关紧要的结尾。 | 直接防止输出过长。 |
| 使用停止序列(Stop Sequences) | 设置如 “。”、“总结完毕”、“```” 等停止词,让模型在合适位置自然结束。 | 避免生成格式尾部的多余内容。 |
| 明确输出格式与长度 | 在指令中要求“用100字以内总结”、“以要点列表形式输出”、“返回JSON对象”。 | 引导模型进行自我约束。 |
| 流式传输(Streaming) | 虽然不减少总 Token 数,但可以边生成边处理,提升用户体验,并在达到所需信息后提前中断。 | 改善感知速度,允许提前终止。 |
3.3 数据预处理优化
在数据送入模型前进行“瘦身”。
| 策略 | 具体做法 | 适用场景 |
|---|---|---|
| 文本清洗与压缩 | 移除无关的广告、页眉页脚、多余的空格和换行符。 | 处理网页爬取内容、PDF 解析文本。 |
| 摘要或提取关键句 | 先用轻量级模型或算法(如 TextRank)对长文档进行预摘要,再将摘要送入大模型。 | 处理超长文档(远超上下文窗口)。 |
| 分块处理(Chunking) | 将长文档按语义切分成块,分别处理后再合并结果。需设计好块间的上下文传递。 | 文档问答、长文本分析。 |
| 嵌入检索(RAG) | 建立向量数据库,仅检索与问题最相关的文档片段送入上下文,而非整个文档库。 | 知识库问答、私有数据查询。 |
3.4 架构与缓存优化
从系统设计层面减少不必要的调用。
| 策略 | 具体做法 | 预期效果 |
|---|---|---|
| 实现对话状态管理 | 在多轮对话中,只将最新的用户消息和必要的上下文历史(而非全部历史)发送给模型。 | 避免对话历史无限增长导致 Token 爆炸。 |
| 缓存重复结果 | 对相同或相似的查询结果进行缓存(TTL缓存),直接返回缓存内容。 | 对常见、确定性高的查询大幅减少调用。 |
| 使用更经济的模型 | 非核心任务使用小型/廉价模型(如 GPT-3.5-Turbo vs GPT-4)。 | 直接降低单价。 |
| 批量处理(Batching) | 将多个独立请求合并为一个批量请求发送(如果 API 支持)。 | 减少网络开销,有时享有批量折扣。 |
4. 实战工具与代码示例
4.1 使用 tiktoken 库精确计算 Token(Python)
import tiktoken
def num_tokens_from_string(string: str, encoding_name: str = "cl100k_base") -> int:
"""返回文本的 Token 数量。"""
encoding = tiktoken.get_encoding(encoding_name)
num_tokens = len(encoding.encode(string))
return num_tokens
text = "大模型调用,如何优化Token消耗?"
print(f"Token 数: {num_tokens_from_string(text)}") # 输出示例: Token 数: 13
4.2 构建一个带 Token 预算的对话管理器
class ConversationManager:
def __init__(self, system_prompt, max_context_tokens=4096, token_buffer=200):
self.messages = [{"role": "system", "content": system_prompt}]
self.max_context_tokens = max_context_tokens
self.token_buffer = token_buffer # 为输出预留空间
self.encoder = tiktoken.get_encoding("cl100k_base")
def _count_tokens(self, messages):
"""估算消息列表的Token数(简化版)。"""
total = 0
for msg in messages:
total += len(self.encoder.encode(msg["content"]))
total += 4 # 每个消息的格式开销近似值
return total
def add_user_message(self, user_input):
self.messages.append({"role": "user", "content": user_input})
self._prune_context()
def _prune_context(self):
"""修剪上下文,确保不超过Token限制。"""
while self._count_tokens(self.messages) > (self.max_context_tokens - self.token_buffer):
# 优先移除最早的`user`/`assistant`对话,保留`system`提示
if len(self.messages) > 1 and self.messages[1]["role"] in ("user", "assistant"):
removed = self.messages.pop(1)
print(f"已移除历史消息以节省Token: {removed['content'][:50]}...")
else:
break # 无法再修剪
# 使用示例
manager = ConversationManager("你是一个有帮助的助手。", max_context_tokens=2000)
manager.add_user_message("什么是机器学习?")
# ... 多次对话后,上下文会自动修剪
更多推荐

所有评论(0)