🐱

GPT-4, GPT-3.5 の API を利用して JSON だけ生成する (Function calling)

2023/06/15に公開

先日 OpenAI API のアップデートが以下のように発表されました。
https://openai.com/blog/function-calling-and-other-api-updates

一番の注目は Function calling じゃないでしょうか?
名前から「関数を呼び出せるのかな?」と一番最初に想像しちゃいますが、この機能の革新的な部分は「JSON を作成してくれる」ところだと思っています。

JSON を作れると嬉しいことがいっぱいありますね!

  • ダミーデータの作成
  • 生成した JSON をそのままレスポンスとして返すエンドポイントの作成
    • テストとしても良い
  • 関数や外部 API の呼び出し

今までは難しかった JSON の作成

今まで、JSON を作成してもらうにあたって gpt-3.5-turbo を用いて JSON を作成させようとするとほぼ失敗していました。それで gpt-4 のモデルを利用して JSON を作成させますが、このモデルはほとんど正確になる一方で処理速度がかなり遅くなる点がネックでした。あと高い!

これまでどのようにしていたか見てみましょう。

system_prompt = """
API Documentation:

'''
The API endpoint `/v1/forecast` accepts a geographical coordinate, a list of weather variables and responds with a JSON hourly weather forecast for 7 days. Time always starts at 0:00 today and contains 168 hours. All parameters are listed below:

Parameter	Format	Required	Default	Description
latitude, longitude	Floating point	Yes		Geographical WGS84 coordinate of the location
hourly	String array	No		A list of weather variables which should be returned. Values can be comma separated, or multiple &hourly= parameter in the URL can be used.
temperature_unit	String	No	celsius	If fahrenheit is set, all temperature values are converted to Fahrenheit.
windspeed_unit	String	No	kmh	Other wind speed speed units: ms, mph and kn
precipitation_unit	String	No	mm	Other precipitation amount units: inch
timezone	String	No	GMT	If timezone is set, all timestamps are returned as local-time and data is returned starting at 00:00 local-time. Any time zone name from the time zone database is supported. If auto is set as a time zone, the coordinates will be automatically resolved to the local time zone.
past_days	Integer (0-2)	No	0	If past_days is set, yesterday or the day before yesterday data are also returned.
start_date, end_date	String (e.g. 2022-06-30)	No		The time interval to get weather data. A day must be specified as an ISO8601 date (e.g. 2022-06-30).

Hourly Parameter Definition
The parameter `hourly` accepts the following values. Most weather variables are given as an instantaneous value for the indicated hour. Some variables like precipitation are calculated from the preceding hour as an average or sum.

Variable	Valid time	Unit	Description
temperature_2m	Instant	°C (°F)	Air temperature at 2 meters above ground
snowfall	Preceding hour sum	cm (inch)	Snowfall amount of the preceding hour in centimeters. For the water equivalent in millimeter, divide by 7. E.g. 7 cm snow = 10 mm precipitation water equivalent
rain	Preceding hour sum	mm (inch)	Rain from large scale weather systems of the preceding hour in millimeter
showers	Preceding hour sum	mm (inch)	Showers from convective precipitation in millimeters from the preceding hour
weathercode	Instant	WMO code	Weather condition as a numeric code. Follow WMO weather interpretation codes. See table below for details.
snow_depth	Instant	meters	Snow depth on the ground
freezinglevel_height	Instant	meters	Altitude above sea level of the 0°C level
visibility	Instant	meters	Viewing distance in meters. Influenced by low clouds, humidity and aerosols. Maximum visibility is approximately 24 km.
'''
"""

# for i in range(3): みたいにループで囲みリトライする
try:
  response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=[
      {"role": "system", "content": system_prompt},
      {"role": "user", "content": '沖縄の 2023-06-15 の天気は? API ドキュメントを基にこのような JSON を作成してください: `{"start_date":"2023-06-15","end_date":"2023-06-15","latitude":26.2124,"longitude":127.6809}`'}
    ]
  )
  result = json.loads(response.choices[0].message.content)
  # ここで更にオブジェクトのバリデーションを行う
  return result
except:
  print("error", response.choices[0].message.content)

このコードでは API ドキュメントの仕様に沿った JSON を作らせようとしています。

システムプロンプトでは API ドキュメントを見せます。パラメータは文字列だったり、数値だったり、enum 値だったり色々指定する必要があるため、ここではそれらの説明をシステムプロンプトの中で行なっています。

ユーザープロンプトでは例を見せながら、どのように JSON を生成して欲しいか指示しています。

gpt-3.5-turbo だとほぼ JSON のみを出力してくれることはありません。以下は生成例です。

以下が沖縄の2023年6月15日の天気予報データを取得するためのJSONです。

"""
{
  "start_date": "2023-06-15",
  "end_date": "2023-06-15",
  "latitude": 26.2124,
  "longitude": 127.6809,
  "hourly": ["temperature_2m", "snowfall", "rain", "showers", "weathercode", "snow_depth", "freezinglevel_height", "visibility"],
  "temperature_unit": "celsius",
  "windspeed_unit": "kmh",
  "precipitation_unit": "mm",
  "timezone": "Asia/Tokyo"
}
"""

上記のJSONをAPIエンドポイント `/v1/forecast` に送信することで、沖縄の2023年6月15日の天気予報を取得することができます。

今後の JSON 生成

Function calling を利用すると JSON の生成を保証できます。[1]

前節で利用した API ドキュメントの形を JSON Schema へ変換し、API を呼び出してみます。

open_meteo_schema = {
  "type": "object",
  "properties": {
    "hourly": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "temperature_2m",
          "relativehumidity_2m",
          "dewpoint_2m",
          "apparent_temperature",
          "pressure_msl",
          "cloudcover",
          "cloudcover_low",
          "cloudcover_mid",
          "cloudcover_high",
          "windspeed_10m",
          "windspeed_80m",
          "windspeed_120m",
          "windspeed_180m",
          "winddirection_10m",
          "winddirection_80m",
          "winddirection_120m",
          "winddirection_180m",
          "windgusts_10m",
          "shortwave_radiation",
          "direct_radiation",
          "direct_normal_irradiance",
          "diffuse_radiation",
          "vapor_pressure_deficit",
          "evapotranspiration",
          "precipitation",
          "weathercode",
          "snow_height",
          "freezinglevel_height",
          "soil_temperature_0cm",
          "soil_temperature_6cm",
          "soil_temperature_18cm",
          "soil_temperature_54cm",
          "soil_moisture_0_1cm",
          "soil_moisture_1_3cm",
          "soil_moisture_3_9cm",
          "soil_moisture_9_27cm",
          "soil_moisture_27_81cm"
        ]
      }
    },
    "latitude": {
      "type": "number",
      "format": "float",
      "description": "WGS84 coordinate",
    },
    "longitude": {
      "type": "number",
      "format": "float",
      "description": "WGS84 coordinate",
    },
    "start_date": {
      "type": "string",
      "description": "The time interval to get weather data. A day must be specified as an ISO8601 date (e.g. 2022-06-30)."
    },
    "end_date": {
      "type": "string",
      "description": "The time interval to get weather data. A day must be specified as an ISO8601 date (e.g. 2022-06-30)."
    },
    "temperature_unit": {
      "type": "string",
      "default": "celsius",
      "enum": [
        "celsius",
        "fahrenheit"
      ]
    },
    "windspeed_unit": {
      "type": "string",
      "default": "kmh",
      "enum": [
        "kmh",
        "ms",
        "mph",
        "kn"
      ]
    },
    "timeformat": {
      "type": "string",
      "default": "iso8601",
      "enum": [
        "iso8601",
        "unixtime"
      ],
      "description": "If format `unixtime` is selected, all time values are returned in UNIX epoch time in seconds. Please not that all time is then in GMT+0! For daily values with unix timestamp, please apply `utc_offset_seconds` again to get the correct date."
    },
    "timezone": {
      "type": "string",
      "description": "If `timezone` is set, all timestamps are returned as local-time and data is returned starting at 0:00 local-time. Any time zone name from the [time zone database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) is supported."
    },
    "past_days": {
      "type": "integer",
      "enum": [
        1,
        2
      ],
      "description": "If `past_days` is set, yesterdays or the day before yesterdays data are also returned."
    }
  },
  "required": ["latitude", "longitude"]
}

response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "沖縄の 2023-06-15 の天気は?"}
        ],
        functions=[
            {"name": "set_definition", "parameters": open_meteo_schema}
        ],
        function_call={"name": "set_definition"}, # 必ず Function calling を生成するように実行する
    )

try:
  result = json.loads(response.choices[0].message.function_call.arguments)
  return result
except:
  print("error")
  pprint(response.choices[0].message.function_call.arguments)

実行結果としてこのように出力されます。

{
  "start_date": "2023-06-15",
  "end_date": "2023-06-15",
  "latitude": 26.2124,
  "longitude": 127.6809
}

可能性

AI の力を使って JSON を生成できるようになったことで、これまでよりも大きく可能性が広がったのではないのでしょうか!

Native JSON Output From GPT-4 の内容も面白かったので是非読んでみてください!

脚注
  1. たまに JSON のデコードで失敗するっぽいので、例外処理は置いてた方がいいかもしれないです!100回くらい試しましたが1回だけデコードエラーになりました。 ↩︎

Discussion