把 LLM 判断拆成 Choice / Score / Noul:Jev SDK 六条实测

8 阅读5分钟

很多 AI 工作流最后只需要一个路由、一个等级和一个真假判断,却还是让通用大模型生成整段 JSON。最近我试了 TypeSafe AI 的 Jev:它不生成自由文本,只接收 state 和类型化 questions,再把概率化答案交给代码。

本文不复述宣传数字,直接用一条重复扣费工单拆接口,再给出六次真实 API 调用的环境、结果和限制。

一个请求里问三个独立问题

实验使用 @typesafe-ai/sdk@0.6.0。下面是请求结构的简化版:

import { TypeSafeClient, choice, score, noul } from '@typesafe-ai/sdk';

const client = new TypeSafeClient();

const response = await client.systemOne({
  model: 'jev-latest',
  state: {
    message: '我被扣了两次钱,请今天退回多扣的一笔。'
  },
  questions: {
    route: choice('Which queue should handle this request?', {
      refund: 'The user asks to return money already paid.',
      support: 'The user needs help but does not ask for money back.',
      other: 'None of the above.'
    }),
    urgency: score('How urgently does this need human support?', [
      'No active issue or deadline.',
      'An issue exists without an imminent deadline.',
      'Same-day handling or a blocked core function.',
      'Ongoing severe impact that cannot wait.'
    ]),
    explicit_refund: noul(
      'Does the user explicitly ask for money to be returned?'
    )
  }
});

Choice 返回选项、各选项概率和 confidence;Score 返回有序等级的概率加权分、分布和 confidence;Noul 返回命题为 yes 的概率,没有额外 confidence。

问题 ID 只是代码取值的键,模型不会靠 route 这个名字理解问题。完整含义要写进 instructions 和 criteria。候选项没有 other,模型也不会凭空替你补上。

为什么要一起问

这三个问题都能直接根据原始消息回答,彼此不依赖,因此可以共享一份 state,在同一个请求里求值。代码拿到结果后再决定哪些答案要用。

如果下一步必须先抽取订单号、查询支付库,再判断是否真的重复扣费,就不能假装所有步骤都能并行。Jev 解决的是同一份上下文上的独立判断,不会消灭业务数据依赖。

这也是我认为它比“只会输出合法 JSON”更准确的定位:类型化结果只是底线,真正的设计点是预定义问题、概率分布、并行问题和代码组合。

六条调用结果

环境:Node.js v22.21.1、SDK 0.6.0、超时 15 秒、重试 0。2026 年 9 月 20 日,一个本机进程顺序发送六次请求;每次同时提交上述三类问题。请求模型为 jev-latest,响应实际模型均为 jev-1.13.0。

输入路由confidence退款 Noulurgency墙钟耗时
中文重复扣费,要求今天退款refund1.000.991.991962.02 ms
英文重复扣费,要求今天退款refund1.000.992.00782.15 ms
中文明确不要退款,只问发票support1.000.030.08822.18 ms
英文不要退款,协助重置密码support1.000.030.43744.75 ms
中文模糊请求other0.540.020.06823.44 ms
英文无关故事请求other1.000.010.00756.66 ms

模糊请求的 other 概率为 0.70、support 为 0.30,但 confidence 是 0.54。选中项概率和 confidence 不是同一个字段。示例把 route confidence 小于 0.8 的结果送到 human_review,因此它不会自动路由。

这条 0.8 只是演示阈值,没有业务校准。六条合成样本也不是准确率测试集,更不是概率校准证明。

计时数字别看过头

计时范围包住一次 SDK 调用,包含网络往返和响应解析。没有预热、并发、重复统计或通用模型对照,所以不能把第一条 1962 毫秒解释为冷启动,也不能用它证明中文比英文慢。

TypeSafe 官方给过约 70–500 毫秒的端到端数字,以及工作流评测中的 193.6 倍速度、444.6 倍成本差异。官方同时说明评测通常从美国西海岸访问服务,工作流倍率可能位于真实收益高端。国内网络下应该重新测 p50/p95,而不是把宣传页数字写进 SLA。

confidence 应该怎样进入代码

我更倾向于把原始判断和执行策略分开:

function decideAction({ route, refund }) {
  if (route.confidence < 0.8) {
    return { action: 'human_review', reason: 'uncertain_route' };
  }

  const conflicts =
    (route.choice === 'refund' && refund.noul < 0.8) ||
    (route.choice !== 'refund' && refund.noul >= 0.8);

  if (conflicts) {
    return { action: 'human_review', reason: 'signals_conflict' };
  }

  return { action: 'route', destination: route.choice };
}

这个函数只给路由建议,不执行退款。真实交易仍要核对订单、身份、金额和规则。高 confidence 代表分布更集中,不是执行高风险动作的授权。

接入时我会保留的几条边界

  1. 先用带人工标签的数据验证,再定阈值。
  2. 记录响应里的版本化模型 ID;jev-latest 会移动。
  3. 英语是官方说明的主要训练语言,中文必须单独评估。
  4. 低置信、候选缺失或独立判断冲突时,明确回退路径。
  5. 安装 Agent skill 只是给 Coding Agent 补接入知识,不会下载模型,也不能把 Jev 设成代码助手的底层聊天模型。

完整可运行实验在原博客下载包中,默认 --dry-run 不读密钥、不联网;真实运行需要自己的 TYPESAFE_API_KEY。如果你准备在项目里试,我建议先找一个低风险、可回退、每天会重复很多次的判断,而不是一上来接管最终动作。

你们现在有哪些“让 LLM 返回 JSON”的调用,其实可以继续拆成更窄的判断?

相关资料:JavaScript SDK、Primitives、Confidence、Models。完整代码、依赖锁与实验快照见原博客文章。