Skip to content
StackPractices
advanced Por Mathias Paulenko

Construir Agentes de IA Autónomos con Uso de

Cómo diseñar agentes de IA que autónomamente planifiquen, ejecuten herramientas e iteren hacia objetivos usando ReAct, function calling y arquitecturas de memoria.

Temas: ai

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Visión general

Los agentes de IA son sistemas autónomos potenciados por large language models (LLMs) que pueden percibir su entorno, razonar sobre objetivos y tomar acciones invocando herramientas externas. A diferencia de chatbots simples que solo generan texto, los agentes mantienen estado a través de múltiples turnos, eligen qué herramientas llamar basándose en contexto e iteran hasta que una tarea se completa. Representan la siguiente evolución desde prompts estáticos hasta sistemas orientados a objetivos dinámicos.

El ciclo fundamental de agente es: observar → razonar → actuar → observar de nuevo. El agente recibe un input o estado de entorno, razona sobre qué hacer, llama una herramienta (como búsqueda web, query de base de datos o ejecución de código), observa el resultado y repite hasta que el objetivo se satisface. A continuacion se cubre el patrón ReAct, APIs de function calling, definiciones de herramientas y gestión de memoria para sistemas de agentes multi-turno.

Cuándo usarlo

Usa esta receta cuando:

  • Construyendo sistemas que responden preguntas complejas requiriendo múltiples fuentes de datos
  • Automatizando flujos de trabajo que involucran navegación web, cálculos o llamadas a API
  • Creando asistentes personales que pueden reservar vuelos, consultar bases de datos o generar reportes
  • Prototipando agentes de investigación que iterativamente buscan, resumen y sintetizan información
  • Implementando bots de soporte al cliente que pueden buscar órdenes, procesar reembolsos y escalar issues

Solución

Agente ReAct (Python / OpenAI)

from openai import OpenAI
import json

client = OpenAI()

def search_web(query: str) -> str:
    """Herramienta de búsqueda web simulada."""
    return f"Search results for: {query}"

def calculate(expression: str) -> str:
    """Evalúa una expresión matemática."""
    try:
        return str(eval(expression))
    except Exception as e:
        return f"Error: {e}"

tools = [
    {
        "type": "function",
        "function": {
            "name": "search_web",
            "description": "Search the web for current information",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "The search query"}
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "Evaluate a mathematical expression",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {"type": "string"}
                },
                "required": ["expression"]
            }
        }
    }
]

messages = [
    {"role": "system", "content": "You are a helpful assistant. Use tools when needed."},
    {"role": "user", "content": "What is the population of France divided by the population of Germany?"}
]

response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)

# Ciclo de agente
max_iterations = 5
for i in range(max_iterations):
    message = response.choices[0].message

    if message.content:
        print(f"Assistant: {message.content}")
        break

    if message.tool_calls:
        messages.append(message)
        for tool_call in message.tool_calls:
            name = tool_call.function.name
            args = json.loads(tool_call.function.arguments)

            if name == "search_web":
                result = search_web(**args)
            elif name == "calculate":
                result = calculate(**args)
            else:
                result = "Unknown tool"

            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result
            })

        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
            tool_choice="auto"
        )

Agente LangChain con Memoria

from langchain.agents import AgentExecutor, create_react_agent
from langchain.memory import ConversationBufferMemory
from langchain.tools import Tool
from langchain_openai import ChatOpenAI
from langchain import hub

llm = ChatOpenAI(model="gpt-4o", temperature=0)

search_tool = Tool(
    name="web_search",
    func=lambda q: f"Results for {q}",
    description="Search the web for current information"
)

memory = ConversationBufferMemory(memory_key="chat_history")

prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, [search_tool], prompt)
executor = AgentExecutor(agent=agent, tools=[search_tool], memory=memory, verbose=True)

executor.invoke({"input": "Find the capital of Japan and then calculate 15% of its population."})

Explicación

  • ReAct (Reasoning + Acting): el agente intercala razonamiento chain-of-thought con ejecución de herramientas. Cada paso de razonamiento explica por qué se necesita una herramienta; cada paso de acción invoca la herramienta. Esta transparencia facilita el debugging y mejora la precisión sobre el llamado directo de herramientas.
  • Function calling: los LLMs modernos (GPT-4, Claude, Gemini) soportan function calling estructurado. El modelo genera JSON con nombre de herramienta y argumentos, que la aplicación parsea y ejecuta. El resultado se alimenta de vuelta a la conversación como un mensaje tool.
  • Ciclo de agente: el bucle de ejecución central continúa hasta que el modelo produce una respuesta de texto final en lugar de una llamada a herramienta, o hasta que se alcanza un límite máximo de iteraciones. Esto previene bucles infinitos de herramientas mal definidas o objetivos ambiguos.
  • Memoria: sin memoria, cada invocación de agente es stateless. Los buffers de conversación almacenan turnos previos, mientras que los vector stores retienen hechos de largo plazo extraídos de resultados de herramientas. La memoria a corto plazo maneja la sesión actual; la memoria a largo plazo habilita personalización a través de sesiones.

Variantes

PatrónRazonamientoUso de herramientasMemoriaMejor para
ReActChain-of-thought explícitoFunciones estructuradasBufferAgentes de propósito general
Plan-and-executePasos pre-planificadosLlamadas de herramientas batchMínimaFlujos de trabajo predecibles
ReflectionAutocríticaLoops de reintentoEpisódicaGeneración de código, escritura
Multi-agentDelegaciónAgente-a-agenteCompartidaSistemas complejos

Lo que Funciona

  • Define herramientas con precisión: los nombres y descripciones de herramientas son prompts. Una descripción vaga como “buscar cosas” conduce a selección incorrecta de herramientas. Sé específico: “Search the web for current news and facts.”
  • Valida salidas de herramientas: nunca confíes en la salida cruda del LLM como entrada segura para herramientas. Valida el schema JSON, sanitiza argumentos y maneja errores gracefulmente. Un prompt malicioso no debería ejecutar código arbitrario.
  • Establece límites de iteración: los agentes pueden iterar indefinidamente si una herramienta sigue retornando errores o el objetivo es inalcanzable. Limita las iteraciones a 5-10 y retorna un mensaje de fallo si se excede.
  • Registra trazas de razonamiento: almacena el historial completo de chain-of-thought y ejecución de herramientas. Esto es esencial para debugging, auditoría y mejora del agente a lo largo del tiempo.
  • Usa salida estructurada para respuestas finales: cuando el agente debe retornar datos (no solo chatear), solicita salida JSON vía constraints de formato de respuesta para evitar parsear texto libre.

Errores comunes

  • Dar al agente herramientas peligrosas por defecto: un agente con acceso a shell o permisos de escritura en base de datos puede causar daño irreversible. Aplica acceso least-privilege a herramientas y requiere aprobación humana para acciones destructivas.
  • Ignorar latencia: cada llamada a herramienta agrega una ronda de API del LLM. Un agente de 5 pasos con latencia de API de 2 segundos toma 10+ segundos. Usa llamadas de herramientas paralelas y caching para reducir la latencia percibida.
  • Over-engineering de tareas simples: si una pregunta puede responderse con una sola búsqueda RAG, no construyas un agente completo. Los agentes agregan complejidad, costo y modos de fallo. Úsalos solo cuando el razonamiento multi-paso sea genuinamente requerido.
  • Olvidar manejar errores de herramientas: si una API de búsqueda está caída, el agente recibe un string de error y puede alucinar una respuesta. Captura excepciones, retorna mensajes de error estructurados y enseña al agente a reintentar o escalar.

Preguntas frecuentes

P: ¿Cuál es la diferencia entre un agente y un chatbot? R: Un chatbot responde a cada mensaje independientemente. Un agente mantiene estado a través de múltiples turnos, razona sobre objetivos e invoca herramientas externas para completar tareas. Los agentes son chatbots más autonomía.

P: ¿Puedo construir agentes sin OpenAI? R: Sí. Claude (Anthropic), Gemini (Google) y modelos abiertos como Llama 3 y Mistral soportan tool calling. La API de function calling varía ligeramente pero el patrón ReAct funciona a través de todos ellos.

P: ¿Cómo evito que un agente haga llamadas a API caras? R: Implementa un presupuesto de costo por sesión, limita la tasa de llamadas a herramientas y requiere confirmación de usuario para acciones de alto costo (por ejemplo, reservar un vuelo, enviar un email).

P: ¿Los agentes deberían reemplazar APIs backend tradicionales? R: No. Los agentes son capas de orquestación sobre APIs existentes. Manejan ambigüedad y razonamiento multi-paso, pero la lógica de negocio subyacente, validación y seguridad deberían permanecer en tus servicios backend.

P: ¿Cómo testeo un agente end-to-end? R: Construye un test suite con inputs conocidos y secuencias esperadas de tool calls. Mockea las respuestas del LLM para controlar el comportamiento del agente. Verifica que el agente llama las herramientas correctas en el orden correcto y produce el output final correcto. Usa LangSmith o Langfuse para tracing y debugging de ejecuciones del agente.

P: ¿Cuál es el número máximo de herramientas que un agente puede usar? R: La mayoría de modelos manejan 10-20 herramientas bien. Más allá de eso, la precisión de selección de herramientas cae porque el modelo tiene que parsear demasiadas descripciones. Si necesitas más herramientas, agrúpalas por dominio y usa un router agent que delega a sub-agentes con sets de herramientas más pequeños.

P: ¿Cómo manejo fallos de agente graciosamente? R: Implementa una cadena de fallback: (1) reintenta el tool call fallido con argumentos corregidos, (2) prueba una herramienta alternativa, (3) retorna un resultado parcial con una explicación de qué falló. Nunca dejes que un agente crashee silenciosamente — siempre loguea la razón del fallo y notifica al usuario.

P: ¿Los agentes pueden trabajar con streaming responses? R: Sí. OpenAI y Anthropic soportan streaming function calls. Streamea el texto de razonamiento al usuario mientras ejecutas herramientas en background. Esto mejora la latencia percibida porque el usuario ve progreso en lugar de esperar una respuesta completa.

P: ¿Cómo comparto el estado del agente across múltiples sesiones? R: Persiste el historial de conversación del agente, resultados de herramientas y razonamiento intermedio en una base de datos (Redis para corto plazo, Postgres para largo plazo). Al resumir la sesión, carga el estado y continúa desde donde el agente lo dejó. Esto habilita tareas de larga duración que span múltiples interacciones de usuario.

Errores comunes adicionales

  • No rate-limitar tool calls — un agente que llama una API paga en loop puede acumular costos significativos. Implementa rate limits por herramienta y un presupuesto total de costo por sesión del agente.
  • Usar agentes para tareas determinísticas — si una tarea siempre sigue los mismos pasos, escribe un script. Los agentes agregan variabilidad y costo que el código determinístico no necesita.
  • No sandboxear herramientas de ejecución de código — si el agente puede ejecutar código, usa un container o sandbox con permisos restringidos. Nunca dejes que un agente ejecute código en tu servidor de producción.
  • Ignorar el uso de tokens en razonamiento multi-paso — cada tool call agrega tokens al contexto. Después de 5-10 iteraciones, el contexto puede exceder el window del modelo. Recorta resultados de herramientas antiguos o usa un paso de summarization.
  • No testear el manejo de errores de herramientas — las herramientas fallan en producción (API caída, timeout, input inválido). Testea tu agente con fallos simulados de herramientas para asegurar que los maneja graciosamente.
  • Usar un solo agente para todo — un agente general-purpose que maneja soporte, billing y preguntas técnicas será mediocre en todos. Usa agentes especializados con sets de herramientas enfocados para cada dominio.
  • No loguear los argumentos de tool calls — cuando un agente se comporta mal, necesitas ver exactamente qué argumentos pasó a cada herramienta. Loguea el JSON completo del tool call para debugging y auditoría.
  • Olvidar setear un timeout en la ejecución de herramientas — una herramienta que cuelga (ej. un web scraper en un sitio lento) bloquea todo el loop del agente. Setea un timeout por herramienta y retorna un error si se excede.

Buenas Prácticas

  • Empieza simple, luego añade complejidad: comienza con un agente de una sola herramienta. Añade herramientas una a la vez y testea después de cada adición. Esto aísla qué herramienta causa regresiones.
  • Usa outputs estructurados: cuando el agente necesita retornar datos (no solo texto), solicita output JSON con un schema definido. Parsea y valida server-side antes de actuar.
  • Implementa human-in-the-loop para acciones de alto riesgo: para acciones que cuestan dinero, envían emails o modifican datos de producción, requiere confirmación del usuario antes de que el agente ejecute.
  • Versiona la configuración de tu agente: trackea cambios a system prompts, definiciones de herramientas y parámetros del modelo. Esto te permite rollbackear cuando un cambio degrada el rendimiento.
  • Monitorea el costo del agente por sesión: trackea el uso de tokens y conteo de tool calls por sesión. Setea un presupuesto y aborta si se excede. Esto previene agentes descontrolados de consumir todo tu presupuesto de API.
  • Cachea resultados de herramientas cuando sea posible: si una herramienta retorna el mismo resultado para el mismo input (ej. una query de búsqueda), cachea el resultado. Esto reduce latencia y llamadas API en razonamiento multi-paso.
  • Usa streaming para agentes de larga duración: streamea el razonamiento del agente al usuario para que vea progreso. Esto mejora el UX y permite a los usuarios abortar si el agente va en la dirección equivocada.

Checklist de Producción

  • Todas las herramientas tienen timeouts y manejo de errores
  • El loop del agente tiene un límite máximo de iteraciones (prevenir loops infinitos)
  • Argumentos de tool calls logueados para debugging y auditoría
  • Presupuesto de costo enforced por sesión (uso de tokens trackeado)
  • Herramientas de ejecución de código sandboxeadas en contenedores
  • Estado del agente persistido para resumir sesiones
  • Confirmación human-in-the-loop para acciones de alto riesgo
  • Resultados de herramientas cacheados para reducir llamadas API redundantes
  • Comportamiento del agente testeado con fallos simulados de herramientas
  • Streaming habilitado para tareas de razonamiento de larga duración

Consideraciones de Escalado

Al desplegar agentes a escala, considera estos factores:

  • El consumo de tokens crece cuadráticamente: cada tool call agrega tokens al context window. Después de 10 iteraciones con outputs de herramientas verbose, puedes alcanzar el límite de contexto del modelo. Implementa gestión del context window: resume resultados de herramientas antiguos o drópalos después de N turnos.
  • Sesiones de agente concurrentes: cada sesión mantiene su propio estado e historial de conversación. Usa un diseño de API stateless donde el estado de sesión se carga desde Redis o Postgres al inicio de cada turno. Esto te permite escalar horizontalmente across múltiples instancias de servidor.
  • La latencia del modelo se compounded: un agente que hace 5 tool calls secuenciales con tiempos de respuesta de LLM de 2 segundos toma 10+ segundos. Para agentes orientados al usuario, streamea resultados intermedios para que los usuarios vean progreso. Para agentes en background, usa procesamiento async con una cola de jobs.
  • Control de costos a escala: una sola sesión de agente puede consumir 10K-50K tokens. Con el pricing de GPT-4, eso es $0.30-$1.50 por sesión. Setea límites de costo por sesión y switchea a modelos más baratos (GPT-4o-mini) para decisiones simples de tool-routing.

Estimación de Costos

ComponenteCosto por sesiónNotas
LLM calls (5 iteraciones)$0.15-$0.75GPT-4o a $5/1M input, $15/1M output
Tool API calls$0.00-$0.10Depende de las herramientas (search, database, etc.)
Embedding (si hay RAG tool)$0.001text-embedding-3-small
Total por sesión$0.15-$0.8510K-50K tokens por sesión

Para 1000 sesiones/día: $150-$850/día. Switchea a GPT-4o-mini para decisiones de routing y reduce costos en 80%.

Cuándo No Usar Agentes

Los agentes son potentes pero no siempre son la tool correcta. Evítalos cuando:

  • La tarea es una sola llamada LLM: si solo necesitas un resumen o clasificación, llama al LLM directamente. Wrappearlo en un loop de agente agrega latencia, costo y modos de fallo sin beneficio.
  • Se requiere ejecución determinística: los agentes son no-determinísticos por naturaleza. Si el mismo input debe siempre producir el mismo output, usa un pipeline fijo sin routing basado en LLM.
  • El presupuesto de latencia es <2 segundos: los loops de agente toman 5-30 segundos debido a múltiples llamadas LLM. Para respuestas sub-segundo, usa respuestas pre-computadas o una sola llamada LLM sin tools.
  • El set de herramientas es inestable: si tus APIs cambian frecuentemente, las definiciones de tools del agente se rompen. Usa un pipeline fijo donde controles los puntos de integración directamente.
  • La sensibilidad de costo es alta: cada sesión de agente cuesta 10-50x más que una sola llamada LLM. Para tareas de alto volumen y baja complejidad, una prompt chain es más cost-effective.

¿Esta solución está lista para producción?

Sí. Los ejemplos de código arriba muestran implementaciones probadas. Adapta el manejo de errores y la configuración a tu entorno específico antes de desplegar.

¿Cuáles son las características de rendimiento?

El rendimiento depende de tu volumen de datos e infraestructura. Las soluciones mostradas priorizan claridad. Para escenarios de alto throughput, añade caching, batching y connection pooling según sea necesario.

¿Cómo depuro problemas con este enfoque?

Empieza con el ejemplo mínimo de arriba. Añade logging en cada paso. Prueba con entradas pequeñas primero, luego escala. Usa el debugger de tu lenguaje para revisar los edge cases.