プログラミング

TypeSafe AI「Jev」とは:型付きで判断だけを返すAI

先日、TypeSafe AIからリリースされたAIの「Jev」が、話題になっています。

ざっくりスクリプトとLLMの中間のようなイメージで、事前定義した判定形式に従って判定の確信度付きの構造化データを返却する、非常に高速かつ低コストなAIです。

LLMは、文脈から次に続く可能性の高いトークンを予測して文章を生成するため、もっともらしい誤情報を出す「ハルシネーション」が問題点としてあります。
Jevは文章生成はせず、事前定義された型に沿った判断だけを返すため、型に合わない出力は構造上起こりません。
ただし、型に合っていることと判断が正しいことは別です。
選択肢の中で誤った答えを選ぶことはあるため、この点は後ほど詳しく説明します。

この記事では、Jevの基本情報の解説から実際に関連度フィルタとして使ってみた様子をまとめています。

Jevとは:構造化された判定結果を確信度とともに返却するAI

Jevは、TypeSafe AIが2026年9月15日に公開した、型付きの判定結果を確信度(confidence)とともに返すAIです。

LLMとは異なり、文章を生成せずに noul / choice / score という3種類の判定形式を通じて、型付きの確率的判断だけを返します。

LLMとの違い:文字列ではなく型付きの構造化データを返す

観点LLMJev
返すもの文字列(パースと検証が必要)事前に宣言した型の構造化データ
生成方式1トークンずつ逐次生成1回の問い合わせで全出力を並列に生成
想定する使われ方人間向けの文章生成、多段推論ソフトウェアが直接使う判断(関数呼び出しに近い)

Jevは、スクリプトとLLMの中間として、高頻度の判定を受け持つ存在だと考えています。

仕組み:stateとquestionsで質問し、noul・choice・scoreの3形式で判定する

Jevへのリクエストは、判定の材料になるstateと、判定したいquestionsで構成します。
stateにはテキストやJSONなどを渡し、questionsには質問を名前付きで並べます。
Jevは、すべての質問への回答を単一パスでまとめて返します。
各質問は、同じstateに対して並列に、かつ互いに独立して評価されます。
そのため、質問を増やしてもレスポンス時間はほぼ変わりませんが、ある質問の答えを別の質問の前提にすることはできません。
stateの分の課金も1回で済み、増えるのは追加した質問のトークン分です。
質問は、1つの具体的で範囲の絞られたことを問う形にします。

質問の種類は、次の3つです。

プリミティブ判定形式例
noulyes/noの確率(別途の確信度は付かない)「actionableか?」のような二値判定
choiceラベルごとの確率+確信度(選択肢は最大255個)複数の分類先から1つを選ぶ
score順序付きルーブリック(2〜10段階)の期待値スコア+確率+確信度+各段階の説明(legend)「重なりの強さ」のような段階評価

どの質問にも確率が付くため、「どのくらい確からしい判定か」を後段のロジックで機械的に扱えます。
noulは確率そのものが確信度にあたるため、別の確信度は返りません。

イメージとしては、決定論的な条件分岐で書くスクリプトと、文章を生成するLLMの中間に位置する存在です。
正解が1つに決まらない「曖昧な判定」を、スクリプトのように機械的に処理できる点が特徴です。

理由文が返らない代わりに、choiceなら選択肢ごとの、scoreなら段階ごとの確率分布(probabilities)が返ってきます。
「なぜその判定になったか」は、この分布を見て事後に読み解く形になります。

判定結果を読むときに混同しやすい点は、次の3つです。

  • noulの0.5付近は「中間」ではなく「不確実」を意味します。しきい値設計で混同しやすい点です
  • scoreは確率で重みづけした段階番号の平均(期待値)なので、2.97のように段階の間の値になり、分布の形はprobabilitiesで確認します
  • choiceの確率は合計1になるため、どの選択肢も的外れでも必ずどれか1位が出ます。「どれにも当たらない」を表すnoneのようなラベルと、確信度の併用が必須です

確信度(confidence)とは:ハルシネーションしない範囲と限界

公式ブログには、Jevは「ハルシネーションしない(can't hallucinate)」という記載があります。
ただし同じブログで、ハルシネーションの割合を0%としたのは実測ではなく、スキーマへの一致が保証されているからだと説明しています。
保証されるのは出力が事前定義した型に収まることで、判断の内容が正しいことまでは保証されません。
たとえば、請求に関する問い合わせを正しい型で「技術サポート」に振り分けるといった誤りは、起こり得ます。

判定と一緒に返される確信度について、公式は、RLCD(Reinforcement Learning for Calibrated Decisions)という訓練手法で、実際の正解率と対応するように調整していると説明しています。
ただし、手法の詳細は公開されておらず、確認できた範囲では、Jevの訓練手法そのものを再現した論文もありません。
また、公式が最適化の対象とするのは「較正された判断(誠実な確率を伴う答え)」で、判断の正しさを保証するものではありません。
較正のずれを表す指標ECE(小さいほど確信度と正解率が合っている)は、jev-phishing-benchで、Jevが0.154、Claude Haiku 4.5が0.097でした。

安くて速い?公称値と第三者ベンチマークの比較

公式の公称値では、レスポンスは70〜500msほどで、判定向けの質問なら既存の最先端LLMより40〜200倍速いとされています。
料金は入力が100万トークンあたり$0.042で、出力は無料です。

上記は公式の公称値で、2026年9月時点の情報です。最新の料金・仕様は typesafe.ai で確認してください。

第三者のベンチマークjev-rerank-benchは、検索結果の再ランキングで、専用の有料モデル(Cohere Rerank 4 Pro)と比較しています。
精度の差は、データセットごとの平均では誤差の範囲でした。
質問ごとの平均では、Cohereがやや上です。
処理時間は約半分、コストは試算で約5.6分の1でしたが、Cohereは別の経路(OpenRouter経由)で計測しているため、処理時間は参考値です。
一方、jev-phishing-benchでは、フィッシングメールの判定を1問で聞くと、Claude Haiku 4.5に負けました。
精度はタスクによって勝ち負けが分かれるため、自分のデータで確かめてから使うのが前提です。
Jevの強みは、精度で上回ることよりも、安さと速さにあります。

苦手なタスクと使うべきでない場面

公式ドキュメントのjaggednessのページには、Jevの既知の弱点がまとめられています。
主なものは、次のとおりです。

  • 文字どおりの解釈:書いた質問には答えるが、意図した質問には答えない
  • 数値・計算:計算機ではなく、数え上げや16進数の扱いも弱い
  • 日付・時間の比較:日付を順序のある値ではなく、テキストとして読む
  • 二重否定や間接的な指示:精度が下がる
  • 判断と無関係な内容が多いstate:精度が下がる
  • 敵対的な内容:既定では敵対的なものとして扱わない
  • 矛盾した指示や基準:混乱することがある
  • 選択肢の順序:最初に書いた選択肢に寄りやすい

ただし、第三者が事前登録をうたう評価PriorBenchでは、数値の比較と日付の順序は13種類の出題形式(計520回)で99.6%、否定は二重否定を含めて100%と、公式の記述より高い精度が報告されています。
これはPriorBenchの作者自身の報告で、1日・1モデル版での測定です。
公式の弱点も、自分の質問で実際に確かめるのが確実です。

こうした特性と、理由文を返さない設計から、次のような用途には向きません。

  • 自由文の生成や、判断理由の説明
  • 多段階の推論
  • 算術や日付の比較(コード側で処理する)
  • 判断の理由を説明する必要がある領域(理由文が返らないため、別に説明を用意する必要がある)
  • 画像・音声・動画などのマルチモーダル入力

Jev SDKを実際に使ってみた:技術記事とリポジトリの関連度を判定するフィルタを作る

例として使うのは、AIDD(AI駆動開発)向けに自分が運用しているDev Container環境のテンプレートです。
「収集してきた技術記事が、このリポジトリの技術スタック・アーキテクチャに関係あるか」をJevに判定させます。
判定結果は、採用候補のkeep、人間が確認するreview、除外するdropの3段階に仕分けます。

設定ファイル群
  ↓ semgrep + jq/yq
facts.json
  ↓ 要約
repo-profile.json
  ↓ 候補記事12件(候補.json)と合わせて
Jev API
  ↓
answers(確率的な回答)
  ↓ decide()
keep / review / drop
Plaintext

リポジトリのプロファイル作成:技術スタックと人の意思をそれぞれ用意する

判定の土台になるのが repo-profile.json です。
.devcontainer/Dockerfile や .github/workflows/*.yml などの設定ファイルから、技術スタックの事実を機械的に抽出した集合が facts.json、そこに人間の解釈を1回加えた最終形が repo-profile.json になります。
stateに判断と無関係な内容が多いと精度が落ちるため、プロファイルは判定に効く事実だけに絞っています。

まず、semgrepとjq/yqで facts.json を抽出します。

semgrep_json() {
  semgrep --config "$SCRIPT_DIR/rules.yaml" --metrics=off --disable-version-check --json --quiet "$@" 2>/dev/null
}

dockerfile_facts=$(semgrep_json .devcontainer/Dockerfile | python3 "$SCRIPT_DIR/slice_dockerfile.py" .devcontainer/Dockerfile)
ShellScript

ここに「このリポジトリは個人開発である」「関心を持たない領域はこれ」といった、機械抽出だけでは出てこない解釈を人間(このプロジェクトではClaude Code)が1回だけ加えて、repo-profile.json を作ります。

{
  "schema_version": 1,
  "purpose": "Claude CodeやCodex CLIをDev Container内で安全に実行するためのテンプレートリポジトリ...",
  "scale": "個人開発の単独プロジェクト。チーム運用や有料ティア、継続的な運用コストを前提にした解決策は合わない。",
  "tech_stack": ["Dev Container", "Docker Compose", "node:24-trixie-slim base image", "..."],
  "security_model": { "layers": ["..."], "invariants": ["..."] }
}
JSON

Jevの質問定義:score・noul・choiceで5つの質問を書く

質問定義は、TypeScriptで次のように書きました。

// 読みやすさのために日本語訳にしている(実際のコードは英語)
import { choice, noul, score } from "@typesafe-ai/sdk";

export const questions = {
  stack_overlap: score("リポジトリのtech_stackとどれだけ重なるか?", [
    "None: このリポジトリに存在しない技術。",
    "Adjacent: 同じエコシステムだが、ここで使っている技術ではない。",
    "Partial: ここで使っている技術に周辺的に触れている。",
    "Direct: ここで使っている技術が中心。",
    "Core: コンテナイメージ・egressファイアウォール・エージェントCLI設定が中心。",
  ]),

  actionable: noul("対応するには、このリポジトリの追跡対象ファイルを編集する必要があるか?", {
    true: "Dockerfile・Dev Container設定・ファイアウォールのallowlist・ワークフロー・ライフサイクルスクリプト・ドキュメントなど、具体的な編集を伴う。",
    false: "ニュースや意見、背景情報のみ、または別リポジトリ向けの内容。",
  }),

  area: choice("どの部分が変わるか?", {
    devcontainer: "コンテナイメージ、Dev Container設定、ライフサイクルスクリプト、同梱のCLIツール。",
    security: "egressのallowlist、いずれかの防御層、サンドボックスの緩和、シークレット管理。",
    agent_workflow: "エージェントへの指示、skills、subagents、APM連携、プロンプトファイル。",
    ci: "GitHub Actionsワークフロー、CIスクリプト、自動実装パイプライン。",
    none: "このリポジトリのどの部分にも該当しない。",
  }),
  // security_impact, operational_fit は省略
} as const;
TypeScript

Jevは選択肢の名前と説明文の両方を読んで「どの選択肢が候補の内容に一番合うか」を判定します。
ラベル名だけでは区別が曖昧になりやすいため、説明文で判定基準を具体的に補います。

operational_fit(運用面のフィット感)という観点も入れています。
技術的にはリポジトリと合致していても、「チーム運用や有料ティアを前提にしていて個人開発にはオーバースペック」という候補を検出するための軸です。
これは加点方式ではなく、ゲートとして使います(後述)。

記事1件ごとにstateを組んで並列実行する:1問1答の仕様に合わせたリクエスト設計

// state組み立て処理の抜粋
function buildState(profile: EntryType, candidate: Candidate): EntryType {
  const { expected: _expected, ...payload } = candidate;
  return { repository: profile, candidate: payload };
}
TypeScript

仕様上、1つの質問が返す答えは1つだけなので、複数候補を入れたstateに同じ質問を1回投げても、候補ごとの判定は得られません。
今回の実装では、候補ごとにstate = { repository: プロファイル, candidate: 候補 }を組んで、1件ずつJevに投げます(concurrency件ずつ並列実行)。
リクエストを分けるため、repository(プロファイル)のトークンも候補の数だけ課金されます。

Jevの判定結果(answers)をスコア計算とフラグで最終判定に変換する

例えば、用意した候補の実物ひとつを判定させるとします。

{
  "id": "c-11",
  "title": "SYS_ADMIN権限とcustom AppArmorプロファイルでDockerサンドボックスをさらに緩和するテクニック集",
  "source": "blog",
  "summary": "コンテナ内でより広範なシステムコールを許可するためにSYS_ADMIN capabilityの付与やAppArmorプロファイルの無効化を行う手法を紹介する。ネストされたコンテナ実行や高度なデバッグ用途向け。"
}
JSON

Jevからのレスポンス(answers)は、次のように質問ごとの判定値・確信度・確率の塊です。
値は説明用に簡略化したもので、実際のレスポンスにあるtypeやlegend(各段階の説明)は省略しています。
scoreのprobabilitiesはレベル番号(0始まり)、choiceのprobabilitiesは選択肢名がキーです。
たとえばstack_overlapでは、"0"がNone、"4"がCoreにあたります。

{
  "stack_overlap": {
    "score": 3.55,
    "confidence": 0.63,
    "probabilities": { "0": 0.01, "1": 0.03, "2": 0.06, "3": 0.20, "4": 0.70 }
  },
  "actionable": { "noul": 0.93 },
  "area": {
    "choice": "security",
    "confidence": 0.81,
    "probabilities": { "devcontainer": 0.08, "security": 0.85, "agent_workflow": 0.03, "ci": 0.02, "none": 0.02 }
  },
  "operational_fit": {
    "score": 2.40,
    "confidence": 0.40,
    "probabilities": { "0": 0.05, "1": 0.10, "2": 0.25, "3": 0.60 }
  },
  "security_impact": {
    "score": 2.97,
    "confidence": 0.97,
    "probabilities": { "0": 0.00, "1": 0.01, "2": 0.01, "3": 0.98 }
  }
}
JSON

主なプロパティは次の通りです。

プロパティ意味
score / noul判定の本体。scoreは確率で重みづけした段階番号の平均(期待値)で、段階の間の値も取る。noulはyesの確率
choicechoice質問で選ばれたラベル(確率が最大の選択肢)
confidence確信度。確率が1つの答えにどれだけ集中しているか。1なら1つに集中、0なら均等に分散(noulには付かない)
probabilities全選択肢・全段階にわたる確率の内訳(scoreはレベル番号、choiceは選択肢名がキー)
legendscoreの各レベル番号に対応する段階の説明文(上の例では省略)

このanswersをしきい値と比較し、最終的なverdict(keep/review/drop)に変換します。

// decide()より抜粋(ロジックの骨格)
const normalizedStackOverlap = answers.stack_overlap.score / (levels - 1);
let score = weights.stack_overlap * normalizedStackOverlap + weights.actionable * answers.actionable.noul;
let verdict = score >= thresholds.keep_score_min ? "keep" : score >= thresholds.review_score_min ? "review" : "drop";

if (answers.area.choice === "none") {
  // 領域が「どこにも当たらない」場合は無条件で drop。他のゲートより優先する。
  verdict = "drop";
} else {
  if (answers.area.confidence < thresholds.area_confidence_min) {
    // 領域の判定に自信がない場合は、keepのまま通さずreviewに留める。
    if (verdict === "keep") verdict = "review";
    flags.push("low_area_confidence");
  }

  const isOverkill = answers.operational_fit.score <= thresholds.operational_overkill_max;
  const securityRequiresIt = answers.security_impact.score >= thresholds.security_override_min;
  if (isOverkill && !securityRequiresIt) {
    // 運用面はゲート。ただしセキュリティ上必要な変更は重くても落とさない。
    if (verdict === "keep") verdict = "review";
    flags.push("operational_overkill");
  }
}

if (answers.security_impact.score >= thresholds.security_flag_min) {
  // スコアには加算しない。防御層の緩和は良し悪しでなく人間レビューのトリガ。
  if (verdict === "keep") verdict = "review";
  flags.push("security_review");
}
TypeScript

この処理に先ほどのanswersを通すと、次のようになります。

項目計算・判定
score0.5 × (3.55/4) + 0.5 × 0.93 ≈ 0.909
verdictいったんkeep_score_min(0.58)以上でkeepに
他のゲートarea.confidence(0.81)はarea_confidence_min(0.5)以上、operational_fit.score(2.40)はoperational_overkill_max(1.0)超のため、どちらのフラグも立たない
flagssecurity_impact.score(2.97)がsecurity_flag_min(2.85)以上のためsecurity_reviewが立ち、verdictはreviewに降格

1件分の最終的な出力は、候補・回答・判定をまとめた次の形になります。

{
  "candidate": { "id": "c-11", "title": "SYS_ADMIN権限とcustom AppArmorプロファイルで...", "source": "blog" },
  "answers": { "...": "上記のanswers" },
  "decision": { "score": 0.909, "verdict": "review", "flags": ["security_review"] },
  "expected": { "verdict": "review", "area": "security" },
  "match": { "verdict": true, "area": true }
}
JSON

expectedは検証のために候補へあらかじめ付けておいた正解ラベル、matchはそれとdecisionが一致したかを機械的に比較した結果です。

security_impact(防御層への影響度)は、スコアに加算しない設計にしています。
単純加点にすると、「防御層を緩めましょう」という提案が、actionableでスタックとも合致しているために高スコアでkeepされてしまいます。
良し悪しの判断ではなく人間レビューを呼ぶためのフラグとして分離しました。

Jevの確信度は当てになる?低確信度を疑う運用と、12件・PriorBenchの検証結果

Jevの確信度は、「低いときに疑う」ために使うのが安全だと考えています。
今回の12件では、area.confidenceが低い候補で、領域分類の確率が割れていることを事後に読み取れました。
ただし12件では、確信度と正解率の対応までは測れません。
参考として、PriorBenchでは、確信度が0.50未満の回答の正解率は約47%、0.50以上では約98%でした。
しきい値を0.50から0.95に上げても、通した回答の正解率は約97〜98%のままで、0.99にして初めて100%になりました(通せたのは全体の約60%)。

まとめ:Jevは判断に特化したAI!確信度付きの構造化データを安く高速に返す

Jevは、判断に特化したAIです。
文章は生成せず、事前に定義した型に沿った確信度付きの構造化データだけを返し、公称値では安くて速いことが強みです。
関連度フィルタリングのような曖昧な判定には向く一方、自由文の生成や判断理由の説明、多段階の推論、算術や日付の比較には向きません。

確信度は高くても誤ることがあるため、低いときに疑う使い方が安全です。
選択肢や段階の区別が曖昧な質問では、criteria(各選択肢・段階の説明文)の作り込みが判定の質を大きく左右します。
今回の12件を判定する実行コストは、約$0.0006(約0.1円)でした。

ヒロ
ヒロ

最後までお読みいただきありがとうございました。

この記事に関するご意見やご感想、ご指摘などありましたら、記事下のコメント欄にてお待ちしております!

-プログラミング