Decorador @tool de LangChain: Definición de herramientas
Una herramienta (Tool) es el puente de interacción entre el Agent y el mundo exterior.
A través del@tooldecorador, puedes convertir rápidamente cualquier función de Python en una herramienta invocable por el Agent.
Sintaxis básica de @tool
@tool es un decorador proporcionado por LangChain, y su uso es extremadamente simple: al añadir el decorador @tool a una función, esta se convierte en una herramienta.
Ejemplo
# La herramienta más simple: una función normal + decorador @tool
@tool
def hello_tool(name: str) -> str:
"""Saluda a una persona específica.
Args:
name: el nombre de la persona a saludar
"""
return f"Hola, {name}! Bienvenido a Ejemplo."
# Las herramientas también son funciones normales de Python y se pueden llamar directamente
result = hello_tool.invoke({"name": "Xiaoming"})
print(result)
# La herramienta contiene información de descripción generada automáticamente
print(f"\nNombre de la herramienta: {hello_tool.name}")
print(f"Descripción de la herramienta: {hello_tool.description}")
Resultado de la ejecución:
你好,小明!欢迎来到Ejemplo.
Nombre de la herramienta: hello_tool
Descripción de la herramienta: 向指定的人打招呼。
Args:
name: 要打招呼的人的名字
El docstring de la función se convierte automáticamente en la descripción de la herramienta. El Agent depende de esta descripción para determinar "qué puede hacer esta herramienta" y "en qué situaciones debería llamarla". Cuanto más claro sea el docstring, más preciso será el Agent al usar la herramienta. En la descripción, explica el significado de los parámetros, la funcionalidad de la función y los casos de uso.
Uso de diferentes tipos de parámetros
@tool admite varios tipos de parámetros, incluyendo int, float, bool y valores enumerados (enum).
Ejemplo
from typing import Literal
@tool
def search_courses(
keyword: str,
level: Literal["Principiante", "Intermedio", "Avanzado"],
max_results: int = 5,
free_only: bool = True,
) -> str:
"""Busca cursos en Ejemplo.
Args:
keyword: palabra clave de búsqueda
level: nivel de dificultad del curso
max_results: número máximo de resultados a devolver
free_only: si mostrar solo cursos gratuitos
"""
# Simular resultados de búsqueda
courses = {
("python", "Principiante"): "Tutorial básico de Python3 (gratis)",
("python", "Intermedio"): "Práctica de análisis de datos con Python (gratis)",
("python", "Avanzado"): "Machine Learning avanzado con Python (solo miembros)",
("html", "Principiante"): "Tutorial básico de HTML (gratis)",
}
key = (keyword.lower(), level)
course = courses.get(key, f"No se encontraron cursos de {keyword} de nivel {level}")
if free_only and "miembro" in course:
return f"Resultado de búsqueda: {course} — este curso requiere membresía, lo he filtrado por ti"
return f"Resultado de búsqueda: {course}"
# Probar la llamada con diferentes parámetros
print(search_course.invoke({
"keyword": "python",
"level": "Principiante",
}))
# Salida: Resultado de búsqueda: Tutorial básico de Python3 (gratis)
print(search_course.invoke({
"keyword": "python",
"level": "Avanzado",
"free_only": False,
}))
# Salida: Resultado de búsqueda: Machine Learning avanzado con Python (solo miembros)
Registrar herramientas en el Agent
Pasa la herramienta definida al parámetro tools de create_agent() y el Agent podrá usarla.
Ejemplo
load_dotenv()
from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
@tool
def search_courses(keyword: str) -> str:
"""Busca cursos en Python. Introduce una palabra clave y devuelve una lista de cursos relacionados.
Args:
keyword: palabra clave de búsqueda, como python, html, java
"""
courses = {
"python": "Tutorial básico de Python3, Análisis de datos con Python, Introducción al web scraping con Python",
"html": "Tutorial básico de HTML, Novedades de HTML5, Práctica de formularios HTML",
"java": "Tutorial básico de Java, Java orientado a objetos, Framework Spring de Java",
}
return courses.get(keyword.lower(), f"No se encontraron cursos relacionados con {keyword}")
@tool
def get_course_detail(course_name: str) -> str:
"""Obtiene la información detallada de un curso específico, incluyendo el número de capítulos y la duración del estudio.
Args:
course_name: nombre del curso, como "Tutorial básico de Python3"
"""
details = {
"tutorial básico de python3": "30 capítulos en total, duración estimada de 20 horas, adecuado para principiantes desde cero",
"tutorial básico de html": "25 capítulos en total, duración estimada de 15 horas, adecuado para principiantes desde cero",
}
return details.get(
course_name.lower(),
f"Detalles de {course_name}: adecuado para principiantes, contenido abundante, incluye casos prácticos"
)
# Crear el Agent
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
model=model,
tools=[search_courses, get_course_detail],
system_prompt="Eres el asesor de estudios de Ejemplo y ayudas a los usuarios a encontrar los cursos adecuados.",
)
# Ejecutar el Agent
def ask(question: str):
result = agent.invoke({"messages": [HumanMessage(content=question)]})
print(f"Usuario: {question}")
print(f"Asesor: {result['messages'][-1].content}")
print("-" * 60)
ask("Quiero aprender Python, ¿qué cursos me recomiendas?")
ask("¿Cuánto tiempo se tarda en completar el Tutorial básico de Python3?")
Resultado de la ejecución:
Usuario: 我想学 Python,有什么课程推荐? Asesor: 在Ejemplo 中有以下 Python 相关课程: Python3 基础教程、Python 数据分析、Python 爬虫入门。 这些课程很适合 Python 初学者和进阶学习者,您可以根据兴趣选择! ------------------------------------------------------------ Usuario: Python3 基础教程学完需要多久? Asesor: Python3 基础教程共 30 章,预计学习时长 20 小时,非常适合零基础入门学习。 ------------------------------------------------------------
Colaboración de múltiples herramientas
Un Agent puede registrar múltiples herramientas, y el modelo decidirá automáticamente cuándo usar cada herramienta.
Ejemplo
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
@tool
def get_stock_price(symbol: str) -> str:
"""Consulta el precio actual de una acción.
Args:
symbol: símbolo bursátil, como AAPL, GOOGL, TSLA
"""
# Simular datos de acciones
prices = {"AAPL": 185.50, "GOOGL": 142.30, "TSLA": 245.80}
price = prices.get(symbol.upper())
if price is None:
return f"No se encontró el símbolo bursátil {symbol}"
return f"Precio actual de {symbol.upper()}: ${price}"
@tool
def convert_currency(amount: float, from_currency: str, to_currency: str) -> str:
"""Conversión de divisas.
Args:
amount: cantidad
from_currency: código de la moneda original, como USD, CNY
to_currency: código de la moneda objetivo, como CNY, USD
"""
# Simular tipo de cambio
rates = {
("USD", "CNY"): 7.25,
("CNY", "USD"): 0.138,
}
rate = rates.get((from_currency.upper(), to_currency.upper()))
if rate is None:
return f"No se admite la conversión de {from_currency} → {to_currency}"
result = round(amount * rate, 2)
return f"{amount} {from_currency} = {result} {to_currency}"
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
model=model,
tools=[get_stock_price, convert_currency],
system_prompt="Eres un asistente financiero que ayuda a los usuarios a consultar precios de acciones y convertir divisas.",
)
# El Agent decidirá automáticamente qué herramienta llamar
result = agent.invoke({
"messages": [HumanMessage(content="¿Cuál es el precio de las acciones de Apple? ¿Cuánto sería en RMB?")]
})
for msg in result["messages"]:
if msg.type == "tool":
print(f"[Llamando a la herramienta {msg.name}] {msg.content}")
elif msg.type == "ai" and msg.content:
print(f"\nRespuesta final: {msg.content}")
Resultado de la ejecución:
[调用工具 get_stock_price] AAPL 当前价格:$185.5 [调用工具 convert_currency] 185.5 USD = 1344.88 CNY Respuesta final: 苹果(AAPL)当前股价为 $185.50,按当前汇率换算约为 1344.88 人民币。
Parámetros opcionales y valores predeterminados de las herramientas
Establecer valores predeterminados para los parámetros de las herramientas las hace más flexibles de usar.
Ejemplo
@tool
def recommend_tutorial(
language: str,
level: str = "Principiante",
count: int = 3,
) -> str:
"""Recomienda cursos de Ejemplo según el lenguaje de programación y el nivel de dificultad.
Args:
language: lenguaje de programación, como Python, Java, HTML
level: nivel de dificultad, opciones "Principiante", "Intermedio", "Avanzado". Por defecto "Principiante"
count: número de cursos a recomendar, por defecto 3
"""
tutorials = {
"python": ["Python3 Básico", "Python Orientado a Objetos", "Web Scraping con Python", "Análisis de Datos con Python"],
"java": ["Java Básico", "Java Orientado a Objetos", "Java Collection Framework", "Java Multihilo"],
}
all_tutorials = tutorials.get(language.lower(), [f"Tutorial básico de {language}"])
selected = all_tutorials[:count]
return f"Recomendando cursos de nivel {level} de {language}: {'、'.join(selected)}"
# Al llamarla solo hay que pasar los parámetros obligatorios
print(recommend_tutorial.invoke({"language": "Python"}))
# Salida: Recomendando cursos de nivel Principiante de Python: Python3 Básico, Python Orientado a Objetos, Web Scraping con Python
# También se pueden sobrescribir los parámetros predeterminados
print(recommend_tutorial.invoke({
"language": "Java",
"level": "Intermedio",
"count": 2,
}))
# Salida: Recomendando cursos de nivel Intermedio de Java: Java Básico, Java Orientado a Objetos
Los valores predeterminados permiten que el Agent no tenga que especificar todos los parámetros cada vez que llama a una herramienta. Pero ten en cuenta: si un parámetro no tiene valor predeterminado y el Agent no lo proporciona, la llamada fallará. No establezcas valores predeterminados para parámetros críticos.
args_schema de la herramienta: validación personalizada de parámetros
Para necesidades complejas de validación de parámetros, se puede usar un modelo Pydantic como args_schema:
Ejemplo
from langchain.tools import tool
# Definir el modelo de parámetros (proporciona un control más preciso de los parámetros)
class CourseSearchInput(BaseModel):
"""Parámetros de búsqueda de cursos"""
keyword: str = Field(
description="Palabra clave de búsqueda, admite coincidencia difusa",
min_length=1, # Mínimo 1 carácter
max_length=50, # Máximo 50 caracteres
)
category: str = Field(
default="all",
description="Categoría del curso: all (todos), frontend (frontend), backend (backend), data (ciencia de datos)",
pattern=r"^(all|frontend|backend|data)$", # Limitar valores opcionales
)
page: int = Field(
default=1,
description="Número de página, comenzando desde 1",
ge=1, # Mayor o igual que 1
le=100, # Menor o igual que 100
)
@tool(args_schema=CourseSearchInput)
def search_course(keyword: str, category: str = "all", page: int = 1) -> str:
"""Buscar cursos en EJEMPLO (tutorial para principiantes)"""
return f"Búsqueda de '{keyword}' (categoría: {category}, página {page}): se encontraron 15 resultados en total"
# Llamada válida
print(search_course.invoke({"keyword": "Python", "category": "backend", "page": 1}))
# Llamada no válida (category no está entre los valores permitidos)
try:
search_course.invoke({"keyword": "Python", "category": "invalid"})
except Exception as e:
print(f"Error de validación de parámetros: {e}")
Resultado de ejecución:
搜索 'Python' (分类: backend, 第 1 页):共找到 15 条结果 参数校验失败: ...
Comparación de formas de definir herramientas
| Método | Cantidad de código | Escenario de aplicación | Ejemplo |
|---|---|---|---|
| Decorador @tool | Mínima | Herramientas de complejidad simple a media | La mayoría de los escenarios |
| @tool + args_schema | Media | Herramientas que requieren validación precisa de parámetros | Encapsulación de API, operaciones de base de datos |
| Clase Pydantic como herramienta | Bastante | Herramientas con lógica de negocio compleja | Herramientas que contienen estado interno |
| Formato de diccionario | Mínima (pero no recomendado) | Describir herramientas remotas/incorporadas | Herramientas MCP, herramientas del servidor |