メインコンテンツへスキップ
見出し画像

Jev を触ってみた

    はじめに

    シルバーウィークに私も遅ればせながら Jev を触ってみました。
    本記事では私が調べたことや使ってみた所感を記載したいと思います。

    本記事は 2026 年 9 月 19 日時点の情報になります。

    Jev の概要

    Jev は TypeSafe AI が開発した「System One Model(システム・ワン・モデル)」と呼ぶ新カテゴリの最初の公開モデルです。
    ChatGPT や Claude のような LLM が「人間が読む文章」をトークン単位で自己回帰生成するのに対し、Jev は文章・コード・説明を一切生成せず、あらかじめ定義された型(選択肢・スコア・真偽)に対する較正済みの確率分布と、そこから導かれる確信度だけを1回の並列パスで返します。
    (これは実際にレスポンスを見ると分かりやすいと思います)

    The System One name comes from the concept Daniel Kahneman popularized in his book Thinking, Fast and Slow. System 1 thinking is fast and intuitive. System 2 is slower and more deliberate. Here, the emphasis is on fast, focused judgments.

    https://docs.typesafe.ai/concepts/system-one

    System One のモデル名の由来はダニエル・カーネマンのファストアンドスローから来ているようです。
    私も好きな本だったので、なるほどなと思いました。

    以下の情報は現時点のモデルである jev-1.13.0 の情報になります。

    3つのプリミティブ(質問型)

    • Choice: 定義した選択肢から1つ選ぶ(最大255個)

    • Score: 順序付きレベル(2〜10段階)で採点

    • Noul: Yes/No 判定(Yes の確率 0〜1)

    全プリミティブは1回の API 呼び出しで混在・並列評価でき、質問を増やしても応答時間はほとんど変わらないようです。

    Jev が向かないこと

    TypeSafe はモデルのバージョンごとに「jaggedness(ギザギザ)」という既知の弱点ページを公開しています。

    失敗モードが9つ、それぞれ「代わりにこうしろ」とセットで並んでいます。

    • 文字通りに読む…条件を正確に書く。境界ケースは criteria に入れる

    • 計算・カウント…算術はコードでやる

    • 日付・時刻の比較…要素を抽出させ、比較はコードで

    • 間接参照…ホップを減らし、state の該当箇所を名指しする

    • 無関係な情報が多い大きな state…先にフィルタし、質問に必要な分だけ送る

    • 敵対的な入力…criteria を明示的に。セキュリティ境界として扱わない

    • 矛盾した指示や基準…criteria や instruction を合わせる

    • 構造的な不変性…各決定に関して 1 つの方法で尋ね、コード内で同一性を確保する

    • 生成…生成モデルを使う

    最後の「生成」が、Jev の性格を一番よく表しています。
    公式の概要ページにはこう書かれています。

    System One models do not write replies, produce code, or generate explanations of their reasoning.

    https://docs.typesafe.ai/concepts/system-one

    返信も書かない、コードも生成しない、判断理由の説明も生成しない。
    つまり「なぜその判断になったのか説明して」も返ってきません。

    説明が必要な場面では、Jev で判断させて LLM に文章化させる二段構えになります。
    同じ概要ページの「Fast judgments inside a larger workflow」の節にも、 confidence があることで「いつ実行し、いつ人間または推論モデルに エスカレーションするかを決められる」と書かれており、併用が前提の設計です。

    個人的に一番刺さったのは「文字通りに読む」の項にあったこの一文です。

    When you look at a wrong answer and find yourself explaining what you
    really meant, that explanation is the missing half of the instruction.

    https://docs.typesafe.ai/model-jaggedness/jev-1.13

    間違った答えを見て「いや、本当はこういう意味で」と説明したくなったら、その説明こそが instructions に欠けている半分だ、と。
    プロンプトを書くときの指針として汎用的に使えると思います。

    料金体系

    • 入力トークン: $42 / Btok(= $0.042 / Mtok)

    • 出力トークン: 無料($0)

    1回のリクエスト内では、state は1度だけ読み込まれ全質問が並列評価されるため、質問が何個でも state のトークンは1回しか課金されないようです。
    ただし質問文・選択肢のトークンは加算されます(公式は "Extra questions still cost tokens." と明記)。
    別リクエストで同じ state を送り直す場合のキャッシュ割引は、特にドキュメントに記載がないようです。

    現時点だと、ユーザー登録直後に $5 分のクレジットが付与されています。
    個人で試す分には十分なクレジットだと思われます。

    他のモデルと比較したワークフロー評価サイトもあります。(TypeSafe 自身が設計・実施したベンチマーク)

    レート制限・仕様

    • レート制限:250,000 トークン/秒、1,200 リクエスト/分。「需要が非常に大きく上限は予告なく変わりうる」と明記。

    • コンテキスト:1リクエスト64kトークン(state+全質問の合計)。state+最長質問1個で32kトークン。

    • 入力:テキストのみ(文字列・JSONオブジェクト・テキスト配列)。画像・音声・動画は非対応。

    公式スキル

    Claude Code や Codex などのコーディングエージェントに、TypeSafe の文脈を渡すためのスキルが公式から出ています(ライセンスは MIT)。
    スキルの本体は「Jev を使ったコードを正しく書かせる」ことです。
    SKILL.md の中身を見ると、分量の大半は API の使い方、プリミティブの選び分け、state と criteria の設計方法、confidence の誤用の指摘に割かれています。
    そして、スキル自体は API 仕様を持っておらず、ライブドキュメントを読みに行く設計になっていました。
    `llms.txt` を起点に、必要なページだけ取りに行く設計です。

    Jev を提供している他のプロバイダー

    Legal

    Legal に関するドキュメントは 2026 年 9 月 19 日時点の情報になります。

    学習利用(トレーニング)

    Privacy Policy

    "Services" の節と "How We Use the Personal Data We Collect" の末尾に、二度にわたって以下のように記載されていました。

    We will not train or fine tune any artificial intelligence or machine learning models

    https://typesafe.ai/legal/privacy-policy

    (Input を学習・ファインチューニングに使わない。加えて「Input をサービスプロバイダ以外の第三者に開示しない」と併記)

    MCA (4.1) Use of Customer Data

    同じ趣旨の記載がありましたが、末尾に条件が付いていました。

    The foregoing license does not grant TypeSafe the right to, and TypeSafe will not, include Customer Data in a dataset used to train (i.e., to modify the model weights of) any artificial intelligence or machine learning models without Customer’s prior consent.

    https://typesafe.ai/legal/mca

    モデルの重みを変更する意味での学習データに Customer Data を含めない、ただし顧客の事前同意がない限り

    つまり Privacy Policy は無条件、MCA は同意ベースのオプトイン構造です。
    同意の取得方法は規定されていません。
    API 利用で優先されるのは MCA なので、機密データを扱う場合は問い合わせてみた方が良いかもしれません。

    Models

    Models ページにも以下の記載がありました。

    Jev is not fine-tuned or LoRA-adapted with customer data. It is trained with RLCD to return calibrated decisions, and the same weights serve every account.

    https://docs.typesafe.ai/models#customizing-jev

    データ保持期間

    • Privacy Policy の Retention 節:サービス提供や事業目的に合理的に必要な期間保持し、不要になれば削除するか個人を特定できない形にする(法定の保存義務がある場合を除く)。具体的な保持日数の記載はなし。

    • MCA §10.3:TypeSafe は Customer Data を保存・保持する義務を負わず、契約期間中も終了後もいつでも自由に削除できる。Confidential Information はバックアップに残りうるが守秘義務は継続。

    • ゼロデータリテンション(ZDR): ドキュメント上はエンタープライズ顧客向けに提供されている。

    保存リージョン

    米国。International Visitors の項目に、EEA・UK 等から利用する場合、個人データは米国に移転される旨が明記。

    Jev の OSS

    Jev 公開からすでに Jev 再現の OSS がリリースされています。
    例えば、 SemIf (旧 openjev) https://openjev.com/ がそうですが、今回は OSS の方には触れません。

    3 つのプリミティブ型の簡単なサンプル

    本記事では Python でコードを記載しますが、HTTP API と JavaScript SDK も存在します。

    $ pip install typesafe-sdk      # Python 3.10 以上
    $ export TYPESAFE_API_KEY="..." # console.typesafe.ai で取得

    Choice(選択肢から一つ選ぶ)

    from typesafe_sdk import Choice, TypeSafeClient
    
    client = TypeSafeClient()
    
    response = client.system_one(
        state="届いた商品が注文したものと違います。返品したいのですが手続きを教えてください。",
        questions={
            "department": Choice(
                instructions="この問い合わせはどのチームが対応すべきか",
                criteria={
                    "billing":   "請求・支払い・返金に関する問題",
                    "orders":    "注文状況・配送・キャンセル・返品に関する問題",
                    "account":   "ログイン・パスワード・アカウント設定に関する問題",
                },
            ),
        },
    )
    
    answer = response.answers["department"]
    print(answer.choice)        # 例: orders
    print(answer.probabilities) # 例: {'billing': 0.0, 'orders': 1.0, 'account': 0.0}
    print(answer.confidence)    # 例: 1.0

    リクエスト

    • state: 評価を依頼する内容。いわゆるコンテキストに近い。

    • questions.instructions: モデルに対する質問

    • questions.criteria: 選択肢

    レスポンス

    answers を主に利用すると思うので、 answers 以下の値を記載します。
    usage も取得できます。

    • choice: 最も確率の高い選択肢

    • probabilities: 各選択肢の確率。合計で 1 になる。

    • confidence: 信頼度。probabilities から機械的に計算される 0〜1 の値。

    `confidence` が低いときは自動処理せず人間レビューに回す運用が公式推奨です:

    if response.answers["department"].confidence < 0.3:
        route_to_human_review()

    Score(順序のある段階で採点)

    from typesafe_sdk import Score, TypeSafeClient
    
    client = TypeSafeClient()
    
    response = client.system_one(
        state="何度問い合わせても同じ定型文しか返ってこない。もう限界です。解約します。",
        questions={
            "frustration": Score(
                instructions="この顧客の不満の強さ",
                criteria=[
                    "冷静・中立",
                    "やや不満",
                    "明確に不満",
                    "強い怒り・解約や苦情に言及",
                ],
            ),
        },
    )
    
    answer = response.answers["frustration"]
    print(answer.score)          # 例: 3.0
    print(answer.legend)         # 例: {0: '冷静・中立', 1: 'やや不満', 2: '明確に不満', 3: '強い怒り・解約や苦情に言及'}
    print(answer.probabilities)  # 例: {0: 0.0, 1: 0.0, 2: 0.0, 3: 1.0}
    print(answer.confidence)     # 例: 1.0

    Choiceとの違い

    criteria は配列で、順序に意味があります(2〜10段階)。
    「冷静 → 怒り」のように一直線に並ぶなら Score、「請求 / 注文 / アカウント」のように並べても意味がないなら Choice が好ましいです。
    もうひとつ、score は各レベルの確率で重み付けされた期待値なので、 2.9 のような小数で返ることがあります。
    「レベル3寄りだがレベル2の要素も残っている」という中間状態が数値に出るわけです。
    上の例は判断が明確なケースなので 3.0 に振り切れていますが、判断が割れる入力では小数になります。
    カテゴリ分類では潰れてしまう情報です。

    Noul(Yes/No確率で返す)

    from typesafe_sdk import Noul, TypeSafeClient
    
    client = TypeSafeClient()
    
    response = client.system_one(
        state="【重要】アカウントが凍結されました。以下のリンクからパスワードを入力して解除してください。",
        questions={
            "is_phishing": Noul(
                instructions="このメッセージはフィッシングの疑いがあるか",
            ),
        },
    )
    
    answer = response.answers["is_phishing"]
    print(answer.noul)   # 例: 0.95  ← Yes である確率

    他の型との違い
    Noul で他の2つと違うのは、confidence フィールドが無いことです。
    返るのは noul という 0〜1 の確率ひとつだけで、確率そのものが確信度を兼ねているからのようです。

    まとめて投げることもできる

    from typesafe_sdk import Choice, Noul, Score, TypeSafeClient
    
    client = TypeSafeClient()
    
    text = "3日間ずっとStripe連携を試していますが失敗し続けています。売上を逃していて困っています。至急対応してください。"
    
    response = client.system_one(
        state=text,
        questions={
            "department": Choice(
                instructions="この問い合わせはどのチームが対応すべきか",
                criteria={
                    "billing":   "支払い・サブスクリプションの問題",
                    "technical": "バグや連携(インテグレーション)の問題",
                    "sales":     "価格やアカウントに関する質問",
                },
            ),
            "sentiment": Score(
                instructions="顧客がどれくらい不満・怒っているか",
                criteria=["冷静で中立", "不満だが丁寧", "非常に怒っている・強い言葉"],
            ),
            "is_urgent": Noul(
                instructions="このメッセージは緊急性・時間的切迫を伝えているか",
            ),
        },
    )
    
    print("部署:", response.answers["department"].choice) # 例: technical
    print("確率分布:", response.answers["department"].probabilities) # 例: {'billing': 0.16, 'technical': 0.83, 'sales': 0.01}
    print("感情スコア:", response.answers["sentiment"].score) # 例: 1.8
    print("緊急度(Yes確率):", response.answers["is_urgent"].noul) # 例: 0.98

    Advanced: structure

    「説明文を文字列で書かずに JSON で渡せる」ということだと理解しました。
    Jev は構造化された入力を理解するように訓練されているので、テンプレート文字列に詰め込むより JSON をそのまま渡した方が良いようです。

    1. 明確さが増すとき。質問が複数の部分から成るなら、JSON にすればキーにラベルが付くので曖昧さが減る。

    2. 補助データが必要なとき。スキーマ、分類体系、DB の行は元々 JSONである。それを文字列テンプレートにシリアライズせず、JSON のまま(または必要なサブフィールドだけ)渡す。

    実用度が高いのは Choice の選択肢を構造化するパターンです。
    `what`(何を扱うか)/ `not_for`(何を扱わないか)/ `examples` の3点セットで書きます。

    上の Choice の例を書き換えるとこうなります。

    criteria={
        "billing": {
            "what": "請求・支払い・返金・サブスクリプションに関する問題",
            "not_for": "配送状況やアカウントアクセスの問題",
            "examples": ["二重に請求された", "返金はまだですか"],
        },
        "orders": {
            "what": "注文状況・配送・キャンセル・返品に関する問題",
            "not_for": "請求やアカウントアクセスの問題",
            "examples": ["荷物はどこですか", "注文をキャンセルしたい"],
        },
        "account": {
            "what": "ログイン・パスワード・プロフィール・セキュリティの問題",
            "not_for": "請求や配送の問題",
            "examples": ["ログインできない", "メールアドレスを変更したい"],
        },
    }

    なお `what` / `not_for` / `examples` というフィールド名は API の仕様ではなく、自分で決めたものです。
    モデルは名前と値の両方を見るので、中身を表す短い名前を付ければ何でも良いようです。

    実際の例はドキュメントの方を参照してください。

    さいごに

    試しにリバーシの着手選択 AI を作ってみました。合法手の計算はコードでやり、
    「どの手を選ぶか」だけを Jev に投げる構成です。

    response = client.system_one(
        state={
            "turn": "black",
            "board": ["........", "........", "...b....", "bbbbb...",
                      ".wbbb...", "wwwwb...", "wwbb....", "w......."],
        },
        questions={
            "best_move": Choice(
                instructions="黒番として最善の一手を選べ。角の確保と機動力を重視すること。",
                criteria={
                    "a5": "2個裏返す。辺のマスで比較的安定しやすい",
                    "b8": "3個裏返す。ただし角の隣なので相手に角を渡す危険がある",
                    "e7": "1個裏返す。中央付近",
                },
            ),
        },
    )

    合法手は最大でも Choice の上限 255 個に余裕で収まるので、毎手ごとに合法手を列挙して criteria に流し込むだけで動きます。
    返ってくるのは必ず criteria のキーのいずれかなので、非合法手が返ってくる心配をしなくていいのが快適でした。

    体感の応答も速く、1手あたり1回の API 呼び出しで1局60手前後、コストは誤差の範囲です。かなり簡単に作れました。
    プログラムから構造化された選択肢の確率が必要な場面では、採用する選択肢になりそうです。


     
     
    Ruby や Typescript を主に書いています。最近は Python と Rust も好きです。

    あなたへのおすすめ