Jev 技术上手:把判断从生成里拆出来

面向开发者的实测笔记。目标读者:正在用 LLM 做路由、分类、重排、护栏、Agent 决策,但被延迟、成本和 JSON 解析折磨过的人。 信息截至 2026-09-18,模型版本 jev-1.13.0。价格、限流官方标注为“动态调整”,上线前请回查文档。


1. 它到底是什么:一个受约束的判别接口

一句话:输入非结构化 state,输出在你预先定义的候选集上的概率分布。

接口只有一个:

POST https://api.typesafe.ai/v1/systemone
Authorization: Bearer $TYPESAFE_API_KEY
Content-Type: application/json

请求体三件套:state、model、questions。

{
  "state": "Hi, I've been trying to connect my Stripe account for 3 days and it keeps failing. I'm losing sales. Please help ASAP.",
  "model": "jev-latest",
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this",
      "criteria": {
        "billing": "Payment or subscription issues",
        "technical": "Bugs or integration problems",
        "sales": "Pricing or account questions"
      }
    },
    "frustration": {
      "type": "score",
      "instructions": "How frustrated the customer appears",
      "criteria": [
        "Calm, just stating facts",
        "Frustrated but civil",
        "Very angry, strong language"
      ]
    },
    "is_urgent": {
      "type": "noul",
      "instructions": "The message conveys urgency or time-sensitivity"
    }
  }
}

返回:

{
  "model": "jev-latest",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "billing",
      "probabilities": { "billing": 0.84, "technical": 0.159, "sales": 0.001 },
      "confidence": 0.596
    },
    "frustration": {
      "type": "score",
      "score": 1.035,
      "legend": { "0": "Calm, just stating facts", "1": "Frustrated but civil", "2": "Very angry, strong language" },
      "confidence": 0.842
    },
    "is_urgent": { "type": "noul", "noul": 0.999 }
  },
  "usage": { "input_tokens": 312, "output_tokens": 48 }
}

先注意这个细节:department 的概率是 0.84,但 confidence 只有 0.596。这两个不是一个东西,别混用(见第 4 节)。

接入方式(按省事程度排序)

方式 说明
Vercel AI Gateway model id 就是 typesafe-ai/jev,不用等 waitlist,最快验证路径
Python SDK pip install typesafe-sdk,读取 TYPESAFE_API_KEY 环境变量,默认 jev-latest
JS SDK npm i @typesafe-ai/sdk
MCP 社区有 typesafe-mcp(Go) / jev-mcp(Node),可挂进 Claude Code / Claude Desktop
Agent Skill npx skills add typesafe-ai/skills --skill typesafe-ai,直接让编码 Agent 按规范写代码

Agent 集成的一个实用提示(官方自己写的):编码 Agent 特别容易养成“一次请求只问一个问题”的习惯,正好把这个 API 最值钱的部分浪费掉了。装好 skill 后,第一条指令就是让它把所有可能用到的问题塞进同一请求。


2. 三个原语与选型规则

类型 回答什么 返回 上限
Choice 这些选项里选哪个 choice + probabilities + confidence 选项 ≤ 255
Score 落在哪一档 score + legend + probabilities + confidence 等级 2–10 个
Noul 这件事成立吗 noul(0–1,没有 confidence) —

三条容易踩的规则:

  1. question 的 ID 不会送给模型。 问题必须完整写在 instructions 里,别指望 refund_requested 这个 key 能替模型说明意图。
  2. Choice 给全量列表,不要给候选短名单。 多一个选项只多几个 token。列表可能覆盖不全时,必须加 other 或 none of the above,否则模型被迫在错的选项里挑。
  3. Score 的等级要描述“情境”,不要描述“程度”。 写“功能损坏但存在绕过方案”,不要写“中等严重”。模型看不到等级编号,也看不到相邻等级,Score = 1.0 说明不了“中等”——它只是概率加权均值,不同分布可以得到同一个 score。等级之间重叠是低 confidence 的头号来源。

选型口诀:Choice 映射到多个代码分支,Score 映射到一个阈值,Noul 映射到一个 if。如果两个类型都像合适,选你代码能直接行动的那个。

状态工程比 prompt 工程重要

state 支持 string / JSON object / array of text。用对象,不要用裸字符串,然后用点加下标的路径把问题指过去:

questions = {
    "refund_requested": {
        "type": "noul",
        "instructions": "Does `ticket.messages[0].text` request a refund?",
    },
    "policy_supports_refund": {
        "type": "noul",
        "instructions": (
            "Does `refund_policy` support the refund requested "
            "in `ticket.messages[0].text`, given `order.charges`?"
        ),
    },
}

Jev 有 context rot:state 里跟当前判断无关的内容越多,准确率越低。先在你的代码里过滤/检索,只发问题需要的那几个字段。


3. 上下文、价格、延迟、限流

项 值
价格 输入 $0.042 / Mtok($42 / Btok);输出不计费
上下文 单请求 64k token;其中 state + 最长单个问题 ≤ 32k(约 15 万英文字符)
延迟 端到端 70–500ms(官方评测跑在西海岸的笔记本上,自测时注意)
限流 250k tok/s、1200 req/min,官方明说会动态调整;超额返回 429
输入类型 只有文本。图像/音频/视频不支持
语言 英文为主要训练语言;中文等 CJK 明确标注“准确率较低”,上生产前必须用自己的数据测
数据 不用客户请求/响应训练;不做 per-account 微调;企业版有 ZDR

SDK 默认带退避重试并遵守 retry-after。手写 HTTP 的话要自己处理 429。

版本别名要小心:jev-latest / jev-preview 现在都指向 jev-1.13.0。别名会随新版滑动,响应里的 model 字段会告诉你实际是哪个版本答的。如果你按某个版本调好了 confidence 阈值,就 pin 版本号,否则某天会静默漂移。


4. 坑位清单(来自官方 jaggedness 页,含实测细节)

这一节大概是全文最省时间的部分。jev-1.13 已知的失败模式:

失败模式 正确做法
字面理解:它回答你写的问题,不是你想问的问题 把条件写死在 instructions 里,边界情况放进 criteria。当你发现自己在向同事解释“我其实想说的是……”,那句话就是漏掉的半句指令
算术与计数:它不数数,也不做计算;误差随规模增长 计数、求和、日期差值全放代码。要按条件计数,就用代码遍历、逐个提问、自己相加
数值表示:十六进制颜色、RGB 三元组、汇编、二进制指令都表现差 代码里换算好,传英文名或已经分好的桶
日期时间比较:日期被当文本读 拆开:抽取是判断,交给模型;排序、时长、星期几是算术,留代码
多跳与间接:双重否定、属性的属性,准确率掉 减少跳数,别写绕的话
大 state 里的无关细节 先过滤,或用一个 Noul 先做相关性筛选
对抗性内容:state 默认不被当作敌意输入,注入指令能改变答案 别假设它抗注入;上线前把边缘用例测一遍
指令与 criteria 矛盾:true 映射到“否”这类写法明显更差 把 criteria 当成 instruction 的延伸,措辞对齐
它不保证结构不变量 见下
生成 用别的模型

两个会咬人的不变量问题

同一个问题,用 Noul 问和用 yes/no 的 Choice 问,答案可以差很远。 官方给的例子(工单:“我对尺码不满意,我有什么选择?”):

Noul noul Choice yes Choice no Choice confidence
0.22 0.01 0.99 0.97

问题与它的否定式,概率加起来不等于 1:

refund not_refund 和
0.72 0.47 1.19

结论很直接:别在 Noul 上调好阈值后套到 Choice 上;别假设 P(x) + P(¬x) = 1;别拿两个独立问题的结果做算术恒等式校验。 Choice 是相对选择(在选项之间决出胜负),Noul 是绝对判断(可能所有选项都低)。

confidence ≠ probability


5. 四个值钱的工程模式

模式一:Speculative fan-out —— 把所有问题塞进一次请求

同一份 state、所有问题共享一次 prefill,问题之间相互独立、并行求解。

官方在 GDPR 维基条目(约 5.4 万字符)上做了 13 个问题(8 个 Noul + 2 个 Choice + 3 个 Score)的对照实验:一次批量请求 vs 13 次单问请求,便宜 12.2 倍、快 10.0 倍,答案完全一致(多数问题 5 次重复标准差为 0)。

关键在于:把“这次可能用不上”的问题也一起问。 state 是主要成本,多问一个问题近乎免费。这就是官方的 Speculative fan-out:全部问掉,代码自己决定用哪些。

模式二:Composite scoring —— 把复合判断拆成原子问题

不要问“给这个创业项目打分”。分开问市场规模、技术可行性、差异化,权重写在代码里。优先级变了改系数,不用重写 prompt。

代码 5 行,判断力可审计——这是它相对“把一切塞进一段 prompt”的核心优势。

模式三:Confidence-gated routing —— 答案告诉你“是什么”,confidence 告诉你“该不该动”

官方语音银行的示例结构值得直接抄:

action = response.answers["intent"]

if action.confidence < 0.6:
    route_to_support_agent(account_id)          # 模型自己也不确定,别猜

elif action.choice == "check_balance":
    show_balance(account_id)                     # 低风险,0.6 就够

elif action.choice == "approve_transfer":
    if action.confidence > 0.85:
        approve_transfer(account_id)             # 高风险 + 高置信,自动执行
    else:
        ask_user_to_confirm(...)                 # 高风险 + 中置信,先确认

阈值不是一个数,是按动作风险分层的。 官方文档里那句总结很实用:“高风险动作的门槛要高于只读动作,风险容忍度编码在你的代码里,不在模型里。”

模式四:两阶段(真的需要依赖时才做第二次请求)

同一请求内的问题互相看不见对方的答案。只有当第二次请求的内容取决于第一次结果时才拆两次。官方三个正当理由:需要拿答案去取新数据、需要拿答案构造 state、需要拿答案决定下一轮选项集。

有效案例:

已验证的效果数据(可直接引用的锚点)

Cookbook 结果
BM25 召回 30 篇后用 Jev 重排(CLERC 法律 40 条 query) top-1 5% → 18%,top-10 38% → 62%
218 行 ToS 文本一次 Choice 语义检索 + Noul 判断“文档里有没有答案” 单请求完成
450 对商品做知识图谱实体对齐 一个 Score 问题(三档 = 合并/不连/交人工)承载整个决策,无需拟合阈值
SEC 年报 75 个行业分组 Choice + 直接读 confidence 决定报细分行业还是上级大类
引用核查 / RAG 段落分级 / LLM 护栏 一个请求同时做“是否越狱”+“危害程度”打分,阈值卡出 pass/review/block

6. 什么时候不要用它

和现有方案的对比

方案 相比 Jev
OpenAI 风格 structured outputs / 约束解码 官方论点:单纯 mask logits 会让模型变笨——如果一个模型会给非法 token 分配概率,说明它本来就困惑,这时候正确做法是报错而不是硬掰。Jev 是从训练目标上就只输出决策
编码器零样本分类器(BERT / GLiNER / GLiClass 一脉) 同类思路(并行、无生成、结构安全),但需要自己训、自己维护、自己去 handle 自然语言定义的输出空间。Jev 的价值在于把这件事做到足够快、足够便宜、足够可靠,且能用自然语言现场定义候选集
自己微调一个小模型 窄任务上可能更准、更便宜(社区已有人用 Qwen-2.5-1B 复现并行约束解码思路,28 字段工单分类 1900ms → 270ms)。但要为每个任务收数据、训模型、维护。Jev 是 zero-shot + 通用

判断标准:如果这个判断的候选集是封闭的、能用一句话描述、“一个懂行的人看一眼就能答”——用 Jev。否则用别的。


7. 上线前 checklist

  1. 影子模式先跑一到两周:只记录、只告警,不阻断。观察它到底 flag 了什么,再决定阈值。
  2. 把 questions 和阈值常量集中到一个文件。官方原话:人类真正需要 review 的就是问题和阈值,散落各处就没法审。编码 Agent 特别爱把它们写得到处都是。
  3. 自己造评测,不要信任何 benchmark(包括官方的)。官方这套 “workflow eval” 是拿最贵模型的平均输出当参考答案的,量的是“你有多像它们”,不是“你对不对”。
    • 正确的做法是画一条曲线:给定 confidence 阈值 → 自动化比例 vs 准确率。然后回答那个真正的问题:“要 90% 准确率,我能自动处理多少百分比?”
  4. 别信“不会幻觉”这个说法。它保证的是类型不出错,不保证判断正确。类型安全 ≠ 事实正确。官方自己的失败清单里就有字面理解、无关细节干扰、对抗输入这几条——每一条都能让它在合规的类型里给出错误的答案。
  5. 拿真实数据做反面测试。有独立开发者拿德州扑克对照求解器:30 个牌局,与求解器一致率 63%;其中一个明摆着的陷阱局(手里最大牌、正确打法是过牌),它 16 次全下、16 次全错。它在一个封闭选项集上给出高置信度的错误答案是真实存在的——这就是为什么第 1 条和第 3 条不能省。
  6. pin 版本,或者在别名漂移时重新校准阈值。
  7. 英文优先。CJK 要额外验证,并且别把英文数据上调好的阈值直接搬过来。

8. 参考