大家好,我是小木头。Jev 这款决策模型大家应该已经不陌生了吧?它恐怕是过去一个月最热的模型之一。现在,它最大的竞争对手恐怕来了——OpenAI 的 Decisions API 现在可以公开访问了。它不仅能做和 Jev 类似的决策工作,更重要的是,它支持图片输入。我前面也做了几期 Jev 的介绍和入门视频,包括基本的接口、基于公开数据集的评测以及实际的应用。感兴趣的朋友可以去我的频道里看一下。今天这一期,我们来看看 OpenAI 的 Decisions API 到底解决什么问题、怎么调用,以及它适合放在什么样的应用里。
Chat Completions API 和 Responses API 它们的核心能力是生成:你给它一段输入,它返回文字。使用 Structured Outputs 以后,它也可以按照 JSON Schema 返回一个对象。Decisions API 的职责更为专一:你先提供一份文字或图片,再提出几个答案范围明确的问题。它目前支持三种类型的问题:Predicate(判断一个条件成立的概率)、Choice(从固定的选项中选择一个),而 Score 呢,按照有序等级给输入打分。比如一条客服工单,可以同时问它应该分给哪个部门、它是否需要紧急处理。第一个问题类型是 Choice,第二个问题是 Predicate。模型返回部门各选项的概率,以及需要紧急处理的概率。
所以它适合作为模糊输入和确定性业务动作之间的判断层:语言和图片中难以写成固定规则的部分,可以交给模型判断。最后是否执行、是否交给人工复核,仍然可以由代码决定。Decisions、Structured Outputs 和普通代码看起来都能返回一个结构化的结果,但它们解决的是三类问题。Structured Outputs 它的重点是让生成结果符合你定义的对象结构,它适合抽取像订单号、姓名、日期或者生成包含多个字段的结果。
Decisions 只处理答案范围明确的问题,目前支持的是我们刚才提到的类型:像 Predicate、Choice 和 Score,并且会同时返回概率。如果规则完全确定(比如金额超过1,000元就进入人工审核),这种情况下直接写代码更合适。权限检查、状态校验、付款删除和账号变更也不应该只依赖一次模型的判断。因此这里有一个非常重要的考察的点:返回格式正确并不代表判断是正确的,Confidence 值很高,也不代表一个动作已经就获得了授权。
下面直接来看一次实际的调用吧。我们先来打开执行的脚本,再在终端里运行它。Decisions 使用独立的 POST 接口 /v1/decisions,当前负责这个判断的模型呢只有 gpt-6-luna 一个。在工单的例子里,我们把工单文字放进 input,并且在 questions 里面放两个问题:一个 Choice 类型的问题(负责从 billing、technical、shipping 和 other 这几个选项中选择一个),另一个 Predicate 类型的问题(判断工单是否紧急)。
这两个问题互不依赖,所以可以在一次请求里面共用同一份 input。如果后一个问题必须先知道前一个的答案,那么就应该拆成两次请求。响应里的 answers 按照问题的顺序返回,但应用最好呢使用名字(your name)来查找答案,不要只依赖数组的位置。每个问题还可能单独返回 refusal,所以呢也要逐个地检查。在实际的集成中,可以把调用封装成一个轻量级的适配层,统一处理超时、错误、refusal 和返回类型。这样呢在 public beta 阶段,即使 OpenAI 的接口发生了调整,也不需要同时去改动所有的业务流程。这个呢都是在软件工程编程方面的一些实践。
很多 Decisions API 的应用场景或许都会提到模型路由,这里需要特别澄清:OpenAI 并没有在 API 层替你提供候选模型,也没有自动去读取某个用户可以调用哪些模型。请求顶层的 model 只表示由谁来执行这一次的 decision(也就是决策),目前这里只能使用 gpt-6-luna。如果你想用 Decisions 做模型路由,候选模型和每个选项的描述都是由开发者自己提供,本质上这是开发者借助 Decisions 搭建自己的路由器,不是 OpenAI 提供了一套现成的模型路由功能。
这些描述只会影响 Decisions 的选择,并不会改变模型真实的能力。比如你把某个模型描述成擅长复杂推理,Decisions 可能更容易选择它,但那个模型不会因此获得更强推理能力。模型的描述写错过时或者太模糊,最后的结果呢也会跟着出错,这是大家要特别注意的。更稳妥的做法是先由普通的代码来按照账户权限、工具支持、预算数据政策等等过滤模型,然后让 Decisions 在 fast、balanced、deep 这类应用自行定义的路线上选择。最后再由代码把路线映射到具体的模型,官方的语音示例也是选择 reason 这个应用动作,再由应用来调用推理模型,而不是直接选择某个模型 ID。
图片输入是 OpenAI Decisions API 值得单独强调的。截止目前的公开文档,Jev 的 state 仍然只接受文字或结构化的数据并不支持原生的图片输入。OpenAI 的 Decisions API 已经可以把文字和图片放在一起作为判断依据:纯文字可以直接放进 input;文字加图片时,input 要改成只包含 user 角色的消息 content 里同时放入 input_text 和 input_image。图片必须编码成 base64 data URL。在演示里,我先用 base64 命令读取一张本地图片,再通过 jq 把 data URL 嵌入请求体,最后用 curl 把这份 JSON 发送给 Decisions API。
我实际准备了一张破损音箱和包装的照片,并在一次请求里面问了三个问题:商品本体是否破损、主要损伤是什么、损伤程度如何。curl 返回以后,终端里首先显示的是经过 jq 格式化的真实 JSON 响应。为了更直观的看清结果,再把其中三个 answers 单独整理出来我们来看看吧:Predicate(判断商品已经破损)概率是100%;Choice(判断主要损伤是裂纹)置信度呢也是100%;Score(判断损伤程度为中等),它的值是98%。这样三种答案类型和各自的概率信息就能在同一张图片上一次的看清楚了。
这里的 detail 可以选择 low、high、auto 或 original,一个请求最多可以传128张图片。需要注意的是它不接受公网图片的 URL 也不接受 file ID,PDF、其他文件和音频显然也不能当做图片输入。下面我们再把12条工单的验证过程来走一遍:先在终端里打开 tickets.json 来看看模拟的工单和人工打的标签,然后我们打开 run-decisions.mjs 文件看看在代码里它怎样构造请求并且保存结果。最后呢直接运行脚本,每条工单同时判断部门和紧急度,12次请求全部成功。
部门分类12条全部正确;紧急度使用了0.5作为阈值,12条判断里正确了11条。唯一的一条误判是我的“年费被重复扣了两次”,人工标签是不紧急,API 返回的 probability 值是0.58。这个结果不一定没有道理:重复扣款确实可能需要尽快处理。真正的问题是0.5并不是适合所有业务的默认值:如果紧急队列容量小,可以提高阈值;如果漏掉一条需要当天拦截的付款请求代价更大又可能需要降低阈值。当然了也可以设置两个阈值:高概率自动处理,中间去人工复核;低概率呢直接进入普通队列。
在这次12个请求中它的延时是 174-557 毫秒,中位数在211毫秒。总共使用了 4,270 个 input token,按当前的价格估算呢是 $0.000427。这个呢是大致的成本,但这12条数据是我自己编写的只能验证接口和演示集成。我还未在相同条件下运行 Responses API,所以官方所说的约快10倍还不能当做我的测试结论。那如果大家有自有数据呢?自有数据可以从两方面使用:第一种是把业务里的文字或图片作为每次请求的 input,这是显而易见的;第二种是准备带正确答案的测试集评估准确率、各类别召回率、自动处理覆盖率和人工复核比例。
官方也建议使用自己标注过的样本设定路由过滤和人工复核的阈值,你可以使用本地的脚本也可以使用 OpenAI 的 Datasets 和 Evals。每次调整问题选项描述、上下文或者业务阈值以后都用同一份测试集做回归测试,这个和微调(也就是 fine-tuning)是两回事。当前的 gpt-6-luna 不支持 fine-tuning,公开文档里暂时也没有 Decisions 专属的训练接口。这里优化的呢还是在应用层面并不是模型的权重。
最后,一个任务适不适合 Decisions 可以从三个方面来看:首先答案能不能枚举;此外错误成本能不能定义;最后呢就是关于拿到结果以后有没有明确的动作。内容分类、工单路由、风险评分、视觉质检、UI 和语音控制通常还是符合这些条件,我们可以考虑在这些场景中来尝试使用决策模型或者 Decisions API。开放式写作看起来并不合适因为答案并不是一个有限的集合;长链的规划需要生成步骤、使用工具处理中间结果显然也不适合只靠 Decisions API;规则完全确定时看起来普通代码或许仍然更加可靠哦。
Decisions API 本身并不复杂,真正需要长期维护的是问题、选项标注集阈值和动作的策略。模型给概率,代码来做最后的决定。今天的分享就到这里吧,我是小木头,我们下期见。