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 cache(键值缓存)中。模型加载时,KV cache 会为整个 num_ctx 分配内存,而不是随着对话增长而分配。因此,即使提示词只有一行,较大的上下文也会占用相应内存。
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 模型比较,那么在仅使用 CPU 的 VPS 上计算 Qwen 的 27B 标签所示的相同数据,可以看出在 8 到 64 GB 内存之间,权重能为上下文留下的空间很少。
KV 缓存无法容纳时会发生什么
在仅使用 CPU 的 VPS 上,进程会继续占用内存。模型加载期间以及运行长请求时,监控其内存使用情况。
free -m
ps -eo rss,comm --sort=-rss | head -n 5RSS(常驻集大小)以千字节为单位显示。如果 free -m 中的 swap 使用量开始上升,请降低上下文大小。位于 swap 中的 KV 缓存会使生成每个 token 停顿数秒,因为每生成一个新 token 都要读取整个缓存。
如果服务器完全耗尽内存,内核会选择占用内存最多的进程并将其终止。
sudo dmesg | grep -i "killed process"显示 Out of memory: Killed process 1234 (ollama) 的行表示您请求的上下文无法容纳。Ollama 通常会在达到这一步之前拒绝请求,随后请求失败,并在错误消息中列出所需内存和可用内存。
在 GPU 服务器上,故障不太明显。部分层会溢出到系统 RAM,ollama ps 显示 CPU 与 GPU 的分配情况,吞吐量会大幅下降。具体下降幅度取决于硬件,因此请在每种上下文设置下,在自己的服务器上测量每秒生成的 token 数,不要直接采用其他机器上的数据。
预填充时间增长速度高于提示词长度
预填充是指在生成第一个输出 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 通常是长上下文请求中最慢的阶段,因此短提示词测得的 tokens per second 无法预测长上下文请求的性能。如果 prefill 耗时超过前置层设置的超时时间,长提示词通常会返回 上下文截止时间已超时,而不是生成答案。因此,在缩短上下文之前,应先确定是哪个层放弃了请求。
并发场景受此影响最大。每个正在处理的请求都需要自己的缓存,因此上图中的内存用量是每个请求的用量,而不是每台服务器的用量。一个长请求可能长期占用整台机器,使短请求在其后排队。请有意识地设置 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读取模型的最大上下文长度、层数以及键/值头数量。 - 使用该公式计算每个 token 占用的字节数,然后乘以所需的上下文长度。
- 加上权重大小,与可用 RAM 比较,并至少为服务器上的其他任务保留 1 GiB。
- 设置该值并加载模型,然后使用
ollama ps和prompt_eval_count确认实际生效的配置。 - 运行实际工作负载,同时监控
free -m;如果 swap 开始活动,则将上下文长度减半。
大多数任务所需的上下文都比用户设置的少。总结长篇报告时,16k 通常足够。检索前端粘贴 5 个文档片段时,很少会超过 8k。只有需要读取完整文件的编码代理确实可能需要 64k 或更多;对于这类任务,应根据上下文需求规划机器配置,而不是反过来。如果服务器本身还是新部署的,请先从 在 VPS 上完成可用的 Ollama 安装 开始,等模型能够正常加载后再调整上下文。
FAQ
Ollama 的默认上下文长度是多少?
这取决于构建版本和硬件,因此应先检查,不要直接假设。Ollama FAQ 记录的是 4096 个 token,Modelfile 参考文档记录的 num_ctx 默认值是 2048,而上下文长度页面记录的默认值取决于可用 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 个 token 需要 4 GiB,完整的 128k 个 token 则需要 16 GiB;这些内存是在模型权重之外额外占用的。模型加载时会分配缓存,因此即使提示词一直很短,较大的 num_ctx 仍会占用相应内存。
更大的上下文窗口会让 Ollama 变慢吗?
会,主要体现在两方面。预填充计算量会随提示词长度的平方增长,因此长输入会使首个 token 的生成延迟超过仅按长度估算的时间。更大的缓存也会争用内存:在 GPU 主机上,它会促使部分层移入系统 RAM;在 CPU 主机上,它会使系统更接近使用 swap。即使从未用满,较大的 num_ctx 仍会占用相应内存,但不会增加预填充时间。
可以为某个模型永久设置 num_ctx 吗?
可以。编写一个包含 FROM llama3.1:8b 和 PARAMETER num_ctx 16384 的 Modelfile,然后运行 ollama create llama3.1-16k -f ./Modelfile。所有请求 llama3.1-16k 的客户端都会获得该上下文长度,无需再发送任何选项。请求中自带的 num_ctx 仍具有更高优先级,因此这里设置的是默认值,而不是上限。