·

Mở một system prompt (chỉ dẫn hệ thống) đã tồn tại 8 tháng của một coding agent trong production, bạn sẽ thường thấy một văn bản 3.000-5.000 token: nửa đầu là mô tả vai trò dài dòng viết từ ngày đầu dự án, giữa là hàng chục rule được thêm dần mỗi khi có bug mới, cuối là vài đoạn ví dụ copy từ agent khác "để chắc ăn". Không ai dọn dẹp nó vì không ai chắc dòng nào đang thực sự cần thiết — xóa nhầm một câu có thể làm agent hồi quy về hành vi cũ. Vấn đề là: system prompt này được gửi kèm MỌI request, hàng nghìn lần một ngày. Nếu 60% nội dung là văn hoa mỹ hoặc rule trùng lặp không mang thêm giá trị chỉ dẫn, bạn đang trả tiền — và tiêu tốn context window (cửa sổ ngữ cảnh) quý giá — cho rác. Bài này đưa ra một nguyên tắc thiết kế duy nhất để đo lường mọi quyết định về system prompt: mật độ chỉ dẫn tối đa trên mỗi token, và một bộ quy trình cụ thể để đạt được nó — từ nhận diện bloat, áp dụng cấu trúc gọn, đặc tả output format ngắn, phân bổ ngân sách token theo phần, đến đo lường hiệu quả bằng số liệu thật thay vì cảm tính.

Why System Prompts Become Bloated

Không có system prompt nào sinh ra đã cồng kềnh. Bloat (phình to) là kết quả của một quá trình tích lũy chậm, gần như vô hình, kéo dài qua nhiều tháng vận hành thực tế. Hiểu rõ cơ chế này quan trọng hơn việc chỉ biết "phải cắt gọn" — vì nếu không xử lý nguyên nhân gốc, prompt sẽ phình to trở lại chỉ sau vài sprint.

Bốn Cơ Chế Khiến System Prompt Phình To Theo Thời Gian

1. Thêm rule dần dần theo từng bug report. Đây là nguyên nhân phổ biến nhất. Một khách hàng report agent trả lời sai format, kỹ sư thêm một câu "Luôn trả lời theo format X". Tuần sau, một case khác xuất hiện, thêm câu tiếp. Sau sáu tháng, prompt có 40 câu rule rời rạc, phần lớn được viết để vá một sự cố cụ thể tại một thời điểm cụ thể, không ai còn nhớ ngữ cảnh gốc, và không ai dám xóa vì sợ bug cũ tái xuất. Đây chính là "patch accumulation" — vá lỗi bằng cách cộng dồn, không bao giờ trừ đi.

2. Không có ai chịu trách nhiệm dọn dẹp (no owner, no review cadence). Code có code review, có refactor sprint, có linter cảnh báo dead code. System prompt thường không có quy trình tương đương. Nó là một file text tự do, ai cũng có thể sửa, nhưng không ai có nhiệm vụ định kỳ rà soát và loại bỏ phần dư thừa. Kết quả là entropy (độ hỗn loạn) chỉ tăng, không bao giờ giảm.

3. Copy-paste từ agent khác "cho chắc". Khi xây agent mới, một cách nhanh là copy system prompt của agent cũ đã chạy ổn, rồi sửa vài chỗ. Nhưng agent cũ có thể có rule chỉ áp dụng cho use case của nó, không liên quan gì đến agent mới. Những rule này bị mang theo "cho chắc, biết đâu cần" — và không ai xóa vì không ai đủ tự tin khẳng định nó không cần thiết.

4. Chỉ dẫn "just in case" (phòng khi) chưa từng xảy ra. Đây là dạng bloat tinh vi nhất: kỹ sư tưởng tượng ra một tình huống agent có thể xử lý sai, rồi viết thêm rule để "phòng ngừa" — dù tình huống đó chưa từng xảy ra trong dữ liệu thực tế. Những rule phòng ngừa quá mức này không chỉ tốn token, mà đôi khi còn khiến agent trở nên rụt rè, hỏi lại quá nhiều, hoặc từ chối thực hiện các tác vụ hợp lệ vì bị "cảnh báo quá tay".

Vì Sao Bloat Nguy Hiểm Hơn Là Chỉ Tốn Tiền

Chi phí token là hậu quả dễ thấy nhất, nhưng không phải hậu quả nghiêm trọng nhất. Ba vấn đề khác âm thầm nguy hiểm hơn:

  • Loãng chỉ dẫn (instruction dilution): LLM (mô hình ngôn ngữ lớn) phải phân bổ "sự chú ý" (attention) cho toàn bộ context. Khi 5 rule quan trọng nằm lẫn giữa 35 rule thứ yếu, khả năng agent tuân thủ đúng rule quan trọng nhất giảm đi — không phải vì mô hình yếu, mà vì tín hiệu bị pha loãng.
  • Prompt caching (đệm prompt) bị phá vỡ thường xuyên hơn. System prompt dài và thường xuyên bị sửa (để thêm rule mới) nghĩa là cache prefix (tiền tố được cache) bị invalidate (làm mất hiệu lực) nhiều hơn, buộc bạn trả giá full-price cho input token thường xuyên hơn mức cần thiết.
  • Khó debug khi agent hành xử sai. Với 40 rule rời rạc, không ai đủ tự tin trả lời câu hỏi "rule nào đang chi phối hành vi này?" trong vòng vài phút. Việc điều tra một lỗi hành vi biến từ việc đọc 5 dòng thành việc đọc 5.000 token.

Mẹo

Trước khi thêm bất kỳ rule mới nào vào system prompt, tự hỏi: "rule này giải quyết một pattern lỗi lặp lại, hay chỉ vá một sự cố xảy ra một lần?" Nếu là sự cố một lần, cân nhắc xử lý ở tầng khác (validation code, guard rail phía ngoài) thay vì nhồi thêm vào system prompt — nơi mọi request đều phải trả giá cho nó vĩnh viễn.

The Principle of Maximum Instruction Density

Mật độ chỉ dẫn tối đa (maximum instruction density) là tiêu chí duy nhất bạn cần dùng để đánh giá bất kỳ câu chữ nào trong system prompt: mỗi token phải mang một lượng thông tin hành vi cụ thể, không thể suy ra từ token khác, và ảnh hưởng thực sự đến cách agent hành động. Nếu một câu có thể bị xóa mà không làm thay đổi hành vi agent trong bất kỳ tình huống nào, câu đó có mật độ chỉ dẫn bằng 0 — và nó vẫn đang tốn tiền mỗi request.

Công Thức Tư Duy: Giá Trị Trên Mỗi Token

Hãy hình dung mỗi câu trong system prompt như một dòng đầu tư: bạn trả một số token nhất định (chi phí), đổi lại agent thay đổi hành vi theo hướng bạn muốn (giá trị). Một câu văn hoa mỹ như "Bạn là một trợ lý AI cực kỳ thông minh, tận tâm và luôn nỗ lực hết mình để phục vụ người dùng một cách tốt nhất có thể" tốn khoảng 30-35 token nhưng không thay đổi hành vi agent theo bất kỳ cách đo lường được nào — mô hình đã "biết" nó là một trợ lý hữu ích từ pretraining, không cần được nhắc lại bằng ngôn ngữ hoa mỹ. Ngược lại, câu "Luôn gọi search_codebase trước khi mô tả cách code hiện tại hoạt động" chỉ tốn khoảng 15 token nhưng thay đổi hành vi agent một cách trực tiếp, có thể kiểm chứng.

Ba Loại "Rác Token" Cần Loại Bỏ Triệt Để

1. Ngôn ngữ hoa mỹ, tự giới thiệu dư thừa. Các câu như "Bạn là một AI vô cùng hữu ích", "Hãy luôn cố gắng hết sức", "Chúng tôi rất coi trọng sự hài lòng của khách hàng" không mang chỉ dẫn hành vi cụ thể nào. LLM hiện đại đã được huấn luyện để hữu ích mặc định; nhắc lại điều này bằng tính từ không làm nó hữu ích hơn.

2. Lặp ý dưới nhiều hình thức khác nhau. Rất phổ biến: một rule được viết ở đầu prompt theo cách này, rồi được diễn đạt lại ở cuối prompt theo cách khác — vì được thêm vào ở hai thời điểm khác nhau, bởi hai người khác nhau, không ai nhận ra đã có rule tương tự. Ví dụ "Luôn kiểm tra test trước khi commit" ở đoạn 2 và "Đừng bao giờ commit code chưa được test" ở đoạn 15 — hai câu, một ý, tốn gấp đôi token cho cùng một chỉ dẫn.

3. Giải thích lý do (rationale) khi không cần thiết. Có những trường hợp giải thích "vì sao" giúp mô hình generalize (tổng quát hóa) tốt hơn cho case chưa từng thấy — đây là ngoại lệ hợp lý, không phải rác. Nhưng phần lớn thời gian, câu giải thích dài dòng như "Điều này rất quan trọng vì nếu không làm vậy, hệ thống có thể gặp lỗi nghiêm trọng ảnh hưởng đến trải nghiệm người dùng" chỉ lặp lại mức độ quan trọng bằng lời, mà không thêm thông tin hành vi mới. Rule ngắn, trực tiếp, đặt đúng vị trí ưu tiên thường hiệu quả hơn.

Ví Dụ Trước/Sau

Trước (68 token):

You are an extremely helpful, friendly, and highly capable AI assistant
that has been carefully designed to help software engineering teams. It
is very important that you always try your best to be as helpful as
possible to the user in every single response you generate, no matter
what the request is.

Sau (12 token):

Senior software engineering assistant.

Không có thông tin hành vi nào bị mất — mô hình không cần được nhắc "hãy cố gắng hết sức" để cố gắng hết sức. Phần duy nhất còn giá trị thực (vai trò cụ thể: senior software engineering assistant, không phải "trợ lý AI nói chung") được giữ lại, súc tích.

Mẹo

Sau khi viết xong một câu trong system prompt, thử bài test sau: xóa câu đó đi, tưởng tượng agent nhận request khó nhất liên quan đến chủ đề của câu này — nếu bạn không thể chỉ ra một tình huống cụ thể mà thiếu câu đó sẽ khiến agent hành xử khác đi, câu đó không đạt tiêu chuẩn mật độ chỉ dẫn tối đa. Xóa nó.

Structural Patterns for Lean System Prompts

Mật độ chỉ dẫn cao không chỉ nằm ở việc chọn từ, mà còn ở cách tổ chức cấu trúc. Một system prompt được viết dưới dạng đoạn văn liên tục, dài dòng, buộc mô hình phải "đọc" toàn bộ văn xuôi để tìm ra rule nằm ở đâu. Một system prompt được cấu trúc tốt — heading rõ ràng, danh sách gạch đầu dòng, thứ tự ưu tiên minh bạch — giúp cả mô hình và con người bảo trì nó tìm thông tin nhanh hơn, với ít token hơn.

Heading Rõ Ràng Thay Cho Văn Xuôi Liên Tục

Sử dụng heading Markdown (#, ##) để phân chia system prompt thành các khối chức năng rõ ràng: Role (vai trò), Rules/Constraints (quy tắc/ràng buộc), Tools (công cụ), Output Format (định dạng đầu ra), Examples (ví dụ). Heading không chỉ giúp con người scan nhanh — nó còn giúp mô hình định vị chỉ dẫn theo nhóm chức năng, giảm khả năng nhầm lẫn giữa các loại rule khác nhau (ví dụ: rule về tool và rule về format bị trộn lẫn trong một đoạn văn dài).

Danh Sách Gạch Đầu Dòng Thay Cho Câu Ghép Dài

So sánh hai cách viết cùng một nội dung:

Văn xuôi (42 token):

Khi viết code, bạn nên luôn tuân theo style hiện có trong codebase, và
đồng thời bạn cũng cần đảm bảo viết comment cho những đoạn logic phức
tạp, nhưng không cần comment cho những đoạn code đã rõ ràng tự thân.

Danh sách (24 token):

- Match existing codebase style.
- Comment only non-obvious logic.

Danh sách gạch đầu dòng loại bỏ hoàn toàn các từ nối câu ("và đồng thời", "nhưng") không mang thông tin hành vi, chỉ đóng vai trò ngữ pháp. Mỗi rule đứng độc lập, dễ thêm/xóa/sửa từng dòng mà không ảnh hưởng câu khác — một lợi ích bảo trì quan trọng không kém lợi ích tiết kiệm token.

Priority Ordering — Thứ Tự Ưu Tiên Rõ Ràng

Khi có nhiều rule cùng áp dụng cho một tình huống, agent cần biết rule nào thắng khi có xung đột. Đánh số thứ tự ưu tiên rõ ràng (1, 2, 3...) hoặc gom rule theo nhóm "luôn luôn / thường thì / khi nghi ngờ" giúp giảm số token cần dùng để diễn đạt các câu điều kiện phức tạp kiểu "nếu X xảy ra thì làm A, trừ khi Y cũng xảy ra thì làm B thay vì A". Một danh sách có thứ tự ưu tiên rõ ràng thường ngắn hơn một đoạn văn cố gắng diễn đạt toàn bộ logic rẽ nhánh bằng câu chữ.

1. Never modify files outside the current git repository.
2. Run tests after any code change.
3. Prefer editing existing files over creating new ones.

Gộp Rule Liên Quan Thành Một Khối

Rule về cùng một chủ đề nhưng được viết rải rác ở nhiều nơi (vì được thêm vào ở nhiều thời điểm khác nhau) nên được gộp lại thành một khối duy nhất. Ví dụ, nếu có ba rule riêng biệt về việc dùng tool run_tests nằm ở ba vị trí khác nhau trong prompt, gộp chúng thành một mục con "Testing" dưới heading Rules — vừa giảm token do loại bỏ ngữ cảnh lặp lại, vừa giảm rủi ro mâu thuẫn ngầm giữa các rule tưởng như độc lập.

Loại Bỏ Ví Dụ Dư Thừa

Ví dụ minh họa (few-shot example) rất tốn token — mỗi ví dụ đầy đủ có thể chiếm 50-200 token. Nguyên tắc: chỉ giữ ví dụ khi (a) format output đủ phức tạp mà giải thích bằng lời không đủ rõ, hoặc (b) có một edge case dễ hiểu sai mà một ví dụ ngắn giải quyết triệt để hơn nhiều câu giải thích dài. Nếu ba ví dụ đang minh họa cùng một pattern, giữ ví dụ rõ nhất, xóa hai ví dụ còn lại — mô hình không cần lặp lại pattern ba lần để nắm được nó.

Mẹo

Áp dụng "quy tắc 20 block" của prompt caching (đệm prompt) khi cấu trúc lại: nếu system prompt của bạn dùng cache breakpoint (điểm ngắt cache), hãy nhóm phần ổn định nhất (role, rule cốt lõi) lên đầu và phần có khả năng thay đổi thường xuyên (ví dụ theo mùa, rule tạm thời) xuống cuối, ngay trước breakpoint. Cấu trúc rõ ràng theo heading giúp việc này dễ thực hiện và dễ review hơn nhiều so với một khối văn xuôi liên tục.

Output Format Specifications: Getting Precision Without Verbosity

Một trong những phần tốn token oan uổng nhất của nhiều system prompt là đặc tả định dạng đầu ra (output format specification). Nhiều đội viết cả một đoạn dài giải thích bằng lời cách agent nên trình bày câu trả lời — trong khi một schema ngắn hoặc một ví dụ mẫu tối giản thường đạt độ chính xác cao hơn với số token thấp hơn nhiều.

Vì Sao Giải Thích Bằng Lời Kém Hiệu Quả Hơn Schema

Ngôn ngữ tự nhiên vốn mơ hồ khi mô tả cấu trúc dữ liệu chính xác. Câu "hãy trả về kết quả dưới dạng JSON với các trường tên khách hàng, email và trạng thái đơn hàng" để lại nhiều khoảng trống diễn giải: tên trường viết camelCase hay snake_case? trạng thái đơn hàng là string tự do hay enum cố định? có field nào optional không? Agent phải "đoán" — và đôi khi đoán sai, gây lỗi downstream khi code của bạn parse response. Một JSON Schema ngắn loại bỏ hoàn toàn sự mơ hồ này bằng ít token hơn một đoạn giải thích đầy đủ các trường hợp trên.

So sánh:

Giải thích bằng lời (58 token):

Please respond with a JSON object. It should have a field called
"customer_name" which is a string, a field called "email" which is
also a string in valid email format, and a field called "status"
which should be one of the following values: "pending", "shipped",
"delivered", or "cancelled".

Schema ngắn (38 token):

{
  "customer_name": "string",
  "email": "string (email format)",
  "status": "pending | shipped | delivered | cancelled"
}

Trong nhiều trường hợp, cách hiệu quả nhất không phải viết schema trong system prompt mà sử dụng cơ chế structured output (đầu ra có cấu trúc) của chính API — tham số output_config.format với json_schema trên Claude, giúp mô hình bị ràng buộc để tuân theo schema một cách đảm bảo, thay vì chỉ "được nhắc" tuân theo schema qua văn bản trong system prompt. Khi dùng structured output ở tầng API, bạn không cần lặp lại schema đầy đủ trong system prompt nữa — chỉ cần một câu ngắn tham chiếu ("Output theo schema đã cấu hình") là đủ, tiết kiệm token cho mọi request.

Một Ví Dụ Mẫu Tối Giản Thay Vì Giải Thích Dài Dòng

Khi output format có cấu trúc lặp (ví dụ báo cáo nhiều mục, hoặc code review với nhiều finding), một ví dụ mẫu ngắn — chỉ đủ để minh họa pattern, không cần đầy đủ mọi trường hợp — thường truyền đạt cấu trúc hiệu quả hơn liệt kê từng quy tắc format bằng lời.

One line per finding:
`[severity] file:line — description`

Example: `[high] auth.py:42 — SQL query built via string concat, injection risk`

Bốn dòng này (khoảng 35 token) truyền đạt đầy đủ: cấu trúc từng dòng, thứ tự các trường, và một ví dụ cụ thể để loại bỏ mọi mơ hồ về format — hiệu quả hơn nhiều so với một đoạn văn dài giải thích "mỗi finding nên có mức độ nghiêm trọng, sau đó là vị trí file và số dòng, sau đó là mô tả ngắn gọn về vấn đề..."

Khi Nào Vẫn Cần Giải Thích Thêm Bằng Lời

Không phải mọi đặc tả format đều nên rút gọn về schema/ví dụ tối giản. Khi có logic điều kiện phức tạp về việc khi nào dùng format nào (ví dụ: "dùng bullet list nếu có trên 3 item, dùng đoạn văn nếu chỉ có 1-2 item"), một câu ngắn giải thích điều kiện là cần thiết và không thể thay bằng schema đơn thuần. Nguyên tắc vẫn là mật độ chỉ dẫn: giữ câu điều kiện này ngắn gọn, không lặp lại phần đã rõ ràng từ ví dụ.

Mẹo

Nếu API bạn dùng hỗ trợ structured output (đầu ra có cấu trúc) ở tầng tham số riêng (như output_config.format với json_schema), luôn ưu tiên cơ chế này hơn viết schema trong system prompt bằng văn bản. Nó vừa tiết kiệm token trên mọi request, vừa đảm bảo tuân thủ format chặt hơn so với chỉ dựa vào việc mô hình "đọc hiểu" chỉ dẫn văn bản.

Token Budget Allocation for System Prompt Sections

Một cách thực tế để kiểm soát bloat ngay từ đầu là đặt ra ngân sách token (token budget) cho từng phần của system prompt trước khi viết, giống như cách một sản phẩm đặt budget cho từng phần UI trước khi thiết kế. Không có ngân sách, mỗi phần có xu hướng phình to độc lập vì không ai cảm nhận được "chi phí cơ hội" của việc thêm một câu vào phần này so với phần khác.

Gợi Ý Tỷ Lệ Phân Bổ Theo Phần

Tỷ lệ dưới đây là điểm khởi đầu hợp lý cho một system prompt của coding/agentic assistant có độ phức tạp trung bình (tổng ngân sách ví dụ: 800-1.500 token cho toàn bộ system prompt — một mức đủ cho hầu hết agent thực tế mà không cần vượt quá 2.000 token):

Phần Tỷ lệ ngân sách Mục đích
Role definition (định nghĩa vai trò) 8-12% Xác định danh tính, phạm vi hoạt động của agent trong 1-3 câu
Core constraints/rules (ràng buộc & quy tắc cốt lõi) 30-40% Các rule hành vi quan trọng nhất, ảnh hưởng trực tiếp đến an toàn và đúng đắn
Tool usage guidance (hướng dẫn dùng tool) 15-20% Khi nào và cách nào dùng từng tool, đặc biệt các tool có side effect
Output format (định dạng đầu ra) 10-15% Schema/ví dụ mẫu tối giản cho định dạng phản hồi
Examples (ví dụ minh họa) 10-15% Chỉ giữ ví dụ cho case dễ hiểu sai, không lặp pattern đã rõ
Edge cases/safety (trường hợp biên & an toàn) 8-12% Các rule xử lý tình huống hiếm nhưng rủi ro cao

Tỷ lệ này không phải công thức cứng — một agent xử lý dữ liệu tài chính có thể cần tăng edge cases/safety lên 20%, còn một agent chat đơn giản có thể không cần phần tool usage guidance. Điểm quan trọng là một ngân sách rõ ràng, để khi một phần vượt tỷ lệ dự kiến, đó là tín hiệu cảnh báo cần rà soát trước khi merge thay đổi.

Cách Đo Lường Và Ép Buộc Ngân Sách Trong Thực Tế

Đo bằng cách đếm token thực tế của từng khối (không phải đếm ký tự hay từ — token count khác biệt đáng kể, đặc biệt với tiếng Anh kỹ thuật lẫn code). Với mỗi lần chỉnh sửa system prompt trong pull request, thêm bước kiểm tra: đếm token của từng phần theo heading, so với ngân sách đã định. Nếu một phần vượt ngân sách hơn 20%, yêu cầu người review giải thích lý do trước khi merge — tương tự cách một số team áp bundle size budget cho frontend code.

Về mặt tổ chức, cách khả thi nhất là tách system prompt thành các file/section riêng theo chức năng (ví dụ role.md, rules.md, tools.md, format.md) trong quá trình phát triển, rồi ghép lại khi build — giúp mỗi phần có "chủ" rõ ràng và dễ đo ngân sách độc lập, dù bản build cuối vẫn là một system prompt liền mạch.

Mẹo

Khi một rule mới cần thêm vào nhưng phần tương ứng đã sát ngân sách, đừng chỉ "thêm luôn cho chắc" — hãy buộc mình tìm một rule cũ trong cùng phần để rút gọn hoặc gộp trước khi thêm rule mới. Kỷ luật này (tương tự "one in, one out" trong quản lý tồn kho) là cách hiệu quả nhất để ngăn bloat tái diễn sau khi bạn đã dọn dẹp một lần.

Testing System Prompt Efficiency

Tối ưu system prompt mà không đo lường là tối ưu theo cảm tính — bạn có thể vô tình cắt mất một rule quan trọng trong lúc "gọn hóa", làm agent hồi quy về hành vi sai mà không hề biết cho đến khi khách hàng report. Kiểm thử hiệu quả system prompt cần hai trục đo song song: (1) token count trước/sau để định lượng mức tiết kiệm, và (2) chất lượng output trên một bộ golden test case (test case chuẩn) để xác nhận hành vi không đổi hoặc tốt hơn.

Trục Đo Thứ Nhất: Đếm Token Chính Xác

Đây là điểm quan trọng cần lưu ý ngay: tiktoken là tokenizer (bộ mã hóa token) của OpenAI, không phải của Claude. Dùng tiktoken để đếm token cho prompt sẽ dùng với Claude chỉ cho ra số liệu ước lượng gần đúng — có thể lệch 15-30% so với số token thật mà Claude API tính phí, tùy loại nội dung (code và tiếng Việt/tiếng Anh không phải tiếng Anh thuần thường lệch nhiều hơn). tiktoken vẫn có giá trị như một công cụ ước lượng nhanh, offline, không cần gọi API — hữu ích khi bạn đang lặp thử nhiều phiên bản prompt cục bộ và chỉ cần biết "phiên bản này có ngắn hơn phiên bản kia không" một cách tương đối. Nhưng để có số liệu chính xác dùng cho báo cáo, quyết định business, hoặc benchmark thật, luôn xác nhận lại bằng endpoint count_tokens của chính Anthropic SDK — đây là nguồn số liệu chính thức, phản ánh đúng tokenizer thực tế mà Claude dùng để tính phí.

Trục Đo Thứ Hai: Golden Test Case Để Bảo Vệ Chất Lượng

Golden test case là một bộ các cặp (input mẫu, kỳ vọng hành vi) được xác định trước — ví dụ: "với input X, agent phải gọi tool search_codebase" hoặc "với input Y (câu hỏi ngoài chủ đề), agent không được gọi bất kỳ tool code nào". Sau mỗi lần chỉnh sửa system prompt, chạy lại toàn bộ bộ test này với cả prompt cũ và prompt mới, so sánh kết quả. Nếu prompt mới làm một test case chuyển từ PASS sang FAIL, đó là dấu hiệu bạn đã cắt mất một chỉ dẫn có giá trị thực, không phải chỉ là "văn hoa mỹ" như tưởng.

Ví Dụ Code: So Sánh Token Và Golden Test Trước/Sau

Ví dụ dưới đây minh họa quy trình đầy đủ: ước lượng nhanh bằng tiktoken, xác nhận chính xác bằng Anthropic SDK, và chạy golden test case để đảm bảo hành vi không hồi quy sau khi rút gọn prompt.

"""
Compare token footprint and behavioral quality between a bloated system
prompt and a lean, rewritten version.

Two token-counting paths are shown deliberately:
  1. tiktoken - a quick, offline approximation. It is OpenAI's tokenizer,
     NOT Claude's tokenizer, so counts are only a rough proxy (can be off
     by 15-30% for Claude models, more for code-heavy or non-English
     text). Useful only for a fast local diff during iteration.
  2. Anthropic SDK `messages.count_tokens()` - the authoritative source.
     Always validate the real token delta with this before reporting
     savings to stakeholders or making a cost decision.
"""

import anthropic
import tiktoken
from dataclasses import dataclass

MODEL_ID = "claude-opus-4-8"  # swap to claude-sonnet-5 for high-volume, cost-sensitive agents
client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY from env


BLOATED_SYSTEM_PROMPT = """
You are an extremely helpful, friendly, and highly capable AI assistant
that has been carefully designed to help software engineering teams with
a wide variety of coding-related tasks. It is very important that you
always try your best to be as helpful as possible in every response.

When the user asks you to write code, please make sure to always write
clean, readable, well-documented, and maintainable code, following best
practices for the language being used.

If the user asks something unrelated to coding, still try to be helpful,
but gently remind them you are primarily designed for engineering tasks.

CRITICAL: You MUST always use the `run_tests` tool after making any code
change, no matter how small, because it is extremely important that we
verify nothing is broken. If in doubt, always call `run_tests`.

CRITICAL: You MUST always use the `search_codebase` tool before making
any claim about how the existing code works, because guessing is
dangerous and could lead to incorrect answers.

Please format code as fenced blocks with a language tag. When listing
multiple options, use a numbered list. Always be polite and thank the
user for their patience if a task takes multiple steps.
"""

LEAN_SYSTEM_PROMPT = """
Senior software engineering assistant. Coding tasks only; redirect
off-topic requests in one sentence.

1. Call `search_codebase` before describing existing behavior - never guess.
2. Call `run_tests` after every code change, however small.
3. Match the codebase's existing style; comment only non-obvious logic.

- Code: fenced block, language tag.
- Multi-option answers: numbered list.
"""


def tiktoken_estimate(text: str) -> int:
    enc = tiktoken.get_encoding("cl100k_base")
    return len(enc.encode(text))



def claude_token_count(system_prompt: str) -> int:
    resp = client.messages.count_tokens(
        model=MODEL_ID,
        system=system_prompt,
        messages=[{"role": "user", "content": "placeholder user turn"}],
    )
    return resp.input_tokens



@dataclass
class GoldenCase:
    user_message: str
    must_call_tool: str | None = None  # tool we expect the agent to trigger


GOLDEN_CASES = [
    GoldenCase(
        user_message="Update the retry logic in payment_client.py to use exponential backoff.",
        must_call_tool="search_codebase",
    ),
    GoldenCase(
        user_message="What's the weather like today?",
        must_call_tool=None,
    ),
]

TOOLS = [
    {
        "name": "search_codebase",
        "description": "Search the repository for relevant code.",
        "input_schema": {
            "type": "object",
            "properties": {"query": {"type": "string"}},
            "required": ["query"],
        },
    },
    {
        "name": "run_tests",
        "description": "Run the test suite.",
        "input_schema": {"type": "object", "properties": {}},
    },
]


def run_golden_case(system_prompt: str, case: GoldenCase) -> bool:
    response = client.messages.create(
        model=MODEL_ID,
        max_tokens=1024,
        system=system_prompt,
        tools=TOOLS,
        messages=[{"role": "user", "content": case.user_message}],
    )
    called_tools = [b.name for b in response.content if b.type == "tool_use"]
    if case.must_call_tool:
        return case.must_call_tool in called_tools
    return True  # no strict requirement for this case



if __name__ == "__main__":
    print("Token count comparison (tiktoken estimate vs Claude ground truth):\n")
    for label, prompt in [("BLOATED", BLOATED_SYSTEM_PROMPT), ("LEAN", LEAN_SYSTEM_PROMPT)]:
        tk_estimate = tiktoken_estimate(prompt)
        claude_tokens = claude_token_count(prompt)
        print(f"  [{label}] tiktoken (approx)={tk_estimate:>4} | Claude count_tokens={claude_tokens:>4}")

    bloated_tokens = claude_token_count(BLOATED_SYSTEM_PROMPT)
    lean_tokens = claude_token_count(LEAN_SYSTEM_PROMPT)
    saved = bloated_tokens - lean_tokens
    pct = saved / bloated_tokens * 100
    print(f"\nSaved {saved} tokens per request ({pct:.1f}% reduction) - "
          f"multiply by daily request volume to see real cost impact.\n")

    print("Running golden test cases against the LEAN prompt...")
    for i, case in enumerate(GOLDEN_CASES, start=1):
        passed = run_golden_case(LEAN_SYSTEM_PROMPT, case)
        status = "PASS" if passed else "FAIL"
        print(f"  Case {i}: {status} - {case.user_message[:60]}")

Chạy script này cho ra hai loại số liệu bổ trợ nhau: phần trăm token tiết kiệm được (nhân với lưu lượng request hằng ngày để thấy tác động chi phí thật), và kết quả PASS/FAIL của từng golden case để xác nhận rule quan trọng (gọi search_codebase trước khi mô tả code, không gọi tool ngoài chủ đề) vẫn được tuân thủ sau khi rút gọn. Nếu bất kỳ case nào chuyển sang FAIL, đừng merge phiên bản lean — quay lại xem rule nào đã bị cắt quá tay.

Xây Bộ Golden Test Case Đủ Tốt

Một bộ golden test case hữu ích cần bao phủ ít nhất ba nhóm: (1) case điển hình nhất mà agent xử lý mỗi ngày — đảm bảo không hồi quy hành vi cơ bản; (2) case biên đã từng gây lỗi trong quá khứ — chính là những rule "vá lỗi" bạn định xóa, hãy biến chúng thành test case trước khi xóa rule, để nếu xóa sai, test sẽ báo ngay; (3) case ngoài phạm vi (out-of-scope) để xác nhận agent không "quá tay" dùng tool khi không cần. Số lượng case không cần lớn — 10-20 case được chọn kỹ có giá trị hơn 100 case ngẫu nhiên không đại diện.

Quy Trình Lặp Trong Thực Tế

Quy trình khuyến nghị khi tối ưu một system prompt production: (1) chốt bộ golden test case và chạy baseline với prompt hiện tại, ghi lại kết quả PASS/FAIL và token count; (2) áp dụng các nguyên tắc ở phần trên để viết phiên bản lean; (3) chạy lại toàn bộ golden test case với phiên bản mới; (4) so sánh side-by-side — chỉ merge khi token giảm đáng kể VÀ không có case nào hồi quy từ PASS sang FAIL; (5) theo dõi thêm 1-2 tuần trong production với logging chi tiết trước khi coi là ổn định, vì golden test case không bao giờ bao phủ 100% tình huống thực tế.

Mẹo

Lưu baseline (prompt cũ, token count cũ, kết quả golden test cũ) vào version control cùng với prompt — không chỉ lưu prompt mới nhất. Khi một lần tối ưu sau này gây hồi quy không rõ nguyên nhân, khả năng cao là một rule tưởng như "chỉ là văn hoa mỹ" trong một lần cắt gọn trước đó thực ra lại có giá trị ẩn mà golden test case tại thời điểm đó chưa bao phủ — có lịch sử đầy đủ giúp bạn truy vết nhanh hơn nhiều so với đoán mò.