Ollama × Jevライクモデルで始める意思決定モデルの活用

ChatGPTをはじめとするLLMは、チャットや文章生成では既に「超人的」と言えるレベルに達しています。しかし、実際の業務システムに目を向けると、「このサポートチケットはどのチームが担当すべきか」「この問い合わせは緊急か」といった、地味だけれど量が多い判定・分岐処理は、依然として人手のルールベース実装やLLMへの力任せな問い合わせに頼っているケースが少なくありません。

LLMにこうした判定を丸投げすると、応答に数秒〜数十秒かかり、1トークンあたりのコストもかかるうえ、たまに期待した形式で返ってこない(ハルシネーション)という問題が付きまといます。チャットには向いているLLMのアーキテクチャが、「型が決まった小さな決定を高速に、安く、確実に下す」という用途にはあまり向いていないのです。

この課題に対して2026年9月、TypeSafe AI社がSystem One Modelsという新しいモデル層と、その第一号モデルJevを発表しました。そして同時期、Ollama上でも同じ思想を持つ「決定モデル」が複数公開され、その一つが今回実際に手元で動かしてみた tev1 です。

この記事では、Jev / System One Models がどういう発想のモデルなのかを整理した上で、Ollamaで実際にtev1を動かし、公式クライアント(ollamaパッケージ)と、TypeSafe社のSDK(typesafe-sdk)の両方から呼び出せることを確認します。

Jev / System One Models とは

Jevは、TypeSafe AI創業者のDiogo Almeida氏(OpenAI出身、InstructGPT/RLHFの共同発明者)が発表した、判断に特化したAIモデルです。自由な文章を生成する代わりに、あらかじめ定義した「質問」に対して型付きの値と確率を返します。

3つの質問タイプ

Jev、および以降で紹介するOllama上の同系モデルは、いずれも次の3種類の質問タイプをサポートします。

タイプ用途返り値
choice複数の候補から1つを選ぶ選択結果 + 候補ごとの確率 + confidence
noulはい/いいえを判定する0〜1の確率値(1に近いほど「真」)
score段階的な評価をする期待値(score) + 確率分布 + legend(段階の説明) + confidence

noulというあまり聞かない単語が出てきますが、これは「真である確率」を表すJev独自の用語です。

既存LLM(System Two的アプローチ)との対比

TypeSafe社は、人間の「速い・直感的な思考」と「遅い・論理的な思考」を対比したカーネマンの「システム1 / システム2」という概念に倣って、既存のLLMを「System Two的アプローチ」、Jevのような新しいモデル層を「System One Models」と位置づけています。

項目既存LLM(System Two的)System One + Jev
最適化目標人間の好み (RLHF/RLVR)校正済み決定の正確性
出力形式自由文字列(柔軟だが曖昧)型安全な構造化値
サンプリング逐次(トークン単位)並列(単一クエリで全出力)
入力コスト$0.20〜10/MTok$0.042/MTok
出力コスト入力の約5倍無料(計測不能レベル)
応答速度3〜329秒70〜500ms
ハルシネーション発生する型エラーとしては発生しない

学習方法にはRLCD (Reinforcement Learning for Calibrated Decisions) という手法が使われており、「確率的に校正された決定」、つまり「80%と答えたときには実際に80%の確率で正しい」という性質を学習させることに重点を置いています。

向いている用途・向いていない用途

公式ブログ等の情報を総合すると、次のような整理になります。

向いている用途

  • サポートチケットやメールの自動振り分け
  • ポリシー該当性の判定、コンテンツ審査
  • LLMの出力やプロンプトの検証・ガードレール
  • 大量データに対するマップ・リデュース的な特徴抽出

向いていない用途

  • 長文生成や根拠の説明が必要な処理
  • 画像・音声・動画の直接判断(※一部モデルはマルチモーダル対応、後述)
  • 数値計算や日付の境界判定など、本来コードで正確に処理すべきもの

Ollamaで使えるJevライクモデル

Jev自体は2026年10月時点ではTypeSafe社のクラウドAPI上での早期アクセス提供ですが、同じ「状態(state)と型付き質問(questions)から決定を返す」というコンセプトを持つモデルが、Ollama上でも複数公開されています。いずれも/v1/systemoneという共通のAPIエンドポイントを実装しているのが特徴です。

モデル提供元パラメータ数特徴
tev1Together AI4B / 0.8B標準的な決定モデル。精度73.3%(4B)/63.5%(0.8B)
nimbleBespoke Labs9B精度75.7%(4モデル中最高)。M5 Max搭載Macで100ms未満
clefCloudflare27Bマルチモーダル対応、256Kコンテキスト、高精度
clef-flashCloudflare9Bclefの軽量版。約5倍高速、低遅延(中央値38.8ms)

精度はOllama公開の評価(13の公開データセット、3,880件の決定)による比較値[^4]。

選び方のざっくりした指針は次の通りです。

  • 精度最優先・画像入力も使いたい → clef
  • 精度と速度のバランスを取りたい → nimble
  • とにかく軽量に、手元でまず試したい → tev1(本記事の検証対象)
  • 低遅延最優先でマルチモーダルも欲しい → clef-flash

今回は手元の環境に最初にインストールしたtev1を使って、実際にコードから呼び出してみます。

Ollama 公式クライアントを利用する場合

事前準備

Ollama環境にtev1をインストールし、サーバーを起動しておきます。

ollama pull tev1
ollama serve

今回は、Pythonの依存関係を uv で管理し、公式クライアントを追加します。

uv add ollama

サンプルコード

サポートチケットの本文を渡し、「カテゴリ分類(choice)」「緊急対応が必要か(noul)」「深刻度(score)」の3つを一度に判定させる例です。

"""tev1 (Ollama の System One モデル) を使った意思決定サンプル。"""

from __future__ import annotations

import sys

import ollama

if sys.stdout.encoding is not None and sys.stdout.encoding.lower() != "utf-8":
    sys.stdout.reconfigure(encoding="utf-8")


def classify_support_ticket(ticket_text: str) -> None:
    response = ollama.systemone(
        model="tev1",
        state=ticket_text,
        questions={
            "category": {
                "type": "choice",
                "instructions": "このチケットはどのカテゴリに分類されますか?",
                "criteria": {
                    "billing": "請求・支払いに関する問い合わせ",
                    "technical": "製品の不具合やエラーに関する問い合わせ",
                    "account": "アカウントやログインに関する問い合わせ",
                    "other": "それ以外の問い合わせ",
                },
            },
            "is_urgent": {
                "type": "noul",
                "instructions": "このチケットは緊急対応が必要ですか?",
                "criteria": {
                    "true": "サービス停止やデータ損失など、即時対応が必要な内容",
                    "false": "即時対応が不要な内容",
                },
            },
            "severity": {
                "type": "score",
                "instructions": "このチケットの深刻度はどの程度ですか?",
                "criteria": [
                    "軽微: 利用に支障はない",
                    "中度: 一部の機能に影響がある",
                    "重大: 主要機能が使用できない",
                    "致命的: サービス全体が停止している",
                ],
            },
        },
    )

    category = response.answers["category"]
    is_urgent = response.answers["is_urgent"]
    severity = response.answers["severity"]

    print(f"モデル: {response.model}")
    print(f"カテゴリ: {category.choice} (確信度 {category.confidence:.2f})")
    print(f"  内訳: {category.probabilities}")
    print(f"緊急フラグ: {is_urgent.noul:.2f} (真である確率)")
    print(f"深刻度スコア: {severity.score:.2f} -> {severity.legend}")


sample_ticket = (
    "本日未明からアプリにログインできず、決済処理も全て失敗しています。"
    "至急対応をお願いします。"
)
classify_support_ticket(sample_ticket)

実行結果は次のようになりました。

モデル: tev1
カテゴリ: account (確信度 0.47)
  内訳: {'billing': 0.002, 'technical': 0.472, 'account': 0.520, 'other': 0.006}
緊急フラグ: 0.97 (真である確率)
深刻度スコア: 1.89 -> {'0': '軽微: 利用に支障はない', '1': '中度: 一部の機能に影響がある', '2': '重大: 主要機能が使用できない', '3': '致命的: サービス全体が停止している'}

「ログインできず決済も失敗している」という状態に対して、カテゴリはaccount(確信度0.47、technicalも0.47とほぼ同率で割れている)、緊急フラグは0.97とほぼ確実に緊急と判定、深刻度スコアは1.89で「中度〜重大」の間、という結果が得られました。カテゴリ判定が割れている点は、このチケットが本当にaccount/technicalどちらの問題とも言える内容だったことを考えると、むしろ妥当な「迷い」の表現だと言えます。

なお、Windowsのコンソールは既定の文字コードがUTF-8でない場合があるため、sys.stdout.reconfigure(encoding="utf-8")で明示的にUTF-8へ切り替えています。これを入れないと日本語部分が文字化けするので、Windows環境で試す場合は注意してください。

typesafe-sdk を利用する場合

TypeSafe社が配布しているtypesafe-sdk(Jevをクラウド経由で呼ぶための公式SDK)を、ローカルのtev1に対して使いいます。

サンプルコード

"""TypeSafe AI の System One モデル『Jev』を使った意思決定サンプル。"""

from __future__ import annotations

import sys

from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

if sys.stdout.encoding is not None and sys.stdout.encoding.lower() != "utf-8":
    sys.stdout.reconfigure(encoding="utf-8")


def classify_support_ticket(ticket_text: str) -> None:
    with TypeSafeClient() as client:  # model は省略時 "jev-latest"
        result = client.system_one(
            state=ticket_text,
            questions={
                "category": Choice(
                    instructions="このチケットはどのカテゴリに分類されますか?",
                    criteria={
                        "billing": "請求・支払いに関する問い合わせ",
                        "technical": "製品の不具合やエラーに関する問い合わせ",
                        "account": "アカウントやログインに関する問い合わせ",
                        "other": "それ以外の問い合わせ",
                    },
                ),
                "is_urgent": Noul(
                    instructions="このチケットは緊急対応が必要ですか?",
                    criteria={
                        "true": "サービス停止やデータ損失など、即時対応が必要な内容",
                        "false": "即時対応が不要な内容",
                    },
                ),
                "severity": Score(
                    instructions="このチケットの深刻度はどの程度ですか?",
                    criteria=[
                        "軽微: 利用に支障はない",
                        "中度: 一部の機能に影響がある",
                        "重大: 主要機能が使用できない",
                        "致命的: サービス全体が停止している",
                    ],
                ),
            },
        )

    category = result.choices["category"]
    is_urgent = result.nouls["is_urgent"]
    severity = result.scores["severity"]

    print(f"モデル: {result.model}")
    print(f"カテゴリ: {category.choice} (確信度 {category.confidence:.2f})")
    print(f"緊急フラグ: {is_urgent.noul:.2f} (真である確率)")
    print(f"深刻度スコア: {severity.score:.2f} (確信度 {severity.confidence:.2f})")

このコードをローカルのtev1に向けて実行するには、次の3つの環境変数を設定します。

set TYPESAFE_BASE_URL=http://localhost:11434
set TYPESAFE_API_KEY=dummy-local-key
set TYPESAFE_DEFAULT_MODEL=tev1
  • TYPESAFE_BASE_URL: 接続先をTypeSafeのクラウドAPIからローカルのOllamaに変更
  • TYPESAFE_API_KEY: クライアント生成時に必須のパラメータ。Ollamaはキーの内容を検証しないため、空でない適当な文字列でよい
  • TYPESAFE_DEFAULT_MODEL: 省略時はjev-latest(TypeSafeのクラウドモデル名)になってしまうため、明示的にtev1を指定

この状態で実行すると、ollama公式クライアント版と完全に同じ結果が得られます。

モデル: tev1
カテゴリ: account (確信度 0.47)
緊急フラグ: 0.97 (真である確率)
深刻度スコア: 1.89 (確信度 0.44)

ローカルのOllamaモデルを、クラウドサービス向けに作られたSDKでそのまま叩けるというのは地味に嬉しいポイントです。例えば開発中はtev1でコストをかけずに試作し、本番ではTYPESAFE_BASE_URL等の環境変数を外すだけでJevのクラウドAPIに切り替える、といった運用もできそうです。

過信してはいけないポイント

ここまで動かしてみると、Jev / tev1系のモデルは「速くて安い」という印象が強く残りますが、関連する解説記事や論文を読むと、過信すべきでない点がいくつか見えてきます。

confidenceは「正答率」ではない

confidenceとして返る値は「この判定が正しい確率」ではなく、あくまで確率分布の広がりを要約した値です。確信度が高くても、実際には誤っているケースはあります。

同じ入力でも結果が揺れる(非決定性)

同じstate・questionsを送っても、再実行すると確率値が微妙に変動します(例: 0.33→0.35など)。閾値判定でギリギリのケースを扱う場合は、この揺れを考慮したマージンが必要です。

質問は必ず一括送信する

10個の質問を1問ずつ逐次送信すると計6,110入力トークンかかったのに対し、1回のリクエストに全てまとめると773トークンまで圧縮でき、約90%のコスト削減、応答時間も約1/8に短縮されたという報告があります。この記事のサンプルコードも、3つの質問を1回のquestionsにまとめているのはこのためです。

「カスケード」は効く場面と効かない場面がある

Jevの確信度が低いときだけ、より強力なLLM(GPT-6など)にエスカレートする「カスケード戦略」を検証した論文では、テキストから読み取れる判定(選好判定、事実性チェックなど)では費用を25〜41%削減しつつ精度を維持できる一方、数学・コード・論理パズルのように導出が必要な判定ではJevの精度が大きく劣るため、カスケードの効果が薄いことが示されています。

さらに別の論文では、Jevと軽量LLM(GPT-5.6 Luna、Gemini 3.8 Flash、DeepSeek V4.1 Flash)をルーブリック評価者として比較した結果、設計が全く異なるにもかかわらず、同じ箇所で同じ間違いを犯す「相関エラー」が報告されています。Jevの最も確信度が高い誤り84件のうち、実に96.0%でLLM判定も同じ誤答を繰り返したとのことです。この相関エラーのため、「Jevが自信なさげなときだけLLMに任せる」という素朴なカスケード戦略は、期待したほどの精度向上をもたらさないケースがある、という点は導入前に知っておきたい知見です。

おわりに

Jev / System One Modelsは、「LLMにテキスト生成をさせるのではなく、型付きの小さな決定を高速・安価・確率付きで返す」という、チャット向けLLMとは異なる方向性を持つモデル層でした。そして、その思想を踏襲したモデル群がOllama上でも tev1・nimble・clef・clef-flash として既に利用可能で、/v1/systemone という共通APIのおかげで、TypeSafe社自身のSDKですら「接続先を変えるだけ」でローカルモデルに向けられる、という点が今回触ってみて一番の収穫でした。

一方で、「型が壊れない」ことと「判断がいつも正しい」ことは別問題である、という点は各記事・論文が共通して指摘している通りです。confidenceの意味を正しく理解し、質問は必ず一括送信し、そして「カスケードで精度を補えるはず」という期待も、相関エラーの存在を踏まえて検証してから本番導入する、という慎重さが必要そうです。

More Information