¿Y si tu app dejara de ser un monstruo monolítico con un único “asistente” y se convirtiera en un enjambre de agentes que se pasan la pelota sin romper nada?
Tiempo estimado de lectura: 6 min
- Reducir contexto inútil en prompts, aislar fallos y permitir equipos autónomos sin perder UX coherente.
- Descentraliza la responsabilidad cognitiva; centraliza seguridad, auditoría y orden.
- Usa un Event Bus global con contratos claros y BFFs que versionen prompts y auditen inferencias.
- Mide confidence, delegaciones y latencia; trata prompts como código.
Poca gente lo dice así: dividir la inteligencia en agentes especializados no es exotismo. Es economía de tokens, menos alucinaciones y menos puntos únicos de falla. Y sí: también es más trabajo. Pero si tu producto escala, vale cada minuto invertido.
Resumen rápido (lectores con prisa)
Qué es: Un patrón para dividir inteligencia en agentes por dominio y comunicarlos vía un Event Bus global.
Cuándo usarlo: Cuando la complejidad de dominios y volumen hacen ineficiente un único asistente monolítico.
Por qué importa: Reduce tokens, mitiga alucinaciones y falla de forma aislada; facilita equipos autónomos.
Cómo funciona (resumen): Host publica USER_INPUT al bus; agentes calculan confidence; el agente ganador responde y puede delegar sub-problemas a otros agentes vía eventos.
Introducción
Te doy el patrón completo. Arquitectura, contratos de evento, anti-patrones, seguridad y el pacto UX que nadie firma hasta que explota.
Qué estamos resolviendo (en claro)
Puntos
- Reducir contexto inútil en prompts.
- Aislar fallos por dominio.
- Permitir equipos autónomos (cada uno con su BFF y su agente).
- Mantener una UX coherente pese a la descentralización.
Principio arquitectónico imprescindible
Descentraliza la responsabilidad cognitiva. Centraliza la infraestructura técnica que garantiza seguridad, auditoría y orden. Traducción: agentes por dominio + un Event Bus global para hablarse.
Componentes esenciales (resumen)
Host / Shell
Monta el Event Bus y el UI unificado. No ejecuta prompts.
Micro-frontends (Remotes)
UI + agnostic agent client.
BFF por dominio
Ejecuta inferencia, almacena prompts versionados, audita.
Event Bus Global
Canal estándar para intentos y delegaciones.
Orquestación concreta
Coreografía por defecto; orquestador solo si necesitas transacciones distribuidas.
El contrato del evento (no lo negocies)
Si no tienes un schema, tendrás ruido. Exige este JSON mínimo:
{
"id": "uuid",
"source": "inventory-agent",
"intent": "REQUIRE_BILLING_CHECK",
"context": { "userId": "123", "orderId": "405" },
"confidence": 0.87,
"metadata": { "traceId": "abc", "locale":"es-ES" },
"timestamp": 1700000000
}
Reglas rápidas
- confidence < 0.6 => requiere confirmación humana o UI consent.
- context debe llevar solo IDs y flags. No historial de chat completo.
- metadata incluye traceId para rastrear en las trazas distribuidas.
Cómo se mueven los eventos (flujo)
- Usuario pregunta algo en el host.
- Host publica evento USER_INPUT al bus.
- Agentes suscritos calculan su confidence local.
- El agente ganador responde y/o emite delegaciones (events) sobre sub-problemas.
- Si hay delegación, otros agentes reaccionan y devuelven resoluciones.
- Host compone respuestas y muestra coherencia al usuario.
Ejemplo práctico (en vivo)
Usuario: “¿Por qué no ha salido el pedido #405? ¿Falló mi tarjeta?”
- Inventory-agent: detecta pedido retenido → responde estado del envío → emite REQUIRE_BILLING_CHECK.
- Billing-agent: suscribe y consulta pasarela en su BFF → descubre tarjeta vencida → emite BILLING_ISSUE_FOUND.
- Host: recibe ambas respuestas y muestra un flujo: “Pedido retenido. Tarjeta expirada. ¿Actualizar ahora?”
Cómo implementar el Event Bus (dos opciones)
Opción A — RXJS (más control dentro de Angular)
// event-bus.service.ts
import { Subject } from 'rxjs';
export const globalEventBus = new Subject();
// publish: globalEventBus.next(event)
// subscribe: globalEventBus.subscribe(e => ...)
Opción B — CustomEvent (mejor para micro-frontends aislados)
// emit
window.dispatchEvent(new CustomEvent('app:event', { detail: event }));
// listen
window.addEventListener('app:event', e => handle(e.detail));
Anti-patterns del bus (evítalos)
- Enviar cada pensamiento del LLM. Solo conclusiones.
- Compartir historial completo en context.
- Permitir que cualquier módulo escuche todo sin roles.
- Esperar sincronía absoluta entre agentes.
Mecanismo de arbitraje: quién responde primero
Necesitas un árbitro débil. No un maestro, sino una regla simple:
- Cada agente publica su confidence ante USER_INPUT.
- Host espera X ms (ej. 200–400ms) o hasta el primer confidence >= threshold.
- El agente ganador toma la palabra; los demás quedan “en espera” para delegaciones.
Seguridad y datos: no es opcional, es ley
- No inferes en cliente. Nunca. Las keys quedan en los BFF.
- Filtrado por roles en el Event Bus: cada agente tiene claims y scopes.
- Sanitización en BFF: elimina PII innecesaria antes de enviar a LLM.
- Audit log centralizado: guarda hash de mensajes, no el texto completo salvo consentimiento.
- Rate limiting por agente y por usuario.
Prompts y versionado: trátalos como código
Versiona prompts. Haz CI sobre prompts. Un cambio de prompt puede cambiar comportamiento entero. Guarda el promptId en metadata del evento para reproducibilidad.
Observabilidad: métricas mínimas que necesitas YA
- Latencia total por intent (ms).
- Confidence distribution por agente.
- Delegation rate (cuántas veces un agente pide otro agente).
- Fallas y errores por BFF.
- Casos humanos de override (undo/confirm).
Costes y escalado
Agentes especializados consumen menos tokens por prompt. Pero multiplicas llamadas si no cacheas. Cachea respuestas frecuentes en BFF. Si el dominio es alto volumen, considera modelos on-prem o inferencia en región cercana.
UX: cómo evitar que la UX parezca multi-agente
- Unifica la voz: host normaliza tono y formato.
- Muestra trazabilidad solo si el usuario la pide (ej. “ver detalle técnico”).
- Siempre provee undo para acciones críticas.
- Si confidence baja, pide confirmación editable: muestra la transcripción + intención sugerida.
Sincronización del historial
Historial maestro en Host. Los BFFs pueden guardar copias locales por dominio. Para reproducir una conversación: traceId + promptId + promptVersion + snapshot del context.
Tests y despliegue
- Contract tests para el Event Schema.
- E2E con agentes stubs (simula respuestas con confidence).
- Canary deploy de prompts: prueba nuevos prompts con 1% de tráfico antes de publicar.
Cuando usar orquestador en vez de coreografía
Coreografía = menos acoplamiento. Pero si necesitas transacciones distribuidas (ej. reserva + pago atómico), añade un orquestador o un workflow engine (Temporal, Durable Functions). No lo hagas por conveniencia.
Checklist práctico para arrancar en 2 semanas
- [ ] Definir dominios y agentes.
- [ ] Diseñar Event Schema y contract tests.
- [ ] Implementar Bus (CustomEvent + roles).
- [ ] BFFs mínimos con secreto seguro y prompt versionado.
- [ ] UI host de chat, arbiter de confidence y UX confirmaciones.
- [ ] Observability (traces + metrics).
- [ ] Políticas de privacidad y retención.
Cierre directo: lo que debes hacer hoy
No diseñes agentes porque “está de moda”. Distribuye inteligencia solo donde tenga sentido. Empieza por 2 agentes: uno crítico (ej. facturación) y otro de baja prioridad (ej. FAQ). Lanza el Event Bus y valida la coreografía. Mide confidence y delegations la primera semana. Ajusta prompts con datos reales.
¿Quieres el kit para arrancar? Te puedo pasar:
- Event Bus + arbiter en TypeScript.
- BFF skeleton que llama a LLM y valida JSON.
- Prompts versionados y tests de contrato.
Responde “QUIERO EL KIT” y te lo envío listo para pegar en tu repo.
Esto no acaba aquí. Si lo haces bien, tu app dejará de ser un oráculo confuso y empezará a funcionar como un equipo de especialistas que no se pisan el uno al otro. ¿Empezamos por el Event Bus o por el BFF? Responde “BUS” o “BFF”.
Dominicode Labs
Si quieres recursos prácticos y plantillas para implementar este patrón, revisa Dominicode Labs. Encontrarás ejemplos de Event Bus, skeletons de BFF y tests de contrato que aceleran la puesta en marcha.
FAQ
- ¿Por qué dividir en agentes en vez de un asistente monolítico?
- ¿Qué contiene el contrato mínimo de un evento?
- ¿Cómo evito que la UX se perciba como multi-agente?
- ¿Cuándo necesito un orquestador en lugar de coreografía?
- ¿Dónde deben residir las claves y la inferencia?
- ¿Qué métricas son imprescindibles al arrancar?
¿Por qué dividir en agentes en vez de un asistente monolítico?
Dividir reduce contexto inútil en prompts, aisla fallos por dominio y permite equipos autónomos con BFFs propios. Es coste de ingeniería que compensa cuando el producto escala.
¿Qué contiene el contrato mínimo de un evento?
El JSON mínimo incluye id, source, intent, context (solo IDs/flags), confidence, metadata (traceId, locale) y timestamp. Sin schema tendrás ruido.
¿Cómo evito que la UX se perciba como multi-agente?
Unifica la voz desde el Host, muestra trazabilidad solo si el usuario la solicita y siempre ofrece undo para acciones críticas. Normaliza tono y formato antes de mostrar respuestas.
¿Cuándo necesito un orquestador en lugar de coreografía?
Cuando necesitas transacciones distribuidas atómicas (ej. reserva + pago). Para casos simples, coreografía reduce acoplamiento; usa orquestador solo para transacciones complejas.
¿Dónde deben residir las claves y la inferencia?
Nunca en el cliente. Las keys y la inferencia deben vivir en los BFFs o infraestructura backend segura. El cliente solo publica eventos y muestra resultados.
¿Qué métricas son imprescindibles al arrancar?
Latencia por intent, distribución de confidence por agente, delegation rate, fallas por BFF y casos humanos de override.
