Ollama 如何设置 num_ctx 上下文长度
Ollama 默认上下文可能只有 2048 或 4096 token,长提示词会静默截断。了解如何按请求或全局设置 num_ctx,并在增大前估算 KV 缓存所需内存。
num_ctx 的作用,以及长提示词为何被截断
Ollama 上下文长度是已加载模型一次可保存在内存中的 token 数量,num_ctx 是用于设置该值的选项。Ollama 选择的默认值远低于模型声明的最大上下文长度,因此较长的提示词会在模型读取前被截断。响应中不会提示发生了截断。
Ollama 模型库将 Llama 3.1 8B 列为支持 128k 上下文窗口。但标准服务器不会提供这一长度。Ollama 官方文档在不同页面上给出了不同的默认值:FAQ 说明为 4096 个 token,Modelfile 参考文档说明 num_ctx 默认为 2048,而上下文长度页面说明默认值取决于可用 VRAM(显存):低于 24 GiB 时为 4k,24 至 48 GiB 时为 32k,高于 48 GiB 时为 256k。这些值分别曾适用于某些版本。这里的实际教训是:从您自己的运行中服务器读取该值,不要盲目相信任何页面,包括本页。
截断不易察觉,因为模型仍会回答,而且回答读起来仍然通顺。答案实际上是根据输入末尾部分生成的。遗漏文档前半部分的摘要看起来像是模型能力较弱。通常,真正的原因是上下文窗口过小。
检查服务器实际应用的 Ollama 上下文长度
适用于任何构建版本的检查方法是 prompt_eval_count,它表示服务器报告已处理的提示词 token 数量。发送超过上下文容量的内容后,该数字会停在限制值。
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}'该提示词约有 18,000 个单词,远超 4096 个 token。prompt_eval_count 返回的值接近 4096,而不是接近实际 token 数量,因为服务器丢弃了其余内容。使用 "num_ctx":16384 再次运行后,计数会升高。如果您的构建版本返回错误而不是截断内容,结论相同,只是信号更明显。
ollama ps在会输出该列的构建版本中,CONTEXT 列表示当前加载模型实际使用的上下文长度。旁边的 PROCESSOR 列表示模型所处的位置。在没有 GPU 的 VPS 上,100% CPU 属于正常情况。在配备 GPU 的服务器上,如果显示类似 30%/70% CPU/GPU 的拆分,表示权重和缓存已无法全部放入 VRAM;通常原因是提高了 num_ctx。
journalctl -u ollama --no-pager | grep -i n_ctx | tail -n 5推理运行器会在包含 n_ctx 的行中输出上下文大小。不同版本的具体措辞可能变化,因此缺少该行时,应将其视为名称变更,而不能据此证明任何结论。
设置 num_ctx 的 4 个位置
在请求中设置。 将 "options": {"num_ctx": 16384} 发送到 /api/generate 或 /api/chat。此设置优先级最高,并且只对当前请求生效。如果该值与已加载模型当前使用的值不同,服务器会先重新加载模型。您可以在响应的 load_duration 中看到这一点:该值会从接近 0 跳到整秒级别。当模型空闲时间过长而被卸载时,也会出现相同的等待。因此,确定上下文大小后,建议通过 keep_alive 让模型保持驻留。
在交互式会话中设置。 在 ollama run 中输入 /set parameter num_ctx 16384。该设置仅在当前会话中持续有效。
在 Modelfile 中设置。 这会将该值写入命名模型,因此所有客户端都能使用该值,无需在客户端修改配置。
FROM llama3.1:8b
PARAMETER num_ctx 16384ollama create llama3.1-16k -f ./Modelfile
ollama run llama3.1-16k在服务器上设置。 OLLAMA_CONTEXT_LENGTH 会为所有未携带自身 num_ctx 的请求设置默认值。在 systemd 下,应添加 drop-in,而不是编辑 unit 文件。
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_CONTEXT_LENGTH=16384"sudo systemctl daemon-reload
sudo systemctl restart ollama
ollama ps调试他人编写的客户端时,优先级尤其重要。携带 num_ctx 的请求会覆盖服务器默认值,因此聊天前端或 agent 如果自行发送较小的值,就会静默覆盖您在 systemd 中的修改。将编码 agent 指向您的 Ollama 服务器时,应先检查客户端发送的值,再判断服务器是否存在问题。
为什么不能直接将 num_ctx 设为模型最大值
注意力机制会让每个 token 查看它之前的每个 token。系统会保留早期 token 计算出的键和值,因此生成每个新 token 时无需重复计算。这个存储区称为 KV 缓存(键/值缓存)。模型加载时,系统会为整个 num_ctx 分配 KV 缓存,而不是随着对话增长再分配。因此,即使提示词只有一行,较大的上下文也会占用相应的内存。
DigitalOcean 的推理成本教程用一行公式说明了计算方法:
kv_bytes_per_token = 2 * layers * kv_heads * head_dim * bytes_per_value公式中的 2 分别计算键和值。其他数值请根据你自己的模型读取。
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 有 32 个层和 8 个键/值头。头维度等于 embed 除以 heads,因此这里是 4096 / 32 = 128;有些模型会直接将其发布为 llama.attention.key_length。默认缓存使用 f16 值,因此 bytes_per_value 为 2。计算 2 32 8 128 2,结果为 131,072 字节。上下文中的每个 token 都需要 128 KiB 的缓存。将其乘以上下文长度后,成本就不再是抽象数字。
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 行是根据上述公式计算出的结果,不是实测值。总计列加上了 Ollama 库在 August 2026 为 llama3.1:8b 列出的 4.9 GB 下载量,也就是 4.6 GiB;其中未计入计算缓冲区和服务器进程本身。请将其视为最低值。
重点在于增长趋势。在 8k 上,缓存占用 1 GiB,与权重相比可以忽略不计。在模型完整的 128k 上,缓存占用 16 GiB,超过权重的 3 倍,总内存需求接近 20.6 GiB。因此,4 GB VPS 无法在任何实用的上下文长度下加载此模型。8 GB VPS 在 8k 上运行较为宽裕。16 GB VPS 可达到 32k,同时为系统其他部分保留内存。这些阈值都会随着权重增加而提高。因此,如果你在比较更大的模型和这个 8B 模型,Qwen 的 27B 标签在纯 CPU VPS 上的计算结果可以说明,在 8 到 64 GB 内存之间,权重会为上下文留下多少空间。
KV cache 无法容纳时会发生什么
在仅使用 CPU 的 VPS 上,进程的内存占用会持续增长。模型加载期间和长时间运行请求期间,都应监控内存使用情况。
free -m
ps -eo rss,comm --sort=-rss | head -n 5RSS(常驻集大小)以 KB 为单位显示。如果 free -m 中的 swap 使用量开始增长,应减小上下文大小。位于 swap 中的 KV cache 会使生成过程每个 token 停顿数秒,因为每生成一个新 token 都要读取整个 cache。
如果服务器的内存完全耗尽,内核会选择占用内存最多的进程并将其终止。
sudo dmesg | grep -i "killed process"显示 Out of memory: Killed process 1234 (ollama) 的行表示请求的上下文无法容纳。Ollama 通常会在达到这一步前拒绝请求,随后请求失败,并显示所需内存和当前可用内存。
在配备 GPU 的服务器上,失败现象不那么明显。部分层会溢出到系统 RAM,ollama ps 会显示 CPU 和 GPU 的分配情况,吞吐量则会大幅下降。具体下降幅度取决于硬件,因此应在每种上下文设置下测量自己服务器每秒生成的 token 数,不要直接采用其他机器上的数据。
Prefill 时间增长速度高于提示词长度
Prefill 是在第一个输出 token 出现前,对输入执行的处理。每个提示词 token 都会关注它之前的所有 token,因此总计算量会随输入长度的平方增长。提示词长度翻倍时,首 token 等待时间会增加到两倍以上。
响应中包含测量结果,因此无需盲目信任该结论。
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)}'使用短提示词运行一次,再使用长提示词运行一次,然后分别用 token 数除以秒数。在仅使用 CPU 的 VPS 上,Prefill 通常是长上下文请求中最慢的阶段,用短提示词测得的每秒 token 数无法预测长提示词的性能。Prefill 持续时间超过前置层设置的超时时,长提示词通常会返回 上下文截止时间已 exceeded,而不是答案。因此,在缩短上下文前,应先确定是哪个层放弃了请求。
并发请求会放大这个问题。每个正在处理的请求都需要独立的缓存,因此上图中的内存用量是每个请求的用量,而不是每台服务器的用量。一个长请求可能占满服务器资源,使短请求在后面排队。应有意设置 OLLAMA_NUM_PARALLEL,并先阅读 一台自托管 LLM 可以服务多少并发用户,再同时提高这两个数值。
Buy context back with a smaller cache
bytes_per_value in the formula is a setting you control. Ollama's FAQ documents OLLAMA_KV_CACHE_TYPE, with f16 as the default at 2 bytes, plus q8_0 at 1 byte and q4_0 below that. Moving to q8_0 halves the cache, so the 32k row costs 2 GiB instead of 4 GiB. Quantising the weights frees memory from the other side of the same budget, and the GLM tag that actually fits a VPS is worked through quantisation by quantisation if that is the trade you would rather make. The same FAQ documents OLLAMA_FLASH_ATTENTION=1, which some builds want before a quantised cache takes effect.
[Service]
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"Confirm rather than assume: restart the service, load the model at the same num_ctx as before, and compare RSS. Support depends on the model and on the backend, so a setting that changes nothing means your combination is not covered. The documentation lists these options without promising a quality result, so test q4_0 against your own prompts before you rely on it. If these knobs are the reason you are here, Ollama and llama.cpp expose them differently.
选择 num_ctx 的方法
- 从
/api/show中读取模型的最大上下文长度、层数以及 key/value 头数量。 - 使用该公式计算每个 token 占用的字节数,然后乘以所需的上下文长度。
- 加上模型权重大小,与可用 RAM 比较,并至少为服务器上的其他进程保留 1 GiB。
- 设置该值并加载模型,然后使用
ollama ps和prompt_eval_count确认实际生效的配置。 - 运行实际工作负载,同时监控
free -m;如果开始发生 swap,将上下文长度减半。
大多数任务所需的上下文都比用户设置的更少。总结一份长报告时,16k 通常足够。检索前端粘贴五个文档片段时,很少会超过 8k。只有需要读取完整文件的编码代理,才确实可能需要 64k 或更大的上下文;对于这种场景,应根据上下文需求规划机器规格,而不是反过来。如果服务器刚开始使用,请先按照 在 VPS 上完成可用的 Ollama 安装,等模型能够稳定加载后再调整上下文。
FAQ
Ollama 的默认上下文长度是多少?
这取决于构建版本和硬件,因此应先检查,不要直接假设。Ollama FAQ 记录的值是 4096 tokens,Modelfile 参考文档记录的 num_ctx 默认值是 2048,context length 页面则记录了根据可用 VRAM 选择的默认值:低于 24 GiB 时为 4k,24 到 48 GiB 时为 32k,高于 48 GiB 时为 256k。仅使用 CPU 的 VPS 会采用较小的值。对于包含该列的构建版本,ollama ps 会显示实际应用的上下文长度;API 响应中的 prompt_eval_count 则可在所有构建版本中确认该值。
为什么 Ollama 会忽略长提示词的开头?
因为提示词长度超过了上下文窗口,服务器在模型读取前截断了提示词,而且不会返回错误。使用更大的 num_ctx 再次发送相同的提示词,并观察响应中的 prompt_eval_count 是否增大。如果该值没有变化,说明客户端与服务器之间的某个组件自行设置了 num_ctx。聊天前端和代理框架经常会这样做。
更大的 num_ctx 需要多少额外 RAM?
将上下文长度乘以每个 token 的缓存开销,即 2 * layers * kv_heads * head_dim * bytes_per_value。对于 f16 的 Llama 3.1 8B,该开销为每个 token 128 KiB,因此 32k tokens 需要 4 GiB,完整的 128k 需要 16 GiB;这些内存是在权重之外额外占用的。模型加载时会分配缓存,因此较大的 num_ctx 即使在提示词较短时也会占用相应内存。
更大的上下文窗口会让 Ollama 变慢吗?
会,主要体现在两个方面。Prefill 工作量会随提示词长度的平方增长,因此长输入会让首个 token 的生成延迟超过单纯按输入长度估算的结果。更大的缓存也会争用内存:在 GPU 服务器上,它会使部分层转移到系统 RAM;在 CPU 服务器上,它会使机器更容易使用 swap。即使从未填满,较大的 num_ctx 仍会占用内存,但不会增加 prefill 时间。
可以为某个模型永久设置 num_ctx 吗?
可以。编写一个包含 FROM llama3.1:8b 和 PARAMETER num_ctx 16384 的 Modelfile,然后运行 ollama create llama3.1-16k -f ./Modelfile。所有请求 llama3.1-16k 的客户端都会使用该上下文长度,无需发送任何选项。请求中自行携带的 num_ctx 仍然优先,因此这设置的是默认值,而不是上限。