SFの世界を実現するFunction Callingを丁寧に解説!【ChatGPT神アプデ】

OpenAI のAPIがアップデートし大盛りあがりですね。Function Callingという機能が追加されて技術者のメンバーは嬉しいみたいですが、私はすぐに理解できませんでした。
何が嬉しいのか、そして、そもそもFunction Calling とは何者なのかもわからない。
ということで、Function Callingについて、難しい言葉を使わないで、易しく解説していきたいと思います。
公開されているプログラムにコメントもりもりで解説するので、ぜひ最後までお読みください!

なお弊社では、生成AIツール開発についての無料相談を承っています。こちらからお気軽にご相談ください。
→無料相談で話を聞いてみる

目次

Function Calling 機能について

この機能を使うと、OepnAI APIを通じて、関数呼び出しが可能になります。
何が嬉しいかというと、次の2点です。

  • 外部APIや関数を呼ぶべきか呼ばないかをGPT(AI)側に判断させられること
  • 外部APIや関数の値をもとに、回答を生成できること(つまり、学習データ以外のデータにもアクセスできる)

まず、前提として、OpenAI APIを使ってGPTと会話する方法について確認です。

ChatGPTで質問した時は、以下のようになっていると思います。

こちらを実現するプログラムが、以下のPythonで書かれたものです。

import openai

openai.api_key = 'your-api-key'

response = openai.ChatCompletion.create(

        model="gpt-3.5-turbo-0613",

        messages=[

            {"role": "user", "content": "PythonでHello worldと出力するプログラムを書いて"},

        ])

print(response['choices'][0]['message']['content'])

プログラムのmessages のところで、「PythonでHello worldと出力して」と入力しています。
ChatGPTのプロンプトに該当していますね。

実際に実行してみると……

回答としてPythonプログラムが返ってきます。
このときの回答は、純粋にGPTが考えて出力しています。

この前提を踏まえた上で、function_call を使う場合についてお話します。

Function Calling 機能を使う場合、次のプログラムのようにパラメータが追加されます。

import openai

openai.api_key = 'your-api-key'

response = openai.ChatCompletion.create(

        model="gpt-3.5-turbo-0613",

        messages=[

        //プロンプト:ボストンの天気は何ですか?

            {"role": "user", "content": "What's the weather like in Boston?"},

        ],

        //追加されたパラメータ1

     functions=[

            {

               //ここに呼び出す関数情報を記述する。

            }

        ],

        //追加されたパラメータ2

        function_call="auto",

)

print(response['choices'][0]['message']['content'])

追加されたパラメータは以下の2つです。

  • functions
  • function_call

特に重要なのが、functions というパラメーター。
必要なときにGPTに呼び出してもらいたい関数の情報を伝えるために使います。
関数の情報は複数伝えることもでき、

例えば、

  • 天気APIを実行する関数
  • Google Trend APIを実行する関数

の2つを用意します。

import openai

openai.api_key = 'your-api-key'

response = openai.ChatCompletion.create(

        model="gpt-3.5-turbo-0613",

        messages=[

        # プロンプト:ボストンの天気は何ですか?

            {"role": "user", "content": "What's the weather like in Boston?"},

        ],

        # 追加されたパラメータ1

        functions=[

            {

                #  天気APIを実行する関数の情報を記載

            },

            {

                # Google Trend APIを実行する関数の情報を記載

            }

        ],

        # 追加されたパラメータ2

        function_call="auto",

        )

#本来必要な処理は省略している。

print(response['choices'][0]['message']['content'])

プロンプトに「ボストンの天気は何ですか?」と入力されています。
この場合、AIが「天気について尋ねられたので、天気APIを実行する関数を呼び出そう」と自動で判断してくれます。
そうすることで、天気APIの結果をもとにして、「ボストンの天気は晴れ!」と回答してくれるようになるのです。

もし、プロンプトが「ブログネタに使えそうなテックの流行りを教えて」だとしたらどうでしょうか?
Google Trend APIを実行する関数を呼び出そう」とAIが判断して、「今の流行りは OpenAI API のFunction Calling 」と回答してくれるわけです。

この機能を使い、外部APIアクセスを自動で判断できるということは、
学習データ以外のデータにアクセスできるようになったことを意味します。

次は、この理解をもとに、OpenAI 公式ページにあったユースケースについて、深掘りしていきましょう。

なお、OpenAIの公式機能「Custom Instructions」について詳しく知りたい方は、下記の記事を合わせてご確認ください。
【ChatGPT】Custom InstructionsのTwitterでバズった使い方10選

ハンズオンセッション:Function Calling機能を試す

Function Calling を使うと、公式情報によると以下のようなユースケースが考えられるようです。

  1. 外部APIを呼び出して質問に答えるチャットボット
  2. 自然言語をAPI呼び出しに変換
  3. テキストからの構造化データの抽出

その中でも公開されていた1.に関するプログラムを解説していきます。

外部のAPIを呼び出して質問に答えるチャットボット

こちらでは、「天気APIを呼び出して質問に答える」チャットボットを例に解説します。
※デモのため、正確には天気APIを呼び出しておらず、ダミーデータを使っています。

解説の流れとしては、まずはプログラムの全文をお見せし、実行される順番にパートに分けていきます。

import openai

import json

# 6. 天気情報を取得する関数を定義します。

# 本来ならAPIを使いますが、デモのためダミーデータを使います。

def get_current_weather(location, unit="fahrenheit"):

    """Get the current weather in a given location"""

    weather_info = {

        "location": location,

        "temperature": "72", # APIで気温は動的に得られる

        "unit": unit,

        "forecast": ["sunny", "windy"], # APIで天気は動的に得られる

    }

    return json.dumps(weather_info)

def run_conversation():

    # 2. OpenAIのチャットモデルに対するAPIリクエストの作成

    # GPTへの質問文をmessagesという変数に格納している

    messages = [{"role": "user", "content": "What's the weather like in Boston?"}]

    # functionsという変数に、呼ぶ関数情報を設定している。

    functions = [

        {

            # 呼び出す関数の名前や説明

            "name": "get_current_weather", #関数名

            "description": "Get the current weather in a given location", #関数の説明

            # 関数の引数について設定している

            "parameters": {

                "type": "object", # 関数が受け取るパラメータは、JSON形式

                # 引数について設定している。今回は、location と unit の2種類

                "properties": {

                    "location": {# location 引数は、string 型。description は具体例.

                        "type": "string",

                        "description": "The city and state, e.g. San Francisco, CA",

                    },

                    "unit": { #unit 引数は、string 型。

                        "type": "string", 

                        "enum": ["celsius", "fahrenheit"] #許容される文字列2種類を指定

                        },

                },

                "required": ["location"], #location 引数は必須であることを指定

            },

        }

    ]

    # 格納した変数を使い、function calling機能を使った会話リクエストを作る

    response = openai.ChatCompletion.create(

        model="gpt-3.5-turbo-0613",

        messages=messages,

        functions=functions,

        function_call="auto",  

    )

    # 3. APIレスポンスからメッセージを取得します

    response_message = response["choices"][0]["message"]

    # 4. メッセージに関数呼び出しが含まれているかどうかを確認

    if response_message.get("function_call"):

        # 関数呼び出し(function_call)がされていたら、関数を呼び出すための処理を実行する

        # 実行される関数を動的に選ぶための処理。

        # 入力規則は以下。

        # functionsで入力したnameパラメータ(文字列型) : def で定義した関数名(関数型)

        available_functions = {

            "get_current_weather": get_current_weather,

        }

        # GPTが呼び出すと判断した関数の情報を取得します。

        # 今回の場合は、get_current_weather()関数

        function_name = response_message["function_call"]["name"]

        # 引数情報を取得

        function_args = json.loads(response_message["function_call"]["arguments"])

        # functin_to_call に関数型が値が代入される

        fuction_to_call = available_functions[function_name]

        # そのため以下は、get_current_weather()関数が実行されたときと同じ挙動をする。

        function_response = fuction_to_call(

            location=function_args.get("location"),

            unit=function_args.get("unit"),

        )

        # 関数呼び出しで取得した値を messages変数(GPTへの質問文)に追加

        # ここでは、関数呼び出しを行い、質問、回答を持ち合わせている状態

        messages.append(response_message) 

        messages.append(

            {

                "role": "function",

                "name": function_name,

                "content": function_response,

            }

        )  

        #5. 前段までで準備した情報をGPT-3.5 モデルに入力する。

        # こうすることで質問に対しての回答を整理するイメージ

        second_response = openai.ChatCompletion.create(

            model="gpt-3.5-turbo-0613",

            messages=messages,

        ) 

        # 7. 最後に、json形式なので、「ボストンの天気」情報に絞った要素を

        return second_response["choices"][0]["message"]["content"] 

# 1. run_conversation()関数を呼び出し、その出力を表示します。

print(run_conversation())

1. 最初にrun_conversation()関数が呼び出されます
この関数の役割は、OpenAIのチャットモデルを利用してユーザーの質問に基づいた天候情報を取得することです。

# 1. run_conversation()関数を呼び出し、その出力を表示します。

print(run_conversation())

2. run_conversation()関数内で最初に行うことは、OpenAI チャットモデルとの会話リクエストの作成
このプロセスはopenai.ChatCompletion.createメソッドを利用して行われます。
そのために、まずは必要な情報を変数に格納しています。

具体的には、messages変数にはモデルへの質問文(「What’s the weather like in Boston?」)を、functions変数には呼び出すべき関数の設定(get_current_weather)をそれぞれ格納しています。
この後、これらの情報を用いてGPT-3.5モデルに対する会話リクエストを作成しています。

def run_conversation():

    # 2. OpenAIのチャットモデルに対するAPIリクエストの作成

    # GPTへの質問文をmessagesという変数に格納している

    messages = [{"role": "user", "content": "What's the weather like in Boston?"}]

    # functionsという変数に、呼ぶ関数情報を設定している。

    functions = [

        {

            # 呼び出す関数の名前や説明

            "name": "get_current_weather", #関数名

            "description": "Get the current weather in a given location", #関数の説明

            # 関数の引数について設定している

            "parameters": {

                "type": "object", # 関数が受け取るパラメータは、JSON形式

                # 引数について設定している。今回は、location と unit の2種類

                "properties": {

                    "location": {# location 引数は、string 型。description は具体例.

                        "type": "string",

                        "description": "The city and state, e.g. San Francisco, CA",

                    },

                    "unit": { #unit 引数は、string 型。

                        "type": "string", 

                        "enum": ["celsius", "fahrenheit"] #許容される文字列2種類を指定

                        },

                },

                "required": ["location"], #location 引数は必須であることを指定

            },

        }

    ]

    # 格納した変数を使い、function calling機能を使った会話リクエストを作る

    response = openai.ChatCompletion.create(

        model="gpt-3.5-turbo-0613",

        messages=messages,

        functions=functions,

        function_call="auto",  

    )

3. 次に、APIレスポンスから情報を取得します
Function Calling 判定などに必要な情報を一部抜粋してmessage 変数に格納します。

# 3. APIレスポンスからメッセージを取得します

message = response["choices"][0]["message"]

4. message 変数にfunction_call という値があるかを確認します
もしあれば、name パラメータを使って関数(get_current_weather)を呼び出します。

# 4. メッセージに関数呼び出しが含まれているかどうかを確認

    if response_message.get("function_call"):

        # 関数呼び出し(function_call)がされていたら、関数を呼び出すための処理を実行する

        # 実行される関数を動的に選ぶために必要な処理。available_functions への入力規則は以下。

        # functionsで入力したnameパラメータ(文字列型) : def で定義した関数名(関数型)

        available_functions = {

            "get_current_weather": get_current_weather,

        }

        # GPTが呼び出すと判断した関数情報を取得します。

        # 今回の場合は、get_current_weather()関数

        function_name = response_message["function_call"]["name"]

        # 引数情報を取得

        function_args = json.loads(response_message["function_call"]["arguments"])

        # functin_to_call に関数型が値が代入される

        fuction_to_call = available_functions[function_name]

        # そのため以下は、get_current_weather()関数が実行されたときと同じ挙動をする。

        function_response = fuction_to_call(

            location=function_args.get("location"),

            unit=function_args.get("unit"),

        )

        print(response_message)

        # 関数呼び出しで取得した値を messages変数(GPTへの質問文)に追加

        # ここでは、関数呼び出しを行い、質問、回答を持ち合わせている状態

        messages.append(response_message) 

        messages.append(

            {

                "role": "function",

                "name": function_name,

                "content": function_response,

            }

        )

5. get_current_weather(location, unit=”fahrenheit”)関数について:
上述したrun_conversation()関数内で、呼び出されています。
この関数は、location 引数で指定された場所の現在の天候情報をJSON形式で取得。
その後、JSON形式から文字列に変換し、値を返します。

# 5. 天気情報を取得する関数を定義します。

# 本来ならAPIを使いますが、デモのためダミーデータを使います。

def get_current_weather(location, unit="fahrenheit"):

    """location 引数から与えられた場所の気温データ取得"""

    weather_info = {

        "location": location,

        "temperature": "72", # APIを使えば、気温は動的に得られる

        "unit": unit,

        "forecast": ["sunny", "windy"], # APIを使えば、天気は動的に得られる

    }

    return json.dumps(weather_info)

6. 2つ目のAPIレスポンス:
messages 変数には、質問や関数呼び出しで取得した回答が格納されています。
それを再度、GPTに入力することで整理し、最終的な回答が得られます。

#6. 前段で準備した情報をGPT-3.5 モデルに入力する。

        # こうすることで、get_current_weather()関数からの値が取得できる

        second_response = openai.ChatCompletion.create(

            model="gpt-3.5-turbo-0613",

            messages=messages,

        )

7. return second_response[“choices”][0][“message”][“content”]について:
今回の場合はget_current_weather()関数の戻り値が格納されています。

# 7. 最後に、「ボストンの天気」の情報が得られます。
return second_response["choices"][0]["message"]["content"]

ボストンの天気情報が、たしかに取得できてますね!

Function Callingを利用する際の注意点

Function Callingを利用するにあたって注意する点は以下の2つです。

  • トークンの使用量が大きくなりすぎるケースがある
  • 1度のリクエストで複数の情報は得られない

通常それほど差し障りのある問題ではありませんが、念頭には置いておいてください。

トークンの使用量が大きくなりすぎるケースがある

上の例のように、欲しい情報が指定位置の天気情報だけのような場合は大した使用量にはなりません。

しかし、例えば「東京のホテルを教えて」というような内容の場合、AIは複数件のホテル施設名や金額、プラン、その他オプション情報をつけて返答します。このような場合、あっという間にトークン数が膨れ上がり、トークン数オーバーによる400エラー(BadRequest)の発生が起こることをはじめ、利用料金が嵩んでしまうことも予想されます。

最近のOpenAIのAPI Modelは、比較的大きいトークンリクエストに耐えられるものもありますが、ユーザーの問いかけへの返答に対してたくさんの情報量が求められる場合には、マッチしづらいケースがあります。

1度のリクエストで複数の情報を得ることはできない

Function Callingでは、複数個のfunctionsを設定することができますが、assistantのResponseの中には1度に1つのfunctionの情報しか含まれません。

例えば、「ボストンの現在の天気とおすすめのお店を教えて」のようなプロンプトの場合です。この場合は、指定位置の天気情報を取得する処理と、指定位置のお店情報を取得する処理について、個別に都度応答と処理を行う必要があります。

なお、ChatGPTのAPIついて詳しく知りたい方は、下記の記事を合わせてご確認ください。
ChatGPT APIとは?利用方法や料金の確認方法、活用事例15選を紹介

Function Callingを使いこなそう

Function Calling機能は、GPTが外部データへのアクセスを可能にしました。
それほど変わっていないように感じるかもしれませんが、実はとても画期的な機能で、ものすごく賢いGPTの回答が学習データにとどまらないことを意味します。

一例ですが、以下のような振る舞いが可能になりました。

  • ユーザーが”ボストンの天気は何ですか?”と質問する
  • AIはそれを解析し、「天気について尋ねられたので、天気APIを実行する関数を呼び出そう」と自動的に判断する
  • その結果、天気APIのデータを基に、「ボストンの天気は晴れです!」と回答する

興味のある方はぜひ、Function Callingを試してみてくださいね。

サービス紹介資料

【無料】2023年2月版生成系AIの業務活用なら!

・生成系AIを活用したPoC開発

・生成系AIの業務活用コンサルティング

・システム間API連携

サービス紹介資料

生成系AIの業務活用なら!

・生成系AIを活用したPoC開発

・生成系AIのコンサルティング

・システム間API連携

弊社では法人向け生成AI研修・勉強会を行なっています

いかがだったでしょうか?

弊社では、法人様向けに生成AI研修を行なっております。
また、研修に加えて本事業の支援も行わせていただいております。

詳しくはこちらをご覧ください!
セミナー実績 | WEEL

研修をさせていただいた企業様からは、以下のような好評をいただいております。

生成AIについて包括的に理解ができ、多くの業務で生成AIが活用されるようになった。
生成AIの事例を知ることで、社内で生成AIを活用するためのアイデアがたくさん出てくるようになった。
全社で生成AIに関する認識を共有できたので、生成AIに関するプロジェクトの理解が得られるようになった。

研修の内容としては、以下のようなものになっています。

1. 生成AIの概要
2. 生成AIの歴史
3. 生成AIのリスク・対応策
4. 生成AIの活用事例
5. 生成AIの未来予測
6. 〇〇業界特有の生成AI活用法(様々な業界に対応)
7. プロンプトエンジニアリング実践(ChatGPTや画像生成AIへの入力文)
8. AI開発実践(Pythonプログラミング)
9. 生成AIの社内導入手順

※企業様のニーズに合わせて内容はカスタマイズしています。

「社員に生成AIに関する知識を身に付けてもらいたい」
「今後、生成AIを活用して事業展開をしていきたい」

と考えている方は、まずはご相談から承っておりますので、ぜひご連絡ください。

➡︎生成AIセミナー・社内勉強会について相談をしてみる

生成AIを社内で活用していきたい方へ

「生成AIを社内で活用したい」「生成AIの事業をやっていきたい」という方に向けて、朝の通勤時間に読めるメルマガを配信しています。

最新のAI情報を日本最速で受け取りたい方は、以下からご登録ください。

また、弊社紹介資料もご用意しておりますので、併せてご確認ください。

投稿者

  • Leon Kobayashi

    必ずフォローすべきAIエバンジェリスト(自称) => 元東証一部上場ITコンサル (拙者、早口オタク過ぎて性に合わず退社)<-イマココ 【好きなもの】リコリコ・しゃぶ葉 宜しくおねがいします。

  • URLをコピーしました!
  • URLをコピーしました!
目次