5. Vista previa de agentes — El momento "Ajá"
Hasta este punto, has construido sistemas donde tú controlas el flujo. Escribes el prompt, llamas al LLM y procesas la respuesta. El LLM es poderoso, pero sigue simplemente tus instrucciones.
Este capítulo introduce un cambio fundamental: ¿qué pasaría si el LLM decidiera qué sucede después?
Por qué existe este capítulo
El problema: Si continúas construyendo chatbots hasta el Capítulo 11 sin esta vista previa, probablemente pensarás "los agentes son solo mejores chatbots".
La verdad: Los agentes son fundamentalmente diferentes. Toman decisiones que controlan tu programa, no solo generan texto.
La solución: Este capítulo te muestra hacia dónde vamos, para que cuando construyas pipelines y gestión de estado en los próximos capítulos, entiendas por qué cada pieza importa.
Qué aprenderás (y qué no)
Este capítulo (Capítulo 5):
- Una vista previa mínima de 15 minutos del pensamiento agéntico
- Cómo la salida del LLM puede activar diferentes rutas de código
- La diferencia conceptual entre enrutamiento y bucles de agentes
Capítulos posteriores (Comenzando en el Capítulo 12):
- Sistemas de agentes reales con herramientas, bucles y autocorrección
- Implementaciones listas para producción
- Patrones avanzados como coordinación multi-agente
¿Por qué la brecha? Antes de construir agentes, necesitas fundamentos: ingeniería de prompts (Cap 4), pipelines de ejecución (Cap 6), salida estructurada (Cap 7), gestión de estado (Cap 8) e integración de conocimiento (Cap 9-11).
Una nota sobre el enfoque de aprendizaje: Este capítulo se enfoca en conceptos, no en detalles de implementación. Las técnicas reales de diseño y desarrollo vienen en el Capítulo 12 y posteriores. No te preocupes si no sabes cómo implementar todo lo que ves aquí—eso es intencional. Entender qué es un agente conceptualmente es suficiente por ahora.
Comencemos.
5.1) De chatbot a tomador de decisiones
El concepto: En lugar de responder la pregunta, el LLM decide qué herramienta usar
En los Capítulos 3 y 4, construiste sistemas de chat donde el LLM genera respuestas de texto. El flujo era directo:
- El usuario hace una pregunta
- El LLM genera una respuesta
- Muestras la respuesta
Esta es una cadena estática: el flujo está predeterminado. El único trabajo del LLM es producir texto.
Ahora cambiemos el tipo de solicitud. Un usuario pregunta: "¿Cuánto es 847 × 923?"
Tu chatbot del Capítulo 3 intentaría responder esto, pero los LLM no calculan realmente—predicen tokens que parecen plausibles. Para "2 + 2", la respuesta "4" aparece tan a menudo en los datos de entrenamiento que el LLM lo hace bien. Pero para "847 × 923"—un cálculo que el LLM nunca ha visto—generará algo que parece un número pero probablemente esté mal.
Lo que realmente quieres:
- El LLM reconoce: "Esto es un problema matemático"
- El LLM decide: "Usar la herramienta calculadora, no mi predicción de tokens"
- Python ejecuta: 847 × 923 = 781,781
- El sistema devuelve: La respuesta correcta
El trabajo del LLM no es calcular—es enrutar a la herramienta que puede hacerlo.
Aquí está la idea clave: el LLM no necesita resolver las matemáticas—necesita decidir usar una calculadora.
Este es el cambio de chatbot a tomador de decisiones. El LLM examina la solicitud y la enruta a la herramienta apropiada. Está tomando una decisión sobre el flujo del programa, no solo generando texto.
Visualicemos esta diferencia:
En la cadena estática, el LLM intenta responder todo directamente. En el sistema de enrutamiento dinámico, el LLM enruta la solicitud a la herramienta correcta.
Lo visual: Compara una "Cadena lineal" (Estática) vs. un "Enrutador" (Dinámico)
Así es como una cadena estática maneja cualquier solicitud del usuario:
El LLM intenta responder todo directamente. Ya sea que preguntes "¿Cuál es la capital de Francia?" o "¿Cuánto es el 15% de 240?", el LLM genera una respuesta de texto. Podría acertar con la capital (ha visto "París" muchas veces en los datos de entrenamiento), pero probablemente calculará mal el porcentaje.
El problema: El LLM usa el mismo enfoque para todas las preguntas—predicción de tokens—incluso cuando existen mejores herramientas.
Ahora introduzcamos un sistema que puede manejar diferentes tipos de solicitudes de manera diferente:
- Preguntas fácticas: "¿Cuál es la capital de Francia?" → Usar herramienta de búsqueda
- Problemas matemáticos: "¿Cuánto es el 15% de 240?" → Usar calculadora
- Conversacional: "¿Cómo estás?" → El LLM responde directamente
Aquí hay un enrutador dinámico que hace esto posible:
El LLM examina la entrada y elige una ruta basándose en qué tipo de solicitud es. Esta es lógica de enrutamiento, y el LLM actúa como el enrutador.
La diferencia clave: En lugar de siempre generar texto, el LLM ahora decide cómo manejar cada solicitud—enrutándola a la herramienta apropiada.
Conclusión clave: De generar respuestas a elegir acciones
Este es el cambio fundamental en los sistemas agénticos:
Uso tradicional del LLM: Haces una pregunta, el LLM escribe una respuesta.
Uso agéntico del LLM: Haces una pregunta, el LLM decide qué hacer, y tu código ejecuta esa decisión.
La salida del LLM ya no es solo texto para el usuario—son instrucciones para tu programa.
Piénsalo así: en un chatbot tradicional, el LLM es un empleado que responde preguntas de clientes. En un sistema agéntico, el LLM es un gerente que decide qué departamento debe manejar cada solicitud.
Esta capacidad de toma de decisiones es la esencia de los sistemas agénticos. El LLM no solo responde—controla qué hace tu programa después. Basándose en la entrada, puede activar una calculadora, llamar a una API de búsqueda o responder directamente. El comportamiento del programa cambia según la decisión del LLM.
Pero ¿cómo implementamos esto realmente? Veamos el código mínimo que hace que esto funcione.
5.2) La ejecución mínima
El activador: Un prompt de sistema que fuerza salida estructurada
Para hacer que el LLM actúe como enrutador, necesitamos restringir su salida. En lugar de generar una respuesta completa, queremos que genere una decisión estructurada—un objeto JSON que le dice a nuestro código tanto qué hacer como qué datos usar.
Aquí hay un prompt de sistema que hace esto:
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
import json
llm = ChatOpenAI(model="gpt-4o-mini")
system_prompt = """Eres un asistente de enrutamiento. Analiza la solicitud del usuario y responde con JSON.
Formato de salida:
{
"action": "CALC" | "SEARCH" | "CHAT",
"input": "entrada limpia para la herramienta"
}
Ejemplos:
Usuario: "Calcula 15 por 7"
Salida: {"action": "CALC", "input": "15 * 7"}
Usuario: "¿Cuál es la capital de Japón?"
Salida: {"action": "SEARCH", "input": "capital de Japón"}
Usuario: "¡Hola!"
Salida: {"action": "CHAT", "input": "¡Hola!"}
Extrae la información esencial y formátala para la herramienta apropiada.
Solo genera JSON válido, nada más."""
def get_routing_decision(user_input: str) -> dict:
messages = [
SystemMessage(content=system_prompt),
HumanMessage(content=user_input)
]
response = llm.invoke(messages)
# Parsear respuesta JSON
try:
decision = json.loads(response.content.strip())
return decision
except json.JSONDecodeError:
# Respaldo si el LLM no genera JSON válido
return {"action": "CHAT", "input": user_input}
# Pruébalo con entradas DIFERENTES a los ejemplos
print(get_routing_decision("¿Cuánto es 25 * 48?"))
# Salida: {'action': 'CALC', 'input': '25 * 48'}
print(get_routing_decision("¿Quién escribió Hamlet?"))
# Salida: {'action': 'SEARCH', 'input': 'autor de Hamlet'}
print(get_routing_decision("¿Cómo estás hoy?"))
# Salida: {'action': 'CHAT', 'input': '¿Cómo estás hoy?'}Este prompt es deliberadamente restrictivo. No estamos pidiendo al LLM que sea creativo—le estamos pidiendo que clasifique la entrada y extraiga la información relevante en un formato estructurado.
Observa lo que está sucediendo aquí:
- El LLM lee la pregunta del usuario
- Determina la intención (cálculo, búsqueda fáctica, conversación)
- Extrae y limpia la información esencial
- Genera un objeto JSON con tanto la acción como la entrada limpia
- Nuestro código recibe estos datos estructurados y enruta en consecuencia
El LLM no está respondiendo la pregunta—está decidiendo qué debería responder la pregunta.
El puente: Conectar decisiones del LLM con ejecución de herramientas
Ahora necesitamos conectar la decisión del LLM con la ejecución real de herramientas. Aquí está el puente mínimo:
def safe_calculator(expression: str) -> float:
try:
# Usar eval con namespace restringido por seguridad
# Nota: eval() tiene riesgos de seguridad.
# En producción, usa una librería de parseo matemático como sympy o evaluación basada en ast.
result = eval(expression, {"__builtins__": {}}, {})
return float(result)
except:
raise ValueError(f"No se pudo calcular: {expression}")
def search(query: str) -> str:
"""Función de búsqueda placeholder"""
# En realidad, esto llamaría a una API de búsqueda
# Ahora recibe consulta limpia como "autor de Hamlet"
return f"Resultados de búsqueda para: {query}"
def route_and_execute(user_input: str) -> str:
"""Obtener decisión del LLM y ejecutar la herramienta apropiada"""
decision = get_routing_decision(user_input)
action = decision["action"]
tool_input = decision["input"] # Entrada limpia por el LLM
if action == "CALC":
try:
result = safe_calculator(tool_input)
return f"Resultado del cálculo: {result}"
except ValueError as e:
return f"No se pudo realizar el cálculo: {e}"
elif action == "SEARCH":
result = search(tool_input)
return result
else: # CHAT
# Para consultas conversacionales, deja que el LLM responda directamente
response = llm.invoke([HumanMessage(content=tool_input)])
return response.content
# Probar el flujo completo
print(route_and_execute("¿Cuánto es 25 * 48?"))
# El LLM extrae "25 * 48" → la calculadora recibe entrada limpia
# Salida: Resultado del cálculo: 1200.0
print(route_and_execute("¿Quién escribió Hamlet?"))
# El LLM reformatea a "autor de Hamlet" → mejor consulta de búsqueda
# Salida: Resultados de búsqueda para: autor de Hamlet
print(route_and_execute("¿Cómo estás hoy?"))
# Salida: ¡Estoy bien, gracias por preguntar!Este es el patrón de enrutamiento mínimo en su forma más simple:
- Decisión del LLM: Obtener decisión de enrutamiento del LLM
- Ejecución de código: El código Python verifica la decisión
- Invocación de herramienta: Se llama a la función apropiada
- Retorno de resultado: La salida se formatea y devuelve
La idea crítica: La salida del LLM ya no es texto para el usuario—son datos estructurados que controlan el comportamiento de tu programa. El campo action le dice a tu código qué ruta tomar, y el campo input proporciona los datos limpios para esa herramienta. Esta respuesta JSON se convierte en lógica ejecutable.
Esto es fundamentalmente diferente de un chatbot. En un chatbot, la salida del LLM va directamente al usuario. Aquí, la salida del LLM va a tu código, que luego decide qué ejecutar.
Lo que construimos en esta sección es un enrutador—un precursor de agentes completos. Demuestra el principio fundamental (el LLM controla el flujo), pero carece de la iteración y autocorrección que definen los bucles de agentes verdaderos.
Este sistema de enrutamiento simple tiene una limitación importante: no puede corregir sus propios errores. Exploremos por qué eso importa y qué viene después.
5.3) Entendiendo la terminología: Enrutador vs Agente
Lo que acabamos de construir es un enrutador. Pero ¿cómo se compara con los agentes completos? Establezcamos definiciones claras:
Enrutador (lo que acabamos de construir)
- Toma una decisión de clasificación por solicitud
- Elige qué herramienta/ruta ejecutar
- Ejecuta una vez y retorna
- Sin iteración, sin estado, sin autocorrección
- Ejemplo: Clasificador de emails, detector de intención, selector de herramientas
Asistente con llamada a herramientas (Capítulo 13)
- El LLM puede invocar herramientas directamente vía API de llamada a funciones
- Todavía típicamente de un solo turno (una solicitud → una respuesta)
- Más sofisticado que el enrutamiento, pero no necesariamente iterativo
- Ejemplo: "Buscar el clima y resumir" en una llamada
Agente (bucle completo) (Capítulos 14-17)
- Itera a través de ciclos pensar → actuar → observar
- Mantiene estado entre iteraciones
- Puede revisar decisiones basándose en resultados
- Implementa autocorrección
- Ejemplo: Asistente de depuración que prueba correcciones hasta que el código funcione
Sistema agéntico (término paraguas)
- Cualquier sistema donde la salida del LLM influye en el flujo de control
- Incluye enrutadores, asistentes con llamada a herramientas y agentes completos
- "Agéntico" describe la propiedad; "agente" describe una arquitectura específica
- Ejemplo: Todos los anteriores exhiben comportamiento agéntico
Lo que construimos en 5.2 es un enrutador—toma una decisión y la ejecuta. Esto funciona bien para tareas de clasificación directas, pero no puede manejar situaciones que requieren múltiples pasos, verificación o corrección de curso. Si la decisión inicial es subóptima o si la tarea resulta ser más compleja de lo esperado, el enrutador no tiene mecanismo para adaptarse.
Los agentes resuelven esta limitación introduciendo iteración. Pueden observar el resultado de una acción, reconsiderar su enfoque e intentar de nuevo. Esto los hace adecuados para tareas complejas de múltiples pasos donde el camino a la solución no está claro desde el principio.
En los Capítulos 14-17, aprenderás cómo construir estos bucles de agentes iterativos.
Nota: Recientemente, modelos de razonamiento como o1 y o3 pueden manejar algunas tareas de múltiples pasos internamente, reduciendo la necesidad de bucles explícitos en ciertos escenarios. Exploraremos cuándo usar bucles vs. modelos de razonamiento mientras construyes agentes reales en la Parte IV.
Lo que deberías entender ahora:
- Los sistemas agénticos comienzan con flujo de control: La salida del LLM determina qué hace tu código después
- El enrutamiento es la forma más simple: Una decisión, una herramienta, un resultado—lo que construimos en 5.2
- Los agentes reales necesitan bucles: Para manejar tareas de múltiples pasos, verificación y recuperación de errores (Capítulos 14-17)
- La autocorrección es la diferencia clave: Los agentes pueden observar resultados y ajustar su enfoque
Lo que no hemos cubierto (y no lo haremos hasta capítulos posteriores):
- Cómo componer pipelines de ejecución (Capítulo 6)
- Cómo manejar salida estructurada (Capítulo 7)
- Cómo gestionar memoria de conversación (Capítulo 8)
- Cómo definir herramientas apropiadamente (Capítulo 12)
- Cómo implementar bucles de agentes con autocorrección (Capítulos 14-17)
- Cómo hacer que los agentes estén listos para producción (Capítulos 18-26)
Este capítulo fue sobre el cambio conceptual—entender qué hace que un sistema exhiba comportamiento agéntico. Los detalles de implementación vienen después.
En el próximo capítulo, continuaremos construyendo fundamentos prácticos: componiendo pipelines de ejecución con LangChain Expression Language (LCEL). Estos son los bloques de construcción que usarás cuando implementemos agentes reales en la Parte IV.
El momento "ajá" está completo. Ahora entiendes que los sistemas agénticos son aquellos donde el LLM decide, y tu código ejecuta. Todo lo demás—herramientas, bucles, gestión de estado—se trata de hacer ese ciclo de decisión-ejecución más robusto y capaz.
Continuemos construyendo la base.