2026 年 9 月 15 日,TypeSafe AI 发布了 Jev——第一个 System One 模型。它不聊天、不写代码、不生成任何一个字,只输出带校准概率的结构化判断。
这不是又一个"更快更便宜的小模型"。它把 AI 的能力边界重新切了一刀:把「判断」从「生成」里拆出来,做成一次函数调用。
这篇文章从零讲清:怎么调、每个参数什么意思、什么时候该用它、以及最容易踩的坑。
一、Jev 到底是个什么东西
名字来自卡尼曼的《思考,快与慢》。LLM 是 System 2——慢、深、逐 token 推理;Jev 想做 System 1——快、直觉、一次给出判断。
传统的自回归 LLM:输入 token 序列,一个一个吐出 token,直到生成结束。Jev 砍掉了整个逐 token 解码过程,改用并行采样,在一次前向计算里同时产出所有判断结果。
它的能力面被压缩到极窄的三件事:
| 原语 | 回答什么 | 返回什么 |
|---|---|---|
| Choice | 这几个选项里是哪个? | choice、probabilities、confidence |
| Score | 处在哪个等级? | score、legend、probabilities、confidence |
| Noul | 这件事是真的吗? | noul(0 到 1 的概率) |
除此之外,它什么都不会。
代价换来的是:70–500 毫秒的端到端延迟、输入 $0.042/百万 token、输出完全免费、类型错误率数学上为 0。相比前沿 LLM,快了 40–200 倍,便宜了最多 444 倍。
值得强调的一点:输出永远锁定在你预设的选项集合内。代码不需要从自由文本里捞值,也不需要 JSON 解析兜底——这是它敢宣称"零类型错误"的机制来源。
它的定位不是替代 LLM,而是做 Agent 系统的前置反射弧:让前沿模型专注复杂推理,把所有高频小判断——路由、分类、风控初筛、内容过滤、是否终止——交给 Jev。
二、准备工作
2.1 拿 API Key
到 typesafe.ai 加入 waitlist,通过后在 console.typesafe.ai 获取 API Key。
Console 里的 Playground 很有用:可以可视化地配置问题、定义 Schema,直接看到带概率的决策结果。建议先在 Playground 里把 Schema 调好,再写代码。
2.2 装 SDK
# Python
pip install typesafe-sdk
# JavaScript / TypeScript
npm install @typesafe-ai/sdk
SDK 会自动处理请求构造、类型校验,以及 429/529 的指数退避重试。
2.3 设环境变量
export TYPESAFE_API_KEY="你的 key"
SDK 默认读取这个变量。
三、第一个请求
先看最小可用的 HTTP 调用:
POST https://api.typesafe.ai/v1/systemone
Authorization: Bearer <API_KEY>
Content-Type: application/json
{
"state": "Help! My payouts have been failing for 3 days.",
"model": "jev-latest",
"questions": {
"is_urgent": {
"type": "noul",
"instructions": "Does this convey urgency?"
}
}
}
返回:
{
"model": "jev-latest",
"answers": {
"is_urgent": {
"type": "noul",
"noul": 0.92
}
},
"usage": { "input_tokens": 312, "output_tokens": 48 }
}
0.92 —— 92% 的概率"这条消息传达了紧急感"。
用 Python SDK 是同样的形状:
from typesafe_sdk import TypeSafeClient
with TypeSafeClient() as client:
response = client.system_one(
state="Help! My payouts have been failing for 3 days.",
questions={
"is_urgent": {
"type": "noul",
"instructions": "Does this convey urgency?",
},
},
)
print(response.answers["is_urgent"].noul)
已经是全部的核心流程了。 剩下的都是细节。
四、请求参数完全手册
顶层请求体只有三个字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
state | string | object | array | 是 | 被评估的内容。是材料,不是问题 |
model | string | 是 | 目前填 jev-latest |
questions | map<string, Question> | 是 | 题目映射。key 由你定,答案用同一个 key 返回 |
4.1 state:你要判断的材料
三种形态:
| 形态 | 适合场景 | 例子 |
|---|---|---|
| 字符串 | 单条消息、文章、段落 | "My card was charged twice." |
| 对象(推荐) | 命名字段、关联记录、应用状态 | {"message": "...", "order_id": "A-104"} |
| 数组 | 消息序列、记录列表 | ["Hi", "My number is TS1337.", "我卡被扣了两次。"] |
默认用对象。 每个部分有名字,字段之间的关系才清晰。文档给的心智模型很好:把 state 想象成"递给专家小组做判断之前,要先摆到桌面上的全部材料"。
{
"ticket": {
"subject": "Duplicate charge",
"messages": [
{"from": "customer", "text": "I was charged twice for order A-104. Please refund the duplicate."},
{"from": "support", "text": "We are checking the charges."}
]
},
"order": {
"id": "A-104",
"charges": [
{"amount_usd": 49, "status": "captured"},
{"amount_usd": 49, "status": "captured"}
]
},
"refund_policy": "Duplicate charges are eligible for a refund."
}
这一整块算一个 state,即使里面既有对话、又有订单、又有政策。判断需要对比哪几个部分,就把它们放在一起。
硬限制:Jev 只吃文本。 图片、音频、视频一律不支持(至少目前)。非文本输入必须先转成文本或结构化字段。另一个关键限制:主要训练语言是英文,中文等 CJK 文字可以使用,但准确率明显更低。 中文生产环境务必自己测数据,并且紧盯 confidence。
4.2 questions:每道题的四个字段
| 字段 | 说明 |
|---|---|
| key(题目 ID) | 你起的名字。不会发给模型,也不参与推理——纯粹给你的代码做映射用 |
type | "noul" / "choice" / "score" |
instructions | 核心字段。要评估的具体判断,支持字符串、对象、数组 |
criteria | 答案空间。形态随 type 而变 |
最容易犯的错:以为题目 ID 会告诉模型什么。 你写 "refund_requested",模型完全看不到这个 key。instructions 里必须把问题写完整,别指望 ID 自带语义。
4.3 三种 Question 的 criteria 形态
Noul(可选):
"is_urgent": {
"type": "noul",
"instructions": "Does this convey urgency?",
"criteria": {
"true": "Explicitly time-sensitive",
"false": "No urgency expressed"
}
}
Choice(必填,map<string, string|null>):
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations",
"sales": "Pricing, upgrades, new accounts"
}
}
选项之间无序。如果选项集合可能覆盖不全,加一个 other 或 none of the above。
Score(必填,有序数组,至少 2 个等级):
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["Calm", "Frustrated", "Very angry"]
}
4.4 响应体
{
"model": "jev-latest",
"answers": { "<你给的 key>": { "...": "..." } },
"usage": { "input_tokens": 312, "output_tokens": 48 }
}
model 返回的是具体版本 ID(如 jev-1.13.0),不是别名。这一点很重要——见第十节的坑。
三种答案的具体结构:
// Noul:注意没有 confidence
{ "type": "noul", "noul": 0.92 }
// Choice
{
"type": "choice",
"choice": "technical",
"probabilities": { "billing": 0.08, "technical": 0.85, "sales": 0.07 },
"confidence": 0.82
}
// Score
{
"type": "score",
"score": 1.6,
"legend": { "0": "Calm", "1": "Frustrated", "2": "Very angry" },
"probabilities": { "0": 0.05, "1": 0.3, "2": 0.65 },
"confidence": 0.78
}
Score 的 score 是概率加权后的连续值,会落在两个等级之间(1.6 就是"介于 Frustrated 和 Very angry 之间")。legend 把数字索引映射回你的等级描述。
五、三种原语怎么选
判断标准只有一条:哪种答案形态能让你的代码直接行动。
- Choice —— 答案落在无序的已知集合里。工单派给哪个部门、文档是什么类型、代码是什么语言。选项映射到代码分支最直接。
- Score —— 答案落在一条能描述清楚的光谱上。Bug 严重度、客户愤怒度、技能水平。等级由你定义。
- Noul —— 干净的是非题,且概率本身就有信息量。这条消息是否在报 Bug?是否在要求退款?
别混淆 Noul 和 Score
这是新手最常翻车的地方:
"这个候选人 Python 强不强?" —— 坏问题。
Noul 返回 0.5 的意思是"是和否概率相等",不是"中等水平"。条件定义不清,这个概率根本没法解读。
正确做法二选一:
- 想测水平 → 用 Score,明确定义等级:
["无经验", "会一点", "日常使用", "深度专家"] - 想要是非 → 把条件写死:"简历是否写明候选人在工作中使用过 Python?"
好问题 vs 坏问题
System One 模型是为**"一个懂行的人在有一秒时间、且手握足够上下文时能做出的判断"**设计的。
- ✅ 好问题:"这条消息是否传达了紧急感?"
- ❌ 坏问题:"分析这条消息并决定最佳行动方案。"
后者需要慢推理。看到这类问题就是信号:拆成几个小问题,答案在代码里组合。
比如"给这个创业项目打分"应该拆成:市场规模、技术可行性、差异化——三个独立的 Score 问题,然后在代码里按重要性加权。优先级变了就改权重数字,不用重写 prompt。
六、把 state 设计对:字段路径引用
state 是结构化对象时,instructions 里要用反引号包裹的点加索引路径,明确点名要判断的是哪一部分:
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`?"
),
},
}
不加反引号,模型就不知道该看哪块。 这是把结构化 state 用好的关键细节。
高级技巧:instructions 和 criteria 都能传 JSON
instructions(三种类型通用)、Score 的每个等级描述、Noul 的 criteria.true/false,类型都是 EntryType——即 string | object | array | null。
什么时候该结构化:
- 问题有多个部分时:JSON 的 key 起了标注作用,比堆成一句话清晰得多
- 已有 schema / taxonomy / 数据库行时:本来就是 JSON,直接塞进去,别自己拼字符串模板
"instructions": {
"question": "Does the claimed sender identity conflict with the sending domain?",
"compare": ["ticket.sender.display_name", "ticket.sender.email"],
"focus": "Compare the named organization with the email domain."
}
七、一次问多个:它和 LLM 最大的区别
一个请求里的所有问题都是并行评估的,彼此独立,各自用你给的 key 返回。
这意味着:
- 加问题几乎不增加延迟
- 只多付那点输入 token
由此衍生出官方推荐的 Speculative fan-out(投机性扇出) 模式:把所有可能需要的判断一次性全问上,代码自己决定用哪条答案。一个工单如果最后发现不是 Bug 报告,忽略那条严重度评分就行了。
官方给的数据很有说服力:把 13 个问题打包成一次调用,相比 13 次独立调用,便宜 11.5 倍、快 9.6 倍,且答案完全一致。
文档里有句话很直白:"问一个可能用不上的问题几乎是免费的。" 它还专门点名说,写代码的 agent 最容易犯"一次调用只问一个问题"的毛病。
需要多次请求的情况
同一请求内的问题互相独立,一个答案不会成为另一个问题的上下文。
只有当你的代码必须先拿到第一个答案才能构造第二个请求时,才发第二次请求——比如需要拿答案去取更多数据、或者需要根据答案决定下一题的选项。
"两次请求是例外,不是常态。" 如果第二题其实能对着原始 state 问,那就放进第一次请求,让代码忽略不需要的答案。
上下文预算
- 64k token / 请求:
state+ 全部问题合计 - 32k token:适用于
state+ 单个最长的问题
大约相当于 15 万英文字符的英文文本。问题数量本身没有上限,只受 token 预算约束。
八、置信度:真正让它可落地的那个特性
这是 Jev 最有工程价值的部分,也是最容易被当成"附赠品"忽略的部分。
probabilities 和 confidence 的分工
probabilities是原始分布:Choice 是各选项上的分布,Score 是各等级上的分布。confidence是从这个分布算出来的一个统计量,折叠成 0–1 的单一数字,方便你直接卡阈值。
判断置信度的本质是看分布的形状:越尖越自信,越平越不确定。
- Choice 低置信 → 没有哪个选项明显胜过其他
- Score 低置信 → 等级定义模糊、维度混杂,或者 state 信息不足
文档明确说了:confidence 只是推荐的默认算法,你完全没有被锁死。完整的 probabilities 都给到你了,想要别的度量(比如熵、margin)自己算就行。
RLCD:为什么它敢让你信这个概率
Jev 用 RLCD(Reinforcement Learning for Calibrated Decisions) 训练。
- RLHF 优化人类偏好
- RLVR 优化可验证答案
- RLCD 优化的是"判断对不对"加上"模型自称的把握是否诚实"
具体含义:当模型对一批任务都报 90% 把握时,其中应该有约 90% 真的正确。
这是自动化分流的数学前提。没有校准的概率只是装饰品。
一个必须记住的限定:校准是群体层面的性质,它不保证单个答案正确。
三档阈值:让代码显式处理不确定性
response = client.system_one(
state=user_message,
questions={
"action": Choice(
instructions="What is the user trying to do?",
criteria={
"check_balance": "View account balance",
"approve_transfer": "Approve the pending withdrawal request",
"support": "Get help with an issue",
},
),
},
)
action = response.answers["action"]
confidence = action.confidence
if confidence < 0.5:
# 模型真的不确定。别猜。
route_to_human(user_message)
elif action.choice == "check_balance":
# 低风险。跳错页面可以补救。
show_balance(account_id)
elif action.choice == "approve_transfer":
if confidence > 0.9:
# 高风险 + 高置信:确认后执行
confirm_then_execute(account_id)
else:
# 高风险 + 中等置信:先验证
ask_user_to_confirm(account_id)
三个关键点:
- 阈值不是一个数字。 同一个系统里,不同操作的阈值应该不同——取决于出错的后果。
- 只读操作和销毁性操作不能用同一把尺子。 上面例子里,
check_balance无论如何都执行,approve_transfer却要 0.9 以上。 - 区间划分要按你的风险承受度定。 官方建议:先保守,用自己的数据测,再根据观察结果调整。
文档里有一句话值得抄在墙上:
如果一个智能系统——无论人还是机器——无法表达诚实的不确定性,那这个系统就不可信。
九、计费、限流与错误处理
计费
只按输入 token 计费,输出免费。 $0.042 / 百万 token。
这意味着一个反常的结论:问题问得多不心疼,state 塞得大才心疼。 这正好和第七节的并行策略互相强化。
限流
- 250,000 token / 秒
- 1,200 请求 / 分钟
官方警告:这些限值正在动态调整中,可能无预警变化。企业级自定义额度需联系 sales。
错误码
| 状态码 | 含义 | 怎么办 |
|---|---|---|
401 Unauthorized | API Key 缺失或无效 | 检查 Authorization 头 |
422 Unprocessable Entity | 请求体校验失败(缺字段 / 题目格式错) | body 会指出问题字段 |
429 Too Many Requests | 超限 | 指数退避重试,别立刻重试 |
529 Overloaded | TypeSafe 暂时过载 | 退避后重试 |
429/529 一定用指数退避。用官方 SDK 的话默认就带退避策略,还会遵守响应里的 retry-after 头。
列出可用模型
curl https://api.typesafe.ai/v1/models \
-H "Authorization: Bearer $TYPESAFE_API_KEY"
GET /v1/models 返回你账号可用的模型名和别名。
十、踩坑清单
- 以为题目 ID 会传给模型。 不会。
instructions必须自包含地把问题写完整。 - 中文直接上生产。 英文才是主训练语言,CJK 只是"能处理"。中文场景必须先测,并且严密监控 confidence。
- 用
jev-latest别名跑已调优的阈值。 别名会随新版本漂移,model返回的版本 ID 才是真相。如果你按某个版本校准过阈值,就把版本号 pin 死(如jev-1.13.0),升级自己挑时间。 - 把 Noul 的 0.5 当成"中等"。 它是"是非概率各半",不是程度上的中等。要测程度就用 Score。
- 一次调用只问一个问题。 并行是免费的,这是最大的浪费。
- 让同请求内的问题互相依赖。 它们彼此独立,前一个答案不会成为后一个的上下文。
- 拿它做开放推理。 让它解释理由、写文本、生成方案——纯属用错工具。
- 忘记给 state 里的字段加反引号路径。 结构化 state 不指路,模型就瞎猜。
- 忽略置信度。 把
confidence当附赠品直接扔掉,等于放弃了这个模型一半的价值。
结语
Jev 的价值不在于"又一个更便宜更快的模型",而在于它把不确定性变成了可编程的接口。
choice 告诉你是什么,confidence 告诉你该不该采取行动。这两条轴一分开,"AI 自动决策"这件事第一次有了可工程化的边界:高置信自动执行、中置信加确认、低置信转人工。
从架构上,这意味着一个很清晰的切分——LLM 做开放式推理和生成,Jev 做路上所有高频的小判断。
对已经写了大量 if prompt says ... 这种脆弱逻辑的人来说,这大概是今年最值得动手试的一次接口升级。
参考:官方文档 https://docs.typesafe.ai/(该站提供 llms.txt 完整索引,可以直接喂给你的 coding agent)。
评论与来信
如果正文触发了新的想法,可以把第一封留言写在右侧;提交后会先进入审核。
写下你的想法
提交后进入审核队列,通过后显示于左侧。