



2026年6月17日,ollama 发布了 v0.30.9 最新版本。 从这次发布信息来看,虽然版本号只是一次常规迭代,但实际改动并不小,覆盖了模型架构支持、LFM2 解析与渲染修复、ollama 在启动 Claude 及其他 coding agent 或 assistant 场景下只输出一个 token 的问题修复,以及单条消息超过当前上下文窗口时直接返回错误的新机制。
从变更规模来看,本次版本一共包含 3 次提交,涉及 16 个文件,1 位贡献者,总代码层面为 280 行新增、103 行删除。 如果从工程角度看,这不是一次简单的 bugfix,而是一轮围绕兼容层、解析器、渲染器、调度器以及 llama-server 行为约束的系统性修订。
一、本次版本官方变更摘要
v0.30.9 的核心更新点可以概括为四项:
这四项看起来很简短,但背后实际牵涉到了多个模块:
二、提交与改动规模概览
本次发布对应的提交时间集中在 2026年6月15日 和 2026年6月16日。
提交内容主要包括:
从 diff 信息看,本次更新共涉及这些方向:
可以说,这个版本虽然改动文件数量不算夸张,但大部分都落在底层逻辑与边界行为上,因此对稳定性和兼容性影响非常直接。
三、支持 Cohere2Moe 架构:版本能力边界继续扩大
官方 changelog 的第一条就是:
这说明 v0.30.9 新增了对 Cohere2Moe 架构的支持。 虽然在用户给出的 diff 片段中,没有展开 Cohere2Moe 具体实现文件的完整细节,但这项支持已经明确列入本次版本变更说明,意味着在模型架构适配层面,ollama 的兼容能力又向前推进了一步。
结合本次 compat 层的组织方式调整,可以看出新增架构支持并不是零散加入,而是继续沿用“补丁加兼容实现”的方式推进。这一点从 compat 目录的变更就很明显。
四、llama.cpp 版本更新:底层依赖继续前进
本次有一个很基础但很重要的变化,就是 LLAMA_CPP_VERSION 从:
更新为:
也就是说,ollama v0.30.9 所依赖的 llama.cpp 版本进一步推进了。
这类变更的重要性在于,很多上层能力其实并不完全由 ollama 自己独立实现,尤其是模型加载、架构识别、UI 辅助工具、服务行为等,都会受到底层 llama.cpp 版本的直接影响。 因此,兼容层补丁、架构注册、工具链修复、上下文行为调整,往往都需要围绕这个版本基线同步演进。
这也是为什么本次更新里,compat 层相关文件变更这么集中。
五、compat 层补丁命名方式调整:从单补丁走向有序补丁集
本次一个非常明显的工程化调整,是补丁文件命名发生了变化。
原来的:
llama/compat/llama-cpp-hooks.patch现在重命名为:
llama/compat/001-llama-cpp-hooks.patch同时,新增了一个补丁文件:
llama/compat/002-llama-cpp-ui-empty-assets.patch此外,原来的:
llama/compat/models/llama-cpp-laguna.patch也重命名为了:
llama/compat/models/003-llama-cpp-laguna.patch这说明 compat 层已经从“某个单一补丁文件”的思路,转向“按数字顺序执行的一组补丁”的思路。 这个变化在文档和 CMake 逻辑中也有完整呼应。
原本 README 中对 patch 的说明,还是强调某一个 hooks patch。 更新后则明确指出:
*.patch 都会按数值顺序执行这个变化的重要性非常高,因为它让 compat 层从“零散 patch”变成“可维护的 patch pipeline”。
也就是说,后续新增架构、修补 UI 工具、加 loader hooks,不需要继续把所有修改塞进一个 patch 文件里,而是可以按顺序拆分成多个补丁,既方便维护,也方便判断哪一步出问题。
六、新增 UI 空资源补丁:没有前端资源时也能生成空资产表
新增补丁 002-llama-cpp-ui-empty-assets.patch 的内容比较明确,修改的是:
tools/ui/embed.cpp改动核心有两点:
asset_dir 不再强制要求必须传入第三个参数,而是当 argc == 4 时才使用 argv[3],否则设为空字符串use_gzip 的判断增加了 asset_dir 非空校验也就是:
原来逻辑默认认为一定存在资源目录。
现在则允许资源目录为空,并在这种情况下依然正常执行,只是不会去寻找 _gzip 目录。
这与 README 中新增的描述完全一致:
这个改动的实际意义是:
在某些构建环境下,如果没有准备 UI 静态资源,以前可能会因为 embed 流程强依赖 asset 目录而出错。 而现在,构建过程可以在“无 UI 资源”条件下继续进行,自动生成空资源表,增强了构建兼容性和可移植性。
七、compat 文档更新:清楚界定补丁层与新架构层
llama/compat/README.md 的更新,不只是名称替换,而是对整个 compat 设计意图进行了更清晰的说明。
几个关键信息包括:
001-llama-cpp-hooks.patch 是在 llama.cpp 文件中增加小范围、附加式调用点的补丁002-llama-cpp-ui-empty-assets.patch 用于在没有 UI assets 时生成空资产表compat.cmake 和 apply-patch.cmake 用来按数字顺序应用当前目录下所有 patchmodels/ 目录是“新增架构层”,用于补充 llama.cpp 暂不支持的新架构这个描述非常关键,因为它帮助开发者理解:
从工程维护角度说,这类文档更新的价值不低,因为它减少了后续继续迭代时的理解成本。
八、补丁应用脚本增强:按文件名排序,日志更清晰,失败信息更精准
llama/compat/apply-patch.cmake 这次修改非常值得关注。
核心变化包括:
_patch_entries,每个元素包含“文件名和完整路径”_patch_entries 排序后,再逐个提取 PATCH_FILE这几个调整看似细碎,实际对构建调试帮助很大。
以前如果目录里 patch 变多,单看“已跳过”或者“已应用”很难知道是哪一个。 现在会明确显示具体的 patch 相对路径,问题定位成本明显下降。
另外,原逻辑虽然也能处理 *.patch,但现在进一步强调并落实了“按数字文件名顺序执行”的机制,这和补丁重命名成 001、002、003 形成了严格配套。
九、compat.cmake 同步更新:补丁文件路径改为新的编号名称
llama/compat/compat.cmake 的改动不多,但作用很明确:
OLLAMA_LLAMA_CPP_COMPAT_PATCH_FILE 指向的新路径由 llama-cpp-hooks.patch 改成 001-llama-cpp-hooks.patch这意味着整个构建系统已经正式接受新的补丁命名规则,旧文件名不再是主路径。
十、laguna 兼容层修正:调用参数从成员变量改为方法
llama/compat/models/003-llama-cpp-laguna.patch 与 llama/compat/models/laguna.cpp 也有更新。
在 laguna.cpp 中,这一行发生了变化:
hparams.n_layerhparams.n_layer()也就是:
ml.get_key_or_arr("laguna.attention.layer_types", hparams.is_swa_impl, hparams.n_layer(), false);
这通常意味着 n_layer 的访问方式发生了接口层调整,或者是需要通过方法而不是直接成员访问来获得层数。
虽然只是单行改动,但这种变化往往是为了适配底层结构定义变更,避免编译或运行时不一致。
同时,patch 文件重命名到 003 也说明 laguna 架构 patch 现在纳入统一的顺序补丁集管理。
十一、llama server prompt 处理逻辑变化:从自动截断改为直接报错
这次更新中,最值得用户关注的改动之一,就是 llm/llama_server.go 的 prompt 长度处理策略变了。
此前逻辑是这样的:
NumKeep 保留前部,再丢弃中间一段,拼接尾部而在 v0.30.9 中,这套逻辑被彻底删除,改为:
s.options.NumCtx 作为当前 runner 的有效上下文长度len(tokens) >= s.options.NumCtx,则直接返回一个 api.StatusErrorhttp.StatusBadRequestthe prompt is longer than the context length currently available to the model; shorten the prompt or adjust the context length in settings
也就是说,现在不再偷偷帮你截断输入,而是明确拒绝超限 prompt。
这个变化非常重要,因为它代表设计思路发生了变化:
这样做的优点是:
这也与 changelog 中“当单条消息大于当前上下文窗口时会返回错误”完全一致。
十二、对应测试同步改变:不再测试截断,而是测试拒绝请求
llm/llama_server_test.go 的测试名称也直接改了:
TestLlamaServerCompletionTruncatesPromptAsTokensTestLlamaServerCompletionRejectsPromptOverContext这说明测试意图已经完全变化。
新的测试重点验证:
api.StatusError400 Bad Request/completion 接口不应该被调用也就是说,当 tokenized prompt 已经超过上下文长度时,流程会在进入 completion 前就被拦截掉。
旧测试里还会检查被截断后的 token 数组是否符合预期,例如保留哪些 token。 这些检查现在全部删除,因为新的行为已经不再是“截断后继续生成”,而是“直接拒绝”。
这个改动对使用者来说非常关键,特别是依赖长 prompt、长 system message 或长上下文拼接的应用,要开始适应“超限显式报错”的新规则。
十三、修复 ollama 启动 Claude 等 coding agent 只输出一个 token 的问题
官方 changelog 中还有一条非常关键:
虽然用户给出的 diff 片段没有单独展开这条问题在某个具体文件中的完整上下文说明,但结合本次上下文处理与 context shift 策略调整,可以看到该问题已经作为本版本核心修复项之一被明确列出。
也就是说,这次更新除了提升稳定性和兼容性,还直接修复了某些 assistant、coding agent 典型场景下输出异常短、只生成一个 token 的问题。 对于依赖这类场景的用户,这个修复价值非常直接。
十四、LFM2 parser 重大修复:thinking 现在是真正“可选”的了
本次版本中,LFM2 parser 的修复是最系统的一部分之一。 核心目标就是修复“模型没有输出 thinking 时”的解析行为。
以前逻辑中的初始状态是:
LFM2CollectingThinking这意味着一旦 thinking 功能开启,parser 会默认认为模型会进入 thinking 采集模式。
但现实问题在于,LFM2 模型并不是每次都会输出 <think> 标签。
有些回合会直接回答,没有显式思维段。
因此这次新增了一个全新的初始状态:
LFM2LookingForThinking它的注释写得很清楚:
<think> 标签<think> 开头,再决定后续进入 thinking 还是 content 采集状态这实际上是在修复一个非常本质的问题:
十五、LFM2 初始化逻辑变化:不再默认进入 thinking 采集
在 setInitialState 中,原来当 thinking 启用时会做两件事:
p.state = LFM2CollectingThinkingp.needsThinkingLeadingTrim = true而现在改为:
p.state = LFM2LookingForThinking注释说明得也很明确:
<think> 标签这个改动让 parser 的初始化行为更符合 LFM2 的真实输出模式。
十六、LFM2 在最终 chunk 的处理更聪明:不再无意义等待不完整 think 标签
Add 方法里新增了一段重要逻辑:
done 为 true,且当前状态仍然是 LFM2LookingForThinking<think> 开头LFM2CollectingContent这意味着:
如果流式输出已经结束,但 parser 还在犹豫“会不会出现 think 标签”,那么到了最后一个 chunk,它必须做出判断。
而只要最终并没有形成 <think> 前缀,就不能继续把内容扣在缓冲区里,而要把它当作直接回答释放出来。
这对于 direct answer 场景尤其关键,避免了 thinking 模式下正常回答被错误吞掉或延迟输出。
十七、LFM2 状态机新增分支:显式区分 reasoning turn 和 direct answer turn
在 eat() 中,新增了 LFM2LookingForThinking 分支,逻辑非常完整:
<think> 开头,就切入 LFM2CollectingThinking<think> 的部分前缀,比如 <th,则继续等待更多数据<think>,那就认定这是 direct answer这个设计非常适合流式场景,因为流式输出里标签经常会被拆开分片,比如:
<think>以前如果 parser 没有中间态,就很容易误判。 现在它会在“部分前缀”阶段保持等待,在“足够判断不是 think 标签”时立即切到 direct answer。
十八、LFM2 测试全面补强:大量样例围绕“没有 thinking 标签”的情况展开
model/parsers/lfm2_test.go 这次增加和调整了大量测试用例,几乎可以看作对 parser 行为的重新校验。
重点变化包括:
<think> 标签,现在统一补上direct_answer_with_thinking_enableddirect_answer_with_tool_callstreaming_direct_answerstreaming_thinking_split_open_tagstreaming_direct_answer_starting_with_angle< 开头时,前期会与 <think> 存在歧义<think> 后再输出为普通内容LFM2CollectingThinking 改成 LFM2LookingForThinkingempty_thinking_content 被改成 empty_thinking_block</think> 改为合法的 <think></think>Just contentdirect_answer_with_leading_whitespace这些测试变化说明,本次修复不是打补丁式地修一两个条件判断,而是把 LFM2 thinking 机制按“可选思考、可直接回答、支持流式拆标签”的真实行为重新梳理了一遍。
十九、LFM2 renderer 修复:可从 Thinking 字段重建 think 标签
除了 parser,model/renderers/lfm2.go 也有明显增强。
这次先新增了两个常量:
lfm2ThinkingOpenTag = "<think>"lfm2ThinkingCloseTag = "</think>"最关键的逻辑新增是:
当满足以下条件时:
r.IsThinking 为 trueassistantmessage.Thinking 不为空content 中又还没有包含 </think>则会自动把 content 重建为:
<think> + Thinking + </think> + content官方注释写得也非常清楚:
<think>...</think> 块这个变化说明,renderer 现在能更正确地处理“消息对象里的 Thinking 字段”和“模型原始模板里的 think 标签”之间的转换关系。
二十、过去 assistant 消息的 reasoning 清理逻辑同步调整
此前 renderer 就有一段逻辑,用于在不保留历史 thinking 时,去掉旧 assistant 消息中的 reasoning 内容。
不过那时是直接查找字面量 </think>。
现在逻辑更统一了:
lfm2ThinkingCloseTag这个改动配合前面的“Thinking 字段重建”就很顺了:
<think>...</think>这样不仅逻辑更统一,也避免了 thinking 信息在多轮历史中的格式不一致问题。
二十一、LFM2 renderer 测试新增多种关键场景
model/renderers/lfm2_test.go 这次增加了大量测试,重点围绕“Thinking 字段重建”和“是否保留历史 thinking”两个问题。
新增的重要测试包括:
thinking_field_reconstructed_when_enabled<think>reason</think>thinking_field_stripped_for_non_last_when_disabledthinking_precedes_tool_callsdirect_answer_history_has_no_think_tagsnon_thinking_renderer_drops_thinking_metadata_on_prefillarbitrary_roles_are_rendered_verbatim这些测试与 parser 侧的修复共同说明: v0.30.9 不是只修“解析”,而是把“解析”和“渲染”两个方向都对齐了,保证有 thinking 和没 thinking 的两类 LFM2 输出都能闭环工作。
二十二、server 调度策略变化:context shift 默认行为被重新定义
server/sched.go 的改动,是本次另外一个核心变化。
之前存在一个常量:
contextShiftSmallContextLimit = 8192旧的 resolveContextShift 逻辑是:
shift,就以它为准numCtx > 0 且 numCtx < 8192 时,默认启用 context shift也就是说:
而现在,这整套逻辑被简化成:
resolveContextShift(shift *bool) bool { return shift == nil || *shift }也就是:
不再依赖上下文长度,也不再区分是否大于 8k。
这就是 changelog 中“context shift for context windows larger than 8k”的关键落地点。 它意味着对于更大的 context window,默认策略不再自动禁用 shift,而是统一按“默认开启、除非明确关闭”的方式处理。
二十三、多处 context shift 计算同步改造:不再传入 numCtx
由于 resolveContextShift 的签名变了,调度流程中多个调用点也全部更新:
getRunner 中不再传 opts.NumCtxload 中不再传 effectiveModelContext(launchOpts.NumCtx, f)req.opts.NumCtx 后,也不再用 effectiveNumCtx 再判断 shiftneedsReload 中也不再依据 optsNew.NumCtx 重新推导 context shift简而言之,现在 context shift 的决定权只取决于:
shift如果没有显式关闭,那么默认就是开。
这使得调度逻辑更简单,也减少了不同上下文大小下行为不一致的问题。
二十四、context shift 测试同步重写:从“小上下文启用、大上下文禁用”改为“默认启用”
server/sched_test.go 的测试也对应重写了。
旧测试覆盖的是:
新测试改成只验证三种情况:
这意味着“8192 这个阈值”在行为层面已经不再是决策依据。
二十五、本次更新如何理解:不是加功能那么简单,而是在统一边界行为
把所有改动串起来看,v0.30.9 的价值并不只是新增了某个模型架构,或者修了一个 parser bug。 它更像是在统一几个关键边界:
这些看似分散,但本质上都围绕一件事:
二十六、本次版本所有重点变更清单汇总
为了便于快速复盘,可以把 v0.30.9 的变更完整总结为以下内容:
LLAMA_CPP_VERSION 从 b9509 更新到 b9626llama-cpp-hooks.patch 重命名为 001-llama-cpp-hooks.patch002-llama-cpp-ui-empty-assets.patchllama-cpp-laguna.patch 重命名为 003-llama-cpp-laguna.patchembed.cpp 支持没有 UI assets 时生成空资产表laguna.cpp 中 hparams.n_layer 改为 hparams.n_layer()LFM2LookingForThinking 初始状态<think> 开标签<think>...</think> 逻辑二十七、结语
代码地址:github.com/ollama/ollama
如果只看 changelog,ollama v0.30.9 似乎是一次“支持新架构加若干修复”的常规更新。 但从具体 diff 来看,这个版本真正有价值的地方在于,它对多个底层边界行为做了统一和纠正: