给OpenClaw装上“省钱三件套”:Token消耗直降63%
上周用OpenClaw接了个微信小助手,功能不算复杂——日程管理加信息摘要。跑了三天,Token消耗直接飙到预算的4倍。盯着后台用量曲线,整个人都不好了。
OpenClaw能力确实强,推理速度也快,但如果不做Token优化,成本能把你吃穷。
核心思路就三个:压缩上下文、缓存高频请求、动态切换模型。实测组合使用可以把Token开销降50%-70%,下面一个个拆。
先说结论
| 优化策略 | 实施难度 | Token节省比例 | 适用场景 |
|---|---|---|---|
| Prompt压缩 + 上下文裁剪 | ⭐⭐ | 30%-40% | 所有场景 |
| 语义缓存层 | ⭐⭐⭐ | 20%-50% | 高频重复查询 |
| 动态模型路由 | ⭐⭐ | 30%-60% | 多任务混合场景 |
三个方案可以叠加,我最终那个微信小助手项目综合下来Token消耗降了约63%。
环境准备
我的开发环境:
-
Python 3.11+
-
openai SDK(OpenClaw兼容OpenAI协议)
-
redis(做缓存层用)
-
tiktoken(Token计数)
bash
pip install openai tiktoken redis
OpenClaw走标准OpenAI兼容协议,用openai SDK直接调。我这边统一走聚合接口,方便后面做模型路由切换:
python
from openai import OpenAI
client = OpenAI(
api_key="your-key",
base_url="https://4sapi.com/v1" # 聚合接口,一个Key调OpenClaw和其他模型
)
方案一:Prompt压缩 + 上下文裁剪
最简单也是回报最高的优化。很多人写Prompt跟写作文似的,废话一大堆,Token就是这么烧掉的。
1.1 System Prompt瘦身
我原来的System Prompt长这样(别笑,很多人都这么写):
python
# ❌ 反面教材:178 tokens
system_prompt_bad = """
你是一个非常专业的日程管理助手。你需要帮助用户管理他们的日程安排,
包括但不限于创建新的日程、修改已有的日程、删除日程、查询某个时间段的日程安排。
你应该用友好、专业的语气回复用户。当用户的请求不明确时,你应该主动询问更多细节。
你需要以 JSON 格式返回结构化的日程数据,包含 title、start_time、end_time、description 等字段。
请注意,所有时间都应该使用 ISO 8601 格式。
"""
优化后:
python
# ✅ 优化后:62 tokens
system_prompt_good = """日程助手。操作:创建/修改/删除/查询。
输出JSON:{title,start_time,end_time,desc},时间用ISO8601。
不明确时追问。"""
效果一样,Token少了65%。模型不需要你用“非常专业的”“包括但不限于”这种修饰语,它理解意图的能力比你想象的强。
1.2 对话历史滑动窗口
这才是Token消耗的大头。微信聊天场景下用户可能连续聊几十轮,每次把完整历史全塞进去,Token会指数级增长。
python
import tiktoken
def count_tokens(messages, model="gpt-4o"):
"""计算消息列表的token数"""
enc = tiktoken.encoding_for_model(model)
total = 0
for msg in messages:
total += len(enc.encode(msg["content"])) + 4 # 每条消息的overhead
return total
def trim_conversation(messages, max_tokens=2000):
"""
滑动窗口裁剪:保留system prompt + 最近N轮对话
超出max_tokens时,从最早的用户消息开始砍
"""
system_msgs = [m for m in messages if m["role"] == "system"]
chat_msgs = [m for m in messages if m["role"] != "system"]
# 从最新的消息往前保留,直到塞不下
trimmed = []
current_tokens = count_tokens(system_msgs)
for msg in reversed(chat_msgs):
msg_tokens = count_tokens([msg])
if current_tokens + msg_tokens > max_tokens:
break
trimmed.insert(0, msg)
current_tokens += msg_tokens
return system_msgs + trimmed
# 实际使用
conversation = [
{"role": "system", "content": system_prompt_good},
# ... 可能有30轮对话历史
]
# 裁剪到2000 tokens以内
trimmed = trim_conversation(conversation, max_tokens=2000)
response = client.chat.completions.create(
model="openclaw", # 通过聚合接口调用
messages=trimmed,
max_tokens=500
)
实测30轮对话的场景,不裁剪每次请求大概4000-6000 tokens,裁剪后稳定在1500-2000 tokens。光这一步就砍掉了60%的输入Token。
1.3 摘要压缩(进阶)
滑动窗口有个问题——早期上下文丢了。用户第3轮说了个重要信息,到第20轮已经被裁掉了。
解决方案:对被裁掉的历史做摘要,塞进system prompt。
python
def summarize_old_context(old_messages):
"""用便宜的小模型给旧对话做摘要"""
summary_response = client.chat.completions.create(
model="deepseek-v3", # 用便宜模型做摘要
messages=[
{"role": "system", "content": "用50字内总结以下对话的关键信息,只保留事实。"},
{"role": "user", "content": str(old_messages)}
],
max_tokens=100
)
return summary_response.choices[0].message.content
def smart_trim(messages, max_tokens=2000):
"""智能裁剪:旧消息摘要 + 新消息保留"""
system_msgs = [m for m in messages if m["role"] == "system"]
chat_msgs = [m for m in messages if m["role"] != "system"]
if count_tokens(messages) <= max_tokens:
return messages
# 找到需要裁掉的部分
keep_msgs = []
cut_msgs = []
current_tokens = count_tokens(system_msgs) + 200 # 预留摘要空间
for msg in reversed(chat_msgs):
msg_tokens = count_tokens([msg])
if current_tokens + msg_tokens > max_tokens:
cut_msgs = chat_msgs[:chat_msgs.index(msg) + 1]
break
keep_msgs.insert(0, msg)
current_tokens += msg_tokens
if cut_msgs:
summary = summarize_old_context(cut_msgs)
system_msgs[0]["content"] += f"\n[历史摘要]{summary}"
return system_msgs + keep_msgs
摘要用DeepSeek V3这种便宜模型跑,成本几乎可以忽略,但保住了关键上下文。
方案二:语义缓存层
微信小助手上线后发现一个规律:40%的请求在语义上是重复的。比如不同用户都在问「明天天气怎么样」「帮我看看明天天气」「明天天气」,意思一模一样,每次都调API太浪费了。
python
import redis
import hashlib
import json
r = redis.Redis(host='localhost', port=6379, db=0)
def get_cache_key(messages):
"""
生成缓存key
简单方案:直接对最后一条用户消息做hash
进阶方案:可以用embedding做语义相似度匹配
"""
last_user_msg = ""
for msg in reversed(messages):
if msg["role"] == "user":
last_user_msg = msg["content"]
break
# 归一化:去空格、转小写
normalized = last_user_msg.strip().lower()
return f"llm_cache:{hashlib.md5(normalized.encode()).hexdigest()}"
def cached_completion(messages, model="openclaw", ttl=3600):
"""带缓存的API调用"""
cache_key = get_cache_key(messages)
# 查缓存
cached = r.get(cache_key)
if cached:
print("[Cache HIT] 省了一次API调用")
return json.loads(cached)
# 缓存未命中,调API
response = client.chat.completions.create(
model=model,
messages=messages,
max_tokens=500
)
result = {
"content": response.choices[0].message.content,
"usage": {
"prompt_tokens": response.usage.prompt_tokens,
"completion_tokens": response.usage.completion_tokens
}
}
# 写缓存,TTL 1小时
r.setex(cache_key, ttl, json.dumps(result, ensure_ascii=False))
print(f"[Cache MISS] 消耗tokens: {response.usage.total_tokens}")
return result
这个方案虽然简单粗暴(纯文本hash),在我的场景下缓存命中率达到了35%。想做得更精细可以用embedding算语义相似度,命中率能到50%以上,但要多一次向量计算的开销,小项目可能不值得。
方案三:动态模型路由
这是我觉得最“聪明”的优化——不是所有请求都需要用最贵的模型。
思路很简单:简单问题用便宜模型,复杂问题才上OpenClaw或Claude Opus 4.6。
python
def classify_complexity(user_message):
"""
简单的复杂度分类器
实际项目中可以用关键词匹配 + 小模型判断
"""
simple_patterns = ["你好", "谢谢", "查询", "几点", "天气", "帮我看看"]
complex_patterns = ["分析", "对比", "总结", "规划", "建议", "方案"]
msg = user_message.lower()
for p in complex_patterns:
if p in msg:
return "complex"
for p in simple_patterns:
if p in msg:
return "simple"
# 默认中等
return "medium"
def route_model(user_message):
"""根据复杂度选择模型"""
complexity = classify_complexity(user_message)
model_map = {
"simple": "deepseek-v3", # 简单问题,成本约0.001元/次
"medium": "openclaw", # 中等问题
"complex": "claude-opus-4.6", # 复杂推理
}
model = model_map[complexity]
print(f"[Router] 复杂度={complexity}, 使用模型={model}")
return model
# 实际调用
def smart_chat(messages):
last_msg = ""
for m in reversed(messages):
if m["role"] == "user":
last_msg = m["content"]
break
model = route_model(last_msg)
response = client.chat.completions.create(
model=model,
messages=messages,
max_tokens=500
)
return response
跑了一周数据,请求分布大概是:简单45%、中等35%、复杂20%。算下来整体成本比全部用OpenClaw低了约55%。
整体架构
text
用户消息 → 微信
↓
上下文裁剪/摘要压缩
↓
语义缓存命中? ──是──→ 直接返回缓存结果
↓否
复杂度分类器
↓
├─ 简单 → DeepSeek V3
├─ 中等 → OpenClaw
└─ 复杂 → Claude Opus 4.6
↓
写入缓存
↓
返回用户
DeepSeek V3、OpenClaw、Claude Opus 4.6都通过星链4SAPI的聚合接口调,一个API Key切换不同模型,不用分别管各家的鉴权和SDK。星链4SAPI是一个AI模型聚合平台,兼容OpenAI协议,低延迟直连,做模型路由只需要换model参数,代码完全不用改。
踩坑记录
坑1:tiktoken不支持OpenClaw的tokenizer
tiktoken目前没有OpenClaw的编码器,我用gpt-4o的编码器近似计算,实测误差在5%-10%左右。做预算控制够用了,但精确计费还是以API返回的usage为准。
坑2:缓存key的归一化不够
一开始只做了去空格+转小写,结果「查日程」和「帮我查一下日程」就对不上。后来加了停用词过滤,命中率从20%提到了35%。上embedding方案能更高,但要多一次向量计算的开销,小项目可能不值得。
坑3:模型路由的分类器别用大模型做
我一开始想用LLM来判断复杂度,结果判断本身就要消耗Token——脱裤子放屁。后来改成关键词匹配加消息长度启发式规则,零成本搞定。
小结
三招总结:
-
Prompt压缩 + 滑动窗口:最简单,立刻能做,输入Token砍30-40%
-
语义缓存:适合有重复查询模式的场景,省20-50%的API调用
-
动态模型路由:把便宜模型用在简单任务上,整体成本降30-60%
三个叠加起来,微信小助手的日均Token消耗从280万降到了103万,成本从每天大概40块降到15块左右。对个人项目来说,这个差距就是“能跑下去”和“跑不起”的区别。
OpenClaw本身能力没问题,关键是学会该省省、该花花。有问题评论区聊。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)