claude-skills/

Anthropic公式スキル・プラグインの日本語ディレクトリ

last sync 22h ago
スキルOfficialdevelopment

🔧create-atomic-tool

プラグイン
atomic-agents

説明

`BaseTool[InSchema, OutSchema]` のサブクラスを構築します。 入出力スキーマ、`BaseToolConfig`、`run()`(および任意の `run_async()`)、環境変数ベースのシークレット管理、型付きの失敗出力に対応しています。 次のような場合に使用: ユーザーが「ツールを追加したい」「ツールを作成したい」「APIをツールとしてラップしたい」「`BaseTool` を構築したい」「計算機/検索/天気ツールを作りたい」と依頼した場合、または `/atomic-agents:create-atomic-tool` を実行した場合。

原文を表示

Build a `BaseTool[InSchema, OutSchema]` subclass — input/output schemas, `BaseToolConfig`, `run()` (and optional `run_async()`), env-driven secrets, typed failure outputs. Use when the user asks to "add a tool", "create a tool", "wrap an API as a tool", "build a `BaseTool`", "make a calculator/search/weather tool", or runs `/atomic-agents:create-atomic-tool`.

ユースケース

  • ツールを追加・作成したいとき
  • APIをツールとしてラップするとき
  • 計算機/検索/天気ツールを作るとき
  • BaseTool をサブクラス構築するとき

本文(日本語訳)

Atomic Agents ツールの作成

ツールとは、agentが呼び出せる決定論的な機能のことです。Atomic Agentsにおいて、すべてのツールは BaseTool[InSchema, OutSchema] のサブクラスとして実装され、型付きの run()(および省略可能な run_async())を持ちます。入出力スキーマは、LLMに対するツールのシグネチャとしての役割と、実行時のPydanticバリデーションとしての役割を兼ねています。

より詳細な情報(MCPとの相互運用、スタンドアロンパッケージとしての配布、高度なエラーパターンなど)については、../framework/references/tools.md を参照してください。 このスキルは実践的なアプローチを取ります: 明確化 → 実装 → 検証。


このスキルと上位の framework スキルの使い分け

  • このスキル: ユーザーが特定のツールを作成しようとしている場合 — APIのラッパー、計算機の作成、ページのスクレイピング、DBへのクエリなど。
  • framework スキル: Atomic Agentsに関する一般的な質問、またはツールの作成以外の作業をしている場合。

フェーズ 1 — 明確化

以下の項目をまとめて一つのメッセージで確認します:

  1. ツールは何をするものか? 一文で説明してください。これがクラスのdocstringになり、LLMのツール説明として使われます。
  2. 入力と出力。 名前、型、単位。不明な場合は、スキーマのペアを提案して確認します。
  3. 外部依存関係。 HTTP API? DB? ローカル計算のみ? HTTPの場合、認証方法は?(APIキーの環境変数、OAuth、なし)
  4. 同期・非同期、またはその両方? プロジェクトの他の部分が非同期の場合、またはI/Oバウンドの処理の場合は、run() と並行して run_async() も実装することを検討してください。
  5. 失敗のパターン。 レート制限、リソース未発見、ネットワークエラー — agentにどう見せるべきか? デフォルト: 例外を投げるのではなく、型付きの失敗出力を使用します。

すでにコンテキストから明らかな質問はスキップしてください。


フェーズ 2 — 計画

ファイルの配置と構造を簡潔なブロックで確認し、実装に進みます:

  • ファイル: <project>/tools/<tool_name>_tool.py(プロジェクト内ツール — ../framework/references/project-structure.md 参照)
  • スキーマ: <ToolName>Input<ToolName>Output、必要に応じて型付きの失敗シェイプ
  • Config: ツールにAPIキー、ベースURL、タイムアウト、リトライが必要な場合は <ToolName>Config(BaseToolConfig) を使用
  • 同期・非同期: どちらか一方、または両方を選択
  • エラーパターン: 型付き失敗出力(推奨)vs 例外送出(プログラマーエラーのみ)

フェーズ 3 — 実装

スケルトン — ローカル計算、configなし

from pydantic import Field
from atomic_agents import BaseIOSchema, BaseTool


class CalculatorInput(BaseIOSchema):
    """評価する算術式。"""
    expression: str = Field(..., description="Pythonスタイルの算術式。例: '2 + 2 * 3'")


class CalculatorOutput(BaseIOSchema):
    """式を評価した結果。"""
    result: float = Field(..., description="数値の結果。")


class CalculatorTool(BaseTool[CalculatorInput, CalculatorOutput]):
    """単純な算術式を安全に評価します。"""

    def run(self, params: CalculatorInput) -> CalculatorOutput:
        import ast, operator as op
        ops = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv}
        def ev(n):
            if isinstance(n, ast.Constant): return n.value
            if isinstance(n, ast.BinOp): return ops[type(n.op)](ev(n.left), ev(n.right))
            raise ValueError("unsupported")
        return CalculatorOutput(result=ev(ast.parse(params.expression, mode="eval").body))

スケルトン — HTTPバックエンド、configと型付き失敗あり

import os
import httpx
from typing import Literal, Optional
from pydantic import Field
from atomic_agents import BaseIOSchema, BaseTool, BaseToolConfig


class WeatherConfig(BaseToolConfig):
    api_key: str = Field(
        default_factory=lambda: os.environ.get("WEATHER_API_KEY", ""),
        description="天気サービスのAPIキー。",
    )
    base_url: str = Field(
        default="https://api.weather.example/v1",
        description="天気APIのベースURL。",
    )
    timeout: float = Field(default=15.0, ge=1.0, le=120.0, description="リクエストタイムアウト(秒)。")


class WeatherInput(BaseIOSchema):
    """現在の気象情報のリクエスト。"""
    city: str = Field(..., description="都市名。例: 'Brussels'")


class WeatherOutput(BaseIOSchema):
    """現在の気象情報、または型付きの失敗情報。"""
    status: Literal["ok", "error"] = Field(..., description="結果コード。")
    temperature_c: Optional[float] = Field(default=None, description="気温(摂氏)。")
    summary: Optional[str] = Field(default=None, description="人間が読めるサマリー。")
    error: Optional[str] = Field(default=None, description="status='error' の場合の失敗メッセージ。")


class WeatherTool(BaseTool[WeatherInput, WeatherOutput]):
    """天気APIから指定した都市の現在の気象情報を取得します。"""

    def __init__(self, config: WeatherConfig | None = None):
        super().__init__(config or WeatherConfig())

    def run(self, params: WeatherInput) -> WeatherOutput:
        cfg: WeatherConfig = self.config
        if not cfg.api_key:
            return WeatherOutput(status="error", error="WEATHER_API_KEY が設定されていません")
        try:
            r = httpx.get(
                f"{cfg.base_url}/current",
                params={"city": params.city},
                headers={"Authorization": f"Bearer {cfg.api_key}"},
                timeout=cfg.timeout,
            )
            r.raise_for_status()
        except httpx.HTTPError as e:
            return WeatherOutput(status="error", error=str(e))
        data = r.json()
        return WeatherOutput(status="ok", temperature_c=data["temp_c"], summary=data["summary"])

    async def run_async(self, params: WeatherInput) -> WeatherOutput:
        cfg: WeatherConfig = self.config
        if not cfg.api_key:
            return WeatherOutput(status="error", error="WEATHER_API_KEY が設定されていません")
        async with httpx.AsyncClient(timeout=cfg.timeout) as client:
            try:
                r = await client.get(
                    f"{cfg.base_url}/current",
                    params={"city": params.city},
                    headers={"Authorization": f"Bearer {cfg.api_key}"},
                )
                r.raise_for_status()
            except httpx.HTTPError as e:
                return WeatherOutput(status="error", error=str(e))
        data = r.json()
        return WeatherOutput(status="ok", temperature_c=data["temp_c"], summary=data["summary"])

厳守ルール

  • ジェネリクスのパラメータが実行時の型情報を保持します。input_schema / output_schema をクラス属性として別途定義しないでください — フレームワークが管理するプロパティを隠蔽してしまいます。
  • run() は辞書ではなく、出力スキーマのインスタンスを返してください。
  • シークレットは環境変数 / BaseToolConfig 経由で管理し、ハードコードは禁止です。
  • HTTPツールには必ずタイムアウトを設定してください。ツールはagentのリクエストパス上で実行されます。
  • 非同期フックは async def run_async です。arun ではありません — フレームワークは run_async を呼び出します。
  • レート制限、404、上流APIからのバリデーション拒否などの通常の失敗は、型付き失敗出力としてモデル化してください。raise はプログラマーエラーのためだけに使用します。

フェーズ 4 — agentへの組み込み

2つの統合パターンがあります(詳細は ../framework/references/tools.md を参照):

シングルツールagent — agentの出力スキーマがそのままツールの入力スキーマになります:

agent = AtomicAgent[UserQuery, WeatherInput](config=config)
tool = WeatherTool()

call = agent.run(UserQuery(question="weather in Brussels?"))
result = tool.run(call)

ルーターagent — agentがツール呼び出しスキーマの判別共用体(discriminated union)を通じてツールを選択します。 agentが2〜10個のツールを選択する場合に使用してください。 数十個以上の場合は、../framework/references/orchestration.md の検索+実行パターンを参照してください。


フェーズ 5 — 検証

uv run python -c "from <project>.tools.<tool_name>_tool import <ToolName>Tool, <ToolName>Input; t = <ToolName>Tool(); print(t.run(<ToolName>Input(...)))"
  • docstringに関するエラーでインポートが失敗する場合は、スキーマにdocstringを追加してください。
  • self.input_schemaNone の場合、ジェネリクスのパラメータが不足しています — class FooTool(BaseTool): ではなく class FooTool(BaseTool[FooInput, FooOutput]): と記述してください。

フェーズ 6 — 引き渡し

以下をユーザーに伝えます:

  • ツールの配置場所とインポート方法。
  • agentがそれをどう使うか(シングルツール or ルーターパターン)。
  • オプションの次のステップ:
    • このツールを呼び出すagentの作成 → create-atomic-agent スキル
    • ツールを中心としたマルチagentの配線 → ../framework/references/orchestration.md
    • MCPとの相互運用やツールの配布パッケージ化 → ../framework/references/tools.md

アンチパターン

  • class FooTool(BaseTool): にクラス属性として input_schema = ... を定義するパターン — ジェネリクスを使用してください: BaseTool[FooInput, FooOutput]
  • run() から OutputSchema(...) ではなく辞書やプリミティブ型を返すパターン
  • 通常の上流エラーで例外を送出するパターン — 型付き出力としてモデル化してください
  • HTTP / DBの呼び出しにタイムアウトを設定しないパターン
  • MCPTransportType.STREAMABLE_HTTP の使用 — 正しい値は HTTP_STREAM です
  • async def arun(...) の実装 — フレームワークは run_async を呼び出します

MCPとの相互運用、atomic download 向けのツールパッケージ化、高度なルーターパターンなど、より詳細な情報については ../framework/references/tools.md を参照してください。

原文(English)を表示

Create an Atomic Agents Tool

A tool is a deterministic capability an agent can invoke. In Atomic Agents, every tool is a BaseTool[InSchema, OutSchema] subclass with a typed run() (and optional run_async()). The input/output schemas double as the tool's signature for the LLM and as Pydantic validation at runtime.

For deep material (MCP interop, distributing as a standalone package, advanced error patterns), the authority is ../framework/references/tools.md. This skill is the action-oriented path: clarify → write → verify.

When this fires vs the umbrella framework skill

  • This skill: the user is creating a specific tool — wrapping an API, building a calculator, scraping a page, querying a DB.
  • framework skill: questions about Atomic Agents in general, or the user is doing something other than authoring a tool.

Phase 1 — Clarify

Bundle into one message:

  1. What does the tool do? One sentence. This becomes the class docstring and feeds the LLM's tool description.
  2. Inputs and outputs. Names, types, units. If unclear, propose a schema pair and confirm.
  3. External dependencies. HTTP API? DB? Local computation only? If HTTP, what auth (API key env var, OAuth, none)?
  4. Sync, async, or both? If the rest of the project is async or the call is I/O bound, plan a run_async() alongside run().
  5. Failure modes. Rate limits, not-found, network errors — how should the agent see them? Default: typed failure output, not raised exceptions.

Skip any question already answered in context.

Phase 2 — Plan

Confirm the location and shape in one short block, then proceed:

  • File: <project>/tools/<tool_name>_tool.py (in-project tool — see ../framework/references/project-structure.md).
  • Schemas: <ToolName>Input, <ToolName>Output, optionally a typed failure shape.
  • Config: <ToolName>Config(BaseToolConfig) if the tool needs API keys, base URLs, timeouts, retries.
  • Sync vs async: pick one or both.
  • Error pattern: typed failure output (preferred) vs raise (only for programmer error).

Phase 3 — Implement

Skeleton — local computation, no config

from pydantic import Field
from atomic_agents import BaseIOSchema, BaseTool


class CalculatorInput(BaseIOSchema):
    """Arithmetic expression to evaluate."""
    expression: str = Field(..., description="Python-style arithmetic, e.g. '2 + 2 * 3'.")


class CalculatorOutput(BaseIOSchema):
    """Result of evaluating the expression."""
    result: float = Field(..., description="Numeric result.")


class CalculatorTool(BaseTool[CalculatorInput, CalculatorOutput]):
    """Evaluate simple arithmetic expressions safely."""

    def run(self, params: CalculatorInput) -> CalculatorOutput:
        import ast, operator as op
        ops = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv}
        def ev(n):
            if isinstance(n, ast.Constant): return n.value
            if isinstance(n, ast.BinOp): return ops[type(n.op)](ev(n.left), ev(n.right))
            raise ValueError("unsupported")
        return CalculatorOutput(result=ev(ast.parse(params.expression, mode="eval").body))

Skeleton — HTTP-backed, with config and typed failure

import os
import httpx
from typing import Literal, Optional
from pydantic import Field
from atomic_agents import BaseIOSchema, BaseTool, BaseToolConfig


class WeatherConfig(BaseToolConfig):
    api_key: str = Field(
        default_factory=lambda: os.environ.get("WEATHER_API_KEY", ""),
        description="API key for the weather service.",
    )
    base_url: str = Field(
        default="https://api.weather.example/v1",
        description="Base URL for the weather API.",
    )
    timeout: float = Field(default=15.0, ge=1.0, le=120.0, description="Request timeout (s).")


class WeatherInput(BaseIOSchema):
    """A request for current weather conditions."""
    city: str = Field(..., description="City name, e.g. 'Brussels'.")


class WeatherOutput(BaseIOSchema):
    """Current weather conditions, or a typed failure."""
    status: Literal["ok", "error"] = Field(..., description="Outcome code.")
    temperature_c: Optional[float] = Field(default=None, description="Temperature in Celsius.")
    summary: Optional[str] = Field(default=None, description="Human-readable summary.")
    error: Optional[str] = Field(default=None, description="Failure message when status='error'.")


class WeatherTool(BaseTool[WeatherInput, WeatherOutput]):
    """Fetch current conditions for a city from the weather API."""

    def __init__(self, config: WeatherConfig | None = None):
        super().__init__(config or WeatherConfig())

    def run(self, params: WeatherInput) -> WeatherOutput:
        cfg: WeatherConfig = self.config
        if not cfg.api_key:
            return WeatherOutput(status="error", error="WEATHER_API_KEY not set")
        try:
            r = httpx.get(
                f"{cfg.base_url}/current",
                params={"city": params.city},
                headers={"Authorization": f"Bearer {cfg.api_key}"},
                timeout=cfg.timeout,
            )
            r.raise_for_status()
        except httpx.HTTPError as e:
            return WeatherOutput(status="error", error=str(e))
        data = r.json()
        return WeatherOutput(status="ok", temperature_c=data["temp_c"], summary=data["summary"])

    async def run_async(self, params: WeatherInput) -> WeatherOutput:
        cfg: WeatherConfig = self.config
        if not cfg.api_key:
            return WeatherOutput(status="error", error="WEATHER_API_KEY not set")
        async with httpx.AsyncClient(timeout=cfg.timeout) as client:
            try:
                r = await client.get(
                    f"{cfg.base_url}/current",
                    params={"city": params.city},
                    headers={"Authorization": f"Bearer {cfg.api_key}"},
                )
                r.raise_for_status()
            except httpx.HTTPError as e:
                return WeatherOutput(status="error", error=str(e))
        data = r.json()
        return WeatherOutput(status="ok", temperature_c=data["temp_c"], summary=data["summary"])

Hard rules

  • Generic parameters carry the runtime type info. Never also assign input_schema / output_schema as class attributes — it shadows the framework-managed property.
  • run() returns the output schema instance, not a dict.
  • Secrets via env / BaseToolConfig, never hardcoded.
  • HTTP tools always set a timeout. Tools run in the agent's request path.
  • The async hook is async def run_async, not arun — the framework calls run_async.
  • Convert routine failures (rate limits, 404s, validation rejects from the upstream API) into a typed failure output. Reserve raise for programmer error.

Phase 4 — Wire it into an agent

Two integration shapes (see ../framework/references/tools.md for more):

Single-tool agent — agent's output schema is the tool's input schema:

agent = AtomicAgent[UserQuery, WeatherInput](config=config)
tool = WeatherTool()

call = agent.run(UserQuery(question="weather in Brussels?"))
result = tool.run(call)

Router agent — agent picks among tools via a discriminated union of tool-call schemas. Use this when the agent has 2–10 tools to choose from. For dozens, see the search+execute pattern in ../framework/references/orchestration.md.

Phase 5 — Verify

uv run python -c "from <project>.tools.<tool_name>_tool import <ToolName>Tool, <ToolName>Input; t = <ToolName>Tool(); print(t.run(<ToolName>Input(...)))"

If imports fail with the docstring error, add the docstring on the schema. If self.input_schema is None, the generic parameters are missing — write class FooTool(BaseTool[FooInput, FooOutput]):, not class FooTool(BaseTool):.

Phase 6 — Hand off

Tell the user:

  • Where the tool lives and what to import.
  • How the agent should use it (single-tool or router shape).
  • Optional next steps:
    • The agent that calls it → create-atomic-agent skill.
    • Multi-agent wiring around the tool → ../framework/references/orchestration.md.
    • MCP interop or packaging the tool for distribution → ../framework/references/tools.md.

Anti-patterns

  • class FooTool(BaseTool): with input_schema = ... class attributes — use generics: BaseTool[FooInput, FooOutput].
  • Returning a dict or primitive from run() instead of OutputSchema(...).
  • Raising on routine upstream failures — model them as typed output.
  • No timeout on HTTP / DB calls.
  • MCPTransportType.STREAMABLE_HTTP — the correct value is HTTP_STREAM.
  • Implementing async def arun(...) — the framework calls run_async.

For deeper material — MCP interop, packaging a tool for atomic download, advanced router patterns — load ../framework/references/tools.md.

原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。