공유 가능한 에이전트 도구 설계
도구 스키마 표준, 문서화 요구 사항, 재사용을 위한 패키징을 다룹니다.
공유 가능한 에이전트 도구 설계은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 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'])
오류 코드
표준 오류 응답 형식을 정의합니다. 모든 도구는 코드, 메시지, 선택적 세부 정보로 구성된 동일한 오류 구조를 반환해야 합니다. 이렇게 하면 에이전트가 도구 내부를 알지 못해도 프로그래밍 방식으로 오류를 처리할 수 있습니다.
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())스키마의 사용 예시
도구 스키마에 구체적인 예시를 추가합니다. 예시는 두 가지 목적에 사용됩니다. 사람이 도구를 빠르게 이해하도록 돕고, 도구 호출의 정확도를 높이기 위한 퓨샷 시연으로 에이전트의 컨텍스트에 삽입할 수 있습니다.
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를 호출하는 도구는 호출 제한을 준수해야 합니다. 토큰 버킷 또는 슬라이딩 윈도우를 사용해 도구별 호출 제한기를 구현합니다. 제한을 초과하면 재시도까지의 대기 시간을 초 단위로 포함한 표준 호출 제한 오류를 반환합니다.
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 키 또는 자격 증명, 유형과 기본값을 포함한 모든 매개변수, 모든 오류 코드, 완전한 사용 예시를 하나 이상 포함합니다.
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 함수 호출 호환 형식
OpenAI와 클로드의 기본 도구 사용 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지식 확인
도구를 호출하기 전에 에이전트 프레임워크가 입력을 자동으로 검증할 수 있게 하는 공유 가능한 도구 스키마의 부분은 무엇입니까?
복습: 공유 가능한 에이전트 도구 설계
이 레슨의 핵심 내용:
- 스키마: 이름, 설명, 버전, 매개변수(JSON Schema), 반환값, 호출 제한
- 오류 코드: 코드, 메시지, 세부 정보가 포함된 표준 ToolError
- 입력 검증: 실행 전에 jsonschema로 매개변수 스키마를 기준으로 검증
- 예시: 퓨샷 컨텍스트로 스키마에 포함
- 호출 제한: 호출 제한 오류와 재시도 대기 시간이 포함된 슬라이딩 윈도우
- 패키징: README, 테스트, 버전 관리를 포함하고 pip로 설치할 수 있는 패키지
다음 주제: 플러그인 검색 및 동적 도구 로딩
자주 묻는 질문
“공유 가능한 에이전트 도구 설계” 강의는 무료인가요?
네 — “공유 가능한 에이전트 도구 설계” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
“공유 가능한 에이전트 도구 설계”에서 뭘 배우나요?
도구 스키마 표준, 문서화 요구 사항, 재사용을 위한 패키징을 다룹니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Agents을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“공유 가능한 에이전트 도구 설계” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 공유 가능한 에이전트 도구 설계
- 플러그인 검색 및 등록
- 도구 버전 관리 및 호환성
- 에이전트 플러그인 마켓플레이스 구축