Decorador @tool para crear herramientas
El decorador @tool representa la forma más directa y recomendada de crear herramientas personalizadas en LangChain. Este enfoque simplifica significativamente el proceso de desarrollo al permitir que cualquier función Python se convierta automáticamente en una herramienta utilizable por los modelos de lenguaje.
La principal ventaja del decorador @tool es su capacidad para inferir automáticamente los metadatos necesarios directamente del código Python. Esto incluye el nombre de la herramienta, su descripción y el esquema de argumentos, eliminando la necesidad de definir manualmente estas especificaciones.
Importación y uso básico
Para utilizar el decorador, debemos importarlo desde langchain.tools:
from langchain.tools import tool
Una vez importado, podemos transformar cualquier función en una herramienta simplemente añadiendo el decorador:
@tool
def calculadora_simple(a: int, b: int, operacion: str) -> int:
"""Realiza operaciones matemáticas básicas entre dos números.
Args:
a: Primer número
b: Segundo número
operacion: Tipo de operación ('suma', 'resta', 'multiplicacion', 'division')
"""
if operacion == "suma":
return a + b
elif operacion == "resta":
return a - b
elif operacion == "multiplicacion":
return a * b
elif operacion == "division":
if b != 0:
return a // b
raise ValueError("No se puede dividir por cero")
raise ValueError("Operación no válida")
Inferencia automática de metadatos
El decorador @tool utiliza introspección de Python para extraer automáticamente la información necesaria:
- Nombre de la herramienta: Se deriva del nombre de la función (
calculadora_simple) - Descripción: Se extrae de la primera línea del docstring
- Esquema de argumentos: Se construye a partir de los type hints y la documentación de parámetros
Podemos verificar cómo se han inferido estos metadatos:
print(f"Nombre: {calculadora_simple.name}")
print(f"Descripción: {calculadora_simple.description}")
print(f"Esquema de args: {calculadora_simple.args}")
Ejemplo práctico: herramienta de formateo de texto
Veamos otro ejemplo que demuestra el manejo de tipos más complejos:
@tool
def formatear_texto(texto: str, estilo: str, mayusculas: bool = False) -> str:
"""Aplica diferentes estilos de formato a un texto.
Args:
texto: El texto a formatear
estilo: Estilo a aplicar ('titulo', 'snake_case', 'camel_case')
mayusculas: Si convertir todo a mayúsculas
"""
if estilo == "titulo":
resultado = texto.title()
elif estilo == "snake_case":
resultado = texto.lower().replace(" ", "_")
elif estilo == "camel_case":
palabras = texto.split()
resultado = palabras[0].lower() + "".join(w.capitalize() for w in palabras[1:])
else:
resultado = texto
return resultado.upper() if mayusculas else resultado
Integración con modelos de lenguaje
Una vez creadas las herramientas, podemos integrarlas directamente con un modelo de chat:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# Vincular las herramientas al modelo
llm_con_herramientas = llm.bind_tools([calculadora_simple, formatear_texto])
# Usar las herramientas
respuesta = llm_con_herramientas.invoke(
"Calcula 15 + 27 y luego formatea el resultado como título"
)
Ventajas del decorador @tool
El uso del decorador @tool ofrece múltiples beneficios técnicos:
- Simplicidad: Convierte funciones Python estándar en herramientas sin código adicional
- Mantenibilidad: Los cambios en la función se reflejan automáticamente en la herramienta
- Consistencia: Garantiza que el esquema de la herramienta coincida con la implementación
- Legibilidad: El código es más limpio y fácil de entender
Buenas prácticas
Al utilizar el decorador @tool, es fundamental seguir ciertas buenas prácticas:
- Type hints obligatorios: Todos los parámetros deben tener anotaciones de tipo
- Docstrings descriptivos: La documentación debe ser clara y específica para que el modelo entienda cuándo usarla
- Manejo de errores: Incluir validaciones y excepciones apropiadas
- Valores por defecto: Utilizar parámetros opcionales cuando sea apropiado
Validación automática de esquemas y tipos
Una de las características más valiosas del decorador @tool es su capacidad para realizar validación automática de los datos de entrada antes de que lleguen a la función. Esta validación se basa en los type hints de Python y utiliza Pydantic internamente.
Mecanismo de validación
Cuando un modelo invoca una herramienta creada con @tool, LangChain intercepta automáticamente los argumentos y los valida contra el esquema inferido:
from typing import List
@tool
def procesar_numeros(numeros: List[int], multiplicador: float) -> List[float]:
"""Multiplica una lista de números enteros por un factor.
Args:
numeros: Lista de números enteros a procesar
multiplicador: Factor por el que multiplicar cada número
"""
return [num * multiplicador for num in numeros]
# Uso correcto
resultado = procesar_numeros.invoke({"numeros": [1, 2, 3], "multiplicador": 2.5})
print(resultado) # [2.5, 5.0, 7.5]
Si intentamos pasar datos incorrectos, la validación detectará el error:
try:
# Esto generará un error de validación
procesar_numeros.invoke({"numeros": ["a", "b", "c"], "multiplicador": 2.5})
except Exception as e:
print(f"Error de validación: {e}")
Validación de tipos complejos
El sistema de validación maneja tipos complejos de Python de forma sofisticada:
from typing import Dict, Optional, Union
@tool
def analizar_datos_usuario(
usuario_id: int,
metadatos: Dict[str, Union[str, int]],
fecha_limite: Optional[str] = None
) -> Dict[str, any]:
"""Analiza datos de usuario con validación de tipos complejos.
Args:
usuario_id: Identificador único del usuario
metadatos: Diccionario con información adicional del usuario
fecha_limite: Fecha límite en formato ISO (opcional)
"""
resultado = {
"usuario_id": usuario_id,
"metadatos_procesados": len(metadatos),
"claves_disponibles": list(metadatos.keys())
}
if fecha_limite:
resultado["fecha_valida"] = True
return resultado
Validación con restricciones personalizadas
Aunque el decorador @tool no permite validaciones complejas directamente en los type hints, podemos combinar la validación automática con validaciones manuales dentro de la función:
@tool
def crear_usuario(
nombre: str,
edad: int,
email: str,
activo: bool = True
) -> Dict[str, any]:
"""Crea un nuevo usuario con validaciones adicionales.
Args:
nombre: Nombre completo del usuario (mínimo 2 caracteres)
edad: Edad del usuario (debe ser mayor a 0 y menor a 150)
email: Dirección de correo electrónico válida
activo: Estado inicial del usuario
"""
# Validaciones adicionales
if len(nombre.strip()) < 2:
raise ValueError("El nombre debe tener al menos 2 caracteres")
if not (0 < edad < 150):
raise ValueError("La edad debe estar entre 1 y 149 años")
if "@" not in email or "." not in email:
raise ValueError("El email debe tener un formato válido")
return {
"nombre": nombre.strip(),
"edad": edad,
"email": email.lower(),
"activo": activo
}
Beneficios de la validación automática
La validación automática de esquemas y tipos proporciona ventajas significativas:
- Seguridad: Previene errores de runtime por tipos incorrectos
- Consistencia: Garantiza que los datos siempre cumplan el contrato esperado
- Debugging: Facilita la identificación de problemas en la integración
- Documentación: El esquema sirve como documentación viva de la API
Esta validación automática convierte las herramientas creadas con @tool en interfaces robustas que pueden manejar de forma segura las invocaciones desde modelos de lenguaje.
Personalización y artefactos
El decorador @tool permite personalizar varios aspectos de la herramienta y devolver datos adicionales mediante artefactos.
Personalización de nombre y descripción
Puedes sobrescribir el nombre y la descripción inferidos automáticamente:
@tool("busqueda_web") # Nombre personalizado
def search(query: str) -> str:
"""Busca información en la web."""
return f"Resultados para: {query}"
print(search.name) # busqueda_web
Para una descripción más detallada:
@tool("calculadora", description="Realiza cálculos aritméticos. Úsala para cualquier problema matemático.")
def calc(expression: str) -> str:
"""Evalúa expresiones matemáticas."""
return str(eval(expression))
Artefactos de herramientas
Los artefactos permiten devolver datos adicionales que no se envían al modelo, pero que son útiles para la aplicación. Esto separa el contenido para el modelo de los metadatos para tu código:
from langchain.tools import tool
from typing import Tuple
@tool(response_format="content_and_artifact")
def recuperar_documento(doc_id: str) -> Tuple[str, dict]:
"""
Recupera un documento de la base de datos.
Args:
doc_id: Identificador del documento
"""
# Contenido para el modelo
contenido = f"Contenido del documento {doc_id}: Lorem ipsum..."
# Metadatos para la aplicación (no van al modelo)
metadatos = {
"doc_id": doc_id,
"autor": "Juan García",
"fecha": "2024-01-15",
"version": "1.2"
}
return contenido, metadatos
Al invocar la herramienta, puedes acceder a ambos valores:
resultado = recuperar_documento.invoke({"doc_id": "abc123"})
print(f"Para el modelo: {resultado.content}")
print(f"Metadatos: {resultado.artifact}")
Los artefactos son especialmente útiles para:
- Referencias bibliográficas: Información de citación sin saturar el contexto
- Identificadores de recursos: IDs, URLs o rutas de archivos
- Metadatos de búsqueda: Scores de relevancia, fechas, versiones
- Datos estructurados: JSON u otras estructuras que la aplicación necesita procesar
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en LangChain
Documentación oficial de LangChain
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, LangChain es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de LangChain
Explora más contenido relacionado con LangChain y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Usar el decorador @tool para crear herramientas, entender cómo se infieren automáticamente metadatos desde el código, personalizar nombres y descripciones, trabajar con type hints y docstrings, y crear herramientas con múltiples parámetros y validación.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje