查看: 7|回复: 0

一个给 LLM 用的 Jev 式封装,连视觉模型也能用

[复制链接]

12

主题

1

回帖

38

积分

新手上路

积分
38
发表于 前天 19:40 | 显示全部楼层 |阅读模式
作者:Allan Riordan Boll(在 Google 工作,博客 allanrbo)
Jev 以及围绕它出现的那些可自托管项目(比如 OpenJev 和 SemIf)让我很好奇。 读它们让我知道了一个巧妙的技巧:读取 LLM 的 token 概率。
显然这对一些人来说是个老技巧(比如可以看 OpenAI 的 logprobs cookbook),但对我来说是新的。
基本思路
我认为核心想法是写这样一个提示词:
State: 我的订单到货时是破的,我要退款。
Question: 该由哪个团队处理?
[A] billing
[B] shipping
[C] returns
只回答最佳选项的字母。
然后在一个兼容的 Chat Completions 请求里加上几个 JSON 参数:

  1. {
  2. "max_completion_tokens": 1,
  3. "logprobs": true,
  4. "top_logprobs": 20
  5. }
复制代码


LLM API 会返回那个字母,以及模型对其他候选 token 的对数概率。
对每个问题重复这个过程。强制它只生成一个 token,避免了冗长的回答,而且超级快——不过处理输入仍然是要花时间的。但如果后端支持,每个问题可以共享同一个 state 前缀的 KV 缓存。
好玩的地方:视觉模型也能用
Jev 目前文档里的请求格式只描述了文本/JSON 状态的。 我在自己的本地实验里加了一个 attachments 字段用来放图片。
我的例子是:抓取摄像头帧、发送 base64 JPEG、然后打印一张表——有没有人、在室内还是室外、场景有多亮。
在我的 RTX 3090 上跑 Gemma 4 12B,大约每秒 1 帧,每帧问三个问题。
我也拿它对着 OpenAI 的 gpt-6-luna 跑过,大约是 0.2 FPS。 大概是因为我没有做任何努力去避免「每个问题每帧都单独走一遍它系统的一个连接」的开销。
专用的计算机视觉模型肯定效率高得多,但我喜欢这里的灵活性:改一个判断条件,只需要用平实的语言描述它。
代码(要点)
作者给了一个完整可跑的 Python 脚本。关键部分:
请求格式(他的扩展):
  1. {
  2. "state": "Inspect this webcam frame. Judge only what is visibly present.",
  3. "attachments": [],
  4. "questions": {
  5. "person": { "type": "noul", "instructions": "Is a person visible?" },
  6. "plant":  { "type": "noul", "instructions": "Is a plant visible?" },
  7. "setting": {
  8. "type": "choice",
  9. "instructions": "Where is the camera?",
  10. "criteria": { "indoors": null, "outdoors": null, "unclear": null }
  11. },
  12. "light": {
  13. "type": "score",
  14. "instructions": "How bright is the scene?",
  15. "criteria": ["dark", "dim", "bright"]
  16. }
  17. }
  18. }
复制代码

attachments 是他对 Jev 请求格式的扩展——可以是图片路径或 base64 data URL。它们只加载一次,供所有问题共用。
三种问题类型各自映射成字母选项:
· choice → 直接用它给的 criteria
· noul → {"true": ..., "false": ...}
· score → 把每个档位编号成 "0", "1", "2"……
然后用一个关键约束把它们统一起来:
问它只要一个选项字母,这样那个字母的对数概率就代表那个选项。
请求体分两种后端(OpenAI 需要 Responses 接口才能拿到足够多的备选,llama.cpp 用 Chat 接口):
  1. # OpenAI
  2. endpoint = "/responses"
  3. body = {
  4. "model": model,
  5. "input": [{"role": "user", "content": content}],
  6. "reasoning": {"effort": "none"},
  7. "max_output_tokens": 16,
  8. "top_p": 1,
  9. "top_logprobs": 20,
  10. "include": ["message.output_text.logprobs"],
  11. }
  12. # llama.cpp / 兼容接口
  13. endpoint = "/chat/completions"
  14. body = {
  15. "model": model,
  16. "messages": [{"role": "user", "content": content}],
  17. "max_completion_tokens": 1,
  18. "temperature": 0,
  19. "reasoning_effort": "none",
  20. "logprobs": True,
  21. "top_logprobs": 1024,
  22. }
复制代码

注意 top_p: 1——这是为了避免剪掉备选。
把返回的分数归一化,这里有个细节处理得很稳:
peak = max(logprobs[letter] for letter in letters if letter not in missing)
weights = [math.exp(logprobs[letter] - peak) if letter not in missing else 0 for letter in letters]
total = sum(weights)
然后是一个我认为很值得学的防御性检查:
一个被省略的 token 不可能排到最后一个返回的备选之前。只有当它们的合计归一化概率低于 1e-6 时,才允许它为零。
如果缺了选项,就检查:
missing_weight / (total + missing_weight) >= 1e-6
→ 那就报错「API 省略了不可忽略的选项分数」
这个检查的意义在于:如果 API 悄悄没返回某个选项的分数,你会得到一张错误的概率分布——而错误是静默的。他选择直接报错,而不是假装那个选项概率为零。
最后按问题类型还原答案:
· choice → 概率最大的那个
· noul → probabilities["true"]
· score → 期望档位(各档位编号乘其概率求和)
主循环
摄像头预览和一个后台 worker 并行——worker 一次给一帧打分:
executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
用 OpenCV 只为了取摄像头,不做任何实际的计算机视觉。 图片经过摄像头 → base64 → 请求体 → logprobs → 表格这条路。
我的判断
这是 Jev 线里少见的「把机制讲透并给出可跑代码」的一条。
它有三个具体的价值:
① 把「Jev 那套」还原成两个 JSON 参数。 logprobs 加 top_logprobs,任何兼容 Chat Completions 的接口都能做——你不需要 Jev 的 API,甚至不需要一个专门的模型。
② 视觉是把这套东西的适用面突然放大的那一步。 Jev 官方只文档化了文本/JSON 状态。加一个 attachments 字段之后,同一套打分逻辑就能处理图像——而那些判断(有没有人、室内室外、多亮)你可以用平实语言随时改,不用重训模型。
③ 那个「API 省略选项」的检查,是这套打法里最容易被忽略的坑。 所有人都会写「归一化然后取最大」,但很少有人会检查「我拿到的分母是不是完整的」。他的处理方式值得直接抄。
诚实的边界:作者自己也说了,专用 CV 模型效率高得多,而且 3090 上 1 FPS、OpenAI 上 0.2 FPS 都不是能上生产的数字。它的价值在灵活性,不在性能。
链接
原文:http://allanrbo.blogspot.com/202 ... llms-including.html
OpenAI 的 logprobs cookbook:https://cookbook.openai.com/examples/using_logprobs
OpenJev:https://github.com/TheoLeeCJ/SemIf-OpenJev
#Jev #logprobs #视觉模型
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注公众号

相关侵权、举报、投诉及建议等,请发 E-mail:2776601884@qq.com

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.|青ICP备2025004122号-1

在本版发帖
关注公众号
返回顶部