Saltar al contenido
AI Workforce OS
ES EN
Crear cuenta

Arquitectura de AI Workforce OS

AI Workforce OS separa la decisión de la ejecución: un backend en Go es la fuente de verdad, un runtime en Python solo razona con el modelo y un frontend en Next.js dibuja la oficina a partir de eventos. Esta página es para quien despliega, extiende o audita el sistema: cómo encajan las piezas y cómo viaja una petición de punta a punta.

7 min de lectura

En esta página
  1. Componentes
  2. Fronteras entre backend y runtime
  3. Flujo de una petición
  4. Canal en tiempo real
  5. Modelo de datos
  6. Multi-tenencia
  7. Simulación frente a IA real
  8. Despliegue y borde
  9. Estructura del repositorio
Volver arriba

Componentes#

Todo el estado de negocio vive en el backend; el runtime no guarda estado entre llamadas. Postgres, Redis, el almacenamiento de objetos y el sandbox son opcionales en desarrollo y tienen un modo de reserva.

ComponenteTecnologíaResponsabilidad
FrontendNext.js, React Three FiberOficina 3D y modo dashboard. Consume REST y el WebSocket; toda animación deriva de eventos reales.
BackendGo, chiAPI REST /api/v1, WebSocket /ws, orquestador, aprobaciones, auditoría, presupuesto, autenticación y RBAC.
Agent runtimePython, FastAPI, CrewAIRecibe tarea y contexto, llama al proveedor del modelo y devuelve un resultado estructurado.
PostgresPostgreSQLDatos de todas las organizaciones, con row-level security y migraciones SQL numeradas. Sin DATABASE_URL el backend corre en memoria para demos.
RedisRedisPub/sub de eventos hacia el hub WebSocket, bloqueos y límites de tasa. Sin REDIS_URL los eventos y bloqueos son locales al proceso.
Almacenamiento de objetosCompatible con S3 (RustFS en desarrollo)Bytes de los archivos del drive. Sin S3_ENDPOINT y S3_BUCKET se guardan en Postgres.
Sandbox runnerGo, DockerEjecuta en contenedores desechables y sin red el código que un agente pidió y alguien aprobó. Solo con SANDBOX_URL y SANDBOX_TOKEN.
CaddyCaddyProxy de producción: TLS automático, envía /api/* y /ws al backend y el resto al frontend.

Fronteras entre backend y runtime#

La regla central: el runtime nunca habla con Postgres, Redis ni sistemas externos. Recibe una tarea con su contexto y devuelve un resultado, solicitudes de herramientas y consumo; solo contacta al proveedor del modelo. El backend decide permisos, autonomía y presupuesto, y es el único que ejecuta herramientas.

El backend llama al runtime por HTTP con JSON en RUNTIME_URL. Si RUNTIME_TOKEN está definido en ambos lados, cada llamada lleva ese secreto como token bearer. El motor de IA queda detrás de una interfaz propia (AgentEngine), de modo que CrewAI puede reemplazarse sin tocar el backend.

Endpoint del runtimeUso
POST /v1/plan, /v1/plan-phases, /v1/plan-phase, /v1/replanDescomponer una petición o un proyecto en tareas con dependencias.
POST /v1/run-taskEjecutar una tarea; devuelve output, consults, tool_requests y usage.
POST /v1/consult, /v1/review, /v1/counterargumentConsulta entre agentes, revisión de calidad y contraargumento para aprobaciones de alto riesgo.
POST /v1/synthesize, /v1/summarizeReporte consolidado y resúmenes.
POST /v1/route, /v1/chat-replyEnrutar un mensaje de chat y generar la respuesta de un agente.
POST /v1/estimate, /v1/context-sizes, /v1/provider-testEstimación de costo, tamaño del prompt y prueba de la clave de una organización.
GET /healthzEstado y modo: simulation o live.

Consejo

El runtime de agentes tiene su propia página con el contrato completo, los proveedores y la política de datos.

Flujo de una petición#

La solicitud del usuario llega al orquestador, que pide un plan al runtime, lo valida y crea tareas. Cada tarea la ejecuta un agente que puede pedir herramientas; las acciones sensibles esperan aprobación humana y al final se sintetiza un reporte.

  1. El usuario envía el texto con POST /api/v1/requests (o por el chat, que lo enruta). El backend responde 202 con request_id y emite request.received.
  2. El orquestador pide el plan al runtime y lo valida antes de aceptarlo: claves duplicadas, dependencias desconocidas o circulares y profundidad máxima. Luego emite plan.created y task.created.
  3. Las tareas se ejecutan respetando depends_on. Cada una cambia el estado del agente (agent.state_changed) y puede generar consultas entre agentes (message.sent) y una revisión de calidad (task.reviewed).
  4. Las herramientas que pide el runtime pasan por la política de autonomía y los permisos. Si hace falta aprobación, se crea un registro pendiente (approval.requested) y la tarea queda en awaiting_approval.
  5. Un usuario con permiso decide con POST /api/v1/approvals/{id}/decision. La tarea continúa o se bloquea.
  6. Cuando todas las tareas terminan, el runtime sintetiza el reporte y el backend emite report.created y request.completed.
  7. En paralelo se registra el costo de cada llamada y se aplica el presupuesto: budget.warning avisa y budget.exceeded pausa la ejecución.
Secuencia resumidaTexto
POST /api/v1/requests {"text": "..."}      -> 202 {"request_id": "..."}
  orchestrator -> runtime POST /v1/plan          -> tasks + depends_on
  backend validates the plan                     -> plan.created, task.created*
  per task: runtime POST /v1/run-task            -> output, consults, tool_requests, usage
  tool_requests -> policy/autonomy check         -> approval.requested (if needed)
  POST /api/v1/approvals/{id}/decision           -> approval.resolved, task resumes
  runtime POST /v1/synthesize                    -> report.created, request.completed

Canal en tiempo real#

GET /ws abre un WebSocket por organización. El backend publica cada evento en Redis (canal aiworkforce:events) y un hub reenvía el cuadro solo a los clientes cuyo org_id coincide; un cuadro sin org_id se descarta. Un cuadro con audience llega solo a esos usuarios.

Al conectar llega un evento hello con la lista de agentes y después los eventos de dominio. El cliente debe recargar el estado por REST tras reconectar, porque no hay replay.

  • El servidor envía ping cada 30 s y cierra si no recibe pong en 60 s.
  • Un cliente lento, con 256 cuadros en cola, se desconecta.
  • Con autenticación activa, el socket se cierra con el código 4401 a los 15 minutos (la vida por defecto del token de acceso) para que el cliente reconecte con un token nuevo.
Cuadro de eventoJSON
{
  "id": "<uuid>",
  "type": "task.completed",
  "ts": "2026-01-01T12:03:22Z",
  "org_id": "<org id>",
  "agent_id": "sales",
  "payload": { "task": { "id": "...", "status": "done" } }
}
GrupoTipos de evento
Conexiónhello, error, metrics.updated, activity.logged
Peticionesrequest.received, request.queued, request.dequeued, request.resumed, request.status_changed, request.completed, plan.created, report.created
Tareastask.created, task.started, task.completed, task.failed, task.blocked, task.retrying, task.reviewed, task.steered, task.reassigned
Agentes y chatagent.created, agent.state_changed, message.sent, chat.message, chat.typing, route.decided, initiative.raised
Aprobacionesapproval.requested, approval.progress, approval.reminder, approval.resolved
Costoscost.estimated, budget.warning, budget.exceeded, budget.resumed, provider.no_balance
Proyectosproject.created, project.launched, project.delta, project.status_changed, project.budget_warning, project.plan_change_*
Recompensas y oficinacoins.earned, shop.purchased, item.equipped, item.unequipped, office.item_placed, office.item_removed, office.layout_updated

Nota

La tabla agrupa los eventos principales; algunos módulos emiten tipos adicionales con el mismo formato de cuadro.

Modelo de datos#

Las tablas viven en Postgres y todas las de negocio llevan org_id. Esta es la vista resumida; el esquema completo está en las migraciones numeradas de backend/internal/infrastructure/postgres/migrations/.

EntidadTablas principalesNotas
Organizaciones y usuariosorganizations, users, memberships, refresh_tokens, invitationsRoles por membresía: owner, admin, member, viewer.
Agentesagents, agent_profiles, agent_settings, agent_controlsEstado, herramientas, permisos y nivel de autonomía.
Peticiones y tareasrequests, tasks, request_runs, task_checkpoints, reportsEstados de tarea: pending, running, blocked, awaiting_approval, done, failed.
Aprobacionesapprovals, standing_approvals, approval_executionsDoble aprobación, permisos permanentes por proyecto y ejecución única de lo aprobado.
Proyectosprojects, project_templates, plan_changesPlanes con nodos, presupuesto y versiones de plantilla.
Memoriamemories, memory_docsMemoria a largo plazo por agente.
Habilidadesskills, skill_versions, skill_assignmentsVersionadas, con flujo de revisión.
Recompensascoin_wallets, coin_ledger, shop_inventory, shop_equipment, shop_officeMonedas por trabajo terminado y objetos de personaje y oficina.
Gobierno y costoaudit_logs, audit_chain_heads, usage_entries, budget_capsAuditoría encadenada, ledger de consumo y topes.

Multi-tenencia#

El org_id viaja dentro del token de acceso y nunca se toma de la entrada del cliente. La capa HTTP lo coloca en el contexto de la petición y todas las consultas se filtran por él. Un usuario puede pertenecer a varias organizaciones y cambia de una a otra abriendo una sesión nueva.

Como defensa en profundidad, Postgres aplica row-level security:

  • Cada transacción fija app.org_id con set_config(..., true), local a la transacción; las políticas comparan org_id con app_current_org().
  • Si el valor falta, la consulta no ve ni escribe filas: falla cerrado.
  • La aplicación hace SET ROLE a un rol sin privilegios de propietario (DB_APP_ROLE, por defecto app_user, sin BYPASSRLS); las migraciones usan MIGRATE_DATABASE_URL.
  • El hub del WebSocket también filtra por organización.

Consejo

La página de autenticación y multitenencia detalla roles, sesiones y las políticas de RLS.

Simulación frente a IA real#

El contrato HTTP del runtime es el mismo en ambos modos; cambia el motor. En simulación (con SIMULATION=true o sin ningún proveedor configurado) el runtime devuelve resultados guionados con latencias simuladas, sin red y sin costo. En modo real usa los proveedores configurados y su GET /healthz informa mode: live y el proveedor.

Si hay un proveedor configurado pero el motor real no arranca, el runtime responde en simulación con status: degraded y un aviso; no hay fallback silencioso. El backend refleja el modo en su propio GET /healthz como runtime_mode.

Estado del backendBash
curl http://localhost:8080/healthz
# {"status":"ok","runtime":"up","runtime_mode":"simulation"}

Consejo

SIMULATION=true siempre gana: no se hace ninguna llamada de red, incluso con claves presentes.

Despliegue y borde#

Hay tres archivos de Compose:

  • docker-compose.yml: desarrollo con cuentas. Levanta postgres, redis, objects (almacenamiento S3), agent-runtime, backend y frontend; el sandbox runner está en el perfil sandbox.
  • docker-compose.demo.yml: se combina con el anterior para el modo demo abierto (AUTH_ENABLED=false).
  • docker-compose.prod.yml: producción. caddy, postgres, redis, agent-runtime, backend y frontend; solo Caddy publica puertos (80 y 443).

Nota

En producción Caddy termina TLS, agrega cabeceras de seguridad (HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy) y enruta por ruta: /api/* y /ws al backend, todo lo demás al frontend.

Estructura del repositorio#

El backend sigue capas: api depende de application, que depende de domain; infrastructure implementa los puertos.

Texto
backend/            Go (chi)
  cmd/server/       main.go: composition root (wires every service)
  cmd/sandbox-runner/  code-execution runner
  internal/         domain/ application/ api/ auth/ events/ projects/ drive/
                    skills/ memory/ rewards/ connections/ controls/ audit/ ...
  internal/infrastructure/{postgres,redis,runtime,memory}
  internal/infrastructure/postgres/migrations/   numbered SQL migrations
agent-runtime/      Python FastAPI (CrewAI engine, simulation engine)
  app/              main.py, engine.py, simulation.py, crewai_engine.py, providers.py ...
frontend/           Next.js + React Three Fiber (src/app, src/components, src/lib)
e2e/                Playwright
sandbox/            sandbox container images
Caddyfile           production edge (TLS, reverse proxy)
docker-compose.yml  dev stack; docker-compose.demo.yml, docker-compose.prod.yml
docs/               SPEC.md and architecture/*.md
landing/            this public site