搞懂 LLM 缓存机制:一个改动让 API 调用省 80% Token
做 AI 工作流平台的时候,token 费用是绕不开的问题。我们的 Consumer 服务每天要调几千次模型 API,一个工作流跑下来可能串联 5-6 个节点,每个节点都带着一大坨 system prompt。算下来,光是重复发送 system prompt 的 token 就占了总消耗的一大半。
直到我搞明白了 KV Cache 这个东西,才发现原来 API 厂商已经帮你做好了缓存——你只需要知道怎么触发它。
KV Cache 是什么
Transformer 的注意力公式大家都见过:
Attention(Q, K, V) = softmax(Q·K^T / √d) · V
关键点在于:Decoder-only 架构(GPT、Claude、Gemma 这些)用的是 causal masking,每个 token 只看前面的 token。这意味着历史 token 的 K 和 V 算完之后就不会变了。
所以模型推理的时候会把历史 token 的 Key 和 Value 矩阵缓存起来,下次只需要算新 token 的 Q,然后跟缓存的 K、V 做注意力计算就行。这就是 KV Cache。
对于 API 用户来说,这个机制被包装成了 Prompt Caching(前缀缓存):如果你连续多次请求的 prompt 前缀相同,服务端会复用之前算好的 KV Cache,跳过重复计算。
用 curl 实测:缓存命中 vs 未命中
说再多不如跑一下。用 Anthropic 的 API 直接测,先发一个带 cache_control 的请求:
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: prompt-caching-2024-07-31" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 256,
"system": [
{
"type": "text",
"text": "你是一个专业的视频脚本创作助手。你需要根据用户的需求,生成包含分镜描述、旁白文案、画面建议的完整视频脚本。每个分镜需要包含:场景描述、镜头运动、时长建议、旁白内容。输出格式为 JSON...(此处省略,实际是一段 2000+ token 的详细 system prompt)",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{"role": "user", "content": "帮我写一个30秒的产品宣传视频脚本,产品是一款智能手表"}
]
}'
看返回的 usage 字段:
{
"usage": {
"input_tokens": 2150,
"output_tokens": 180,
"cache_creation_input_tokens": 2048,
"cache_read_input_tokens": 0
}
}
第一次请求,cache_creation_input_tokens: 2048,说明缓存刚建立。紧接着换个用户问题再发一次:
# 同样的 system prompt,只改 messages
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: prompt-caching-2024-07-31" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 256,
"system": [
{
"type": "text",
"text": "你是一个专业的视频脚本创作助手...(同上,完全一样的 system prompt)",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{"role": "user", "content": "帮我写一个15秒的美食探店短视频脚本"}
]
}'
返回:
{
"usage": {
"input_tokens": 120,
"output_tokens": 195,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 2048
}
}
cache_read_input_tokens: 2048——2048 个 token 全部命中缓存。Anthropic 对缓存读取的计费是正常价格的 1/10,相当于这 2048 个 token 只花了原来十分之一的钱。
这对工作流引擎意味着什么
我们的架构是这样的:用户在画布上拖节点建工作流,点运行后 Consumer 按 DAG 拓扑顺序执行每个节点。每个节点调一次模型 API。
一个典型的视频制作工作流可能长这样:
[创意构思] → [脚本生成] → [分镜描述] → [配音文案] → [画面提示词]
5 个节点,每个都带一段 system prompt 描述它的角色和输出格式。如果这些 system prompt 有公共前缀(比如都以项目背景、品牌调性开头),那从第二个节点开始,公共前缀部分就能命中缓存。
算一笔账。假设每个节点的 system prompt 有 2000 token,其中 1500 token 是公共前缀:
| 无缓存 | 有缓存 | |
|---|---|---|
| 节点 1 | 2000 token(全价) | 2000 token(全价,建立缓存) |
| 节点 2-5 | 2000 × 4 = 8000 token(全价) | 500 × 4 = 2000 全价 + 1500 × 4 = 6000 缓存价(1/10) |
| 总计 | 10000 token | 2000 + 2000 + 600 = 4600 等效 token |
省了 54%。如果工作流节点更多、公共前缀更长,省得更多。
OpenAI 也有类似机制
OpenAI 的 Prompt Caching 更简单粗暴——自动生效,不需要你手动标记 cache_control。只要请求的前缀匹配到 1024 token 以上,就会自动缓存:
curl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [
{
"role": "system",
"content": "你是一个专业的视频脚本创作助手...(同样的长 system prompt)"
},
{
"role": "user",
"content": "写一个30秒的产品宣传视频脚本"
}
]
}'
返回的 usage 里会有 prompt_tokens_details.cached_tokens:
{
"usage": {
"prompt_tokens": 2150,
"completion_tokens": 180,
"prompt_tokens_details": {
"cached_tokens": 2048
}
}
}
OpenAI 缓存命中的价格是正常的 1/2(Anthropic 是 1/10,这点 Anthropic 更划算)。缓存 TTL 大概 5-10 分钟,流量大的时候可能更长。
实际落地:怎么在工作流引擎里用好缓存
搞清楚原理之后,优化方向就很明确了:
1. system prompt 设计:公共部分放前面
把所有节点共享的上下文(项目背景、品牌调性、输出语言偏好)放在 system prompt 的最前面,节点特有的指令放后面。这样公共前缀越长,缓存命中率越高。
# 不好的写法:每个节点的 system prompt 完全独立
system_prompt = f"你是一个{node_type}专家。{node_specific_instructions}"
# 好的写法:公共前缀 + 节点特有指令
system_prompt = f"""{project_context}
{brand_guidelines}
{output_language_preference}
---
当前任务:你是一个{node_type}专家。
{node_specific_instructions}"""
2. 同模型的节点尽量连续执行
KV Cache 是跟模型绑定的。如果工作流里混用了 Claude 和 GPT,它们的缓存是完全独立的。DAG 调度的时候,同模型的节点尽量排在一起执行,能最大化缓存命中。
3. 对 Anthropic API 显式标记 cache_control
OpenAI 自动缓存不用管,但 Anthropic 需要你在 system prompt 上加 cache_control: {"type": "ephemeral"}。漏了这个标记,缓存就不会生效。
4. 注意缓存 TTL
Anthropic 的缓存 TTL 是 5 分钟(Pro 用户 1 小时),OpenAI 大概 5-10 分钟。如果两次请求间隔超过 TTL,缓存就失效了。对于工作流引擎来说,节点之间的执行间隔通常在秒级,所以基本不用担心。但如果是用户手动触发的"继续运行",间隔可能比较长,这时候缓存大概率已经过期了。
Claude Code 的缓存策略也值得参考
顺便提一下,Claude Code 的 prompt 结构设计得很精巧,分了好几层缓存:
- 第一层:全局 system instructions(所有用户共享,全球级缓存)
- 第二层:用户级内容,比如 CLAUDE.md 文件(组织级缓存)
- 第三层:对话历史(会话级缓存,在关键断点设置
cache_control)
这个分层思路对工作流引擎也有启发:全局配置 > 项目配置 > 节点配置,越稳定的内容放越前面,缓存命中率就越高。
还有个细节:Claude Code 会监控 cache_read_input_tokens,如果这个值突然下降超过 5% 且超过 2000 token,就说明缓存被打破了。我们也可以在 Consumer 里加类似的监控,追踪每次 API 调用的缓存命中率,发现异常及时排查。
一句话总结
KV Cache 是模型推理层面的优化,API 厂商把它包装成了 Prompt Caching 给开发者用。对于我们这种多节点工作流场景,只要把 system prompt 的公共部分提到前面、给 Anthropic 的请求加上 cache_control,就能省掉大量重复 token 的费用。改动不大,效果立竿见影。