mcpField
A
ag2-add-custom-tool

ag2-add-custom-tool

github.com/ag2aiエージェントワークフロー1268 回インストール13 週間前に更新
リポジトリ

ag2-add-custom-tool について

AG2 の開発者向けに、Agent へ Python ツールを追加する方法を説明するスキルです。スキーマ生成、同期・非同期実行、型付き結果、入力検証、依存性注入を扱います。

主な機能

  1. 01注釈からツールスキーマを生成
  2. 02同期・非同期関数に対応
  3. 03Pydantic Field による入力検証
  4. 04型付き・マルチモーダル結果の返却
  5. 05実行時依存関係の注入

活用シーン

  1. 01Agent から API を呼び出す
  2. 02ツール経由でデータベースを照会
  3. 03画像や構造化データを返す
  4. 04最終ツール結果でターンを終了

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ag2ai/ag2-skills --skill ag2-add-custom-tool

インストール後、Claude Code で/ag2-add-custom-toolを入力して読み込まれたか確認します。

同梱ファイルreferences/dependency_injection.md

詳細

カスタム Python ツールを追加する

使用するタイミング

ユーザーが Agent に実世界での操作を実行させたい場合に使用します。たとえば、API の呼び出し、データベースへのクエリ、計算、画像の返却などです。提供済みのツール(ウェブ検索、コード実行、シェル)を使いたい場合は、代わりに ag2-use-builtin-tools と ag2-shell-tool を参照してください。

60 秒でできる手順

from ag2 import Agent, tool
from ag2.config import OpenAIConfig

@tool
def calculate_shipping_cost(destination: str, weight_kg: float) -> str:
    """Calculates shipping cost for a package to a destination."""
    return "$15.00"

agent = Agent(
    "shipping",
    prompt="Use tools when helpful.",
    config=OpenAIConfig(model="gpt-4o-mini"),
    tools=[calculate_shipping_cost],
)

@tool デコレーターは、関数のシグネチャ、型ヒント、docstring から LLM 向けのスキーマを生成します。docstring は LLM が目にする説明です。人間だけでなく、LLM が読むことを想定して記述してください。

デコレーターのない通常の関数を tools=[...] に渡すこともできます。AG2 が自動的にラップします。

def get_weather(location: str) -> str:
    """Returns the current weather for a given location."""
    return "Sunny, 22°C"

agent = Agent("weather", tools=[get_weather])

また、@agent.tool を使って既存のエージェントにツールを追加できます。

agent = Agent("calc")

@agent.tool
def multiply(a: int, b: int) -> int:
    """Multiplies two integers and returns the result."""
    return a * b

同期と非同期

def と async def のどちらもサポートされています。デフォルトでは、同期ツールはスレッド内で実行されるため、ブロッキング I/O によってイベントループが停止することはありません。非常に高速な純粋な Python ツールでは、これを無効にできます。

@tool(sync_to_thread=False)
def format_name(first: str, last: str) -> str:
    """Formats a full name."""
    return f"{last.upper()}, {first.capitalize()}"

ネイティブの非同期ツールは、メインのイベントループで直接実行されます。

import aiohttp

@tool
async def fetch(url: str) -> str:
    """Fetches a URL with aiohttp."""
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as r:
            return await r.text()

Pydantic Field で入力を検証する

Annotated[T, Field(...)] を使って、LLM に厳密な範囲を指定します。フレームワークはこれらを JSON Schema に反映します。

from typing import Annotated
from pydantic import Field
from ag2 import tool

@tool
def set_temperature(
    temp: Annotated[int, Field(description="Target temperature.", ge=10, le=30)],
    mode: Annotated[str, Field(description="Mode.", pattern="^(heat|cool|auto)$")],
) -> str:
    """Sets the thermostat."""
    return f"Set to {temp}°C in {mode} mode."

デコレーターでツール名と説明を上書きすることもできます。

@tool(name="custom_math_tool", description="Performs advanced math.")
def math_op(a: int, b: int) -> int:
    return a + b

型付きの Input / ToolResult を返す

通常の str 型の戻り値は、自動的に TextInput でラップされます。より複雑なペイロードを返すには、Input のサブタイプを返すか、ToolResult と組み合わせてください。

from ag2 import DataInput, ImageInput, TextInput, ToolResult, tool

@tool
def get_status(task_id: str) -> TextInput:
    return TextInput(f"Task {task_id} is in progress.")

@tool
def get_user_profile(user_id: str) -> DataInput:
    return DataInput({"id": user_id, "name": "Alice", "role": "admin"})

@tool
def fetch_chart(chart_id: str) -> ImageInput:
    return ImageInput(f"https://charts.example.com/{chart_id}.png")

@tool
def analyze_product(product_id: str) -> ToolResult:
    """Returns image + structured metadata in one tool call."""
    return ToolResult(
        ImageInput(f"https://cdn.example.com/products/{product_id}.jpg"),
        {"id": product_id, "name": "Widget Pro", "stock": 42},
    )

任意の形式の生バイト列には、BinaryInput(data=..., media_type="application/pdf") を使用してください。

final=True でターンを早期に終了する

ツールが最終的な回答を正確に把握している場合は、余分な LLM との往復を省略できます。

from ag2 import ToolResult, tool

@tool
def handoff_to_human(ticket_id: str) -> ToolResult:
    """Escalates and returns the final user-facing message verbatim."""
    return ToolResult(f"Ticket {ticket_id} was escalated.", final=True)

final=True の ToolResult に含められるパートは、TextInput または DataInput のいずれか、ちょうど 1 つだけです。

依存性注入(Context / Inject / Variable / Depends)

ツールは、LLM に公開せずに実行時の値を取得できます。全項目が記載された表については references/dependency_injection.md を参照してください。基本的な使い方は次のとおりです。

from typing import Annotated
from ag2 import Context, Inject, Variable, tool

@tool
def query_db(query: str, ctx: Context) -> str:
    """Runs a SQL query."""
    db = ctx.dependencies["db"]
    return db.execute(query)

@tool
def fetch(url: str, http: Annotated[object, Inject("http_session")]) -> str:
    """Fetches with a shared HTTP session."""
    return http.get(url).text

@tool
def send(text: str, api_key: Annotated[str, Variable()]) -> str:
    """Sends a message via the configured channel."""
    ...

Inject のアノテーションは、LLM 向けのスキーマから除外されます。これは内部的な注入の仕組みです。

詳細情報

  • references/dependency_injection.md — Context、Inject、Variable、Depends の違い、デフォルト値、ファクトリー、可変性、オーバーライド。
  • website/docs/user-guide/tools/tools.mdx — 合成された JSON Schema を含む、@tool の詳細リファレンス。
  • website/docs/user-guide/depends.mdx — Depends のライフサイクル、yield ベースの後処理、キャッシュ、テスト時のオーバーライド。
  • website/docs/user-guide/multimodal/inputs.mdx — Input ファクトリーの階層とプロバイダーのサポート一覧。
  • website/docs/user-guide/tools/toolkits.mdx — 関連するツールを再利用可能な Toolkit にまとめる方法。
  • website/docs/user-guide/tools/tool_middleware.mdx — 単一のツールを対象とする非同期フック(検証、マスキング、承認。ag2-hitl も参照してください)。

よくある問題

  • 曖昧な docstring — LLM はこれを使って、ツールを「いつ」呼び出すかを判断します。「配送先と重量に基づいて送料を計算します」は、「送料計算」よりもはるかに適切です。
  • 型ヒントがない — 型ヒントがないと、フレームワークは有用な JSON Schema を生成できず、LLM がツールをまったく呼び出さない可能性があります。
  • イベントループをブロックする — CPU 負荷の高い処理やネットワークアクセスを行う同期ツールを def で記述し、sync_to_thread=False を渡すと、ループがブロックされます。デフォルトの動作(スレッドでの実行)は安全です。無効にするのは、負荷の軽い純粋な Python 処理に限ってください。
  • ツール内の関数レベルのインポート — リポジトリの規約で禁止されています。import はモジュールの先頭に移動してください。
  • ツール本体内での関数のネスト定義 — これも禁止されています(呼び出しのたびに関数が再生成されます)。
  • 構造化データを返すつもりで dict を直接返す — DataInput(...) でラップしてください。そうすることで、フレームワークはテキストに変換せず、構造化データとして扱います。
  • final=True にはパートがちょうど 1 つ必要なことを忘れる — 複数の Input を final=True で組み合わせると、例外が発生します。

コメント