Ollama num_ctx: đặt context length cho prompt dài
Ollama có thể âm thầm cắt prompt dài vì context mặc định thấp. Xem cách đặt num_ctx theo request hoặc server, và tính KV cache RAM trước khi tăng.
num_ctx là gì và vì sao prompt dài của bạn bị cắt
Độ dài context của Ollama là số token mà model đã load có thể giữ trong memory cùng lúc. num_ctx là option đặt giá trị này. Ollama chọn giá trị mặc định thấp hơn nhiều so với mức tối đa mà model công bố. Vì vậy, prompt dài hơn sẽ bị cắt trước khi model kịp đọc. Response không cho biết việc này đã xảy ra.
Llama 3.1 8B được ghi có context window 128k trên thư viện model của Ollama. Server dùng cấu hình mặc định sẽ không cung cấp mức đó. Tài liệu riêng của Ollama đưa ra các giá trị mặc định khác nhau ở những trang khác nhau: FAQ nói 4096 token, tài liệu tham chiếu Modelfile nói num_ctx mặc định là 2048, còn trang về context length nói giá trị mặc định được chọn theo VRAM khả dụng: 4k khi dưới 24 GiB, 32k từ 24 đến 48 GiB và 256k khi cao hơn mức đó. Mỗi giá trị từng đúng với một số bản build. Bài học hữu ích là: hãy đọc giá trị trên chính server đang chạy của bạn thay vì tin vào bất kỳ trang nào, kể cả trang này.
Việc cắt bị âm thầm vì model vẫn trả lời và câu trả lời vẫn có vẻ hợp lý. Nội dung đó được tạo từ phần cuối của input. Một bản tóm tắt bỏ sót nửa đầu tài liệu có thể khiến bạn nghĩ model yếu. Nguyên nhân thường là context window quá nhỏ.
Kiểm tra context length mà server thực sự áp dụng cho Ollama
Cách kiểm tra hoạt động trên mọi bản build là prompt_eval_count, tức số prompt token mà server báo đã xử lý. Gửi nhiều token hơn context có thể chứa, số này sẽ dừng ở giới hạn.
sudo apt update && sudo apt install -y jq
LONG=$(python3 -c "print('the quick brown fox jumps over the lazy dog. ' * 2000)")
jq -n --arg p "$LONG" '{model:"llama3.1:8b", prompt:$p, stream:false, options:{num_ctx:4096}}' |
curl -s http://localhost:11434/api/generate -d @- |
jq '{prompt_eval_count, prompt_eval_duration}'Prompt đó dài khoảng 18,000 từ, nhiều hơn rất xa so với 4096 token. prompt_eval_count trả về giá trị gần 4096 thay vì gần số token thực tế, vì server đã bỏ phần còn lại. Chạy lại với "num_ctx":16384 thì số đếm tăng lên. Nếu bản build của bạn trả về lỗi thay vì cắt bớt prompt, đó vẫn là cùng một kết luận nhưng tín hiệu rõ hơn.
ollama psCột CONTEXT, trên những bản build có hiển thị, chứa context length mà model đã load đang sử dụng. Cột PROCESSOR ngay bên cạnh cho biết model đang được chạy ở đâu. 100% CPU là bình thường trên VPS không có GPU. Giá trị phân bổ như 30%/70% CPU/GPU trên máy có GPU nghĩa là weights và cache không còn vừa trong VRAM, và num_ctx tăng cao thường là nguyên nhân.
journalctl -u ollama --no-pager | grep -i n_ctx | tail -n 5Inference runner in context size trong một dòng có chứa n_ctx. Cách diễn đạt chính xác thay đổi giữa các bản release, vì vậy nếu không thấy dòng này, hãy xem đó là tên hiển thị đã thay đổi, không phải bằng chứng cho bất kỳ kết luận nào.
Bốn nơi để đặt num_ctx
Trong request. Gửi "options": {"num_ctx": 16384} đến /api/generate hoặc /api/chat. Thiết lập này luôn được ưu tiên hơn các thiết lập khác và chỉ áp dụng cho lần gọi đó. Nếu giá trị khác với giá trị mà model đã load đang chạy, server sẽ reload model trước. Bạn có thể thấy việc này trong load_duration của response: thời gian tăng từ gần 0 lên vài giây.
Trong interactive session. Trong ollama run, nhập /set parameter num_ctx 16384. Thiết lập này chỉ có hiệu lực trong session đó.
Trong Modelfile. Cách này ghi cố định giá trị vào một model có tên cụ thể, nên mọi client đều nhận được giá trị đó mà không cần thay đổi ở phía client.
FROM llama3.1:8b
PARAMETER num_ctx 16384ollama create llama3.1-16k -f ./Modelfile
ollama run llama3.1-16kTrên server. OLLAMA_CONTEXT_LENGTH đặt giá trị mặc định cho mọi request không chứa num_ctx riêng. Với systemd, hãy thêm drop-in thay vì chỉnh sửa unit file.
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_CONTEXT_LENGTH=16384"sudo systemctl daemon-reload
sudo systemctl restart ollama
ollama psThứ tự ưu tiên đặc biệt quan trọng khi bạn debug client của người khác. Request có num_ctx sẽ được ưu tiên hơn giá trị mặc định của server. Vì vậy, một giao diện chat hoặc agent tự gửi giá trị nhỏ có thể âm thầm vô hiệu thay đổi bạn đã thực hiện trong systemd. Khi bạn trỏ coding agent vào Ollama server của mình, hãy kiểm tra client gửi gì trước khi quy lỗi cho server.
Vì sao bạn không thể chỉ đặt num_ctx bằng mức tối đa của model
Attention khiến mỗi token xem tất cả token đứng trước nó. Các key và value được tính cho những token trước đó được giữ lại để không phải tính lại cho mỗi token mới. Phần lưu trữ đó là KV cache (key/value cache). KV cache được cấp phát cho toàn bộ num_ctx khi model được load, không tăng dần theo độ dài cuộc hội thoại. Vì vậy, context lớn vẫn chiếm bộ nhớ ngay cả khi prompt chỉ có một dòng.
Hướng dẫn về chi phí inference của DigitalOcean nêu phép tính trong một dòng:
kv_bytes_per_token = 2 * layers * kv_heads * head_dim * bytes_per_valueSố 2 tính riêng key và value. Hãy lấy các số còn lại từ chính model của bạn.
curl -s http://localhost:11434/api/show -d '{"model":"llama3.1:8b"}' |
jq '.model_info | {ctx: ."llama.context_length", layers: ."llama.block_count", heads: ."llama.attention.head_count", kv_heads: ."llama.attention.head_count_kv", embed: ."llama.embedding_length"}'Llama 3.1 8B có 32 layer và 8 head key/value. Kích thước head là embed chia cho heads, nên ở đây 4096 / 32 = 128. Một số model công bố trực tiếp giá trị này dưới tên llama.attention.key_length. Cache mặc định giữ các giá trị f16, nên bytes_per_value bằng 2. Phép tính 2 32 8 128 2 cho kết quả 131,072 byte. Như vậy, mỗi token trong context cần 128 KiB cache. Nhân với độ dài context thì chi phí không còn là một con số trừu tượng.
The data behind this chart
[
{
"label": "4k",
"kv_cache_gib": 0.5,
"total_ram_gib": 5.1
},
{
"label": "8k",
"kv_cache_gib": 1,
"total_ram_gib": 5.6
},
{
"label": "16k",
"kv_cache_gib": 2,
"total_ram_gib": 6.6
},
{
"label": "32k",
"kv_cache_gib": 4,
"total_ram_gib": 8.6
},
{
"label": "64k",
"kv_cache_gib": 8,
"total_ram_gib": 12.6
},
{
"label": "128k",
"kv_cache_gib": 16,
"total_ram_gib": 20.6
}
]6 dòng đó là kết quả tính từ công thức trên, không phải số đo thực tế. Cột tổng cộng thêm 4.9 GB dung lượng download mà thư viện Ollama liệt kê cho llama3.1:8b vào tháng 8 năm 2026. Con số này tương đương 4.6 GiB. Phép tính không bao gồm các buffer phục vụ compute và bản thân server process. Hãy xem đây là mức tối thiểu.
Điểm quan trọng nằm ở xu hướng này. Ở 8k, cache chiếm 1 GiB, không đáng kể so với weights. Ở mức tối đa 128k của model, cache chiếm 16 GiB, lớn hơn ba lần weights, đưa tổng bộ nhớ lên gần 20.6 GiB. Vì vậy, VPS 4 GB không thể load model này ở mức context hữu dụng. VPS 8 GB chạy thoải mái ở 8k. VPS 16 GB đạt 32k và vẫn còn chỗ cho các thành phần khác của máy. Mỗi ngưỡng này đều tăng theo dung lượng weights. Nếu bạn đang cân nhắc model lớn hơn so với bản 8B này, các phép tính tương tự cho tag 27B của Qwen trên VPS chỉ dùng CPU cho thấy weights để lại rất ít bộ nhớ cho context trong khoảng 8 đến 64 GB.
Điều gì xảy ra khi KV cache không vừa bộ nhớ
Trên VPS chỉ có CPU, tiến trình sẽ tăng mức sử dụng bộ nhớ. Hãy theo dõi tiến trình khi model đang được nạp và khi một request dài đang chạy.
free -m
ps -eo rss,comm --sort=-rss | head -n 5RSS (resident set size) được hiển thị theo kilobyte. Nếu swap đang dùng trong free -m bắt đầu tăng, hãy giảm context. KV cache nằm trong swap sẽ khiến quá trình sinh token bị dừng vài giây cho mỗi token, vì mỗi token mới phải đọc toàn bộ cache.
Nếu máy hết hoàn toàn bộ nhớ, kernel sẽ chọn tiến trình lớn nhất và kill tiến trình đó.
sudo dmesg | grep -i "killed process"Dòng có nội dung Out of memory: Killed process 1234 (ollama) nghĩa là context bạn yêu cầu không vừa bộ nhớ. Ollama thường từ chối trước khi đến bước này. Khi đó request sẽ fail với thông báo cho biết lượng bộ nhớ cần dùng và lượng bộ nhớ còn trống.
Trên máy có GPU, lỗi này ít rõ ràng hơn. Một phần layer sẽ tràn sang RAM hệ thống, ollama ps hiển thị phần phân bổ giữa CPU và GPU, còn throughput giảm mạnh. Mức giảm phụ thuộc vào phần cứng. Vì vậy, hãy đo số token mỗi giây trên chính máy của bạn ở từng mức context thay vì tin vào số liệu từ máy của người khác.
Thời gian prefill tăng nhanh hơn prompt
Prefill là phần xử lý input trước khi token output đầu tiên xuất hiện. Mỗi token trong prompt attend đến mọi token đứng trước nó, vì vậy tổng khối lượng xử lý tăng theo bình phương độ dài input. Khi tăng gấp đôi prompt, thời gian chờ token đầu tiên tăng hơn gấp đôi.
Response chứa số liệu đo, nên bạn không phải mặc nhiên tin điều đó.
jq -n --arg p "$LONG" '{model:"llama3.1:8b", prompt:$p, stream:false, options:{num_ctx:16384}}' |
curl -s http://localhost:11434/api/generate -d @- |
jq '{tokens: .prompt_eval_count, prefill_seconds: (.prompt_eval_duration/1000000000)}'Chạy lệnh đó với một prompt ngắn rồi chạy lại với prompt dài, sau đó chia số token cho số giây trong từng trường hợp. Trên VPS chỉ dùng CPU, prefill thường là phần chậm nhất của request có context dài. Giá trị token mỗi giây đo bằng prompt ngắn sẽ không dự đoán chính xác tốc độ này.
Concurrency gây ảnh hưởng rõ nhất trong trường hợp này. Mỗi request đang được phục vụ cần cache riêng. Vì vậy, lượng memory trong biểu đồ trên tính theo từng request chứ không phải theo server. Một request dài có thể chiếm toàn bộ máy, khiến các request ngắn phải xếp hàng phía sau. Hãy đặt OLLAMA_NUM_PARALLEL một cách có chủ đích, rồi đọc một LLM tự host có thể phục vụ bao nhiêu user đồng thời trước khi cùng tăng cả hai giá trị.
Mua lại context bằng cache nhỏ hơn
bytes_per_value trong công thức là một setting bạn có thể kiểm soát. FAQ của Ollama ghi rõ OLLAMA_KV_CACHE_TYPE, với f16 là giá trị mặc định ở mức 2 byte, cùng q8_0 ở mức 1 byte và q4_0 thấp hơn mức đó. Chuyển sang q8_0 sẽ giảm một nửa dung lượng cache, nên dòng 32k chỉ tốn 2 GiB thay vì 4 GiB. FAQ cũng ghi rõ OLLAMA_FLASH_ATTENTION=1; một số bản build yêu cầu setting này thì quantised cache mới có hiệu lực.
[Service]
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"Hãy xác nhận thay vì giả định: khởi động lại service, load model với cùng num_ctx như trước rồi so sánh RSS. Việc hỗ trợ phụ thuộc vào model và backend, nên nếu setting không làm thay đổi gì thì tổ hợp của bạn không được hỗ trợ. Tài liệu liệt kê các option này nhưng không đảm bảo kết quả về chất lượng, vì vậy hãy kiểm thử q4_0 với các prompt của chính bạn trước khi dựa vào nó. Nếu bạn đang tìm các knob này, Ollama và llama.cpp cung cấp chúng theo cách khác nhau.
Cách chọn num_ctx
- Đọc context tối đa của model, số layer và số head key/value từ
/api/show. - Tính số byte trên mỗi token theo công thức, rồi nhân với context bạn muốn dùng.
- Cộng thêm dung lượng weight, so sánh với RAM còn trống và giữ lại ít nhất 1 GiB cho các thành phần khác của máy.
- Đặt giá trị, load model, rồi xác nhận giá trị đã áp dụng bằng
ollama psvàprompt_eval_count. - Chạy workload thực tế trong khi theo dõi
free -m, rồi giảm một nửa context nếu swap bắt đầu hoạt động.
Hầu hết công việc cần ít context hơn mức người dùng thường cấp. Tóm tắt một báo cáo dài thường chỉ cần 16k. Một retrieval front end chèn 5 đoạn tài liệu hiếm khi vượt quá 8k. Coding agent đọc toàn bộ file là trường hợp thực sự cần 64k hoặc hơn. Với trường hợp này, bạn nên tính cấu hình máy dựa trên context, thay vì làm ngược lại. Nếu server còn mới, hãy bắt đầu từ một cài đặt Ollama đang hoạt động trên VPS rồi tinh chỉnh context sau khi model load ổn định.
FAQ
Độ dài context mặc định trong Ollama là bao nhiêu?
Giá trị này phụ thuộc vào bản build và phần cứng, vì vậy hãy kiểm tra thay vì mặc định một giá trị. FAQ của Ollama ghi nhận 4096 token, tài liệu tham chiếu Modelfile ghi nhận giá trị mặc định num_ctx là 2048, còn trang tài liệu về độ dài context ghi nhận giá trị mặc định được chọn theo VRAM khả dụng: 4k khi dưới 24 GiB, 32k từ 24 đến 48 GiB và 256k khi trên 48 GiB. VPS chỉ dùng CPU sẽ rơi vào mức thấp. ollama ps in context được áp dụng trên những bản build có cột này, còn prompt_eval_count trong API response xác nhận giá trị đó trên mọi bản build.
Vì sao Ollama bỏ qua phần đầu của prompt dài?
Vì prompt dài hơn context window, nên server đã cắt prompt trước khi model nhận được, nhưng không trả về lỗi. Gửi lại cùng prompt với num_ctx lớn hơn và theo dõi prompt_eval_count trong response tăng lên. Nếu con số đó không thay đổi, có thành phần nào đó giữa bạn và server đang tự đặt num_ctx. Trường hợp này thường gặp với chat frontend và agent framework.
Tăng num_ctx cần thêm bao nhiêu RAM?
Lấy độ dài context nhân với chi phí cache trên mỗi token, tức 2 * layers * kv_heads * head_dim * bytes_per_value. Với Llama 3.1 8B ở f16, giá trị này là 128 KiB cho mỗi token, nên 32k token cần 4 GiB và 128k đầy đủ cần 16 GiB ngoài phần weights. Cache được cấp phát khi model load, vì vậy num_ctx lớn vẫn chiếm lượng bộ nhớ đó ngay cả khi prompt của bạn luôn ngắn.
Context window lớn hơn có làm Ollama chậm hơn không?
Có, theo 2 cách. Công việc prefill tăng theo bình phương độ dài prompt, nên input dài làm chậm thời điểm nhận token đầu tiên nhiều hơn mức có thể suy ra từ độ dài của input. Cache lớn hơn cũng tranh chấp bộ nhớ: trên máy GPU, nó đẩy một phần layer vào RAM hệ thống; trên máy CPU, nó đẩy máy đến gần việc dùng swap hơn. num_ctx lớn dù không bao giờ được dùng hết vẫn chiếm bộ nhớ, nhưng không làm tăng thời gian prefill.
Có thể đặt num_ctx cố định cho một model không?
Có. Tạo một Modelfile chứa FROM llama3.1:8b và PARAMETER num_ctx 16384, rồi chạy ollama create llama3.1-16k -f ./Modelfile. Mọi client yêu cầu llama3.1-16k sẽ nhận context đó mà không cần gửi thêm option. Request có num_ctx riêng vẫn được ưu tiên, vì vậy đây là giá trị mặc định chứ không phải giới hạn tối đa.