共有可能なエージェントツールの設計
ツールスキーマの標準、ドキュメント要件、再利用のためのパッケージ化を学びます。
「共有可能なエージェントツールの設計」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Agents学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Agentsコースには全4レッスンが含まれています。
ツールを共有可能にする要素
共有可能なエージェントツールとは、他の開発者がソースコードを読まなくても、自分のエージェントシステムに組み込めるツールです。明確で機械可読なスキーマ、人間が読みやすいREADME、明確に定義されたエラーコード、予測可能な動作を備えています。スクリプトではなく、ライブラリとして考えてください。
ツールのスキーマ標準
共有可能なツールにはすべて、入力、出力、メタデータを記述するスキーマが必要です。スキーマは、ツールの作成者とそれを使用するエージェントの間の契約です。主要なすべてのエージェントフレームワークとの互換性を最大限に高めるため、JSON Schemaを基盤にしてください。
TOOL_SCHEMA_TEMPLATE = {
'name': 'get_weather',
'description': 'Returns current weather conditions for a city. '
'Use when the user asks about weather or temperature.',
'version': '1.2.0',
'parameters': {
'type': 'object',
'properties': {
'city': {
'type': 'string',
'description': 'City name (e.g., London, Tokyo)',
'minLength': 1,
'maxLength': 100
},
'units': {
'type': 'string',
'enum': ['celsius', 'fahrenheit'],
'default': 'celsius',
'description': 'Temperature unit'
}
},
'required': ['city']
},
'returns': {
'type': 'object',
'properties': {
'temperature': {'type': 'number'},
'condition': {'type': 'string'},
'humidity_pct': {'type': 'number'}
}
},
'rate_limit': {'calls_per_minute': 60}
}
if __name__ == '__main__':
print(f"Tool: {TOOL_SCHEMA_TEMPLATE['name']} (v{TOOL_SCHEMA_TEMPLATE['version']})")
print('Description:', TOOL_SCHEMA_TEMPLATE['description'])
print('Required params:', TOOL_SCHEMA_TEMPLATE['parameters']['required'])
エラーコード
標準的なエラー応答形式を定義してください。すべてのツールが、code、message、任意のdetailsで構成される同じエラー構造を返すようにします。これにより、エージェントはツール内部の実装を知らなくても、プログラムからエラーを処理できます。
from enum import Enum
class ToolErrorCode(Enum):
INVALID_PARAMS = 'INVALID_PARAMS'
NOT_FOUND = 'NOT_FOUND'
RATE_LIMITED = 'RATE_LIMITED'
UPSTREAM_ERROR = 'UPSTREAM_ERROR'
TIMEOUT = 'TIMEOUT'
UNAUTHORISED = 'UNAUTHORISED'
INTERNAL_ERROR = 'INTERNAL_ERROR'
class ToolError(Exception):
def __init__(self, code: ToolErrorCode, message: str, details: dict = None):
self.code = code
self.message = message
self.details = details or {}
def to_dict(self) -> dict:
return {
'error': True,
'code': self.code.value,
'message': self.message,
'details': self.details
}
try:
raise ToolError(
ToolErrorCode.NOT_FOUND,
'City not found in database',
{'city': 'Atlantis', 'suggestion': 'Did you mean Athens?'}
)
except ToolError as e:
print(e.to_dict())入力検証
実行前に、スキーマに対して入力を検証してください。自動検証にはjsonschemaを使用します。不正な入力によってツールの内部ロジックで原因の分かりにくい障害が発生するのを防ぐため、説明的なエラーを返して早期に失敗させてください。
import jsonschema # pip install jsonschema
def validate_tool_input(params: dict, schema: dict) -> dict:
"""
Validate params against schema.
Returns validated (and default-filled) params.
Raises ToolError on validation failure.
"""
try:
# Fill in defaults
filled = dict(params)
for prop, definition in schema['properties'].items():
if prop not in filled and 'default' in definition:
filled[prop] = definition['default']
# Validate against schema
jsonschema.validate(instance=filled, schema=schema)
return filled
except jsonschema.ValidationError as e:
raise ToolError(
ToolErrorCode.INVALID_PARAMS,
f'Validation failed: {e.message}',
{'path': list(e.path), 'schema_path': list(e.schema_path)}
)
# Example usage:
try:
params = validate_tool_input({'city': 'London'}, TOOL_SCHEMA_TEMPLATE['parameters'])
print('Valid params:', params)
except ToolError as e:
print('Error:', e.to_dict())スキーマ内の使用例
ツールのスキーマに具体的な例を追加してください。例には2つの目的があります。人間がツールをすぐに理解できるようにすることと、エージェントのコンテキストにfew-shotデモンストレーションとして注入し、ツール呼び出しの精度を高めることです。
TOOL_WITH_EXAMPLES = {
'name': 'search_knowledge_base',
'description': 'Search the company knowledge base for documentation.',
'parameters': {
'type': 'object',
'properties': {
'query': {'type': 'string'},
'top_k': {'type': 'integer', 'default': 5, 'minimum': 1, 'maximum': 20}
},
'required': ['query']
},
'examples': [
{
'description': 'Search for onboarding docs',
'input': {'query': 'how to onboard new users', 'top_k': 3},
'output': {'results': [{'title': 'User Onboarding Guide', 'score': 0.95}]}
},
{
'description': 'Search with default top_k',
'input': {'query': 'API authentication'},
'output': {'results': [{'title': 'API Auth Docs', 'score': 0.88}]}
}
]
}
if __name__ == '__main__':
print('Tool:', TOOL_WITH_EXAMPLES['name'])
for ex in TOOL_WITH_EXAMPLES['examples']:
print(f" - {ex['description']}: input={ex['input']} -> output={ex['output']}")
ツールのレート制限
外部APIを呼び出すツールは、レート制限を遵守する必要があります。トークンバケットまたはスライディングウィンドウを使用して、ツールごとのレートリミッターを実装してください。制限を超えた場合は、再試行までの秒数を示すretry-afterとともに、標準のRATE_LIMITEDエラーを返します。
import time
from collections import deque
class SlidingWindowRateLimiter:
def __init__(self, max_calls: int, window_seconds: int):
self.max_calls = max_calls
self.window = window_seconds
self._calls = deque() # timestamps of recent calls
def check(self) -> tuple:
"""
Returns (allowed: bool, retry_after_seconds: float)
"""
now = time.time()
# Remove calls outside the window
while self._calls and self._calls[0] < now - self.window:
self._calls.popleft()
if len(self._calls) >= self.max_calls:
oldest = self._calls[0]
retry_after = (oldest + self.window) - now
return False, round(retry_after, 1)
self._calls.append(now)
return True, 0.0
weather_limiter = SlidingWindowRateLimiter(max_calls=10, window_seconds=60)
allowed, retry = weather_limiter.check()
if not allowed:
raise ToolError(ToolErrorCode.RATE_LIMITED,
f'Rate limit exceeded. Retry in {retry}s',
{'retry_after': retry})Pythonパッケージとしてのパッケージ化
他の開発者が単一のpip installで自分のエージェントに追加できるように、ツールをインストール可能なPythonパッケージとして構成してください。パッケージは公開APIとしてget_tool_definition()関数とexecute(params)関数を公開します。
# Directory structure:
# agent_tool_weather/
# __init__.py
# tool.py
# schema.py
# pyproject.toml
# README.md
# agent_tool_weather/tool.py
from .schema import SCHEMA
from .errors import ToolError, ToolErrorCode
def get_tool_definition() -> dict:
return SCHEMA
def execute(params: dict) -> dict:
validated = validate_tool_input(params, SCHEMA['parameters'])
city = validated['city']
units = validated['units']
return _fetch_weather(city, units)
def _fetch_weather(city: str, units: str) -> dict:
import requests
url = f'https://api.weather.example.com/current?city={city}&units={units}'
response = requests.get(url, headers={'X-API-Key': 'YOUR_KEY'}, timeout=5)
if response.status_code == 404:
raise ToolError(ToolErrorCode.NOT_FOUND, f'City not found: {city}')
response.raise_for_status()
return response.json()ツールREADMEの作成
明確なREADMEは、利用を広げるうえで不可欠です。ツールの機能、インストールコマンド、必要なAPIキーまたは認証情報、すべてのパラメーターの型とデフォルト値、すべてのエラーコード、少なくとも1つの完全な使用例を含めてください。
README_TEMPLATE = '''
# agent-tool-weather
Get real-time weather conditions for any city.
## Installation
pip install agent-tool-weather
## Setup
import os
os.environ["WEATHER_API_KEY"] = "your-key-here"
## Usage
from agent_tool_weather import get_tool_definition, execute
# In your agent
tool_def = get_tool_definition()
# Execute
result = execute({"city": "Tokyo", "units": "celsius"})
print(result)
# {"temperature": 22.5, "condition": "Partly cloudy", "humidity_pct": 65}
## Parameters
| Name | Type | Required | Default | Description |
|-------|--------|----------|----------|-----------------|
| city | string | Yes | - | City name |
| units | string | No | celsius | celsius or fahrenheit |
## Error Codes
- INVALID_PARAMS: Parameter validation failed
- NOT_FOUND: City not found
- RATE_LIMITED: 60 calls/minute limit exceeded
- UPSTREAM_ERROR: Weather API unavailable
'''
print(README_TEMPLATE[:300])共有可能なツールのテスト
共有可能なツールには、正常系、各エラーコード、エッジケース(空文字列や最大値)、レート制限の動作を網羅する自動テストが必要です。テストはドキュメントでもあります。ツールがどのように動作するかを正確に示すものだからです。
import pytest
def test_valid_city_returns_weather(mock_weather_api):
result = execute({'city': 'London'})
assert 'temperature' in result
assert 'condition' in result
assert isinstance(result['temperature'], (int, float))
def test_missing_required_param_raises_error():
with pytest.raises(ToolError) as exc_info:
execute({}) # city is required
assert exc_info.value.code == ToolErrorCode.INVALID_PARAMS
def test_unknown_city_raises_not_found(mock_weather_api_404):
with pytest.raises(ToolError) as exc_info:
execute({'city': 'Atlantis'})
assert exc_info.value.code == ToolErrorCode.NOT_FOUND
def test_default_units_is_celsius():
params = validate_tool_input({'city': 'Paris'}, TOOL_SCHEMA_TEMPLATE['parameters'])
assert params['units'] == 'celsius'
def test_rate_limit_enforced():
limiter = SlidingWindowRateLimiter(max_calls=2, window_seconds=60)
limiter.check() # call 1
limiter.check() # call 2
allowed, _ = limiter.check() # call 3 — should fail
assert not allowedOpenAI Function-Calling互換形式
OpenAIおよびClaudeのネイティブなツール使用APIとの互換性を確保するため、ツールスキーマが各APIの想定する正確な形式になっていることを確認してください。get_openai_schema()メソッドは、内部スキーマをOpenAI互換形式に変換します。
def get_openai_schema(tool_schema: dict) -> dict:
"""Convert internal tool schema to OpenAI function-calling format."""
return {
'type': 'function',
'function': {
'name': tool_schema['name'],
'description': tool_schema['description'],
'parameters': tool_schema['parameters']
}
}
def get_anthropic_schema(tool_schema: dict) -> dict:
"""Convert to Anthropic tool format."""
return {
'name': tool_schema['name'],
'description': tool_schema['description'],
'input_schema': tool_schema['parameters']
}
# Usage in an agent:
openai_tools = [get_openai_schema(t) for t in [TOOL_SCHEMA_TEMPLATE]]
anthropic_tools = [get_anthropic_schema(t) for t in [TOOL_SCHEMA_TEMPLATE]]
print('OpenAI format:', openai_tools[0]['function']['name'])
print('Anthropic format:', anthropic_tools[0]['name'])ツールのバージョン管理
セマンティックバージョニング(MAJOR.MINOR.PATCH)を使用してください。スキーマに後方互換性のない変更を加えた場合はMAJOR、任意のパラメーターを追加した場合はMINOR、バグを修正した場合はPATCHをインクリメントします。バージョンをスキーマに保存し、get_tool_definition()で公開してください。
VERSION = '1.2.0'
def bump_version(current: str, change_type: str) -> str:
parts = list(map(int, current.split('.')))
if change_type == 'major':
return f'{parts[0]+1}.0.0'
if change_type == 'minor':
return f'{parts[0]}.{parts[1]+1}.0'
if change_type == 'patch':
return f'{parts[0]}.{parts[1]}.{parts[2]+1}'
raise ValueError(f'Unknown change_type: {change_type}')
# Breaking change (removed required param) -> major
print(bump_version('1.2.0', 'major')) # 2.0.0
# Added optional param -> minor
print(bump_version('1.2.0', 'minor')) # 1.3.0
# Bug fix -> patch
print(bump_version('1.2.0', 'patch')) # 1.2.1理解度チェック
ツールの実行関数を呼び出す前に、エージェントフレームワークが入力を自動検証できるようにするのは、共有可能なツールスキーマのどの部分ですか?
要約:共有可能なエージェントツールの設計
このレッスンの重要なポイント:
- スキーマ:name、description、version、parameters(JSON Schema)、returns、rate_limit
- エラーコード:code、message、detailsを持つ標準のToolError
- 入力検証:jsonschemaにより、実行前にparametersスキーマに対して検証
- 例:few-shotコンテキストとしてスキーマに含める
- レート制限:RATE_LIMITEDエラーとretry_afterを使用するスライディングウィンドウ
- パッケージ化:README、テスト、バージョン管理を含むpipでインストール可能なパッケージ
次の内容:プラグインの検出と動的なツール読み込み
よくある質問
「共有可能なエージェントツールの設計」レッスンは無料ですか?
はい。「共有可能なエージェントツールの設計」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。
「共有可能なエージェントツールの設計」で何を学びますか?
ツールスキーマの標準、ドキュメント要件、再利用のためのパッケージ化を学びます。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
AI Agentsを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「共有可能なエージェントツールの設計」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAI Agentsレッスンでコードを書いて実行できますか?
はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 共有可能なエージェントツールの設計
- プラグインの検出と登録
- ツールのバージョン管理と互換性
- エージェントプラグインマーケットプレイスを構築する