Jev 技术上手:把判断从生成里拆出来
面向开发者的实测笔记。目标读者:正在用 LLM 做路由、分类、重排、护栏、Agent 决策,但被延迟、成本和 JSON 解析折磨过的人。 信息截至 2026-09-18,模型版本
jev-1.13.0。价格、限流官方标注为“动态调整”,上线前请回查文档。
1. 它到底是什么:一个受约束的判别接口
一句话:输入非结构化 state,输出在你预先定义的候选集上的概率分布。
- 不生成文本,没有自回归解码。输出空间在请求里就锁死了,所以不存在 schema 违约。
- 每次回答带概率;Choice 和 Score 还带一个
confidence。 - 这不是“更便宜的 LLM”。它做不了生成、算术、多跳推理——这几件事官方自己在 jaggedness 页里明确列成失败模式。
接口只有一个:
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) |
— |
三条容易踩的规则:
- question 的 ID 不会送给模型。 问题必须完整写在
instructions里,别指望refund_requested这个 key 能替模型说明意图。 Choice给全量列表,不要给候选短名单。 多一个选项只多几个 token。列表可能覆盖不全时,必须加other或none of the above,否则模型被迫在错的选项里挑。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
probabilities:全量分布,是你做组合决策时的原始材料。confidence:把分布的“尖峭程度”压成一个 0–1 的数,方便直接卡阈值。Noul没有 confidence,直接用noul值卡。- 官方特意留了口子:confidence 只是他们给的默认度量,你完全可以拿 probabilities 自己算(熵、margin 等),不同场景更优的度量不一样。
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、需要拿答案决定下一轮选项集。
有效案例:
- Skill 选择:第一次对 182 个 skill 全量排序 + 判断“这轮到底需不需要 skill”,第二次只读 top 3 的全文并允许全否。错误加载减少一半以上。这个模式对任何有 skill/tool 目录的 Agent 都直接可用。
- 层级分类:用
Choice的答案决定下一层的候选集,做 beam search 穿过深层层级(专利、零售品类、生物医学、源码)。 - 结构化恢复:先生成块边界,再分类这些块——块在第一次回答之前并不存在。
已验证的效果数据(可直接引用的锚点)
| 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. 什么时候不要用它
- 要生成文本:写代码、写文案、写解释——用生成模型。它不支持字符串输出(连
{output: string}都刻意不支持,为了保住全并行)。 - 能精确计算的事:计数、求和、日期差、正则能匹配的。让模型做这些是在给自己找 bug。
- 要系统 2 式的多跳推理:一条链上多层间接,准确率下滑。拆成多个原子问题 + 代码组合,或者交给推理模型。
- 非英文高精度场景:CJK 官方说明显弱于英文。要用就先测,并且盯紧 confidence。
- 把它当 drop-in 替换:它需要你把散落在 prompt 里的判断逻辑重构成“原子问题 + 代码阈值”。这是重构,不是替换。
和现有方案的对比
| 方案 | 相比 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
- 影子模式先跑一到两周:只记录、只告警,不阻断。观察它到底 flag 了什么,再决定阈值。
- 把 questions 和阈值常量集中到一个文件。官方原话:人类真正需要 review 的就是问题和阈值,散落各处就没法审。编码 Agent 特别爱把它们写得到处都是。
- 自己造评测,不要信任何 benchmark(包括官方的)。官方这套 “workflow eval” 是拿最贵模型的平均输出当参考答案的,量的是“你有多像它们”,不是“你对不对”。
- 正确的做法是画一条曲线:给定 confidence 阈值 → 自动化比例 vs 准确率。然后回答那个真正的问题:“要 90% 准确率,我能自动处理多少百分比?”
- 别信“不会幻觉”这个说法。它保证的是类型不出错,不保证判断正确。类型安全 ≠ 事实正确。官方自己的失败清单里就有字面理解、无关细节干扰、对抗输入这几条——每一条都能让它在合规的类型里给出错误的答案。
- 拿真实数据做反面测试。有独立开发者拿德州扑克对照求解器:30 个牌局,与求解器一致率 63%;其中一个明摆着的陷阱局(手里最大牌、正确打法是过牌),它 16 次全下、16 次全错。它在一个封闭选项集上给出高置信度的错误答案是真实存在的——这就是为什么第 1 条和第 3 条不能省。
- pin 版本,或者在别名漂移时重新校准阈值。
- 英文优先。CJK 要额外验证,并且别把英文数据上调好的阈值直接搬过来。
8. 参考
- 官方发布博客(2026-09-15):https://typesafe.ai/blog/introducing-system-one-models-and-jev
- 官方文档:https://docs.typesafe.ai · 索引:https://docs.typesafe.ai/llms.txt
- 已知失败模式清单(建议先读这个):https://docs.typesafe.ai/model-jaggedness/jev-1.13
- API 全量参考:https://docs.typesafe.ai/api
- 模型与价格/限流:https://docs.typesafe.ai/models
- 模式文档:confidence-routing / fan-out / composite-scoring / intent-routing
- Cookbook 索引(21 个可跑示例):https://docs.typesafe.ai/llms.txt
- 快速上手:https://docs.typesafe.ai/introduction/quickstart
- Agent skill:https://github.com/typesafe-ai/skills
- 为什么不做公开 benchmark:https://typesafe.ai/blog/antibenchmaxxing
- 独立实测(扑克对照求解器 63%):https://backnotprop.com/blog/jev-poker/
- 社区项目汇总:https://github.com/AbdelStark/awesome-typesafe