0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【0から目指すAIエンジニア】Day3:Function callingの基礎

Last updated at Posted at 2025-04-09

はじめに

こんにちは、慶應義塾大学理工学部機械工学科3年の金賢佑です!
私は東大発AIベンチャーの株式会社2WINSの元でインターンをしています!

これからの記事では自分がインターンを通して最強のAIエンジニアを目指していきます!
第三回はFanction callingの基礎を記録しました!

Function callingとは

Function callingとはLLMに「この機能が使える!」と判断させる機能のこと!
なのでLLM側がその機能を実行するわけではありません。

例えばあなたが「東京の天気を摂氏で教えて」といった時、
1.LLMがツールから使える関数、例えばget_current_weatherを選んでくれる。
2.{"location": "東京", "unit": "celsius"}という引数を組み立ててあなたのプログラムにこの関数を実行して!と伝えてくれる。
3.あなたのプログラム側でその関数を実行する。
4.結果をLLMに返し、それを元に自然な文章で返事をしてくれる。

LLMって?

LLMとはLarge Language Model(大規模言語モデル)の略で言語モデルの一種!
前回扱ったChat GPTもLLmの一つですね。
言語モデルについては「3Blue1BrownJapan」さんが出している動画がとてもわかりやすいです!
2025/4/1現在、Chapter7くらいまであります。
https://youtu.be/tc8RTtwvd5U?si=ynBpv-bFHHjfwOCz

Function callingを使ってみよう

関数の定義

以下のコードを実行してください。

import json

def get_current_weather(location, unit='fahrenheit'):
  if 'tokyo' in location.lower():
    return json.dumps({'location':'Tokyo', 'temperature':'10', 'unit': unit})
  elif 'san francisco' in location.lower():
    return json.dumps({'location': 'San Francisco', 'temperature': '72', 'unit':unit})
  elif 'paris' in location.lower():
    return json.dumps({'location':'Paris', 'temperature': '22', 'unit': unit})
  else:
    return json.dumps({'location': location, 'temperature': 'unknown'})

これはもらった場所の名前が「Tokyo」「San Francisco」「Paris」だったらそれぞれの温度を返す、それ以外であればunknownと返す関数!
ちなみに返す内容はJSON形式の文字列。
今回も細かく解説していきます。

import json

jsonライブラリを読み込みます。
この後json.dumps()を使うためにインポートしておきます。

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

get_current_weatherという関数を定義しています。
locationunitという二つの引数が必要とされていますが、unitにはデフォルトで'fahrenheit'という引数が与えられていますね。

2つ目の引数について実例を記載しておきます。

get_current_weather("Tokyo")  # → unit は省略されて 'fahrenheit' になる

get_current_weather("Tokyo", "celsius")  # → unit = 'celsius' に上書き
  if 'tokyo' in location.lower():
    return json.dumps({'location':'Tokyo', 'temperature':'10', 'unit': unit})

lower()は小文字に直す関数です。つまりlocationに渡された値(ユーザーが入力した値)がTOKYOでもTokyoでもToKyoでも全てtokyoに直してくれるんですね。
そうすることで一文目のif分でlocationtokyoが入力されているのかどうか正確に判断することができますね。

その後、TokyoのデータをJSON形式で返しています。

どうしてJSON形式に直すの?

最初に説明したようにFunction callingではプログラムはPython側で実行し、その結果はLLM(今回だとChatGPT)に返しますよね。(その後LLMが自然な文章に直して返事をしてくれる!)
だからLLMが読みやすい形式に直すのがいいんです!

  elif 'san francisco' in location.lower():
    return json.dumps({'location': 'San Francisco', 'temperature': '72', 'unit':unit})
  elif 'paris' in location.lower():
    return json.dumps({'location':'Paris', 'temperature': '22', 'unit': unit})
  else:
    return json.dumps({'location': location, 'temperature': 'unknown'})

その後も同じロジックで処理が書かれていますね。
これでget_current_weather関数の定義が完成しましたね。

toolの作成

先程関数の定義を行いましたが、これだけではFunction callingは機能しません。
LLMが🤖「この関数が使える!」と処理できるようにLLM用の関数の説明書、メニュー表のようなものが必要です。
それをtoolと言ったりします。

以下のコマンドを打ってみてください。

tools = [
  {
    type: "function",
    function: {
      name: "get_current_weather",
      description: "Get the current weather in a given location",
      parameters: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "The city and state, e.g. San Fracisco, CA",
          },
          unit: { type: "string", enum: ["celsius", "fahrenheit"] },
        },
        required: ["location"],
      },
    },
  },
];

今回も細かく解説していきます。

tools = [
    {
        'type': 'function',

「このツールは関数だよ!」と書いてあります。

        'function': {
            'name': 'get_current_weather',
            'description': 'Get the current weather in a given location',

関数の名前と説明文が記載されていますね。

            'parameters': {
                'type':'object',

関数が受け取る引数の情報です。
'type': 'object'は引数がオブジェクトですということを意味しています。
オブジェクトとは複数の値をセットで受け取るためのまとまりだと思ってください。(dict型みたいなもの!)
今回だとlocationというキーにTokyoという値が入っていたりしますよね。

                'properties': {
                    'location': {
                      'type': 'string',
                      'description': 'The city and state, e.g. San Fracisco, CA',
                },
                'unit':{'type': 'string', 'enum': ['celsius', 'fahrenheit']},
            },

propertiesは使えるパラメータの一覧です。
location"Tokyo"などが入ります。
unit"celsius""fahrenheit"のどちらかに限定します。
enumで列挙型にすることでこれを可能にしています。

            'required': ['location'],

            },
        },
    }
]

requiredは必須パラメータです。
ここにunitが入っていないのは、unitにはデフォルトで値が設定されているからでしたね。

Chat Completion APIを呼び出してみよう

以下のコマンドを打ってみてください。

from openai import OpenAI

client = OpenAI()

messages = [
    {'role': 'user',  'content':'東京の天気はどうですか?'}
]

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

print(response.to_json(indent=2))

今回の会話の履歴はユーザーからの「東京の天気を知りたい!」というだけですね。
model'gpt-4o'を使っています。
messagesは先程の会話の履歴のmessagesを(名前が一緒だからわかりにくい)
toolsにも先程作成したtoolsを代入しています。(これも名前が一緒だからわかりにくい)

返ってっきたresponseをJSON形式に直してprintしていますね。
ちなみにindent=2は見やすく整形しているだけです。

実行結果

スクリーンショット 2025-04-06 20.16.23.png

長々と書かれていますが、注目して欲しいのは赤枠の部分。
LLMが先程の会話履歴から、「get_current_weather関数を東京という引数で実行するべきだ」と判断したことが書かれています。
(if 'tokyo' in location.lower(): if 'tokyo' in location.lower() or '東京' in location:`に直しておいた方が良さそうですね。)
この判断をもとにPythonで関数を実行してあげれば良さそうですね。
ではこのメッセージを会話履歴として残しておきましょう。
以下のコマンドを実行してください。

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

responseとはさっき長々とprintされた文のことです。
LLMは出力を何種類も出すのでresponseの最初の選択肢にするようchoices[0]としています。
ちなみに今回[0]としたのは先程の出力で"index":0となっていたのでそれに対応するようにですね。
まとめるとさっきの出力のmessageの部分を丸々response_messageに記録(代入)しています。

2行目では、そうして記録された会話をmessagesappend()、つまり追加しています。
こうして会話がどんどん残るようにしているのですね。

LLMが実行したい関数をこちらで実行してあげよう

以下のコマンドを打ってください。

available_functions = {
    'get_current_weather': get_current_weather,
}

for tool_call in response_message.tool_calls:

  function_name = tool_call.function.name
  function_to_call = available_functions[function_name]
  function_args = json.loads(tool_call.function.arguments)
  function_response = function_to_call(
      location=function_args.get('location'),
      unit=function_args.get('unit'),

  )
  messages.append(
          {
              'tool_call_id': tool_call.id,
              'role': 'tool',
              'name': function_name,
              'content': function_response,

          }
      )
      
print(function_response)

ここあたりから情報量が多くなってくるので、躓いたら前に戻りながら確認してください。

available_functions = {
    'get_current_weather': get_current_weather,
}

先程GPTからの返事の中に"tool_calls"がありました。
その中に使いたい関数が"name": "get_current_weather"として書かれています。
しかし"get_current_weather"は文字列のため、対応する関数と紐付ける必要があります。
上のコードはそれを行なっています。

for tool_call in response_message.tool_calls:

response_massageとは会話が記録される箱でしたね。さっき作りました。
その中で必要とされている関数、tool_callsを一つずつ呼び出しているんですね。

  function_name = tool_call.function.name

呼び出したい関数

  function_to_call = available_functions[function_name]

呼び出したい関数の名前を、最初に作ったavailable_functionsの辞書から取得する。
get_current_weather()が入ります。

  function_args = json.loads(tool_call.function.arguments)

function_argsにはGPTが提案してきた関数の引数を渡します。
今回だと"arguments": "{\"location\":\"東京\"}"でしたね。

  function_response = function_to_call(
      location=function_args.get('location'),
      unit=function_args.get('unit'),

  )

実際に関数を呼び出します。
今回、function_to_callにはget_current_weather()が入っていましたね。

  messages.append(
          {
              'tool_call_id': tool_call.id,
              'role': 'tool',
              'name': function_name,
              'content': function_response,

          }
      )

messagesにこのtool_callにはこういう返信をしたよ〜って記録する!

print(function_response)

結果は

{"location": "Tokyo", "temperature": "10", "unit": "celsius"}

となって、ちゃんと返ってきていますね。

もう一度APIリクエストを送ってみよう

先程の7.で追加したメッセージを使ってAPIにメッセージを送ってみます。

second_response = client.chat.completions.create(
    model='gpt-4o',
    messages=messages,

)
print(second_response.to_json(indent=2))

出力は

{
  "id": "chatcmpl-BJXBntAtL9pgUlUEeKUkRWGBZDi3M",
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": "東京の現在の気温は約10℃です。天気の詳細について知りたい場合は、具体的な情報をご確認ください。",
        "refusal": null,
        "role": "assistant",
        "annotations": []
      }
    }
  ],
  "created": 1743995511,
  "model": "gpt-4o-2024-08-06",
  "object": "chat.completion",
  "service_tier": "default",
  "system_fingerprint": "fp_898ac29719",
  "usage": {
    "completion_tokens": 32,
    "prompt_tokens": 64,
    "total_tokens": 96,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "audio_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  }
}

先程の実行結果も踏まえた回答が送られてきていますね。

実行コードまとめ

#JSONのインポート
import json

def get_current_weather(location, unit='fahrenheit'):
  if 'tokyo' in location.lower():
    return json.dumps({'location':'Tokyo', 'temperature':'10', 'unit': unit})
  elif 'san francisco' in location.lower():
    return json.dumps({'location': 'San Francisco', 'temperature': '72', 'unit':unit})
  elif 'paris' in location.lower():
    return json.dumps({'location':'Paris', 'temperature': '22', 'unit': unit})
  else:
    return json.dumps({'location': location, 'temperature': 'unknown'})

#ツールの作成
tools = [
  {
    type: "function",
    function: {
      name: "get_current_weather",
      description: "Get the current weather in a given location",
      parameters: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "The city and state, e.g. San Fracisco, CA",
          },
          unit: { type: "string", enum: ["celsius", "fahrenheit"] },
        },
        required: ["location"],
      },
    },
  },
];

# OpenAIにAPIを送る
from openai import OpenAI

client = OpenAI()

messages = [
    {'role': 'user',  'content':'東京の天気はどうですか?'}
]

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

#こちら側で関数を実行してあげよう
available_functions = {
    'get_current_weather': get_current_weather,
}

for tool_call in response_message.tool_calls:

  function_name = tool_call.function.name
  function_to_call = available_functions[function_name]
  function_args = json.loads(tool_call.function.arguments)
  function_response = function_to_call(
      location=function_args.get('location'),
      unit=function_args.get('unit'),

  )
  messages.append(
          {
              'tool_call_id': tool_call.id,
              'role': 'tool',
              'name': function_name,
              'content': function_response,

          }
      )
      
print(function_response)

#もう一度リクエストを送ってみよう
second_response = client.chat.completions.create(
    model='gpt-4o',
    messages=messages,

)
print(second_response.to_json(indent=2))

まとめ

今回はFunction callingの基礎を解説していきました。
次回はcurlでopenaiのAPIを動かしてみます!

お疲れ様でした!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?