はじめに
少し前に、TypeSafe AIのjevが話題になりました。文章を生成せず、決められた型の答えと確信度だけを返すモデルで、LLMより桁違いに速くて安いとうたわれています。
同じ発想のAPIとして、OpenAIが2026年10月6日、Decisions APIを全開発者向けの公開ベータで提供開始しました。分類やスコアリングのような「決められた選択肢から選ぶ」処理に絞ったAPIです。
対応モデルはgpt-6-lunaのみ、料金は入力100万トークンあたり0.10ドルです。正式提供は「今後数週間」とされています。※2026年10月時点
これまで同じことをするなら、Responses APIのStructured Outputsでenumを指定するのが定番でした。そこで今回は、レシートの明細を家計簿のカテゴリに分ける題材で、Decisions APIとResponses APIを同じgpt-6-lunaで比べてみました。
Decisions APIとは
Decisions APIは、入力に対して「型の決まった答え」だけを返すAPIです。文章は生成しません。
質問の型は次の3つです。
| type | 答えの形 | 使いどころ |
|---|---|---|
predicate |
はい/いいえの確率(0〜1) | 条件に当てはまるかの判定(スパム判定、要対応かどうか、ポリシー違反の検出) |
choice |
選択肢のどれか1つ+各選択肢の確率 | カテゴリ分類、問い合わせの振り分け |
score |
段階評価の値+各段階の確率 | 程度の評価(緊急度、感情の強さ、品質や関連度の採点) |
答えには、選択肢ごとの確率とconfidence(確信度)が付いてきます。
料金は入力トークン分だけです。文章を生成しないので、出力トークンはそもそも発生しません。
画像も入力できます。jevはまだ画像に対応していないので、ここはDecisions APIの強みですね。ただし、画像はbase64のdata URLに限られ、URLやfile_idは使えません。
公式ドキュメントには「Responses APIより約10倍速い」と書かれています。
呼び出し方とレスポンス
準備
Decisions APIを使うには、openaiのSDKを新しいバージョンに上げる必要があります。JavaScript版は7.30.0以上です。
npm install openai@latest
カテゴリの定義
選択肢はDecisions APIとResponses APIの両方で使い回します。
export const CATEGORIES = [
{ value: "食費", description: "スーパーやコンビニで買った食材・飲み物・お菓子(持ち帰って食べるもの)" },
{ value: "外食", description: "飲食店での食事、カフェ、テイクアウト、フードデリバリー" },
{ value: "日用品", description: "洗剤・ティッシュ・ゴミ袋・電池などの消耗品" },
{ value: "交通", description: "電車・バス・タクシー・新幹線・駐車場・交通系ICのチャージ" },
{ value: "娯楽", description: "映画・本・ゲーム・カラオケなどの趣味や遊び" },
{ value: "医療・美容", description: "病院・薬・ドラッグストアの医薬品・美容院" },
{ value: "その他", description: "上のどれにも当てはまらないもの、中身がわからないもの" },
] as const;
export const INSTRUCTIONS = "レシートの明細1行(店名・品目・金額)を、家計簿のカテゴリに分類してください。";
呼び出し
呼び出しは、client.decisions.createにinputとquestionsを渡すだけです。
import OpenAI from "openai";
import { CATEGORIES, INSTRUCTIONS } from "./categories.ts";
const client = new OpenAI();
export async function classifyWithDecisions(text: string) {
const decision = await client.decisions.create({
model: "gpt-6-luna",
input: text,
questions: [
{
type: "choice",
name: "category",
instructions: INSTRUCTIONS,
choices: CATEGORIES.map(({ value, description }) => ({ value, description })),
},
],
});
const [answer] = decision.answers;
if (answer.type === "refusal") return { category: null, confidence: null, usage: decision.usage };
if (answer.type !== "choice") throw new Error(`unexpected answer type: ${answer.type}`);
return {
category: String(answer.choice),
confidence: answer.confidence,
probabilities: answer.probabilities,
usage: decision.usage,
};
}
answersはquestionsと同じ順番で返ってきます。モデルが答えを断った場合はtype: "refusal"が返るので、その分岐も入れておきます。
レスポンス
「めぐりズム完熟ゆず1枚 ¥165」を渡したときの実際のレスポンスです。
{
"model": "gpt-6-luna",
"answers": [
{
"type": "choice",
"name": "category",
"choice": "医療・美容",
"probabilities": [
{ "value": "食費", "probability": 0.01 },
{ "value": "外食", "probability": 0 },
{ "value": "日用品", "probability": 0.3 },
{ "value": "交通", "probability": 0 },
{ "value": "娯楽", "probability": 0.01 },
{ "value": "医療・美容", "probability": 0.49 },
{ "value": "その他", "probability": 0.19 }
],
"confidence": 0.41
}
],
"usage": {
"input_tokens": 336,
"input_tokens_details": { "cached_tokens": 0, "cache_write_tokens": 0 },
"output_tokens": 0,
"output_tokens_details": { "reasoning_tokens": 0 },
"total_tokens": 336
}
}
めぐりズムは蒸気で温めるアイマスクなので、「医療・美容」と「日用品」のどちらとも言えます。レスポンスを見ると、医療・美容0.49、日用品0.30と確率が割れていて、confidenceも0.41と低めです。モデルが迷っていることが数字でわかります。
output_tokensは0で、Decisions APIは文章を生成しないので出力トークンが発生せず、料金は入力の336トークン分だけです。
Structured Outputsとの違い
同じ分類をResponses APIのStructured Outputsで書くと、次のようになります。
import OpenAI from "openai";
import { CATEGORIES, INSTRUCTIONS } from "./categories.ts";
const client = new OpenAI();
const categoryList = CATEGORIES.map(({ value, description }) => `- ${value}: ${description}`).join("\n");
export async function classifyWithResponses(text: string, effort?: "none") {
const response = await client.responses.create({
model: "gpt-6-luna",
...(effort && { reasoning: { effort } }),
instructions: `${INSTRUCTIONS}\n\nカテゴリ:\n${categoryList}`,
input: text,
text: {
format: {
type: "json_schema",
name: "receipt_category",
strict: true,
schema: {
type: "object",
properties: {
category: { type: "string", enum: CATEGORIES.map(({ value }) => value) },
},
required: ["category"],
additionalProperties: false,
},
},
},
});
const { category } = JSON.parse(response.output_text) as { category: string };
return { category, usage: response.usage };
}
同じ明細を渡したときの結果はこうなりました。
{"category":"日用品"}
usage: input_tokens 159 / output_tokens 69(うち reasoning_tokens 53)
並べてみると、違いは次のとおりです。
| Decisions API | Responses API(Structured Outputs) | |
|---|---|---|
| 書くもの | 質問の型と選択肢 | JSON Schemaとプロンプト |
| 返ってくるもの | 答え+全選択肢の確率+confidence | JSONの文字列(答えだけ) |
| 迷っているかどうか | 確率とconfidenceでわかる | わからない |
| パース | 不要(型つきのオブジェクト) |
JSON.parseが必要 |
| 出力トークン | 発生しない | 生成した分だけ発生。推論トークンも含む |
| 自由な文章や抽出 | できない | できる |
Responses APIは、答えが「日用品」の一言だけで返ってきます。迷ったうえでの日用品なのか、自信を持って日用品なのかは、レスポンスからはわかりません。Decisions APIなら「confidenceが0.6未満なら人に確認してもらう」のような分岐をそのまま書けます。
逆に、品目名や金額を抜き出したい、理由を文章で説明させたいといった用途には、Decisions APIは使えません。公式ドキュメントでも、自分で決めたJSON Schemaでデータを生成したいときはStructured Outputsを使うように案内されています。
実測
条件
レシートの明細48件を、次の3パターンで1件ずつ分類しました。
- Decisions API(gpt-6-luna)
- Responses API(gpt-6-luna、
reasoning.effortはデフォルトのmedium) - Responses API(gpt-6-luna、
reasoning.effort: "none")
料金はusageのトークン数から計算しています(Responses APIは入力$0.10、出力$0.50 / 1M tokens)。
結果
| 正解数 | 中央値 | p95 | 1000件あたりの料金 | |
|---|---|---|---|---|
| Decisions API | 48/48 | 195ms | 280ms | $0.033 |
| Responses API | 47/48 | 1,600ms | 3,081ms | $0.053 |
| Responses API(effort: none) | 47/48 | 1,555ms | 2,783ms | $0.041 |
同じモデルで精度も同等のまま、Decisions APIは中央値で約8倍速く、料金は6割ほどでした。
Responses APIの推論をオフにすると、出力トークンは1,809から679に減りました。それでも中央値は45msしか縮んでいません。遅さの原因は推論より、文字を1つずつ生成する仕組みのほうにありそうです。
1回あたりの入力トークンはDecisions APIのほうが多い(336対159)ものの、出力トークンが発生しない分トータルでは安くなります。選択肢の説明が長いと、この差は縮むかもしれません。
Decisions APIが迷った品目
| confidence | 明細 | 確率 |
|---|---|---|
| 0.41 | ファミリーマート めぐりズム完熟ゆず1枚 ¥165 | 医療・美容0.49 / 日用品0.30 / その他0.19 |
| 0.70 | マツモトキヨシ カロリーメイト 4本 ¥238 | 食費0.74 / 医療・美容0.23 |
| 0.85 | カラオケ ○○ 飲食 投げ飲み放題アルコール ¥1,210 | 娯楽0.87 / 外食0.09 |
人が分類しても迷う明細ほど確率が割れています。confidenceが低いものだけユーザーに確認してもらう、といった使い方ができそうです。
まとめ
Decisions APIは、決められた選択肢から選ぶ処理に特化したAPIです。答えと一緒に確率とconfidenceが返ってきます。
一方で、文章の生成や値の抜き出しはできないので、Structured Outputsの代わりにはなりません。分類・振り分け・スコアリングならDecisions API、データを作るならStructured Outputsという使い分けになります。
分類やスコアリングのためだけにStructured Outputsを使っている人には、Decisions APIはかなりおすすめです!特に、レスポンスの遅さやコストが気になっている人や、「モデルがどれくらい自信を持って答えているのか」を知りたい人には合うと思います!
ゲームなどにも使えるかもしれませんね。
最後まで読んでいただき、ありがとうございました!
もし記事が参考になりましたら、いいねや共有をしていただけると嬉しいです🎉
株式会社サードスコープではAIを活用したシステムやサービスを開発しています。開発と導入支援の経験をもとに、現場での使い方まで踏まえてご提案します。
弊社では人材教育から実務支援まで幅広い業務に対応したAI中心設計による次世代のビジネスプラットフォームを提供しています。
参考