AI Core cho lập trình viên
22/54
Bài 22 / 54~12 phútCỗ máy LLMMiễn phí… lượt xem

Context window — bộ nhớ làm việc hữu hạn

Model chỉ nhìn được số token giới hạn mỗi request. Hết chỗ thì phần cũ bị cắt — đây là lý do thật của việc model 'quên', không phải trí nhớ kém.

TL;DR: Context window là "bộ nhớ làm việc" của model trong một request: dung lượng cố định đo bằng token (bài trước), không phải ký tự hay số lượt hội thoại. Hết chỗ thì model không "nhớ kém dần"; ứng dụng gọi model buộc phải cắt bớt token cũ nhất để nhét phần mới vào, và phần bị cắt biến mất hoàn toàn khỏi request kế tiếp. Vì đơn vị là token, tiếng Việt (tốn token hơn tiếng Anh cho cùng nội dung, bài 02) lấp đầy context nhanh hơn. Chi phí và độ trễ cũng tính theo đúng số token đó: context càng dài, hoá đơn và thời gian chờ càng tăng.

Một chatbot hỗ trợ nội bộ chạy production được vài tuần. Ở tin nhắn đầu, user dặn: "trả lời ngắn gọn, không dùng thuật ngữ tiếng Anh". Hội thoại càng dài, model càng phớt lờ yêu cầu đó, tới tin nhắn 40 thì coi như chưa từng được dặn. Team dev nghi model "yếu dần", rồi thử một cách tốn kém: nhắc lại constraint ở mọi tin nhắn.

Cùng lúc, một bot khác trả lời sai một câu hỏi tra cứu policy, dù đáp án đã được dán nguyên văn vào prompt và không hề bị cắt. Câu hỏi để dành cho cuối bài: sao thông tin có mặt trong context mà model vẫn bỏ sót?

Cả hai cùng một gốc: context window là tài nguyên hữu hạn, và model "dùng" nó không đều. Chuyện không chỉ là còn chỗ hay hết chỗ.

Nhớ lại — Vòng lặp next-token

Bài 01: mỗi bước, model dự đoán token kế tiếp dựa trên toàn bộ token đứng trước nó. Câu hỏi của bài này: "toàn bộ" đó lớn được tới đâu? Context window chính là giới hạn ấy, và khi vượt quá, một phần token đứng trước không còn nằm trong tầm nhìn của model nữa.

1. Analogy: sổ tay có số trang cố định

Bạn ghi chép một cuộc họp dài vào cuốn sổ tay in sẵn số trang: hết trang là hết, không viết thêm vào cuốn cũ được. Họp càng lâu, bạn càng phải xé bớt trang đầu tiên (ghi chép cũ nhất) để có chỗ viết tiếp. Người đọc lại sổ chỉ thấy phần còn nguyên trang; phần đã xé thì mất hẳn chứ không "mờ đi".

Sổ tayContext window
Số trang cố định, in trước khi họpGiới hạn token cố định của một request
Hết trang, xé trang đầu để viết tiếpContext đầy → token cũ nhất bị cắt khỏi request kế tiếp
Đọc lại sổ, dễ nhớ rõ trang đầu còn lại và trang cuối, dễ lướt qua trang giữa dày đặc chữModel dễ "bỏ sót" thông tin nằm giữa context dài, dù trang đó không hề bị xé
Viết càng nhiều trang càng tốn giấy + tốn thời gian chépToken input + output càng nhiều càng tốn tiền + tốn thời gian sinh
💡 Cách nhớ

Context window không phải trí nhớ dài hạn, nó là số trang sổ còn lại trong đúng cuộc họp này. Đóng sổ (kết thúc request) thì phiên sau là sổ mới, trừ khi bạn tự chép lại những gì cần giữ.

2. Context window là gì?

Context window là số token tối đa model "nhìn thấy" được trong một lần gọi. System prompt, toàn bộ lịch sử hội thoại được đưa vào, câu hỏi mới nhất và cả token model đang sinh ra đều cộng chung vào một giới hạn cố định của model đó.

Vài điểm cần khớp với bài trước để khỏi hiểu sai:

  • Đơn vị là token, không phải ký tự hay từ. Cùng giới hạn token, văn bản tiếng Việt chứa được ít câu hơn tiếng Anh, vì mỗi câu tiếng Việt tốn nhiều token hơn (bài 02).
  • Giới hạn khác nhau giữa các model và đổi theo thời gian mỗi lần nhà cung cấp nâng cấp. Đừng nhớ một con số cố định; tra tài liệu chính chủ đúng lúc build, và kiểm luôn tokenizer có đổi không (bài 02).
  • Context không phải bộ nhớ giữa các request. Cảm giác "model nhớ cuộc trò chuyện" là do ứng dụng chủ động gửi lại lịch sử vào context của request mới, cho tới khi lịch sử dài quá không nhét vừa nữa.

Thử ngẫmđóng hẳn app chat rồi mở cuộc trò chuyện mới tinh, hỏi lại đúng câu vừa hỏi ở phiên trước. Model có "nhớ" phiên cũ không, và điều gì thật sự quyết định chuyện đó?

3. Đầy thì model cắt cái gì trước?

Khi tổng token (system + lịch sử + input mới) vượt giới hạn, gửi nguyên cả khối lên thì API thường từ chối request. Nên ứng dụng gọi model phải tự quyết bỏ bớt gì trước khi gửi. Cách phổ biến nhất: giữ input mới nhất, lùi dần về lịch sử cũ hơn, dừng ngay khi vừa đủ chỗ. Token của những lượt cũ nhất bị loại hẳn khỏi request đó.

Plain Text
function build_request(history, current_input):
    total <- đếm_token(system_prompt) + đếm_token(current_input)
    included <- []
    for luot in đảo_ngược(history):     -- duyệt từ MỚI NHẤT lùi về CŨ NHẤT
        if total + đếm_token(luot) > token_limit:
            break                        -- hết chỗ: DỪNG
        included.add(luot)
        total <- total + đếm_token(luot)
    -- lượt CŨ NHẤT chưa được add khi chạm break — mất hẳn khỏi request này
    return system_prompt + đảo_ngược(included) + current_input

Request có trần token cố định chứa system prompt, các lượt gần đây và input hiện tại; lượt cũ nhất rơi ra ngoài trần và bị loại, model không hề biết lượt đó từng tồn tại

Đây là nguyên nhân của sự cố mở bài. Constraint dặn ở tin nhắn 1 không bị model "quên" theo nghĩa suy giảm năng lực: nó bị cắt khỏi request thuần cơ học khi hội thoại đủ dài. Tiếng Việt tốn token hơn (bài 02) nên cửa sổ đầy nhanh hơn, và constraint bị "quên" sớm hơn so với cùng hội thoại bằng tiếng Anh.

4. Vị trí nào trong context dễ bị bỏ sót?

Sự cố thứ hai ở đầu bài vẫn chưa được giải thích: đáp án nằm nguyên trong context, không hề bị cắt, sao model vẫn trả lời sai?

Thử đoán

Bạn dán một tài liệu dài vào prompt, đặt đúng MỘT câu chứa đáp án ở một trong ba vị trí (đầu, giữa, hoặc cuối tài liệu), rồi hỏi model câu cần câu đó mới trả lời đúng. Toàn bộ tài liệu nằm gọn trong context, không vị trí nào bị cắt. Bạn đoán: đặt đáp án ở vị trí nào thì model dễ trả lời sai nhất?

Liu và cộng sự đo đúng hiện tượng này trên nhiều model có context dài (Lost in the Middle: How Language Models Use Long Contexts, arXiv:2307.03172). Khả năng truy xuất cao nhất khi thông tin cần thiết nằm ở đầu hoặc cuối context, và giảm rõ khi nó nằm ở giữa, dù thông tin đó chưa bao giờ bị cắt, chỉ bị "chôn" giữa một khối văn bản dài. Paper đặt tên cho hiện tượng này là lost-in-the-middle.

Tóm lại, "còn trong context" và "model chắc chắn dùng được" là hai chuyện khác nhau. Cửa sổ đủ rộng để chứa một tài liệu không có nghĩa là mọi phần của tài liệu được model chú ý như nhau.

5. Token trong context kéo theo những chi phí gì?

Token trong context là đơn vị bạn trả giá trên mấy mặt cùng lúc:

Thứ bị ảnh hưởngVì saoHệ quả thực tế
"Trí nhớ" hội thoạiĐầy thì cắt token cũ nhất (mục 3)Constraint/ngữ cảnh đầu hội thoại mất dần khi hội thoại dài ra
Độ chính xác truy xuấtVị trí giữa context dễ bị bỏ sót (mục 4)Tài liệu càng dài, đáp án nằm giữa càng dễ bị bỏ qua dù không bị cắt
Chi phí APITính tiền theo tổng token input + outputPrompt dài, đặc biệt tiếng Việt (tốn token hơn — bài 02), hoá đơn tăng nhanh hơn cảm giác
Tốc độ trả lờiModel phải xử lý hết context trước token đầu tiên, và mỗi token sau đó vẫn nhìn lại cả contextContext càng dài, chờ token đầu tiên càng lâu, mỗi token sau cũng nặng hơn một chút

Thử ngẫmmột cuộc gọi hội nghị tính phí theo phút, và mỗi phút thêm cũng làm người điều phối phản ứng chậm hơn một nhịp. Token trong context giống "phút gọi" đó ở điểm nào?

6. Pitfall tổng hợp

❌ Nhầm 1: nghĩ model "yếu trí nhớ" khi hội thoại dài, và cách sửa là đổi sang model "thông minh hơn".

✅ Giới hạn token là cố định theo model, còn việc bỏ lượt nào là do ứng dụng quyết. Đổi model không giải quyết gốc nếu context window mới vẫn hữu hạn. Chỗ cần sửa là ứng dụng: tóm tắt lịch sử cũ, hoặc chủ động nhắc lại constraint quan trọng gần cuối prompt (hay đưa hẳn vào system prompt) thay vì chỉ để ở tin nhắn đầu.

❌ Nhầm 2: "còn chỗ trống trong context là an toàn, nhét bao nhiêu tài liệu cũng được".

✅ Còn chỗ chỉ đảm bảo không bị cắt, không đảm bảo model dùng đều. Fact quan trọng chôn giữa một tài liệu dài có nguy cơ bị bỏ sót (mục 4) dù chưa từng rời khỏi context. Muốn chắc model dùng một thông tin then chốt, đặt nó gần đầu hoặc gần cuối prompt.

❌ Nhầm 3: chỉ đếm token phần mình gõ, quên rằng phần model sinh ra cũng ăn vào cùng ngân sách.

✅ Kiến trúc autoregressive (bài 01) đưa cả token vừa sinh vào chuỗi đang xử lý tiếp, nên input càng dài thì chỗ còn lại cho output càng hẹp. Ước lượng ngân sách phải cộng cả hai chiều, và nhớ tiếng Việt tốn token hơn ở cả hai chiều.

7. 📚 Deep Dive

📚 Nguồn gốc — đọc khi muốn xuống tầng sâu hơn

Paper chính chủ:

Ghi chú: bài không trích kích thước context window của model cụ thể nào vì con số đó đổi theo từng đợt nâng cấp. Tra tài liệu chính chủ đúng lúc build thay vì nhớ một con số cố định.

8. Liên hệ các bài khác

9. Tóm tắt

  • Khi vượt trần, ứng dụng duyệt lịch sử từ mới về cũ và bỏ lượt cũ nhất; model không hề biết lượt đó từng tồn tại.
  • Constraint chỉ nằm ở tin nhắn đầu là thứ đầu tiên rơi ra, nên đặt nó vào system prompt hoặc nhắc lại gần cuối.
  • Lost-in-the-middle: thông tin chôn giữa context dài dễ bị bỏ sót hơn thông tin ở đầu/cuối, dù không hề bị cắt.
  • Context dài làm tăng cả tiền lẫn thời gian chờ token đầu tiên.

10. Tự kiểm tra

Tự kiểm tra
0/5 câu đã trả lời
  1. Q1
    Một chatbot hội thoại dài dần 'quên' yêu cầu được dặn ở tin nhắn đầu tiên. Đây có phải model suy giảm năng lực theo thời gian không? Giải thích cơ chế thật.
  2. Q2
    Vì sao cùng một ứng dụng chatbot, hội thoại bằng tiếng Việt 'quên' constraint đầu hội thoại sớm hơn hội thoại tương đương bằng tiếng Anh?
  3. Q3
    Một đáp án đúng nằm nguyên vẹn trong tài liệu bạn dán vào prompt, không bị cắt bớt. Vì sao model vẫn có thể trả lời sai câu hỏi cần đáp án đó?
  4. Q4
    Từ hiện tượng lost-in-the-middle, bạn nên đặt một constraint quan trọng (ví dụ: 'luôn trả lời bằng tiếng Việt') ở đâu trong một prompt dài để model tuân thủ chắc nhất, và vì sao?
  5. Q5
    Bạn ước lượng ngân sách token cho một tính năng chat, chỉ đếm token của prompt mình gõ. Vì sao ước lượng này thiếu, và thiếu theo hướng nào?

Bài tiếp theo: Sampling — temperature và top-p

Bài này có giúp bạn hiểu bản chất không?

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Thảo luận & góp ý

Hỏi đáp về bài này

Bài tiếp theo

Sampling — temperature và top-p