📖 定义
Context Caching(上下文缓存)是一种优化技术,通过缓存LLM已处理过的上下文片段(如系统提示词、工具描述、文档片段),在下次需要相同输入时直接复用缓存结果,避免重复计算Attention和KV Cache。在Agent场景下,系统提示词通常几千个token不变,缓存后可节省30%-70%的token消耗。
🎭 妙趣比喻:KFC的疯狂星期四套餐
你每周四都去KFC点同一份疯狂的套餐——炸鸡、蛋挞、可乐,跟服务员说"老样子"。服务员记住了你的套餐,不用你再报一遍菜单。
Context Caching就是这个感觉:每次Agent运行时都要加载那几KB的系统提示词(相当于你的"老样子"套餐),如果每次都让LLM重新处理一遍,就是浪费。缓存之后,系统提示词只计算一次,后面的调用只需处理和上一次不同的部分。这就叫——只需要说"老样子",不需要重报一遍菜单。
"凌晨4点02分,我盯着账单上的API消耗数字发呆。100万token中有80万是重复的系统提示词。那一刻我感觉自己像个冤大头——花四级馆子的钱,吃了个路边摊的炒饭。"
🔬 核心原理
Context Caching的核心原理基于Transformer架构的KV Cache机制:
- KV Cache:LLM推理时,每个token生成都需要计算Key和Value向量。如果连续两段输入的前缀相同(如系统提示词),这部分KV Cache可以复用。
- Prefix Caching:最常见的缓存策略——缓存输入前缀的KV Cache,后续请求只需计算差异部分。
- Semantic Cache:更高级的策略——基于语义相似度匹配已缓存的上下文,不要求输入完全相同。
- Prompt Template Cache:专门缓存系统提示词模板,适用于大量请求使用同一套提示词的场景(如Agent服务)。
缓存生效前后的成本对比
| 场景 | 无缓存消耗 | 有缓存消耗 | 节约 |
|---|---|---|---|
| Agent日常对话(每次2K系统提示) | 10000 token | 2000 token | 80% |
| RAG问答(每次重复文档前缀) | 50000 token | 15000 token | 70% |
| 代码审查Agent(固定code standard) | 30000 token | 12000 token | 60% |
| 批量新闻摘要(相同系统提示) | 80000 token | 8000 token | 90% |
🚀 OpenClaw 实战应用
OpenClaw 支持多种Context Caching策略,尤其适合长时间运行的Agent会话:
妙趣AI 的缓存实战数据
在妙趣AI的内容生成Pipeline中,我们使用了Context Caching:
- 生成环境:每日220+术语/教程页面,每个页面共享相同的系统提示词
- 缓存命中率:92%(系统提示词缓存)+ 45%(文档前缀缓存)
- 每日节省:约 150万 token/天 → 等价节省 ¥120/天
- 冷启动优化:Session重启后首个请求延迟降低 65%
💻 代码示例
示例1:OpenClaw 内置Context Cache使用
# OpenClaw Context Caching 实战
# 通过缓存会话上下文,显著降低重复token消耗
from openclaw import sessions_spawn, session_status
import time
# 缓存策略配置
CACHE_CONFIG = {
"strategy": "prefix", # 策略: prefix | semantic | template
"ttl_seconds": 3600, # 缓存过期时间: 1小时
"max_cache_tokens": 8192, # 最大缓存token数
"auto_invalidate": True, # 上下文变更时自动失效
}
def create_agent_with_cache(system_prompt: str, task: str):
"""创建一个带上下文缓存的Agent Session"""
# 1. 创建Agent session(OpenClaw自动启用前缀缓存)
session = sessions_spawn(
runtime="subagent",
mode="session",
label=f"cached-agent-{hash(system_prompt) % 10000}",
task=f"""
[SYSTEM_PROMPT - CACHEABLE]
{system_prompt}
[TASK - NOT CACHEABLE]
{task}
请开始执行任务。
"""
)
return session
# =====================
# 演示缓存效果
# =====================
# 相同的系统提示词 + 不同的任务
system_prompt = """你是妙趣AI的SEO内容生成Agent。
你擅长用幽默有趣的方式解释技术概念。
要求:输出HTML格式页面,包含定义、比喻、代码示例。
遵循风格:周星驰脑洞 + 王家卫时间感 + 干货。
"""
# 创建多个session,共享同一系统提示词
for i in range(5):
task = f"生成第{i+1}个关于AI Agent的术语解释页面"
session = create_agent_with_cache(system_prompt, task)
# 查看session状态
status = session_status(sessionKey=session["sessionKey"])
print(f"Session {i+1}: {session['sessionKey'][:20]}...")
# 理论上第2-5个session的冷启动速度会加快
# 因为系统提示词的KV Cache已被缓存
if i > 0:
print(f" -> 预计缓存命中,冷启动加速 60-80%")
time.sleep(0.1)
示例2:自定义上下文缓存管理器
// 自定义Context Cache管理器
// 针对频繁重复的Agent调用优化
class ContextCacheManager {
constructor(maxCacheSize = 100) {
this.cache = new Map(); // KV Cache存储
this.hitStats = { hit: 0, miss: 0 };
this.maxSize = maxCacheSize;
}
// 生成缓存key - 基于提示词hash
_generateKey(systemPrompt, toolDefinitions) {
const content = systemPrompt + JSON.stringify(toolDefinitions);
let hash = 0;
for (let i = 0; i < content.length; i++) {
const char = content.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32bit integer
}
return `cache_${hash}`;
}
async getOrCompute(contextKey, computeFn) {
// 检查缓存
if (this.cache.has(contextKey)) {
this.hitStats.hit++;
const cached = this.cache.get(contextKey);
// 检查TTL
if (Date.now() - cached.timestamp < cached.ttl * 1000) {
console.log(`🎯 缓存命中: ${contextKey} (命中率: ${this.getHitRate()})`);
return cached.value;
} else {
// TTL过期,移除缓存
this.cache.delete(contextKey);
console.log(`⏰ 缓存过期: ${contextKey}`);
}
}
// 缓存未命中,计算新值
this.hitStats.miss++;
console.log(`❌ 缓存未命中: ${contextKey} (命中率: ${this.getHitRate()})`);
const result = await computeFn();
// 加入缓存
if (this.cache.size >= this.maxSize) {
// 淘汰最旧的缓存
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(contextKey, {
value: result,
timestamp: Date.now(),
ttl: 3600 // 1小时
});
return result;
}
getHitRate() {
const total = this.hitStats.hit + this.hitStats.miss;
return total === 0 ? 0 : (this.hitStats.hit / total * 100).toFixed(1) + "%";
}
// 批量预热缓存(预加载常见上下文)
async warmupCache(commonContexts) {
console.log(`🔥 缓存预热: 加载 ${commonContexts.length} 个常见上下文`);
for (const ctx of commonContexts) {
const key = this._generateKey(ctx.systemPrompt, ctx.tools);
this.cache.set(key, {
value: ctx,
timestamp: Date.now(),
ttl: 7200 // 预热缓存2小时
});
}
console.log(`✅ 预热完成: ${this.cache.size} 个缓存条目`);
}
}
// 在OpenClaw Agent中使用缓存管理器
const cacheManager = new ContextCacheManager();
async function cachedAgentCall(systemPrompt, tools, userMessage) {
const cacheKey = cacheManager._generateKey(systemPrompt, tools);
return await cacheManager.getOrCompute(cacheKey, async () => {
// 实际的OpenClaw Agent调用
const result = await sessions_send(
sessionKey="my-agent-session",
message=`${JSON.stringify(tools)}\n\n${userMessage}`
);
return result;
});
}
// 预热常见上下文
cacheManager.warmupCache([
{
systemPrompt: "你是妙趣AI的术语百科生成Agent...",
tools: ["web_search", "read_file", "write"]
},
{
systemPrompt: "你是妙趣AI的SEO优化Agent...",
tools: ["web_fetch", "edit", "write"]
}
]);
// 测试缓存效果
for (let i = 0; i < 10; i++) {
const result = await cachedAgentCall(
"你是妙趣AI的术语百科生成Agent...", // 相同的系统提示
["web_search", "read_file", "write"],
`处理第${i}个请求`
);
}
// 预计命中率: 90%+
console.log(`最终命中率: ${cacheManager.getHitRate()}`);
示例3:Token成本审计脚本
# 上下文缓存前后成本对比
# 实用脚本:计算你的Agent运行在开启缓存后的节省
def audit_context_caching_cost(agent_config):
"""
审计Agent在不同缓存策略下的成本
agent_config = {
"system_prompt_tokens": 2048, # 系统提示词token数
"avg_input_tokens": 512, # 每次输入token数
"avg_output_tokens": 1024, # 每次输出token数
"calls_per_day": 10000, # 每日调用次数
"price_per_token": 0.000002, # 每token价格(美元)
}
"""
sp = agent_config["system_prompt_tokens"]
inp = agent_config["avg_input_tokens"]
out = agent_config["avg_output_tokens"]
calls = agent_config["calls_per_day"]
price = agent_config["price_per_token"]
# 无缓存:每次调用都处理系统提示词
no_cache_daily = calls * (sp + inp + out) * price
no_cache_monthly = no_cache_daily * 30
# 有缓存:系统提示词只计算一次,后续只处理差异
cache_ratio = 0.7 # 假设70%的输入是重复的
cached_daily = (sp + calls * (inp * (1 - cache_ratio) + out)) * price
cached_monthly = cached_daily * 30
saving_percentage = (1 - cached_daily / no_cache_daily) * 100
print(f"📊 成本审计报告 - {agent_config.get('name', 'Agent')}")
print(f"{'='*50}")
print(f"每日调用次数: {calls:,}")
print(f"缓存重复率: {cache_ratio*100:.0f}%")
print(f"无缓存 - 日费用: \${no_cache_daily:.2f} / 月费用: \${no_cache_monthly:.2f}")
print(f"有缓存 - 日费用: \${cached_daily:.2f} / 月费用: \${cached_monthly:.2f}")
print(f"{'='*50}")
print(f"💰 每月节省: \${no_cache_monthly - cached_monthly:.2f}")
print(f"📈 节约比例: {saving_percentage:.1f}%")
return {
"no_cache_monthly": no_cache_monthly,
"cached_monthly": cached_monthly,
"saving_pct": saving_percentage
}
# 计算妙趣AI的每日内容生成成本
audit_context_caching_cost({
"name": "妙趣AI内容生成Pipeline",
"system_prompt_tokens": 2048,
"avg_input_tokens": 512,
"avg_output_tokens": 2048,
"calls_per_day": 1500,
"price_per_token": 0.000003
})