本文へ
TypeSafe AI ハンズオン
Jev · System One · 2026-09-20確認

テキストを読むAIから、判断を返すAIへ。

TypeSafe AIは判定材料(state)と型を指定した質問を渡し、コードが扱える構造化された答えを返します。Noul・Choice・Scoreを同じチケットで比べます。

公式 Introduction ↗
同じstateを三つの質問で評価 同じstate:決済に失敗 Noul 緊急対応が必要か? noul 0.91 Choice 担当は? 分布 + confidence Score 深刻度は? score 1.3 / 2 illustrative mock 横にスクロールできます

答えの型から質問を選ぶ

各質問は同じstateに対して独立に評価されます。組み合わせはアプリ側で行います。

Noul|真偽

statementがtrueか。noul(0〜1)。0.5は中間技能ではなく不確実さ。

Choice|候補

固定候補から確率が最大の候補を返します。choice、全分布、confidence。

Score|尺度

順序づけた2〜10段階。scoreは確率加重平均。

問い 返る値 目印
Noul noul 0.5は不確実
Choice choice + probabilities + confidence top確率とconfidenceを分ける
Score score + probabilities + confidence 説明レベルは2〜10
confidenceの読み方
confidenceは分布の集中度から得るAPI提供の要約値です。最大確率や正答率と同一ではなく、高い値も正しさの保証ではありません。

Noul:Yesの確率

「このstatementはtrueか?」を尋ねます。別個のconfidenceはありません。

from typesafe_sdk import Noul, TypeSafeClient

with TypeSafeClient() as client:
    result = client.system_one(
        state="返金を求めている。",
        questions={"is_refund": Noul(instructions="この文は返金を求めているか?")},
    )
print(result.nouls["is_refund"].noul)

Choice:候補と分布

choiceは確率が最大の候補です。全候補とconfidenceを読み、top確率と混同しません。

from typesafe_sdk import Choice, TypeSafeClient

with TypeSafeClient() as client:
    result = client.system_one(
        state="二重請求のようだ。",
        questions={"route": Choice(instructions="どの部署が担当?", criteria={"billing":"請求・支払い", "technical":"バグ・連携", "sales":"価格・契約"})},
    )
answer = result.choices["route"]
print(answer.choice)
print(answer.probabilities)
print(answer.confidence)

Score:尺度上の位置

2〜10個の説明レベル。番号は配列位置(0始まり)、scoreは確率加重平均です。

from typesafe_sdk import Score, TypeSafeClient

with TypeSafeClient() as client:
    result = client.system_one(
        state="Safariで画面がクラッシュする。",
        questions={"severity": Score(instructions="深刻度は?", criteria=["機能に影響なし", "壊れるが回避策あり", "回避策なくブロック"])},
    )
answer = result.scores["severity"]
print(answer.score)
print(answer.probabilities)
print(answer.legend)
score 1.0でも、level1に集中した場合とlevel0/2に割れた場合があります。分布とconfidenceを併読します。

モック実験:分布を動かす

APIを呼ばない固定ルールの学習用UIです。値は実モデルの推論ではありません。

Noul:閾値

threshold 0.80

noul 0.50 → 要確認

Noul確率と閾値● p=.50(固定) / 縦線:しきい値0 yesでない1 yes

Choice:分布

billing .84 / technical .15 / sales .01 · choice=billing · confidenceは分布の集中度から得る要約値で、最大確率や正答率と同一ではありません。高い値も正しさの保証ではありません。

Choice候補の確率分布billing.84technical.15sales.01

Score:平均は同じでも分布が違う

[0,1,0] → score 1.0(level1に集中)

Score分布と期待値0120×0 + 1×1 + 2×0 = 1.0

HANDSON / 01〜04

3つの問いを、同じチケットに投げてみる

このハンズオンでは「二重請求です。今日中に返金してください。」という1件のサポートチケットを、返金の有無・担当チーム・緊急度の3方向から判定します。TypeSafe は自由文の回答を解析するのではなく、質問ごとに型付きの結果を返します。

まずオフラインで確認

offline_primitives.py をダウンロードして、APIキーなしで実行できます。このファイルは TypeSafe の代替推論器ではなく、3つの回答の形と分岐を固定データで練習する教材です。

新しい作業フォルダを作り、ダウンロードしたファイルをその中の downloads/ に保存して進めます。requirements.txt を保存してください。

手順1: オフライン例を動かす

python3 downloads/offline_primitives.py

期待される標準出力は次のとおりです。

Noul (yes probability): 0.95
Choice (route): billing
Choice probabilities: {'billing': 0.9, 'technical': 0.05, 'other': 0.05}
Score (urgency expected level): 1.87 / 2.00
Score probabilities: {0: 0.03, 1: 0.07, 2: 0.9}
offline routing: billing queue

ここで Score の 1.87 は、レベル番号と確率の積の合計です。固定した例示分布を使った値であり、実 API の推論結果ではありません。

手順2: 公式 Python SDK を準備する

Python 3.10 以上を用意し、配布された requirements.txt(typesafe-sdk==0.7.0)を使います。

python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r downloads/requirements.txt

APIキーは TypeSafe Console で取得します。ターミナル入力を非表示にして環境変数へ渡すと、キーをコマンド履歴に書きません。

read -s TYPESAFE_API_KEY
export TYPESAFE_API_KEY
printf '\n'

SDK はキーを読み、既定モデル jev-latest と https://api.typesafe.ai を使います。実行するコードは live_sdk_example.py として保存されています。

手順3: 完全なライブコード

"""TypeSafe AI の実 API 用コード例。

実行には `pip install -r ../requirements.txt` と
`TYPESAFE_API_KEY` が必要。教材のオフライン確認では実行しない。
"""

from typesafe_sdk import Choice, Noul, Score, TypeSafeAPIError, TypeSafeClient


def choose_action(result) -> str:
    """回答を組み合わせる純粋な分岐。外部への副作用は持たない。"""
    refund = result.nouls["is_refund"].noul >= 0.8
    route = result.choices["route"]
    urgent = result.scores["urgency"].score >= 1.5
    enough_confidence = route.confidence >= 0.8 and result.scores["urgency"].confidence >= 0.6
    if refund and route.choice == "billing" and urgent and enough_confidence:
        return "billing:urgent-refund-review"
    if route.confidence < 0.5 or result.scores["urgency"].confidence < 0.5:
        return "human-review:uncertain"
    return f"queue:{route.choice}"


def classify_ticket(ticket: str) -> None:
    questions = {
        "is_refund": Noul(instructions="Does `ticket` request a refund?"),
        "route": Choice(
            instructions="Which team should handle `ticket`?",
            criteria={
                "billing": "Payment, charge, or refund issues.",
                "technical": "A software bug or integration failure.",
                "other": "Anything outside billing and technical support.",
            },
        ),
        "urgency": Score(
            instructions="How urgent is `ticket`?",
            criteria=[
                "Can wait; no meaningful near-term impact.",
                "Should be handled this week; workaround or limited impact exists.",
                "Needs attention today; time-sensitive or blocking impact.",
            ],
        ),
    }
    try:
        with TypeSafeClient() as client:
            result = client.system_one(state={"ticket": ticket}, questions=questions)
    except TypeSafeAPIError as error:
        # status/request_id は運用ログに使える。API キーや本文を出力しない。
        print(f"TypeSafe API failed: status={error.status}, request_id={error.request_id}")
        raise

    print("refund probability:", result.nouls["is_refund"].noul)
    print("route:", result.choices["route"].choice)
    print("urgency score:", result.scores["urgency"].score)
    print("urgency confidence:", result.scores["urgency"].confidence)
    print("suggested action:", choose_action(result))


if __name__ == "__main__":
    classify_ticket("二重請求です。今日中に返金してください。")
python downloads/live_sdk_example.py

成功時の数値は入力文・モデル・サービス状態で変わるため、固定の出力を正解として扱いません。見るべき場所は、Noul の result.nouls["is_refund"].noul、Choice の result.choices["route"].choice、Score の result.scores["urgency"].score です。

手順4: 結果を安全に分岐する

choose_action は「返金らしい」「billing が選ばれた」「緊急度が 1.5 以上」「Choice/Score の confidence も十分」という条件をすべて満たしたときだけ billing:urgent-refund-review を返します。これは返金処理を実行する関数ではなく、後続の人手レビューへ渡すラベルです。しきい値は公式の正解値ではなく、業務リスクに合わせて検証・調整するアプリ側の判断です。confidence が低いときは推測せず human-review:uncertain へ送ります。

失敗したときの読み方

3つの演習

  1. Noul: is_refund の instructions を「Does ticket mention a duplicate charge?」へ変え、オフライン例の固定ルールも同じ意味へ変える。請求以外のチケットで noul < 0.2 になる条件をテストに追加する。
    解答: Noul は「強さ」ではなく yes の確率なので、返金・二重請求の判定と緊急度を一つの問いへ混ぜない。
  2. Choice: technical の説明に「API integration, app crash, or broken button」を追加し、other を残したまま配送問い合わせを入力する。
    解答: 順序のないカテゴリなので Choice を使い、候補外を吸収する other を設ける。最大255候補までで、候補の説明は互いに区別できるように書く。
  3. Score: urgency の3レベルを日本語の「待てる / 今週 / 今日」に置き換え、確率 {0: 0.1, 1: 0.2, 2: 0.7} を紙で計算する。
    解答: 0×0.1 + 1×0.2 + 2×0.7 = 1.6。Score は確率加重平均で、confidence は分布の確信度を表す別の値なので、1.6 を confidence と呼ばない。

参照: Introduction、Primitives、Score、Choice、Python SDK、Python SDK Usage、PyPI 0.7.0(確認日 2026-09-20)。