ジャコ Lab

プログラミング関連のメモ帳的ブログです

Google Colab で Function calling を試してみる

Google Colab で Function calling を試してみる

さて、Raspberry Pi Zero 2 向けの USB ハブをポチりましたが、まだ届きません。

仕方ないので、今日は OpenAI API の Function calling を試してみます

書籍を使ってお勉強中です

第二章から「Function calling」を試してみます

Function calling とは?

モデルがアクセスできるツールとして 関数情報を定義 することで、モデルから 使用したい関数 及び その引数 が提示されます。その内容を踏まえて関数の実行し、実行結果を含めて再度モデルに問い合わせることで、最終的な回答をしてくれます。

Function calling を使用せずに天気を聞いた場合

from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=[
        {'role': 'system', 'content': 'You are a helpful assistant.'},
        {'role': 'user', 'content': '今日の東京の天気はどうですか?'},
    ]
)

print(response.to_json())
{
  ~省略~,
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": "申し訳ありませんが、リアルタイムの情報を提供することはできません。今日の東京の天気を確認するには、天気予報のウェブサイトやアプリをご覧になると良いでしょう。例えば、気象庁やウェザーニュースなどの公式サイトが信頼できます。",
        "refusal": null,
        "role": "assistant"
      }
    }
  ],
  ~省略~
}
リアルタムな情報は教えてもらえません

Function calling の使い方

事前準備

  1. 利用可能な関数を定義しておく
def get_weather(location: str) -> dict:
    if 'tokyo' in location.lower():
        return {'location': 'tokyo', 'weather': 'Sunny'}
    elif 'san francisco' in location.lower():
        return {'location': 'san francisco', 'weather': 'Cloudy'}
    elif 'paris' in location.lower():
        return {'location': 'paris', 'weather': 'Rainy'}
    else:
        return {'location': location, 'weather': 'unknown'}

def get_temperature(location: str, unit: Literal['celsius', 'fahrenheit'] = 'celsius') -> dict:
    if 'tokyo' in location.lower():
        return {'location': 'tokyo', 'temperature': '10', 'unit': unit}
    elif 'san francisco' in location.lower():
        return {'location': 'san francisco', 'temperature': '70', 'unit': unit}
    elif 'paris' in location.lower():
        return {'location': 'paris', 'temperature': '22', 'unit': unit}
    else:
        return {'location': location, 'temperature': 'unknown', 'unit': unit}
天気と気温の2種類を用意してみました

実行手順

  1. モデルに質問するついでに、ツールとして利用可能な関数情報をモデルに教えてあげる
  2. 会話履歴にモデルが所望している情報を保存しておく
  3. モデルが所望している情報を基に、用意してある関数を呼び出す
  4. 関数の戻り値をモデルに教えてあげ、最終的な回答を答えてもらう

Function calling を利用してみる

モデルに質問するついでに、ツールとして利用可能な関数情報をモデルに教えてあげる

天気を問い合わせるパターン
client = OpenAI()

messages = [
    {'role': 'system', 'content': 'You are a helpful assistant.'},
    {'role': 'user', 'content': '今日の東京の天気はどうですか?'},
]

response = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=messages,
    tools=[
      {
            'type': 'function',
            'function': {
                'name': 'get_weather',
                'description': 'Get the weather in a given location',
                'parameters': {
                    'type': 'object',
                    'properties': {
                        'location': {
                            'type': 'string',
                            'description': 'The city and state, e.g. San Francisco, CA',
                        },
                    },
                    'required': ['location']
                }
            }
        },
        {
            'type': 'function',
            'function': {
                'name': 'get_temperature',
                'description': 'Get the temperature in a given location',
                'parameters': {
                    'type': 'object',
                    'properties': {
                        'location': {
                            'type': 'string',
                            'description': 'The city and state, e.g. San Francisco, CA',
                        },
                        'unit': {
                            'type': 'string',
                            'enum': ['celsius', 'fahrenheit']
                        },
                    },
                    'required': ['location']
                }
            }
        }
  ]
)

print(response.to_json())

コンソールログ

{
  ~省略~,
  "choices": [
    {
      "finish_reason": "tool_calls",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": null,
        "refusal": null,
        "role": "assistant",
        "tool_calls": [
          {
            "id": "call_e2o3iVsdHmebzjWelKLqBycL",
            "function": {
              "arguments": "{\"location\":\"Tokyo, Japan\"}",
              "name": "get_weather"
            },
            "type": "function"
          }
        ]
      }
    }
  ],
  ~省略~
}

choices[0].message.tool_calls[0].function が以下のように返ってきています

{
    "arguments": "{"location":"Tokyo, Japan"}",
    "name": "get_weather"
}

気温を問い合わせるパターン
client = OpenAI()

messages = [
    {'role': 'system', 'content': 'You are a helpful assistant.'},
    {'role': 'user', 'content': '今日のパリの気温はどうですか?'},
]

response = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=messages,
    tools=[
      {
            'type': 'function',
            'function': {
                'name': 'get_weather',
                'description': 'Get the weather in a given location',
                'parameters': {
                    'type': 'object',
                    'properties': {
                        'location': {
                            'type': 'string',
                            'description': 'The city and state, e.g. San Francisco, CA',
                        },
                    },
                    'required': ['location']
                }
            }
        },
        {
            'type': 'function',
            'function': {
                'name': 'get_temperature',
                'description': 'Get the temperature in a given location',
                'parameters': {
                    'type': 'object',
                    'properties': {
                        'location': {
                            'type': 'string',
                            'description': 'The city and state, e.g. San Francisco, CA',
                        },
                        'unit': {
                            'type': 'string',
                            'enum': ['celsius', 'fahrenheit']
                        },
                    },
                    'required': ['location']
                }
            }
        }
  ]
)

コンソールログ

{
  ~省略~,
  "choices": [
    {
      "finish_reason": "tool_calls",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": null,
        "refusal": null,
        "role": "assistant",
        "tool_calls": [
          {
            "id": "call_gfMhlSqMJJgAUKrEWEE1HFdB",
            "function": {
              "arguments": "{\"location\":\"Paris, France\",\"unit\":\"celsius\"}",
              "name": "get_temperature"
            },
            "type": "function"
          }
        ]
      }
    }
  ],
  ~省略~
}

choices[0].message.tool_calls[0].function が以下のように返ってきています

{
    "arguments": "{"location":"Paris, France","unit":"celsius"}",
    "name": "get_temperature"
}

「今日の東京の天気はどうですか?」と問い合わせると「get_weather」の呼び出しを所望され、
「今日のパリの気温はどうですか?」と問い合わせると「get_get_temperature」の呼び出しを所望されます。
試しに両方問い合わせてみた

今日のサンフランシスコの天気と気温はどうですか?

{
  ~省略~,
  "choices": [
    {
      "finish_reason": "tool_calls",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": null,
        "refusal": null,
        "role": "assistant",
        "tool_calls": [
          {
            "id": "call_ETfATl2HVGeGA2iRzXx9gCeg",
            "function": {
              "arguments": "{\"location\": \"San Francisco, CA\"}",
              "name": "get_weather"
            },
            "type": "function"
          },
          {
            "id": "call_2EoE8uR6uKVJvfukuTUCyOFr",
            "function": {
              "arguments": "{\"location\": \"San Francisco, CA\", \"unit\": \"celsius\"}",
              "name": "get_temperature"
            },
            "type": "function"
          }
        ]
      }
    }
  ],
  ~省略~
}
tool_calls が2つになりました

会話履歴にモデルが所望している情報を保存しておく

response_message = response.choices[0].message
messages.append(response_message.to_dict())

モデルが所望している情報を基に、用意してある関数を呼び出す

available_functions = {
    'get_weather': get_weather,
    'get_temperature': get_temperature
}
for tool_call in response_message.tool_calls:
    function_name = tool_call.function.name
    function_args = json.loads(tool_call.function.arguments)

    func = available_functions[function_name]
    func_response = func(**function_args)

    messages.append({
        'tool_call_id': tool_call.id,
        'role': 'tool',
        'name': function_name,
        'content': json.dumps(func_response)
    })

モデルが所望している情報(=choices[0].message.tool_calls)を使って、事前に用意している関数をコールしてあげます。
関数の戻り値は会話履歴に追加しておきます。

この時点での messages 変数(会話履歴) の中身

[
  {
    "role": "system",
    "content": "You are a helpful assistant."
  },
  {
    "role": "user",
    "content": "今日のサンフランシスコの天気と気温はどうですか?"
  },
  {
    "content": null,
    "refusal": null,
    "role": "assistant",
    "tool_calls": [
      {
        "id": "call_a5CaPZnoIb2tmxnPW1Z4MqUL",
        "function": {
          "arguments": "{\"location\": \"San Francisco, CA\"}",
          "name": "get_weather"
        },
        "type": "function"
      },
      {
        "id": "call_E30nCiTU9ARB4QL5f7Db3RX8",
        "function": {
          "arguments": "{\"location\": \"San Francisco, CA\", \"unit\": \"celsius\"}",
          "name": "get_temperature"
        },
        "type": "function"
      }
    ]
  },
  {
    "tool_call_id": "call_a5CaPZnoIb2tmxnPW1Z4MqUL",
    "role": "tool",
    "name": "get_weather",
    "content": "{\"location\": \"san francisco\", \"weather\": \"Cloudy\"}"
  },
  {
    "tool_call_id": "call_E30nCiTU9ARB4QL5f7Db3RX8",
    "role": "tool",
    "name": "get_temperature",
    "content": "{\"location\": \"san francisco\", \"temperature\": \"70\", \"unit\": \"celsius\"}"
  }
]

関数の戻り値をモデルに教えてあげ、最終的な回答を答えてもらう

response = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=messages,
)

print(response.to_json())
最終的な回答
{
  ~省略~,
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": "今日のサンフランシスコの天気は曇りです。気温は約70度(摂氏約21度)です。",
        "refusal": null,
        "role": "assistant"
      }
    }
  ],
  ~省略~
}
ここまでやってようやく「今日のサンフランシスコの天気と気温はどうですか?」の回答を得ることができました

まとめ

get_weather, get_temperatureは固定値を返却するメソッドになっていますが、お天気 API と連携していれば、リアルな情報が返却されるようになりますね。