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

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

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

目次

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

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

サービス紹介資料

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

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

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

・システム間API連携

サービス紹介資料

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

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

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

・システム間API連携

まとめ

Function Calling機能は、GPTが外部データへのアクセスを可能にしました。
とても画期的な機能で、ものすごく賢いGPTの回答が学習データにとどまらないことを意味します。

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

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

最後に

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

弊社では

・マーケティングやエンジニアリングなどの専門知識を学習させたAI社員の開発
・要件定義・業務フロー作成を80%自動化できる自律型AIエージェントの開発
・生成AIとRPAを組み合わせた業務自動化ツールの開発
・社内人事業務を99%自動化できるAIツールの開発
ハルシネーション対策AIツールの開発
自社専用のAIチャットボットの開発

などの開発実績がございます。

まずは、「1時間の無料相談」にてご相談を承っておりますので、ご興味がある方はぜひご連絡ください。

➡︎生成AIを使った業務効率化、生成AIツールの開発について相談をしてみる。

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

「生成AIを社内で活用したい」「生成AIの事業をやっていきたい」という方に向けて、生成AI社内セミナー・勉強会をさせていただいております。

セミナー内容や料金については、ご相談ください。

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

投稿者

  • Leon Kobayashi

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

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