Python & AI Tutorials Logo
LangChain & LangGraph

2. Fundamentos para construir agentes

En el Capítulo 1, hiciste tu primera llamada a un LLM y viste el flujo básico de solicitud-respuesta. Ahora necesitamos entender qué estamos construyendo realmente: sistemas de IA agéntica (agentic AI). Este capítulo establece los conceptos fundamentales que usarás a lo largo del libro.

Al final de este capítulo, entenderás:

  • Qué hace que un sistema de IA sea "agéntico" (y por qué importa)
  • Por qué existen frameworks como LangChain y LangGraph
  • Cómo funcionan realmente los LLM por debajo (y por qué esto afecta al diseño de agentes)
  • La economía del uso de LLM (tokens, costes y selección de modelo)
  • Cómo escribir prompts efectivos para sistemas de agentes

Este es un capítulo conceptual: volveremos al código práctico en el Capítulo 3. Pero estos conceptos son críticos para entender las decisiones de diseño que tomarás al construir agentes.

2.1) ¿Qué es la IA agéntica? (Chatbot vs agente)

Cuando la mayoría de la gente piensa en "aplicaciones de IA", se imagina chatbots: haces una pregunta, la IA responde, termina la conversación. Pero la IA agéntica es fundamentalmente diferente. Un agente no solo responde: actúa de forma autónoma para lograr objetivos.

La distinción principal

Empecemos con una definición clara:

Chatbot: Un sistema que responde a la entrada del usuario con texto generado. La interacción es puramente conversacional. El chatbot no tiene capacidad de realizar acciones en el mundo más allá de generar su siguiente mensaje.

Agente: Un sistema que puede:

  1. Percibir su entorno (p. ej., leer el estado del pedido, comprobar inventario)
  2. Decidir qué acciones tomar (p. ej., elegir entre: crear ticket, enviar email o escalar)
  3. Actuar sobre esas decisiones (p. ej., llamar a una API, actualizar base de datos)
  4. Iterar hasta lograr un objetivo (p. ej., verificar pedido → crear ticket → confirmar → listo)

La diferencia clave es la autonomía y la acción. Un chatbot es reactivo; un agente es proactivo.

Ejemplo concreto: soporte al cliente

Veamos esta distinción en la práctica.

Enfoque de chatbot:

Usuario: "Quiero un reembolso para el pedido #12345"
 
Chatbot: "Entiendo que te gustaría un reembolso. Para procesarlo, por favor:
1. Visita nuestro portal de reembolsos en example.com/refunds
2. Introduce tu número de pedido
3. Selecciona un motivo
4. Envía el formulario
5. Espera 24-48 horas para una revisión manual
 
¿Hay algo más con lo que pueda ayudarte?"

El chatbot proporciona información pero no realiza ninguna acción. El usuario debe hacer todo el trabajo.

Enfoque de agente:

Usuario: "Quiero un reembolso para el pedido #12345"
 
Agente (razonamiento interno):
1. El usuario quiere reembolso para el pedido #12345
2. Necesito verificar que este pedido exista
3. [Llama a get_order_details(order_id="12345")]
4. Pedido encontrado, elegible para reembolso
5. [Llama a create_refund_ticket(order_id="12345", reason="customer_request")]
6. Ticket creado: TICKET-789
 
Agente: "He creado el ticket de reembolso TICKET-789 para el pedido #12345. 
Nuestro equipo de reembolsos lo procesará en un plazo de 3-5 días hábiles. 
Recibirás una confirmación por correo electrónico en breve."

El agente actuó: verificó el pedido, creó un ticket y confirmó el resultado. El problema del usuario se resuelve sin pasos manuales.

Por qué esto importa para el desarrollo

Entender esta distinción determina cómo diseñas tu sistema:

Desarrollo de chatbot:

  • Enfoque en la calidad de respuesta y el flujo conversacional
  • Principal preocupación: generar texto útil y preciso
  • Arquitectura simple: prompt → LLM → respuesta
  • No necesita integraciones externas

Desarrollo de agentes:

  • Enfoque en la toma de decisiones y la ejecución de acciones
  • Principales preocupaciones: elegir acciones correctas, manejar errores, mantener estado
  • Arquitectura compleja: percepción → razonamiento → selección de acción → ejecución → verificación
  • Requiere integraciones de herramientas, manejo de errores, gestión de estado

El espectro de autonomía

No todos los agentes son igual de autónomos. Hay un espectro:

Nivel 1: acciones asistidas

  • El agente sugiere acciones, el usuario aprueba cada una
  • Ejemplo: "Puedo crear un ticket de reembolso. ¿Quieres que continúe?"
  • Enfoque más seguro para operaciones de alto riesgo

Nivel 2: autonomía acotada

  • El agente actúa dentro de restricciones predefinidas
  • Ejemplo: puede crear tickets y enviar emails, pero no puede procesar reembolsos de más de $500 ni acceder directamente a sistemas de pago
  • Lo más común en sistemas en producción (equilibrio entre eficiencia y seguridad)

Nivel 3: autonomía total

  • El agente actúa de forma independiente para lograr objetivos
  • Ejemplo: gestiona todo el flujo de reembolso sin intervención humana
  • Requiere guardrails y monitorización robustos

Características clave de los agentes

Para resumir, un sistema de IA agéntica tiene estas propiedades centrales:

  1. Uso de herramientas: puede llamar funciones, APIs y servicios externos (sin esto, es solo un chatbot)
  2. Orientado a objetivos: trabaja hacia resultados específicos, no solo responde
  3. De múltiples pasos: divide tareas complejas en secuencias de acciones
  4. Adaptativo: ajusta el comportamiento según resultados intermedios
  5. Con estado: mantiene contexto a través de múltiples interacciones

Las dos primeras son esenciales: sin herramientas y objetivos, no tienes un agente. El resto son factores de calidad que separan a los buenos agentes de los excelentes.

Ahora entiendes qué son los agentes y por qué son potentes.

2.2) ¿Por qué LangChain y LangGraph?

Puede que te estés preguntando: "¿Por qué necesito frameworks? ¿No puedo simplemente llamar a la API de OpenAI directamente?" Vamos a explorar por qué existen los frameworks y qué problemas resuelven.

La complejidad del desarrollo de agentes

Construir un chatbot simple con llamadas directas a la API es sencillo:

python
import openai
 
response = openai.chat.completions.create(
    model="gpt-5-mini",
    messages=[{"role": "user", "content": "¡Hola!"}]
)
print(response.choices[0].message.content)

Esto funciona bien para casos de uso básicos. Pero en cuanto quieres construir un agente, la complejidad se dispara:

Reto 1: flujos de trabajo de múltiples pasos Tu agente necesita:

  • Recuperar documentos relevantes desde una base de conocimiento
  • Decidir qué herramienta llamar según la intención del usuario
  • Ejecutar la herramienta y manejar errores
  • Formatear resultados y responder al usuario

Cada paso requiere orquestación cuidadosa, manejo de errores y gestión de estado.

Reto 2: abstracción del proveedor ¿Qué pasa si quieres:

  • Cambiar de OpenAI a Anthropic o Google?
  • Usar modelos distintos para tareas distintas?
  • Hacer fallback a un modelo más barato si falla el principal?

Con llamadas crudas a la API, tendrías que reescribir una parte significativa del código para cada proveedor.

Reto 3: memoria de conversación Los agentes necesitan recordar contexto:

  • Mensajes anteriores en la conversación
  • Documentos recuperados en consultas anteriores
  • Resultados intermedios de llamadas a herramientas

Gestionar este estado manualmente es propenso a errores y tedioso.

Reto 4: integración de herramientas Tu agente necesita:

  • Definir herramientas disponibles con esquemas
  • Dejar que el LLM elija qué herramienta llamar
  • Parsear argumentos de herramienta desde la salida del LLM
  • Ejecutar herramientas de forma segura con validación
  • Manejar errores de herramientas y lógica de reintento

Esto requiere mucho código boilerplate y consideraciones de seguridad (security) en cada paso.

Reto 5: enrutamiento complejo Los agentes reales necesitan lógica condicional:

  • "Si el usuario pregunta sobre reembolsos, recupera docs de políticas"
  • "Si el usuario quiere un reembolso, crea un ticket"
  • "Si la pregunta no es del tema, rechaza amablemente"

Implementar esto con if-else se vuelve inmantenible rápidamente.

Qué aporta LangChain

LangChain es un framework para construir aplicaciones con LLM. Proporciona:

1. Abstracción de modelos

Interfaz unificada para distintos proveedores de LLM (OpenAI, Anthropic, Google, etc.).

python
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
 
# Misma interfaz, distintos proveedores
openai_llm = ChatOpenAI(model="gpt-5-mini")
anthropic_llm = ChatAnthropic(model="claude-4-5-sonnet")
 
# Ambos usan .invoke() con el mismo formato de mensajes
response = openai_llm.invoke([{"role": "user", "content": "Hola"}])

Cambia de proveedor sin reescribir la lógica de tu aplicación.

2. Cadenas componibles (LCEL)

LangChain Expression Language: una sintaxis para conectar componentes en pipelines.

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
 
# Compón un pipeline con el operador | (como pipes de Unix)
chain = prompt | llm | output_parser
 
# Ejecuta todo el pipeline con una sola llamada
result = chain.invoke({"input": "pregunta del usuario"})

Construye flujos de trabajo complejos sin pasar datos manualmente entre pasos. Aprenderemos LCEL en el Capítulo 6.

3. Memoria de conversación

Gestión del historial de mensajes para conversaciones con estado.

python
from langchain_core.chat_history import InMemoryChatMessageHistory
 
# Helper para historial de mensajes
history = InMemoryChatMessageHistory()
history.add_user_message("Hola")
history.add_ai_message("¡Hola!")
 
# Recupera mensajes cuando haga falta
messages = history.messages
response = llm.invoke(messages)

Abstrae el almacenamiento de mensajes con clases helper en lugar de gestionar listas manualmente. En esta etapa, el historial aún se pasa explícitamente al modelo. Lo integraremos en cadenas en el Capítulo 8. LangGraph (Capítulos 15+) lo hace aún más simple con gestión de estado integrada.

4. Integración de herramientas

Sistema basado en decoradores para exponer funciones Python a los LLM.

python
from langchain_core.tools import tool
 
@tool
def create_ticket(order_id: str, reason: str) -> str:
    """Crea un ticket de soporte para un pedido."""
    # Implementación aquí
    return f"Ticket creado para {order_id}"
 
# LangChain gestiona la generación de esquemas y la integración con el LLM

Convierte cualquier función Python en una herramienta que puede ser descubierta y llamada por LLM con herramientas o agentes, sin escribir manualmente esquemas JSON.

5. Cargadores de documentos y vector stores

Componentes listos para usar para cargar documentos desde varias fuentes y almacenarlos como embeddings buscables.

python
from langchain_community.document_loaders import TextLoader
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
 
# Carga documentos
docs = TextLoader("support_docs.txt").load()
 
# Crea un índice buscable
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(docs, embeddings)
 
# Recupera documentos relevantes
results = vectorstore.similarity_search("política de reembolso")

Construye sistemas RAG usando loaders y vector stores preconstruidos en lugar de implementar desde cero el parsing, embedding y retrieval.

Qué aporta LangGraph

LangGraph extiende LangChain para flujos de trabajo de agentes complejos. Proporciona:

1. Gestión de estado explícita

Define todos los datos del agente en un único esquema tipado en lugar de dispersarlo por variables.

python
from typing import TypedDict, Optional
from langchain_core.messages import BaseMessage
from langchain_core.documents import Document
 
class AgentState(TypedDict):
    messages: list[BaseMessage]
    retrieved_docs: list[Document]
    current_step: Optional[str]
 
# Todos los datos del agente viven aquí: un lugar para inspeccionar al depurar

Se acabó buscar dónde vive el dato o cómo fluye entre pasos. Los cambios de estado son explícitos: los nodos leen de state y devuelven actualizaciones. Tu IDE autocompleta nombres de campos, y los type checkers detectan errores antes de la ejecución.

2. Flujos de trabajo basados en grafos

Construye flujos declarando pasos (nodos) y sus conexiones (aristas), no escribiendo código de orquestación.

python
graph = StateGraph(AgentState)
 
# Define nodos (pasos en tu flujo de trabajo)
graph.add_node("retrieve", retrieve_docs)
graph.add_node("answer", generate_answer)
 
# Define aristas (transiciones entre pasos)
graph.add_edge("retrieve", "answer")

Defines la estructura —"retrieve se ejecuta, luego answer se ejecuta"— y LangGraph gestiona la ejecución. No hace falta escribir código de orquestación para pasar estado entre pasos. El control de flujo, como secuenciación o bifurcación, se declara en la propia estructura del grafo.

3. Enrutamiento condicional

Toma de decisiones en tiempo de ejecución sobre qué ruta seguir en el flujo de trabajo.

python
from langchain_core.messages import BaseMessage
 
def route_request(state):
    last_message = state["messages"][-1].content
    if "refund" in last_message:
        return "create_ticket"
    else:
        return "answer_question"
 
graph.add_conditional_edges(
    "classify",
    route_request,
    {
        "create_ticket": "create_ticket",
        "answer_question": "answer_question",
    }
)

La idea clave: La función de enrutamiento devuelve el nombre del siguiente nodo ("create_ticket" o "answer_question").

Por qué esto importa: Tu lógica de decisión está separada de la ejecución. ¿Cambiar reglas de enrutamiento? Edita una función. ¿Ver todas las rutas posibles? Mira la definición del grafo. ¿Depurar qué ruta se tomó? Inspecciona el trace de ejecución, sin rebuscar en llamadas a funciones anidadas.

4. Checkpointing y persistencia

El estado se checkpointa automáticamente tras cada paso, habilitando recuperación ante fallos y flujos de pausa-reanudación.

python
from langgraph.checkpoint.sqlite import SqliteSaver
 
checkpointer = SqliteSaver("agent_state.db")
graph = graph.compile(checkpointer=checkpointer)
 
# El estado se guarda automáticamente en cada paso
result = graph.invoke(
    input_state,
    config={"configurable": {"thread_id": "user-123"}}
)

Después de cada paso, LangGraph hace checkpoint del estado actual en almacenamiento persistente. Si el proceso se cae, la ejecución puede reanudarse desde el último checkpoint asociado al mismo thread ID.

El checkpointing habilita flujos de pausa y reanudación, como esperar aprobación humana, cuando se combina con enrutamiento condicional o interrupciones.

Cuándo usar cada framework

Usa LangChain cuando:

  • Construyas cadenas simples (prompt → LLM → parser)
  • Implementes sistemas RAG
  • Gestiones contexto basado en mensajes (pasando el historial de chat explícitamente)
  • Abstraigas entre proveedores de LLM

Usa LangGraph cuando:

  • Construyas flujos de trabajo de agentes de múltiples pasos
  • Implementes lógica de enrutamiento condicional
  • Gestiones estado complejo entre pasos
  • Necesites checkpointing y recuperación ante fallos

2.3) Cómo funcionan los LLM (para quienes construyen agentes)

Para construir agentes efectivos, necesitas entender cómo funcionan realmente los LLM. Esto no va sobre las matemáticas de los transformers: va sobre el modelo mental que determina cómo diseñas sistemas de agentes.

El mecanismo central: predicción de tokens

Aquí está la idea fundamental: los LLM no "saben" hechos del modo en que lo hacen las bases de datos. Predicen el siguiente token.

Vamos a desglosarlo con un ejemplo concreto.

Entrada: "The capital of France is"

Lo que podrías pensar que ocurre:

  1. El LLM "busca" la capital de Francia en su base de conocimiento
  2. El LLM "recupera" la respuesta: Paris
  3. El LLM devuelve "Paris"

Lo que ocurre realmente:

  1. El LLM convierte la entrada a tokens: ["The", "capital", "of", "France", "is"]
  2. El LLM calcula una distribución de probabilidad sobre TODOS los posibles tokens siguientes
  3. Token siguiente más probable: "Paris" (probabilidad más alta)
  4. El LLM hace sampling de la distribución (normalmente eligiendo la mayor probabilidad)
  5. El LLM devuelve "Paris"

El LLM no "sabe" que Paris es la capital. Predice que "Paris" es el siguiente token más probable dado el patrón de entrada.

(Nota: La tokenización y las probabilidades son conceptuales y varían según el modelo y el tokenizer.)

Por qué esto importa para los agentes

Este modelo de predicción de tokens tiene implicaciones profundas para el diseño de agentes:

Implicación 1: los LLM pueden alucinar

Como los LLM predicen tokens (no recuperan hechos), pueden generar información plausible pero incorrecta.

python
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke("¿Cuál es la capital de Atlantis?")
print(response.content)
 
# Nota: La salida real puede variar.
# Los modelos modernos podrían reconocer que Atlantis es ficticia y negarse a responder.
# El punto clave:
# sin verificación explícita de hechos, los LLM pueden generar información incorrecta que suena plausible.

Posible salida (histórica o sin restricciones):

La capital de Atlantis es Etheria. Según los registros de Platón, Etheria era una ciudad portuaria situada en el extremo más oriental de la isla, y su nombre derivaba de la creencia de que alcanzaba hasta los cielos.

El LLM genera una respuesta que suena razonable aunque Atlantis sea ficticia. Para agentes, esto significa:

  • Nunca confíes ciegamente en la salida del LLM
  • Valida los hechos contra fuentes autorizadas
  • Implementa guardrails

Implicación 2: el contexto lo es todo

Los LLM solo ven los tokens que les proporcionas. No tienen memoria de conversaciones previas a menos que incluyas ese contexto explícitamente.

python
# Primera llamada
response1 = llm.invoke("Me llamo Alice")
print(response1.content)  # "¡Encantado de conocerte, Alice!"
 
# Segunda llamada (invocación separada)
response2 = llm.invoke("¿Cómo me llamo?")
print(response2.content)  # "No tengo acceso a tu nombre..."

La segunda llamada no tiene contexto de la primera. Para agentes, esto significa:

  • Debes gestionar el historial de conversación
  • Importan los límites de la ventana de contexto
  • La gestión de estado es crítica

Implicación 3: los prompts son instrucciones, no consultas

Como los LLM predicen tokens, la forma en que redactas tu prompt afecta dramáticamente la calidad de salida.

python
# Prompt débil (tipo consulta)
response = llm.invoke("política de reembolso")
# Salida: "¿Qué te gustaría saber sobre la política de reembolso?"
 
# Prompt fuerte (tipo instrucción)
response = llm.invoke(
    "Eres un agente de soporte al cliente. Explica nuestra política de reembolso de forma clara y concisa."
)
# Salida: "Nuestra política de reembolso permite devoluciones en 30 días..."

Para agentes, esto significa:

  • Los prompts son tu mecanismo principal de control
  • La ingeniería de prompts es una habilidad central
  • Los mensajes del sistema definen el comportamiento del agente

Implicación 4: la salida estructurada requiere guía

Los LLM generan texto libre de forma natural. Obtener salida estructurada (JSON, formatos específicos) requiere instrucciones explícitas.

python
# Sin guía de estructura
response = llm.invoke("Extrae el ID del pedido de: 'Quiero un reembolso para el pedido #12345'")
print(response.content)
# Salida: "El ID del pedido es 12345" (texto plano, formato inconsistente)
 
# Con guía de estructura
response = llm.invoke(
    'Extrae el ID del pedido y devuelve SOLO un objeto JSON con el formato: {"order_id": "..."}\n\n'
    "Texto: 'Quiero un reembolso para el pedido #12345'"
)
print(response.content)
# Salida: {"order_id": "12345"} (estructurado, parseable)

Para agentes, esto significa:

  • Usa esquemas para acotar el formato de salida
  • Especifica explícitamente formatos de salida
  • Valida y parsea las respuestas del LLM

El texto libre está optimizado para humanos. Los agentes requieren formato explícito para asegurar legibilidad por máquina.

Determinismo, estocasticidad y LLM modernos

Los LLM son, en esencia, sistemas probabilísticos. Generan texto prediciendo los tokens siguientes más probables, no ejecutando reglas deterministas. Como resultado, la misma entrada no siempre garantiza la misma salida.

En modelos anteriores, las personas desarrolladoras controlaban explícitamente esta aleatoriedad usando parámetros como temperature. Valores más bajos producían salidas más predecibles, mientras que valores más altos fomentaban variación y creatividad.

Muchos modelos modernos orientados al razonamiento ya no exponen parámetros como temperature o top_p. En su lugar, gestionan internamente estrategias de decodificación y sampling para priorizar un razonamiento estable y estructurado. Sin embargo, esto no significa que estos modelos sean totalmente deterministas.

Incluso estos modelos no garantizan salidas idénticas. Las salidas pueden variar debido a:

  • Sampling interno: el modelo puede seguir distintos caminos de razonamiento, produciendo salidas que varían en estructura, detalle o redacción.
  • Actualizaciones del modelo: los proveedores actualizan continuamente los modelos sin avisar, así que el mismo prompt puede dar respuestas diferentes con el tiempo.
  • Filtros de seguridad: la moderación de contenido puede hacer que el modelo responda directamente en un caso, pero matice, rechace o reformule en otro.
  • Políticas de herramientas: en sistemas de agentes, el modelo puede invocar herramientas diferentes —o ninguna— para la misma entrada, cambiando rutas de ejecución.

Lo que esto significa para quienes construyen agentes:

La preocupación clave no es el ajuste de parámetros: es la predictibilidad. Los sistemas de agentes deben diseñarse asumiendo que las salidas del LLM pueden variar en redacción, estructura o incluso conclusiones, a menos que se acoten explícitamente.

Esto conduce a varios principios de diseño concretos para sistemas de agentes:

  • Nunca dependas de redacción exacta para decisiones lógicas: el control de flujo debe depender de señales estructuradas (esquemas, enums, flags), no de hacer matching de frases específicas en la salida del modelo.
  • Fuerza estructura en los límites: siempre que una salida de LLM sea consumida por código, acótala usando esquemas, validadores o formatos estrictos para que el programa nunca tenga que "interpretar" texto libre.
  • Verifica cualquier cosa que importe: los hechos que afecten dinero, permisos o acciones irreversibles deben comprobarse con herramientas o sistemas externos, no confiarse solo al modelo.
  • Trata las salidas del LLM como propuestas, no como decisiones: el modelo sugiere qué hacer, pero el sistema decide si, cuándo y cómo actuar.

En sistemas modernos de agentes, la fiabilidad viene del diseño del sistema, no del ajuste de parámetros. Cuanto más crítica sea la tarea, menos libertad debería tener el modelo, y más estructura debería imponer tu agente.

Idea clave: Construye agentes fiables mediante esquemas, validación e integración de herramientas; no esperando salidas consistentes del LLM.

En la siguiente sección, exploraremos las implicaciones económicas del procesamiento basado en tokens.

2.4) Tokens: el recurso fundamental

Los tokens son la unidad básica que procesan los LLM. Entender los tokens es esencial porque definen tanto lo que es posible (restricciones) como lo que es caro (costes) en sistemas de agentes.

¿Qué es un token?

Un token es la unidad más pequeña de texto sobre la que un LLM razona y genera.

Dependiendo del idioma y del contexto, un token puede representar:

  • Una palabra (agent)
  • Parte de una palabra (calculat, ion)
  • Un número o símbolo (#, 123)
  • Puntuación o espacios en blanco

Los tokens no son caracteres ni palabras: son unidades específicas del modelo creadas por el tokenizer.

Cada pieza de información enviada al modelo o generada por el modelo se mide en tokens:

  • Instrucciones del sistema
  • Mensajes del usuario
  • Documentos recuperados
  • Descripciones de herramientas
  • Salidas del modelo

Los tokens son la moneda fundamental de la interacción con LLM.

Tokens como restricción del sistema

Los tokens no son solo un coste: son un límite duro sobre lo que tu agente puede hacer en una sola solicitud.

Cada LLM tiene una ventana de contexto: un número máximo fijo de tokens que puede procesar a la vez.

Escenario de ejemplo: Tu agente de soporte al cliente necesita:

  • Instrucciones del sistema: 200 tokens
  • Últimos 10 mensajes: ~2.000 tokens
  • 3 artículos de ayuda recuperados: ~1.500 tokens
  • Respuesta generada: ~200 tokens
  • Total: 3.900 tokens

Si la ventana de contexto de tu modelo es de 4.000 tokens, estás al 97,5% de capacidad. Un mensaje largo más y el sistema deja de funcionar.

(Los modelos modernos normalmente tienen ventanas de contexto de 128K+ tokens, pero el principio se mantiene: el contexto es finito y debes diseñar alrededor de este límite.)

Qué pasa cuando superas el límite:

  • Se descartan mensajes antiguos → El agente olvida contexto anterior, rompiendo la continuidad de la conversación
  • Se truncan documentos recuperados → Se pierde información crítica, llevando a respuestas incorrectas
  • La solicitud falla por completo → El sistema no puede responder en absoluto

No puedes pagar para tener más espacio. La ventana de contexto es un límite duro, como intentar meter 2 litros en una botella de 1 litro.

Por eso, los agentes de larga duración deben gestionar activamente qué se mantiene en contexto y qué no. La gestión de tokens es una preocupación arquitectónica central, no un detalle de optimización.

Qué afectan directamente los tokens

Más allá del límite inmediato de la ventana de contexto, los tokens moldean dos decisiones de diseño críticas:

1. Estrategia de memoria: historial completo vs resumen

Mantener el historial completo de la conversación preserva detalle, pero hace que el uso de tokens crezca continuamente.

Una alternativa común es el resumen de memoria:

  • Reemplazar mensajes antiguos por un resumen compacto
  • Preservar la intención mientras reduces el coste de tokens

Este trade-off afecta a:

  • Coste
  • Precisión
  • Consistencia del agente a largo plazo

El diseño de memoria es, por tanto, un problema de gestión de tokens.

2. Tamaño de chunk en RAG y estrategia de retrieval

En Retrieval-Augmented Generation (RAG), los documentos se dividen en chunks antes del retrieval.

  • Chunks grandes

    • Menos llamadas de retrieval
    • Mayor coste de tokens por solicitud
    • Más contexto irrelevante
  • Chunks pequeños

    • Menor coste de tokens
    • Mayor precisión
    • Riesgo de perder información clave

El tamaño de chunk es una decisión de diseño crítica: impacta directamente tanto el coste como la calidad de la respuesta.

Economía de tokens

Entender los costes por token te ayuda a construir sistemas coste-efectivos.

Precios típicos (2026):

ModeloEntrada (por 1M tokens)Salida (por 1M tokens)
GPT-5$1.25$10.00
GPT-5-mini$0.25$2.00
Claude 4.5 Sonnet$3.00$15.00
Gemini 3 Pro$2.00$12.00
Gemini 3 Flash$0.50$3.00

Idea clave: Los tokens de salida cuestan 4–8x más que los tokens de entrada, lo que significa que la generación sin control suele ser el mayor impulsor de costes en sistemas en producción.

Estimación rápida de coste:

Para una solicitud típica con 500 tokens de entrada y 50 tokens de salida usando GPT-5-mini:

  • Entrada: (500 / 1,000,000) × $0.25 = $0.000125
  • Salida: (50 / 1,000,000) × $2.00 = $0.0001
  • Total: ~$0.000225 por solicitud

A 10.000 solicitudes/día: ~$67.5/mes

Optimización de costes en la práctica

Estrategia 1: empareja el modelo con la complejidad de la tarea

Usa modelos más pequeños y baratos para tareas simples:

python
from langchain_openai import ChatOpenAI
 
# Modelo caro para razonamiento complejo
complex_llm = ChatOpenAI(model="gpt-5")
 
# Modelo barato para tareas simples
simple_llm = ChatOpenAI(model="gpt-5-nano")
 
def get_llm_for_task(task_type):
    if task_type == "complex_reasoning":
        return complex_llm
    else:
        return simple_llm

Estrategia 2: equilibra el tamaño de contexto con la calidad

Incluye solo el contexto necesario:

python
# Eficiente: incluye solo los chunks relevantes
relevant_chunks = retrieve_top_k(user_question, k=3)  # ~500 tokens
prompt = f"Contexto: {relevant_chunks}\n\nPregunta: {user_question}"

Importante: Reducir el contexto de forma demasiado agresiva puede perjudicar la precisión de la respuesta. Equilibra ahorro de coste con calidad.

Estrategia 3: controla la longitud de salida

Limita cuánto genera el modelo:

python
# Coste controlado
llm = ChatOpenAI(model="gpt-5-mini", max_tokens=100)
response = llm.invoke(messages)
# Máximo 100 tokens de salida

Idea clave: La gestión de tokens no es solo reducir costes: es diseñar sistemas de agentes fiables y escalables dentro de restricciones duras de recursos.

En la siguiente sección, exploraremos el panorama de modelos y aprenderemos cómo elegir el modelo adecuado para cada tarea.

2.5) Comprender el panorama de modelos

Elegir el LLM adecuado para tu agente requiere equilibrar:

  • Ventana de contexto: ¿cuánto texto puede procesar el modelo?
  • Coste: ¿cuánto cuesta cada solicitud?
  • Latencia: ¿qué tan rápido responde el modelo?
  • Capacidad: ¿qué tan bien razona el modelo?

Vamos a repasar el panorama de modelos en 2026.

Familias principales de modelos

Modelos GPT de OpenAI

ModeloVentana de contextoCoste de entradaCoste de salidaLatenciaMejor para
GPT-5400K tokens$1.25 / 1M$10.00 / 1M~2–4sRazonamiento complejo, código avanzado
GPT-5-mini400K tokens$0.25 / 1M$2.00 / 1M~1.5–3sTareas generales, chat, resumir
GPT-5-nano400K tokens$0.05 / 1M$0.40 / 1M~1–2sClasificación, extracción

Modelos Claude de Anthropic

ModeloVentana de contextoCoste de entradaCoste de salidaLatenciaMejor para
Claude Opus 4.5200K tokens$5.00 / 1M$25.00 / 1M~2–4sRazonamiento profundo, análisis
Claude Sonnet 4.5200K tokens$3.00 / 1M$15.00 / 1M~1.5–3sRendimiento equilibrado, programación

Modelos Gemini de Google

ModeloVentana de contextoCoste de entradaCoste de salidaLatenciaMejor para
Gemini 3.0 Pro1M tokens$2.00 / 1M$12.00 / 1M~3–5sContexto masivo, investigación
Gemini 3.0 Flash1M tokens$0.50 / 1M$3.00 / 1M~1–2sAplicaciones de alto throughput

Funcionalidades específicas por proveedor

OpenAI:

  • Mejor para: agentes de propósito general, flujos multi-dominio amplios, function calling
  • Fuerte en: razonamiento versátil, tooling y ecosistema para devs, actualizaciones frecuentes del modelo

Anthropic:

  • Mejor para: flujos sensibles a seguridad, razonamiento estructurado y extendido
  • Fuerte en: análisis profundo, salidas metódicas, pensamiento extendido con fuerte alineación

Google:

  • Mejor para: ingesta de contexto masivo y tareas multimodales
  • Fuerte en: análisis de documentos a gran escala, comprensión multimodal, procesamiento de alto throughput

Emparejar modelos con tareas

Elige según la complejidad de la tarea, el tamaño de contexto y tus necesidades de latencia:

Por complejidad de tarea

Tareas simples → Modelos optimizados por coste (GPT-5-nano, GPT-5-mini):

  • Clasificación de intención, análisis de sentimiento, extracción de keywords, formateo simple
  • Elige cuando: el coste es la preocupación principal

Tareas moderadas → Modelo equilibrado (GPT-5-mini):

  • Preguntas y respuestas, resumir, selección de herramientas
  • Elige cuando: necesitas equilibrar coste y calidad

Tareas complejas → Modelos orientados a rendimiento (GPT-5, Claude Sonnet, Gemini Pro):

  • Razonamiento de múltiples pasos, generación de código, análisis detallado
  • Elige cuando: importa la capacidad de razonamiento y el coste es aceptable

Máxima complejidad → Modelos premium (Claude Opus):

  • Razonamiento profundamente complejo, decisiones de misión crítica
  • Elige cuando: la precisión es primordial y el coste es secundario

Por necesidades de latencia

Tiempo real (~1s o menos de latencia percibida) → Modelos rápidos (GPT-5-nano, Gemini Flash):

  • Chat de cara al usuario
  • Aplicaciones interactivas

Casi tiempo real (1-3s) → La mayoría de modelos:

  • Tareas estándar de agentes

Batch (>3s) → Modelos capaces:

  • Análisis en segundo plano

Consideraciones de ventana de contexto

Tareas estándar: todos los modelos principales soportan 200K+ tokens, suficiente para la mayoría de flujos de trabajo de agentes.

Casos especiales:

  • Necesitas 400K tokens: familia GPT-5 (análisis de documentos completos, grandes codebases)
  • Necesitas 1M tokens: modelos Gemini (libros completos, conjuntos masivos de documentos)

Consejo práctico: incluso con ventanas de contexto grandes, el retrieval selectivo (RAG) suele producir mejores resultados.

Ideas clave

  1. No existe un único modelo "mejor" → distintos modelos sobresalen en tareas distintas
  2. Empareja el modelo con la complejidad de la tarea → no pagues de más por tareas simples
  3. Ventana de contexto ≠ mejor → usa retrieval selectivo
  4. La latencia afecta la experiencia de usuario → considera el tiempo de respuesta para tareas interactivas

En la práctica: la mayoría de agentes usan múltiples modelos: modelos baratos para tareas simples, modelos capaces para razonamiento complejo. Lo implementaremos en capítulos posteriores.

En la siguiente sección, aprenderemos cómo controlar el comportamiento del modelo mediante prompting efectivo.

2.6) Fundamentos de prompting para sistemas de agentes

Los prompts son tu interfaz principal para controlar el comportamiento del LLM. Para agentes, el prompting efectivo es crítico: determina si tu agente toma decisiones correctas, llama a las herramientas adecuadas y produce salidas fiables.

La anatomía de un prompt

Un prompt completo tiene tres componentes:

1. Mensaje del sistema (rol y restricciones) Define la persona del agente, sus capacidades y límites.

2. Contexto (información relevante) Proporciona la información necesaria para completar la tarea.

3. Instrucción (tarea específica) Le dice al agente exactamente qué hacer.

Veámoslo en la práctica:

python
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-5-mini")
 
response = llm.invoke([
    # Mensaje del sistema: define el rol y las restricciones
    {
        "role": "system",
        "content": """Eres un agente de soporte al cliente de TechCorp.
        
Tus capacidades:
- Responder preguntas sobre políticas de reembolso
- Crear tickets de soporte
- Proporcionar guía de troubleshooting
 
Tus restricciones:
- Responde solo preguntas sobre productos de TechCorp
- Nunca hagas promesas sobre plazos de reembolso
- Sé siempre educado y profesional"""
    },
    
    # Mensaje del usuario: Contexto + Instrucción
    {
        "role": "user",
        "content": """Contexto: El cliente compró el portátil modelo X500 el 2026-01-15.
Hoy es 2026-02-20. Nuestra política de reembolso permite devoluciones dentro de 30 días.
 
Instrucción: El cliente quiere un reembolso. ¿Qué debería decirle?"""
    }
])
 
print(response.content)

Salida:

Entiendo que te gustaría un reembolso por tu portátil modelo X500. Desafortunadamente, 
como tu compra fue el 15 de enero y hoy es 20 de febrero, estamos 
fuera de nuestro periodo de devolución de 30 días. Sin embargo, me encantaría crear un ticket de soporte 
para explorar otras opciones, como servicio de garantía o un cambio. 
¿Te gustaría que continuara con eso?

Mensajes del sistema: fijar el comportamiento del agente

El mensaje del sistema es donde defines la personalidad y las capacidades de tu agente. Esta es la parte más importante del prompting para agentes.

Mensaje del sistema débil:

python
system_message = "Eres un asistente útil."

Mensaje del sistema fuerte:

python
system_message = """Eres un agente de soporte al cliente de TechCorp.
 
ROL:
Ayudas a clientes con solicitudes de reembolso, preguntas de productos y problemas técnicos.
 
CAPACIDADES:
- Responder preguntas usando la documentación proporcionada
- Crear tickets de soporte cuando sea necesario
- Proporcionar troubleshooting paso a paso
 
RESTRICCIONES:
- Responde solo preguntas sobre productos de TechCorp
- Si no sabes algo, dilo: nunca adivines
- Cita siempre las fuentes cuando uses documentación
- Nunca prometas plazos u resultados específicos
 
TONO:
Profesional, empático y orientado a soluciones.
"""

Por qué la versión fuerte funciona mejor:

  1. Capacidades explícitas → El agente sabe qué puede hacer
  2. Restricciones claras → El agente sabe qué evitar
  3. Tono definido → Personalidad consistente
  4. Instrucciones específicas → Reduce ambigüedad

Claridad en la instrucción: sé específico

Los LLM siguen instrucciones literalmente. Las instrucciones vagas producen resultados poco fiables.

Instrucción vaga:

python
instruction = "Ayuda al cliente con su reembolso."

Instrucción específica:

python
instruction = """Analiza la solicitud del cliente y determina:
1. ¿El pedido es elegible para reembolso? (Comprueba fecha de compra vs política de reembolso)
2. Si es elegible: Explica el proceso de reembolso
3. Si no es elegible: Explica por qué y ofrece alternativas
 
Formatea tu respuesta como:
- Elegibilidad: [SÍ/NO]
- Motivo: [Explicación breve]
- Siguientes pasos: [Qué debe hacer el cliente]
"""

Gestión de contexto: proporciona lo necesario

Los agentes necesitan contexto para tomar decisiones, pero demasiado contexto desperdicia tokens y confunde al modelo.

Sobre-contextualización (desperdicio):

python
context = f"""
Historia de la empresa: TechCorp fue fundada en 1995...
Catálogo de productos: Vendemos 500+ productos incluyendo...
Política de reembolso: {refund_policy_text}
Política de envío: {shipping_policy_text}
Política de garantía: {warranty_policy_text}
Historial del cliente: {full_customer_history}
"""
# 5000+ tokens, la mayoría irrelevantes

Contexto selectivo (eficiente):

python
context = f"""
Política relevante: {refund_policy_text}
Detalles del pedido: {order_details}
"""
# 200 tokens, todo relevante

Formato de salida: estructura tus respuestas

Para agentes, a menudo necesitas salida estructurada (JSON, formatos específicos) en lugar de texto libre.

Salida no estructurada (difícil de parsear):

python
response = llm.invoke([
    {"role": "system", "content": "Eres un agente de soporte."},
    {"role": "user", "content": "¿Deberíamos crear un ticket para esta solicitud de reembolso?"}
])
print(response.content)
# Salida: "Sí, creo que deberíamos crear un ticket porque..."
# Problema: difícil de parsear, formato inconsistente

Salida estructurada (fácil de parsear):

python
response = llm.invoke([
    {"role": "system", "content": """Eres un agente de soporte.
    
Responde SIEMPRE en este formato JSON:
{
  "action": "CREATE_TICKET" or "ANSWER_QUESTION" or "ESCALATE",
  "reason": "Explicación breve",
  "response_text": "Qué decirle al cliente"
}"""},
    {"role": "user", "content": "El cliente quiere un reembolso para el pedido #12345, comprado hace 40 días"}
])
print(response.content)

Salida:

json
{
  "action": "CREATE_TICKET",
  "reason": "El pedido está fuera de la ventana de reembolso de 30 días, requiere revisión manual",
  "response_text": "He creado un ticket de soporte para revisar tu solicitud de reembolso. Nuestro equipo se pondrá en contacto contigo en un plazo de 24 horas."
}

Usaremos esquemas de Pydantic para salida estructurada robusta en el Capítulo 7.

Ejemplos few-shot: muestra, no solo digas

Para tareas complejas, proporcionar ejemplos es más efectivo que instrucciones largas.

Zero-shot (solo instrucciones):

python
prompt = """Extrae el ID del pedido, el nombre del producto y el problema de los mensajes del cliente.
 
Mensaje del cliente: "My laptop X500 order #12345 won't turn on"
"""
# Al modelo podría costarle el formato

Few-shot (con ejemplos):

python
prompt = """Extrae el ID del pedido, el nombre del producto y el problema de los mensajes del cliente.
 
Ejemplo 1:
Entrada: "My laptop X500 order #12345 won't turn on"
Salida: {"order_id": "12345", "product": "laptop X500", "issue": "won't turn on"}
 
Ejemplo 2:
Entrada: "Order 67890 - phone not charging"
Salida: {"order_id": "67890", "product": "phone", "issue": "not charging"}
 
Ahora extrae de este mensaje:
Entrada: "My tablet order #11111 has a cracked screen"
Salida:
"""

El modelo aprende el patrón a partir de ejemplos y lo aplica de forma consistente.

Patrones de ingeniería de prompts para agentes

Patrón 1: chain of thought (razonamiento)

Para decisiones complejas, pide al modelo que "piense paso a paso":

python
prompt = """Necesitas decidir si crear un ticket de soporte.
 
Piensa en esto paso a paso:
1. ¿Qué está pidiendo el cliente?
2. ¿Esto se puede responder con documentación existente?
3. ¿Esto requiere intervención manual?
4. ¿Qué acción deberíamos tomar?
 
Mensaje del cliente: "Quiero un reembolso para el pedido #12345 pero perdí el recibo"
 
Razonamiento:
"""

El modelo mostrará su razonamiento explícitamente, haciendo las decisiones más transparentes y fiables.

Patrón 2: generación acotada (seguridad)

Limita las salidas posibles del modelo:

python
prompt = """Clasifica la intención del cliente. Responde con EXACTAMENTE UNA de estas opciones:
- REFUND_REQUEST
- PRODUCT_QUESTION
- TECHNICAL_ISSUE
- OFF_TOPIC
 
Mensaje del cliente: "¿Cómo restablezco mi contraseña?"
 
Clasificación:
"""

Esto evita que el modelo genere salidas inesperadas. Al acotar explícitamente las opciones:

  • Evita errores de parsing (siempre una de cuatro opciones definidas)
  • Bloquea acciones no intencionadas (evita la ejecución de acciones no definidas)
  • Simplifica la depuración (un espacio de salida limitado hace más fácil rastrear problemas)

Patrón 3: autocrítica (calidad)

Pide al modelo que verifique y mejore su propia salida:

python
prompt = """Genera una respuesta al cliente y luego críticala.
 
Mensaje del cliente: "Quiero un reembolso"
 
Paso 1 - Genera respuesta:
[Tu respuesta aquí]
 
Paso 2 - Crítica:
- ¿Esta respuesta es precisa?
- ¿Es útil?
- ¿Sigue la política de la empresa?
- ¿Qué podría mejorarse?
 
Paso 3 - Respuesta final (incorporando la crítica):
[Respuesta mejorada aquí]
"""

Este enfoque de múltiples pasos suele producir salidas de mayor calidad porque:

  • Detecta errores temprano: el modelo revisa su propio razonamiento antes de finalizar
  • Mejora el tono y la claridad: la autorreflexión ayuda a identificar lenguaje poco claro o inadecuado
  • Asegura el cumplimiento de políticas: el paso de crítica verifica la adhesión a las directrices

Errores comunes de prompting

Error 1: asumir que el modelo "sabe" cosas

Los LLM no tienen acceso a información en tiempo real ni a contexto implícito. Proporciona siempre de forma explícita todos los datos necesarios.

python
# Mal: asume que el modelo sabe la fecha actual
prompt = "¿Este pedido es elegible para reembolso? Pedido #12345"
 
# Bien: proporciona toda la información necesaria
prompt = f"""¿Este pedido es elegible para reembolso?
Pedido #12345
comprado {purchase_date}
Hoy: {current_date}
Política: devoluciones de 30 días
"""

Error 2: instrucciones ambiguas

Verbos vagos como "gestionar", "procesar" o "encargarte de" dejan demasiado margen para la interpretación. Sé explícito sobre la acción exacta necesaria.

python
# Mal: ¿qué significa "gestionar"?
prompt = "Gestiona esta solicitud de reembolso"
 
# Bien: acción explícita
prompt = "Determina si esta solicitud de reembolso es elegible. Si sí, crea un ticket. Si no, explica por qué."

Error 3: sobrecargar con contexto

Incluir información irrelevante desperdicia tokens, aumenta la latencia y puede confundir al modelo. Recupera solo lo necesario para la tarea específica.

python
# Mal: 10.000 tokens de contexto, la mayoría irrelevante
prompt = f"""
{entire_knowledge_base}
 
Pregunta: ¿Cuál es la política de reembolso?
"""
 
# Bien: recupera solo la sección relevante
prompt = f"""
{refund_policy_section}
 
Pregunta: ¿Cuál es la política de reembolso?
"""

Error 4: formato inconsistente

Cuando el formato de salida varía, el código downstream se rompe. Especifica siempre el formato exacto que esperas, especialmente para datos estructurados.

python
# Mal: a veces JSON, a veces texto plano
prompt = "Responde con tu decisión"
 
# Bien: especifica siempre el formato
prompt = 'Responde en formato JSON: {"decision": "...", "reason": "..."}'

Ideas clave

El prompting efectivo para agentes requiere:

  1. Mensajes de sistema claros → Definen rol, capacidades, restricciones
  2. Instrucciones específicas → Le dicen al modelo exactamente qué hacer
  3. Contexto selectivo → Proporciona solo información relevante
  4. Salida estructurada → Especifica el formato explícitamente
  5. Ejemplos few-shot → Muestran el patrón que quieres
  6. Prompts de razonamiento → Piden pensamiento paso a paso

El prompting es una habilidad que mejora con la práctica. A lo largo de este libro, verás estos patrones aplicados en sistemas reales de agentes.


Resumen del capítulo

Ahora entiendes los conceptos fundamentales para construir sistemas de IA agéntica:

IA agéntica vs chatbots:

  • Los agentes actúan de forma autónoma para lograr objetivos
  • Los agentes usan herramientas y toman decisiones de múltiples pasos
  • Los agentes mantienen estado y se adaptan según los resultados

Por qué importan los frameworks:

  • LangChain proporciona abstracción de modelos, cadenas, memoria, herramientas y RAG
  • LangGraph añade gestión de estado, enrutamiento y checkpointing
  • Los frameworks reducen boilerplate y habilitan flujos de trabajo complejos

Mecánica de LLM:

  • Los LLM predicen tokens; no recuperan hechos
  • El contexto es explícito, no implícito
  • Los prompts son instrucciones, no consultas
  • La salida estructurada requiere guía
  • Las ventanas de contexto son finitas

Economía de tokens:

  • Los tokens de salida cuestan 4–8x más que los tokens de entrada
  • La elección de modelo tiene un impacto de coste de 10-100x
  • El contexto selectivo reduce costes de forma drástica
  • Monitorización y presupuestos evitan gasto descontrolado

Selección de modelo:

  • Empareja el modelo con la complejidad de la tarea
  • Considera requisitos de contexto y restricciones de latencia
  • Usa arquitecturas multi-modelo para optimización de coste
  • Empieza barato, mejora solo si hace falta

Fundamentos de prompting:

  • Los mensajes del sistema definen el comportamiento del agente
  • Las instrucciones específicas producen resultados fiables
  • El contexto selectivo mejora la calidad y reduce el coste
  • La salida estructurada habilita parsing y validación
  • Los ejemplos few-shot enseñan patrones de forma efectiva