AI Agents en Producción: Cómo Construir Sistemas que Realmente Toman Decisiones
Aprende a construir AI agents reales en producción: arquitectura multi-agente, memoria persistente y orquestación con LangGraph, OpenSage y Claude en 2026.
El 90% de los 'AI Agents' No Son Agents
Tienes un script que llama a GPT-4, parsea la respuesta y ejecuta una función.
Eso no es un agent. Es un wrapper con delirios de grandeza.
*El problema real no es el modelo. Es que confundes automatización con autonomía.*
Un agent de verdad toma decisiones, crea subtareas, selecciona herramientas dinámicamente y se adapta cuando algo falla. No sigue un flujo predeterminado. Lo construye en tiempo de ejecución.
Este artículo te muestra la diferencia entre ambos — y cómo construir el segundo.
---
Qué Hace Real a un AI Agent
La definición técnica importa aquí. Un agent tiene tres capacidades que un script no tiene:
→ Razonamiento sobre el entorno: observa el estado actual y decide qué hacer a continuación.
→ Uso dinámico de herramientas: no tiene un tool hardcodeado. Elige entre un conjunto de opciones según la tarea.
→ Memoria persistente: recuerda contexto entre pasos, no solo entre mensajes.
Sin las tres, tienes automatización. Con las tres, tienes un agent.
Frameworks como OpenSage demuestran esto llevado al extremo: el propio agent genera su topología de sub-agents durante la ejecución. No hay un desarrollador diseñando el flujo — el LLM decide qué sub-agents crear, ejecutar y terminar según la tarea en curso. En benchmarks como SWE-Bench Pro alcanza un 59,0% frente al 40,2% de SWE-agent. En Terminal-Bench 2.0, un 78,4%.
La arquitectura self-programming no es ciencia ficción. Es producción en 2026.
---
El Error de Arquitectura más Común
❌ Enfoque incorrecto — agent lineal:
```python
Esto NO es un agent. Es un pipeline disfrazado.
def run_agent(user_input: str) -> str:
step1 = call_llm(f"Analiza esto: {user_input}")
step2 = search_web(step1)
step3 = call_llm(f"Resume esto: {step2}")
return step3
```
El flujo está hardcodeado. Si `search_web` falla, el sistema se rompe. Si la tarea requiere un paso extra, no puede añadirlo.
✅ Enfoque correcto — agent con razonamiento sobre herramientas:
```python
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
@tool
def buscar_web(query: str) -> str:
"""Busca información actualizada en internet sobre cualquier tema."""
Integración con Firecrawl o Tavily aquí
return firecrawl_search(query)
@tool
def ejecutar_codigo(code: str) -> str:
"""Ejecuta código Python en un sandbox seguro y devuelve el output."""
return sandbox_execute(code)
@tool
def guardar_resultado(data: str, filename: str) -> str:
"""Persiste un resultado en almacenamiento para uso posterior."""
return storage_save(data, filename)
llm = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [buscar_web, ejecutar_codigo, guardar_resultado]
prompt = ChatPromptTemplate.from_messages([
("system", "Eres un agent que resuelve tareas complejas. Usa las herramientas disponibles en el orden que consideres óptimo."),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
agent = create_openai_tools_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True, max_iterations=10)
result = executor.invoke({"input": "Investiga las últimas tendencias en frameworks de AI agents y genera un informe estructurado"})
```
Ahora el agent decide si buscar, ejecutar código o guardar. Y en qué orden. Y cuántas veces.
---
Memoria: El Componente que Nadie Implementa Bien
*La memoria real no es un historial de mensajes. Es una arquitectura de estado persistente.*
Hay tres tipos que necesitas entender:
1. Memoria de Trabajo (In-Context)
El historial de mensajes del thread actual. Útil para conversaciones cortas. Se pierde cuando termina la sesión.
2. Memoria Episódica (External Store)
Resúmenes y hechos clave almacenados en una base de datos vectorial como Pinecone o Supabase pgvector. El agent recupera contexto relevante antes de cada paso.
```python
from langchain_community.vectorstores import SupabaseVectorStore
from langchain_openai import OpenAIEmbeddings
Recuperar contexto relevante antes de ejecutar
def get_relevant_context(task: str, user_id: str) -> str:
vectorstore = SupabaseVectorStore(
client=supabase_client,
embedding=OpenAIEmbeddings(),
table_name="agent_memory",
query_name="match_memories"
)
docs = vectorstore.similarity_search(
query=task,
k=5,
filter={"user_id": user_id}
)
return "\n".join([d.page_content for d in docs])
```
3. Memoria Semántica (Structured State)
Hechos explícitos sobre el usuario o el proyecto. Almacenados en tablas relacionales. El agent puede leerlos y actualizarlos directamente.
Sin los tres niveles, tu agent empieza desde cero en cada ejecución. Con los tres, acumula inteligencia.
---
Orquestación Multi-Agente: Cuándo y Cómo
Un solo agent tiene límites. Las tareas complejas requieren especialización.
El patrón correcto es orchestrator + worker agents:
→ El orchestrator recibe la tarea de alto nivel.
→ Decide qué sub-agents especializados necesita.
→ Los instancia, les asigna subtareas y consolida resultados.
Esto es exactamente lo que hace OpenSage con su self-generating agent topology: el LLM principal genera dinámicamente los sub-agents que necesita, con sus propios toolsets, y los termina cuando completan su función.
```python
Pattern simplificado de orchestrator
from langgraph.graph import StateGraph, END
from typing import TypedDict, List
class OrchestratorState(TypedDict):
task: str
subtasks: List[str]
results: List[str]
final_output: str
def decompose_task(state: OrchestratorState) -> OrchestratorState:
"""Orchestrator descompone la tarea en subtareas"""
response = llm.invoke(f"""
Descompón esta tarea en subtareas independientes:
{state['task']}
Devuelve una lista JSON de subtareas.
""")
state['subtasks'] = parse_json(response)
return state
def execute_worker(state: OrchestratorState) -> OrchestratorState:
"""Worker agent ejecuta cada subtarea"""
results = []
for subtask in state['subtasks']:
result = worker_executor.invoke({"input": subtask})
results.append(result['output'])
state['results'] = results
return state
def consolidate(state: OrchestratorState) -> OrchestratorState:
"""Orchestrator consolida todos los resultados"""
state['final_output'] = llm.invoke(
f"Consolida estos resultados en una respuesta coherente: {state['results']}"
)
return state
Construir el grafo
graph = StateGraph(OrchestratorState)
graph.add_node("decompose", decompose_task)
graph.add_node("execute", execute_worker)
graph.add_node("consolidate", consolidate)
graph.set_entry_point("decompose")
graph.add_edge("decompose", "execute")
graph.add_edge("execute", "consolidate")
graph.add_edge("consolidate", END)
orchestrator = graph.compile()
```
---
El Stack Correcto para Producción en 2026
No hay un framework que lo haga todo bien. Estos son los roles correctos para cada herramienta:
→ LangGraph: orquestación de flujos complejos con estado explícito. Es el mejor para multi-agent workflows con lógica condicional.
→ OpenSage: agents que necesitan autonomía máxima y self-programming. Excelente para tareas de ingeniería de software y seguridad.
→ Claude via API de Anthropic: el mejor modelo para razonamiento sobre código y tareas que requieren seguir instrucciones complejas.
→ Firecrawl: acceso a datos web limpios para los tools de búsqueda del agent. Resuelve el problema de datos en formato incorrecto para LLMs.
→ Supabase: persistencia de estado, memoria episódica con pgvector, y autenticación para agents multi-usuario.
→ Coasts / contenedores especializados: hosts containerizados para aislar la ejecución de agents en producción.
El error más común es intentar usar un solo framework para todo. Cada capa tiene una herramienta óptima.
---
Qué Vigilar en Producción
Los agents fallan de formas distintas a las APIs tradicionales. Los problemas más frecuentes:
→ Bucles infinitos: el agent entra en un loop razonando sobre la misma subtarea. Solución: `max_iterations` y circuit breakers explícitos.
→ Tool hallucination: el agent intenta usar una herramienta que no existe. Solución: validar siempre el nombre del tool antes de ejecutar.
→ Contexto contaminado: memoria de una sesión afecta a otra. Solución: namespacing estricto por `user_id` y `session_id` en todos los stores.
→ Latencia acumulada: 5 pasos de razonamiento × 2 segundos por llamada = 10 segundos. Solución: paralelizar subtareas independientes con `asyncio`.
---
Puntos Clave
→ Un agent real tiene razonamiento dinámico, selección de tools y memoria persistente — no un flujo hardcodeado.
→ La arquitectura self-programming (OpenSage) demuestra que los agents pueden generar su propia topología en runtime con resultados superiores en benchmarks reales.
→ La memoria en tres capas (in-context, episódica, semántica) es lo que separa agents útiles de chatbots con herramientas.
→ Multi-agent con orchestrator + workers es el patrón más robusto para tareas complejas en producción.
→ El stack óptimo combina LangGraph + Claude + Firecrawl + Supabase — cada herramienta en su capa correcta.
*Los developers que entienden agents como sistemas de estado — no como chatbots con superpoderes — son los que construyen los productos que no se pueden copiar fácilmente.*
Lee el artículo completo en brianmenagomez.com
Más sobre mis servicios en brianmenagomez.com
Herramientas: Conversor IAE CNAE · Gestorias cerca de ti · Calculadora IRPF

