見出し画像

ChatGPTがデータ出力に対応

8月6日のChatGPTのアップデートで超強力な機能が開放されました。その名もStructured Output。構造化出力ってことですが具体的にはプログラムコードで使えるJSONで返答をスキーマ定義で正確に定義、指示出来るようになりました。

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

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

openai.com

どのくらい進歩したかというグラフがサイトにあります。なんと40%から100%へ飛躍。 たった1年で…しかも利用価格も値下げとなっています。

ということで、OpenAIのAPIを使っている方、躊躇していた方にはビッグニュースじゃないでしょうか。

1. 具体的なリクエスト例 tools で関数を使う方法

関数定義内で設定することにより、構造化出力をtools経由できますstrict: true。この機能は、gpt-4-0613およびgpt-3.5-turbo-0613以降のすべてのモデルを含む、ツールをサポートするすべてのモデルで動作します。構造化出力が有効になっている場合、モデル出力は指定されたツール定義と一致します。

(リクエスト例)

"model": "gpt-4o-2024-08-06", を指定することで新機能が使えます。

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
        }
      }
    }
  ]
}

(結果)

{
  "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. 具体的なリクエスト例 response_formatを使う方法

パラメータの新しいオプションである を介して JSON スキーマを指定できるようになりました。これは、モデルがツールを呼び出すのではなく、構造化された方法でユーザーに応答する場合に便利です。この機能は、最新の GPT-4o モデルである、本日リリースされた およびで動作します。 が とともに提供されると、モデル出力は提供されたスキーマと一致します。json_schemaresponse_formatgpt-4o-2024-08-06gpt-4o-mini-2024-07-18response_formatstrict: true

{
  "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
      }
    }
  }
}

(結果)

{
  "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."
}


安全性

開発を簡素化するために、refusalAPI 応答に新しい文字列値が追加され、開発者はモデルがスキーマに一致する出力ではなく拒否を生成したかどうかをプログラムで検出できるようになりました。応答に拒否が含まれず、モデルの応答が途中で中断されていない場合 ( で示されるfinish_reason)、モデルの応答は指定されたスキーマに一致する有効な JSON を確実に生成します。

(例)

{
2
  "id": "chatcmpl-9nYAG9LPNonX8DAyrkwYfemr3C8HC",
3
  "object": "chat.completion",
4
  "created": 1721596428,
5
  "model": "gpt-4o-2024-08-06",
6
  "choices": [
7
    {
8
      "index": 0,
9
      "message": {
10
        "role": "assistant",
11
        "refusal": "I'm sorry, I cannot assist with that request."
12
      },
13
      "logprobs": null,
14
      "finish_reason": "stop"
15
    }
16
  ],
17
  "usage": {
18
    "prompt_tokens": 81,
19
    "completion_tokens": 11,
20
    "total_tokens": 92
21
  },
22
  "system_fingerprint": "fp_3407719c7f"
23
}


更に凄い、ネイティブSDKサポート

Python および Node SDK が更新され、構造化出力のネイティブ サポートが追加されました。ツールのスキーマや応答形式を指定するのは、Pydantic または Zod オブジェクトを指定するのと同じくらい簡単です。SDK は、サポートされている JSON スキーマへのデータ型の変換、JSON 応答の型指定データ構造への逆シリアル化、拒否が発生した場合の解析などを行います。

python の例

素晴らしいと思ったのはpydantic が使われているところです。
Pydantic のBaseModelの継承をすることで手間なく、既存の広く使われる書き方で、スキーマを記述することが出来ます。

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)


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)


Pydanticとは

動的型付け言語であるPythonで、静的型付け言語のように型のチェックやデータ検証をし堅牢性をサポートするライブラリです。以前に記事にしたFastAPIも機能の多くをこのライブラリで実現しています。

Samuel Colvin が作成した Pydantic は、Python でのデータ検証とエラー処理に不可欠なツールです。ダウンロード数は 1,000 万回を超え、最新バージョンは 1.8.2 で、Web 開発や複雑なデータ処理に特に役立ち、データ構造が厳密に維持されることを保証します。


もっと詳しい解説

書こうと思ったら、すでにわかりやすい動画がありました。実際に手を動かして試せるように解説してくださっているので、実務で使いたい方にはおすすめです。





サポートありがとうございます😊 ベトナムにお越しの際はお声がけくださいね🌻