Diseñando apps con subagentes de IA: cómo trocear problemas, orquestar roles y no generar caos

Diseñando apps con subagentes de IA: cómo trocear problemas, orquestar roles y no generar caos

Guía práctica para diseñar sistemas con subagentes de IA: cómo dividir tareas, coordinar agentes y evitar que tu arquitectura se vuelva incontrolable.
Compartir:

Los subagentes de IA prometen velocidad y modularidad, pero mal diseñados se convierten en un enjambre imposible de depurar.

Vamos a ver cómo trocear problemas, orquestar roles y poner límites claros para que tu sistema siga siendo predecible.

Si no puedes dibujar tu arquitectura de agentes en una hoja A4, probablemente tu diseño es más complejo de lo que necesitas.

De un solo agente a una arquitectura de subagentes

Un único agente "todoterreno" con contexto infinito suena bien… hasta que:

  • Se vuelve lento y caro (hace de todo, todo el tiempo).
  • Mezcla responsabilidades (razona, programa, llama APIs, decide negocio…).
  • Es difícil de probar y versionar (un cambio en el prompt rompe medio sistema).

Los subagentes permiten algo más cercano a una arquitectura de microservicios:

  • Cada agente tiene un propósito claro.
  • Cada uno expone una interfaz limitada (inputs/outputs bien definidos).
  • El orquestador decide quién hace qué y en qué orden.

En el ecosistema de Claude Code, esto se concreta en subagentes especializados que cooperan dentro de un mismo flujo. Una buena introducción a qué son y cómo se crean está en este artículo: Subagentes de Claude Code: Qué son y cómo crear el tuyo.

Aquí nos centraremos en el cómo diseñarlos bien desde el punto de vista de arquitectura.

Paso 1: trocear el problema como si fueras a diseñar microservicios

Piensa en subagentes como en servicios internos:

  • Deben tener una única responsabilidad principal.
  • Deben recibir inputs claros y producir outputs estructurados.
  • Deben ocultar su complejidad interna al resto del sistema.

Preguntas para descubrir subagentes

Para una app que usa IA, recorre el flujo de usuario y pregúntate:

  1. ¿Dónde hay tipos de razonamiento claramente diferentes?
    • Ejemplo: extraer datos de texto vs. escribir código vs. resumir conversaciones.
  2. ¿Dónde cambia el “nivel” de la tarea?
    • Alto nivel (definir estrategia) vs. bajo nivel (ejecutar pasos concretos).
  3. ¿Dónde necesitas capacidades peligrosas o privilegiadas?
    • Acceso a base de datos, sistema de ficheros, llamadas a APIs críticas.
  4. ¿Qué partes podrían reutilizarse en otras features?
    • Un agente que limpia datos puede servir para varios flujos.

Con esto suelen aparecer 3–7 subagentes naturales. Más de 10 para un solo flujo suele ser señal de sobre-diseño.

Ejemplo de desglose típico

En una aplicación que genera código a partir de requisitos de usuario, podrías tener:

  • analista_requisitos: estructura lo que pide el usuario.
  • arquitecto_codigo: decide enfoque técnico y componentes.
  • generador_codigo: produce archivos concretos.
  • revisor_seguridad: revisa riesgos y patrones peligrosos.
  • orquestador: coordina el flujo completo.

No hace falta que todos sean agentes separados desde el día 1, pero separar responsabilidades desde diseño ayuda a escalar luego.

Paso 2: diseñar protocolos entre agentes (inputs, outputs, contratos)

Sin buenos protocolos, los agentes se convierten en humanos mandándose correos vagos.

Tu objetivo: cada agente debería poder sustituirse por una implementación distinta (o una versión nueva) sin romper al resto, siempre que respete el contrato.

Define contratos explícitos

Para cada subagente:

  • Input: qué recibe, en qué formato, qué campos son obligatorios.
  • Output: qué devuelve, en qué formato, qué errores puede producir.
  • Suposiciones: qué se espera que haya pasado antes de llamarlo.

En la práctica, esto significa usar estructuras claras (JSON, tipos) en vez de prompts ambiguos.

// Ejemplo de contrato de un subagente de análisis de requisitos

export interface AnalizarRequisitosInput {
  rawText: string; // Lo que escribió el usuario
  contextoProyecto?: string; // Opcional
}

export interface AnalizarRequisitosOutput {
  objetivos: string[];
  restricciones: string[];
  tecnologiasSugeridas: string[];
  riesgosDetectados: string[];
}

Sobre ese contrato, el prompt del subagente se centra en cómo rellenar esa estructura, no en inventar el protocolo cada vez.

Usa “idiomas” internos estables

Aunque los modelos cambien, tu sistema puede mantener:

  • Un vocabulario interno estable: campos, enums, tipos comunes.
  • Códigos de estado semiestándar ("ok", "necesita_mas_contexto", "error").
  • Esquemas compartidos (por ejemplo, con Zod/TypeScript para validar outputs).

Esto reduce el caos cuando actualizas prompts o modelos.

Paso 3: el papel del orquestador

El orquestador es el cerebro del sistema: decide qué subagente llamar, en qué orden y con qué contexto.

Evita que cada subagente "hable" directamente con todos los demás. Si necesitas comunicación entre agentes, que pase a través del orquestador.

Responsabilidades del orquestador

El orquestador suele encargarse de:

  • Mantener el estado global del flujo.
  • Decidir la siguiente acción en función de resultados parciales.
  • Reintentar llamadas con ajustes mínimos (re-prompts, filtros, etc.).
  • Enviar resúmenes como contexto, en lugar de todo el historial.
async function flujoGeneracionCodigo(peticionUsuario: string) {
  const analisis = await llamarSubagente("analista_requisitos", {
    rawText: peticionUsuario,
  });

  const arquitectura = await llamarSubagente("arquitecto_codigo", {
    objetivos: analisis.objetivos,
    restricciones: analisis.restricciones,
  });

  const codigo = await llamarSubagente("generador_codigo", {
    arquitectura,
  });

  const informeSeguridad = await llamarSubagente("revisor_seguridad", {
    codigo,
    contexto: analisis,
  });

  return { codigo, informeSeguridad };
}

Fíjate en que el orquestador controla qué ve cada subagente. Esto es clave para seguridad y coste.

¿Orquestador LLM o código clásico?

Puedes orquestar de dos formas:

  • Orquestador en código (TypeScript, Python…):
    • Ventajas: predictible, testeable, fácil de depurar.
    • Úsalo para flujos que ya conoces bien.
  • Orquestador como agente LLM:
    • Ventajas: mayor flexibilidad, útil cuando los caminos del flujo no están claros.
    • Riesgo: más difícil de testear, puede tomar decisiones inesperadas.

En muchos casos, un enfoque híbrido funciona bien: código controla el esqueleto del flujo y un agente decide solo partes bien acotadas (por ejemplo, prioridades o selección de herramientas).

Paso 4: aislar capacidades peligrosas

En cuanto añades herramientas potentes (filesystem, APIs de producción, ejecuciones de código) el riesgo se dispara.

Los subagentes son tu oportunidad para encerrar cuchillos afilados en cajas de seguridad.

Principios de seguridad con subagentes

  1. Menos es más:
    • Cada subagente ve solo las herramientas que necesita.
  2. Contexto mínimo:
    • Un agente con acceso a la base de datos no necesita ver el historial completo del chat.
  3. Guard rails fuera del modelo:
    • Implementa límites en código (whitelists, límites de tiempo, límites de tokens).
  4. Registro detallado de acciones:
    • Loggea cada uso de herramientas sensibles con quién, qué y cuándo.

Patrón: agente portero + agente operador

Para capacidades peligrosas, crea un dúo:

  • agente_portero:
    • Sin acceso directo a herramientas peligrosas.
    • Evalúa si la acción solicitada tiene sentido y está permitida.
  • agente_operador:
    • Tiene acceso a las herramientas.
    • Solo ejecuta acciones que el portero ha aprobado y estructurado.

Esto reduce mucho la probabilidad de que un desvío en el razonamiento acabe en una acción destructiva.

Paso 5: evitar el enjambre incontrolable

Más agentes no siempre equivalen a mejor rendimiento. Hay patrones de caos bastante frecuentes.

Antipatrones típicos

  1. Chat interno infinito entre agentes

    • Subagente A llama a B que llama a C que vuelve a llamar a A…
    • Síntoma: costes disparados, latencias enormes, logs ilegibles.
  2. Duplicación de responsabilidad

    • Varios agentes "tocan" la misma lógica de negocio con prompts parecidos.
    • Síntoma: resultados inconsistentes, difícil saber quién rompió qué.
  3. Prompts que se autocontradicen

    • El orquestador pide A, el subagente está configurado para B.
    • Síntoma: comportamientos erráticos solo en ciertos caminos del flujo.
  4. Dependencias circulares de contexto

    • A necesita output completo de B, B necesita contexto completo de A.
    • Síntoma: prompts gigantescos y difíciles de mantener.

Salvaguardas prácticas

Algunas defensas sencillas:

  • Límite de saltos:
    • Máximo N llamadas encadenadas entre subagentes por petición.
  • Presupuestos por petición:
    • Tope de coste/tiempo por request antes de abortar.
  • Métricas por agente:
    • Mide uso, latencia, tasa de error y coste por subagente.
  • Tests de humo por flujo:
    • Casos simples que recorren el flujo completo y verifican que nadie cambió el contrato.

Paso 6: diseñar prompts como especificaciones, no como charlas

En arquitecturas con subagentes, el prompt es la especificación del módulo.

Si los prompts cambian de estilo o de requisitos cada semana sin control, no tienes arquitectura, tienes artesanía.

Estructura recomendada de prompt de subagente

Para cada subagente, intenta seguir una plantilla estable:

  1. Rol y objetivo
    • "Eres un analista de requisitos de software…"
  2. Alcance y no-alcance
    • Qué hace y qué no hace este agente.
  3. Formato de entrada (para el modelo)
    • Explica cómo interpretará la entrada estructurada.
  4. Pasos internos de razonamiento (breves)
    • No hace falta encadenar 20 bullets, solo una guía.
  5. Formato de salida exacto
    • Incluye un ejemplo de JSON válido.
[ROL]
Eres un analista de requisitos de software que transforma texto libre del usuario
en una especificación estructurada para otros agentes.

[ALCANCE]
- Debes identificar objetivos, restricciones, tecnologías sugeridas y riesgos.
- No debes escribir código ni proponer arquitecturas detalladas.

[FORMATO DE SALIDA]
Devuelve SIEMPRE un JSON válido que cumpla este esquema:
{
  "objetivos": string[],
  "restricciones": string[],
  "tecnologiasSugeridas": string[],
  "riesgosDetectados": string[]
}

[PROCESO]
1) Lee atentamente el texto del usuario.
2) Extrae objetivos explícitos e implícitos.
3) Extrae restricciones y condiciones.
4) Sugiere tecnologías razonables.
5) Identifica riesgos relevantes.
6) Devuelve solo el JSON final.

Esta claridad vuelve más predecibles las interacciones entre agentes.

Paso 7: introduce subagentes de forma incremental

No hace falta reescribir tu app entera con una flota de agentes desde el día uno.

En la práctica, funciona bien este orden:

  1. Identifica el cuello de botella actual
    • ¿Dónde se complica más tu prompt o tu código ahora mismo?
  2. Extrae un subagente de alto valor
    • Por ejemplo, un revisor de seguridad, o un analizador de inputs de usuario.
  3. Define el contrato y escribe tests
    • Antes de enchufarlo al flujo completo.
  4. Observa métricas y calidad
    • ¿Mejoró algo: coste, claridad de código, mantenibilidad, calidad?
  5. Repite donde tenga sentido, no por moda.

Con esto construyes una arquitectura de agentes que crece porque lo necesitas, no porque la idea suena bien.

Cierre: subagentes sí, enjambre no

Los subagentes de IA, como los de Claude Code descritos en este artículo introductorio, son una herramienta potente para modularizar sistemas basados en modelos grandes.

La clave está en tratarlos como componentes de arquitectura:

  • Trocea problemas como si diseñaras microservicios.
  • Define contratos estrictos entre agentes.
  • Centraliza orquestación y controla el contexto.
  • Encierra las capacidades peligrosas en agentes con límites claros.
  • Mide, limita y versiona para que el sistema no se vuelva inmanejable.

Si diseñas la colaboración entre agentes con el mismo rigor que tu backend, puedes aprovechar la flexibilidad de la IA sin renunciar a la estabilidad de tu sistema.

Compartir:

Artículos relacionados