EP08. "Prompt Caching 加速省钱 + Code Execution 沙盒跑代码"
🔒 登录后可标记已读- Prompt Caching 能省下「重复处理同一段内容」的运算成本——Claude 处理 prompt 要经过分词、embedding、语境化好几个预处理步骤,没有 caching 时,这些工作每次请求都要重跑一遍,纯属浪费
- Caching 不是自动的——要自己在指定的 message block 上手动加 cache breakpoint(
cache_control: {"type": "ephemeral"}),断点之前的内容才会被缓存,断点之后照常处理 - 缓存默认只活 5 分钟,想延长到 1 小时要多付 cache-write 的费用;内容只要有一丝改动(哪怕加一个字),缓存就直接失效
- Code Execution Tool 让 Claude 在隔离的 Docker 容器里真的跑 Python 代码,但容器没有网络连接,配合 Files API 上传数据文件,才能把外部数据"喂"进这个完全隔离的沙盒
- 前置知识:读过 [EP04](./EP04_工具调用基础.md) 理解工具调用(tool schema)基本概念;这篇笔记涉及的两个功能(Caching、Code Execution)都是叠加在标准
messages.create()之上的进阶选项
重点内容
Prompt Caching 解决什么问题
Claude 处理一次请求,一般要经过这几步预处理:
- 把 prompt 切成一个个 token
- 给每个 token 生成 embedding
- 根据上下文调整每个 token 的含义
- 才轮到真正生成输出
没有 caching 时,这些预处理的运算成果,每次请求完就被丢掉了。开启 caching 后,Claude 会把预处理结果存进一张查找表,同样的内容再次出现时,直接复用,不用重算。
好处:
- 命中缓存的部分响应更快
- 命中缓存的部分成本更低
- 不用手动区分"这次该写缓存还是该读缓存"——第一次请求自动写入,后续请求自动读取
限制:
- 缓存默认5 分钟过期;想延长到 1 小时,要多付更高的 cache-write 费用
- 只有「重复出现相同内容」的场景才有意义,不是随便加上去就有用
- 越频繁复用同一段内容,收益越大
最适合的场景:文档分析工作流(同一份长文档反复被问不同问题)、迭代式编辑任务(基础内容不变,只是反复微调某个局部)。
Cache Breakpoint 怎么运作
📌 Caching 不是自动发生的——要手动在特定的 message block 上加缓存断点(cache breakpoint)。
规则:
- 断点之前(含断点本身)的内容会被缓存,断点之后的内容照常正常处理,不进缓存
- 要用"展开格式"(expanded format),把
cache_control字段设成{"type": "ephemeral"}——简写格式(shorthand)没有地方能加这个字段,不支持缓存 - 后续请求只有在"直到断点为止的内容完全一致"时才会命中缓存——哪怕只是多加了一个「please」这种小改动,缓存也会直接失效
⚠️ Cache breakpoint 可以跨越多条 message、多种类型。如果断点放在比较靠后的 message 上,它前面所有的 message(不管是 user 还是 assistant)都会被算进缓存内容里。
可以加断点的内容类型:system prompt、tool 定义、图片 block、tool use/result block——其中 system prompt 和 tool 定义是最值得优先加缓存的对象(因为它们通常是整个应用里最稳定不变的部分)。
处理顺序与上限:
- Claude 处理的顺序固定是:tools → system prompt → messages
- 一次请求最多允许 4 个缓存断点
- 缓存生效有最低门槛:累计至少 1024 token(跨所有 message/block 加总)才会触发缓存
实际怎么写:给 Tools 和 System Prompt 加缓存
Tool Schema 加缓存(只在最后一个 tool 上加断点,前面的 tools 也会一起被缓存进去,因为处理顺序是整批一起算):
if tools:
tools_clone = tools.copy()
last_tool = tools_clone[-1].copy()
last_tool["cache_control"] = {"type": "ephemeral"}
tools_clone[-1] = last_tool
params["tools"] = tools_clone
📌 这里特地先 .copy() 出一份副本再改,不要直接改原始的 tools 列表——避免后续代码里工具顺序变动时,不小心把缓存断点标记也一起带歪了。
System Prompt 加缓存:
if system:
params["system"] = [
{
"type": "text",
"text": system,
"cache_control": {"type": "ephemeral"}
}
]
缓存命中的表现:
- 第一次请求(写入缓存):回应里会带
cache_creation_input_tokens=1772这类字段,代表这次帮你写了多少 token 进缓存 - 后续请求(命中缓存):改成看到
cache_read_input_tokens=1772,代表这次直接读缓存,省了重新处理这批 token 的运算 - ⚠️ 缓存内容只要有任何一个字符不同,就会失效,退回正常处理
💡 如果一次请求里只有部分组件改变了(比如 tools 没变、system prompt 变了),会出现"部分命中缓存"的情况——处理顺序(tools → system prompt → messages)决定了改动出现在哪一层,前面没变的部分照样能命中缓存。
Code Execution + Files API:让 Claude 真的跑代码
Files API:允许把文件单独上传,拿到一个文件 ID,之后用这个 ID 引用文件,不用再把整份文件编码成 base64 塞进消息本体。适合需要反复引用同一份文件、或者文件本身比较大的场景。
Code Execution Tool:服务端功能,让 Claude 能在隔离的 Docker 容器里执行 Python 代码。特点:
- 容器没有网络访问权限
- 同一段对话里可以多次执行代码
📌 因为容器完全没有对外连接,Files API 就成了把外部数据"喂"进这个沙盒的主要方式——这两个功能组合起来才是真正的威力所在,单独用 Code Execution 没办法碰到你自己的数据。
典型工作流:
- 用 Files API 上传数据文件(CSV 等)
- Message 里加一个 container upload block,带上文件 ID
- 请 Claude 做分析
- Claude 自己写代码、执行,处理这份文件
- 生成可下载的产出(比如一张图表)
上传文件:
file_metadata = upload('streaming.csv')
组装带文件引用的消息,并开启 Code Execution 工具:
messages = []
add_user_message(
messages,
[
{
"type": "text",
"text": """Run a detailed analysis to determine major drivers of churn.
Your final output should include at least one detailed plot summarizing your findings."""
},
{"type": "container_upload", "file_id": file_metadata.id},
],
)
chat(
messages,
tools=[{"type": "code_execution_20250522", "name": "code_execution"}]
)
回应里会出现的内容:说明性文字、记录了实际执行代码的 server tool use block、以及包含执行输出的 code execution result block。
拿生成的文件:在回应里找 type: "code_execution_output" 的 block,里面带着产出文件的 file ID,凭这个 ID 下载。
应用场景:数据分析画图之外,还能用在图片处理、文档格式转换、数学计算、自动生成报告等任何"需要真的跑代码才能算出来/做出来"的任务。
常见错误
- ❌ 以为缓存是全自动的——不手动加
cache_control: {"type": "ephemeral"}断点,内容不会被缓存 - ❌ 用简写格式(shorthand)传 system/tools,却想加缓存——简写格式没地方放
cache_control字段,必须改用展开格式 - ❌ 内容小改动了还期待命中缓存——哪怕只多了一个字,断点前的内容就必须完全一致才会命中,改了就等于重新写一次缓存
- ❌ 直接在原始
tools列表上改cache_control,不先.copy()——容易在后续逻辑改动工具顺序时,让缓存标记跟着跑位 - ❌ 以为开了 Code Execution Tool 就能让 Claude 直接读到你服务器上的文件——容器没有网络,必须先用 Files API 上传、再用
container_uploadblock 引用
Sources
官方文档:
- Building with the Claude API(Claude Academy 课程)— https://academy.claude.com/courses/building-with-the-claude-api