OpenAI Agents SDKにおけるツール制御

1.エージェントとツールの動作制御

エージェントがツールを実行した後の振る舞いは、Agent.tool_use_behavior パラメータで制御する。主なオプションは以下のとおり。

  • run_llm_again: ツール実行後、その出力をLLMに戻す。LLMはツールの結果を解釈し、最終的な回答を作成するか、別のツールを呼び出すかを判断する(デフォルト)。
  • stop_on_first_tool: 最初に呼び出されたツールの出力を最終回答として扱い、LLMへの再問い合わせを行わずに処理を終了する。
  • agent.StopAtTools.stop_at_tool_names: 特定のツールが実行された場合のみ、実行を停止してその出力を返す。

特定のツールで実行を停止する例

データベースの照会結果や、法的な文言を含む証明書の発行など、LLMによる再構成(要約や言い換え)を介さずに、ツールの出力をそのままユーザーに見せたい場合に有効。

from agents import Agent, Runner, function_tool, StopAtTools

@function_tool
def issue_certificate(user_id: int) -> str:
    # 厳密なフォーマットが求められる証明書データを出力する想定
    return f"CERTIFICATE-ID: {user_id}-999 (Verified on 2026-01-04)"

# issue_certificateツールが呼ばれた時のみ、即座にレスポンスを返す設定
agent = Agent(
    name="Certificate Agent",
    instructions="ユーザーの証明書を発行せよ。",
    tools=[issue_certificate],
    tool_use_behavior=StopAtTools.stop_at_tool_names(["issue_certificate"])
)

2.Pydanticによる複雑なツール入力の定義

SDKは Pydantic をサポートしており、ツールの引数として複雑な構造化データを定義できる。これにより、単一の strint だけでなく、階層構造を持つデータやリストを受け取ることが可能になる。

(1) Pydanticを利用するメリット

  • 自動スキーマ生成: BaseModel を継承したクラスを引数に使うと、SDKが自動的に適切なJSON Schemaを生成してLLMに伝える。
  • 型安全とバリデーション: LLMが誤った型や不足したデータを渡した場合、SDK(Pydantic)が ValidationError をスローする。これにより、ハルシネーションによる不正なデータ入力からシステムを保護できる。

(2) 構造化データを用いた実装例

例えば、複数の項目を一度に登録するようなケースで有効。

from pydantic import BaseModel
from typing import List
from agents import function_tool

class InventoryItem(BaseModel):
    item_id: str
    quantity: int
    category: str

@function_tool
def update_inventory(items: List[InventoryItem]) -> str:
    """在庫リストを一括更新する"""
    # 内部システムへの登録ロジック
    return f"{len(items)}件のアイテムを更新した。"

3.算術演算ツールと強制実行

LLMは複雑な計算(算術演算)においてハルシネーションを起こしやすい。信頼性の高いシステムを構築するには、計算をLLMに任せず、専用のツールを実行させることが推奨される。さらに、tool_choice="required" を設定することで、LLMに回答を生成する前に必ずツールを使うことを強制できる。

計算ツールの強制実行例

積立貯蓄のシミュレーション計算を例にする。

from agents import Agent, Runner, function_tool, ModelSettings

@function_tool
def calculate_savings(monthly_amount: float, years: int, annual_rate: float) -> str:
    """毎月の積立額から将来の資産額を計算する"""
    rate = annual_rate / 100 / 12
    months = years * 12
    total = monthly_amount * ((1 + rate)**months - 1) / rate
    return f"推定資産額: ${total:,.2f}"

savings_agent = Agent(
    name="Savings Advisor",
    instructions="あなたは貯蓄アドバイザーだ。計算ツールを必ず使え。",
    tools=[calculate_savings],
    # ツール実行後にLLMを再度動かさず、計算結果をそのまま返す設定
    tool_use_behavior="stop_on_first_tool",
    model_settings=ModelSettings(
        tool_choice="required" # ツールの使用を強制
    )
)

# 実行
result = Runner.run_sync(savings_agent, "毎月500ドルを6%の利率で20年積み立てるといくらになる?")
print(result.final_output)

この構成により、LLMが勝手に計算を行うリスクを排除し、プログラムによる正確な計算結果のみをユーザーに提供できるようになる。