跳到主要内容
那条 prompt 里最该改的不是形容词,是输出契约

那条 prompt 里最该改的不是形容词,是输出契约

老墨
老墨

· 阅读约 5 分钟

这类 prompt 我见过太多遍,大致长这样:

判断这条通知是否紧急。紧急就立刻提醒我,不紧急就攒起来一起发。

二十来个字。读着通顺,实际什么也没说清。三件事挤在一句里:判断、定级、决定要不要打断人。模型只会挑最省事的那件做,通常是第一件,后两件自由发挥。

Hush 那个项目我翻了大半天。真正值得抄的不是拿 Gemma 2B 在本地跑这一层,也不是 Temporal 那套定时器,是它把模型的活划得有多窄:只提取三个变量,紧急度、类别、截止时间。分数不归它算,归 Python。紧急度乘类别后果权重,再乘截止时间衰减因子,一行乘法。

诗眼不在措辞上,在输出契约的边界上。

先说为什么不能让模型直接给分。

“给这条通知的紧急程度打 0 到 1 分”——这句话的毛病不是模型打不准,是它打的 0.7 和代码里那个 0.7 不是一回事。温度设 0.0、贪心解码,翻转率确实能压到很低,作者报的 0% 我信。但那摁住的是采样噪声,不是刻度漂移。同一个 0.7,今天代表“老板半夜问一句”,明天代表“银行风控短信”,模型自己都不觉得矛盾,因为每次它只看得见一条。

三个格子就不一样。格子填错,你事后能查出来;分数偏了,你看不出来。

我一开始认定三个变量里最重的是紧急度。想了一晚上,推翻。

算笔账。促销类权重 0.05,紧急度给满 1.0,衰减因子顶到 3.0,乘出来 0.15,正好压在丢弃线上——低于 0.15 直接进丢弃区,永远不会出现在那份三小时一发的四行简报里。反过来,一条促销被漏判成工作类,权重回到 0.50,紧急度 0.85,乘出来 0.425,稳稳进简报。

前一种错是静默的,不进简报、不打断、不留声响,审计日志里躺着,你不会去翻。后一种错最多烦你一次。

所以这条 prompt 里字重最重的是【类别】,不是紧急度。紧急度偏了,分数差一截,多半还有简报兜着;类别偏了,权重直接乘个 0.05,怎么都救不回来。

作者显然也想到了。医院、急诊、救护车、紧急这几个词挂了确定性正则,命中就强制归健康,防止安全告警被当成促销扔掉。这是对的。但它绕开了模型——某种意义上是承认:这条 prompt 在类别这一栏上,不可靠到需要模型之外的东西来兜底。

这里岔一句。“推敲”这词出自有人为一句诗里该用“推”还是“敲”,在驴背上想得忘了看路,一头撞进别人的仪仗队。一个字能把人逼到不看路。一千多年后我们为一个字段名纠结一下午,没什么长进,也没什么丢人的。

扯回来。

评论区那段我不太同情。有人问小模型在 0.55 阈值附近会不会被 prompt 措辞带翻,作者拿分数分布回应:负控最高 0.26,紧急样例最低 0.75,中间 0.49 是空的。十五个样例撑出来的空档,那不叫“没有风险”,那叫“没测到”。测试集从 15 加到 150,那个空档大概率会缩水,缩到多少没人知道。

但作者另一半是对的,而且他自己强调得不够。确定性评分器把模型的不确定性关进了三个变量里——模型爱怎么翻怎么翻,翻的范围也就是类别填错、时间读错,它翻不出一个 0.83 的分数来,因为分数不归它给。这一条能被工程手段验证,比“我们做了充分测试”结实得多。

我自己试着把这条 prompt 写一遍。V1:

从这条通知里抽三样:
urgency: 0-1 的数字
category: health / money / people / work / promo
deadline: ISO 时间,没有就 null

跑下来最大的问题就是上面说的那个:类别没有退路。五选一,模型必须选一个,选错就是静默死刑。V2 加双候选:

category 拿不准时,不要猜。写成 "health|work" 这种双候选,
按可能性排序,最多两个。不要输出分数,不要给建议。

多一个字段,多占几十个 token,换的是评分器那边有一条路可走——双候选取权重高的那个,或者干脆按未知处理走个中间值。都比静默丢弃强。

但 V2 带出两个新毛病。一是“不要输出分数”这种否定句,模型对“不要”的服从性一向不稳,你越写它越容易在边上蹭一下;二是 2B 的模型对格式的耐性本来就薄,多一个字段、多一层嵌套,解析出错率会不会跟着涨,我没底。

V3 把否定改成正面写死:

只输出以下三个键,不输出其他内容:
{"urgency": <0.00-1.00>, "category": <五选一,拿不准时写两个候选用 | 分隔>, "deadline": <ISO 时间或 null>}

“不输出其他内容”还是留了个否定,但后面紧跟的是完整的键表,模型的锚点在键上,不在禁令上。这是我目前的经验:正面清单比禁令压得住格式,禁令只能当补丁。

定稿先放这:

从这条通知里抽三样,只抽三样。只输出下面这个 JSON,不输出其他内容:
{"urgency": <0.00-1.00>, "category": <health|money|people|work|promo,
拿不准时写两个候选用 | 分隔,按可能性排序>, "deadline": <ISO 时间或 null>}

丑话说在前面:这版我没在 2B 上跑过。双候选这种格式小模型接不接得住,我心里是真没数,说不定下周又推翻重来。上次觉得完美的那版,隔了三天回头看就扎眼,这次也未必例外。