Prompt Caching & Context Caching 2026 - Kiến trúc Tái sử dụng KV Cache Provider-level cho Claude, OpenAI, Gemini với Redis Edge và ClickHouse Analytics
Posted on: 4/14/2026 11:10:40 PM
Table of contents
- 1. Vì sao Prompt Caching là tầng tối ưu quan trọng nhất năm 2026
- 2. Kiến trúc KV Cache bên trong Transformer — để hiểu cái gì đang được cache
- 3. Ba cách làm khác nhau: Claude, OpenAI, Gemini
- 4. Kinh tế học của prompt cache — khi nào cache, khi nào đừng
-
5. Năm pattern prompt cache cho multi-agent production
- 5.1. Pattern "Static Foundation" — system prompt + tool schema
- 5.2. Pattern "Document Pinning" — ghim tài liệu tham khảo trong session
- 5.3. Pattern "Conversation Extend" — turn rolling với cache memory
- 5.4. Pattern "Agent Fan-out" — một prefix, nhiều worker
- 5.5. Pattern "Cache-aware Routing" — LLM Gateway quyết định model theo cache state
- 6. Redis Edge Cache — tầng bổ trợ bên ngoài provider
- 7. ClickHouse Analytics — đo và tối ưu cache hit ratio
- 8. Các failure mode hay gặp và cách né
- 9. Benchmark thực tế — prompt cache đáng giá bao nhiêu với agent của bạn
- 10. Prompt Cache vs Long Context — khi nào dùng cái gì
- 11. Những gì sẽ đến trong 2026-2027
- 12. Checklist triển khai prompt cache cho agent của bạn
- 13. Kiến trúc tham khảo end-to-end
- 14. Kết luận — prompt cache là cái bật mặc định cho agent 2026
Khi một agent của bạn bắt đầu một request với hai chục nghìn token gồm system prompt, danh sách tool, vài ba tài liệu tham khảo và bộ nhớ hội thoại, phần lớn chi phí và độ trễ không đến từ phần mới mà đến từ việc model phải đọc lại toàn bộ ngữ cảnh cũ. Đây là vấn đề mà cả Anthropic, OpenAI và Google đã âm thầm giải quyết trong 2024-2025 bằng một kỹ thuật đơn giản mà sâu sắc: Prompt Caching — tái sử dụng trạng thái KV cache của chính model cho những prefix token đã xuất hiện trước đó. Khác với semantic caching ở tầng ứng dụng (so trùng bằng vector), prompt caching hoạt động ở tầng inference: model đọc prefix một lần, ghi lại KV cache vào bộ nhớ GPU trong vài phút, và mọi request tiếp theo có cùng prefix chỉ cần tính phần khác biệt. Kết quả là chi phí đầu vào giảm 75-90%, time-to-first-token giảm 2-5 lần, và latency tổng của agent có thể rút ngắn một nửa mà không thay đổi một dòng logic nghiệp vụ. Bài viết này đi sâu vào kiến trúc bên trong của prompt caching, cách Claude (Anthropic), OpenAI và Gemini cài đặt khác nhau, và cách kết hợp với Redis edge cache cùng ClickHouse analytics để vận hành multi-agent ở quy mô production năm 2026.
1. Vì sao Prompt Caching là tầng tối ưu quan trọng nhất năm 2026
Một agent production hiện đại không chạy với "vài trăm token đầu vào". Một lần gọi Claude cho một coding agent điển hình thường bao gồm: 4-8K token system prompt và hướng dẫn, 10-30K token khai báo tool và schema, 5-20K token tài liệu tham khảo (đặc tả API, CLAUDE.md, snippet code), 2-5K token memory từ các lượt trước, và cuối cùng là vài trăm token câu hỏi mới của người dùng. Gần như mọi byte trong nhóm đầu không thay đổi giữa hai lượt gọi liên tiếp. Tuy nhiên cho đến khi prompt caching xuất hiện, mỗi request đều tính tiền và tính thời gian cho toàn bộ phần đó.
Bản chất của vấn đề nằm ở kiến trúc decoder-only Transformer. Khi model xử lý một prefix, nó tính ra các ma trận Key và Value cho từng token ở mỗi tầng attention; tập ma trận này chính là KV cache. Nó được dùng để suy luận từng token đầu ra một cách hiệu quả nhờ cơ chế autoregressive. Nhưng thông thường sau khi response kết thúc, KV cache bị xoá khỏi bộ nhớ GPU. Nếu cùng prefix quay lại ở request sau, model sẽ phải tính lại từ đầu — đây là lãng phí khổng lồ về cả compute lẫn tiền bạc.
Prompt caching là lời đáp đơn giản: giữ KV cache của prefix trong bộ nhớ GPU (hoặc trong một tier lưu trữ nhanh nào đó) một vài phút, gắn với một khoá xác định bằng nội dung. Khi request tiếp theo đến, chỉ cần so khớp tiền tố để tái nạp KV cache, rồi model bắt đầu sinh từ điểm khác biệt. Chi phí được tính tiền khác: phần cache write có thể đắt hơn bình thường (Claude tính 1.25x giá input token), phần cache read cực rẻ (0.1x giá input token trên Claude), và latency giảm mạnh.
Khác biệt cốt lõi so với Semantic Caching
Semantic caching (bài 1018) hoạt động ở tầng ứng dụng: nhận một prompt, encode thành vector, tìm trong Redis hoặc vector DB một prompt đã hỏi trước đây có độ tương đồng cao, rồi trả về cached response — bỏ qua model. Prompt caching hoạt động ở tầng inference: model vẫn sinh response mới, nhưng bỏ qua việc tính lại KV cache cho prefix. Hai kỹ thuật này không thay thế nhau, chúng bổ sung nhau. Semantic cache bắt những câu hỏi lặp lại nguyên vẹn; prompt cache tối ưu cho 99% request còn lại mà vẫn chia sẻ chung system prompt và tool schema.
2. Kiến trúc KV Cache bên trong Transformer — để hiểu cái gì đang được cache
Trước khi so sánh ba provider, cần một bức tranh rõ ràng về cái thực sự được tái sử dụng. Trong một layer attention, với input là chuỗi token có độ dài L, model tính ba ma trận Q, K, V. Trong inference autoregressive, chỉ có Q của token hiện tại cần tính mới, còn K và V của tất cả token trước đó có thể giữ nguyên. Đây là lý do KV cache tồn tại ngay cả trong một request đơn: nhờ nó mà sinh token thứ N không tốn chi phí O(N²) mà chỉ O(N). Prompt caching chỉ đơn giản là đưa KV cache vượt ra khỏi ranh giới một request, cho nó sống lâu hơn và dùng chung giữa nhiều request.
graph TB
Req1["Request 1
prefix P + query Q1"] --> P1["Tính KV cho P
đắt và chậm"]
P1 --> Gen1["Sinh response với Q1"]
Gen1 --> Save["Lưu KV(P) vào cache store
khoá = hash(P), TTL 5 phút"]
Save --> Req2["Request 2
prefix P + query Q2"]
Req2 --> Match{"So khớp prefix
hash(P) trùng?"}
Match -- Hit --> Load["Tái nạp KV(P)
từ bộ nhớ GPU"]
Match -- Miss --> P2["Tính KV(P) lại từ đầu"]
Load --> Gen2["Sinh response với Q2
đã bỏ qua compute của P"]
P2 --> Gen2
style P1 fill:#e94560,stroke:#fff,color:#fff
style Load fill:#4CAF50,stroke:#fff,color:#fff
style Match fill:#0f3460,stroke:#fff,color:#fff
style Save fill:#2c3e50,stroke:#fff,color:#fff
Vòng đời một prefix trong prompt cache
Có ba chi tiết quan trọng ẩn trong sơ đồ này mà mọi engineer cần ghi nhớ. Một là cache key được tính bằng nội dung: chỉ cần khác một token, chỉ cần thay đổi thứ tự hai câu, hash sẽ khác và cache sẽ miss. Hai là cache phải là prefix đúng nghĩa: không thể cache một đoạn ở giữa prompt rồi hy vọng match. Ba là cache được lưu trên GPU hoặc tier gần GPU, không phải trên đĩa từ xa — TTL ngắn là điều kiện bắt buộc để mô hình kinh tế hoạt động được.
3. Ba cách làm khác nhau: Claude, OpenAI, Gemini
Đây có lẽ là phần hữu ích nhất cho ai đang chọn provider. Ba công ty cài đặt prompt caching theo ba triết lý khác nhau — hiểu được sự khác biệt này giúp bạn thiết kế lớp trừu tượng phía client sao cho tận dụng tốt từng nền tảng.
| Khía cạnh | Claude (Anthropic) | OpenAI | Gemini (Google) |
|---|---|---|---|
| Kích hoạt | Thủ công: gắn cache_control vào block |
Tự động: mọi request ≥ 1024 token | Hai chế độ: implicit (auto) và explicit (API) |
| Ngưỡng tối thiểu | 1024 token (Sonnet/Opus), 2048 (Haiku) | 1024 token | 4096 token (explicit), 1024 (implicit) |
| TTL | 5 phút (mặc định) hoặc 1 giờ (trả thêm phí) | 5-10 phút, không kiểm soát được | Khả cấu hình (phút đến giờ) cho explicit |
| Giá cache write | 1.25x giá input (5 phút), 2x (1 giờ) | Bằng giá input bình thường | Rẻ hơn input bình thường với explicit cache |
| Giá cache read | 0.1x giá input (giảm 90%) | 0.5x giá input (giảm 50%) | ~0.25x giá input + phí lưu trữ theo giờ |
| Số breakpoint | Tối đa 4 breakpoint trong một request | 1 breakpoint prefix duy nhất | 1 cache object, tham chiếu bằng ID |
| Cache key visibility | Ẩn, tự quản lý | Ẩn, tự quản lý | Lộ ra ngoài: bạn nhận được name của cache |
3.1. Claude: Cache breakpoint thủ công, linh hoạt nhất
Anthropic chọn con đường "opt-in thủ công": bạn phải đánh dấu các block bạn muốn cache bằng trường cache_control. Đổi lại, bạn được đặt tới bốn cache breakpoint trong một request, nghĩa là bạn có thể tách system prompt, tool schema, tài liệu tham khảo và vài turn đầu của hội thoại vào các tầng cache riêng biệt. Mỗi breakpoint tương ứng một prefix hash, và Claude sẽ thử khớp từ breakpoint cuối cùng về đầu — ai trùng trước thì được cache hit.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=[
{
"type": "text",
"text": "Bạn là một trợ lý kỹ sư hệ thống...",
"cache_control": {"type": "ephemeral"} # breakpoint 1
},
{
"type": "text",
"text": TAI_LIEU_THAM_KHAO_DAI_50K_TOKEN,
"cache_control": {"type": "ephemeral"} # breakpoint 2
}
],
tools=[...], # tự động nằm trong cache của breakpoint gần nhất
messages=[
{"role": "user", "content": "Hãy tóm tắt tài liệu tôi vừa gửi."}
]
)
print(response.usage)
# cache_creation_input_tokens: 51234 (lần đầu — write)
# cache_read_input_tokens: 0
# input_tokens: 23 (chỉ phần không cache)
Response usage trả về ba trường riêng biệt: cache_creation_input_tokens (token đã được ghi vào cache), cache_read_input_tokens (token đã đọc lại từ cache) và input_tokens (phần không thuộc cache). Đây là mỏ vàng cho quan sát chi phí — chỉ cần parse ba trường này là bạn biết chính xác cache đang hoạt động ra sao.
Chiến lược đặt breakpoint cho Claude
Quy tắc vàng: đặt breakpoint ở những nơi có khả năng chia sẻ cao nhất và thay đổi ít nhất. Thứ tự từ ổn định xuống biến đổi: system prompt → tool schema → tài liệu tham khảo → memory → user query. Tránh đặt breakpoint vào chính user message — nó thay đổi mỗi lượt, cache sẽ miss ngay. Với coding agent, pattern phổ biến nhất là đặt 2 breakpoint: một sau system prompt, một sau tool list.
3.2. OpenAI: Cache tự động, ít lựa chọn nhưng dễ dùng
OpenAI đi hướng ngược lại: mọi request ≥ 1024 token đều tự động được cache mà không cần client làm gì. Hệ thống router của họ băm prefix theo đơn vị 128 token, tìm trong một bảng hash toàn cụm, và nếu trùng thì tái sử dụng. Bạn nhận được tín hiệu qua trường prompt_tokens_details.cached_tokens trong response. Không có API để chọn cái gì cache, không có breakpoint, không có TTL cấu hình.
from openai import OpenAI
client = OpenAI()
resp = client.chat.completions.create(
model="gpt-5",
messages=[
{"role": "system", "content": SYSTEM_PROMPT_DAI_5K},
{"role": "user", "content": "Câu hỏi 1"},
]
)
# resp.usage.prompt_tokens_details.cached_tokens = 0 (lần đầu)
resp = client.chat.completions.create(
model="gpt-5",
messages=[
{"role": "system", "content": SYSTEM_PROMPT_DAI_5K},
{"role": "user", "content": "Câu hỏi 2"},
]
)
# resp.usage.prompt_tokens_details.cached_tokens = 5100 (hit)
Ưu điểm: không có công việc tích hợp, mọi ứng dụng hiện có tự động hưởng lợi. Nhược điểm: cache được chia sẻ toàn cụm, có thể bị evict bất cứ lúc nào; không có cách nào đảm bảo một prefix cụ thể sẽ còn ở đó trong 5 phút nữa. Với agent có traffic đều thì không sao, nhưng với các workload burst (agent chạy theo lịch, cron, hoặc một user đơn lẻ gọi ít lần/giờ) bạn sẽ thấy tỷ lệ hit thấp đáng kể.
3.3. Gemini: Cache là một đối tượng có tên
Google chọn lối tiếp cận "first-class object": bạn dùng cachedContents.create để tạo một cache object có tên và TTL, sau đó chuyền tên đó vào các request sinh sau. Về mặt kỹ thuật, cache này có thể được lưu trong tier nhanh hơn và sống lâu hơn ephemeral cache thông thường — trade-off là bạn phải trả phí lưu trữ theo giờ cho tới khi xoá.
from google import genai
client = genai.Client()
# Bước 1: tạo cache
cache = client.caches.create(
model="gemini-2.5-pro",
config={
"system_instruction": "Bạn là trợ lý phân tích pháp luật...",
"contents": [...], # 200K token tài liệu
"ttl": "3600s", # 1 giờ
}
)
print(cache.name) # "cachedContents/legal-briefs-fabc42"
# Bước 2: dùng cache nhiều lần
resp = client.models.generate_content(
model="gemini-2.5-pro",
contents="Tóm tắt điểm quan trọng nhất của tài liệu",
config={"cached_content": cache.name}
)
Gemini thích hợp nhất cho các workload có một cơ sở tri thức rất lớn, dùng đi dùng lại: ví dụ một agent hỗ trợ pháp luật luôn tham chiếu cùng bộ luật, hoặc một agent customer support luôn tham chiếu cùng knowledge base. Với các workload mà prefix thay đổi theo user, mô hình này kém hiệu quả hơn Claude vì phí quản lý cache đắt hơn phí đọc từ cache.
4. Kinh tế học của prompt cache — khi nào cache, khi nào đừng
Một câu hỏi thực tế hay bị bỏ qua: có phải cứ cache là rẻ hơn không? Câu trả lời là không. Cache write trên Claude đắt hơn input thường 25% ở chế độ 5 phút và 100% ở chế độ 1 giờ. Nếu bạn cache một prefix rồi chỉ gọi một lần trong 5 phút sau đó, bạn đang trả nhiều hơn bình thường. Điểm hoà vốn phụ thuộc vào số lần đọc lại trung bình của cùng một prefix — chính là reuse ratio.
Công thức đơn giản để quyết định có cache hay không, tính trên một prefix có độ dài T token, với giá input = 1 đơn vị chuẩn:
Không cache: chi phí = T * N (N là số lần gọi trong TTL)
Cache 5 phút: chi phí = 1.25T + 0.1T * (N-1)
Cache 1 giờ: chi phí = 2.00T + 0.1T * (N-1)
Điểm hoà vốn cache 5 phút: N >= 1.28
Điểm hoà vốn cache 1 giờ: N >= 2.11
Kết luận ngay: chỉ cần prefix được dùng lại từ 2 lần trở lên trong TTL, cache 5 phút luôn rẻ hơn. Cache 1 giờ cần ít nhất 3 lần reuse để có lợi. Điều này thay đổi bức tranh thiết kế: thay vì lo "cache có đủ hit không", câu hỏi đúng là "làm sao đảm bảo prefix được gọi ít nhất 2 lần trong 5 phút". Đây là lý do phần tiếp theo — pattern "cache warming" — cực kỳ quan trọng.
5. Năm pattern prompt cache cho multi-agent production
Đây là các pattern đã được kiểm chứng ở các hệ agent chạy production mà tác giả đã quan sát hoặc tham gia xây. Mỗi pattern giải một bài toán khác nhau; trong một hệ thống đủ lớn, bạn sẽ thấy mình dùng cả năm.
5.1. Pattern "Static Foundation" — system prompt + tool schema
Đơn giản nhất và hiệu quả nhất. Đặt một cache breakpoint ngay sau system prompt và tool list. Đây là phần thay đổi gần như không bao giờ (trừ khi deploy version mới). Với một agent có 15K token system + tool, pattern này giảm chi phí input 85-90% mà không cần logic gì phức tạp. Đây nên là việc đầu tiên bạn làm khi áp dụng prompt cache.
5.2. Pattern "Document Pinning" — ghim tài liệu tham khảo trong session
Khi user upload một file PDF 50K token rồi hỏi 5-10 câu về file đó, cache toàn bộ nội dung file vào breakpoint thứ hai. Mọi câu hỏi tiếp theo đọc cache ở giá 0.1x. Trong Claude, pattern này thường được triển khai với một retry mechanism để tự động warm cache khi user vừa upload:
def pin_document(session_id: str, document: str):
"""Warm cache cho document vừa upload."""
client.messages.create(
model="claude-sonnet-4-6",
max_tokens=10,
system=[
{"type": "text", "text": BASE_SYSTEM, "cache_control": {"type": "ephemeral"}},
{"type": "text", "text": document, "cache_control": {"type": "ephemeral"}},
],
messages=[{"role": "user", "content": "ACK"}]
)
# Không quan tâm response, chỉ cần cache được ghi
5.3. Pattern "Conversation Extend" — turn rolling với cache memory
Với hội thoại dài, kỹ thuật là đặt cache breakpoint vào turn áp chót, không phải turn cuối. Khi turn mới đến, breakpoint của lần gọi trước đã ở vị trí "turn cuối cũ" và vẫn trùng khớp với prefix mới, nên cache hit. Mỗi lần gọi lại dịch breakpoint lên một turn. Pattern này giữ tỷ lệ hit cao ngay cả trong hội thoại 50+ turn, với điều kiện khoảng cách giữa các turn dưới 5 phút.
sequenceDiagram
participant U as User
participant A as Agent
participant M as Claude API
U->>A: Turn 1
A->>M: system + tools [BP1] + turn1 [BP2]
M-->>A: cache_write(BP1 + BP2)
U->>A: Turn 2
A->>M: system + tools [BP1] + turn1 + turn2 [BP2]
M-->>A: cache_read(BP1 + BP2 phần chung) + write phần mới
U->>A: Turn 3
A->>M: system + tools [BP1] + turn1 + turn2 + turn3 [BP2]
M-->>A: cache_read tối đa
Rolling breakpoint giữ hit ratio cao trong hội thoại dài
5.4. Pattern "Agent Fan-out" — một prefix, nhiều worker
Khi một orchestrator tạo ra N subagent song song, tất cả có cùng context và tool khác nhau chỉ ở phần "phân vai". Cache prefix chung ở breakpoint 1, phần role-specific ở breakpoint 2. Mỗi subagent chỉ trả tiền 0.1x cho phần chung và 1.25x cho phần riêng nhỏ. Đây là nơi prompt cache hoàn vốn nhanh nhất — N càng lớn, tiết kiệm càng nhiều.
5.5. Pattern "Cache-aware Routing" — LLM Gateway quyết định model theo cache state
Pattern nâng cao: LLM gateway (bài 1022) duy trì một bảng tracking prefix đã cache trên provider nào trong vài phút gần đây. Khi request mới đến, gateway chọn provider mà prefix đang còn hot thay vì provider rẻ nhất thuần túy. Điều này đặc biệt quan trọng trong multi-provider fallback: với một request có prefix 40K token, route sang một provider có cache hit rẻ hơn nhiều so với fallback sang provider khác dù giá danh nghĩa cao hơn.
6. Redis Edge Cache — tầng bổ trợ bên ngoài provider
Prompt cache của provider có một giới hạn không thể vượt: nó sống trong GPU cluster của họ, bạn không thấy và không kiểm soát được. Nhưng với các workload phân tán đa region hoặc có mô hình truy cập đặc thù, bạn có thể xây một tầng Redis edge cache trước API provider để tận dụng cả hai thế giới.
graph TB
Agent["Agent Client"] --> Gateway["LLM Gateway
+ Semantic Router"]
Gateway --> L1{"L1: Semantic Cache
Redis Vector Set
(Bài 1018)"}
L1 -- Hit 10% --> Return["Trả response
không gọi model"]
L1 -- Miss --> L2["L2: Prompt Cache Warm Table
Redis Hash: prefix_hash -> last_seen"]
L2 --> ProvRoute{"Chọn provider
theo cache state"}
ProvRoute -->|prefix warm| Claude["Claude API
cache_read 0.1x"]
ProvRoute -->|prefix cold| OpenAI["OpenAI API
automatic cache"]
Claude --> CH["ClickHouse
usage log"]
OpenAI --> CH
Return --> CH
style L1 fill:#e94560,stroke:#fff,color:#fff
style L2 fill:#e94560,stroke:#fff,color:#fff
style ProvRoute fill:#0f3460,stroke:#fff,color:#fff
style CH fill:#2c3e50,stroke:#fff,color:#fff
Kiến trúc caching ba tầng: semantic + prompt warm tracking + provider native
Redis đóng hai vai trò riêng. Vai trò một là semantic cache (đã bàn ở bài 1018): chặn nguyên những prompt đã xuất hiện. Vai trò hai là prefix warm table: Redis Hash lưu SHA256(prefix) -> last_seen_timestamp. Khi một request đi qua gateway, ta tính hash prefix, tra bảng, và biết ngay prefix này có "còn nóng" không. Thông tin đó dùng để chọn provider và cũng dùng cho cache warmer job — một cronjob định kỳ gọi các prefix sắp hết hạn với max_tokens=1 để làm mới TTL mà không tốn compute đáng kể.
import redis, hashlib, time
r = redis.Redis(host="redis-edge.prod", decode_responses=True)
WARM_KEY = "prompt_cache:warm"
TTL_SECONDS = 240 # 4 phút, trước khi 5 phút hết hạn
def prefix_hash(system: str, tools: list) -> str:
h = hashlib.sha256()
h.update(system.encode())
for t in tools:
h.update(t["name"].encode())
h.update(t.get("description", "").encode())
return h.hexdigest()
def track(prefix_key: str):
r.hset(WARM_KEY, prefix_key, int(time.time()))
r.expire(WARM_KEY, 3600)
def is_warm(prefix_key: str) -> bool:
last = r.hget(WARM_KEY, prefix_key)
if not last: return False
return (time.time() - int(last)) < TTL_SECONDS
Đừng lưu KV cache thật vào Redis
Cám dỗ tự nhiên là "nếu Redis nhanh thế, sao không tự lưu KV cache trong Redis?" Câu trả lời: KV cache của các model lớn chiếm hàng chục MB cho vài nghìn token, và phải ở tier gần GPU để có ý nghĩa. Trừ khi bạn vận hành chính infrastructure inference (vLLM, TGI, SGLang), hãy để provider quản lý KV cache của họ. Redis chỉ nên lưu metadata: hash, timestamp, usage stats.
7. ClickHouse Analytics — đo và tối ưu cache hit ratio
Một team vận hành prompt cache nghiêm túc cần nhìn thấy ba số liệu mỗi giờ: cache hit ratio theo prefix, chi phí tiết kiệm ước tính, và prefix top 20 đang ngốn bộ nhớ nhất. ClickHouse là nơi tự nhiên để lưu dữ liệu này vì nó phù hợp với workload append-heavy có sẵn (mỗi request LLM là một event), và các truy vấn OLAP dạng group-by trên hàng chục triệu event trả kết quả trong giây.
CREATE TABLE llm_requests
(
request_id String,
timestamp DateTime64(3),
provider LowCardinality(String),
model LowCardinality(String),
prefix_hash FixedString(64),
input_tokens UInt32,
cache_read_tokens UInt32,
cache_write_tokens UInt32,
output_tokens UInt32,
latency_ms UInt32,
cost_usd Float64,
agent_name LowCardinality(String),
tenant_id String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(timestamp)
ORDER BY (tenant_id, agent_name, prefix_hash, timestamp)
TTL timestamp + INTERVAL 180 DAY;
Với schema này, cache hit ratio trong 24 giờ qua chỉ là một truy vấn:
SELECT
agent_name,
sum(cache_read_tokens) AS read,
sum(cache_write_tokens) AS write,
sum(input_tokens) AS fresh,
round(read / (read + write + fresh) * 100, 2) AS hit_ratio_pct,
round(sum(cost_usd), 2) AS actual_cost,
round(sum(read) * 10 / 1e6 + sum(fresh) * 1 / 1e6 * 3, 2) AS cost_without_cache
FROM llm_requests
WHERE timestamp >= now() - INTERVAL 1 DAY
GROUP BY agent_name
ORDER BY actual_cost DESC;
Một materialized view có thể tự động rollup các số liệu này theo giờ, cho dashboard real-time với chi phí gần bằng không. Xa hơn, bạn có thể join bảng llm_requests với một bảng prompt_versions để biết version nào của system prompt gây cache miss — một tín hiệu sớm cho biết có ai đó vừa deploy đổi system prompt mà không thông báo.
8. Các failure mode hay gặp và cách né
Nếu bạn áp dụng prompt cache một vài tuần, bạn sẽ va vào ít nhất ba trong số các vấn đề sau. Tác giả từng mất vài ngày debug cho mỗi loại, nên ghi lại đây để người sau tiết kiệm thời gian.
1. Cache miss thầm lặng do non-determinism trong prompt builder
Nguyên nhân phổ biến nhất: một hàm build system prompt gọi datetime.now() hoặc iterate một dict không được sort, hoặc nhét một ID ngẫu nhiên vào header. Kết quả là hash prefix thay đổi mỗi request, cache không bao giờ hit. Biện pháp: viết test unit khóa prefix thành fixture, chạy hai lần, assert bằng.
2. Cache write quá tay do đặt breakpoint sai chỗ
Đặt breakpoint sau user message sẽ làm mỗi turn đều write cache mà không bao giờ read. Chi phí gấp 1.25x liên tục mà không có lợi ích. Cách phát hiện: xem tỷ lệ cache_creation / cache_read trong usage — con số này nên dưới 0.1 trong agent ổn định. Nếu gần 1, bạn đang write mà không read.
3. Cache invalidation do tool schema biến đổi ngầm
Một số framework tự sắp xếp tool theo thứ tự đăng ký, và thứ tự này có thể thay đổi khi import khác nhau (Python import order không tất định). Hệ quả: cùng một agent, cùng một deploy, nhưng hai request có cache khác nhau vì tool list khác thứ tự. Giải pháp: luôn sorted(tools, key=lambda t: t["name"]) trước khi gửi, ngay cả khi tốn vài micro giây.
4. Cache hit ratio giảm đột ngột sau A/B test
Khi bạn chạy A/B test bằng cách thêm một câu vào system prompt cho 50% traffic, bạn vô tình tách một prefix đang hot thành hai prefix lạnh. Cache hit ratio tụt một nửa ngay trong giờ đầu. Cách né: A/B test ở tầng sau breakpoint cache, ví dụ trong user message prefix hoặc trong tool choice, không trong system prompt.
9. Benchmark thực tế — prompt cache đáng giá bao nhiêu với agent của bạn
Những con số dưới đây được đo trên một coding agent nội bộ tác giả tham gia, sử dụng Claude Sonnet với 18K token system prompt, 22K token tool schema, trung bình 8K token tài liệu tham khảo mỗi phiên, và khoảng 15 turn mỗi phiên. Hai cấu hình được so sánh: không cache (baseline) và cache với hai breakpoint (system+tool, reference docs).
| Chỉ số | Không cache | Có cache 2 BP | Cải thiện |
|---|---|---|---|
| Chi phí input/ phiên (USD) | $0.452 | $0.063 | -86% |
| Time to first token (ms) | 2840 | 610 | -78% |
| Latency tổng trung bình (ms) | 8920 | 4180 | -53% |
| Hit ratio (turn từ 2 trở đi) | - | 94.2% | - |
| Throughput agent đồng thời | 22 | 41 | +86% |
Điểm đáng chú ý nhất không phải là con số tiết kiệm chi phí (dù rất lớn), mà là ảnh hưởng đến throughput. Khi TTFT giảm từ 2.8s xuống 0.6s, một worker có thể phục vụ nhiều request hơn trong cùng thời gian. Ở quy mô production, việc này đồng nghĩa bạn có thể giảm số worker đi gần một nửa mà vẫn giữ SLO — một khoản tiết kiệm cơ sở hạ tầng vượt xa tiền model.
10. Prompt Cache vs Long Context — khi nào dùng cái gì
Một câu hỏi hay xuất hiện sau khi đội vận hành đã quen với prompt cache: "Nếu model có 1M context, tại sao không nhét mọi thứ vào context và dựa vào cache?" Câu trả lời: đây là một pattern hợp lý, nhưng nó có một loại chi phí mà prompt cache không xoá được — attention vẫn phải chạy qua toàn bộ context ở mỗi lần generate. KV cache giúp tính K/V nhanh ở lần đầu, nhưng decoding vẫn phải attend qua toàn bộ KV mỗi bước token. Với 500K token trong context, mỗi token output mất nhiều ms hơn so với context 20K — bất kể cache hit.
Quy tắc thực dụng: dùng long context + cache khi bạn cần toàn bộ thông tin chính xác và không thể retrieve một cách chọn lọc (ví dụ: phân tích một hợp đồng pháp luật từ đầu tới cuối); dùng RAG + cache khi bạn có thể tách tri thức và chỉ nạp phần liên quan (ví dụ: coding agent tra API reference). Đừng bỏ qua RAG chỉ vì long context tồn tại — trong 95% use case, RAG + cache 20K prefix vẫn rẻ và nhanh hơn 500K context + cache.
11. Những gì sẽ đến trong 2026-2027
Prompt caching đang phát triển nhanh, và một số hướng có thể thay đổi cách bạn thiết kế hệ thống trong vài quý tới.
12. Checklist triển khai prompt cache cho agent của bạn
Nếu bạn đã đọc đến đây và muốn áp dụng ngay tuần này, đây là trình tự tối thiểu tác giả đề xuất.
- Đo trước khi tối ưu. Log
usage.input_tokenscủa mọi request vào ClickHouse trong 48 giờ. Tìm top 10 agent có chi phí cao nhất — đây là nơi cache có ROI tốt nhất. - Ghim system prompt + tool list vào breakpoint 1 duy nhất. Đừng làm gì phức tạp hơn trước khi thấy hit ratio > 80% cho pattern này.
- Thêm test unit khoá prefix. Hash của prefix phải ổn định giữa các lần build. Test này là tuyến phòng thủ chính chống cache miss thầm lặng.
- Đo chi phí cache write vs cache read. Nếu tỷ lệ write/read > 0.15 sau 24 giờ, bạn đang đặt breakpoint sai chỗ.
- Mở breakpoint 2 cho tài liệu tham khảo động. Chỉ làm sau khi breakpoint 1 đã ổn định — đây là nơi dễ sai.
- Xây cache warmer cho workload burst. Cronjob 4 phút gọi các prefix quan trọng với
max_tokens=1để giữ TTL. - Thêm ClickHouse dashboard. Ba biểu đồ: hit ratio theo agent, chi phí tiết kiệm hàng ngày, top prefix hash theo usage.
- Giám sát regression khi deploy. Mỗi lần deploy đổi system prompt, theo dõi hit ratio 30 phút đầu — tụt > 20% là tín hiệu prefix thay đổi ngoài ý muốn.
13. Kiến trúc tham khảo end-to-end
graph TB
subgraph Client["Agent Client Layer"]
A1[Coding Agent]
A2[Research Agent]
A3[Support Agent]
end
subgraph Gateway["LLM Gateway"]
PM[Prompt Builder
deterministic]
PH[Prefix Hasher]
SC[Semantic Cache
Redis Vector Set]
WT[Warm Table
Redis Hash]
PR[Provider Router]
end
subgraph Providers["Providers with native cache"]
CL[Claude API
4 breakpoints]
OA[OpenAI API
auto cache]
GM[Gemini API
named cache]
end
subgraph Observability["Observability"]
CH[(ClickHouse
llm_requests)]
MV[Materialized Views
hourly rollups]
DB[Dashboards
hit ratio + cost]
end
Client --> PM
PM --> PH
PH --> SC
SC -- miss --> WT
SC -- hit --> A1
WT --> PR
PR --> CL
PR --> OA
PR --> GM
CL --> CH
OA --> CH
GM --> CH
CH --> MV
MV --> DB
style SC fill:#e94560,stroke:#fff,color:#fff
style WT fill:#e94560,stroke:#fff,color:#fff
style PR fill:#0f3460,stroke:#fff,color:#fff
style CH fill:#2c3e50,stroke:#fff,color:#fff
style MV fill:#2c3e50,stroke:#fff,color:#fff
Kiến trúc multi-agent tận dụng đồng thời prompt cache provider, Redis edge cache và ClickHouse analytics
Kiến trúc này không phải là ý tưởng suông. Nó là tổng hợp các pattern đã chạy production ở các đội ngũ tác giả quan sát trong 12 tháng qua. Ba tầng semantic cache → prompt cache → provider native hoạt động bổ sung cho nhau: semantic cache bắt 5-15% request lặp lại hoàn toàn (ROI: 100% — không gọi model), prompt cache warm table giúp định tuyến để tỷ lệ provider cache hit đạt 80-95% cho phần còn lại, và ClickHouse giữ bức tranh dài hạn để team sản phẩm thấy được giá trị của mỗi tầng.
14. Kết luận — prompt cache là cái bật mặc định cho agent 2026
Nếu phải chọn một tối ưu có ROI cao nhất trên một giờ công sức cho một agent production năm 2026, tác giả sẽ không ngần ngại chọn prompt caching. Nó không đòi hỏi đổi kiến trúc lớn, không can thiệp vào chất lượng model, và không có nhược điểm về tính đúng đắn. Chỉ cần đặt một cache breakpoint đúng chỗ, bạn đã giảm 80% chi phí input và một nửa latency cho 95% request. Không nhiều tối ưu nào trong hệ thống AI đạt được tỷ lệ đó.
Tuy nhiên, điểm quan trọng nhất không phải ở con số tiết kiệm — mà ở cái nó mở ra. Khi chi phí 20K token system prompt gần như bằng không sau lần đầu, bạn có thể viết hướng dẫn dài hơn, chi tiết hơn, có nhiều ví dụ few-shot hơn, kèm theo CLAUDE.md dày và tool schema giàu mô tả — những thứ trực tiếp cải thiện chất lượng agent. Prompt cache không chỉ là một tối ưu chi phí: nó mở khoá một không gian thiết kế mới cho những agent phức tạp và chính xác hơn, vì nó biến chi phí cố định của việc "dạy" model thành thứ miễn phí kể từ lần sử dụng thứ hai.
Kết hợp với Redis edge cache cho tầng semantic và prefix warmth tracking, cộng với ClickHouse cho observability, bạn có một stack hoàn chỉnh sẵn sàng cho quy mô multi-agent production. Đây là base layer nên có trước khi bàn đến các tối ưu phức tạp hơn như routing đa model, dynamic tool selection, hay memory architecture phức hợp. Ở năm 2026, prompt caching không còn là lựa chọn — nó là mặc định.
Nguồn tham khảo
- Anthropic - Prompt Caching Documentation
- OpenAI - Prompt Caching Guide
- Google - Gemini API Context Caching
- Anthropic Blog - Announcing Prompt Caching
- vLLM - Automatic Prefix Caching
- SGLang - RadixAttention and Efficient KV Cache Reuse
- CacheBlend - Fast Large Language Model Serving for RAG with Cached Knowledge Fusion
- ClickHouse - MergeTree Engine Documentation
- Redis 8 - Vector Sets Documentation
- anhtu.dev - MCP Giao thức Kết nối Vạn năng cho Hệ thống AI Multi-Agent 2026
- anhtu.dev - Semantic Caching cho LLM Agents 2026
- anhtu.dev - LLM Gateway 2026
Redis 8 Vector Sets 2026 - Kiến trúc Native Vector Search với HNSW, Quantization Q8/BIN và Hybrid Filter cho Multi-Agent AI
Agent Router & Model Cascading 2026 - Kiến trúc Routing Thông minh cho Multi-Agent với Semantic Classifier, Bandit Feedback, Redis và ClickHouse
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.