# Forja — Arquitectura técnica > Documento vivo con las decisiones técnicas. ## Visión general Forja es un **único servicio FastAPI** (:8000): - Todo el dominio de gobierno, runtime LangGraph, guardrails y persistencia. - UI embebida (HTMX) para uso humano + API REST completa bajo `/api`. ## Diagrama ``` ┌────────────────────────────────────────────────────────────┐ │ forja-core :8000 │ │ UI HTMX (humanos) + REST API (/api) + dominio completo │ │ │ │ LLM (strategy) │ Guardrails (strategy) │ LangGraph │ │ │ │ + checkpoints│ └────────────────────────────────────────────────────────────┘ ``` ## Patrones aplicados - **Strategy / Plugin**: `LLMProvider`, `GuardrailEngine`, `AgentRegistry`, `PolicyStore` son `Protocol`s con varias implementaciones intercambiables vía factory que lee `Settings`. - **Configuration over code**: agentes y políticas como YAML versionados; cambios sin redeploy. - **Fail-closed por defecto**: errores en validadores cuentan como bloqueo. - **Trazabilidad obligatoria**: `trace_id` UUID propagado por middleware → structlog → API → JSONL. ## Persistencia | Capa | Tecnología | Por qué | |---|---|---| | Registries (agents/policies/datasets/models) | YAML por versión + index.yaml | Humano lo escribe; diff legible; hash SHA-256 normalizado. Base común en `registry/yaml_store.py`. | | Logs de violaciones | JSONL append-only | Inmutable, auditable, fácil de shipear. | | Logs de ejecuciones | JSONL append-only | Idem. | | Checkpoints LangGraph | SQLite | Tool right; persistencia entre restarts (HITL). | | Training runs / evaluaciones / promociones | JSON por entidad bajo `data/` | Estado mutable consultable por id; los registros de promoción aprobada van además a JSONL append-only. | ## Ciclo MLOps (training → evaluación → promoción) 1. `POST /api/training/runs` envía un job al backend configurado (`TRAINING_BACKEND=mock|azure_ml`) con dataset, modelo base y, opcionalmente, el agente y su **versión candidata**. 2. `POST /api/training/runs/{id}/evaluate` ejecuta los escenarios canónicos (`agents//examples/*.txt`) contra la versión candidata por el runtime gobernado completo; pasa si no hay violaciones bloqueantes ni fallos. 3. `POST /api/promotions` crea la petición; exige run `succeeded`, evaluación `passed` y que la evaluación cubra **exactamente** la versión a promocionar. 4. Un operador aprueba/rechaza (UI `/promotions`); al aprobar, la versión pasa a `active` en el registry y queda un registro de auditoría append-only. ## HITL — Patrón Se usa la API dinámica `interrupt(payload)` de LangGraph ≥0.2 dentro del nodo `approve_gate`. Sólo pausa cuando hay acciones de alto riesgo (`risk_score >= threshold` o `requires_approval=True`). El cliente envía `Command(resume={"approved_action_ids": [...]})` vía `POST /api/executions/{trace_id}/approve`. ## Observabilidad - `structlog` con renderer JSON. - Middleware FastAPI inyecta `X-Trace-Id` y bind-ea contexto. - Cada nodo del grafo añade un step a `decision_path` con `duration_ms`. ## Errores - Azure OpenAI / OpenAI: retry exponencial 1s/2s/4s; si `LLM_FALLBACK_PROVIDER` está configurado, `FallbackLLMProvider` delega en el secundario; luego `status=failed`. - Validador: `fail_closed` por defecto. - Schema mismatch en LLM output: 1 retry, luego `failed`. - Checkpoint corrupto: 500 explícito (sin auto-recovery). ## Decisiones pospuestas Ver [`docs/futuro.md`](docs/futuro.md).