作者通过修改Modelfile(676行精简到668行),解决模型"先出答案再推理"导致数值计算错误的问题,是本地大模型调优的深度实践。
Flash Onyx 2.4 已上线 Ollama。
和 2.3 一样的 gemma4 权重。同样十行 PARAMETER,一直精确到 repeat_last_n 256。Modelfile 从 676 行缩到 668 行,却多了 324 个词——这本身就说明了这是一个怎样的版本:删了 81 行,加了 73 行,底下什么都没动。
我没有做任何训练。我花了好几周和一份文本文件较劲,最后出来的东西是我跑过的最好的本地模型。
这是 2.3 的 speed 规则,当时我很得意:
先说答案。裁决、数字、命令,或 auth.py:88 要放在开篇。理由在后面,如果还需要的话。
这是条好规则。但它也是我教自己的模型对我撒谎的方式。
问 2.3"8,240 的 15% 是多少","先说答案"就是在前四个词里放一个数字的指令。它还没有算出那个数字。于是它生成一个看起来形状对的东西,然后在下面做推理,大约三分之一的时候推理落到了别的地方。你得到一条回复,开头是 1,240,结尾是 1,236,这两个都比单独任何一个更糟,因为你现在得自己做算术来找出该信任哪半句。
2.4 把这条规则一分为二:
那个顺序是给你已经掌握的东西用的。还需要计算或推理才能得到的数字或裁决,永远不放在回复开头:短的步骤在前,最后一步用不同路径检查结果,答案最后出来,一次,因为一个在算出之前就说出来的数字是猜,而以一个数字开头然后算出另一个数字结尾的回复,比单独哪个都差。
对于它读过、跑过、或被告知的东西,"先说答案"仍然适用。对于需要算出来的东西,顺序颠倒。步骤、检查、答案,一次。
检查那部分是我会偷的部分。不是"验证你的工作"——每个模型都会点头但没人在做。是不同的路径:
用产生答案的那条路径之外的另一条路,试着打破你自己的答案:代回去、用具体值跑一遍代码、重新数一遍、用它不能违反的约束去检验(一个需要五分钟才能做出来的零件,无论有多少台机器,都不会做得更快)。
那句括号里的内容在做真正的工作。五台机器五分钟做五个零件;一百台机器做一百个零件要多久?任何一个按数字做模式匹配模型都会说一百分。约束是不能违反的:一个零件要五分钟,所以一百台机器仍然要五分钟,如果你要的零件数少于机器数,多余的机器就闲置着。2.4 把这写成了一类问题,而不是关于零件的事实。
这条让我把笔记本放下了一会儿。
问一个没有挂载任何工具的模型 ripgrep 是否有某个 flag,你会得到"我检查过了,没有这个 flag。"它没有检查。没有什么可以用来检查。这句话命名了一个不存在的来源,这是模型能有的最坏失败模式,因为一个捏造的来源和真实的来源读起来完全一样,而且还带着真实东西的自信。
"我跑了"、"我检查了"、"帮助输出显示":每一种都声称本轮对话里发生了一次工具调用,而背后没有任何工具,那就是捏造的来源,最坏的那种。诚实的形式是"凭记忆,ripgrep 没有这个 flag;rg --help | grep frob 可以确认":你记得的东西,标出来,再加上检查它的命令。
同样的思路延伸到 ledger,这是 2.3 用来报告任何多部分内容的格式:
DONE 行要带上证明它的东西,加引号:测试输出、日志行、grep 匹配。后面什么都没引用的 DONE 是捏造进展的样子。
我曾经写过一整篇文章叫《你的 AI agent 没完成。它只是告诉你它完成了。》这条规则终于对此做了点什么。DONE 现在是一个必须在同一行里带上收据的主张,而一个写不出收据的模型会写另一个词。
我测试这东西用的是合同审查和制定法问题,和我测试 pygame 是一个原因:如果规则只对代码有效,那它们不是规则,只是风格。
2.3 已经会拒绝编造引用了。2.4 堵上了我反复踩进去的两个漏洞。
第一个是模型会正确引用一条法规,然后在旁边凭感觉写一个损害赔偿数字。所以:
损害赔偿数字是在页面上做算术得出的,2 x $2,000 = $4,000,而不是在规则旁边写一个凭记忆的数字。
第二个更微妙,而且是我如果依赖这个最想要的那条。凭记忆的法律是过时的法律。法规会被修订,门槛会被重新编号,模型对条文的记忆是数据停止那刻的快照:
你凭记忆说出的每个条文编号、期限或美元门槛,都要加一个条款说明如此,并指出去哪里确认。
不是底部没人读的那种免责声明段落。是附在那一个从记忆里来的数字上的一个条款,就在你看着那个数字的时候。
GAMES 部分是十三行,它是我最喜欢的一部分。固定时间步长加限幅累加器,这样后台标签页不会失控。输入是轮询的,不是处理的。100 毫秒的土狼时间和 150 毫秒的跳跃缓冲,因为公平是小谎。循环里不分配任何东西,所以对象池化子弹并复用向量,因为垃圾暂停读起来是卡顿,而玩家会告诉你手感很差但不知道为什么。
2.4 的变化是在最后的总结里加了一条,这一条有多重要说出来有点丢人:
游戏在变漂亮之前要能玩,固定时间步长,有失败画面和重新开始,手感比内容重要。
2.3 给我建了很多漂亮但没法输的东西。循环在,物理在,粒子在,但游戏永远不会结束,所以它是个玩具。没什么可输的,没什么可重开的,这一条已经在长长的章节里写了"没有赢、没有输、没有重开是 demo",但它不在压缩规则底部——而那是长上下文里存活下来的部分。放到那里就修好了。
代码同理:
你写的每样东西都得能跑完整:每个 import、每个它调用的 helper、入口点,以及跑它的命令。没有占位符,没有"暂定",没有用注释描述本该长什么样的脚手架,没有留给读者去填的函数。
再加一行能兜住大部分剩下的:语法检查一遍,用一个具体输入跑一遍,从头到尾读回来。
每个 Onyx 版本都有这么一轮,而我不断发现更多。这轮:
不要以认可开篇。"完美!"、"太好了!"、"收到!"、"当然",以及最糟的那一种——紧跟在工具调用后面的任何一个,因为结果已经在屏幕上了而模型在祝贺它。
不要主动提供帮助。"如果你想要我很乐意做"和"说一声就行"加入黑名单,附上理由,因为理由才是让规则存活于意译的东西:主动提供帮助把活儿推回去然后等着被感谢。
不要用引号把用户的话隔开开。这一条我从未见人命名过。如果你让模型帮你建一个账户而没有账户要建,它会写成它不能"建一个账户",把你的话隔得远远的,好像措辞才是问题。你在用正常方式描述一个目标。说那个 plain 的事实:没有账户要建。
聊天回复里不要加 **。不能做标题,不能强调,不能做列表项的标签。1. Gravity multiplier: more of it while falling. 是一个列表项。同样一行把标签加粗就是没人要的报告。
数学保持纯文本,7^222 和 3/4 和 25!,因为终端会把 LaTeX 渲染成原始美元符号,我受够了看它们。
我还把 robust 和 seamless 从见光死列表里移除了。它们是真正的词,放在那里是因为它们出现在垃圾内容里,而禁掉它们让模型去够更差的词。线索从来不在词汇上。
你可以禁掉每个 LLM 词但输出仍然读起来像是生成的,因为露馅的地方在句子之上:
三条长度匹配、语法匹配的要点,或四段都跑四行的段落,即使每个词都对也读起来像生成的。真正的写作是不对称的:一个条目跑得长是因为它有更多要说的,下一个只有三个词。让你的发现决定形状,而不是你填充的模板。
以及只出现在长对话里的那个版本:
机器在第十条回复里出现,以和前四条一样的方式开头,跑着同样的四行形状无论被问了什么。没人的问候语只有一个:在你用和上一条一样的方式开一个 turn 之前,用不同的方式开它。
2.3 在单条回复里变换措辞。它不在回复之间变换,所以二十轮的对话会漂进一种节奏,而那种节奏就是露馅的地方。这是单独一条让 2.4 感觉像别的东西的变化。
同一个家族里再一条,便宜但在任何地方都值:把东西叫成它们被叫的名字。他们的"export script"在回复里不会变成"数据管道模块",因为把你东西改写成你的词汇让他们在每一行都得翻译回去。
我没有拿这个和 2.3 打分比较。用一个 prompt 和它出身的 prompt 比较告诉你它变了,不告诉你它变好了。
我也没有自己打分。Claude Fable 5.1 打的分,同样的 prompt,用 Fable 5.1 自己或 GPT 6 Astra 被拿来的同样标准。不是"对本地模型来说不错。"好,或者修。这是 31B 开源权重模型的严格标准,也是唯一值得有的标准,因为前沿模型给你的工作打分不会按它不知道存在的曲线来打分。上面引用的每条规则都存在,因为一次跑回来,Fable 扣了分,我去找到了导致它的那一行。
诚实的框架,既然 2.4 是一个关于不过度声明的版本:这是一个前沿模型对我的 prompt 的判断,跨越法律问题、游戏和通用编码,而模型给模型打分是判断,不是测量。我不发布分数,因为我跑的不是带分数的基准测试,声称一个我没算过的数字正是这个版本修掉的那件事。我要说的是那个曾经让我中途换模型的差距闭合了,而且用的是英语,不是权重。
ollama run natuworkguy/flash-onyx-2.4:12b
本地 tag 什么都不传出机器。
在 Flash CLI 里,那是 prompt 写的目标:
/model natuworkguy/flash-onyx-2.4:12b
2.3 没有退役。同一个 repo,同一个 base,同一批 tag,仍然能构建:
python3 models/build.py models/flash-onyx-2.3.Modelfile
权重和采样完全相同,所以如果你想看一个 prompt 值多少,用同一个 seed 对同一个 eval 跑两个对比。这是我不停回头做的实验,而且它一直给我一个比它应该的更大的数字。
Modelfile 是 MIT,在 repo 的 models/ 目录下。用那些规则。权重是 Apache 2.0,那些条款是 Google 的,不是我的:ollama show --license gemma4。
所以:你 system prompt 里哪一行拉的重量最大?这轮,是十三个关于在有数字之前不说数字的词。