跳到主要内容
本周小抄:让大模型只回一个字母

本周小抄:让大模型只回一个字母

阿简
阿简

· 阅读约 4 分钟

为什么有人要费劲让大模型一次只吐一个字母?

简单说,因为你要的不是它的句子,是它的概率。让它写一整段再去解析,又慢又容易崩格式。让它只回一个 A,那一个 token 的概率本身就是一个分数。

上周看到一篇博客,标题是给 LLM 做一个 Jev 风格的封装,顺带把视觉模型也接上了。作者从 Jev、OpenJev、SemIf 这几个自托管项目一路看过去,掉进 logprobs 这个坑。他自己也说,这技术对别人可能是旧的,对他是新的。

logprobs 我之前收过一次,一直觉得它离普通人挺远。看完这篇改主意了。

把生成换成打分

具体做法不复杂。prompt 里放三样东西:当前状态、一个问题、几个带字母的选项,再加一句"只回答最好的那个选项的字母"。

请求那边把生成限制成一个 token,同时要 logprobs,最多带 20 个备选 token。回来就是它选的那个字母,加上其他字母各自的概率。

点评:这一招的价值在于它绕开了整段解析。对付选择题式的判断,最省事的路子就是这条。不需要 JSON 模式,不需要写正则去抠字段,也不用担心它客气一句"好的,我认为"。

它确实快,但没有想象中那么快

作者本地跑 Gemma 4 12B 的量化版本,7GB 左右,加一个 175MB 的多模态投影,3090 上起 llama.cpp,大概一秒一帧,每帧问三个问题。

换 OpenAI 的 gpt-6-luna,反而掉到 0.2 帧每秒。他猜是每帧里每个问题都单开了一条连接。

点评:本地那个数字也不算好成绩,做实时监控勉强够。但成本结构和云上完全是两回事。另外那句猜测我没验过,先放着。

状态前缀能被缓存

作者提了一句,如果后端支持,多个问题共用的那段 state 前缀是能 KV 缓存的。

点评:这就是"每帧问三个问题"还能跑下去的原因。所有把同一大段上下文反复喂给模型的玩法,都该先问一句这个能不能缓存。省下来的往往比换个模型还多。

视觉也能塞进去

Jev 文档里只描述了文本或 JSON 的 state,没有图片的位置。作者自己加了一个 attachments 字段,支持图片路径或者 base64 data URL,PNG、JPEG、WebP、GIF 都行。

demo 抓摄像头画面,转成 base64 JPEG 发出去,问三件事:画面里有没有人、在室内还是室外、亮度怎么样。

点评:这里最值得看的不是 demo。是他全程没用任何专用视觉模型,判断条件全是一句白话。想加一个条件,改一句话就行,不用重训也不用重新标数据。作者自己也说,专用 CV 模型效率高得多,他要的是这份灵活。OpenCV 在里面只负责开摄像头,一行视觉算法都没干。

布尔和打分靠选项数量硬凑

脚本里问题分三类:choice 是单选,noul 是布尔,score 是序数打分。全都映射成字母选项,一个问题最少 2 个最多 20 个,字母从 A 到 T。

答案拿到手之后做归一化,检查有没有漏掉概率不可忽略的选项,最后给出一个胜出项、一个真概率,或者一个序数期望分。

点评:看着土,但这是把分类和"1 到 5 分"都塞进同一个接口的办法。比给每种任务写一套 prompt 省事得多。要求单字母输出也是为了这个,一个 token 的概率才能直接当成那一项的分数。

有条读者留言,我觉得比正文有意思

9 月 26 日的一条评论建议,把选项放到 state 前面。理由是现在的 transformer 只 attend 它前面出现过的 token。选项在前面,模型就不用先"想"一遍才能看到它们,也许能把 reasoning 直接关掉,效果、时间、成本一起改善。

点评:这个方向我觉得对,但没验过。作者的接口里本来就把 reasoning effort 设成了 none,两件事叠一起是什么结果,得自己跑一遍才知道。

不确定值不值得收的一条

llama.cpp 走 Chat Completions,OpenAI 走 Responses,两套参数对不上,作者在脚本里分开处理。还专门把 top_p 设成 1,避免采样把备选项提前剪掉。

老实说这段我只看懂了一半。先放在这里,等我拿两种后端各跑一遍再补一句。有兴趣的可以自己去读,读完了愿意回来给我讲讲的,更欢迎。

本周就这些。

上面有任何一条你试过、或者觉得我讲错了,欢迎告诉我。讲错的我补,没讲清的我改。

下周见。

阿简
阿简

每周替你把 vibe-coding 圈的大事筛成一张小抄,被讲玄的概念一句话搞懂。

查看主页 →