NORA — revues IA du catalogue + gouvernance steward¶
NORA est la couche de revue steward posée par-dessus le daemon
catalog-sync. Le daemon propose des
enrichissements de catalogue via un LLM local (description ≤20 mots,
tags PII), NORA met ces propositions en file d'attente comme
proposals_pending qu'un steward (data owner, analyste, compliance
officer) revoit dans le cockpit et soit accepte en l'état, édite,
fusionne avec la valeur actuelle, ou rejette.
Les propositions acceptées se propagent synchronement vers
Catalogue (PATCH description / classification) et Vector store (upsert
d'embedding dans la collection catalog consommée par la recherche
sémantique d'ADEN), donc les agents IA aval voient la nouvelle
description en quelques millisecondes — pas besoin d'attendre la
prochaine fenêtre catalog-sync de 6 h.
Sprint 71-75 — bout-en-bout livré 2026-05-14
La chaîne complète (drawer cockpit → DB cockpit-backend → Catalogue
PATCH → Vector store upsert) est live sur demo.akko-ai.com et validée
end-to-end par l'agent akko-e2e-tester
(verify_nora_full_chain.py). Voir
ADR-060 NORA rebrand + Lego discipline.
Pourquoi une couche steward ?¶
L'auto-enrichissement seul ne suffit à aucune équipe ayant des obligations de conformité. Le positionnement AKKO est gouvernance-first, pas « le LLM écrit dans ton catalogue et tu lui fais confiance ». NORA capture le moment précis où un humain prend la responsabilité d'un changement de métadonnée :
- Trace d'audit OCSF : chaque accept / reject est loggé avec l'identité du steward, la proposition LLM d'origine, la valeur acceptée, et les accusés de réception aval (status OM PATCH, ack Vector store upsert).
- Garde PII : un tag PII proposé par le LLM ne s'applique jamais automatiquement. Le steward décide quelles colonnes sont sensibles, avec la traçabilité complète pour RGPD Art. 30 + DORA Art. 13.
- Édition steward : la proposition est un point de départ, pas une valeur finale. Les stewards combinent régulièrement la formulation LLM avec une connaissance métier (« score de risque AML [...] validé par Compliance 2026-05-14 »).
- Chemin de rollback : chaque proposition acceptée est rejouable depuis la table d'audit. Un accept erroné peut être annulé sans toucher au warehouse.
Architecture¶
flowchart LR
subgraph Daemon["akko-catalog-sync"]
ENR[Enrichissement<br/>LLM local<br/>≤20 mots + PII]
end
subgraph DB["schéma catalog_sync (Postgres)"]
PROP[proposals_pending]
end
subgraph Cockpit["cockpit /#nora"]
LIST[Liste pending<br/>+ badge KPI]
DRAW[Drawer<br/>Actuel / Proposé / Éditeur]
BTN["4 boutons<br/>Rejeter · Accepter tel quel<br/>Appliquer édit · Fusionner"]
end
subgraph Backend["cockpit-backend"]
API["POST /api/nora/proposals/{id}/accept"]
OM_PATCH[Client OM PATCH]
MV_UPS[Client Milvus upsert]
end
subgraph Sinks
OM[(OpenMetadata)]
MV[(Milvus<br/>collection: catalog)]
AUDIT[(audit_log<br/>OCSF)]
end
ENR --> PROP
PROP --> LIST
LIST --> DRAW --> BTN
BTN --> API
API --> OM_PATCH --> OM
API --> MV_UPS --> MV
API --> AUDIT
Le handler d'acceptation steward dans cockpit-backend fait trois
écritures dans l'ordre : DB (proposals_pending.status='accepted' +
decision_mode + accepted_value), Catalogue PATCH (description /
classification), Vector store upsert (re-embed via Passerelle IA (LiteLLM), remplace le
vecteur dans la collection catalog). Si OM ou Vector store échouent,
l'écriture DB reste autoritaire et un reconciler peut rejouer plus
tard — la décision steward n'est jamais perdue.
Les quatre modes de décision¶
Le drawer expose quatre boutons mappés sur l'enum decision_mode du
schéma catalog-sync :
| Bouton | decision_mode |
Ce qui est persisté |
|---|---|---|
| Rejeter | rejected |
Proposition abandonnée, valeur OM actuelle inchangée. |
| Accepter tel quel | as_is |
La proposition LLM devient la nouvelle description OM verbatim. |
| Appliquer édit | edited |
La valeur textarea steward (édition libre) devient la nouvelle description OM. |
| Fusionner | merged |
Une valeur fusionnée custom (actuelle + proposition + steward) devient la nouvelle description OM. Utile quand le LLM a capté une facette manquante mais que la description actuelle a une formulation grade-conformité. |
UI cockpit¶
La page /#nora du cockpit affiche :
- Table 8 colonnes : Asset FQN, Champ (description / tag), Actuel, Valeur proposée, Confiance, Enricher, Proposé le, Actions
- Badge KPI dans la sidebar : compteur pending (ex.
NORA Reviews 6) - Drawer au clic ligne : trois sections (Valeur OM actuelle / Valeur LLM proposée / Textarea éditable) + quatre boutons en pied.
- Placeholder fallback : les tables sans documentation rendent
(non documenté)en italique gris discret pour rendre l'état vide explicite plutôt que confus. - Liste pending temps réel : après accept, la ligne disparaît du filtre pending et le badge décrémente.
Surface API¶
Endpoints backend (cockpit-backend, FastAPI) :
GET /api/nora/proposals?status=pending— liste les revues pending, scopées aux namespaces autorisés du steward via Moteur de politiques (OPA) (data owners ne voient que leur domaine, akko-admin voit tout).POST /api/nora/proposals/{id}/accept— body :{mode, value?, note?}oùmode ∈ {as_is, edited, merged, rejected}. Retourne le status OM PATCH, l'ack Vector store upsert, et l'id de la ligne d'audit.GET /api/nora/audit— log d'audit paginé (steward, mode, valeurs avant / après, status OM PATCH, status Vector store upsert, timestamp).
Câblage (chart values)¶
# helm/akko/charts/akko-cockpit-backend/values.yaml
nora:
dsnSecretRef:
name: "akko-catalog-sync-pg" # réutilise le DSN de catalog-sync
key: "dsn"
schema: "catalog_sync"
openmetadata:
baseUrl: "http://openmetadata:8585/api/v1"
servicePrefix: "trino-akko" # FQN 3-segments → OM 4-segments
oidc:
tokenUrl: "http://akko-akko-keycloak:8080/realms/akko/protocol/openid-connect/token"
existingSecret: "akko-openmetadata-oidc"
milvus:
uri: "http://akko-akko-milvus:19530"
collection: "catalog"
litellm:
baseUrl: "http://akko-akko-litellm:4000"
embedModel: "akko-embed"
apiKeyValue: "akko-dev-litellm-key" # ou apiKey.secretName en prod
Le chart force imagePullPolicy: Always sur le cockpit (et sur
cockpit-backend quand son image est rebuild en cours de sprint) pour
éviter les régressions de layer stale documentées dans
gotcha_image_pull_policy_always_dev_demo.
Limites connues (état Sprint 71-75)¶
- Accept-latency : SLO actuel à 5 s mais les runs à froid mesurent
5 à 8 s (OM PATCH synchrone + Vector store upsert avant le toast). La
propagation async est planifiée (toast < 500 ms, écritures aval en
background) ou le SLO est passé à 10 s. Voir suivi tâche
#347. - Dépendance reseed : NORA ne fait remonter que les propositions
effectivement émises par catalog-sync. Sur un cluster frais (ou
après un wipe warehouse), lancer le DAG catalog-sync avant d'ouvrir
/#nora. - Visibilité cross-tenant : la garde Moteur de politiques est par-namespace ; un
steward ne voit que les propositions dont le FQN est dans sa liste
autorisée. Le scoping multi-tenant des
proposals_pendingeux-mêmes (ex. multi-realm client) est une tâche Phase 2.
Liens¶
akko-catalog-sync— le daemon producteur des propositions NORA.ADEN— l'agent langage-naturel → SQL qui consomme la collection Vector storecatalog.OpenMetadata— le catalogue canonique vers lequel NORA écrit.- ADR-060 — naming + discipline Lego derrière NORA.
- Runbook :
akko-technical-map/runbooks/nora-om-milvus-aden-end-to-end.md.