跳到主要内容
一句话从第十四行挪到第一行,三个种子全过了

一句话从第十四行挪到第一行,三个种子全过了

老墨
老墨

· 阅读约 4 分钟

旧稿:

目标运行环境是 Python 3.9(Mac 自带 python3)。不要使用 str | Path 这类联合类型标注,3.9 会抛 TypeError。

问题不在这句话的字上。

我改过四遍。删掉 prompt 里所有管道联合类型,加上正确的示例签名,措辞从"不要使用"改成"必须避免"再改回来。每次重建,每个种子,str | Path 照样冒出来。

然后我把同一段话,一个字没动,挪到 PYTHON 段落的第一行。

三个种子,全过。

第一反应是巧合。盯着终端看了半分钟,脑子里不是"找到了",是"这不可能"。

模型不是把你的 system prompt 当一份检查清单在逐条执行。它更像一段会出现在输出附近的文本,按位置分配注意力:段首那句给整段定调,段中第十四行那句是个细节。同样的话,同样的字,承重等级不一样。真正在动的是【位置】。

我那条规则在第十四行待了很久。它待在那儿这件事本身,就在告诉模型:这条不重要。

以前我说过,改 prompt 就是逐字推敲,一个字的分量不对就换一个字。这话只对了一半。位置也是字重的一部分,而且比换词便宜得多。

同一个构建里我还动了另一处。

旧稿:

回答中声明的保证,必须是代码实际提供的保证。

新稿:

重命名之前,先写 flush() 和 fsync()。

具体那条立刻生效,原则那条毫无动静。当时 Onyx 写的原子保存函数长这样:临时文件、重命名、然后告诉用户崩溃不会截断配置。没有同步调用,那句承诺就是假的。点名 flush() 和 fsync() 之后,代码和关于代码的声明一起修好了。

原则性规则描述了一个目标,没给出任何一个能对着打钩的动作。模型读得懂,只是不知道手该往哪放。它和埋在第十四行的那条一样,被当成可牺牲的装修。

删除和禁止的区别,是同一件事的另一面。

早些时候我在 prompt 里放过一条优秀 Slack 消息的范例。之后 Onyx 起草真实 Slack 消息,就用那个范例的固定问候语开头。用户只打了个招呼,它回 Morning——它看不见时钟。

我加规则:不要复用你自己指令里的示例。重建三次,没用。把那句短语删掉,下一次构建就好了。

如果某句话不能出现在输出里,它就不能出现在提示词里。禁止无效,删除有效。你没法写一条规则去撤销一段已经躺在上下文里的文本,就像你没法靠在第十四行补一句"这很重要"把第十四行抬起来。

写到这儿得自己泼盆冷水。

前面三个种子全过,我差点把它当结论。但单种子的坑我踩过。有一次我花了六次修订,想拦住 Onyx 用教科书讲座的语气解释竞态条件,改到第六版,顺手换个种子把同一个提示又跑一遍——三次输出大多是干净散文。种子 7 是异常值。我在重写一条本来就在工作的规则,改了六次。

固定种子让运行可复现,代价是让一个倒霉样本看起来像确定性的规则失败。我现在的规矩是:改之前先跑三个种子读 diff;要真信一个修复,得再跑一批不固定种子的。三个种子对同一个方向够用,对相反方向不够——有个读者在评论里算过,三成触发率下三个种子干净通过的概率还有三成多。

不过段首这个效应,我在另外两处又见过两次,所以暂时信它。

规则反复失败的时候,先移动位置,再考虑重写。我删掉过一条针对表达式解析器的规则,那才是该认输的地方——六十行递归下降代码里维持全套验证不变量,是模型权重的账,措辞买不到。但 str | Path 这种,是我自己把承重墙砌错了地方。

定稿大概长这样:

PYTHON
目标环境 Python 3.9(Mac 自带 python3)。
不要使用 `str | Path` 这类联合类型标注,3.9 会在运行时抛 TypeError。
(其余条款……)

规则本身一个字没改,只是从第十四行挪到了第一行。

这套只在 gemma4 权重上、三次、一条 prompt 里成立,是发现,不是定律。哪天换个模型不好使了,我大概又要重新纠结一遍。为一个位置挪动纠结一下午,说出去有点丢人。

Flash 那 680 行提示词开源在 GitHub 上。我自己都说不准,里面哪些行是承重墙。