Hermes Agent Wiki 非公式・日本語wiki

ツールを追加する

目次

ツールを書き始める前に、一度立ち止まって考えてみてください。それはスキルで足りるものではありませんか?

指示とシェルコマンドと既存のツールの組み合わせで表現できる機能なら、スキルにします(arXiv 検索、git の作業手順、Docker の管理、PDF の処理など)。

API キーを使った端から端までの連携、独自の処理ロジック、バイナリデータの取り扱い、ストリーミングが必要なら、ツールにします(ブラウザ操作、音声合成、画像の解析など)。

全体像

ツールを 1 つ追加すると、触るファイルは2 つです。

  1. tools/your_tool.py — ハンドラー、スキーマ、利用可否のチェック関数、registry.register() の呼び出し
  2. toolsets.py — ツール名を _HERMES_CORE_TOOLS(または個別のツールセット)に追加する

tools/*.py のうち、トップレベルで registry.register() を呼んでいるファイルは起動時に自動で見つかります。読み込むファイルの一覧を手で管理する必要はありません。

ステップ 1: 組み込みツールのファイルを作る

ツールのファイルは、どれも同じ形をしています。

# tools/weather_tool.py
"""Weather Tool -- look up current weather for a location."""

logger = logging.getLogger(__name__)

# --- Availability check ---

def check_weather_requirements() -> bool:
    """Return True if the tool's dependencies are available."""
    return bool(os.getenv("WEATHER_API_KEY"))

# --- Handler ---

def weather_tool(location: str, units: str = "metric") -> str:
    """Fetch weather for a location. Returns JSON string."""
    api_key = os.getenv("WEATHER_API_KEY")
    if not api_key:
        return json.dumps({"error": "WEATHER_API_KEY not configured"})
    try:
        # ... call weather API ...
        return json.dumps({"location": location, "temp": 22, "units": units})
    except Exception as e:
        return json.dumps({"error": str(e)})

# --- Schema ---

WEATHER_SCHEMA = {
    "name": "weather",
    "description": "Get current weather for a location.",
    "parameters": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "City name or coordinates (e.g. 'London' or '51.5,-0.1')"
            },
            "units": {
                "type": "string",
                "enum": ["metric", "imperial"],
                "description": "Temperature units (default: metric)",
                "default": "metric"
            }
        },
        "required": ["location"]
    }
}

# --- Registration ---

from tools.registry import registry

registry.register(
    name="weather",
    toolset="weather",
    schema=WEATHER_SCHEMA,
    handler=lambda args, **kw: weather_tool(
        location=args.get("location", ""),
        units=args.get("units", "metric")),
    check_fn=check_weather_requirements,
    requires_env=["WEATHER_API_KEY"],
)

守るべき決まり

ステップ 2: 組み込みツールをツールセットに追加する

toolsets.py に、ツール名を書き足します。

# If it should be available on all platforms (CLI + messaging):
_HERMES_CORE_TOOLS = [
    ...
    "weather",  # <-- add here
]

# Or create a new standalone toolset:
"weather": {
    "description": "Weather lookup tools",
    "tools": ["weather"],
    "includes": []
},

~~ステップ 3: 読み込み用の import を書く~~(現在は不要)

トップレベルで registry.register() を呼んでいるツールのモジュールは、tools/registry.py の discover_builtin_tools() が自動で見つけます。import の一覧を手入れする必要はありません。tools/ にファイルを置けば、起動時にそのまま拾われます。

非同期のハンドラー

ハンドラーの中で非同期のコードを動かしたいときは、is_async=True を付けます。

async def weather_tool_async(location: str) -> str:
    async with aiohttp.ClientSession() as session:
        ...
    return json.dumps(result)

registry.register(
    name="weather",
    toolset="weather",
    schema=WEATHER_SCHEMA,
    handler=lambda args, **kw: weather_tool_async(args.get("location", "")),
    check_fn=check_weather_requirements,
    is_async=True,  # registry calls _run_async() automatically
)

同期と非同期の橋渡しはレジストリ側が引き受けます。自分で asyncio.run() を呼ぶ場面はありません。

task_id が必要なハンドラー

セッションごとの状態を扱うツールには、task_id が **kwargs 経由で渡ってきます。

def _handle_weather(args, **kw):
    task_id = kw.get("task_id")
    return weather_tool(args.get("location", ""), task_id=task_id)

registry.register(
    name="weather",
    ...
    handler=_handle_weather,
)

エージェントループが横取りするツール

一部のツール(todo_list、memory、session_search、delegate_task)は、セッションごとのエージェントの状態にアクセスする必要があります。これらはレジストリに届く前に、エージェントループ(agent/tool_executor.py。agent/conversation_loop.py から呼ばれます)が横取りします。スキーマ自体はレジストリが持ったままですが、この横取りを通らずに来た場合、dispatch() は予備のエラーを返します。

任意: セットアップウィザードへの組み込み

作ったツールに API キーが必要なら、hermes_cli/config.py に書き足します。

OPTIONAL_ENV_VARS = {
    ...
    "WEATHER_API_KEY": {
        "description": "Weather API key for weather lookup",
        "prompt": "Weather API key",
        "url": "https://weatherapi.com/",
        "tools": ["weather"],
        "password": True,
    },
}

チェックリスト

  • [ ] ハンドラー、スキーマ、チェック関数、登録処理を書いたツールのファイルを作った
  • [ ] toolsets.py の適切なツールセットに追加した
  • [ ] プラグインではなく組み込みのコアツールにすべきものか、あらためて確かめた
  • [ ] ハンドラーが JSON 文字列を返し、エラーは {"error": "..."} で返っている
  • [ ] 任意: hermes_cli/config.py の OPTIONAL_ENV_VARS に API キーを追加した
  • [ ] 任意: 一括処理のために toolset_distributions.py に追加した
  • [ ] hermes chat -q "Use the weather tool for London" で動かして確かめた