SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-10

Ollama 如何修改 num_ctx 上下文长度限制

Ollama 默认上下文窗口常导致长提示词被静默截断。本文介绍如何通过 num_ctx 参数调整限制,并提供检查服务器实际上下文容量的方法,帮助你避免因显存不足导致的模型性能下降或推理错误。

num_ctx 的作用以及长提示词被截断的原因

Ollama 的上下文长度是指加载的模型一次性可以存入内存的 token 数量,而 num_ctx 是设置该值的选项。Ollama 默认选择的值远低于模型所标称的最大值,因此较长的提示词在模型读取之前就会被截断。响应中没有任何提示会告知你发生了这种情况。

Llama 3.1 8B 在 Ollama 模型库中列出的上下文窗口为 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 这样的拆分意味着权重加上缓存已无法放入显存,而提高 num_ctx 通常是导致此问题的原因。

journalctl -u ollama --no-pager | grep -i n_ctx | tail -n 5

推理运行程序会在包含 n_ctx 的行中打印其上下文大小。具体措辞会随版本更新而变动,因此如果未找到该行,应视为重命名,而非证明了任何问题。

设置 num_ctx 的四个位置

在请求中。 发送 "options": {"num_ctx": 16384}/api/generate/api/chat。此设置优先级最高,且仅对该次调用生效。如果该值与当前加载模型的运行参数不同,服务器会先重新加载模型,这可以在响应的 load_duration 中观察到:其数值会从接近 0 瞬间跳升至数秒。

在交互式会话中。ollama run 内部,输入 /set parameter num_ctx 16384。该设置仅在当前会话期间有效。

在 Modelfile 中。 这会将该值固化到指定的模型中,因此每个客户端无需进行任何更改即可使用该值。

FROM llama3.1:8b
PARAMETER num_ctx 16384
ollama create llama3.1-16k -f ./Modelfile
ollama run llama3.1-16k

在服务器上。 OLLAMA_CONTEXT_LENGTH 为所有未携带自身 num_ctx 的请求设置默认值。在使用 systemd 时,请添加 drop-in 文件,不要直接编辑单元文件。

sudo systemctl edit ollama.service
[Service]
Environment="OLLAMA_CONTEXT_LENGTH=16384"
sudo systemctl daemon-reload
sudo systemctl restart ollama
ollama ps

当你在调试他人的客户端时,优先级问题最为关键。携带 num_ctx 的请求会覆盖服务器默认值,因此聊天前端或代理如果发送了较小的值,会悄悄抵消你在 systemd 中所做的更改。当你 将编程代理指向你的 Ollama 服务器 时,在质疑服务器之前,请先检查客户端发送的内容。

为什么不能直接将 num_ctx 设置为模型的最大值

注意力机制(Attention)要求每个 token 都参考之前的所有 token。为了避免为每个新 token 重复计算,系统会保留之前 token 的键(key)和值(value),即 KV 缓存(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 个键/值头。头维度(head dimension)由 embed 除以 heads 得出,即 4096 / 32 = 128,部分模型会直接发布为 llama.attention.key_length。默认缓存存储 f16 类型的值,因此 bytes_per_value 为 2。计算得出 2 32 8 128 2 = 131,072 字节。这意味着每增加一个上下文 token,就需要 128 KiB 的缓存。将此数值乘以上下文长度,成本就不再是抽象的概念了。

ChartLlama 3.1 8B at f16: KV cache and total RAM by context length, in GiB
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 库在 2026 年 8 月列出的 llama3.1:8b 下载大小(4.9 GB,即 4.6 GiB),但未计入计算缓冲区和服务器进程本身的开销。请将其视为最低内存需求。

核心在于内存占用规模。在 8k 上下文时,缓存仅占用 1 GiB,相对于模型权重而言微不足道。但在模型支持的 128k 上下文上限时,缓存占用高达 16 GiB,是模型权重大小的三倍多,总内存需求接近 20.6 GiB。因此,4 GB 的 VPS 无法在任何有效上下文长度下加载此模型。8 GB 的 VPS 在 8k 上下文下运行良好。16 GB 的 VPS 可以达到 32k 上下文,并留有余量供系统其他进程使用。所有这些阈值都会随模型权重大小而变动。如果您正在评估比 8B 更大的模型,可以参考 在仅 CPU 的 VPS 上运行 Qwen 27B 的计算过程,了解在 8 GB 到 64 GB 内存之间,权重占用后留给上下文的空间有多小。

当 KV cache 空间不足时会发生什么

在仅使用 CPU 的 VPS 上,进程内存占用会持续增长。请在模型加载及处理长请求时监控该进程。

free -m
ps -eo rss,comm --sort=-rss | head -n 5

RSS(常驻内存集)以千字节为单位显示。如果 free -m 中的 swap 使用量开始攀升,请减小上下文长度。如果 KV cache 存放在 swap 中,生成速度会降至每 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 上,预填充通常是长上下文请求中最耗时的部分,仅凭短提示词测得的每秒 token 数无法预测长提示词的性能。

并发处理是受此影响最严重的场景。每个正在处理的请求都需要占用独立的缓存,因此上图中的内存占用是按请求计算的,而非按服务器计算。一个长请求可能会占用计算资源,导致短请求在队列中等待。请谨慎设置 OLLAMA_NUM_PARALLEL,并在提高该数值前阅读 自托管 LLM 可支持的并发用户数

通过更小的缓存找回上下文

公式中的 bytes_per_value 是一个可控设置。Ollama 的 FAQ 文档记录了 OLLAMA_KV_CACHE_TYPE,其中 f16 为默认值(2 字节),此外还有 q8_0(1 字节)及更低的 q4_0。切换到 q8_0 可将缓存减半,因此 32k 行的开销将从 4 GiB 降至 2 GiB。同一份 FAQ 还记录了 OLLAMA_FLASH_ATTENTION=1,某些构建版本在启用量化缓存前需要此项。

[Service]
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"

确认而非假设:重启服务,以与之前相同的 num_ctx 加载模型,并对比 RSS。支持情况取决于模型和后端,如果设置后没有变化,说明你的组合不支持该功能。文档列出这些选项时并未保证质量,因此在依赖它们之前,请针对你自己的提示词测试 q4_0。如果这些参数正是你关注的内容,请参阅 Ollama 和 llama.cpp 对它们的公开方式不同

选择 num_ctx 的方案

  1. /api/show 读取模型的最大上下文长度、层数以及键值(key/value)头数。
  2. 使用公式计算每个 token 占用的字节数,然后乘以您所需的上下文长度。
  3. 加上模型权重大小,与剩余内存进行对比,并至少预留 1 GiB 内存供系统其他进程使用。
  4. 设置该值,加载模型,然后通过 ollama psprompt_eval_count 确认实际应用的配置。
  5. 在运行实际工作负载时监控 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 需要多少额外内存?

将上下文长度乘以每个 token 的缓存成本,即 2 * layers * kv_heads * head_dim * bytes_per_value。以 f16 精度下的 Llama 3.1 8B 为例,每个 token 的成本为 128 KiB,因此 32k 个 token 需要 4 GiB,而完整的 128k 则需要在模型权重之外额外占用 16 GiB。缓存是在模型加载时分配的,因此即使你的提示词很短,较大的 num_ctx 也会占用相应的内存。

更大的上下文窗口会降低 Ollama 的速度吗?

是的,主要体现在两个方面。预填充(prefill)的工作量随提示词长度的平方增长,因此长输入导致的首次 token 生成延迟远超其长度本身所暗示的程度。此外,更大的缓存会争夺内存资源:在 GPU 服务器上,这会将模型层挤压到系统内存中;在 CPU 服务器上,则会迫使机器使用交换分区(swap)。即使你从未填满较大的 num_ctx,它依然会占用内存,尽管不会增加预填充时间。

我可以为单个模型永久设置 num_ctx 吗?

可以。编写一个包含 FROM llama3.1:8bPARAMETER num_ctx 16384 的 Modelfile,然后运行 ollama create llama3.1-16k -f ./Modelfile。此后,任何请求 llama3.1-16k 的客户端都将获得该上下文设置,无需额外发送选项。如果请求中携带了自定义的 num_ctx,则以请求中的值为准,因此这设置的是默认值而非上限。