Tóm tắt: Đặt cache_control: {"type": "ephemeral"} lên block cuối cùng của phần prefix không đổi. Thứ tự prefix luôn là tools → system → messages. Lần gọi đầu, phần đó được ghi vào cache (usage.cache_creation_input_tokens). Các lần sau, trong thời gian TTL (mặc định 5 phút, mỗi lần hit lại được làm mới), prefix giống hệt sẽ đọc từ cache (usage.cache_read_input_tokens) với giá rẻ hơn nhiều. Vòng tool use cơ bản xem Claude API tool use.
Luồng hoạt động
flowchart LR A["Request: tools + system + messages"] --> B{"Prefix đến breakpoint đã có trong cache?"} B -->|"có"| C["cache_read_input_tokens (rẻ)"] B -->|"không"| D["cache_creation_input_tokens (ghi cache)"] C --> E["Phần sau breakpoint: input_tokens"] D --> E E --> F[Model sinh output]
Cache khớp theo prefix chính xác. Chỉ cần đổi một ký tự trong tools hoặc system thì mọi breakpoint phía sau đều bị miss.
Cấu hình
Cache system prompt + tài liệu dài
from pathlib import Path
import anthropic
client = anthropic.Anthropic()
RUNBOOK = Path("runbook-bgp.md").read_text() # tài liệu dài, ít đổi
SYSTEM = [
{"type": "text", "text": "Bạn là trợ lý NOC. Trả lời ngắn, dẫn mục runbook."},
{
"type": "text",
"text": RUNBOOK,
"cache_control": {"type": "ephemeral"}, # breakpoint: cache tools + toàn bộ system
},
]
def ask(question: str) -> str:
resp = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
system=SYSTEM,
messages=[{"role": "user", "content": question}],
)
u = resp.usage
print(f"write={u.cache_creation_input_tokens} read={u.cache_read_input_tokens} "
f"uncached={u.input_tokens} out={u.output_tokens}")
return resp.content[0].text
ask("Peer BGP flap liên tục thì kiểm tra gì trước?") # lần 1: write > 0
ask("Khi nào được clear bgp soft?") # lần 2: read > 0Cache hội thoại nhiều lượt (agent loop)
Đặt breakpoint ở block cuối của message mới nhất. Ở lượt sau, toàn bộ lịch sử trước đó sẽ là cache hit. Cách gọn nhất là dùng tham số top-level cache_control (có trong SDK Python hiện hành, đã kiểm tra với anthropic 0.109). SDK sẽ tự gắn breakpoint vào block cuối có thể cache của request:
resp = client.messages.create(model="claude-sonnet-5-5", max_tokens=2048,
cache_control={"type": "ephemeral"},
system=SYSTEM, tools=TOOLS, messages=messages)Với SDK cũ hơn, tự gắn bằng hàm:
def with_cache_on_last(messages):
"""Gắn cache_control vào block cuối của message cuối, không sửa list gốc."""
msgs = [dict(m) for m in messages]
last = msgs[-1]
content = last["content"]
if isinstance(content, str):
content = [{"type": "text", "text": content}]
content = [dict(b) if isinstance(b, dict) else b.model_dump(exclude_none=True) for b in content]
content[-1]["cache_control"] = {"type": "ephemeral"}
last["content"] = content
return msgs
resp = client.messages.create(model="claude-sonnet-5-5", max_tokens=2048,
system=SYSTEM, tools=TOOLS,
messages=with_cache_on_last(messages))Tham số và giới hạn
| Mục | Giá trị |
|---|---|
| Kiểu cache | {"type": "ephemeral"} |
| TTL | Mặc định 5 phút. Muốn 1 giờ thì dùng {"type": "ephemeral", "ttl": "1h"} |
| Số breakpoint tối đa | 4 trong một request |
| Chỗ đặt được | Tool definition, block system, block trong messages (text, image, tool_use, tool_result) |
| Độ dài tối thiểu | Prefix ngắn hơn ngưỡng của model (cỡ một đến vài nghìn token) sẽ không được cache và không báo lỗi |
| Giá (tương đối với input thường) | Ghi cache 5 phút ~1.25x, ghi 1 giờ ~2x, đọc cache ~0.1x |
| Phạm vi cache | Theo organization/workspace, không chia sẻ giữa các tổ chức |
Kiểm tra giá và ngưỡng chính xác cho từng model trên trang pricing trước khi tính chi phí.
Đọc usage
| Trường | Ý nghĩa |
|---|---|
cache_creation_input_tokens |
Token vừa được ghi vào cache (tính giá ghi) |
cache_read_input_tokens |
Token đọc từ cache (tính giá đọc) |
input_tokens |
Token sau breakpoint cuối, không cache |
cache_creation.ephemeral_5m_input_tokens / ephemeral_1h_input_tokens |
Lượng token ghi, tách theo TTL |
Tổng input của request = input_tokens + cache_creation_input_tokens + cache_read_input_tokens.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
| Cả write và read đều bằng 0 | Prefix ngắn hơn ngưỡng tối thiểu | Chỉ cache phần dài (runbook, tool list lớn) |
| Lần nào cũng write, không bao giờ read | Prefix thay đổi mỗi lần: timestamp, UUID, thứ tự tool ngẫu nhiên, json.dumps không sort_keys |
Đưa phần động ra sau breakpoint, giữ tools/system cố định |
| Read giảm sau vài phút không dùng | Hết TTL 5 phút | Dùng "ttl": "1h" cho tác vụ thưa, chấp nhận giá ghi cao hơn |
400 khi thêm breakpoint |
Quá 4 cache_control trong một request |
Gom lại: tools/system dùng 1 breakpoint, hội thoại dùng 1 |
| Cache miss sau khi thêm hoặc bớt tool | Tools nằm đầu prefix nên mọi phần sau bị miss | Cố định danh sách tool trong một phiên |
| Request song song đều write | Cache chỉ sẵn sàng sau khi response đầu tiên bắt đầu trả về | Gửi 1 request "làm nóng" trước rồi mới fan-out |