Decorador @tool

Intermedio
LangChain
LangChain
Actualizado: 02/12/2025

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 - Autor del tutorial

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