【OpenAI API】Structured Outputs の使い方を簡単に解説

【OpenAI API】Structured Outputs の使い方を簡単に解説

Anycloudのやましたです!

OpenAIが2024年8月6日に「Structured Outputs」という新機能を発表しました。

この機能は、開発者が提供したJSON Schemaに厳密に準拠したモデル出力を生成することを可能にします。

今回は、OpenAIの公式情報を参考に、この新機能について見ていこうと思います。

Structured Outputsの概要

OpenAIが新たに導入したStructured Outputsは、開発者が指定したJSON Schemaに完全に一致する出力を保証する機能です。これは、昨年のDevDayで発表されたJSON modeをさらに進化させたものになります。

公式の要点は以下の3点です。

  1. 開発者が提供したスキーマに100%準拠した出力を生成
  2. 新しいGPT-4oモデル(gpt-4o-2024-08-06)で利用可能
  3. 複雑なJSON schemaに対する理解と生成能力が大幅に向上

特に注目すべきは、複雑なJSON schemaへの対応です。OpenAIの新モデル「gpt-4o-2024-08-06」は、Structured Outputsを使用した場合、複雑なJSONスキーマの追従に関するテストで100%のスコアを出しています。

comparison-chart-of-models

上の図は各モデルにおけるStructured Outputsの性能を比較したものです。

gpt-4o-2024-08-06モデルはStructured Outputs (strict=true)を使用した場合、100%の正確性を達成しています。

使い方

実際にStructured Outputsをどのように使用するかを見ていきます。OpenAIは、Structured Outputsを2つの形式でAPIに導入しています。

  1. Function calling
  2. response_formatパラメータの新しいオプション

ここでは、Node.jsを使用して両方の方法を解説します。

1. Function callingを使用する方法

Function callingを使用する場合、 tools パラメータ内で strict: true を設定します。

import OpenAI from 'openai';

const client = new OpenAI();

const completion = await client.chat.completions.create({
    model: 'gpt-4o-2024-08-06',
    messages: [
        { role: 'system', content: 'You are a helpful assistant. The current date is August 6, 2024. You help users query for the data they are looking for by calling the query function.' },
        { role: 'user', content: 'look up all my orders in may of last year that were fulfilled but not delivered on time' }
    ],
    tools: [{
        type: 'function',
        function: {
            name: 'query',
            description: 'Execute a query.',
            strict: true,
            parameters: {
                type: 'object',
                properties: {
                    table_name: {
                        type: 'string',
                        enum: ['orders', 'customers', 'products']
                    },
                    columns: {
                        type: 'array',
                        items: {
                            type: 'string',
                            enum: ['id', 'status', 'expected_delivery_date', 'delivered_at', 'shipped_at', 'ordered_at', 'canceled_at']
                        }
                    },
                    conditions: {
                        type: 'array',
                        items: {
                            type: 'object',
                            properties: {
                                column: { type: 'string' },
                                operator: { 
                                    type: 'string',
                                    enum: ['=', '>', '<', '>=', '<=', '!=']
                                },
                                value: {
                                    anyOf: [
                                        { type: 'string' },
                                        { type: 'number' },
                                        {
                                            type: 'object',
                                            properties: {
                                                column_name: { type: 'string' }
                                            },
                                            required: ['column_name']
                                        }
                                    ]
                                }
                            },
                            required: ['column', 'operator', 'value']
                        }
                    },
                    order_by: {
                        type: 'string',
                        enum: ['asc', 'desc']
                    }
                },
                required: ['table_name', 'columns', 'conditions', 'order_by']
            }
        }
    }]
});

console.log(completion.choices[0].message.tool_calls[0].function.arguments);

2. response_formatパラメータを使用する方法

response_formatパラメータを使用する場合、json_schemaオプションを指定します

import OpenAI from 'openai';

const client = new OpenAI();

const completion = await client.chat.completions.create({
    model: 'gpt-4o-2024-08-06',
    messages: [
        {
            "role": "system",
            "content": "You are a helpful math tutor. Only use the schema for math responses.",
        },
        { "role": "user", "content": "solve 8x + 3 = 21" },
    ],
    response_format: {
        type: "json_schema",
        json_schema: {
            name: "math_response",
            strict: true,
            schema: {
                type: "object",
                properties: {
                    steps: {
                        type: "array",
                        items: {
                            type: "object",
                            properties: {
                                explanation: { type: "string" },
                                output: { type: "string" }
                            },
                            required: ["explanation", "output"]
                        }
                    },
                    final_answer: { type: "string" }
                },
                required: ["steps", "final_answer"]
            }
        }
    }
});

const message = completion.choices[0]?.message;
if (message?.content) {
    const parsedContent = JSON.parse(message.content);
    console.log(parsedContent.steps);
    console.log(parsedContent.final_answer);
} else {
    console.log("No valid response or refusal.");
}

※ Structured Outputsに対応しているのは限られたモデルです、対応されているモデルは以下になります

使用ケース

対応モデル

Function callingを使用する場合

  • gpt-4-0613以降のすべてのモデル
  • gpt-3.5-turbo-0613以降のすべてのモデル

response_formatを使用する場合

  • gpt-4o-2024-08-06
  • gpt-4o-mini-2024-07-18

何が嬉しいのか?

Structured Outputsの導入によって、開発者にとって嬉しい機能だと思います。

では具体的に何が嬉しいのか、メリットを紹介します。

開発者の生産性向上とアプリケーションの堅牢性向上

これが最大のメリットだと思います。これまでは、モデルに特定の形式で出力させるために、プロンプト内に[format]といったセクションを作成し、細かい指示を書く必要がありました。また、指定したフォーマットで確実に出力される保証がなく、たまに予期せぬ形式の回答が返ってくることがありました。

これに対し、Structured Outputsを使えば、そういった面倒で不確実な作業が不要になります。開発者はJSON Schemaを指定するだけで、モデルが確実にそのスキーマに従った出力を生成してくれるようになりました。

この機能により、出力の解析や後処理にかかる時間を削減できるだけでなく、予期せぬ形式の出力によるエラーやバグのリスクも大きく減少します。結果として、開発プロセス全体の効率化とアプリケーションの信頼性向上につながったように思えます。

幅広いユースケースへの対応

Structured Outputsは、様々な場面で活用できます。

  • データ抽出:非構造化データから特定の情報を抽出する際に便利です。
  • UI生成:ユーザーの意図に基づいて動的にUIを生成できます。
  • 構造化レスポンス:質問応答システムで、回答と推論過程を分離して出力できます。

例えば、会議の議事録から行動項目、期限、担当者を抽出したり、ユーザーの要求に基づいてWebサイトのレイアウトを動的に生成したりすることが可能になります。

これらの用途において、Structured Outputsは出力の一貫性と正確性を保証してくれるため、開発者は出力の後処理に頭を悩ませることなく、アプリケーションのコア機能の開発に集中できます。

このあたりの使い方は「Additional use cases」というパートに書かれているので気になる方は見てみてください。

https://openai.com/index/introducing-structured-outputs-in-the-api/

技術的詳細

Structured Outputsの裏側では、どんな技術が使われているのでしょうか?

OpenAIは、制約付きサンプリング(または制約付きデコーディング)と呼ばれる技術を採用しています。この方法では、モデルの出力を動的に制約し、指定されたスキーマに従った有効なトークンのみを生成します。

具体的には、提供されたJSON Schemaをコンテキストフリー文法(CFG)に変換し、サンプリング中に各トークンが生成された後、どのトークンが次に有効かを動的に決定します。これにより、無効なトークンの確率を実質的にゼロにし、常に有効なJSONを生成することができるのです。

この方法は、単純な正規表現や有限状態機械(FSM)を使用する他のアプローチと比較して、より複雑で入れ子になったデータ構造を扱うことができる利点があります。

おわりに

Structured Outputsは、OpenAI APIの利用者にとってとても嬉しい新機能です。

この機能により

  1. 開発者は複雑なJSON Schemaに準拠した出力を簡単に得られるようになりました
  2. アプリケーションの堅牢性が向上し、出力の後処理に関する心配が減りました
  3. データ抽出、UI生成、構造化レスポンスなど、幅広いユースケースに対応できるようになりました

ただし、いくつかの制限事項(対応モデルの制限、初回リクエスト時の追加レイテンシなど)があることにも注意が必要です。

皆さんも、ぜひStructured Outputsを試してみてください!

Anycloudではプロダクト開発の支援を行っています

プロダクト開発をお考えの方はぜひAnycloudにご相談ください。

まずは相談する

記事を書いた人

やました

PdM

やました

Twitter

株式会社AnycloudでPdMをしています