import asyncioasync def handle_parallel_tool_calls(response, available_functions: dict): """複数のFunction Callを並列処理する""" parts = response.candidates[0].content.parts # Function Callを全て抽出 function_calls = [ part.function_call for part in parts if hasattr(part, 'function_call') and part.function_call ] if not function_calls: return None # 並列実行 async def execute_function(fc): func = available_functions.get(fc.name) if func: if asyncio.iscoroutinefunction(func): return await func(**dict(fc.args)) else: return func(**dict(fc.args)) return {"error": f"Function {fc.name} not found"} results = await asyncio.gather(*[execute_function(fc) for fc in function_calls]) # 結果をFunction Responseとして整形 function_responses = [ { "function_response": { "name": fc.name, "response": result } } for fc, result in zip(function_calls, results) ] return function_responses# 使用例async def main(): model = genai.GenerativeModel("gemini-2.0-flash", tools=tools) # 複数情報を同時に必要とする質問 response = model.generate_content( "iPhone 15 Pro の在庫状況と、3万円以下のスマートフォンのおすすめを同時に教えてください" ) available_functions = { "search_products": search_products, "get_inventory": get_inventory, } function_responses = await handle_parallel_tool_calls(response, available_functions) if function_responses: # 全ての結果をGeminiに返す final_response = model.generate_content([ "iPhone 15 Pro の在庫状況と、3万円以下のスマートフォンのおすすめを同時に教えてください", response.candidates[0].content, {"role": "user", "parts": function_responses} ]) print(final_response.text)asyncio.run(main())
エラー処理とリトライ戦略
ツールの実行が失敗した場合の堅牢なエラー処理を実装します:
from dataclasses import dataclassfrom typing import Any, Optionalimport time@dataclassclass ToolResult: success: bool data: Optional[Any] = None error: Optional[str] = Nonedef execute_with_retry( func, args: dict, max_retries: int = 3, backoff_factor: float = 1.5) -> ToolResult: """リトライ付きのツール実行""" last_error = None for attempt in range(max_retries): try: result = func(**args) return ToolResult(success=True, data=result) except ConnectionError as e: last_error = str(e) wait_time = backoff_factor ** attempt print(f"接続エラー (試行 {attempt + 1}/{max_retries}): {wait_time:.1f}秒後にリトライ") time.sleep(wait_time) except ValueError as e: # バリデーションエラーはリトライしない return ToolResult(success=False, error=f"引数エラー: {str(e)}") except Exception as e: last_error = str(e) if attempt == max_retries - 1: break return ToolResult(success=False, error=f"最大リトライ回数超過: {last_error}")def safe_tool_handler(fc, available_functions: dict) -> dict: """エラーセーフなツールハンドラー""" func = available_functions.get(fc.name) if not func: return {"error": f"未定義のツール: {fc.name}"} result = execute_with_retry(func, dict(fc.args)) if result.success: return result.data else: # エラーをGeminiに伝えて代替案を提案させる return { "error": result.error, "fallback_suggestion": "情報を取得できませんでした。別の方法で回答を試みます。" }
ツールチェーン:複数ステップの自動化
複数のツールを連鎖して実行するエージェントパターン:
def run_tool_chain(initial_query: str, max_steps: int = 5) -> str: """ 複数ステップのツールチェーンを自動実行するエージェントループ """ model = genai.GenerativeModel("gemini-2.0-flash", tools=tools) conversation = [{"role": "user", "parts": [initial_query]}] for step in range(max_steps): response = model.generate_content(conversation) parts = response.candidates[0].content.parts # テキスト応答の場合は終了 if all(hasattr(p, 'text') for p in parts): return parts[0].text # Function Callsを処理 tool_responses = [] for part in parts: if hasattr(part, 'function_call') and part.function_call: fc = part.function_call result = safe_tool_handler(fc, available_functions) tool_responses.append({ "function_response": { "name": fc.name, "response": result } }) # 会話履歴に追加してループ継続 conversation.append({ "role": "model", "parts": parts }) conversation.append({ "role": "user", "parts": tool_responses }) return "最大ステップ数に達しました"# 使用例: 複数ステップが必要なタスクresult = run_tool_chain( "在庫が10個以上ある3万円以下のスマートフォンを探して、最安値のものを注文してください")print(result)
ここまでのツールチェーンは、会話履歴の管理・ツールの実行・ループ終了判定をすべて自分のコードで握る方式でした。2026年6月、Gemini API に Managed Agents が公開プレビューとして加わり、もう一つの選択肢が現実的になりました。これは Google ホストの隔離 Linux サンドボックス内で、計画・推論・コード実行・ファイル操作・ブラウジングまでを自律的・ステートフルにこなすエージェントです。