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 の使い方
事前準備
- 利用可能な関数を定義しておく
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種類を用意してみました
実行手順
- モデルに質問するついでに、ツールとして利用可能な関数情報をモデルに教えてあげる
- 会話履歴にモデルが所望している情報を保存しておく
- モデルが所望している情報を基に、用意してある関数を呼び出す
- 関数の戻り値をモデルに教えてあげ、最終的な回答を答えてもらう
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」の呼び出しを所望されます。
「今日のパリの気温はどうですか?」と問い合わせると「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 と連携していれば、リアルな情報が返却されるようになりますね。