🤖

openai chat competion api function calling 概説

2023/06/20に公開

openai が chatGPT plugin に続いて function calling をサービスした。

https://openai.com/blog/function-calling-and-other-api-updates

ネーミングのせいで判りづらいが、この機能を「関数を実行する機能」だと思ってるとひどく混乱すると思われる。小生は混乱した。

ちなみに、apiの正式名称は "Chat Completion API" なので、"ChatGPT API"と呼ぶのは間違いである。

https://platform.openai.com/docs/guides/gpt/chat-completions-api

まあ伝わるけどね....

openai側の事情を忖度

  • openai chat completion api としては、自然言語の処理そのものしかやるつもりがない。
    • 例: 問い合わせにURLを書いてもそのサイトを読みに行くことはしない。できない。
    • 例: 何かファイルを作っても何処かにアップロードしたりはしない。できない。
  • 何か別のプログラムを動かしたいとすれば、openaiサービスの外で動かしてもらうしかない。
    • chatGPT plugin の場合は、外部サーバに投げるという手段を採っている。 OpenAI Platform

じゃあ openai chat completion apiを使う場合はどうしたらいいのか?

一旦処理を中断して、クライアント側に処理を持ち帰り、必要な情報を付加してから処理を続ける。

ということにしたらしい。

function_calling の利用方法

オフィシャルのドキュメントもサンプルソースもあるにはある。英語でよければだが。

https://platform.openai.com/docs/guides/gpt/function-calling

response = openai.ChatCompletion.create(...)

オフィシャルの前述のページではつぎのようなサンプルソースがあるが、これにfunctions関連が増える。

import openai

openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Who won the world series in 2020?"},
        {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
        {"role": "user", "content": "Where was it played?"}
    ]
)

次の通りの名前付き引数を与える。

  • "messages" 問い合わせ情報
  • "functions" 関数テーブル
  • "function_call" function_callを利用するか否か

ねんのためソースを確認してみたが、どうもこれそのままapiに渡しちゃってるようだ。

https://github.com/openai/openai-python/blob/main/openai/api_resources/abstract/engine_api_resource.py#L127-L161

関数テーブル List[dict]

次のdictで関数1個の仕様を示す。

  • "name" 関数名 英数字
  • "description" 説明。日本語で可。おそらくGPTがこの仕様を読解してるのだと思われる。
  • "parameters"
    • "type" functionの戻り型
    • "properties"
      • パラメタ名: 型情報 "type", "enum", "description"

問い合わせ情報 message List[dict]

次のdictを含む配列。

  • role: "user"
  • content: 問い合わせ内容テキスト

初回はユーザからの質問だけが入ってるはず。

response : dict

  • "id" 呼び出すたびに別の文字列が返ってくる。 GUIDっぽい。 課金情報と紐づいているのかもしれない
  • "created" 事実上はサーバ時刻
  • "usage" 処理トークン数が返ってくる。これの統計をとればおおまかな課金額がわかるはずだが、オープンソースでそれに留意してる例はあまり見ない
  • choices[] 本件の目玉

response.choices[]

配列の1要素につき次の情報がある。

  • ”finish_reason" 問い合わせに対する現在状態。
    • openaiのサンプルソースですらこの値を参照してないが、本来はチェックするべきだろう。
      • function calling利用時は "function_call"の文字列が返るので、後述するfunction callが必要。
      • "stop" は正常終了
  • "message"
    • "function_call"
      • "name" 呼び出してほしい関数名
        • create() の 関数テーブルの [].name に一致するはずである。
        • サンプルソースですら決め打ちで関数を読んじゃってるが、本来は関数テーブルを参照して呼び出すべき。
      • "arguments" 関数に渡してほしい引数のjson文字列
        • JSONをデコードした後は、関数テーブルの parameters.properties.keys() に一致するキーを含んでいるはずである。

これもサンプルソース等では choices[0] を決め打ちで使ってるが、配列なので全て処理する必要があるはずだ。

function 呼び出し

  • response.choice[].message.function_call.name に応じて処理を呼び出し、
  • その結果を 問い合わせ情報に list.append() して、
  • もう一度 openai.ChatCompletion.create を呼び出す。

オフィシャルのサンプルソースですら、2往復しか想定してないが、本来は毎回 choices[].finish_reason をチェックしつつ再リクエストを投げるべきだるろう。

Discussion