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?

Function Callingで天気検索ツール付きAIアシスタントを作る

0
Last updated at Posted at 2026-10-01

「東京の天気を教えて」のような質問に、AIが最新の天気データを取得して回答するアシスタントを作ります。

今回のポイントは、AI自身に天気APIを直接呼ばせるのではなく、AIには「どの関数を、どの引数で呼ぶか」を判断させ、実際のAPI呼び出しはアプリケーション側で行うことです。

OpenAI APIのFunction Calling(現在のドキュメントではTool Callingとも表記されます)は、このような外部データ連携に利用できます。

Function Callingの流れ

Function Callingを使った天気検索処理の流れは次の通りです。

処理は次の4段階です。

  1. AIに利用可能な関数の名前・説明・引数を渡す
  2. AIが質問に応じて関数呼び出しを返す
  3. アプリケーションが実際の天気APIを呼び出す
  4. 結果をAIに渡し、自然な文章を生成する

重要なのは、AIが関数の実装そのものを実行するわけではない点です。AIは構造化された呼び出し情報を返すだけで、APIアクセスや認証、エラー処理はアプリケーションが担当します。

環境を準備する

今回はPythonとOpenAI公式SDKを使います。天気データの取得には、サンプルとしてOpen-MeteoのAPIを利用します。

python -m venv .venv
source .venv/bin/activate
python -m pip install -U openai requests

Windows PowerShellの場合は、仮想環境の有効化を次のように行います。

.venv\Scripts\Activate.ps1
python -m pip install -U openai requests

APIキーはソースコードに直接書かず、環境変数で設定します。

export OPENAI_API_KEY="YOUR_API_KEY"
export OPENAI_MODEL="gpt-5"

Windows PowerShellでは次の形式です。

$env:OPENAI_API_KEY="YOUR_API_KEY"
$env:OPENAI_MODEL="gpt-5"

OPENAI_MODELには、利用している環境で使用可能なモデルIDを指定してください。

天気検索関数を実装する

まず、都市名から緯度・経度を取得し、その座標を使って現在の天気を取得します。

import requests


def weather_description(code: int) -> str:
    if code == 0:
        return "快晴"
    if code in {1, 2, 3}:
        return "晴れまたは曇り"
    if code in {45, 48}:
        return "霧"
    if code in {51, 53, 55, 56, 57}:
        return "霧雨"
    if code in {61, 63, 65, 66, 67}:
        return "雨"
    if code in {71, 73, 75, 77}:
        return "雪"
    if code in {80, 81, 82}:
        return "にわか雨"
    if code in {95, 96, 99}:
        return "雷雨"
    return "不明"


def get_weather(location: str) -> dict:
    geocoding = requests.get(
        "https://geocoding-api.open-meteo.com/v1/search",
        params={
            "name": location,
            "count": 1,
            "language": "ja",
            "format": "json",
        },
        timeout=10,
    )
    geocoding.raise_for_status()
    places = geocoding.json().get("results", [])

    if not places:
        return {"error": f"{location}の位置情報が見つかりません"}

    place = places[0]
    forecast = requests.get(
        "https://api.open-meteo.com/v1/forecast",
        params={
            "latitude": place["latitude"],
            "longitude": place["longitude"],
            "current": (
                "temperature_2m,apparent_temperature,"
                "precipitation,weather_code"
            ),
            "timezone": "auto",
        },
        timeout=10,
    )
    forecast.raise_for_status()

    current = forecast.json()["current"]

    return {
        "location": place["name"],
        "country": place.get("country", ""),
        "temperature_c": current["temperature_2m"],
        "apparent_temperature_c": current["apparent_temperature"],
        "precipitation_mm": current["precipitation"],
        "description": weather_description(current["weather_code"]),
        "timezone": forecast.json().get("timezone"),
    }

Function Callingで返してほしい値は、できるだけ小さなJSONにします。今回なら都市名、気温、体感温度、降水量、天気の説明だけで十分です。

AIに天気ツールを渡す

次に、AIが利用できる関数をJSON Schemaで定義します。

import json
import os

from openai import OpenAI


client = OpenAI()
model = os.getenv("OPENAI_MODEL", "gpt-5")

weather_tool = {
    "type": "function",
    "name": "get_weather",
    "description": "指定された都市の現在の天気を取得します。",
    "parameters": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "都市名。例: 東京、大阪、札幌",
            }
        },
        "required": ["location"],
        "additionalProperties": False,
    },
    "strict": True,
}

strictを有効にする場合、requiredにすべてのプロパティを含め、additionalPropertiesをfalseにする必要があります。ここを忘れると、リクエスト時にスキーマエラーになりやすいです。

Function Callingの結果を処理する

以下がアシスタント本体です。

from weather import get_weather


def run_assistant(question: str) -> str:
    input_items = [
        {
            "role": "user",
            "content": question,
        }
    ]

    for _ in range(3):
        response = client.responses.create(
            model=model,
            input=input_items,
            tools=[weather_tool],
            parallel_tool_calls=False,
        )

        function_calls = [
            item
            for item in response.output
            if item.type == "function_call"
        ]

        # 関数呼び出しがなければ、通常の回答が完成している
        if not function_calls:
            return response.output_text

        # AIの関数呼び出し情報を次の入力に引き継ぐ
        input_items.extend(response.output)

        for call in function_calls:
            arguments = json.loads(call.arguments)

            if call.name == "get_weather":
                result = get_weather(**arguments)
            else:
                result = {"error": f"未対応の関数です: {call.name}"}

            input_items.append(
                {
                    "type": "function_call_output",
                    "call_id": call.call_id,
                    "output": json.dumps(
                        result,
                        ensure_ascii=False,
                    ),
                }
            )

    return "ツール呼び出しが繰り返されたため、処理を中断しました。"


if __name__ == "__main__":
    question = input("質問: ").strip()

    if not question:
        question = "東京の現在の天気を教えてください"

    print(run_assistant(question))

Function Callingでは、AIが返したcall_idと同じ値をfunction_call_outputに設定します。この対応付けがないと、どの関数呼び出しに対する結果なのかAIが判断できません。

また、関数名をそのまま実行するのではなく、if call.name == "get_weather"のように呼び出し可能な関数を明示的に分岐させています。任意の関数名を実行する設計は避けたほうが安全です。

実行します。

python main.py

入力例です。

質問: 札幌の現在の天気と気温を教えて

実行時には、AIが内部でおおむね次のような呼び出しを生成します。

{
  "type": "function_call",
  "name": "get_weather",
  "arguments": "{\"location\":\"札幌\"}"
}

その後、アプリケーションが天気APIを呼び出し、取得結果をAIへ返します。最終的には「札幌は現在○℃で、雨です。体感温度は○℃、降水量は○mmです」のような自然な回答になります。気温や天気は実行時のデータに応じて変わります。

実装時に気を付けること

まず、天気APIの失敗を考慮します。都市名が見つからない、外部APIがタイムアウトする、レスポンス形式が変わる、といったケースがあります。今回の例ではtimeoutとraise_for_status()を設定していますが、本番では例外を捕捉し、AIには安全なエラーメッセージだけを返す設計にします。

次に、AIの回答をそのまま信じないことです。気温や降水量などの事実は、必ずツールの戻り値を根拠に回答させます。システムプロンプトを追加する場合は、次のような指示が有効です。

instructions = """
あなたは天気案内アシスタントです。
天気に関する質問では必ず get_weather を利用してください。
ツールの結果にない気象情報を推測してはいけません。
"""

そして、APIキーは絶対にクライアント側へ埋め込まないようにします。Webアプリケーションにする場合も、Function Callingと外部APIの呼び出しはサーバー側で実行します。

まとめ

Function Callingを使うと、AIの自然言語理解と、通常のプログラムによる外部API連携を組み合わせられます。

今回の実装で押さえるポイントは次の通りです。

  • AIには関数名・説明・JSON Schemaを渡す
  • 実際のAPI呼び出しはアプリケーション側で行う
  • 結果はfunction_call_outputとしてAIへ返す
  • call_idで関数呼び出しと結果を対応付ける
  • スキーマ検証、タイムアウト、APIキー管理を実装する

天気検索をニュース検索、在庫照会、社内データ検索などに置き換えれば、同じ構成で実用的なAIアシスタントへ拡張できます。詳しい仕様はOpenAI公式のFunction Callingガイドも参照してください。

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?