Nếu bạn đã từng làm việc với Claude Code, Cursor, hay bất kỳ coding agent nào trong một phiên (session) kéo dài vài giờ, bạn chắc đã từng cảm nhận một điều lạ: agent bắt đầu "chậm" hơn, trả lời "lú" hơn, hoặc đơn giản là quên mất điều bạn vừa nói cách đây 20 phút. Đó không phải cảm giác chủ quan. Đó là hệ quả trực tiếp của việc context window — bộ nhớ làm việc của agent — đang đầy dần lên, và bạn cần biết khi nào nên can thiệp bằng compression (nén ngữ cảnh) trước khi chất lượng công việc sụp đổ.
Bài viết này không nói về kỹ thuật compression cụ thể (những phần đó nằm ở các bài sau trong module này). Bài viết này trả lời một câu hỏi thực dụng hơn: làm sao biết khi nào cần nén, và vì sao việc đó quan trọng hơn bạn nghĩ.
Context Window: Một Tài Nguyên Có Hạn
Context window của một LLM (large language model) không phải là bộ nhớ vô hạn — nó là một khối token có kích thước cố định, tính theo số token mà model có thể "nhìn thấy" cùng một lúc trong một lượt gọi API. Với các model hiện nay như Claude Sonnet hay Claude Opus, con số này dao động từ 200.000 token cho bản tiêu chuẩn đến 1 triệu token cho bản mở rộng (extended context). Nghe thì có vẻ rất lớn, nhưng trong một phiên làm việc agentic thực tế, con số đó biến mất nhanh hơn bạn tưởng.
Hãy hình dung context window như một cái ba lô có sức chứa cố định, và mọi thứ agent cần "nhớ" để trả lời đúng đều phải nhét vào đó:
- System prompt — hướng dẫn hệ thống, persona, các quy tắc vận hành. Với các coding agent phức tạp (như Claude Code), phần này có thể chiếm vài nghìn đến vài chục nghìn token, đặc biệt khi có CLAUDE.md, custom instructions, hay nhiều skill được nạp.
- Tool definitions — định nghĩa schema của tất cả các tool mà agent có quyền gọi (Read, Edit, Bash, Grep, các MCP tool...). Một agent có vài chục tool đã có thể tốn 5.000–15.000 token chỉ để mô tả "tôi có thể làm gì", trước khi làm bất cứ việc gì.
- Conversation history — toàn bộ các lượt hỏi-đáp, các lần agent suy nghĩ (reasoning), các lần gọi tool và kết quả trả về. Đây là phần tăng nhanh nhất theo thời gian.
- File contents — mỗi khi agent đọc một file để hiểu code, toàn bộ nội dung file đó (hoặc phần được đọc) sẽ nằm trong context, tồn tại xuyên suốt phần còn lại của session trừ khi bị dọn đi.
- Tool outputs — kết quả của các lệnh Bash, log build, output test, kết quả grep/search... Đây là phần "âm thầm" ăn token nhiều nhất mà ít người để ý, vì output của một lệnh
npm testhaygit logdài có thể lên tới vài nghìn token chỉ trong một lần gọi.
Tất cả các thành phần này chia sẻ chung một ngân sách token (token budget). Không có ngăn riêng cho "lịch sử hội thoại" và ngăn riêng cho "nội dung file" — tất cả cộng dồn vào cùng một tổng, và tổng đó bị giới hạn cứng bởi context window của model. Khi tổng này chạm ngưỡng, một trong hai điều sẽ xảy ra: hoặc tool client tự động cắt bớt phần cũ nhất (thường là mất thông tin không kiểm soát), hoặc bạn nhận lỗi context length exceeded và phiên làm việc bị ngắt hoàn toàn.
Điều đáng chú ý hơn: token không tăng đều. Trong một buổi debug, có những lượt agent chỉ tốn vài trăm token (một câu hỏi ngắn), nhưng có những lượt tốn hàng chục nghìn token trong một lần (đọc một file 2.000 dòng, hoặc output của một lần chạy test suite đầy đủ). Nếu bạn không theo dõi, ngân sách context có thể bị "ăn" gần hết chỉ sau 3-4 hành động tưởng chừng vô hại.
Mẹo:
- Dùng lệnh /context (trong Claude Code) hoặc tính năng tương đương của tool bạn dùng để xem % context đã sử dụng theo thời gian thực — đừng đợi đến khi tool cảnh báo.
- Ước lượng trước: một file code trung bình 300-500 dòng tốn khoảng 3.000-6.000 token. Nếu bạn dự tính đọc 5-6 file lớn trong một task, hãy tính trước ngân sách còn lại có đủ không.
- Với các MCP server có nhiều tool, kiểm tra xem có đang nạp tool không cần thiết cho task hiện tại không — mỗi tool definition không dùng vẫn chiếm chỗ.
Nhận Diện Năm Dấu Hiệu Cảnh Báo
Vấn đề lớn nhất không phải là context window có hạn — ai cũng biết điều đó — mà là hầu hết engineer không nhận ra mình đã vượt điểm tới hạn cho đến khi hậu quả xảy ra. Dưới đây là năm dấu hiệu cụ thể, có thể quan sát được ngay trong lúc làm việc với agent, báo hiệu rằng đã đến lúc compress.
1. Agent lặp lại hành động đã làm. Đây là dấu hiệu rõ ràng nhất. Nếu bạn thấy agent đọc lại một file nó vừa đọc cách đây 10 phút, hoặc grep lại một pattern nó đã tìm thấy, nhiều khả năng thông tin đó đã bị "trôi" ra khỏi vùng chú ý hiệu quả của model — không phải vì nó biến mất khỏi context, mà vì model không còn "để ý" đến nó nữa (xem phần "lost in the middle" ở mục dưới). Khi agent phải làm lại việc đã làm, đó là lãng phí token gấp đôi và dấu hiệu context đang quá tải thông tin nhiễu.
2. Agent quên quyết định hoặc ràng buộc đã thống nhất trước đó. Ví dụ: bạn đã nói ở đầu session "không dùng lodash, dự án này không có dependency đó", nhưng 40 lượt sau, agent lại đề xuất import { debounce } from 'lodash'. Đây là dấu hiệu cực kỳ đáng tin cậy rằng các chỉ dẫn quan trọng đã bị chôn quá sâu trong lịch sử hội thoại dài, và model không còn ưu tiên nó ngang với thông tin ở gần cuối context.
3. Tốc độ phản hồi chậm lại rõ rệt. Thời gian xử lý của LLM không tuyến tính theo độ dài input, nhưng có tương quan: context càng dài, thời gian để model "đọc" toàn bộ trước khi sinh token đầu tiên (time-to-first-token) càng lâu. Nếu đầu session agent trả lời trong 3-5 giây nhưng đến lượt thứ 50 phải chờ 20-30 giây cho một câu trả lời tương đương độ phức tạp, đó là dấu hiệu context đã phình to đáng kể.
4. Chất lượng reasoning giảm — câu trả lời chung chung, hời hợt, hoặc sai lệch so với phong cách ban đầu. Khi context chứa quá nhiều thông tin không còn liên quan (log lỗi cũ đã fix, các đoạn code đã bị xóa, các nhánh suy nghĩ đã bỏ), model phải "chia sẻ" khả năng chú ý (attention) cho tất cả thông tin đó, kể cả phần không còn giá trị. Kết quả là câu trả lời trở nên loãng, kém sắc bén hơn so với đầu phiên.
5. Cảnh báo trực tiếp từ tool — thanh % context, thông báo "approaching context limit", hoặc lỗi context length exceeded. Đây là dấu hiệu "muộn" nhất và cũng dễ nhận biết nhất. Claude Code hiển thị % context còn lại; nhiều IDE agent (Cursor, Windsurf) cũng có chỉ báo tương tự. Vấn đề là nếu bạn chỉ hành động khi thấy cảnh báo này, bạn đã bỏ lỡ 4 dấu hiệu sớm hơn — và thường là đã chịu thiệt hại về chất lượng từ trước đó khá lâu.
Mẹo:
- Tạo thói quen tự hỏi sau mỗi 15-20 lượt trao đổi: "Agent có vừa lặp lại việc gì không?" — nếu có, đó là tín hiệu cần tóm tắt (summarize) hoặc reset ngay, đừng chờ đến dấu hiệu thứ 5.
- Ghi lại các quyết định/ràng buộc quan trọng vào một file riêng (ví dụ NOTES.md hoặc trực tiếp trong CLAUDE.md) ngay khi vừa thống nhất, để không phụ thuộc hoàn toàn vào việc model "nhớ" trong context.
- Nếu bạn cần agent tuân thủ nghiêm ngặt một ràng buộc trong suốt session dài, hãy nhắc lại ràng buộc đó định kỳ (mỗi khi giao task mới) thay vì tin rằng nó vẫn "nổi" trong vùng chú ý.
Vì Sao Compression Không Chỉ Là Chuyện Tiết Kiệm Tiền
Rất nhiều engineer nghĩ về context compression như một bài toán chi phí thuần túy: context dài hơn = nhiều token hơn = bill API cao hơn. Điều đó đúng, nhưng nó chỉ là phần nổi của vấn đề — và nếu bạn chỉ tối ưu cho chi phí, bạn sẽ bỏ lỡ lý do quan trọng hơn: context dài làm giảm chất lượng suy luận của model, không phụ thuộc vào việc bạn có trả tiền hay không.
Hiện tượng được nghiên cứu rộng rãi trong giới AI gọi là "lost in the middle" (thất lạc ở giữa) — các nghiên cứu về hiệu năng LLM trên input dài cho thấy model có xu hướng chú ý tốt nhất đến thông tin ở đầu và cuối context, còn thông tin nằm ở giữa một context rất dài thường bị "lu mờ" trong quá trình model tổng hợp câu trả lời — dù về lý thuyết thông tin đó vẫn "có mặt" trong input. Với một agent coding session, điều này có nghĩa: quyết định thiết kế quan trọng bạn đưa ra ở lượt thứ 5, nếu session kéo dài đến lượt thứ 80, có nguy cơ bị model "bỏ qua" khi ra quyết định ở lượt 80, ngay cả khi input đó vẫn nằm trong context window.
Bên cạnh vấn đề chất lượng, còn có vấn đề latency (độ trễ). Context dài hơn đồng nghĩa với nhiều token hơn cần được xử lý ở bước "đọc" input trước khi model bắt đầu sinh ra token trả lời. Với các phiên tương tác liên tục — nơi bạn đang debug real-time cùng agent — độ trễ tăng dần này phá vỡ nhịp làm việc, làm giảm năng suất thực tế của cả session, không chỉ là con số hiển thị trên hóa đơn.
Và cuối cùng là vấn đề suy luận (reasoning quality). Khi context chứa nhiều "rác" — các đoạn hội thoại đã không còn liên quan, các phương án đã bị loại bỏ, các file đã đọc nhưng không còn cần — model phải dành một phần "băng thông suy luận" để xử lý và lọc bỏ những thông tin đó trong lúc suy nghĩ. Với các bài toán cần suy luận nhiều bước (multi-step reasoning) như debug logic phức tạp hay thiết kế kiến trúc, phần băng thông bị "hao hụt" này có thể là khác biệt giữa một giải pháp đúng và một giải pháp na ná đúng.
Nói cách khác: compression không phải một tính năng "nice-to-have" để giảm bill cuối tháng. Nó là công cụ để bảo vệ chất lượng đầu ra trong các phiên làm việc dài — thứ ảnh hưởng trực tiếp đến việc bạn có ship đúng code hay không, chứ không chỉ ảnh hưởng đến báo cáo chi phí của team.
Mẹo:
- Đừng chỉ theo dõi chi phí token trên dashboard — theo dõi cả "độ chính xác cảm nhận được" của agent qua thời gian trong cùng một session. Nếu bạn thấy mình phải sửa lại câu trả lời của agent nhiều hơn ở cuối session so với đầu session, đó là dấu hiệu chất lượng đang giảm do context, không phải do task khó hơn.
- Với các quyết định kiến trúc/thiết kế quan trọng, hãy đặt chúng gần cuối context (nhắc lại ngay trước khi giao task mới) thay vì chỉ dựa vào việc chúng đã được nói ở đầu session.
- Nếu task cần suy luận sâu (deep reasoning), hãy compress trước khi giao task đó, không phải sau — cho model "không gian sạch" để tập trung suy luận thay vì lọc nhiễu.
Cái Giá Của Việc Không Compress — Các Tình Huống Thực Tế
Lý thuyết là một chuyện, nhưng hậu quả thực tế của việc bỏ qua compression thường rõ ràng và đau đớn hơn nhiều. Dưới đây là ba tình huống điển hình mà hầu hết engineer làm việc với coding agent đều đã từng gặp — hoặc sẽ gặp.
Tình huống 1: Buổi debug kéo dài nhiều giờ. Bạn bắt đầu debug một lỗi race condition khó tái hiện. Agent đọc log, đọc code, đề xuất giả thuyết, bạn test, giả thuyết sai, agent đọc thêm log mới, đề xuất giả thuyết khác... Sau 3 giờ và khoảng 60 lượt trao đổi, context của bạn chứa đầy các giả thuyết đã bị bác bỏ, các đoạn log dài đã không còn giá trị chẩn đoán, và nhiều lần đọc file trùng lặp. Đến lượt thứ 60, agent bắt đầu đề xuất lại một giả thuyết đã bị loại từ lượt thứ 15 — không phải vì nó "ngu", mà vì thông tin "giả thuyết này đã sai" bị chôn giữa hàng chục nghìn token log không liên quan. Nếu bạn đã compress (tóm tắt lại: "đã loại 4 giả thuyết A, B, C, D vì lý do X, Y, Z, W; đang tập trung vào E") sau mỗi giờ, agent sẽ giữ được "bộ nhớ làm việc" sạch và tập trung hơn nhiều.
Tình huống 2: Buổi code review một pull request lớn, nhiều file. Bạn yêu cầu agent review một PR thay đổi 25 file. Agent đọc từng file, đưa ra nhận xét. Đến file thứ 18, context đã chứa toàn bộ nội dung của 17 file trước — kể cả các file không liên quan đến vấn đề đang xét ở file 18. Khi bạn hỏi "logic ở file 3 và file 18 có nhất quán không?", agent trả lời không chính xác vì phần nội dung file 3 đã bị "lu mờ" theo hiệu ứng lost-in-the-middle. Tệ hơn, nếu không compress, bạn có nguy cơ chạm context limit giữa PR, buộc phải bắt đầu lại toàn bộ review từ đầu — mất toàn bộ ngữ cảnh đã xây dựng, gấp đôi thời gian lẫn chi phí.
Tình huống 3: Refactor xuyên nhiều file trong một service lớn. Bạn giao agent nhiệm vụ đổi tên một field xuyên suốt 40 file, kèm cập nhật logic liên quan. Agent làm việc tuần tự, mỗi file agent đọc, sửa, chạy test. Sau khoảng 25 file, context đã đầy nội dung diff và output test của 24 file trước. Agent bắt đầu áp dụng convention không nhất quán — ở file 5 nó dùng một pattern đặt tên, đến file 30 nó tự nghĩ ra pattern khác, vì "quy tắc" mà bạn đặt ra ở đầu session đã bị chìm nghỉm. Kết quả: bạn phải dừng lại, review lại toàn bộ 30 file đã sửa để đảm bảo tính nhất quán — công việc mà lẽ ra compression định kỳ (tóm tắt lại quy tắc + tiến độ sau mỗi 10 file) có thể ngăn chặn hoàn toàn.
Điểm chung của cả ba tình huống: cái giá không compress không chỉ là "tốn thêm tiền token". Nó là thời gian làm lại, lỗi logic lọt qua, và mất niềm tin vào agent — những cái giá đắt hơn nhiều so với chi phí của một lệnh tóm tắt.
Mẹo:
- Với task dài có thể chia giai đoạn rõ ràng (debug, review, refactor theo batch file), chủ động compress ở ranh giới giữa các giai đoạn, đừng chờ context đầy.
- Trước một task review/refactor nhiều file, ước lượng trước: "N file × chi phí token trung bình/file" so với ngân sách context còn lại — nếu vượt quá 60-70% ngân sách, lên kế hoạch compress giữa chừng ngay từ đầu.
- Sau một buổi debug dài, luôn yêu cầu agent viết lại một bản tóm tắt "kết luận + các giả thuyết đã loại" trước khi tiếp tục, dù bạn không compress toàn bộ session.
Xây Dựng Chính Sách Compression Cá Nhân
Biết dấu hiệu cảnh báo là chưa đủ — bạn cần một chính sách rõ ràng, có thể lặp lại, để không phải ra quyết định cảm tính mỗi lần. Dưới đây là một khung tham khảo bạn có thể áp dụng và điều chỉnh theo phong cách làm việc của mình.
Bước 1 — Đặt ngưỡng theo dõi (threshold). Một quy tắc thực dụng, đã được nhiều engineer áp dụng: bắt đầu cân nhắc compression khi context đạt khoảng 50-60% dung lượng, và bắt buộc compress khi đạt 75-80%. Lý do đặt ngưỡng sớm: bạn cần chỗ trống cho các hành động sắp tới (đọc file mới, output tool mới), không phải đợi đến khi gần hết mới lo.
Bước 2 — Gắn compression với ranh giới công việc tự nhiên, không chỉ với số %. Ví dụ: hoàn thành xong một task con (task đã merge, bug đã fix và verify), đó là điểm tốt để tóm tắt và "gọn hóa" trước khi chuyển sang task tiếp theo — dù % context chưa chạm ngưỡng. Compress theo ranh giới ngữ nghĩa (semantic boundary) tốt hơn compress theo lịch cứng, vì nó giữ được tính mạch lạc của thông tin còn lại.
Bước 3 — Phân loại mức độ "aggressive" theo loại task.
- Task đơn giản, ngắn (fix một bug rõ ràng, viết một function nhỏ): không cần chính sách compression phức tạp, session thường kết thúc trước khi cần.
- Task trung bình (review PR, refactor vài file): compress theo ranh giới — sau mỗi nhóm 5-10 file, hoặc sau mỗi giờ làm việc liên tục.
- Task dài, nhiều giai đoạn (debug phức tạp, thiết kế kiến trúc, migration lớn): cần chính sách compress chủ động và định kỳ, kết hợp với việc lưu trạng thái quan trọng ra file bên ngoài context (ví dụ ghi vào một file progress.md hoặc decisions.md) để không phụ thuộc hoàn toàn vào context window.
Bước 4 — Xác định "cái gì phải giữ lại" trước khi compress. Trước khi tóm tắt, hãy tự hỏi: quyết định nào không thể mất, ràng buộc nào bắt buộc phải giữ, giả thuyết nào đã loại và không cần xét lại? Một bản tóm tắt tốt phải giữ lại các mục này rõ ràng, tách biệt khỏi phần chi tiết có thể bỏ.
Bước 5 — Đo lại sau khi compress. Sau khi tóm tắt, hỏi lại agent một câu kiểm tra nhanh (ví dụ: "Nhắc lại các ràng buộc chính của task này") để xác nhận thông tin quan trọng không bị mất trong quá trình nén — đây là bước kiểm định (validation) đơn giản nhưng thường bị bỏ qua.
Mẹo:
- Viết chính sách này ra thành một checklist ngắn, dán vào README nội bộ của team hoặc vào CLAUDE.md của dự án, để cả team áp dụng nhất quán, không phụ thuộc vào cảm tính từng người.
- Với các dự án dùng agent thường xuyên cho task dài, cân nhắc chuẩn hóa việc ghi "trạng thái tiến độ" ra file ngoài (progress log) như một phần quy trình bắt buộc, không chỉ dựa vào context.
- Định kỳ xem lại chính sách này sau vài tuần sử dụng thực tế — ngưỡng % phù hợp có thể khác nhau tùy loại task và tùy model bạn dùng (model có context window 1 triệu token sẽ có ngưỡng % khác với model 200.000 token, nhưng bản chất vấn đề chất lượng suy luận vẫn tương tự).
Tổng Kết
Context window là tài nguyên có hạn, được chia sẻ giữa system prompt, tool definitions, lịch sử hội thoại, nội dung file, và output của tool — tất cả cộng dồn vào một ngân sách token duy nhất. Bạn có thể nhận diện thời điểm cần compress qua năm dấu hiệu cụ thể: agent lặp lại hành động, agent quên quyết định/ràng buộc đã thống nhất, phản hồi chậm dần, chất lượng reasoning giảm sút, và cảnh báo trực tiếp từ tool. Quan trọng hơn, compression không chỉ là bài toán tiết kiệm chi phí — nó là công cụ chống lại hiệu ứng "lost in the middle", giảm latency, và bảo vệ chất lượng suy luận trong các session dài. Bỏ qua compression trong các tình huống thực tế như debug kéo dài, review PR lớn, hay refactor nhiều file có thể dẫn đến làm lại công việc, lỗi logic lọt qua, và mất niềm tin vào agent — cái giá đắt hơn nhiều so với một lệnh tóm tắt đúng lúc. Cách tốt nhất để tránh rơi vào tình huống đó là xây dựng cho mình một chính sách compression rõ ràng: đặt ngưỡng theo dõi, gắn compression với ranh giới công việc tự nhiên, phân loại mức độ theo loại task, xác định thông tin bắt buộc giữ lại, và luôn kiểm định lại sau khi nén. Các bài tiếp theo trong module này sẽ đi sâu vào từng kỹ thuật compression cụ thể — summarization hội thoại, tóm tắt gia tăng, tóm tắt diff code, và quản lý context theo phân cấp.