Ollama 提示词缓存——改在哪里,决定账单大小

Ollama 提示词缓存——改在哪里,决定账单大小

同一台全新虚拟机上跑了六个探针:完全重复的请求只重估 1,539 个 token,耗时 0.2 s,而冷启动那次为 75.6 s 买单——可一个词被挪到提示词中部,就又收回 49 s。缓存不是开关,它是一座边境关卡:你改在哪里,账单就开在哪里。六张账单摆在一起,差距 370×,而开关从未被碰过。

HUMAN作者 @msvram40m2 分钟读完

verify: 六个探针、一台全新虚拟机、seed 42 · temp 0 · ollama 0.40.16 · 回执在下方,日志 promptcache-2026-11-12.log

prompt-eval 秒数 · 1,539 token 提示词 · qwen3:4b · 2 vCPU · 冷请求 = 100% 基准线 冷基准 75.6 s 75.6 sP1 冷启动 0.2 sP2 重复 1.4 sP3 尾部 49.2 sP4 中部编辑 79.9 sP5 重排 4.2 sP6 近尾部

Fig 1 — 六个探针,同一条提示词。变暗的柱子就是回执:提示中部的编辑付掉了冷账单的 65%,而重排开出的价码比冷加载本身还高。

ollama 的提示词缓存到底存了什么?

不是你的文本。ollama 评估提示词时,会为每个 token 算一个 KV 张量——该 token 对其之前所有内容的注意力足迹——模型驻留期间,runner 把这些张量留在 RAM 里。新提示会与这份库存从头部开始、逐 token 比对。凡是精确延续旧前缀的 token 都免费。第一个不同的 token 就是边境:从那里到结尾的一切都按全价重读。这正是我们 KV-cache 那篇讲卸载时机时的同一份 KV 驻留——服务速度的缓存,就是占住 RAM 的缓存(而 keep_alive 决定两者各自活多久——账单里算尺寸的那一半归上下文窗口那篇)。

前缀命中——token 免费。 从位置 0 起逐字节相同的内容,重新处理不花一分钱。实测:1,539 个 token 用了 0.2 s。
第一处分歧——边境。 改一个 token,其后所有 token 全部作废。没有往回找的折扣;尾部永远重新计费。
驻留——keep_alive。 缓存与模型同寿。卸载模型,折扣随它入土;如果你的循环闲聊带停顿,就钉死 keep_alive。

为什么完全重复只花 0.2 秒?

探针是:一条 1,539 token 的提示词——八个编号章节的 fixture 日志事实,加一个问题——在全新虚拟机上冷启动提问。引擎以 20.4 tok/s 读完全部 1,539 个 token,提示评估计费 75.6 s。同一问题,模型仍驻留时原样重发:0.2 s。同一个 endpoint,同一个 body,什么都没拨动——ollama 不需要 flag、不需要 cache: true、不需要预热咒语就做到了。你每次重复提问都在享受这笔折扣;这篇讲的是怎么别把它交回去。

curl http://localhost:11434/api/generate -d '{
  "model": "qwen3:4b",
  "prompt": "<your 1,500-token prompt>",
  "keep_alive": "45m",
  "think": false,
  "options": {"seed": 42, "temperature": 0, "num_predict": 128}
}'

verify: P1 冷启动 75.6 s → P2 重复 0.2 s,探针 body 逐字节一致,模型驻留 · promptcache-2026-11-12.log

你改在哪里,决定你付多少

我们保住 1,539 token 的正文,只挪一个词,挪三种地方。改最后一句里的词:4.2 s——共享的头部活了下来,只有尾部被重读。在问题之后追加一句:1.4 s——近乎全中,五个新 token 按边际计费。改第 41/96 条事实里的一个词,正文中部:49.2 s——冷账单的 65%,只为挪动一个形容词。缓存有大象的记忆、海关关员的恕道:它记得一切,而一份填错的表格就作废小本子里所有印章——对错表格之后的一切作废。

探针改了什么prompt-eval整次请求对冷启动
P1 冷启动无——全新虚拟机,首次提问75.6 s111.8 s基准
P2 重复无——body 完全一致0.2 s26.6 s快 370×
P3 尾部问题后 +1 句1.4 s27.2 s快 54×
P4 中部编辑第 41/96 条事实里 1 个词49.2 s75.2 s冷账单的 65%
P5 重排章节 1–2 ↔ 7–8 对调79.9 s135.5 s比冷启动还糟
P6 近尾部最后一句里 1 个词4.2 s34.0 s快 18×

1539 token 提示词 · prompt-eval 是重读的账单;“整次请求”另加 128 token 的生成,全程稳在 ~25–29 s。

为什么重排比冷加载还贵?

因为边境在第 ~1 个 token。把头两章和末两章对调,共享前缀就是空的——匹配当场死亡,每个 token 重新计费。但 P5 计的是 79.9 s,不是 75.6 s:比它为之扔掉折扣的冷加载还高 5.7%。我们不打算假装抓到了元凶——诚实的备注是:那次探针的生成过程也慢了 2.1×(55.4 s,其余每次请求都是 25.5–29.4 s,同 seed、同预算)。热机状态会以缓存示意图画不出的方式加入账单;确定性那篇已在 temp 0 下领教过这位证人,翻车的是一个 token。我们能作为回执声明的是:重排过的提示词不是“同一条提示词,换个顺序”。它是一张全新的账单,外加噪声。

修正清单

症状正在发生什么修正
每次请求都像冷加载模型在请求之间被卸载——缓存随之死亡调高 keep_alive;查看回复里的 load_duration
提示词改动极小,延迟巨大编辑落在提示中部;尾部从分歧点起重新计费把易变部分挪到提示词尾部
“同一条提示词”每次 run 计费不同顺序或模板内容在请求间漂移了把头部逐字节冻结;diff 提示词本身,别 diff 直觉
回执 · 六个探针,一台全新虚拟机(qwen3:4b · ollama 0.40.16 · 2 vCPU · 提示词 1,539 tok)
P1 cold-full-ask      prompt_eval 75,646 ms   (1,539 tok · 20.4 tok/s)  wall 111.8 s
P2 exact-repeat       prompt_eval    203 ms                             wall  26.6 s
P3 suffix-only        prompt_eval  1,402 ms   (+5 tok)                  wall  27.2 s
P4 mid-edit-kill      prompt_eval 49,176 ms   (1 word, fact 41/96)      wall  75.2 s
P5 reorder-full-miss  prompt_eval 79,878 ms   (secs 1–2 ↔ 7–8)          wall 135.5 s
P6 edit-near-end      prompt_eval  4,189 ms   (1 word, last sentence)   wall  34.0 s

一条提示词。六张账单,从 0.2 到 79.9 秒——370× 的落差,没动任何设置,没拨任何 flag。缓存一直开着。唯一的变量,是我们敢在哪里下笔修改。

最后测试 2026-11-12

qwen3:4b · 2,497,293,931 B · sha256 359d7dd4…74e7 · ollama 0.40.16 · 一台全新 Arch 虚拟机,2 vCPU——CPU 实测,不对 VRAM 作任何断言