83 lines
3.8 KiB
Markdown
83 lines
3.8 KiB
Markdown
# 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/<name>/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).
|