Antigravity で Python を使い始めて最初の壁が「クイックスタートはできたけど、実際のアプリで役立つ機能を実装するには何を学べばいいか」という段階です。
テキスト生成はできます。ストリーミングもわかりました。でも、現実のアプリでは「AIが外部の API を呼んで、その結果を踏まえて答えを返す」という処理が必要になってきます。これを実現するのが Function Calling(ツール呼び出し) です。
今回は Antigravity × Python 環境で Gemini API の Function Calling を実装する方法を、動くコードを中心に説明します。
Function Calling とは(なぜ必要なのか
LLM は言語処理は得意ですが、現在時刻を正確に答えたり、リアルタイムの株価を調べたりはできません。「2026年4月の東京の天気は?」と聞いても、それっぽい回答を作るだけです。
Function Calling は、「モデルが特定の関数を呼び出したい」と判断したとき、その関数の名前と引数をJSON形式で返す仕組みです。アプリ側でその関数を実行し、結果をモデルに返すと、モデルは正確なデータを使って最終回答を生成します。
つまり:
- アプリ側: 関数を定義し、実際に実行する
- モデル側: どの関数をいつ呼ぶか判断し、引数を生成する
この役割分担がFunction Callingの本質です。
Antigravity での開発環境セットアップ
Antigravity を開いてターミナルから仮想環境を作ります。
# プロジェクトフォルダを作成
mkdir gemini-function-calling
cd gemini-function-calling
# 仮想環境
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 依存関係インストール
pip install google-generativeai python-dotenvAntigravity の Python 拡張が有効になっていれば、仮想環境のインタープリタを自動検出してくれます。もし認識されない場合は Ctrl+Shift+P → Python: Select Interpreter → .venv/bin/python を選択してください。
.env ファイルに API キーを設定します:
GEMINI_API_KEY=YOUR_GEMINI_API_KEY基本的な Function Calling の実装
まず最も単純な例で仕組みを理解します。「現在時刻を返す関数」を定義し、Gemini に渡します。
# basic_function_calling.py
import os
import json
import datetime
import google.generativeai as genai
from dotenv import load_dotenv
load_dotenv()
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
# ① 関数の定義(Gemini が呼び出す候補)
def get_current_time(timezone: str = "Asia/Tokyo") -> dict:
"""現在時刻を返す"""
now = datetime.datetime.now()
return {
"time": now.strftime("%Y-%m-%d %H:%M:%S"),
"timezone": timezone
}
# ② Gemini に渡すツール定義(JSON Schema 形式)
tools = [
{
"function_declarations": [
{
"name": "get_current_time",
"description": "現在の日時を取得します",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "タイムゾーン(例: Asia/Tokyo, UTC)"
}
},
"required": []
}
}
]
}
]
# ③ モデルとチャットを開始
model = genai.GenerativeModel("gemini-2.0-flash", tools=tools)
chat = model.start_chat()
# ④ ユーザーメッセージを送信
response = chat.send_message("今何時ですか?")
# ⑤ モデルが Function Call を要求しているか確認
for part in response.parts:
if hasattr(part, "function_call"):
func_call = part.function_call
print(f"モデルが要求した関数: {func_call.name}")
print(f"引数: {dict(func_call.args)}")
# ⑥ 実際に関数を実行
result = get_current_time(**dict(func_call.args))
# ⑦ 実行結果をモデルに返す
final_response = chat.send_message(
genai.protos.Part(
function_response=genai.protos.FunctionResponse(
name=func_call.name,
response={"result": result}
)
)
)
print(f"\nAI の回答: {final_response.text}")実行すると以下のような出力になります:
モデルが要求した関数: get_current_time
引数: {}
AI の回答: 現在の時刻は 2026-04-20 09:00:00(Asia/Tokyo)です。
重要なのは ⑤ のステップです。モデルは「この関数を実行してください」という指示を返しているだけで、実際の実行はアプリ側の責任です。
実用的な例:複数ツールの同時定義
現実のアプリでは複数の関数を定義することが多いです。「計算機能」と「単位変換」を組み合わせた例を見てみます。
# multi_tool_example.py
import os
import google.generativeai as genai
from dotenv import load_dotenv
load_dotenv()
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
# ツール実装
def calculate(expression: str) -> dict:
"""数式を評価する(安全な式のみ)"""
allowed_chars = set("0123456789+-*/()., ")
if not all(c in allowed_chars for c in expression):
return {"error": "不正な文字が含まれています", "result": None}
try:
result = eval(expression) # 本番では safer eval を使うこと
return {"result": result, "expression": expression}
except Exception as e:
return {"error": str(e), "result": None}
def convert_units(value: float, from_unit: str, to_unit: str) -> dict:
"""単純な単位変換(km⇔miles, kg⇔lbs)"""
conversions = {
("km", "miles"): lambda v: v * 0.621371,
("miles", "km"): lambda v: v * 1.60934,
("kg", "lbs"): lambda v: v * 2.20462,
("lbs", "kg"): lambda v: v / 2.20462,
}
key = (from_unit.lower(), to_unit.lower())
if key in conversions:
result = conversions[key](value)
return {"result": round(result, 4), "from": f"{value} {from_unit}", "to": f"{result:.4f} {to_unit}"}
return {"error": f"{from_unit} → {to_unit} の変換は対応していません"}
# ツール名と関数のマッピング
TOOL_FUNCTIONS = {
"calculate": calculate,
"convert_units": convert_units,
}
tools = [
{
"function_declarations": [
{
"name": "calculate",
"description": "数学的な計算式を評価します",
"parameters": {
"type": "object",
"properties": {
"expression": {"type": "string", "description": "評価する数式(例: 2 + 3 * 4)"}
},
"required": ["expression"]
}
},
{
"name": "convert_units",
"description": "単位変換を行います(km⇔miles, kg⇔lbs)",
"parameters": {
"type": "object",
"properties": {
"value": {"type": "number", "description": "変換する値"},
"from_unit": {"type": "string", "description": "変換元の単位"},
"to_unit": {"type": "string", "description": "変換先の単位"}
},
"required": ["value", "from_unit", "to_unit"]
}
}
]
}
]
def chat_with_tools(user_message: str) -> str:
"""ツール付きチャットの汎用関数"""
model = genai.GenerativeModel("gemini-2.0-flash", tools=tools)
chat = model.start_chat()
response = chat.send_message(user_message)
# Function Call のループ処理(複数回呼ばれる可能性もある)
max_iterations = 5
for _ in range(max_iterations):
func_calls = [p for p in response.parts if hasattr(p, "function_call")]
if not func_calls:
break # Function Call がなければ最終回答
# 全ての Function Call を実行して結果を返す
function_responses = []
for part in func_calls:
fc = part.function_call
func = TOOL_FUNCTIONS.get(fc.name)
if func:
result = func(**dict(fc.args))
else:
result = {"error": f"Unknown function: {fc.name}"}
function_responses.append(
genai.protos.Part(
function_response=genai.protos.FunctionResponse(
name=fc.name,
response={"result": result}
)
)
)
response = chat.send_message(function_responses)
return response.text
# テスト
print(chat_with_tools("42 * 1.08 の計算と、その結果を km から miles に変換して"))
# 期待: 計算結果 45.36 を miles 変換した 28.18 miles が返されるmax_iterations のループが重要です。Gemini がFunction Callを複数回要求する場合(例:計算してから変換)、このループで処理します。上限を設けないと無限ループになる可能性があるので、実際のプロダクトでは必ずガードを入れてください。
Antigravity でのデバッグのコツ
Function Calling でつまずきやすいのが「なぜ関数が呼ばれないのか」という問題です。Antigravity のデバッグパネルを使ったトラブルシューティングの方法を紹介します。
レスポンスの中身を詳しく見るには:
# デバッグ用:レスポンスの構造を出力
def debug_response(response):
print("--- レスポンス構造 ---")
print(f"候補数: {len(response.candidates)}")
for i, candidate in enumerate(response.candidates):
print(f"候補 {i}: finish_reason={candidate.finish_reason}")
for j, part in enumerate(candidate.content.parts):
if hasattr(part, "text"):
print(f" Part {j}: テキスト = {part.text[:100]}")
elif hasattr(part, "function_call"):
print(f" Part {j}: Function Call = {part.function_call.name}({dict(part.function_call.args)})")finish_reason が STOP なら通常終了、MAX_TOKENS ならトークン上限超過です。関数が呼ばれない場合は、ツール定義の description が曖昧すぎることが多いです。「いつ、どんな場合に使う関数か」を具体的に書くとモデルが適切に判断しやすくなります。
全体を振り返って:次のステップへ
Function Calling を覚えると、「AI + リアルデータ」のアプリが作れるようになります。今回作ったコードをベースに、まず手元にある API(天気、地図、自社DB)との連携を試してみてください。
Gemini API の Function Calling に関しては、Google GenAI SDK for Python の公式ドキュメントに多くのサンプルがあります。特に tool_config を使った「特定のツールだけ強制的に呼ぶ」設定は、チャットボットの動作を安定させる際に役立ちます。
Python で AI アプリ開発