こんにちは、HolySheep AI(今すぐ登録)のシニアAI統合エンジニア、田中です。本日はGemini 2.5 ProのFunction Calling機能を活用したツール呼び出しの自動化ワークフローについて、 실무で遭遇した具体的なエラーケースめながら詳しく解説します。
私は2024年末から複数のプロダクション環境でGemini 2.5 Proを運用していますが、Function Callingの設定で{" ConnectionError: timeout"}や{"401 Unauthorized"}に直面した経験が很多人的と思います。本記事ではこれらの厄介なエラーを解決しながら、高效な自動化ワークフローを構築する方法を紹介します。
Function Callingとは?なぜ重要か
Function Calling(関数呼び出し)は、LLMが外部APIやデータベースと安全に連携するための仕組みです。Gemini 2.5 Proでは{"tools"}パラメータを使用して、モデルが呼び出すべき関数を定義できます。これにより、...
- リアルタイム天気情報の取得
- データベースのクエリ実行
- 外部APIとの 안전한連携
- 複雑なワークフローの自动化
が可能になります。HolySheep AIのGemini 2.5 ProはTTOK辺り$3.50という競爭力のある 가격으로 제공되며、レートは¥1=$1(公式¥7.3=$1の85%節約)でご利用いただけます。
環境構築と前提条件
必要なライブラリ
# 必要なパッケージのインストール
pip install openai httpx python-dotenv
バージョン確認
python --version # Python 3.9以上を推奨
pip list | grep -E "openai|httpx"
プロジェクト構造
project/
├── .env # APIキー管理
├── tools/
│ ├── __init__.py
│ ├── weather.py # 天気情報取得ツール
│ ├── database.py # データベース操作ツール
│ └── notification.py # 通知ツール
├── workflow.py # メイン自動化スクリプト
└── requirements.txt
実践的なFunction Calling実装
ここからは私が実際に運用しているワークフローを基に、段階的に実装方法を説明します。
Step 1: 基本設定とAPI接続
import os
from openai import OpenAI
from dotenv import load_dotenv
.envファイルからAPIキー読み込み
load_dotenv()
HolySheep AI設定
client = OpenAI(
api_key=os.getenv("HOLYSHEEP_API_KEY"),
base_url="https://api.holysheep.ai/v1" # 必ずこのエンドポイントを使用
)
接続確認
def test_connection():
try:
response = client.chat.completions.create(
model="gemini-2.5-pro",
messages=[{"role": "user", "content": "hello"}],
max_tokens=10
)
print(f"✅ 接続成功: {response.choices[0].message.content}")
return True
except Exception as e:
print(f"❌ 接続エラー: {type(e).__name__}: {e}")
return False
if __name__ == "__main__":
test_connection()
私は最初にここで{"ConnectionError: timeout"}に遭遇しました。原因是プロキシ設定の缺失です。
Step 2: 関数の定義(Tools宣言)
# tools/weather.py
"""天気情報取得ツール"""
TOOLS = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "指定した都市の現在、天気情報を取得します",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "都市名(例:東京、ニューヨーク)"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度の単位"
}
},
"required": ["location"]
}
}
},
{
"type": "function",
"function": {
"name": "get_forecast",
"description": "指定した都市の5日間予報を取得します",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "都市名"
}
},
"required": ["location"]
}
}
}
]
関数実装
def get_weather(location: str, unit: str = "celsius") -> dict:
"""実際は外部API(OpenWeatherMap等)を呼び出す"""
# モックデータ
return {
"location": location,
"temperature": 22 if unit == "celsius" else 72,
"condition": "晴れ",
"humidity": 65,
"timestamp": "2025-01-15T10:30:00Z"
}
def get_forecast(location: str) -> dict:
"""5日間予報を取得"""
return {
"location": location,
"forecast": [
{"day": 1, "temp": 22, "condition": "晴れ"},
{"day": 2, "temp": 18, "condition": "曇り"},
{"day": 3, "temp": 15, "condition": "雨"},
{"day": 4, "temp": 20, "condition": "晴れ"},
{"day": 5, "temp": 24, "condition": "快晴"}
]
}
Step 3: 完全自动化ワークフロー
import json
from tools.weather import TOOLS, get_weather, get_forecast
def execute_workflow(user_query: str):
"""Function Callingを活用した自動化ワークフロー"""
messages = [
{"role": "system", "content": "あなたは有用的な天気アシスタントです。"},
{"role": "user", "content": user_query}
]
# 最初のリクエスト
response = client.chat.completions.create(
model="gemini-2.5-pro",
messages=messages,
tools=TOOLS,
tool_choice="auto"
)
assistant_message = response.choices[0].message
messages.append(assistant_message)
# 関数呼び出しがある場合
while assistant_message.tool_calls:
for tool_call in assistant_message.tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
print(f"🔧 関数呼び出し: {function_name}")
print(f" 引数: {arguments}")
# 関数の実行
if function_name == "get_weather":
result = get_weather(**arguments)
elif function_name == "get_forecast":
result = get_forecast(**arguments)
else:
result = {"error": "不明な関数"}
# 結果をメッセージに追加
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})
# 結果を受けて再度LLMに送信
response = client.chat.completions.create(
model="gemini-2.5-pro",
messages=messages,
tools=TOOLS
)
assistant_message = response.choices[0].message
messages.append(assistant_message)
return assistant_message.content
実行例
if __name__ == "__main__":
result = execute_workflow("東京の今日の天気と明日の予報を教えてください")
print(f"\n📝 最终回答:\n{result}")
実践事例:マルチツール連携ワークフロー
ここからは私が実際に構築した、より複雑なマルチツール連携の例を紹介します。
# tools/database.py
"""データベース操作ツール"""
DB_TOOLS = [
{
"type": "function",
"function": {
"name": "query_orders",
"description": "注文データを検索します",
"parameters": {
"type": "object",
"properties": {
"customer_id": {"type": "string"},
"status": {
"type": "string",
"enum": ["pending", "shipped", "delivered"]
},
"limit": {"type": "integer", "default": 10}
}
}
}
},
{
"type": "function",
"function": {
"name": "update_order_status",
"description": "注文ステータスを更新します",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"new_status": {"type": "string"}
},
"required": ["order_id", "new_status"]
}
}
}
]
def query_orders(customer_id: str = None, status: str = None, limit: int = 10):
"""注文データのクエリ(実際はSQL/ORMを使用)"""
return {
"orders": [
{"order_id": "ORD-001", "customer": "佐藤太郎", "total": 15000, "status": "shipped"},
{"order_id": "ORD-002", "customer": "鈴木花子", "total": 8500, "status": "pending"}
],
"total_count": 2
}
def update_order_status(order_id: str, new_status: str):
"""注文ステータス更新"""
return {"success": True, "order_id": order_id, "new_status": new_status}
# complete_automation.py
"""完全自动化ワークフロー - 天気×注文管理×通知"""
from openai import OpenAI
import json
import os
from datetime import datetime
client = OpenAI(
api_key=os.getenv("HOLYSHEEP_API_KEY"),
base_url="https://api.holysheep.ai/v1"
)
全ツール定義
ALL_TOOLS = [
# 天気ツール
{
"type": "function",
"function": {
"name": "get_weather",
"description": "都市の天気取得",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location"]
}
}
},
# 注文管理ツール
{
"type": "function",
"function": {
"name": "query_orders",
"description": "注文検索",
"parameters": {
"type": "object",
"properties": {
"customer_id": {"type": "string"},
"status": {"type": "string", "enum": ["pending", "shipped", "delivered"]}
}
}
}
},
{
"type": "function",
"function": {
"name": "send_notification",
"description": "ユーザーに通知を送信",
"parameters": {
"type": "object",
"properties": {
"user_id": {"type": "string"},
"message": {"type": "string"},
"channel": {"type": "string", "enum": ["email", "sms", "push"]}
},
"required": ["user_id", "message"]
}
}
}
]
def send_notification(user_id: str, message: str, channel: str = "email"):
"""通知送信(実際はSendGrid/Twilio等を使用)"""
print(f"📧 通知送信: {user_id} via {channel}")
return {"success": True, "sent_at": datetime.now().isoformat()}
def run_automated_workflow(query: str):
"""自动化ワークフロー実行"""
messages = [
{"role": "system", "content": """あなたは注文管理アシスタントです。
ユーザーの要求に応じて、ツールを呼び出してください。
天気によって配送状況を判断し、適切な通知を行います。"""},
{"role": "user", "content": query}
]
max_iterations = 10
iteration = 0
while iteration < max_iterations:
iteration += 1
response = client.chat.completions.create(
model="gemini-2.5-pro",
messages=messages,
tools=ALL_TOOLS,
tool_choice="auto"
)
choice = response.choices[0].message
messages.append(choice)
if not choice.tool_calls:
print(f"✅ ワークフロー完了({iteration}イテレーション)")
return choice.content
# ツール呼び出し処理
for tool_call in choice.tool_calls:
fn_name = tool_call.function.name
fn_args = json.loads(tool_call.function.arguments)
print(f"🔧 実行: {fn_name}({fn_args})")
# 関数実行
if fn_name == "get_weather":
result = {"temperature": 22, "condition": "晴れ", "location": fn_args.get("location")}
elif fn_name == "query_orders":
result = {"orders": [{"id": "123", "status": "shipped"}]}
elif fn_name == "send_notification":
result = send_notification(**fn_args)
else:
result = {"error": "Unknown function"}
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})
実行
if __name__ == "__main__":
result = run_automated_workflow(
"東京都墨田区在住の客户の保留中注文を確認し、"
"天気が悪い場合は配送を延期する旨を通知してください"
)
print(f"\n📝 結果:\n{result}")
エラー處理とデバッグ技巧
レイテンシ測定ユーティリティ
import time
import httpx
def measure_latency(endpoint: str, api_key: str) -> dict:
"""APIレイテンシ測定"""
latencies = []
for i in range(5):
start = time.time()
try:
with httpx.Client(timeout=30.0) as client:
response = client.post(
f"{endpoint}/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
},
json={
"model": "gemini-2.5-pro",
"messages": [{"role": "user", "content": "ping"}],
"max_tokens": 5
}
)
elapsed = (time.time() - start) * 1000
latencies.append(elapsed)
print(f" Request {i+1}: {elapsed:.1f}ms (status: {response.status_code})")
except Exception as e:
print(f" Request {i+1}: ERROR - {e}")
return {
"avg": sum(latencies) / len(latencies),
"min": min(latencies),
"max": max(latencies)
}
測定実行
metrics = measure_latency(
endpoint="https://api.holysheep.ai/v1",
api_key=os.getenv("HOLYSHEEP_API_KEY")
)
print(f"\n📊 レイテンシ結果:")
print(f" 平均: {metrics['avg']:.1f}ms")
print(f" 最小: {metrics['min']:.1f}ms")
print(f" 最大: {metrics['max']:.1f}ms")
よくあるエラーと対処法
エラー1: {"ConnectionError: timeout"}
症状:リクエスト送信時にタイムアウトエラーが発生する
原因:ネットワークプロキシの設定缺失、またはAPIエンドポイントへのルートが不安定
# 解決策:httpx Clientにタイムアウトとプロキシ設定を追加
from httpx import Client, Timeout, Proxy
カスタムクライアント設定
custom_client = OpenAI(
api_key=os.getenv("HOLYSHEEP_API_KEY"),
base_url="https://api.holysheep.ai/v1",
http_client=Client(
timeout=Timeout(60.0, connect=10.0), # 合計60秒、接続10秒
proxies={ # 必要に応じてプロキシ設定
"http://": os.getenv("HTTP_PROXY"),
"https://": os.getenv("HTTPS_PROXY")
}
)
)
リトライロジック付きリクエスト
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def robust_request(messages, tools):
return custom_client.chat.completions.create(
model="gemini-2.5-pro",
messages=messages,
tools=tools
)
エラー2: {"401 Unauthorized"}
症状:認証エラーでAPI呼び出しが拒否される
原因:APIキーが無効または期限切れ、または環境変数の読み込み失敗
# 解決策:APIキーの検証と.env設定の確認
import os
from pathlib import Path
def validate_api_key():
"""APIキーの妥当性チェック"""
# 1. 環境変数または.envファイルからキー取得
api_key = os.getenv("HOLYSHEEP_API_KEY")
if not api_key:
# .envファイルが見つからない場合
env_path = Path(__file__).parent / ".env"
if env_path.exists():
from dotenv import load_dotenv
load_dotenv(env_path)
api_key = os.getenv("HOLYSHEEP_API_KEY")
if not api_key:
raise ValueError("""
❌ HOLYSHEEP_API_KEYが設定されていません!
設定手順:
1. https://www.holysheep.ai/register でアカウント作成
2. ダッシュボードからAPIキーを取得
3. .envファイルに HOLYSHEEP_API_KEY=your_key を追加
""")
# 2. キー形式チェック(sk-で始まる必要がある)
if not api_key.startswith("sk-"):
raise ValueError(f"❌ APIキー形式が無効です: {api_key[:10]}...")
# 3. 接続テスト
client = OpenAI(api_key=api_key, base_url="https://api.holysheep.ai/v1")
try:
client.chat.completions.create(
model="gemini-2.5-pro",
messages=[{"role": "user", "content": "test"}],
max_tokens=5
)
print("✅ APIキー認証成功")
except Exception as e:
raise ValueError(f"❌ 認証失敗: {e}")
return api_key
エラー3: {"InvalidRequestError: tools parameter format error"}
症状:Function Calling使用時にツール定義の形式エラー
原因:toolsパラメータの構造がGeminiの要件を満たしていない
# 解決策:ツール定義の正しい形式を定義
def create_valid_tools():
"""Gemini 2.5 Pro互換のツール定義"""
# ❌ 間違い:typeフィールドがない
bad_tools = [{"function": {"name": "test", ...}}]
# ✅ 正しい形式
good_tools = [
{
"type": "function", # ← 必ず必要
"function": {
"name": "get_weather",
"description": "指定した都市の天気を取得します。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "都市名(例:東京)"
}
},
"required": ["location"]
}
}
}
]
# ツール定義のバリデーション
def validate_tool(tool):
assert "type" in tool, "toolにはtypeフィールドが必要です"
assert tool["type"] == "function", "typeは'function'である必要があります"
assert "function" in tool, "function定義がありません"
fn = tool["function"]
assert "name" in fn, "関数名が必要です"
assert "parameters" in fn, "parameters定義が必要です"
assert fn["parameters"]["type"] == "object", "parametersのtypeは'object'"
return True
for tool in good_tools:
validate_tool(tool)
print("✅ ツール定義バリデーション通過")
return good_tools
エラー4: {"RateLimitError: rate limit exceeded"}
症状:リクエスト頻度制限に到達し、しばらく利用不可
原因:短時間での大量リクエスト
# 解決策:レート制限への対応とリクエスト間隔制御
import time
import asyncio
from collections import deque
class RateLimiter:
"""トークンベースのレ이트リミッター"""
def __init__(self, max_requests: int = 60, window_seconds: int = 60):
self.max_requests = max_requests
self.window = window_seconds
self.requests = deque()
def wait_if_needed(self):
"""必要に応じて待機"""