Arquitectura de Agentes
Explora la arquitectura fundamental de los agentes de IA: percepción, razonamiento, ciclos de acción, estrategias de planificación y patrones de gestión de estado.
El Ciclo del Agente
Todo agente de IA sigue un ciclo fundamental: percibir el entorno, razonar sobre el estado actual, planificar acciones, ejecutarlas, observar los resultados y repetir. Esta arquitectura es lo que permite a los agentes completar de forma autónoma tareas complejas.
El ciclo del agente no es necesariamente lineal. Los agentes modernos usan replanificación dinámica: si un paso falla, el agente no reinicia: vuelve a evaluar el plan desde el estado actual y se adapta.
Capa de Percepción
La capa de percepción es como el agente recibe información sobre su entorno. Procesa múltiples canales de entrada simultáneamente.
class PerceptionLayer:
def __init__(self):
self.channels = {}
def register_channel(self, name, parser):
self.channels[name] = parser
def perceive(self, inputs):
perception = {}
for channel, data in inputs.items():
if channel in self.channels:
perception[channel] = self.channels[channel](data)
return perception
def parse_user_input(self, text):
return {
"raw": text,
"intent": self._classify_intent(text),
"entities": self._extract_entities(text),
"urgency": self._detect_urgency(text)
}
def _classify_intent(self, text):
intents = {
"generate": ["create", "write", "implement", "build"],
"analyze": ["analyze", "review", "check", "audit"],
"modify": ["change", "update", "refactor", "fix"],
"query": ["what", "how", "why", "where", "when"]
}
for intent, keywords in intents.items():
if any(kw in text.lower() for kw in keywords):
return intent
return "unknown"
def _extract_entities(self, text):
# Extract file paths, function names, etc.
import re
files = re.findall(r'[\w./-]+\.\w+', text)
return {"files": files}
def _detect_urgency(self, text):
urgent_markers = ["urgent", "asap", "critical", "immediately", "now"]
return any(m in text.lower() for m in urgent_markers)
perceiver = PerceptionLayer()
result = perceiver.parse_user_input(
"Urgent: refactor the auth module in src/auth.py to use JWT tokens"
)
print(f"Intent: {result['intent']}")
print(f"Files: {result['entities']['files']}")
print(f"Urgent: {result['urgency']}")Canales de Percepción
Canal | Fuente | Tipo de Dato | Ejemplo |
|---|---|---|---|
Entrada del usuario | Mensaje en lenguaje natural | Texto con intención/entidades | "Encuentra el bug en process.py" |
Sistema de archivos | Archivos del proyecto | Contenido de archivos, metadatos | Código fuente, configuraciones |
Entorno | SO, procesos | Estado del sistema, variables de entorno | Servicios en ejecución, puertos |
Memoria | Almacenamiento del agente | Interacciones pasadas, hechos | Decisiones anteriores |
Salida de herramientas | Resultados de comandos | stdout, stderr, códigos de salida | Resultados de pruebas, logs de build |
APIs externas | Servicios web | JSON, XML, binario | Issues de GitHub, Slack |
Diseña tu capa de percepción para que sea extensible. A medida que tu agente crezca, querrás añadir nuevos canales de entrada (Slack, correo electrónico, alertas de monitoreo) sin reescribir la arquitectura central.
Motor de Razonamiento
El motor de razonamiento es el cerebro del agente. Interpreta las percepciones, toma decisiones y formula planes.
class ReasoningEngine:
def __init__(self, llm_client):
self.llm = llm_client
self.plan = []
def analyze_state(self, perception, memory):
prompt = f"""
Current state: {perception}
Available context: {memory.get_relevant(perception)}
Analyze the situation and determine:
1. What is the primary goal?
2. What constraints exist?
3. What tools are needed?
4. What could go wrong?
"""
return self.llm.complete(prompt)
def decompose_goal(self, goal, available_tools):
prompt = f"""
Goal: {goal}
Available tools: {list(available_tools.keys())}
Break this goal into sequential steps.
Each step must specify:
- Action description
- Tool to use
- Expected outcome
- Error recovery strategy
"""
response = self.llm.complete(prompt)
steps = self._parse_steps(response)
self.plan = steps
return steps
def _parse_steps(self, llm_response):
# Parse LLM response into structured steps
steps = []
for line in llm_response.split("\n"):
if line.strip().startswith("- Step"):
steps.append({"raw": line.strip()})
return steps
def select_tool(self, step, tool_registry):
scores = {}
for name, tool in tool_registry.items():
relevance = self._compute_relevance(step, tool["description"])
scores[name] = relevance
return max(scores, key=scores.get)
def _compute_relevance(self, step, description):
# Simplified relevance scoring
keywords = set(step.lower().split())
desc_keywords = set(description.lower().split())
overlap = keywords & desc_keywords
return len(overlap) / max(len(keywords), 1)
engine = ReasoningEngine(llm_client=None) # Would use real LLM
tools = {
"read_file": {"description": "Read file contents from disk"},
"bash": {"description": "Execute shell commands"},
"web_search": {"description": "Search the internet"},
}
print(engine.select_tool("read the configuration file", tools))Estrategias de Planificación
Los agentes usan diferentes estrategias de planificación según la complejidad y los requisitos de la tarea:
# Sequential planning configuration
planning:
strategy: sequential
max_retries: 3
stop_on_failure: true
plan:
- id: 1
action: "lint"
tool: "bash"
command: "ruff check src/"
- id: 2
action: "type_check"
tool: "bash"
command: "mypy src/"
depends_on: [1]
- id: 3
action: "test"
tool: "bash"
command: "pytest tests/"
depends_on: [2]
- id: 4
action: "build"
tool: "bash"
command: "python -m build"
depends_on: [3]La planificación secuencial es simple pero frágil. Un fallo en el paso 1 detiene todo el pipeline. Incluye siempre estrategias de recuperación de errores y considera la ejecución paralela para pasos independientes.
Replanificación Dinámica en Acción
class DynamicPlanner:
def __init__(self, llm):
self.llm = llm
self.execution_history = []
def execute_plan(self, initial_goal, tools):
current_goal = initial_goal
current_plan = self._create_plan(current_goal)
while not self._goal_achieved(current_goal):
for step in current_plan:
result = self._execute_step(step, tools)
self.execution_history.append({
"step": step,
"result": result,
"success": result["status"] == "ok"
})
if result["status"] == "error":
# Dynamic re-planning
recovery = self._plan_recovery(step, result)
if recovery["strategy"] == "retry":
current_plan = self._adjust_plan(
current_plan, step, recovery
)
elif recovery["strategy"] == "alternative":
alternative = recovery["alternative_action"]
current_plan = self._replace_step(
current_plan, step, alternative
)
elif recovery["strategy"] == "abort":
return {"status": "failed", "at": step}
return {"status": "success", "history": self.execution_history}
def _create_plan(self, goal):
return [{"id": 1, "action": f"Process: {goal}"}]
def _execute_step(self, step, tools):
# Simulated execution
return {"status": "ok", "output": f"Executed {step['action']}"}
def _goal_achieved(self, goal):
return False
def _plan_recovery(self, failed_step, error):
return {"strategy": "retry", "max_attempts": 3}
def _adjust_plan(self, plan, failed_step, recovery):
return plan
def _replace_step(self, plan, old_step, new_action):
return plan
planner = DynamicPlanner(llm=None)
result = planner.execute_plan("Deploy application", {})
print(f"Execution status: {result['status']}")Gestión de Estado
Los agentes deben rastrear el estado a lo largo del ciclo de ejecución. El estado incluye el objetivo actual, los pasos completados, los resultados de las herramientas y el contexto acumulado.
{
"agent_state": {
"session_id": "sess_abc123",
"status": "executing",
"current_goal": "Refactor payment module",
"plan": {
"strategy": "hierarchical",
"sub_goals": [
{
"id": "sg_1",
"description": "Analyze current payment code",
"status": "completed",
"result": "Found 3 areas for improvement"
},
{
"id": "sg_2",
"description": "Implement error handling improvements",
"status": "in_progress",
"current_step": {
"action": "edit",
"file": "src/payment/processor.py",
"target": "validate_amount function"
}
},
{
"id": "sg_3",
"description": "Add test coverage for new error paths",
"status": "pending",
"dependencies": ["sg_2"]
}
]
},
"memory": {
"short_term": [
{"role": "user", "content": "Refactor the payment module..."},
{"role": "assistant", "content": "I'll analyze the current code first."}
],
"working": {
"current_file": "src/payment/processor.py",
"last_function": "validate_amount",
"changes_made": 2
}
},
"tool_history": [
{"tool": "grep", "pattern": "def validate", "result": "Found at line 142"},
{"tool": "read", "file": "src/payment/processor.py", "lines": "140-180"}
]
}
}Máquina de Estados para el Ciclo de Vida del Agente
from enum import Enum
class AgentState(Enum):
IDLE = "idle"
PERCEIVING = "perceiving"
REASONING = "reasoning"
PLANNING = "planning"
EXECUTING = "executing"
OBSERVING = "observing"
ERROR = "error"
COMPLETED = "completed"
class StateMachine:
def __init__(self):
self.state = AgentState.IDLE
self.transitions = {
AgentState.IDLE: [AgentState.PERCEIVING],
AgentState.PERCEIVING: [AgentState.REASONING, AgentState.ERROR],
AgentState.REASONING: [AgentState.PLANNING, AgentState.ERROR],
AgentState.PLANNING: [AgentState.EXECUTING, AgentState.ERROR],
AgentState.EXECUTING: [AgentState.OBSERVING, AgentState.ERROR],
AgentState.OBSERVING: [AgentState.REASONING, AgentState.COMPLETED, AgentState.ERROR],
AgentState.ERROR: [AgentState.IDLE, AgentState.REASONING],
AgentState.COMPLETED: [AgentState.IDLE]
}
def transition_to(self, new_state):
if new_state in self.transitions[self.state]:
old = self.state
self.state = new_state
print(f"State: {old.value} → {new_state.value}")
return True
raise ValueError(f"Cannot transition from {self.state} to {new_state}")
def reset(self):
self.state = AgentState.IDLE
sm = StateMachine()
sm.transition_to(AgentState.PERCEIVING)
sm.transition_to(AgentState.REASONING)
sm.transition_to(AgentState.PLANNING)
sm.transition_to(AgentState.EXECUTING)
sm.transition_to(AgentState.OBSERVING)
sm.transition_to(AgentState.COMPLETED)Usa una máquina de estados para hacer el comportamiento del agente predecible y depurable. Cada transición de estado puede registrarse, monitorearse y probarse de forma independiente.
Arquitectura de Integración de Herramientas
La capa de integración de herramientas conecta el razonamiento del agente con las acciones externas. Gestiona el descubrimiento, la invocación y el procesamiento de resultados.
class ToolManager:
def __init__(self, registry, permission_system):
self.registry = registry
self.permissions = permission_system
self.execution_log = []
async def execute(self, tool_name, params):
# Check permissions first
if not self.permissions.is_allowed(tool_name, params):
return {
"status": "denied",
"tool": tool_name,
"reason": "Permission denied"
}
# Validate parameters against schema
tool_def = self.registry.get(tool_name)
errors = self._validate_params(tool_def, params)
if errors:
return {"status": "error", "errors": errors}
# Execute with timeout
try:
result = await self.registry.call(tool_name, **params)
processed = self._process_output(result)
self.execution_log.append({
"tool": tool_name,
"params": params,
"result": processed,
"timestamp": __import__('time').time()
})
return {"status": "success", "data": processed}
except Exception as e:
return {"status": "error", "error": str(e)}
def _validate_params(self, tool_def, params):
errors = []
schema = tool_def.get("parameters", {})
for key, spec in schema.items():
if spec.get("required") and key not in params:
errors.append(f"Missing required parameter: {key}")
return errors
def _process_output(self, output):
if isinstance(output, str) and len(output) > 1000:
return {
"content": output[:1000] + "...[truncated]",
"full_length": len(output)
}
return {"content": output}Comparación de Arquitecturas: Frameworks de Agentes
Aspecto | Loop Simple | Jerárquico | Dirigido por Eventos | Reactivo |
|---|---|---|---|---|
Estructura | Lineal percibir→razonar→actuar | Sub-agentes con managers | Bus de eventos pub/sub | Estímulo-respuesta |
Estado | Pila única | Distribuido | Almacén de eventos compartido | Mínimo |
Escalabilidad | Limitada | Alta | Muy alta | Moderada |
Aislamiento de errores | Pobre | Bueno | Excelente | Aceptable |
Caso de uso | Automatización simple | Flujos de trabajo complejos | Sistemas en tiempo real | IA para videojuegos |
Ejemplo | Asistente personal | Codificación multi-agente | Agente de monitoreo | Chatbot |
El ciclo del agente (percibir → razonar → planificar → actuar → observar) es el patrón universal que subyace a todas las arquitecturas de agentes. Domina este ciclo y podrás implementar cualquier framework de agentes.
Ejercicios de Práctica
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
En el ciclo del agente, después de ejecutar una acción y observar el resultado, ¿qué sucede si el objetivo aún no se ha logrado?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
¿Qué estrategia de planificación es la más adecuada para un flujo de trabajo lineal como build → pruebas → despliegue?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
¿Cuál es el propósito de la capa de percepción en una arquitectura de agentes?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
Cuando un paso falla durante la ejecución secuencial, ¿cuál es el enfoque de recuperación recomendado para un agente robusto?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
En el patrón de Gestor de Herramientas (Tool Manager), ¿qué verificación ocurre antes de ejecutar una herramienta?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
Un agente está depurando un problema complejo y encuentra resultados inesperados que invalidan su plan inicial. ¿Qué estrategia de planificación es la más adecuada para este escenario?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
¿Cuál es el beneficio de usar una máquina de estados para gestionar el ciclo de vida del agente?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
En una arquitectura de planificación jerárquica, ¿qué sucede cuando un subobjetivo falla?
- El ciclo del agente (percibir → razonar → planificar → actuar → observar) es el patrón universal de todas las arquitecturas de agentes
- La capa de percepción procesa entradas de múltiples canales: mensajes del usuario, archivos, entorno, APIs
- El motor de razonamiento interpreta el estado y toma decisiones, delegando a menudo en los LLM para el análisis complejo
- Las estrategias de planificación incluyen secuencial, jerárquica, replanificación dinámica y enfoques Monte Carlo
- La replanificación dinámica permite a los agentes recuperarse de fallos y adaptarse a observaciones inesperadas
- Las máquinas de estados hacen el comportamiento del agente predecible, registrado y testeable
- Los gestores de herramientas manejan la verificación de permisos, la validación de parámetros, la ejecución y el procesamiento de salidas
- Diferentes arquitecturas (loop simple, jerárquica, dirigida por eventos) se adaptan a diferentes casos de uso