☰ Mục lục series
Kinh nghiệm kỹ thuật (Engineering)

Bài học từ việc xây dựng Claude Code: Prompt caching là tất cả

Bản dịch từ claude.dev/blog/lessons-from-building-claude-code-prompt-caching-is-everything/

Tác giả bài gốc: Thariq Shihipar (Member of Technical Staff). Đăng ngày 30/04/2026 trên claude.dev.

Trong kỹ thuật, người ta thường nói rằng “cache chi phối mọi thứ quanh mình”, và quy luật ấy cũng đúng với agent.

Những sản phẩm chạy agent trong thời gian dài như Claude Code trở nên khả thi nhờ prompt caching, cho phép chúng tôi tái sử dụng phần tính toán từ các lượt trao đổi trước đó, đồng thời giảm đáng kể latency và chi phí.

Ở Claude Code, chúng tôi xây dựng toàn bộ harness xoay quanh prompt caching. Tỷ lệ cache hit cao với prompt giúp giảm chi phí và cho phép chúng tôi đặt giới hạn tốc độ (rate limit) rộng rãi hơn cho các gói thuê bao. Vì vậy, chúng tôi thiết lập cảnh báo cho tỷ lệ cache hit của prompt và công bố sự cố nghiêm trọng (SEV) nếu tỷ lệ này quá thấp.

Đây là những bài học (thường trái với trực giác) mà chúng tôi rút ra khi tối ưu prompt caching ở quy mô lớn.

Tổ chức prompt để tận dụng cache

Bố cục system prompt từ trên xuống: chỉ dẫn hệ thống cơ bản và tool (cache toàn cục), CLAUDE.md và bộ nhớ (cache theo dự án), trạng thái session như môi trường, MCP và phong cách đầu ra (cache theo session), rồi đến các message, tăng dần sau mỗi lượt.

System prompt của Claude Code được tổ chức sao cho những phần ổn định vẫn nằm trong cache và chỉ có bản thân cuộc hội thoại dài thêm qua từng lượt.

Prompt caching hoạt động bằng cách khớp tiền tố (prefix matching): API cache mọi thứ từ đầu request đến từng điểm ngắt cache_control. Điều này có nghĩa là thứ tự sắp xếp nội dung cực kỳ quan trọng: bạn muốn càng nhiều request có chung tiền tố càng tốt.

Cách tổ chức prompt cũng ảnh hưởng đến chất lượng đầu ra, chứ không chỉ cache hit — các nguyên tắc cơ bản của prompt engineering là nửa còn lại của việc này.

Cách tốt nhất là đặt nội dung tĩnh trước, nội dung động sau. Với Claude Code, thứ tự đó là:

  1. System prompt tĩnh và tool (cache toàn cục)
  2. CLAUDE.md (cache trong phạm vi một dự án)
  3. Context của session (cache trong phạm vi một session)
  4. Các message trong cuộc hội thoại

Bằng cách này, chúng tôi tối đa hóa số session cùng tận dụng được cache hit.

Nhưng cách làm này có thể mong manh đến bất ngờ. Chúng tôi từng phá vỡ thứ tự này vì nhiều lý do, chẳng hạn: đưa timestamp quá chi tiết vào system prompt tĩnh, xáo trộn thứ tự định nghĩa tool theo cách không xác định, và cập nhật tham số của tool (ví dụ, những agent mà tool Agent có thể gọi).

Dùng message để gửi cập nhật

Đôi khi thông tin bạn đưa vào prompt không còn cập nhật, ví dụ khi prompt chứa thời gian hoặc người dùng thay đổi một file. Bạn có thể muốn cập nhật prompt, nhưng việc đó sẽ dẫn đến cache miss và có thể khiến người dùng tốn khá nhiều tiền.

Thay vào đó, hãy cân nhắc liệu bạn có thể truyền thông tin này qua message ở lượt tiếp theo của agent hay không. Trong Claude Code, chúng tôi thêm thẻ <system-reminder> vào message tiếp theo của người dùng hoặc kết quả tool, kèm thông tin cập nhật cho model, để giúp giữ nguyên cache.

Đừng đổi model giữa session

Mỗi model có prompt cache riêng, và điều này có thể khiến phép tính chi phí prompt caching khá trái với trực giác.

Ví dụ, nếu cuộc hội thoại của bạn với Opus đã dài 100k token và bạn muốn hỏi một câu khá dễ trả lời, chuyển sang Haiku thực ra sẽ đắt hơn để Opus trả lời, vì chúng tôi phải xây dựng lại prompt cache cho Haiku.

Nếu cần đổi model, cách tốt nhất là dùng subagent. Tiếp nối ví dụ trên, bạn có thể triển khai một subagent yêu cầu Opus chuẩn bị message “bàn giao” cho một model khác về công việc cần hoàn thành. Chúng tôi thường làm vậy với các Explore agent của Claude Code, vốn dùng Haiku.

Không bao giờ thêm hoặc bỏ tool giữa session

Thay đổi bộ tool giữa cuộc hội thoại là một trong những cách phổ biến nhất khiến prompt caching bị phá vỡ. Nghe có vẻ hợp lý: bạn chỉ nên cung cấp cho model những tool mà bạn nghĩ nó cần ngay lúc này. Nhưng vì tool là một phần của tiền tố đã cache, thêm hoặc bỏ một tool sẽ làm mất hiệu lực cache của toàn bộ cuộc hội thoại.

Dùng Plan Mode để thiết kế phù hợp với cache

Plan Mode là một ví dụ rất hay về việc thiết kế tính năng dựa trên các ràng buộc của caching. Cách làm theo trực giác sẽ là: khi người dùng vào plan mode, thay bộ tool bằng một bộ chỉ có các tool đọc dữ liệu, nhưng làm vậy sẽ phá vỡ cache.

Thay vào đó, chúng tôi luôn giữ tất cả tool trong request và biến chính EnterPlanMode cùng ExitPlanMode thành tool. Khi người dùng bật Plan Mode, agent nhận một message hệ thống giải thích rằng nó đang ở Plan Mode và các chỉ dẫn cần tuân theo: khám phá codebase, không sửa file, và gọi ExitPlanMode khi lập kế hoạch xong. Các định nghĩa tool không bao giờ thay đổi.

Điều này còn có thêm một lợi ích: vì EnterPlanMode là tool mà model có thể tự gọi, nó có thể tự vào plan mode khi phát hiện một vấn đề khó, mà không làm gián đoạn cache.

Dùng tìm kiếm tool để hoãn tải thay vì loại bỏ

Nguyên tắc tương tự cũng áp dụng cho tool tìm kiếm tool của chúng tôi. Claude Code có thể tải hàng chục MCP tool, và đưa tất cả vào mọi request sẽ tốn kém, nhưng bỏ chúng giữa cuộc hội thoại lại phá vỡ cache.

Giải pháp của chúng tôi là defer_loading. Thay vì bỏ tool, chúng tôi gửi các bản khai báo tối giản (chỉ có tên tool, kèm defer_loading: true) để model có thể “khám phá” qua việc tìm kiếm tool khi cần. Schema đầy đủ của tool chỉ được tải khi model chọn tool đó. Cách này giữ tiền tố đã cache ổn định vì các bản khai báo tối giản luôn có mặt theo cùng một thứ tự.

Bạn cũng có thể dùng tool tìm kiếm tool qua API của chúng tôi để đơn giản hóa việc này.

Nén context mà không phá vỡ cache

Cách cơ chế nén context hoạt động với prompt caching. Trước: hệ thống và tool cùng nhiều lượt hội thoại, với context window gần đầy. Một lệnh gọi nén context được tách nhánh tái sử dụng cùng tiền tố để có cache hit với giá bằng một phần mười, thêm “Tóm tắt cuộc hội thoại này”, rồi trả về bản tóm tắt khoảng 20k token. Sau: hệ thống và tool, một compact_boundary, bản tóm tắt cuộc hội thoại, các file và context được đính kèm lại, cùng chỗ trống cho hội thoại mới.

Khi context window đầy, Claude Code tách nhánh một lệnh gọi dùng cache để tóm tắt cuộc hội thoại, rồi tiếp tục với bản tóm tắt thay cho các message ban đầu.

Nén context (compaction) là việc xảy ra khi context window không còn chỗ trống. Chúng tôi tóm tắt cuộc hội thoại đến thời điểm đó rồi tiếp tục một session mới với bản tóm tắt ấy.

Compaction tương tác với prompt caching theo những cách rất dễ làm sai. Để nén một cuộc hội thoại, bạn phải gửi toàn bộ hội thoại cho model để nó viết bản tóm tắt. Cách đơn giản nhất là thực hiện một lệnh gọi API riêng với system prompt riêng (chẳng hạn “tóm tắt nội dung này”) và không đính kèm tool, nhưng đó chính là cái bẫy chi phí. Prompt caching chỉ áp dụng khi tiền tố của request khớp với nội dung đã cache, từng byte một, ngay từ đầu. Cuộc hội thoại chính của bạn được cache dưới một system prompt và bộ tool; lệnh gọi tóm tắt dùng system prompt khác và không có tool, nên các tiền tố đã khác nhau ngay ở token đầu tiên và không phần cache nào được áp dụng. Cuối cùng, bạn phải trả toàn bộ giá input không dùng cache cho cả cuộc hội thoại đang gửi vào — và hội thoại càng dài (tức là ngay từ đầu bạn càng cần compaction), lệnh gọi đó càng đắt.

Giải pháp: tách nhánh an toàn cho cache

Khi chạy compaction, chúng tôi dùng system prompt, context của người dùng, context hệ thống và định nghĩa tool giống hệt cuộc hội thoại gốc. Chúng tôi đặt các message của cuộc hội thoại gốc ở đầu, rồi thêm prompt nén context dưới dạng một message mới của người dùng ở cuối.

Từ góc nhìn của API, request này gần như giống hệt request cuối của cuộc hội thoại gốc: cùng tiền tố, cùng tool, cùng lịch sử. Vì thế, tiền tố đã cache được tái sử dụng. Những token mới duy nhất chính là prompt nén context.

Tuy nhiên, điều này có nghĩa là chúng tôi cần dành một “vùng đệm nén context” (compaction buffer) để context window có đủ chỗ cho message nén context và các token đầu ra của bản tóm tắt.

Compaction khá phức tạp, nhưng may là bạn không cần tự trải qua những bài học này: dựa trên kinh nghiệm từ Claude Code, chúng tôi đã tích hợp compaction trực tiếp vào API, để bạn có thể áp dụng những cách làm này trong ứng dụng của mình.

Những bài học rút ra

Dưới đây là một vài cách làm mà chúng tôi thấy hữu ích để tối ưu prompt caching khi xây dựng agent:

  1. Prompt caching là phép khớp tiền tố. Bất kỳ thay đổi nào ở bất kỳ đâu trong tiền tố đều làm mất hiệu lực mọi thứ phía sau nó. Hãy thiết kế toàn bộ hệ thống xoay quanh ràng buộc này. Sắp xếp đúng thứ tự, phần lớn việc caching sẽ tự hoạt động mà bạn không cần làm thêm gì.
  2. Dùng message thay vì thay đổi system prompt. Bạn có thể muốn sửa system prompt để làm những việc như vào plan mode, thay đổi ngày tháng, v.v., nhưng thực ra chèn chúng vào message trong cuộc hội thoại sẽ tốt hơn.
  3. Đừng đổi tool hoặc model giữa cuộc hội thoại. Dùng tool để mô hình hóa việc chuyển trạng thái (như plan mode) thay vì thay đổi bộ tool. Hoãn tải tool thay vì loại bỏ chúng.
  4. Theo dõi tỷ lệ cache hit như cách bạn theo dõi uptime. Chúng tôi đặt cảnh báo khi cache bị gián đoạn và coi đó là sự cố. Chỉ vài điểm phần trăm trong tỷ lệ cache miss cũng có thể ảnh hưởng rất lớn đến chi phí và latency.
  5. Các thao tác tách nhánh cần dùng chung tiền tố với cuộc hội thoại gốc. Nếu cần chạy một phép tính phụ (nén context, tóm tắt, thực thi skill), hãy dùng các tham số an toàn cho cache giống hệt nhau để tận dụng cache hit trên tiền tố của cuộc hội thoại gốc.

Claude Code được xây dựng xoay quanh prompt caching ngay từ ngày đầu; để đạt kết quả tốt nhất khi xây dựng agent, chúng tôi khuyên bạn cũng nên làm như vậy.

Bắt đầu với Claude Code ngay hôm nay.

Bài viết này do Thariq Shihipar, thành viên đội ngũ kỹ thuật của nhóm Claude Code, thực hiện.

Nội dung gốc: claude.dev Blog © Anthropic, PBC, giấy phép đăng lại có sự cho phép của Anthropic. Bản dịch và ghi chú: Lê Văn Dũng.