什么是 Jev?你应该用它来做什么?它是 TypeSafe 的决策模型,接收一段文本加上一个带类型的问题,并返回一个带类型的答案,该答案是你在预定义答案集合中所选的其中一个选项。所以,如果我有一个关于工单路由的问题,比如这张工单应该归到账单、技术还是账户,Jev 会查看工单的文本,并用预定义选项之一来作答,billing。但它比这还要更酷,因为它会给出该答案选项的校准概率,以及每个其他选项的概率。哦,还有一个整体置信度分数(稍后详述)。TypeSafe 将这些称为 System One 模型,而 Jev 是其中的第一个。
但 Jev 实际返回什么呢?让我们一探究竟!它返回三个原语,下面每个都有真实的 API 响应。以下是如何在解读概率时不欺骗自己。以及如何用 OpenRouter API key 调用 Jev。
什么是决策模型(System One 模型)?
Jev 是一个决策模型。System One 模型是一种接收一份状态,并在给定该状态的情况下返回一个决策的模型。在这种情况下,Jev 的决策始终是你预先定义的一个带类型的值,外加一个从 0 到 1 的实际概率数字。这个名字来源于 Daniel Kahneman 在 《思考,快与慢》中所称的 System One:快速的、模式匹配式的思考,而 System Two 则是缓慢且深思熟虑的。
那么,是什么让它成为一个决策模型,而不是某种预言机?决策模型必须从你事先确定的一小组值中返回结果。没有自由形式的文本,因此你无需解析任何内容,也不必担心模型幻觉。
现在,TypeSafe 对 Jev 进行了校准,使其在给出 0.8(即 80% 的概率)时,含义确实如此——就像 80% 的降雨概率一样,Jev 在这类回答上大约有 80% 的时候是正确的(System One 概念)。
这只有在对许多回答取平均时才成立。任何单个回答仍然可能是错的。这带来了实际差异;这一点我们稍后会再回来讨论。
Jev 与 LLM:各自返回什么
一旦你理解了 Jev 和 LLM 各自做什么,它们之间的区别就变得清晰了。两者都能读取自然语言,但分歧完全在输出端。
以下是两者的并排对比:
| 生成式 LLM | Jev | |
|---|---|---|
| 输出 | Token:散文、代码、你要求的 JSON | 一个带类型的答案加上一个概率分布 |
| 结构有保证吗? | 只有在使用结构化输出时才行,而且内容仍然可能是错的 | 始终是你给定的选项、等级之一,或是一个是/否概率 |
| 不确定性 | 隐藏在行文之中 | 以可设定阈值的数字形式返回 |
| 输入 | 文本,通常还包括图像、音频或视频 | 仅文本(字符串、JSON 对象、文本数组) |
| OpenRouter 上的价格 | 因模型而异,输入和输出均计费 | 每百万输入 token $0.042,输出免费 |
每当你需要以文字作为主要输出时,例如回复、摘要或代码补丁,就用 LLM。当你需要做出可转化为代码操作的决策时,就用 Jev。几乎在所有真实场景中,这两个系统都是协同工作的:Jev 负责路由和验证,LLM 负责提供语言。这正是配套文章 Jev vs LLM:何时使用哪一个所讲的内容。它在 140 个支持案例上对这条分工进行了基准测试,并用 TypeScript 将两者串联起来。
Jev 的三大原语:Choice、Score 和 Noul
你向 Jev 提出的每一个问题都属于三种类型之一。一个请求包含两部分:state,即待评估的文本,以及一个 questions 对象,其中包含一个或多个带类型的问题。Jev 会在一次处理中回答所有问题,每个答案都会以你给定的键返回。
下面的示例是我在 2026 年 9 月 21 日发起的真实调用。它们通过 OpenRouter 的 Decisions API 针对 typesafe/jev-1.13 运行,如果你设置了 OPENROUTER_API_KEY,就可以按原样运行这些示例。
Choice:从固定选项集中选择一个选项
当你从 API 获得一个 Choice 时,它总是包含三样东西:被选中的选项、每个选项的概率,以及一个置信度值。
这条命令会发送请求:
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "My invoice for September shows two charges for the Pro plan. I only have one workspace.",
"questions": {
"team": {
"type": "choice",
"instructions": "Which team should handle this ticket?",
"criteria": {
"billing": "Charges, invoices, and refunds",
"technical": "Bugs, outages, and broken features",
"account": "Login, password, and profile changes"
}
}
}
}' 这个响应展示了 API 返回的内容:
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"team": {
"type": "choice",
"choice": "billing",
"probabilities": { "technical": 0, "account": 0, "billing": 1 },
"confidence": 1
}
},
"usage": { "input_tokens": 357, "output_tokens": 38, "cost": 0.000014994 },
"id": "gen-dec-1790013975-0Cpw7ykY8YRfP85l4eqS",
"provider": "TypeSafe"
} Jev 在做出选择之前会阅读 criteria 的描述,所以要像给新员工做简报那样来撰写它们。TypeSafe 的 API 参考文档指出,问题 id(此处为 team)永远不会被发送给模型。所有含义都必须体现在描述中。其次,model 字段指明了 OpenRouter 所服务的具体快照。
Score:按照你描述的有序等级进行评分
Score 接收一个有序的层级描述数组。它返回一个代表该文本得分的数字,以及一个将数组中每个索引映射到其描述的 legend,外加 Jev 对每个层级的概率和一个置信度值。
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "Export to CSV fails with a 500 error for every workspace in our org since this morning. We can still download JSON exports, but our finance team can only import CSV.",
"questions": {
"severity": {
"type": "score",
"instructions": "How severe is this bug report?",
"criteria": [
"Cosmetic; no impact to functionality",
"Broken or degraded feature, but a workaround exists",
"Blocking issue; no workaround exists"
]
}
}
}' {
"model": "typesafe/jev-1.13-20260917",
"answers": {
"severity": {
"type": "score",
"score": 1.15,
"legend": {
"0": "Cosmetic; no impact to functionality",
"1": "Broken or degraded feature, but a workaround exists",
"2": "Blocking issue; no workaround exists"
},
"probabilities": { "0": 0, "1": 0.85, "2": 0.15 },
"confidence": 0.77
}
},
"usage": { "input_tokens": 354, "output_tokens": 17, "cost": 0.000014868 },
"id": "gen-dec-1790013976-PwmCQLkieCXkoK3qb8Ja",
"provider": "TypeSafe"
} 得分 1.15 是各层级的概率加权平均值,即 0.85 乘以 1 加上 0.15 乘以 2,如上述响应所示。你自己运行时结果会相差百分之几,因为 Jev 的概率在不同调用之间会略有波动。把“我们仍然可以下载 JSON 导出”解读为一种变通方案,Jev 主要落在层级 1。它也给“阻断”赋予了一定权重,因为财务部门无法使用这个变通方案。
由于得分处于有序尺度上,你必须将各层级按从最低到最高的真实顺序排列,并用文字描述每个层级。TypeSafe 文档列出了 Score 原语的详细信息,包括最多 10 个层级的限制。
Noul:一个是/否命题成立的概率
Noul 是三者中最简单的原语。它以命题为输入,返回答案为“是”的概率。
下面的命令发送该请求:
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "I was charged twice for September. Please refund the duplicate charge.",
"questions": {
"refund": {
"type": "noul",
"instructions": "Is the customer asking for money back?"
}
}
}' 以下是 API 返回的响应:
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"refund": { "type": "noul", "noul": 0.99 }
},
"usage": { "input_tokens": 287, "output_tokens": 20, "cost": 0.000012054 },
"id": "gen-dec-1790013977-LxrJdV3aOEliWmRmdmh9",
"provider": "TypeSafe"
} 请记住,Noul 的答案没有单独的 confidence 字段。概率就是整个答案。下一节将解释如何解读它。
在一次调用中同时询问全部三个
你甚至可能会问自己,嘿,那个 questions 对象里能放多少个问题?想放多少就放多少。state 不必是普通字符串:它可以是一个 JSON 对象,具备由此带来的一切灵活性。然后你的指令就可以用反引号按名称引用它的各个字段。
假设你想在单次往返中,从同一个工单领域一次性询问一个 Choice、一个 Score 和一个 Noul。
发送此请求的命令如下所示。
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": {
"ticket": "Hi, I was charged twice for my Pro subscription this month. Please fix this before my next payroll run on Friday."
},
"questions": {
"team": {
"type": "choice",
"instructions": "Which team should handle `ticket`?",
"criteria": {
"billing": "Charges, invoices, and refunds",
"technical": "Bugs, outages, and broken features",
"account": "Login, password, and profile changes"
}
},
"urgency": {
"type": "score",
"instructions": "How urgent is `ticket`?",
"criteria": [
"No deadline; routine question",
"Customer wants a fix soon but nothing is blocked",
"Customer names a deadline or something is blocked now"
]
},
"refund": {
"type": "noul",
"instructions": "Is the customer in `ticket` asking for money back?"
}
}
}' 下面显示的是 API 返回的响应。
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"team": {
"type": "choice",
"choice": "billing",
"probabilities": { "technical": 0, "billing": 1, "account": 0 },
"confidence": 1
},
"urgency": {
"type": "score",
"score": 2,
"legend": {
"0": "No deadline; routine question",
"1": "Customer wants a fix soon but nothing is blocked",
"2": "Customer names a deadline or something is blocked now"
},
"probabilities": { "0": 0, "1": 0, "2": 1 },
"confidence": 1
},
"refund": { "type": "noul", "noul": 0.82 }
},
"usage": { "input_tokens": 447, "output_tokens": 69, "cost": 0.000018774 },
"id": "gen-dec-1790013867-chEjwDPvoiiffM3J3eDF",
"provider": "TypeSafe"
} 如果一切顺利,每个答案都会落在你为它指定的键下。在代码中,你随后就能直接访问 answers.team.choice 和 answers.refund.noul。
如何解读 Jev 的概率和置信度
在解读 Jev 的概率和置信度时,有三条准则需要牢记。
当 Noul 接近 0.5 时,那意味着 Jev 在说它不知道该作何判断。 把它理解为一种不确定状态。我们来做个小实验,向你展示这是怎么回事。我用同一个退款问题——“客户是在要求退款吗?”——针对“我这个月卡上有两笔扣款。如果其中一笔是误扣,我有哪些选择?”这一状态,在两次独立的调用中分别提问。一次返回的 Noul 为 0.52,而在完全相同的消息上(没错,一字不差),Jev 返回的 Noul 为 0.49。如果你忍不住想说“哎呀,客户就是想要退款”,请想一想,客户在这条消息里从未要求过退款。他们是在绕着它打转,而 Jev 感受到两个方向的拉扯,你我也同样无法确定。与此同时,早先那次 Noul 调用中明确的退款请求达到了 0.99。看出规律了吗?当结果处于中间地带时,把它当作第三种结果,一种你可以据此采取行动的结果。追问一个问题,或者把工单转交给人工处理。
Choice 和 Score 置信度是衡量分布集中程度的指标。 分布的权重越集中在某一个结果上,置信度就越高。集中在单一选项上的选择会产生一个高置信度数值,范围从 0 到 1。置信度数值越高,说明在各选项之间的犹豫越少。但 Jev 把注意力集中在某个选项上,并不意味着答案就是对的。TypeSafe 的置信度数值来自其 probabilities 的形状,而非其实际正确性(置信度文档)。在极端情况下,一个把所有权重都分配给某一个结果的模型,其置信度将为 1.0,而集中程度越低,该数值就越小。
你可以用计费示例来验证这一点。这个 state 更加模糊:“我昨天升级到了 Pro,但仪表盘仍然显示 Free,而且我还被扣费了。到底是哪个?”下面的响应会准确展示 API 返回了什么:
{
"type": "choice",
"choice": "billing",
"probabilities": { "account": 0.02, "technical": 0.19, "billing": 0.79 },
"confidence": 0.69
} Jev 仍然选择 billing,尽管其五分之一的权重落在 technical 上,置信度降至 0.69。置信度衡量的是 Jev 在多个选项之间有多纠结,但这些选项是否选得好,则取决于你。
根据你自己的标注数据来选择你自己的阈值。 操作建议很简单。校准在总体上成立,这意味着适合你的正确截断值取决于你的错误有多昂贵。TypeSafe 的 置信度指南 建议三个区间:如果置信度超过某个阈值,就自动执行。在中间区间谨慎推进,标记出来以便增加一层用户确认,或者干脆标记出来供人工审核。如果置信度低于另一个阈值,则转交给人工或不同系统处理。
如何设置这些区间?标注几百个示例。对每个示例,检查 Jev 的概率落在哪里。然后选择一些区间,确保“自动执行”区间内的错误率是你能够接受的。
Jev 留给你的代码去做的部分
有一些事情 Jev 不会做,但你可能会期望它作为模型去做。这些部分仍然留在你自己的代码中,或者交给 LLM 处理:
-
文本生成。每个 Jev 响应都是前面展示的三种带类型形态之一,绝不是补全,绝不是 JSON 正文,绝不是解释。当流水线需要一句话时,由 LLM 生成,再由 Jev 校验。verified cascade cookbook 展示了这一模式。
-
可见推理。Jev 只返回你各选项上的分布,别的什么都不返回。没有理由说明,没有思维链。把该分布作为用于记录日志和设定阈值的信号。若需要审计追踪,记录请求 ID、问题名称、概率,以及你的代码所应用的阈值。不要把
state本身记入日志,因为工单和文档包含客户数据。 -
工具调用、对话或多步计划。Jev 在一次往返中回答你针对所传入状态提出的问题。它不进行工具调用,不保持对话,也不采取任何步骤。它在智能体内部作为决策部分发挥作用,例如对工具调用进行门控。
这就是三条!还有两条约束需要牢记:
-
Jev 只接受文本,即字符串、JSON 对象或文本数组。不接受图像、音频或视频。
-
精确算术、日期运算和阈值比较不是 Jev 的职责。先计算好这些,再把语义部分交给 Jev。
何时使用 Jev 模型
现在你已经了解了 Jev 是做什么的,接下来我们聊聊它什么时候适用:当你的 LLM 预期给出的答案可以归入一个枚举、一个布尔值或一个数字时。
何时使用 Jev
- 路由与分诊: 你确定队列、负责人、优先级。每张工单一次 Choice 和一次 Score。
- 大规模分类与打标签: 成本足够低(见下方定价),可以对每一行都运行;输出本身就已经是一个标签。
- 为智能体操作设卡: 在运行破坏性工具之前,先问一个 Noul:用户的请求是否授权删除这些文件;低于你的阈值就拦截。
- 验证 LLM 输出: 先用 LLM 起草,然后问 Jev:这份草稿是否基于你提供的策略文本。
- 排序与过滤: 按照所描述的分级对候选项打分,然后排序。
何时跳过 Jev
当答案需要是散文,或者当输入是一张图片,或者当逻辑是确定性的。一条精确规则?正则表达式或数据库查询胜过任何模型。想在写代码之前先试试,Jev Lab 可以在浏览器中对实时调用运行其中几种模式。
如何在 OpenRouter 上访问 Jev
你需要一个 OpenRouter API key 和一个模型 ID:typesafe/jev-1.13(固定版本)或 ~typesafe/jev-latest(用于跟踪最新版本的别名)。同一个调用可以通过三种不同方式发起:使用 OpenRouter SDK、TypeSafe SDK,或原始 HTTP。
使用 OpenRouter SDK 的 Decisions API
下面的代码展示的调用与“置信度”一节中那个更模糊的工单里的调用相同:一次POST https://openrouter.ai/api/alpha/decisions通过 OpenRouter TypeScript SDK 发起的调用alpha.decisions。该代码已使用 SDK 1.3.11 测试通过,安装方式为npm install @openrouter/sdk。由于顶层的await,请使用以下方式运行bun run或作为 ES 模块运行。开始吧:
import { OpenRouter } from '@openrouter/sdk';
const openrouter = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });
const decision = await openrouter.alpha.decisions.create({
decisionsRequest: {
model: 'typesafe/jev-1.13',
state: 'I upgraded to Pro yesterday but the dashboard still says Free and I got charged. Which one is it?',
questions: {
team: {
type: 'choice',
instructions: 'Which team should handle this ticket?',
criteria: {
billing: 'Charges, invoices, and refunds',
technical: 'Bugs, outages, and broken features',
account: 'Login, password, and profile changes',
},
},
},
},
});
const team = decision.answers.team;
if (team.type === 'choice') {
console.log(team.choice, team.probabilities, team.confidence);
} billing { account: 0.01, technical: 0.15, billing: 0.84 } 0.77 请注意,这里的数值与之前在同一工单上的运行结果不同(0.79 和 0.69)。Jev 的概率每次调用都会有所变化,因此最好将阈值设置在区间上,而不是精确值上。最后请注意,OpenRouter SDK 返回的 decision.usage.inputTokens 和 decision.usage.outputTokens 采用 camelCase 命名,而非原始 API 的 input_tokens 和 output_tokens。
指向 OpenRouter 的 TypeSafe JavaScript SDK
第二条调用路径是 TypeSafe SDK:将 SDK 的 base URL 指向 OpenRouter,并将你的 OpenRouter API key 作为 API key 传入。SDK 会在 base URL 后追加 /v1/systemone。像 jev-1.13 这样的裸模型名称会映射到 typesafe/jev-1.13。下面的代码是使用 @typesafe-ai/sdk 对 Jev 的同一调用;该代码已在 SDK 0.6.0 上测试通过:
import { TypeSafeClient } from '@typesafe-ai/sdk';
const client = new TypeSafeClient({
apiKey: process.env.OPENROUTER_API_KEY,
baseURL: 'https://openrouter.ai/api',
});
const result = await client.systemOne({
model: 'jev-1.13',
state: 'Export to CSV fails with a 500 error for every workspace in our org since this morning. We can still download JSON exports, but our finance team can only import CSV.',
questions: {
severity: {
type: 'score',
instructions: 'How severe is this bug report?',
criteria: [
'Cosmetic; no impact to functionality',
'Broken or degraded feature, but a workaround exists',
'Blocking issue; no workaround exists',
],
},
},
});
console.log(result.answers.severity); {
type: "score",
score: 1.19,
legend: {
"0": "Cosmetic; no impact to functionality",
"1": "Broken or degraded feature, but a workaround exists",
"2": "Blocking issue; no workaround exists",
},
probabilities: {
"0": 0,
"1": 0.81,
"2": 0.19,
},
confidence: 0.72,
} 指向 OpenRouter 的 TypeSafe Python SDK
在 Python 中实现同样的思路,使用 typesafe-sdk 0.7.1(pip install typesafe-sdk):
import os
from typesafe_sdk import TypeSafeClient
client = TypeSafeClient(
api_key=os.environ["OPENROUTER_API_KEY"],
base_url="https://openrouter.ai/api",
)
result = client.system_one(
model="jev-1.13",
state="There are two charges on my card this month. If one of them is a mistake, what are my options?",
questions={
"refund": {"type": "noul", "instructions": "Is the customer asking for money back?"}
},
)
print(result.answers["refund"]) type='noul' noul=0.49 将你的 OpenRouter 密钥安全妥善地保存在服务端。TypeSafe SDK 指南中有关于别名映射、响应封装以及错误处理的详细说明。
Jev 定价与上下文窗口
来自 2026 年 9 月 21 日的 OpenRouter 模型页面:
| OpenRouter 上的 Jev 1.13 | |
|---|---|
| 输入价格 | $0.042 / 百万 tokens |
| 输出价格 | $0 |
| 上下文窗口 | 32,000 tokens |
| 模态 | 输入文本,输出决策 |
| 提供商 | TypeSafe |
总结一下:上面那个三问题工单调用使用了 447 个输入 token,花费 $0.000019(约千分之二美分)。一百万条这种规模的工单大约花费 $19。输出是免费的,而且答案很小(上面每次调用的输出都在 17 到 69 个 token 之间)。由于 probabilities 内部的键顺序在每次调用之间会发生变化,我建议你按名称而非位置来索引条目。
OpenRouter 列出的是 32k 上下文窗口。TypeSafe 的 模型页面列出每次请求总计 64k token,其中 32k 用于你的 state 加上最长的问题。
Jev 是开源的吗?
快速问一下:Jev 是专有的吗?答案显而易见(是的),但让我解释一下。TypeSafe 没有发布 Jev 的权重,也没有发表关于 Jev 的论文,而且所有想使用 Jev 的人调用的都是同一个托管模型。
试一试
现在该用你自己的 OpenRouter API key 来亲自体验这一切了!拿到它,粘贴上面某条 curl 命令,然后把 state 换成来自你自己系统的真实消息。
- 如果你更想使用 TypeSafe 的客户端,请阅读 TypeSafe SDK 指南。
- 深入了解如何用 Jev 对工具调用进行门控,在智能体动作前放置一个 Noul。
- 阅读 Jev vs LLM:何时使用哪一个 了解基准测试,以及先路由再写入的模式。
- 在制定预算之前,请查看 模型页面,了解该模型当前的定价情况!
常见问题
什么是 Jev?
Jev 是 TypeSafe 打造的一款决策模型。你与大多数模型交互的方式是向它们发送文本。而使用 Jev 时,你不仅仅这样做。相反,你向它发送文本外加一个或多个带类型的问题,返回给你的答案也是带类型的答案,并附有经过校准的概率。没有文本生成。TypeSafe 将这一类模型称为 System One 模型。
Jev 是谁打造的?
Jev 是一款决策模型。它由 TypeSafe 打造,这是一家由 Diogo Almeida、Erik Gafni 和 Sasha Sheng 创立的 AI 实验室。Jev 于 2026 年 9 月 15 日开放抢先体验。目前,当前版本是 Jev 1.13。你可以通过 TypeSafe 自家的 API 或通过 OpenRouter 获取它。
Jev 是大语言模型吗?
不。Jev 是一个非生成式决策模型,而 LLM 是生成式模型:Jev 像 LLM 一样读取你的自然语言,但随后不生成任何文本、任何 token。根据你的问题,Jev 返回三种答案形态之一:要么是在一组预先确定的选项之间做出选择,要么是在一组已定义的等级上给出评分,要么是给出“是”与“否”的概率。由于没有生成的文本,就没有任何东西需要解析,也不存在输出幻觉的可能。
Jev 用来做什么?
好吧,假设你得决定哪张支持工单被路由到哪个团队。你得把这篇博客文章归类到某个内容类型。你想基于一个置信度阈值来对 AI 智能体的工具调用进行把关。你得对一组候选进行排序。你还得对照某项策略来核验一份草稿。听起来很熟悉吧?在这些场景中,以前都是你来向 LLM 提问,然后用正则表达式解析出一个布尔值或枚举值。来认识一下 Jev,这款专为这类决策而打造的模型,用来取代那种“LLM 加正则表达式”的工作流。
我如何访问 Jev?
所以你想通过 OpenRouter 访问 Jev?那么,你只需要一个 OpenRouter API key,并向以下 URL 发送一个 POST 请求:https://openrouter.ai/api/alpha/decisions,模型为 typesafe/jev-1.13。或者,你也可以使用 OpenRouter SDK 中的 decisions 客户端。你甚至可以使用 TypeSafe 的 JavaScript 和 Python SDK。只需将 base URL 设置为 https://openrouter.ai/api,并传入你的 OpenRouter key 即可。
Jev 的费用是多少?
目前,截至 2026 年 9 月 21 日,OpenRouter 上的 Jev 1.13 每百万输入 token 收费 $0.042(输出完全免费)。这意味着,针对一个简短支持工单的典型三问题调用大约使用 450 个输入 token,成本约为千分之二美分。所以请务必记得查看模型页面以了解当前定价。
Jev 是开源的吗?
不是。Jev 是一个专有模型,因此没有公开的权重,也没有论文。不过,你可以通过 TypeSafe API 或 OpenRouter 调用它。
What is Jev? And what should you use it for? It’s TypeSafe’s decision model that takes a passage of text plus a typed question, and returns a typed answer that is one of the choices among your predefined set of answers. So if I have a question about ticket routing, like should this ticket go to billing, technical, or account, Jev looks at the text of the ticket and answers with one of the predefined choices, billing. But it’s even cooler than that because it gives you the calibrated probability of the answer choice as well as the probability of each of the other choices. Oh, and there’s also an overall confidence score (more on that later). TypeSafe calls these System One models, and Jev is the first of them.
But what does Jev actually return? Let’s find out! It returns three primitives, and there are real API responses for each below. Here’s how to read the probabilities without fooling yourself. And here’s how to call Jev with an OpenRouter API key.
What is a decision model (System One model)?
Jev is a decision model. A System One model is one that takes in a piece of state and, given that state, returns a decision. In this case, Jev’s decision is always a typed value you defined in advance, plus an actual probability number from zero up to one. The name comes from what Daniel Kahneman called System One in Thinking, Fast and Slow: fast, pattern-matching thinking, where System Two is slow and deliberate.
So what makes this a decision model and not some oracle? A decision model must return from a small set of values you decided beforehand. There is no free-form text, so nothing you need to parse or worry about hallucination.
Now, TypeSafe calibrated Jev so that when it says 0.8 (an 80% chance), it means that, like an 80% chance of rain, Jev is right about that kind of answer about 80% of the time (System One concepts).
That’s only true when you average across many answers. Any single answer can still be wrong. That makes a practical difference; it’s a point we’ll come back to later.
Jev vs LLM: what each one returns
The difference between Jev and LLMs becomes clear once you understand what both do. Both can read natural language, but the split is entirely on the output side.
Here’s how the two compare side by side:
| Generative LLM | Jev | |
|---|---|---|
| Output | Tokens: prose, code, JSON you asked for | A typed answer plus a probability distribution |
| Shape guaranteed? | Only with structured outputs, and the content can still be wrong | Always one of your options, levels, or a yes/no probability |
| Uncertainty | Hidden inside the prose | Returned as numbers you can threshold on |
| Input | Text, and often images, audio, or video | Text only (strings, JSON objects, arrays of text) |
| Price on OpenRouter | Varies by model, input and output billed | $0.042 per million input tokens, output free |
Whenever you need words as the main output, for example, a reply, summary, or code patch, reach for an LLM. When you need to make a code-actionable decision, use Jev. In just about every real-world case, the two systems work together: Jev routes and verifies things, while the LLM provides the language. And that’s what the companion post Jev vs LLM: when to use each is all about. It benchmarks that split on 140 support cases and ties the two together in TypeScript.
Jev’s three primitives: Choice, Score, and Noul
Every question you ask Jev is one of three types. A request has two parts: state, the text to evaluate, and a questions object containing one or more typed questions. Jev answers all in one pass, each answer returned under the key you gave.
The examples below are real calls I placed on September 21, 2026. They run against typesafe/jev-1.13 through OpenRouter’s Decisions API, and if you set OPENROUTER_API_KEY, you can run the examples as written.
Choice: pick one option from a fixed set
When you get a Choice from the API, it always has three things: the selected option, a probability for every option, and a confidence value.
This command sends the request:
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "My invoice for September shows two charges for the Pro plan. I only have one workspace.",
"questions": {
"team": {
"type": "choice",
"instructions": "Which team should handle this ticket?",
"criteria": {
"billing": "Charges, invoices, and refunds",
"technical": "Bugs, outages, and broken features",
"account": "Login, password, and profile changes"
}
}
}
}' This response shows what the API returned:
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"team": {
"type": "choice",
"choice": "billing",
"probabilities": { "technical": 0, "account": 0, "billing": 1 },
"confidence": 1
}
},
"usage": { "input_tokens": 357, "output_tokens": 38, "cost": 0.000014994 },
"id": "gen-dec-1790013975-0Cpw7ykY8YRfP85l4eqS",
"provider": "TypeSafe"
} Jev reads the criteria descriptions before making a choice, so write them as you would brief a new hire. TypeSafe’s API reference says the question id (team here) is never sent to the model. All the meaning has to be in the descriptions. Second, the model field names the exact snapshot OpenRouter served.
Score: rate on ordered levels you describe
Score takes an ordered array of level descriptions. It returns a number representing the score for the text and a legend mapping every index in the array to its description, plus Jev’s probability for each level and a confidence value.
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "Export to CSV fails with a 500 error for every workspace in our org since this morning. We can still download JSON exports, but our finance team can only import CSV.",
"questions": {
"severity": {
"type": "score",
"instructions": "How severe is this bug report?",
"criteria": [
"Cosmetic; no impact to functionality",
"Broken or degraded feature, but a workaround exists",
"Blocking issue; no workaround exists"
]
}
}
}' {
"model": "typesafe/jev-1.13-20260917",
"answers": {
"severity": {
"type": "score",
"score": 1.15,
"legend": {
"0": "Cosmetic; no impact to functionality",
"1": "Broken or degraded feature, but a workaround exists",
"2": "Blocking issue; no workaround exists"
},
"probabilities": { "0": 0, "1": 0.85, "2": 0.15 },
"confidence": 0.77
}
},
"usage": { "input_tokens": 354, "output_tokens": 17, "cost": 0.000014868 },
"id": "gen-dec-1790013976-PwmCQLkieCXkoK3qb8Ja",
"provider": "TypeSafe"
} The score, 1.15, is the probability-weighted average of the levels, 0.85 times 1 plus 0.15 times 2, as shown in the above response. Your own run will land a few hundredths away because Jev’s probabilities vary slightly between calls. Reading “we can still download JSON exports” as a workaround, Jev lands mostly on level 1. It also gives some weight to “blocking” because finance can’t use the workaround.
Because scores are on an ordinal scale, you must keep the levels in a real order from least to most and describe each level in words. The TypeSafe docs list the Score primitive details, including a cap of 10 levels.
Noul: probability that a yes/no proposition holds
Noul is the simplest primitive of the three. It takes as input a proposition and returns the probability that the answer is yes.
The command below sends the request:
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "I was charged twice for September. Please refund the duplicate charge.",
"questions": {
"refund": {
"type": "noul",
"instructions": "Is the customer asking for money back?"
}
}
}' Here’s the response that the API returned:
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"refund": { "type": "noul", "noul": 0.99 }
},
"usage": { "input_tokens": 287, "output_tokens": 20, "cost": 0.000012054 },
"id": "gen-dec-1790013977-LxrJdV3aOEliWmRmdmh9",
"provider": "TypeSafe"
} Keep in mind that a Noul answer has no separate confidence field. The probability is the whole answer. The next section explains how to interpret it.
Ask all three in one call
You might even be asking yourself, hey, how many questions can go in that questions object? As many as you want. The state doesn’t have to be a plain string: it can be a JSON object with all the flexibility that implies. Your instructions can then refer to its fields by name in backticks.
Let’s say you want to ask a Choice, a Score, and a Noul all at once from the same ticket domain in a single round trip.
The command which sent this request is shown below.
curl https://openrouter.ai/api/alpha/decisions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": {
"ticket": "Hi, I was charged twice for my Pro subscription this month. Please fix this before my next payroll run on Friday."
},
"questions": {
"team": {
"type": "choice",
"instructions": "Which team should handle `ticket`?",
"criteria": {
"billing": "Charges, invoices, and refunds",
"technical": "Bugs, outages, and broken features",
"account": "Login, password, and profile changes"
}
},
"urgency": {
"type": "score",
"instructions": "How urgent is `ticket`?",
"criteria": [
"No deadline; routine question",
"Customer wants a fix soon but nothing is blocked",
"Customer names a deadline or something is blocked now"
]
},
"refund": {
"type": "noul",
"instructions": "Is the customer in `ticket` asking for money back?"
}
}
}' The response shown below is the one the API returned.
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"team": {
"type": "choice",
"choice": "billing",
"probabilities": { "technical": 0, "billing": 1, "account": 0 },
"confidence": 1
},
"urgency": {
"type": "score",
"score": 2,
"legend": {
"0": "No deadline; routine question",
"1": "Customer wants a fix soon but nothing is blocked",
"2": "Customer names a deadline or something is blocked now"
},
"probabilities": { "0": 0, "1": 0, "2": 1 },
"confidence": 1
},
"refund": { "type": "noul", "noul": 0.82 }
},
"usage": { "input_tokens": 447, "output_tokens": 69, "cost": 0.000018774 },
"id": "gen-dec-1790013867-chEjwDPvoiiffM3J3eDF",
"provider": "TypeSafe"
} If all went smoothly, each answer lands under the key you gave it. In code, you’ll then be able to go answers.team.choice and answers.refund.noul directly.
How to read Jev probabilities and confidence
There are three guidelines to keep in mind when reading Jev’s probabilities and confidence.
When the Noul is near 0.5, that’s Jev saying it doesn’t know what to think. Read it as a state of uncertainty. Let’s do a quick experiment to show you how. I asked the same refund question, “Is the customer asking for money back?”, about the state “There are two charges on my card this month. If one of them is a mistake, what are my options?” on two separate calls. One returned a Noul of 0.52, and on the exact same message (yes, verbatim), Jev returned a Noul of 0.49. If you’re tempted to say, “Aw, the customer is just asking for money back,” consider that the customer never once asked for money back in the message. They’re circling it, but Jev is feeling the tug in two directions, and you and I wouldn’t be certain either. Meanwhile, the explicit refund request in the earlier Noul call hit 0.99. See the pattern? When the outcome is in the middle, treat it as a third outcome, one you can take action on. Ask a follow-up question, or hand the ticket over to a person.
Choice and Score confidence is a measure of how concentrated the distribution is. The more all of the distribution’s weight falls on one outcome, the higher the confidence. A choice concentrated around a single option yields a high confidence number, on a scale from 0 to 1. The higher the confidence number, the less hesitation there is between the options. But just because Jev concentrates its attention on an option doesn’t mean the answer is right. TypeSafe draws its confidence numbers from the shape of its probabilities, not their actual correctness (confidence docs). In the extreme case, a model that assigns all of its weight to one outcome will have a confidence of 1.0, while less concentration will diminish that number.
You can verify that with the billing example. This state was muddier: “I upgraded to Pro yesterday but the dashboard still says Free and I got charged. Which one is it?” The response below shows you exactly what the API returned:
{
"type": "choice",
"choice": "billing",
"probabilities": { "account": 0.02, "technical": 0.19, "billing": 0.79 },
"confidence": 0.69
} Jev still picks billing, although a fifth of its weight sits on technical, and the confidence drops to 0.69. Confidence is a measure of how torn Jev is between options, but whether those options are well chosen is up to you.
Pick your own thresholds from your own labeled data. The operational advice is simple. Calibration holds in aggregate, which means that the right cutoff for you depends on how expensive your mistakes are. TypeSafe’s confidence guide suggests three bands: Act automatically if the confidence passes one threshold. Proceed with caution in the middle, flagging for an additional layer of user confirmation or simply flagging for review. And route to a person or different system if the confidence falls below the other threshold.
How do you set those bands? Label a few hundred examples. For each of them, inspect where Jev’s probabilities fall. Then pick bands that ensure the error rate within the act band is one that you can live with.
What Jev leaves to your code
There are a few things that Jev doesn’t do that you might expect it to do as a model. Those stay in your own code or with an LLM:
-
Text generation. Every Jev response is one of the three typed shapes shown earlier, never a completion, never a JSON body, never an explanation. When a pipeline needs a sentence, an LLM produces it and Jev checks it. The verified cascade cookbook shows this pattern.
-
Visible reasoning. Jev returns only the distribution over your options, nothing else. No rationale, no chain of thought. Keep the distribution as the signal to log and threshold on. For an audit trail, log the request ID, names of the questions, the probabilities, and the threshold your code applied. Keep the
stateitself out of the log because tickets and documents contain customer data. -
Tool calls, conversation, or multi-step plans. Jev answers the questions you sent about the state you sent in one round trip. It makes no tool calls, holds no conversation, and takes no steps. It works inside agents as the deciding part, for example gating tool calls.
That’s three! Here are two more constraints to keep in mind:
-
Jev accepts text only, strings, JSON objects, or arrays of text. No images, audio, or video.
-
Exact arithmetic, date math, and threshold comparisons aren’t Jev’s job. Compute them first and hand Jev the semantic part.
When to use the Jev model
Now that you’ve got the idea of what Jev does, let’s talk about when it makes sense: when the answer your LLM is expected to provide fits into an enum, a boolean, or a number.
When to use Jev
- Routing and triage: you determine queue, owner, priority. One Choice and one Score per ticket.
- Classification and tagging at scale: cheap enough (see pricing below) to run on every row; the output is already a label.
- Gating agent actions: before a destructive tool run, ask a Noul whether the user’s request authorizes deleting these files; block below your threshold.
- Verifying LLM output: draft with an LLM, then ask Jev whether that draft is grounded in the policy text you supplied.
- Ranking and filtering: score candidates against described levels, then sort.
When to skip Jev
When the answer needs to be prose, or when the input is an image, or when the logic is deterministic. An exact rule? A regex or database lookup beats any model. To poke at it before coding, Jev Lab runs several of these patterns in the browser on live calls.
How to access Jev on OpenRouter
You’ll need an OpenRouter API key and a model ID: typesafe/jev-1.13 (pinned) or ~typesafe/jev-latest (alias to track the newest version). The same call can be made three different ways: using the OpenRouter SDK, the TypeSafe SDK, or raw HTTP.
Decisions API with the OpenRouter SDK
The code below shows the same call as the one in the muddier ticket in the confidence section: a POST https://openrouter.ai/api/alpha/decisions call via the OpenRouter TypeScript SDK’s alpha.decisions. The code has been tested with SDK 1.3.11, installed via npm install @openrouter/sdk. Because of the top-level await, run it with bun run or as an ES module. Let’s go:
import { OpenRouter } from '@openrouter/sdk';
const openrouter = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });
const decision = await openrouter.alpha.decisions.create({
decisionsRequest: {
model: 'typesafe/jev-1.13',
state: 'I upgraded to Pro yesterday but the dashboard still says Free and I got charged. Which one is it?',
questions: {
team: {
type: 'choice',
instructions: 'Which team should handle this ticket?',
criteria: {
billing: 'Charges, invoices, and refunds',
technical: 'Bugs, outages, and broken features',
account: 'Login, password, and profile changes',
},
},
},
},
});
const team = decision.answers.team;
if (team.type === 'choice') {
console.log(team.choice, team.probabilities, team.confidence);
} billing { account: 0.01, technical: 0.15, billing: 0.84 } 0.77 Note that the numbers here differ from the earlier run on the same ticket (0.79 and 0.69). Jev’s probabilities vary somewhat from call to call, so it’s best to set threshold values on bands rather than exact values. Finally, note that the OpenRouter SDK returns decision.usage.inputTokens and decision.usage.outputTokens in camelCase, rather than the raw API’s input_tokens and output_tokens.
TypeSafe JavaScript SDK pointed at OpenRouter
The second calling path is the TypeSafe SDK: point the SDK’s base URL at OpenRouter and pass your OpenRouter API key as the API key. The SDK will append /v1/systemone to the base URL. Bare model names like jev-1.13 map to typesafe/jev-1.13. The code below is the same call to Jev, using @typesafe-ai/sdk; the code has been tested with SDK 0.6.0:
import { TypeSafeClient } from '@typesafe-ai/sdk';
const client = new TypeSafeClient({
apiKey: process.env.OPENROUTER_API_KEY,
baseURL: 'https://openrouter.ai/api',
});
const result = await client.systemOne({
model: 'jev-1.13',
state: 'Export to CSV fails with a 500 error for every workspace in our org since this morning. We can still download JSON exports, but our finance team can only import CSV.',
questions: {
severity: {
type: 'score',
instructions: 'How severe is this bug report?',
criteria: [
'Cosmetic; no impact to functionality',
'Broken or degraded feature, but a workaround exists',
'Blocking issue; no workaround exists',
],
},
},
});
console.log(result.answers.severity); {
type: "score",
score: 1.19,
legend: {
"0": "Cosmetic; no impact to functionality",
"1": "Broken or degraded feature, but a workaround exists",
"2": "Blocking issue; no workaround exists",
},
probabilities: {
"0": 0,
"1": 0.81,
"2": 0.19,
},
confidence: 0.72,
} TypeSafe Python SDK pointed at OpenRouter
The same idea in Python, with typesafe-sdk 0.7.1 (pip install typesafe-sdk):
import os
from typesafe_sdk import TypeSafeClient
client = TypeSafeClient(
api_key=os.environ["OPENROUTER_API_KEY"],
base_url="https://openrouter.ai/api",
)
result = client.system_one(
model="jev-1.13",
state="There are two charges on my card this month. If one of them is a mistake, what are my options?",
questions={
"refund": {"type": "noul", "instructions": "Is the customer asking for money back?"}
},
)
print(result.answers["refund"]) type='noul' noul=0.49 Keep your OpenRouter key safe and sound on the server-side. The TypeSafe SDK guide has details about the alias mapping, the response envelope, and error handling.
Jev pricing and context window
From the OpenRouter model page on September 21, 2026:
| Jev 1.13 on OpenRouter | |
|---|---|
| Input price | $0.042 per million tokens |
| Output price | $0 |
| Context window | 32,000 tokens |
| Modality | Text in, decisions out |
| Provider | TypeSafe |
To sum up: the three-question ticket call above used 447 input tokens and cost $0.000019 (about two thousandths of a cent). A million tickets of that size come to about $19. Output is free and the answers are tiny (between 17 and 69 tokens across every call above). Because the key order inside probabilities shifts between calls, I suggest you index entries by name rather than by position.
OpenRouter lists a 32k context window. TypeSafe’s model page lists 64k tokens per request total, with 32k for your state plus the longest question.
Is Jev open source?
Quick question: Is Jev proprietary? The answer is obvious (yes), but let me explain. TypeSafe hasn’t published Jev’s weights or a paper on Jev, and everyone who wants to use Jev calls the same hosted model.
Try it
Now it’s time to see all this with your very own OpenRouter API key! Grab it, paste one of the curl commands above, and change the state to a real message from your own system.
- If you would rather use TypeSafe’s client, read the TypeSafe SDK guide.
- Work through how to gate tool calls with Jev, putting a Noul in front of an agent action.
- Read Jev vs LLM: when to use each for benchmarks, and the route-then-write pattern.
- And before you budget, check the model page to see what the current pricing looks like for that model!
FAQ
What is Jev?
Jev is a decision model made by TypeSafe. You interact with most models by sending them text. You don’t do only that with Jev. Instead, you send it text plus one or more typed questions, and the answers you get back are typed answers with calibrated probabilities. No text generation. TypeSafe calls this category of models System One models.
Who makes Jev?
Jev is a decision model. It was built by TypeSafe, an AI lab that was founded by Diogo Almeida, Erik Gafni, and Sasha Sheng. Jev was released in early access on September 15, 2026. Right now, the current version is Jev 1.13. You can get it through TypeSafe’s own API or through OpenRouter.
Is Jev an LLM?
No. Jev is a non-generative decision model, while LLMs are generative models: Jev reads your natural language like LLMs do, but then generates no text, no tokens. Jev returns one of three answer shapes, depending on your question: either a choice among a set of pre-determined options, a score on a defined set of levels, or the probability of yes vs no. Because there’s no generated text, there’s nothing to parse and no chance of output hallucination.
What is Jev used for?
Okay, so you’ve got to decide which support ticket gets routed to which team. You’ve got to classify this blog post into a content type. You want to gate an AI agent’s tool call based on a confidence threshold. You’ve got to rank a set of candidates in order. And you’ve got to verify a draft against a policy. Sound familiar? In these situations, you’re the one who would previously ask an LLM a question, then use a regex to parse out a boolean value or an enum value. Meet Jev, the model purpose-built for just these kinds of decisions, replacing that LLM-plus-regex workflow.
How do I access Jev?
So you want to access Jev through OpenRouter? Well, you only need an OpenRouter API key and a POST to the following URL: https://openrouter.ai/api/alpha/decisions, with model typesafe/jev-1.13. Alternatively, you can use the decisions client in OpenRouter’s SDK. You can even use the TypeSafe JavaScript and Python SDKs. Just set the base URL to https://openrouter.ai/api and pass your OpenRouter key.
What does Jev cost?
Right now, Jev 1.13 on OpenRouter costs $0.042 per million input tokens (and output is completely free) as of September 21, 2026. Which means a typical three-question call on a short support ticket used around 450 input tokens, costing you roughly two thousandths of a cent. So always remember to check out the model page to see current pricing.
Is Jev open source?
No. Jev is a proprietary model and as such there are no published weights and no paper. You can, however, call it through the TypeSafe API or OpenRouter.