Construir Agentes de IA Autónomos con Uso de Herramientas
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.
Visión general
Un chatbot responde; un agente actúa. Donde un simple chatbot genera una respuesta y se detiene, un agente sigue: mira la tarea, decide qué herramienta le serviría, la llama, lee el resultado y vuelve a decidir hasta terminar. El salto desde prompts estáticos a agentes es exactamente ese ciclo: observar → decidir → actuar → repetir, con el modelo dirigiendo.
Ese ciclo es lo que hace funcionar el patrón ReAct: pasos de razonamiento alternan con llamadas a herramientas para que el agente pueda planear, actuar y corregirse sobre la marcha. Y para los casos en los que el ciclo aún se rompe, el patrón LLM Fallback mantiene el sistema en pie.
A continuación se cubre el patrón ReAct, las APIs de function calling, la definición de herramientas y la gestión de memoria para sistemas de agentes multi-turno.
Cuándo usarlo
Usa esta receta cuando:
- Tu sistema responde preguntas complejas que requieren múltiples fuentes de datos.
- Automatizas flujos de trabajo que mezclan navegación web, cálculos o llamadas a API.
- Construyes asistentes personales que pueden reservar vuelos, consultar bases de datos o generar reportes.
- Prototipas agentes de investigación que iterativamente buscan, resumen y sintetizan información.
- Implementas bots de soporte al cliente que pueden buscar órdenes, procesar reembolsos y escalar issues.
No uses un agente cuando una sola llamada al LLM o una búsqueda RAG responda la pregunta: un agente agrega latencia, costo y modos de fallo sin beneficio.
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": "Busca en la web información actual",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "The search query"}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "Evalúa una expresión matemática",
"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="Busca en la web información actual"
)
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 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.
El Loop del Agente
Todo agente — ReAct, planner-executor, o un loop casero — corre el mismo ciclo: observar el estado, decidir la siguiente acción, llamar a una herramienta, devolver el resultado, repetir hasta terminar o rendirse. El loop es donde los agentes ganan realmente su complejidad; acierta el loop y el resto es fontanería.
def run_agent(task: str, max_steps: int = 10) -> str:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": task},
]
for step in range(max_steps):
response = client.chat.completions.create(
model="gpt-5",
messages=messages,
tools=tools,
tool_choice="auto",
)
msg = response.choices[0].message
messages.append(msg)
# Sin tool calls -> el agente decidió que terminó
if not msg.tool_calls:
return msg.content
# Ejecutar cada tool call y devolver resultados
for call in msg.tool_calls:
fn = TOOL_MAP.get(call.function.name)
try:
result = fn(**json.loads(call.function.arguments)) if fn else f"Tool desconocida: {call.function.name}"
except Exception as e:
result = f"Error de tool: {e}" # devolver el fallo — el modelo puede reintentar
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": str(result)[:4000], # acotar la salida o el contexto explota
})
return "Máximo de pasos alcanzado — la tarea puede estar incompleta"
Tres detalles separan un loop que funciona de una demo: max_steps como techo duro (los agentes se loopean para siempre en tareas ambiguas), los errores de tool devueltos como resultados en lugar de lanzados (el modelo a menudo puede recuperarse), y la salida de tools acotada (un resultado de búsqueda de 100KB se come el contexto).
Cuando el Loop Falla
Los modos de fallo a planificar: loops infinitos (el agente no sabe que terminó — siempre acota los pasos), alucinación de tools (el modelo llama tools que no existen — valida contra TOOL_MAP), hinchazón de contexto (salidas largas expulsan mensajes tempranos — acota y resume), y coste desbocado (cada iteración es una llamada completa — un agente de 10 pasos en modelos premium no es gratis).
Variantes
|| Patrón | Razonamiento | Uso de herramientas | Memoria | Mejor para | ||--------|------------|---------------------|---------|------------| || ReAct | Chain-of-thought explícito | Funciones estructuradas | Buffer | Agentes de propósito general | || Plan-and-execute | Pasos pre-planificados | Llamadas de herramientas batch | Mínima | Flujos de trabajo predecibles | || Reflection | Autocrítica | Loops de reintento | Episódica | Generación de código, escritura | || Multi-agent | Delegación | Agente-a-agente | Compartida | Sistemas 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: “Busca en la web noticias y datos actuales”.
- Valida salidas de herramientas: nunca confíes en la salida cruda del LLM como entrada segura para herramientas. 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: 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 restricciones 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.
- 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.
Solución de Problemas
- Los outputs del modelo son inconsistentes: usa temperature 0 para tareas determinísticas, emplea seed si la API lo soporta y versiona el prompt.
- Prompt injection filtra contexto: separa el input del usuario de las instrucciones del sistema.
- Costos altos de tokens: cachea embeddings, resume contextos largos y elige modelos más pequeños para tareas simples.
- La recuperación devuelve chunks irrelevantes: ajusta tamaño de chunk, overlap y filtros de metadata. Evalúa las métricas de recuperación por separado de la generación.
- Los scores de evaluación no coinciden con el juicio humano: la revisión humana sigue siendo la verdad de referencia.
Consideraciones de Escalado
Al desplegar agentes a escala, considera estos factores:
- El consumo de tokens crece cuadráticamente: cada tool call agrega tokens a la ventana de contexto. Después de 10 iteraciones con salidas de herramientas verbosas, puedes alcanzar el límite de contexto del modelo.
- Sesiones de agente concurrentes: cada sesión mantiene su propio estado e historial de conversación. Esto te permite escalar horizontalmente entre múltiples instancias de servidor.
- La latencia del modelo se acumula: 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, transmite resultados intermedios para que los usuarios vean progreso.
- Control de costos a escala: una sola sesión de agente puede consumir 10K-50K tokens. A los precios de GPT-4, eso es $0.30-$1.50 por sesión. Establece límites de costo por sesión y cambia a modelos más baratos (GPT-4o-mini) para decisiones simples de enrutamiento de herramientas.
Estimación de Costos
|| Componente | Costo por sesión | Notas | ||-----------|-----------------|-------| || LLM calls (5 iteraciones) | $0.15-$0.75 | GPT-4o a $5/1M input, $15/1M output | || Tool API calls | $0.00-$0.10 | Depende de las herramientas (search, database, etc.) | || Embedding (si hay RAG tool) | $0.001 | text-embedding-3-small | || Total por sesión | $0.15-$0.85 | 10K-50K tokens por sesión |
Para 1000 sesiones/día: $150-$850/día. Cambia a GPT-4o-mini para decisiones de enrutamiento y reduce costos en 80%.
Cuándo No Usar Agentes
Los agentes son potentes pero no siempre son la herramienta correcta. Evítalos cuando:
- La tarea es una sola llamada LLM: si solo necesitas un resumen o clasificación, llama al LLM directamente. Envolverlo 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.
- El presupuesto de latencia es <2 segundos: los loops de agente toman 5-30 segundos debido a múltiples llamadas LLM.
- El set de herramientas es inestable: si tus APIs cambian frecuentemente, las definiciones de tools del agente se rompen.
- 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 cadena de prompts es más rentable.
Lectura Adicional
- Anthropic — Building Effective Agents — cuándo usar agentes vs. workflows
- OpenAI — Guía de Function Calling — la mecánica del uso de herramientas
- LangChain — Agentes — construcción de agentes multi-turn con herramientas
- ReAct: Synergizing Reasoning and Acting — el paper fundacional del patrón
Preguntas frecuentes
¿Cuál es la diferencia entre un agente y un chatbot?
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.
¿Puedo construir agentes sin OpenAI?
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.
¿Cómo evito que un agente haga llamadas a API caras?
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).
¿Los agentes deberían reemplazar APIs backend tradicionales?
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.
¿Cómo testeo un agente end-to-end?
Construye un test suite con inputs conocidos y secuencias esperadas de tool calls. Simula 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.
¿Cuál es el número máximo de herramientas que un agente puede usar?
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 analizar 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.
¿Cómo manejo fallos de agente graciosamente?
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.
Recursos Relacionados
Aplicar lo que funciona en Prompt Engineering
Cómo escribir prompts útiles para LLMs usando asignación de roles, few-shot examples, razonamiento chain-of-thought y formato de salida estructurada.
RecipeRAG con LangChain y bases vectoriales
Cómo construir un pipeline de Retrieval-Augmented Generation (RAG) usando LangChain y bases de datos vectoriales para búsqueda potenciada por IA
RecipeCrea búsqueda semántica con embeddings en Python, JS y Java
Crea un motor de búsqueda semántica con embeddings de texto y similitud vectorial. Incluye ejemplos en Python, JavaScript y Java con FAISS, pgvector y OpenAI.
RecipeAgentes de IA con Uso de Herramientas: ReAct en Práctica
Construye agentes de IA autónomos que pueden usar herramientas y APIs externas para completar tareas complejas.
RecipeGenerar Imágenes Programáticamente con Modelos de IA
Cómo crear, editar y optimizar imágenes usando las APIs de DALL-E, Stable Diffusion y Midjourney con prompt engineering, procesamiento por lotes y moderación de contenido.
RecipeAnálisis de Sentimiento con Python y NLTK
Puntúa el sentimiento de texto con NLTK VADER y léxicos personalizados en Python. Clasifica reviews, procesa CSVs y analiza tendencias con ejemplos.