見出し画像

OpenAI API の Structured Outputs の使い方

以下の記事が面白かったので、簡単にまとめました。

Introducing Structured Outputs in the API


1. Structured Outputs

昨年のDevDayで、「JSONモード」を導入しました。これは、OpenAIのモデルを使用して信頼性の高いアプリを構築しようとしている開発者にとって便利な構成要素です。「JSONモード」は、有効なJSON出力を生成するためのモデルの信頼性を向上させますが、モデルの応答が特定のスキーマに準拠することを保証するものではありません。本日、APIに「Structured Outputs」を導入します。これは、モデルによって生成された出力が、開発者が提供するJSONスキーマと完全に一致するように設計された新機能です。

複雑なJSONスキーマのフォローの評価では、「Structured Outputs」を備えた新しいモデル「gpt-4o-2024-08-06」は完璧な100%のスコアを獲得しました。比較すると、「gpt-4-0613」のスコアは40%未満です。

2. Structured Outputs の使い方

APIでは、2つの形式で「Structured Outputs」を導入しています。

2-1. Function Calling

Tool経由の「Structured Outputs」は、function定義内で strict: true を設定することで利用できます。この機能は、「gpt-4-0613」「gpt-3.5-turbo-0613」以降のツールをサポートするすべてのモデルで動作します。「Structured Outputs」が有効になっている場合、モデル出力は指定されたTool定義と一致します。

・リクエスト

POST /v1/chat/completions
{
  "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"]
            },
            "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"],
                        "additionalProperties": false
                      }
                    ]
                  }
                },
                "required": ["column", "operator", "value"],
                "additionalProperties": false
              }
            },
            "order_by": {
              "type": "string",
              "enum": ["asc", "desc"]
            }
          },
          "required": ["table_name", "columns", "conditions", "order_by"],
          "additionalProperties": false
        }
      }
    }
  ]
}

・出力JSON

{
  "table_name": "orders",
  "columns": ["id", "status", "expected_delivery_date", "delivered_at"],
  "conditions": [
    {
      "column": "status",
      "operator": "=",
      "value": "fulfilled"
    },
    {
      "column": "ordered_at",
      "operator": ">=",
      "value": "2023-05-01"
    },
    {
      "column": "ordered_at",
      "operator": "<",
      "value": "2023-06-01"
    },
    {
      "column": "delivered_at",
      "operator": ">",
      "value": {
        "column_name": "expected_delivery_date"
      }
    }
  ],
  "order_by": "asc"
}

2-2. response_format

開発者は、「response_format」の新パラメータ「json_schema」を介して JSONスキーマを指定できるようになりました。これは、モデルがToolを呼び出すのではなく、構造化された方法でユーザーに応答する場合に便利です。この機能は、「gpt-4o-2024-08-06」「gpt-4o-mini-2024-07-18」で動作します。「response_format」が strict: true で指定されると、モデル出力は指定されたスキーマと一致します。

・リクエスト

POST /v1/chat/completions
{
  "model": "gpt-4o-2024-08-06",
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful math tutor."
    },
    {
      "role": "user",
      "content": "solve 8x + 31 = 2"
    }
  ],
  "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"],
              "additionalProperties": false
            }
          },
          "final_answer": {
            "type": "string"
          }
        },
        "required": ["steps", "final_answer"],
        "additionalProperties": false
      }
    }
  }
}

・出力JSON

{
  "steps": [
    {
      "explanation": "Subtract 31 from both sides to isolate the term with x.",
      "output": "8x + 31 - 31 = 2 - 31"
    },
    {
      "explanation": "This simplifies to 8x = -29.",
      "output": "8x = -29"
    },
    {
      "explanation": "Divide both sides by 8 to solve for x.",
      "output": "x = -29 / 8"
    }
  ],
  "final_answer": "x = -29 / 8"
}

3. 安全な Structured Outputs

「Structured Outputs」機能は、既存の安全性ポリシーに準拠し、モデルが安全でないリクエストを拒否できるようにします。開発を簡素化するために、API応答に新しい拒否文字列値が追加されました。これにより、開発者は、モデルがスキーマに一致する出力ではなく拒否を生成したかどうかをプログラムで検出できます。応答に拒否が含まれず、モデルの応答が途中で中断されていない場合 (finish_reason で示される)、モデルの応答は、指定されたスキーマに一致する有効な JSON を確実に生成します。

{
  "id": "chatcmpl-9nYAG9LPNonX8DAyrkwYfemr3C8HC",
  "object": "chat.completion",
  "created": 1721596428,
  "model": "gpt-4o-2024-08-06",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "refusal": "I'm sorry, I cannot assist with that request."
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 81,
    "completion_tokens": 11,
    "total_tokens": 92
  },
  "system_fingerprint": "fp_3407719c7f"
}

4. Native SDK サポート

OpenAIのPythonおよびNode SDKは、「Structured Outputs」のNativeサポートで更新されました。Toolのスキーマまたは応答形式としてスキーマを提供するのは、PydanticまたはZodを提供するのと同じくらい簡単です。OpenAIのSDKは、サポートされているJSONスキーマへのデータ型の変換、JSON応答の型指定データ構造への逆シリアル化、拒否が発生した場合の解析を処理します。

次の例は、Function Callingによる「Structured Outputs」のNativeサポートを示しています。

・Python

from enum import Enum
from typing import Union

from pydantic import BaseModel

import openai
from openai import OpenAI


class Table(str, Enum):
    orders = "orders"
    customers = "customers"
    products = "products"


class Column(str, Enum):
    id = "id"
    status = "status"
    expected_delivery_date = "expected_delivery_date"
    delivered_at = "delivered_at"
    shipped_at = "shipped_at"
    ordered_at = "ordered_at"
    canceled_at = "canceled_at"


class Operator(str, Enum):
    eq = "="
    gt = ">"
    lt = "<"
    le = "<="
    ge = ">="
    ne = "!="


class OrderBy(str, Enum):
    asc = "asc"
    desc = "desc"


class DynamicValue(BaseModel):
    column_name: str


class Condition(BaseModel):
    column: str
    operator: Operator
    value: Union[str, int, DynamicValue]


class Query(BaseModel):
    table_name: Table
    columns: list[Column]
    conditions: list[Condition]
    order_by: OrderBy


client = OpenAI()

completion = client.beta.chat.completions.parse(
    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=[
        openai.pydantic_function_tool(Query),
    ],
)

print(completion.choices[0].message.tool_calls[0].function.parsed_arguments)

Node

import OpenAI from 'openai';
import z from 'zod';
import { zodFunction } from 'openai/helpers/zod';

const Table = z.enum(['orders', 'customers', 'products']);
const Column = z.enum([
    'id',
    'status',
    'expected_delivery_date',
    'delivered_at',
    'shipped_at',
    'ordered_at',
    'canceled_at',
]);
const Operator = z.enum(['=', '>', '<', '<=', '>=', '!=']);
const OrderBy = z.enum(['asc', 'desc']);

const DynamicValue = z.object({
    column_name: z.string(),
});

const Condition = z.object({
    column: z.string(),
    operator: Operator,
    value: z.union([z.string(), z.number(), DynamicValue]),
});

const QueryArgs = z.object({
    table_name: Table,
    columns: z.array(Column),
    conditions: z.array(Condition),
    order_by: OrderBy,
});

const client = new OpenAI();

const completion = await client.beta.chat.completions.parse({
    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: [zodFunction({ name: 'query', parameters: QueryArgs })],
});
console.log(completion.choices[0].message.tool_calls[0].function.parsed_arguments);

Nativeサポートは、「response_format」でも利用できます。

・Python

from pydantic import BaseModel

from openai import OpenAI


class Step(BaseModel):
    explanation: str
    output: str


class MathResponse(BaseModel):
    steps: list[Step]
    final_answer: str


client = OpenAI()

completion = client.beta.chat.completions.parse(
    model="gpt-4o-2024-08-06",
    messages=[
        {"role": "system", "content": "You are a helpful math tutor."},
        {"role": "user", "content": "solve 8x + 31 = 2"},
    ],
    response_format=MathResponse,
)

message = completion.choices[0].message
if message.parsed:
    print(message.parsed.steps)
    print(message.parsed.final_answer)
else:
    print(message.refusal)

・Node

import OpenAI from 'openai';
import { zodResponseFormat } from 'openai/helpers/zod';
import { z } from 'zod';


const Step = z.object({
    explanation: z.string(),
    output: z.string(),
})

const MathResponse = z.object({
    steps: z.array(Step),
    final_answer: z.string(),
})


const client = new OpenAI();

const completion = await client.beta.chat.completions.parse({
    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: zodResponseFormat(MathResponse, 'mathResponse'),
});

const message = completion.choices[0]?.message;
if (message?.parsed) {
    console.log(message.parsed.steps);
    console.log(message.parsed.final_answer);
} else {
    console.log(message.refusal);
}

5. その他のユースケース

開発者は、さまざまなユースケースの構造化データを生成するために、OpenAIのモデルを頻繁に使用します。その他の例は、次のとおりです。

5-1. ユーザーの意図に基づいてユーザーインターフェースを動的に生成

開発者は「Structured Outputs」を使用して、コードまたはUIを生成するアプリを作成できます。次の例はすべて同じ response_format を使用しており、ユーザー入力に基づいてさまざまなUIを生成するために使用できます。

・System

You are a user interface assistant. Your job is to help users visualize their website and app ideas.

・Response format

{
  "type": "json_schema",
  "json_schema": {
    "name": "ui",
    "description": "Dynamically generated UI",
    "strict": true,
    "schema": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "description": "The type of the UI component",
          "enum": ["div", "button", "header", "section", "field", "form"]
        },
        "label": {
          "type": "string",
          "description": "The label of the UI component, used for buttons or form fields"
        },
        "children": {
          "type": "array",
          "description": "Nested UI components",
          "items": {
            "$ref": "#"
          }
        },
        "attributes": {
          "type": "array",
          "description": "Arbitrary attributes for the UI component, suitable for any element",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "description": "The name of the attribute, for example onClick or className"
              },
              "value": {
                "type": "string",
                "description": "The value of the attribute"
              }
            },
            "additionalProperties": false,
            "required": ["name", "value"]
          }
        }
      },
      "required": ["type", "label", "children", "attributes"],
      "additionalProperties": false
    }
  }
}

・Assistant
 - 庭師のためのランディングページ

 - アプリのサインアップ画面

"type": "form",
  "label": "Sign Up Form",
  "children": [
    {
      "type": "header",
      "label": "Sign Up for MovieReviews",
      "children": [],
      "attributes": [{ "name": "className", "value": "signup-header" }]
    },
    {
      "type": "field",
      "label": "Username",
      "attributes": [
        { "name": "type", "value": "text" },
        { "name": "placeholder", "value": "Enter your username" },
        { "name": "name", "value": "username" },
        { "name": "required", "value": "true" },
        { "name": "className", "value": "input-text" }
      ],
      "children": []
    },
    {
      "type": "field",
      "label": "Email",
      "attributes": [
        { "name": "type", "value": "email" },
        { "name": "placeholder", "value": "Enter your email" },
        { "name": "name", "value": "email" },
        { "name": "required", "value": "true" },
        { "name": "className", "value": "input-email" }
      ],
      "children": []
    },
    {
      "type": "field",
      "label": "Password",
      "attributes": [
        { "name": "type", "value": "password" },
        { "name": "placeholder", "value": "Create a password" },
        { "name": "name", "value": "password" },
        { "name": "required", "value": "true" },
        { "name": "className", "value": "input-password" }
      ],
      "children": []
    },
    {
      "type": "field",
      "label": "Confirm Password",
      "attributes": [
        { "name": "type", "value": "password" },
        { "name": "placeholder", "value": "Confirm your password" },
        { "name": "name", "value": "confirm_password" },
        { "name": "required", "value": "true" },
        { "name": "className", "value": "input-password-confirm" }
      ],
      "children": []
    },
    {
      "type": "button",
      "label": "Sign Up",
      "attributes": [
        { "name": "type", "value": "submit" },
        { "name": "className", "value": "submit-button" }
      ],
      "children": []
    }
  ],
  "attributes": [
    { "name": "action", "value": "/signup" },
    { "name": "method", "value": "POST" },
    { "name": "className", "value": "signup-form" }
  ]

 - 株価ウィジェット

"type": "div",
  "label": "",
  "children": [
    {
      "type": "header",
      "label": "Stock App",
      "children": [],
      "attributes": [
        {
          "name": "style",
          "value": "font-size: 24px; font-weight: bold; text-align: center; margin-bottom: 20px;"
        }
      ]
    },
    {
      "type": "div",
      "label": "",
      "children": [
        {
          "type": "div",
          "label": "Company Name: XYZ Corp",
          "children": [],
          "attributes": [
            {
              "name": "style",
              "value": "font-size: 20px; font-weight: bold; margin-bottom: 10px;"
            }
          ]
        },
        {
          "type": "div",
          "label": "Chart Area",
          "children": [],
          "attributes": [
            {
              "name": "style",
              "value": "width: 100%; height: 300px; border: 1px solid #ccc; margin-bottom: 20px;"
            }
          ]
        }
      ],
      "attributes": [
        {
          "name": "style",
          "value": "padding: 20px; border: 1px solid #ddd; margin-bottom: 20px;"
        }
      ]
    },
    {
      "type": "div",
      "label": "",
      "children": [
        {
          "type": "button",
          "label": "Refresh",
          "children": [],
          "attributes": [
            {
              "name": "style",
              "value": "margin-right: 10px; padding: 10px 20px; background-color: #4CAF50; color: white; border: none; font-size: 16px;"
            },
            { "name": "type", "value": "button" }
          ]
        },
        {
          "type": "button",
          "label": "Change Time Scale",
          "children": [],
          "attributes": [
            {
              "name": "style",
              "value": "margin-right: 10px; padding: 10px 20px; background-color: #2196F3; color: white; border: none; font-size: 16px;"
            },
            { "name": "type", "value": "button" }
          ]
        },
        {
          "type": "button",
          "label": "Export Data",
          "children": [],
          "attributes": [
            {
              "name": "style",
              "value": "padding: 10px 20px; background-color: #f44336; color: white; border: none; font-size: 16px;"
            },
            { "name": "type", "value": "button" }
          ]
        }
      ],
      "attributes": [{ "name": "style", "value": "text-align: center;" }]
    }
  ],
  "attributes": [
    {
      "name": "style",
      "value": "max-width: 600px; margin: 0 auto; font-family: Arial, sans-serif;"
    }
  ]
}

5-2. 最終的な回答を、それを支える理由や追加のコメントから分離

モデルに思考の連鎖のための別のフィールドを与えると、最終的な回答の品質を向上させるのに役立ちます。

・リクエスト

{
  "model": "gpt-4o-2024-08-06",
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant"
    },
    {
      "role": "user",
      "content": "9.11 and 9.9 -- which is bigger?"
    }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "reasoning_schema",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "reasoning_steps": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The reasoning steps leading to the final conclusion."
          },
          "answer": {
            "type": "string",
            "description": "The final answer, taking into account the reasoning steps."
          }
        },
        "required": ["reasoning_steps", "answer"],
        "additionalProperties": false
      }
    }
  }
}

・Structured Output

{
  "reasoning_steps": [
    "First step is to compare the numbers 9.11 and 9.9.",
    "Both numbers have the same whole number part, which is 9.",
    "To compare the decimal parts, convert them to the same number of decimal places.",
    "9.11 has two decimal places: it is 9.11.",
    "9.9 has one decimal place: it can be rewritten as 9.90.",
    "Now, compare 9.11 and 9.90 by looking at the decimal parts.",
    "Compare 11 with 90.",
    "90 is greater than 11, so 9.90 is greater than 9.11."
  ],
  "answer": "9.9 is bigger than 9.11."
}

5-3. 非構造化データから構造化データを抽出

たとえば、会議メモから ToDo、期限、割り当てなどを抽出するようにモデルに指示します。

・リクエスト

POST /v1/chat/completions
{
  "model": "gpt-4o-2024-08-06",
  "messages": [
    {
      "role": "system",
      "content": "Extract action items, due dates, and owners from meeting notes."
    },
    {
      "role": "user",
      "content": "...meeting notes go here..."
    }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "action_items",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "action_items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "description": {
                  "type": "string",
                  "description": "Description of the action item."
                },
                "due_date": {
                  "type": ["string", "null"],
                  "description": "Due date for the action item, can be null if not specified."
                },
                "owner": {
                  "type": ["string", "null"],
                  "description": "Owner responsible for the action item, can be null if not specified."
                }
              },
              "required": ["description", "due_date", "owner"],
              "additionalProperties": false
            },
            "description": "List of action items from the meeting."
          }
        },
        "required": ["action_items"],
        "additionalProperties": false
      }
    }
  }
}

・Structured Output

{
  "action_items": [
    {
      "description": "Collaborate on optimizing the path planning algorithm",
      "due_date": "2024-06-30",
      "owner": "Jason Li"
    },
    {
      "description": "Reach out to industry partners for additional datasets",
      "due_date": "2024-06-25",
      "owner": "Aisha Patel"
    },
    {
      "description": "Explore alternative LIDAR sensor configurations and report findings",
      "due_date": "2024-06-27",
      "owner": "Kevin Nguyen"
    },
    {
      "description": "Schedule extended stress tests for the integrated navigation system",
      "due_date": "2024-06-28",
      "owner": "Emily Chen"
    },
    {
      "description": "Retest the system after bug fixes and update the team",
      "due_date": "2024-07-01",
      "owner": "David Park"
    }
  ]
}

6. 内部構造

JSONスキーマに一致するモデル出力の信頼性を向上させるために、2つのアプローチを採用しました。まず、最新のモデル「gpt-4o-2024-08-06」を学習して、複雑なスキーマを理解し、それらに一致する出力を最適に生成する方法を学びました。ただし、モデルの動作は本質的に非決定論的です。このモデルの性能は向上しましたが (ベンチマークでは93%)、それでも開発者が堅牢なアプリを構築するために必要な信頼性を満たしていませんでした。そのため、モデルの出力を制限して100%の信頼性を実現する、決定論的なエンジニアリングベースのアプローチも採用しました。

6-1. 制約付きデコード

「制約付きサンプリング」または「制約付きデコード」と呼ばれる手法に基づいています。デフォルトでは、モデルをサンプリングして出力を生成する場合、モデルは完全に制約を受けず、次の出力として語彙から任意のトークンを選択できます。この柔軟性により、モデルは間違いを犯すことができます。たとえば、有効なJSONが生成されない場合でも、モデルはいつでも中括弧トークンを自由にサンプリングできます。有効な出力を強制するために、利用可能なすべてのトークンではなく、提供されたスキーマに従って有効なトークンのみにモデルを制約します。

有効なトークンはモデルの出力全体で異なるため、実際にこの制約を実装するのは難しい場合があります。次のスキーマがあるとします。

{
  "type": "object",
  "properties": {
    "value": { "type": "number" }
  },
  "required": ["value"],
  "additionalProperties": false
}

出力の先頭で有効なトークンには、{、{“、{\n などが含まれます。ただし、モデルが {“val をすでにサンプリングすると、{ は有効なトークンではなくなります。したがって、動的制約デコードを実装し、レスポンスの先頭で事前に判断するのではなく、各トークンが生成された後にどのトークンが有効であるかを判断する必要があります。

これを行うには、提供されたJSON スキーマを「CFG」(context-free grammar) に変換します。文法は言語を定義する一連のルールであり、「CFG」は特定のルールに準拠する文法です。JSONとJSONスキーマは、言語内で有効なものを定義するルールを持つ特定の言語と考えることができます。英語で動詞のない文は有効ではないのと同様に、JSONで末尾にコンマがあることは有効ではありません。

したがって、JSONスキーマごとに、そのスキーマを表す文法を計算し、そのコンポーネントを前処理して、モデルのサンプリング中に簡単にアクセスできるようにします。これが、新しいスキーマを使用した最初のリクエストでレイテンシ ペナルティが発生する理由です。サンプリング中に効率的に使用できるこのアーティファクトを生成するには、スキーマを前処理する必要があります。

サンプリング中、すべてのトークンの後に、推論エンジンは、以前に生成されたトークンと、次に有効なトークンを示す文法内のルールに基づいて、次に生成する有効なトークンを決定します。次に、このトークンのリストを使用して次のサンプリング ステップをマスクします。これにより、無効なトークンの確率が実質的に 0 に下がります。スキーマを前処理したので、キャッシュされたデータ構造を使用して、レイテンシ オーバーヘッドを最小限に抑えながら、これを効率的に実行できます。

6-2. 代替アプローチ

この問題に対する代替アプローチでは、制約付きデコードに「FSM」(finite state machines) または「regexes」 (通常は FSM で実装) を使用することがよくあります。これらは、各トークンが生成された後にどのトークンが有効かを動的に更新するという点で同様に機能しますが、CFGとはいくつかの重要な違いがあります。特に、CFGはFSMよりも幅広いクラスの言語を表現できます。実際には、これは上記の値スキーマのような非常に単純なスキーマでは問題になりません。ただし、ネストされたデータ構造や再帰的なデータ構造を含むより複雑なスキーマでは、違いが意味を持つことがわかります。たとえば、FSMは一般に再帰型を表現できないため、FSMベースのアプローチでは深くネストされたJSONの括弧を一致させるのに苦労する可能性があります。以下は、構造化出力を備えた「OpenAI API」でサポートされているが、FSMでは表現できないサンプルの再帰スキーマです。

{
  "name": "ui",
  "description": "Dynamically generated UI",
  "strict": true,
  "schema": {
    "type": "object",
    "properties": {
      "type": {
        "type": "string",
        "description": "The type of the UI component",
        "enum": ["div", "button", "header", "section", "field", "form"]
      },
      "label": {
        "type": "string",
        "description": "The label of the UI component, used for buttons or form fields"
      },
      "children": {
        "type": "array",
        "description": "Nested UI components",
        "items": {
          "$ref": "#"
        }
      },
      "attributes": {
        "type": "array",
        "description": "Arbitrary attributes for the UI component, suitable for any element",
        "items": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "description": "The name of the attribute, for example onClick or className"
            },
            "value": {
              "type": "string",
              "description": "The value of the attribute"
            }
          }
        }
      }
    },
    "required": ["type", "label", "children", "attributes"],
    "additionalProperties": false
  }
}

各UI 要素には、ルートスキーマを再帰的に参照する任意の子要素を含めることができることに注意してください。この柔軟性は、CFGアプローチによって実現されます。

7. 制限と制約

「Structured Outputs」を使用する際には、いくつかの制限事項に留意する必要があります。

・「Structured Outputs」では、ドキュメントで詳細が説明されているJSONスキーマのサブセットのみが許可されます。これにより、可能な限り最高のパフォーマンスを確保できます。

・新しいスキーマを使用した最初のAPI応答では追加のレイテンシが発生しますが、後続の応答はレイテンシのペナルティなしで高速になります。これは、最初のリクエスト中に、上記のようにスキーマを処理し、後で高速に再利用できるようにこれらの成果物をキャッシュするためです。一般的なスキーマは、最初のリクエストで処理するのに10秒未満かかりますが、より複雑なスキーマでは最大1分かかる場合があります。

・モデルが安全でないリクエストを拒否することを選択した場合、モデルはスキーマに従わない可能性があります。拒否することを選択した場合、返されるメッセージでは、これを示すために拒否ブール値がtrueに設定されます。

・生成が完了する前にmax_tokensまたは別の停止条件に達した場合、モデルはスキーマに従わない可能性があります。

・「Structured Outputs」では、あらゆる種類のモデルエラーを防ぐことはできません。たとえば、モデルはJSONオブジェクトの値内で依然として間違いを犯す可能性があります (たとえば、数式のステップを間違える)。開発者が間違いを見つけた場合は、システムの説明に例を提供するか、タスクをより単純なサブタスクに分割することをお勧めします。

・「Structured Outputs」は「Parallel Function Calling」と互換性がありません。「Parallel Function Calling」が生成されると、提供されたスキーマと一致しない場合があります。「Parallel Function Calling」を無効にするには、parallel_tool_calls: false を設定します。

・「Structured Outputs」で提供される JSONスキーマは、「Zero Data Retention」 (ZDR) の対象ではありません。

8. 可用性

「Structured Outputs」は、現在APIで一般公開されています。

「Functin Calling」の「Structured Outputs」は、APIで「Function Calling」をサポートするすべてのモデルで利用できます。gpt-4o、gpt-4o-mini、
gpt-4-0613、gpt-3.5-turbo-0613以降のすべてのモデル、および「Function Calling」をサポートするファインチューニングされたモデルが含まれます。この機能は、Chat Completion API、Assistant API、Batch API で利用できます。Vision入力とも互換性があります。

「response_format」の「Structured Outputs」は、「gpt-4o-mini」および「 gpt-4o-2024-08-06」と、これらのモデルに基づくファインチューニングで利用できます。この機能は、Chat Completion API、Assistant API、Batch API で利用できます。Vision入力とも互換性があります。

新しい 「gpt-4o-2024-08-06」に切り替えることで、開発者は「gpt-4o-2024-05-13」と比較して、入力を50% (2.50 ドル/100 万入力トークン)、出力を33% (10.00 ドル/100 万出力トークン) 節約できます。

「Structured Outputs」について詳しくは、ドキュメントを参照してください。



この記事が気に入ったらサポートをしてみませんか?