AI Core cho lập trình viên/Context window — bộ nhớ làm việc hữu hạ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ỗ, model không "nhớ kém" dần đi — hệ thống 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à tốc độ sinh cũng tính theo đúng số token đó: context càng dài, hoá đơn và độ trễ càng tăng.

Một chatbot hỗ trợ nội bộ chạy production 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ử 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, không hề bị cắt bớt. Câu hỏi để cuối bài: sao thông tin có mặt trong context mà model vẫn bỏ sót?

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

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

Bài trước: 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 bài này: "toàn bộ" đó lớn được tới đâu? Context window chính là giới hạn của "toàn bộ" đó — 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 cuộc họp dài vào một cuốn sổ tay in sẵn số trang — hết trang là hết, không thể viết thêm vào cuốn cũ. Cuộc họp càng dài, 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 trang mới. Người đọc lại sổ sau này chỉ thấy phần còn nguyên trang — phần đã bị xé thì mất hẳn, không phải "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), 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 có thể "nhìn thấy" trong một lần gọi: system prompt, toàn bộ lịch sử hội thoại đã đưa vào, câu hỏi mới nhất, và cả token model đang sinh ra — tất cả 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ông 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 khi nhà cung cấp nâng cấp — đừng nhớ một con số cố định; tra tài liệu chính chủ tại đúng thời điểm build, 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ử đó quá dài để nhét vừa.

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, ứng dụng phía trên model phải quyết định bỏ bớt gì trước khi gửi request. 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 đó.

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 bị cắt ra ngoài khi vượt trần

Đây là cơ chế đứng sau 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 một cách 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 — constraint bị "quên" sớm hơn so với cùng hội thoại bằng tiếng Anh.

4. Thử đoán: vị trí nào trong context dễ bị bỏ sót

Sự cố thứ hai ở đầu bài chưa được giải thích: đáp án nằm nguyên trong context, không hề bị cắt — vậy vì 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 tài liệu, giữa tài liệu, hoặc cuối tài liệu — rồi hỏi model câu cần câu đó để trả lời đúng. Toàn bộ tài liệu đề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 ngôn ngữ dài context (Lost in the Middle: How Language Models Use Long Contexts, arXiv:2307.03172): hiệu 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õ rệt khi nó nằm ở giữa — dù thông tin đó chưa bao giờ bị cắt, chỉ là bị đặt "chôn" giữa một khối văn bản dài. Hiện tượng này được chính paper gọi tên: lost-in-the-middle.

Nói cách khác: "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 mọi phần của tài liệu đó được model "chú ý" như nhau.

5. Hệ quả dây chuyền — không chỉ là chuyện "nhớ"

Token trong context là đơn vị bạn trả giá trên ba trục 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 sinh từng token một (bài 01), phải "đọc" hết context trước khi sinhContext càng dài, độ trễ trước token đầu tiên càng lớn

6. Pitfall tổng hợp

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

✅ Đây là giới hạn kiến trúc, không phải năng lực. Đổi model không giải quyết gốc nếu context window mới vẫn hữu hạn — đúng 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 thay vì chỉ ở đầ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ù nó 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 phần model sinh ra cũng ăn vào cùng ngân sách:

✅ Kiến trúc autoregressive (bài 01) tính cả token vừa sinh vào chuỗi đang được model xử lý tiếp — input càng dài, 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ú: kích thước context window cụ thể của từng model không được trích trong bài vì là con số đổi theo từng đợt nâng cấp model — tra tài liệu chính chủ tại thời điểm 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

  • Context window là giới hạn token cố định cho một request — bao gồm system prompt, lịch sử hội thoại, input mới và cả token đang được sinh.
  • Đầy thì hệ thống cắt bớt token cũ nhất để nhét phần mới vào; phần bị cắt biến mất khỏi request, không phải model suy giảm năng lực.
  • Đơn vị luôn là token — tiếng Việt tốn token hơn nên lấp đầy context nhanh hơn, giữ được ít lượt hội thoại hơn cho cùng độ dài văn bản.
  • Còn chỗ trong context không đồng nghĩa model dùng đều mọi phần: thông tin nằm giữa một context dài dễ bị bỏ sót hơn thông tin ở đầu/cuối (lost-in-the-middle), dù không hề bị cắt.
  • Token input và output cùng tính vào một ngân sách — prompt dài kéo theo chi phí cao hơn và độ trễ lớn hơn.

10. Tự kiểm tra

Tự kiểm tra
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.

Không. Model không có trạng thái "mệt" hay "yếu dần". Khi tổng token của lịch sử hội thoại vượt giới hạn context, ứng dụng phải bỏ bớt token cũ nhất trước khi gửi request mới — tin nhắn đầu tiên là ứng viên bị loại sớm nhất.

Đó là cắt cơ học ở tầng dựng request, xảy ra trước khi model kịp "thấy" gì, không phải lỗi suy luận của model.

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?

Context window đo bằng token, không đo bằng số câu hay số lượt. Tiếng Việt tốn nhiều token hơn tiếng Anh cho cùng nội dung (bài 02) — nên cùng độ dài hội thoại tính theo số câu, bản tiếng Việt lấp đầy giới hạn token nhanh hơn.

Cửa sổ đầy sớm hơn nghĩa là các lượt cũ (bao gồm tin nhắn dặn constraint) bị đẩy ra khỏi request sớm hơn.

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 đó?

"Còn trong context" không đảm bảo model chú ý đều tới mọi phần. Nghiên cứu Lost in the Middle (Liu et al., arXiv:2307.03172) cho thấy hiệu năng truy xuất cao nhất khi thông tin nằm ở đầu hoặc cuối context, và giảm khi nó nằm ở giữa.

Nếu đáp án bị "chôn" giữa một tài liệu dài, model có xu hướng bỏ sót nó dù chưa từng bị cắt khỏi request.

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?

Gần đầu hoặc gần cuối prompt — đây là hai vùng có hiệu năng truy xuất cao nhất theo mục 4. Đặt giữa một khối văn bản dài là vị trí rủi ro nhất để model bỏ sót.

Với hội thoại nhiều lượt, cân nhắc nhắc lại constraint gần cuối (gần input mới nhất) thay vì chỉ dựa vào tin nhắn đầu — vì tin nhắn đầu còn có nguy cơ bị cắt hẳn khỏi context khi hội thoại dài ra (mục 3).

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?

Context window tính cả token output model sinh ra, không chỉ input. Kiến trúc autoregressive (bài 01) đưa mỗi token vừa sinh trở lại làm một phần chuỗi đang được xử lý tiếp — nên output cũng ăn vào cùng ngân sách token với input.

Ước lượng chỉ đếm input sẽ hụt: prompt dài để lại ít chỗ hơn cho output, và cả hai chiều cùng tốn tiền theo đúng số token — hụt càng rõ nếu nội dung là tiếng Việt (tốn token hơn ở cả input lẫn output).

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

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.

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

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

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Sampling — temperature và top-p