はじめに ── なぜこの記事を書くのか
LLMに「このチケットはどの部署宛てか」を答えさせるコードを、筆者は何度も書いてきました。JSONで返させ、パースし、検証し、落ちたら再試行する。判断は1行なのに、防御コードのほうが長くなるのが常でした。
2026年9月15日、米TypeSafe AI社が Jev というモデルを公開しました。売りは「文字列を生成せず、型付きの判断と確信度を返す」。読んだ瞬間、あの防御コードの山が浮かびました。
考えてほしいのは、次の問いです。
あなたのコードがLLMに任せている判断のうち、本当に文章を必要としていたものはいくつあるか。
なお、筆者にはまだ早期アクセスが届いていません。そこで本記事では APIを一度も呼ばず、公開SDKと公式ドキュメントの回答例だけで、1コアのXeon環境で確かめられる範囲を確かめました。その過程で、公式ドキュメント同士の食い違いを2つ見つけています。
この記事の対象読者
この記事で得られること
読み終えたあと、自分のコードのどの分岐をJevに置き換え、どこをLLMに残すかを判定できるようになります。
そのための材料を順に揃えます。
- 3種類の質問形式と、その選び方
- confidence(確信度)の閾値を、公式の定義に頼らず決める方法
- 被害の大きさに応じて、自動実行・確認・人間対応を切り替えるコード
この記事で扱わないこと
- 内部アーキテクチャと学習手法の数理(非公開のため)
- 実APIでの速度・精度の実測(公開値の検算にとどめます)
発表情報
発表日: 2026年9月15日
発表主体: TypeSafe AI(創業者はRLHFの共同発明者と紹介されるDiogo Almeida氏)
提供形態: 早期アクセス(ウェイトリスト制)
1. Jevとは何か ── マークシートで答えるAI
このセクションで分かること:Jevが何者で、何を約束しているモデルなのか
最初に、全体で使うたとえを置きます。
LLMは 記述式で答える受験者 です。自由に書けるぶん解答欄の形式を無視することもあり、採点者であるコードはまず答案を読み解く必要があります。
Jevは マークシートで答える受験者 です。選択肢は出題者が事前に印刷しておき、受験者は欄外に何も書けません。その代わり、各マークを どれくらい濃く塗ったか で自信の度合いを伝えます。
公式の定義では、JevはTypeSafe AI社の最初の System One Model です。ソフトウェア内部の速い判断に特化し、文章を生成しないモデルの分類名で、カーネマンの「速い思考(システム1)」が由来です。Jevの名は、効率向上が需要を増やすと説いた経済学者ジェヴォンズから取られています。
入出力の形は、発表ブログの一文に集約されています。
unstructured state in, typed probabilistic decisions out.
(和訳:非構造の状態を入れ、型付きの確率的な判断を出す)
state は判断材料として渡すテキストやJSONです。以降はこの対応表で読めます。
| 公式用語 | たとえ | 型 |
|---|---|---|
| state | 問題文 | 文字列またはJSON |
| criteria | 印刷された選択肢 | 辞書またはリスト |
| probabilities | 各マークの塗りの濃さ | 0〜1の実数の辞書 |
| confidence | 答案全体のメリハリ | 0〜1の実数 |
| あなたのコード | 採点者 | ── |
2. LLMとどこが違うのか ── 解答欄の外に書けない
前節で、Jevは選択肢の外に答えを書けない受験者だと整理しました。それで何を得て何を失うのかを押さえないと、次節で質問形式を選ぶ基準が作れません。
このセクションで分かること:Jevが文字列生成を捨てた代わりに得たもの
公式ブログの比較表から抜粋します。
| 観点 | 既存LLM | Jev |
|---|---|---|
| 学習の目標 | 人間の好み(RLHF)、検証可能な報酬(RLVR) | 較正された判断(RLCD) |
| 出力 | 文字列。パースと検証が必要 | 事前定義した型の値と確率 |
| 生成方式 | 1トークンずつ逐次 | 全設問を1回で並列評価 |
| 応答時間 | 3〜329秒 | 70〜500ミリ秒 |
| 入力単価 | 100万トークンあたり0.20〜10ドル | 同0.042ドル |
| 出力単価 | 入力の約5倍 | 無料 |
RLHF は人間の好みに、RLVR は機械採点できる報酬に合わせる強化学習です。RLCD は、出力確率が実際の正答率と一致するよう調整するTypeSafe社の手法です。
公式が「ハルシネーションゼロ」とうたう根拠はここです。選択肢以外を書く欄がないので、存在しない部署名や壊れたJSONは原理的に出ません。
これは 答えが型の外に出ない という意味で、正解の保証ではありません。較正は予測の集団についての性質で、個々の答えの正しさは保証しないと公式も明記しています。
失ったものも明確で、Jevは返信文もコードも理由の説明も書けません。
3. 3種類の質問形式 ── 選択・段階・マルバツ
前節で、Jevの答えは必ず事前定義した型に収まることを見ました。つまり型の選び方が、そのまま判断の質を決めます。
このセクションで分かること:Choice・Score・Noulのどれを使うべきか
| 形式 | たとえ | 返る値 | 使いどころ |
|---|---|---|---|
| Choice | 多肢選択 |
choice, probabilities, confidence
|
答えが列挙済み候補のどれか |
| Score | 段階評価 |
score, probabilities, confidence
|
答えが順序つき尺度上の位置 |
| Noul | マルバツ |
noul(Yesの確率) |
答えがYesかNo |
Noul は公式の造語で、confidenceが付きません。0.5付近は「中くらい」ではなく「YesとNoが同程度にありうる」という意味なので、程度を測るならScoreを使います。Choiceの選択肢は最大255個で、漏れがありうるなら other を必ず用意します。
公式Quick startのコードです。
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient
client = TypeSafeClient() # 環境変数 TYPESAFE_API_KEY を読む
ticket = ("Hi, I've been trying to connect my Stripe account for 3 days "
"and it keeps failing. I'm losing sales. Please help ASAP.")
response = client.system_one(
state=ticket,
questions={
"department": Choice(
instructions="Which team should handle this",
criteria={
"billing": "Payment or subscription issues",
"technical": "Bugs or integration problems",
"sales": "Pricing or account questions",
},
),
"frustration": Score(
instructions="How frustrated the customer appears",
criteria=["Calm", "Frustrated", "Very angry"],
),
"is_urgent": Noul(instructions="The message conveys urgency"),
},
)
print(response.answers["department"].choice)
3つの設問は同じstateに対して 独立かつ並列に 評価されます。設問を足しても応答時間はほぼ変わらず、互いの文脈も汚しません。
設計原則は「1設問は詳しい人が数秒で判断できる粒度に絞る」です。「このピッチを評価して」ではなく、市場規模・実現性・差別化を別々のScoreで聞き、重みはコードの係数で決めます。
4. confidenceの正体を公式サンプルから逆算する
前節で、ChoiceとScoreにはconfidenceが付くことを見ました。実務ではこの値に閾値を引いて自動化の可否を決めますが、算出式は非公開です。閾値の前に、何を測る値なのかを知る必要があります。
このセクションで分かること:confidenceの算出式の有力候補と、自前で再計算すべき理由
公式の説明は「確率分布の広がりから計算した0〜1の値」までで、代わりに次の一文があります。
you are never locked into our definition.
(和訳:当社の定義に縛られることはない)
そこで、ドキュメントに載ったChoiceの回答例6件に候補式を当てはめました。まず記号を導入します。
$K$(ケー、整数)は選択肢の数です。1問に印刷されたマルの数に当たります。
$p_{\max}$(ピー・マックス、0〜1の実数)は全選択肢の確率の最大値です。いちばん濃く塗られたマークの濃さに当たります。
c = \frac{K \cdot p_{\max} - 1}{K - 1}
この式が言っているのは、「最大確率に、均等塗りなら0、1つだけ塗り潰しなら1になる目盛りを付け直したもの」です。均等塗りでは $p_{\max} = 1/K$ で分子が0に、塗り潰しでは $p_{\max} = 1$ で分子と分母が一致します。
比較用にもう1つ。$p_i$(ピー・アイ、0〜1の実数)を $i$ 番目の選択肢の確率とします。
c_H = 1 - \frac{-\sum_{i} p_i \log p_i}{\log K}
この式が言っているのは、「塗りの散らばり(エントロピー)を最大の散らばりで割り、裏返したもの」です。
import math
SAMPLES = [ # (出典, probabilities, 公式のconfidence)
("choice/department", [0.02, 0.38, 0.60], 0.39),
("choice/shipping", [0.63, 0.37, 0.0, 0.0, 0.0], 0.53),
("choice/resolution", [0.10, 0.37, 0.24, 0.29], 0.16),
("choice/tone", [0.08, 0.92, 0.0], 0.88),
("choice/easy", [0.0, 1.0, 0.0], 1.00),
("quickstart/dept", [0.159, 0.84, 0.001], 0.596),
]
def rescaled_max(p):
k = len(p)
return (k * max(p) - 1) / (k - 1)
def entropy_conf(p):
h = -sum(x * math.log(x) for x in p if x > 0)
return 1 - h / math.log(len(p))
CANDIDATES = {"rescaled_max": rescaled_max, "1-entropy": entropy_conf,
"max_prob": max}
for name, p, official in SAMPLES:
row = " ".join(f"{f(p):.3f}" for f in CANDIDATES.values())
print(f"{name:18s} {official:.3f} {row}")
TOL = 0.015 # docsの確率が小数2桁に丸められている分の許容誤差
for cname, f in CANDIDATES.items():
hits = sum(abs(f(p) - o) <= TOL for _, p, o in SAMPLES)
print(f"{cname:13s}: {hits}/{len(SAMPLES)}")
| サンプル | 公式値 | 目盛り直し $c$ | エントロピー $c_H$ | 最大確率 |
|---|---|---|---|---|
| choice/department | 0.390 | 0.400 | 0.315 | 0.600 |
| choice/shipping | 0.530 | 0.537 | 0.591 | 0.630 |
| choice/resolution | 0.160 | 0.160 | 0.063 | 0.370 |
| choice/tone | 0.880 | 0.880 | 0.746 | 0.920 |
| choice/easy | 1.000 | 1.000 | 1.000 | 1.000 |
| quickstart/dept | 0.596 | 0.760 | 0.594 | 0.840 |
| 一致件数 | ── | 5/6 | 2/6 | 1/6 |
Choiceページの5件は目盛り直し式で丸め誤差内に収まりました。ところがQuick startの1件だけは大きく外れ、エントロピー式にぴたりと一致します。
同じ公式ドキュメントの中に、別の式で計算されたように見える例が混ざっていました。
これは筆者にとって予想外でした。原因は外から判断できず、サンプルも6件なので 仮説 として扱ってください。ただ、実務上の結論は仮説の真偽に左右されません。
閾値判定には、probabilitiesから自分で計算した確信度を使う。
サーバー側の定義が変わっても、閾値の意味がぶれません。
5. 型安全を手元で確かめる ── 公式例が公式SDKに弾かれた
前節で、ドキュメントの回答例が一枚岩ではないと分かりました。ではテスト用のモック(APIの代わりの偽の応答)を、ドキュメント例から作ってよいのか。SDKがどこまで型を守るかを確かめます。
このセクションで分かること:SDK 0.6.0が応答のどこを検証し、どこを素通しするか
typesafe-sdk は応答JSONを、高速デシリアライザmsgspecで型付きオブジェクトに変換します。その内部処理に公式例と壊した応答を流します。
import json
import httpx2
from typesafe_sdk import SystemOneResponse
SCORE = {"type": "score", "score": 1.035, "confidence": 0.842,
"legend": {"0": "Calm", "1": "Frustrated", "2": "Angry"}}
PROBS = {"probabilities": {"0": 0.05, "1": 0.86, "2": 0.09}}
BAD = {"type": "choice", "choice": "a", "confidence": "high",
"probabilities": {"a": 1.0}}
def decode(answers: dict) -> str:
body = {"model": "jev-latest", "answers": answers,
"usage": {"input_tokens": 312, "output_tokens": 48}}
resp = httpx2.Response(200, content=json.dumps(body).encode())
try:
r = SystemOneResponse._decode(resp) # 内部メソッド。検証専用
return f"OK: {sorted(r.answers)}"
except Exception as e:
return f"{type(e).__name__}: {e}"
cases = {
"quickstart_as_is": {"frustration": SCORE}, # Quick startのScore例
"score_with_probs": {"frustration": {**SCORE, **PROBS}},
"confidence_is_str": {"tone": BAD},
"unknown_type": {"x": {"type": "rank"}},
}
for name, answers in cases.items():
print(f"{name:18s} -> {decode(answers)}")
| ケース | 結果 | 読み取れること |
|---|---|---|
| Quick startのScore例そのまま | ValidationError at answers.frustration.probabilities
|
Scoreにもprobabilitiesが必須 |
| probabilitiesを追加 | OK | 欠落が原因 |
| confidenceが文字列 | ValidationError at answers.tone.confidence
|
壊れた値は位置つきで止まる |
未知の形式 rank
|
OK(警告して無視) | 形式追加でアプリは落ちない |
公式Quick startのScore回答例は、公式SDK 0.6.0の型検証を通りません。
SDKの型定義ではScoreにもprobabilitiesがあるので、Quick startの例が古い形のまま残っていると考えられます。コピーしてモックを作ると、テストがいきなり落ちます。逆に言えば、型の外に出ないという約束を、SDKがクライアント側でも二重に守っています。
| 検証環境 | 値 |
|---|---|
| CPU / メモリ | Intel Xeon 2.10GHz × 1コア / 3.9GB |
| OS / Python | Ubuntu 24.04 / 3.12.3 |
| typesafe-sdk | 0.6.0(msgspec 0.21.1, httpx2 2.13.0) |
| 検証日 | 2026-09-16 |
ここまでのまとめ
- Jevは選択肢の外に答えず、塗りの濃さで自信を伝えます
- 設問は数秒で判断できる粒度に分解し、合成はコードで行います
- 閾値には 自前で再計算した確信度 を使います
- 公式例をモックに使う前に、SDKの型検証を通します
ここからは、どの分岐を任せるかを決めます。
6. 確信度で振り分ける ── 被害の大きさで閾値を変える
前節までで、閾値には自前の確信度を使うと決めました。残る問いは閾値をいくつにするかです。公式の答えは「一つの数字ではない」。間違えたときの被害が大きい操作ほど、閾値を上げます。
このセクションで分かること:自動実行・確認・人間対応の3経路を、操作の危険度ごとに切り替えるコード
たとえで言えば、メリハリの強い答案は通し、迷いの見える答案は本人に確認し、均等塗りに近い答案は人間の試験官に回します。
from dataclasses import dataclass
from typesafe_sdk import ChoiceAnswer
@dataclass(frozen=True)
class Gate:
act: float # これ以上なら自動実行
review: float # これ以上なら確認付き、未満は人間へ
GATES = {"read_only": Gate(act=0.5, review=0.3),
"destructive": Gate(act=0.9, review=0.5)}
def own_confidence(ans: ChoiceAnswer) -> float:
"""公式値に依存せず、probabilitiesから自前で再計算する。"""
k = len(ans.probabilities)
return (k * max(ans.probabilities.values()) - 1) / (k - 1)
def route(ans: ChoiceAnswer, risk: str) -> str:
g, c = GATES[risk], own_confidence(ans)
if c >= g.act:
return f"AUTO ({ans.choice}, c={c:.2f})"
if c >= g.review:
return f"CONFIRM({ans.choice}, c={c:.2f})"
return f"HUMAN (c={c:.2f})"
閾値を Gate に分離したので、判定ロジックに触れずに危険度の種類を増やせます。公式の回答例を流し込みます。
from typesafe_sdk import ChoiceAnswer
from gate import route
def ans(choice: str, **probs: float) -> ChoiceAnswer:
return ChoiceAnswer(choice=choice, confidence=0.0, probabilities=probs)
EXAMPLES = { # docs.typesafe.ai/primitives/choice の回答例
"department": ans("returns", shipping=0.02, billing=0.38, returns=0.6),
"resolution": ans("exchange", information=0.1, exchange=0.37,
replacement=0.24, refund=0.29),
"tone": ans("frustrated", angry=0.08, frustrated=0.92, calm=0.0),
}
for name, a in EXAMPLES.items():
for risk in ("read_only", "destructive"):
print(f"{name:11s} {risk:12s} -> {route(a, risk)}")
| 設問 | 自前confidence | 読み取り専用の操作 | 破壊的な操作 |
|---|---|---|---|
| department | 0.40 | 確認付きで実行 | 人間へ |
| resolution | 0.16 | 人間へ | 人間へ |
| tone | 0.88 | 自動実行 | 確認付きで実行 |
同じ確信度でも、操作の危険度で行き先が変わります。resolutionは顧客が要望を書いていないため分布が平らで、どちらでも人間に回ります。モデルが迷いを表明できること自体が安全装置です。
閾値 0.3 / 0.5 / 0.9 は公式の例に合わせた出発点です。自分のデータで誤判定率を見ながら調整してください。
7. 速さと安さの数字を検算する
前節で安全な組み込み方が揃いました。残るのは置き換える価値の有無です。公式の倍率を、APIを呼ばずに検算します。
このセクションで分かること:公式の倍率がそれぞれ何と何の比較か
JEV_INPUT_PER_MTOK = 0.042 # $/100万トークン
LLM_INPUT_RANGE = (0.20, 10.0) # ブログ表の既存LLM入力単価
DEMO = {"jev": (0.000081, 0.114), "llm": (0.013880, 8.566)} # (USD, 秒)
cost_x = DEMO["llm"][0] / DEMO["jev"][0]
time_x = DEMO["llm"][1] / DEMO["jev"][1]
print(f"トップページ動画: {cost_x:.1f}倍安い / {time_x:.1f}倍速い")
lo, hi = (x / JEV_INPUT_PER_MTOK for x in LLM_INPUT_RANGE)
print(f"入力単価の倍率 : {lo:.1f}倍 〜 {hi:.1f}倍")
calls_tokens = 588 * 1_000_000 # docsの5問リクエスト例を100万回
for price in (JEV_INPUT_PER_MTOK, *LLM_INPUT_RANGE):
print(f"単価${price}: 入力代 ${calls_tokens / 1e6 * price:,.2f}")
| 比較 | コスト | 速度 | 出典 |
|---|---|---|---|
| トップページの並走動画 | 171.4倍安い | 75.1倍速い | 1問い合わせの実行例 |
| 見出しの数字 | 444.6倍安い | 193.6倍速い | ワークフロー評価 |
| 入力単価の比 | 4.8〜238.1倍安い | ── | ブログ比較表 |
| 588トークン×100万回の入力代 | Jev 24.70ドル、LLM 117.60〜5,880ドル | ── | 単価から試算 |
見出しの倍率は、4種類の業務ワークフロー評価から来ています。公式ブログは、これが現実の効果として 高めの側 だと自ら断り、次の注記も添えています。
- 基準の正解はGPT-6 AstraとFable 5.1の予測平均で、両社のモデルに有利
- ワークフローは社内チームの作成で、偏りがありうる
- 料金が補助されていないことは、長期運用でしか証明できない
トップページの「Claude Fable 5系のFable 5.1より入力が238倍安い」は、上限10ドルとの比に一致します。
倍率の大小よりも、出力が無料で、設問を増やしても応答時間がほぼ変わらない構造のほうが、設計への影響は大きいと筆者は考えています。
使うか分からない設問も1回に全部載せておける設計を、公式は 投機的ファンアウト と呼びます。
8. どの分岐をJevに任せるか ── 判定フロー
ここまでで、Jevの約束・型の選び方・確信度の扱い・コスト構造が揃いました。冒頭で約束した判定は、これらを順に問うだけで決まります。
このセクションで分かること:自分のコードの分岐を、Jev向きかLLM向きかに振り分ける手順
| 問い | 根拠となった節 |
|---|---|
| 文章が必要か/候補を列挙できるか | 第2節:答えは事前定義した型に限られる |
| 数秒で判断できるか/255個以内か | 第3節:設問の粒度とChoiceの上限 |
| ゲートの設定 | 第4節・第6節:自前の確信度と危険度別の閾値 |
公式クックブックには、RAG文書の選別や引用の裏取り、LLMアプリのガードレールといった例が並びます。LangChainやMCPで組んだエージェントの「次に呼ぶツール」も、候補を列挙できるので対象です。
公式が インテント振り分け と呼ぶ併用形もあります。Jevが種類を判定し、文章が必要なものだけをLLMへ回す、つまり記述式の問題だけを記述式の受験者に回す運用です。
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
モックで probabilities の位置にValidationError |
Quick startのScore例が古い形 | Scoreの回答にもprobabilitiesを含める |
| confidenceがいつも低い | 選択肢の説明が重なっている | criteriaをオブジェクトにし、対象外の範囲や例文も書く |
| Noulが0.5付近に張り付く | 設問の定義が曖昧 | criteriaのtrue/falseを書くか、Scoreに替える |
| 当てはまらないのに無理に選ばれる | 逃げ道の選択肢がない |
other を追加する |
| 候補が256個以上ある | Choiceの上限は255 | 階層分類で段階的に絞る |
| 認証エラー | APIキー未設定、または早期アクセス前 |
TYPESAFE_API_KEY を設定、ウェイトリスト登録 |
用語集
| 用語 | 定義 | たとえ |
|---|---|---|
| System One Model | ソフトウェア内部の速い判断に特化し、文章を生成しないAIモデルの分類 | マークシート専用の受験者 |
| state | 判断材料として渡すテキストやJSON | 問題文 |
| Choice / Score / Noul | 多肢選択・段階評価・Yes確率の3形式 | 設問の種類 |
| probabilities | 各選択肢の確率 | 各マークの塗りの濃さ |
| confidence | 確率分布の集中度を0〜1にまとめた値 | 答案全体のメリハリ |
| 較正 | 確率が実際の正答率と一致すること | 濃く塗った答えほど実際に正解している状態 |
学習ロードマップ
LLMの基礎は、次の記事で扱っています。
まとめ
- Jevは文章の代わりに 型付きの判断と確率 を返します。答えが型の外に出ないことは保証されますが、正しさは保証されません
- 設問は3形式。数秒で判断できる粒度に分解し、合成はコードで行います
- confidenceの式は非公開で、公式例同士にも食い違いがありました。閾値には自前で計算した値を使います
- 閾値は一つではなく、操作の危険度ごとに変えます
- 設計に効くのは倍率の大きさより、出力無料と並列評価という構造です
冒頭の問いに戻ります。筆者が過去に書いた分類コードを振り返ると、文章が本当に必要だった箇所は半分もありませんでした。早期アクセスが届いたら、今回の閾値を実データで測って続編で報告します。
参考文献
- TypeSafe AI 公式サイト ── 製品概要、価格、FAQ
- Introducing System One Models & Jev(邦題訳:System One ModelとJevの紹介)── 発表記事。比較表と評価の注記
- TypeSafe Docs: Confidence(邦題訳:確信度)── 危険度別の閾値の例
- TypeSafe Docs: Choice(邦題訳:選択形式)── 逆算に使った回答例5件の出典
- TypeSafe Docs: Quick start(邦題訳:クイックスタート)── 逆算の残り1件と、Score回答例の出典
最後まで読んでいただき、ありがとうございました。AI関連の検証ネタはXで発信しています。
zennでも関連記事を執筆しています。
noteでは非エンジニアに向けて執筆しました。