OpenAIのDecisions APIでテキストや画像を分類し、確率とスコアを取得する
目次
問い合わせの窓口振り分けや画像の傷判定などでは、自由な文章生成ではなく分類結果や確率値だけを直接受け取りたい場面が多い。
OpenAIのDecisions APIは、テキストや画像を入力し、あらかじめ指定した形式で判定結果を取得できるエンドポイントだ。
公式ガイドによると、従来のResponses APIと比べて約10倍高速に動作すると案内されている。
2026年10月13日時点ではパブリックベータで、対応モデルは gpt-6-luna のみ。送信先は専用の POST /v1/decisions となる。
判定の種類に応じた回答の型
通常のテキスト生成では、モデルが出力した文章から正規表現やパーサーで目的の値を取り出す手間がかかる。
Decisions APIではリクエスト側で回答の型を指定するため、レスポンス側もその型に応じたフィールドから直接値を取得できる。
| 質問の型 | 用途の例 | 返される主な値 |
|---|---|---|
predicate | 写真の商品に傷があるか | 条件が真となる推定確率 probability |
choice | 問い合わせをどの部署へ送るか | 用意した候補から選ばれた choice |
score | 不具合の深刻さを段階で評価する | 各段階の確率から計算した score |
部署名のように順序のない分類には choice、軽微・中程度・重大のように順序がある段階評価には score を使う。
predicate の戻り値は単なる真偽値ではなく0〜1の確率で返るため、どの数値を基準にフラグを立てるかはアプリ側の閾値設計となる。
任意のキーを持つJSONが必要な場合は、指定したスキーマに沿って出力させる Structured Outputs のほうが向いている。
問い合わせから注文番号や要望を抽出しつつ、返信文のドラフトも生成するような処理はこちらが担当する。
また、外部関数を呼び出す引数をモデルに作らせたいなら function calling を使う。
Decisions APIは判定結果を返すところまでを担うため、チケット移動などの具体的な後続処理はアプリ側で実行する。
Python SDKでの実装例
リクエストにはモデル名を指定する model、判定対象となるテキストや画像を入れる input、判定内容を定義する questions リストを指定する。
結果は answers リストとして返り、リクエスト時に各質問へ付与した name で結果を照合できる。複数の質問を同時に投げる場合は一意な name を割り当てておくと判別しやすい。形式の詳細は APIリファレンス に定義されている。
Python SDKはバージョン3.26.0以降が必要となる。環境変数 OPENAI_API_KEY を設定した上で、問い合わせを「アカウント」「契約」「その他」へ振り分ける実装例は次のようになる。
from openai import OpenAI
api = OpenAI()
result = api.decisions.create(
model="gpt-6-luna",
input="機種変更してから確認コードを受け取れず、ログインできません。",
questions=[{
"name": "support_queue",
"type": "choice",
"instructions": "この問い合わせを受け付ける窓口を選ぶ。",
"choices": [
{"value": "account", "description": "ログイン、認証、アカウント設定"},
{"value": "plan", "description": "プラン変更、契約、解約"},
{"value": "other", "description": "上記以外、または内容を判断できない相談"},
],
}],
)
for item in result.answers:
if item.type == "refusal":
print(item.name, "回答拒否")
elif item.type == "choice":
print(item.name, item.choice)
print("各候補の確率:", item.probabilities)
print("confidence:", item.confidence)
choices の value はプログラム側で処理しやすい識別子、description はモデルがその選択肢を選ぶための判断基準となる。
リファレンス上、選択肢は2〜255個の範囲で指定し、値の重複は不可。どの候補にも当てはまらないケースに備えて other のような受け皿を定義しておくのが安全だ。
なお、安全基準などに抵触して判定が行われなかった質問には、通常の判定値の代わりに type: "refusal" が格納される。同じリクエスト内に複数の質問がある場合でも、質問ごとに個別の型で結果が返ってくるため、ループ処理側で type を分岐してハンドリングする。
確率値・スコアの扱いと閾値設計
choice の回答には、選択された値に加えて各候補の推定確率 probabilities と、判定全体に対するモデルの確信度 confidence が含まれる。
各候補の確率の合計とは別に、モデル自身がその判定にどれだけ確信を持っているかが confidence で表される。ただし確信度が高いからといって判定が常に正しいとは限らないため、実際の運用では過去の問い合わせデータなど正解ラベル付きのサンプルで正答率を検証し、人による目視確認へ回す境界値を調整していく必要がある。
score は、設定した各段階の番号に確率を掛け合わせた期待値(加重平均)として算出される。番号は0始まりで指定する。
| 段階の番号 | 評価基準の例 | 想定確率 |
|---|---|---|
| 0 | 軽微(見た目のみの問題) | 0.2 |
| 1 | 中程度(操作に支障はあるが回避策がある) | 0.5 |
| 2 | 重大(操作不能で回避策もない) | 0.3 |
この設定例でのスコアは 0 × 0.2 + 1 × 0.5 + 2 × 0.3 = 1.1 となる。
これは最多得票の「段階1」が選ばれたという意味ではなく、段階全体の確率分布を数値化したものだ。同じ1.1でも確率の内訳が異なる場合があるため、判定ロジックを組む際は段階ごとの確率値もあわせて参照できる。
なお、同一の入力に対して複数の判定を行いたい場合は、1回のリクエスト内の questions に並べて同時に評価できる。
ただし「傷ありと判定された場合のみ修理カテゴリを判定する」といった前段の判定結果に依存する処理は、リクエストを分けて順次実行する構成となる。
画像入力時の指定方法と資料の食い違い
テキストと画像を同時に渡す場合は、リクエストの input にテキストと画像オブジェクトを並べて指定する。
公式ガイドでは、次のように画像を文字列化したBase64データURL(data:image/...)を埋め込む形式が案内されている。
[
{"input_text": "この商品写真に傷や破損があるか確認してください。"},
{"input_image": "data:image/jpeg;base64,..."}
]
一方で、Web上で公開されている画像URL(HTTP/HTTPS)の指定可否については、2026年10月13日時点で公式資料間に食い違いが見られる。
| 資料 | 画像URLに関する記述 |
|---|---|
| 公式ガイドの画像入力節 | Base64データURLが必須で、HTTP/HTTPS URLはサポート外 |
| APIリファレンス | Base64データURLと公開HTTP/HTTPS URLの両方に対応 |
現状どちらの仕様が有効かは資料上から判断できないため、安全側に倒すならBase64形式でエンコードして渡すのが無難だ。
なお、事前にファイルをアップロードして参照する file_id については、両資料とも非対応と明記されている。
入力100万トークンあたり0.10ドルの専用料金
公式ガイドの料金欄によると、Decisions APIで gpt-6-luna を利用する場合の料金は、入力100万トークンあたり0.10ドル($0.10)。
課金対象は入力トークンのみで、判定結果を出力するトークンやプロンプトキャッシュの読み書きには追加料金が発生しない。データ処理の地域指定(データレジデンシー)による割増料金や、一定以上のコンテキスト長に対する料金倍率は通常どおり適用される。
この料金は /v1/decisions エンドポイント専用の体系であり、通常のChat CompletionsやResponses APIで gpt-6-luna を呼び出す場合の価格設定とは異なる。
プロンプトや質問の定義による挙動の違いは、ブラウザ上の Decisions Playground でも試せる。