¿Cómo se registran las tools en un agente LangChain? El flujo de bind_tools() al LLM
No hay un if "ahora toca esta tool" en ningún sitio. El LLM decide qué tool invocar leyendo sus docstrings. bind_tools() es el puente entre tu lista de funciones Python y el razonamiento del modelo.
La confusión habitual: ¿dónde está el if?
Al ver un agente con varias tools, la primera intuición es buscar en el código dónde se decide cuál invocar — un if intent == "crear_ticket" o un router explícito. Esa lógica no existe. No hay ningún bloque de control que diga "ahora toca esta tool". La decisión vive dentro del LLM, no en el código Python.
Esto es anti-intuitivo para cualquier desarrollador que viene de programación imperativa. La forma en que el modelo sabe cuándo invocar una tool, con qué argumentos, y cuándo no invocar ninguna, es leyendo el docstring de cada función decorada con @tool.
El patrón completo: de tools.py a bind_tools()
El flujo de registro tiene tres pasos. Primero se definen las tools en tools.py con el decorator @tool. Luego se importan en main.py y se agrupan en una lista. Finalmente, esa lista se pasa al LLM con bind_tools():
# tools.py
from langchain_core.tools import tool
@tool
def guardar_titulo(titulo: str) -> str:
"""
Guarda el título del artículo de blog propuesto por el usuario.
Invoca esta tool cuando el usuario proporcione un título concreto.
Devuelve confirmación del título guardado.
"""
DRAFT["titulo"] = titulo
return f"Título guardado: {titulo}"
@tool
def crear_ticket_blog(slug_final: str = "") -> str:
"""
Crea el ticket en Jira SOLO cuando el usuario haya confirmado
explícitamente todos los campos (título, contenido, fecha, slug).
No invocar si falta algún campo o sin confirmación del usuario.
"""
# implementación real...# main.py from tools import guardar_titulo, guardar_contenido, guardar_fecha, crear_ticket_blog tools = [guardar_titulo, guardar_contenido, guardar_fecha, crear_ticket_blog] llm = ChatOpenAI(model="gpt-4o-mini", temperature=0).bind_tools(tools) # o con Ollama: # llm = ChatOllama(model="gemma2:9b").bind_tools(tools)
bind_tools() serializa cada función como un JSON Schema (nombre + descripción del docstring + esquema de parámetros de los type hints) y lo incluye en el contexto del modelo en cada invocación.
Qué ve el LLM y cómo decide
En cada turno de conversación, el LLM recibe: el historial de mensajes, el system prompt, el mensaje del usuario, y la lista de tools disponibles serializada en JSON. El modelo razona sobre todo ese contexto y decide una de tres cosas: responder directamente, invocar una tool (con qué argumentos), o no invocar ninguna.
Respuesta directa
El mensaje del usuario no encaja con ninguna tool disponible
"¿Cuántos tokens tiene GPT-4?" → respuesta del LLM
Invocar tool
El docstring de una tool encaja semánticamente con lo que el usuario pide
"Guarda el título: Chatbot con Jira" → guardar_titulo()
No invocar
Pregunta fuera del alcance definido en el system prompt o ningún docstring coincide
"Háblame de la guerra civil" → rechazo del agente
Para verlo en acción: activar verbose=True al instanciar el LLM. El log muestra exactamente por qué el modelo invocó (o no invocó) cada tool en cada turno — el razonamiento interno completo.
El takeaway
¿Quieres aplicar esto en tu equipo?
En Kuuli Technologies llevamos estas metodologías a equipos reales. Escríbenos si quieres una sesión de trabajo o una consultoría sobre agentes IA y SDD.