Diseño de herramientas de agentes reutilizables
Estándares de esquemas de herramientas, requisitos de documentación y empaquetado para su reutilización.
Diseño de herramientas de agentes reutilizables es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.
¿Qué hace que una herramienta sea reutilizable?
Una herramienta de agente reutilizable es aquella que otros desarrolladores pueden incorporar a sus sistemas de agentes sin leer el código fuente. Tiene un esquema claro y legible por máquinas, un README legible para humanos, códigos de error bien definidos y un comportamiento predecible. Considérela una biblioteca, no un script.
Estándares de esquemas de herramientas
Toda herramienta reutilizable debe tener un esquema que describa sus entradas, salidas y metadatos. El esquema es el contrato entre quien crea la herramienta y el agente que la utiliza. Báselo en JSON Schema para lograr la máxima compatibilidad con los principales frameworks de agentes.
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'])
Códigos de error
Defina un formato estándar para las respuestas de error. Todas las herramientas deben devolver la misma estructura de error: código, mensaje y detalles opcionales. Esto permite que el agente gestione los errores mediante programación sin conocer los detalles internos de la herramienta.
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())Validación de entradas
Valide las entradas según el esquema antes de la ejecución. Utilice jsonschema para realizar la validación automáticamente. Produzca un error descriptivo de forma inmediata en lugar de permitir que las entradas no válidas provoquen fallos crípticos en las profundidades de la lógica de la herramienta.
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())Ejemplos de uso en el esquema
Añada ejemplos concretos al esquema de la herramienta. Los ejemplos cumplen dos propósitos: ayudan a los usuarios a entender rápidamente la herramienta y pueden inyectarse en el contexto del agente como demostraciones few-shot para mejorar la precisión de las llamadas a herramientas.
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']}")
Limitación de velocidad en las herramientas
Las herramientas que llaman a API externas deben respetar los límites de velocidad. Implemente un limitador de velocidad por herramienta mediante un token bucket o una ventana deslizante. Devuelva un error estándar RATE_LIMITED con los segundos que deben transcurrir antes de reintentar cuando se supere el límite.
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})Empaquetado como paquete de Python
Estructure la herramienta como un paquete de Python instalable para que otros desarrolladores puedan añadirla a sus agentes con un único pip install. El paquete expone una función get_tool_definition() y una función execute(params) como API pública.
# 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()Redacción del README de la herramienta
Un README claro es esencial para fomentar la adopción. Incluya: qué hace la herramienta, el comando de instalación, las claves de API o credenciales necesarias, todos los parámetros con sus tipos y valores predeterminados, todos los códigos de error y al menos un ejemplo de uso completo.
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])Pruebas de una herramienta reutilizable
Una herramienta reutilizable debe tener pruebas automatizadas que cubran: el caso correcto, cada código de error, los casos límite (cadenas vacías y valores máximos) y el comportamiento de la limitación de velocidad. Las pruebas son documentación: muestran exactamente cómo se comporta la herramienta.
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 allowedFormato compatible con las llamadas a funciones de OpenAI
Para garantizar la compatibilidad con las API nativas de uso de herramientas de OpenAI y Claude, asegúrese de que el esquema de la herramienta tenga exactamente el formato que esperan esas API. El método get_openai_schema() convierte el esquema interno al formato compatible con 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'])Control de versiones de la herramienta
Utilice el versionado semántico: MAJOR.MINOR.PATCH. Incremente MAJOR cuando haya cambios incompatibles en el esquema, MINOR al añadir parámetros opcionales y PATCH para corregir errores. Almacene la versión en el esquema y expóngala mediante 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.1Comprobación de conocimientos
¿Qué parte del esquema de una herramienta reutilizable permite al framework de agentes validar automáticamente las entradas antes de llamar a la función de ejecución de la herramienta?
Repaso: diseño de herramientas de agentes reutilizables
Conceptos clave de esta lección:
- Esquema: nombre, descripción, versión, parámetros (JSON Schema), valores devueltos y rate_limit
- Códigos de error: ToolError estándar con código, mensaje y detalles
- Validación de entradas: jsonschema valida las entradas según el esquema de parámetros antes de la ejecución
- Ejemplos: incluidos en el esquema como contexto few-shot
- Limitación de velocidad: ventana deslizante con el error RATE_LIMITED y retry_after
- Empaquetado: paquete instalable mediante pip con README, pruebas y control de versiones
Siguiente tema: descubrimiento de plugins y carga dinámica de herramientas.
Preguntas frecuentes
¿La lección «Diseño de herramientas de agentes reutilizables» es gratis?
Sí — el texto completo de «Diseño de herramientas de agentes reutilizables» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.
¿Qué aprenderé en «Diseño de herramientas de agentes reutilizables»?
Estándares de esquemas de herramientas, requisitos de documentación y empaquetado para su reutilización. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar AI Agents?
No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Diseño de herramientas de agentes reutilizables»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de AI Agents?
Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Diseño de herramientas de agentes reutilizables
- Descubrimiento y registro de plugins
- Versionado y compatibilidad de herramientas
- Creación de un marketplace de plugins para agentes