ADEN — Autonomous Data Exploration Node¶
ADEN est la réponse open-source souveraine d'AKKO à Databricks Genie, Snowflake Copilot et Dremio Text-to-SQL. Il convertit les questions en langage naturel en SQL Trino, applique l'autorisation par utilisateur, bride les requêtes dispendieuses, caviarde les PII et publie le résultat sous forme de dashboard Streamlit — entièrement à l'intérieur de votre cluster.
Pour la page de référence du service (endpoints, variables, images) voir Services / ADEN. Cette page traite de la capacité IA et de la place d'ADEN dans la stack.
Pourquoi ADEN¶
| Fonctionnalité | ADEN | Databricks Genie | Snowflake Copilot |
|---|---|---|---|
| Auto-hébergé, OSS | Oui | Non | Non |
| Application du RBAC | OPA | Unity Catalog | Snowflake Roles |
| Caviardage PII | Oui | Oui | Oui |
| Maîtrise du coût via EXPLAIN | Oui | Non | Non |
| Publication de dashboard | Streamlit | Session Genie | Aucune |
| Mémoire de conversation | Oui | Oui | Oui |
| 100 % hors ligne | Oui | Non | Non |
Architecture¶
flowchart LR
USER[Cockpit chat] -->|POST /ask| FE[/api/cockpit/aden/]
FE --> A[ADEN<br/>FastAPI]
A --> CACHE{Cache TTL<br/>5 min}
CACHE -->|miss| OM[OpenMetadata<br/>_search]
OM --> OPA[OPA<br/>/v1/data/akko/aden/allow]
OPA --> LLM[LiteLLM<br/>qwen2.5-coder:7b]
LLM --> VALID[Validation sqlglot<br/>+ denylist de mots-clés]
VALID --> GATE[Trino EXPLAIN TYPE IO<br/>AKKO_COST_BYTES_LIMIT]
GATE -->|ok| EXEC[Exécution Trino<br/>token OAuth utilisateur]
EXEC --> MASK[Caviardage PII<br/>via tags OM]
MASK --> PUB[Publication Streamlit<br/>PVC akko-aden-reports]
PUB -->|URL| USER
Cycle de vie d'une requête¶
Chaque appel ADEN est un pipeline strict — chaque étape suppose la précédente réussie.
- Lookup cache — cache TTL (5 min) indexé par
sha256(question | role). Passerforce=truepour contourner. - Recherche catalogue — OpenMetadata
_searchrenvoie le top-N des tables candidates. - Vérification OPA par table — pour chaque candidate, POST sur
/v1/data/akko/aden/allowavec{user, role, table}. Les tables refusées sont silencieusement retirées du contexte. - Génération LLM —
qwen2.5-coder:7breçoit le prompt système, les tables autorisées et les 3 derniers tours de conversation (sisession_idest fourni). - Validation
sqlglot— la sortie LLM est parsée en dialecte Trino. Tout ce qui n'est pas un uniqueSELECT / WITH / UNION / EXCEPT / INTERSECTest rejeté. Une denylist (INSERT,UPDATE,DELETE,DROP, etc.) sert de seconde barrière.LIMIT 10000est injecté s'il est absent. EXPLAIN (TYPE IO)— maîtrise du coût — Trino estime les octets scannés. Au-delà du seuil (défaut 1 GiB,AKKO_COST_BYTES_LIMIT) retourHTTP 413. L'utilisateur peut renvoyer avecconfirm_cost=true.- Exécution Trino — avec le token OAuth de l'appelant : row-filters et column-masks en amont s'appliquent toujours.
- Caviardage PII — les colonnes taguées
PIIdans OpenMetadata sont masquées avant la réponse. - Dashboard Streamlit — le résultat est écrit dans le PVC
akko-aden-reportset rendu surhttps://reports.<domain>/.
Explicabilité — la trace reasoning.pipeline_steps¶
Chaque réponse ADEN porte un objet reasoning avec une trace pas-à-pas que
le cockpit rend sous forme de panneau « Comment ADEN a répondu ». Chaque
étape enregistre :
| Champ | Signification |
|---|---|
step |
Nom machine de la phase (ex. catalog_search, opa_authorisation, semantic_layer_match) |
description |
Explication en clair de ce qu'ADEN a fait à cette phase |
source |
Backend consulté : verified_queries_cache, openmetadata, open_policy_agent, dbt_semantic_layer, local_llm, trino_executor |
duration_ms |
Temps d'exécution |
result |
Charge JSON compacte montrant la preuve (tables matchées, décisions OPA par table, modèle sémantique choisi…) |
Exemple (dry-run, execute: false) :
{
"reasoning": {
"intent": "list 5 customers",
"pipeline_overview": "1) requête vérifiée ? → 2) recherche catalogue → 3) filtrage OPA par rôle → 4) priorisation tables → 5) couche sémantique dbt → 6) génération SQL par le LLM local → 7) validation syntaxique Trino → 8) exécution avec votre token → 9) réponse",
"sources_queried": ["verified_queries_cache", "openmetadata_catalog", "open_policy_agent", "dbt_semantic_layer", "local_llm"],
"pipeline_steps": [
{"step": "question_received", "source": "aden", "duration_ms": 0},
{"step": "catalog_search", "source": "openmetadata+trino_information_schema", "duration_ms": 335, "result": {"candidate_count": 2}},
{"step": "opa_authorisation", "source": "open_policy_agent", "duration_ms": 19, "result": {"allowed_count": 2}},
{"step": "semantic_layer_match", "source": "dbt_semantic_layer", "duration_ms": 0, "result": {"matched_model": null, "reason": "Aucun modèle métier validé — SQL généré ad-hoc."}}
]
}
}
Ceci répond à la question que pose tout prospect régulé : « comment l'IA a trouvé cette table, pourquoi ce schéma, est-ce qu'elle a bien consulté le catalogue avant d'appeler le LLM ? » — la preuve est dans la réponse elle-même, pas à corréler dans une ligne de log.
Garanties sécurité — SELECT only¶
ADEN est strictement read-only par construction. Même dans le pire
cas où une injection de prompt convaincrait le LLM d'émettre DROP
TABLE, quatre couches indépendantes bloquent l'écriture :
- Vérification
sqlglot— seulsSELECT,WITH,UNION,EXCEPT,INTERSECTsont acceptés. Tout le reste → HTTP 400. - Regex mot-clés avec strip des commentaires —
INSERT UPDATE DELETE DROP CREATE ALTER TRUNCATE GRANT REVOKE MERGE CALL REPLACErejetés comme mots isolés (les colonnesdeleted_atpassent toujours), après suppression des commentaires--et/* */. - Garde multi-statement — seconde vérification
;<alphanum>attrape tout payload qui contournerait le parser. - Flag OPA
read_onlysursvc-aden— même si le process ADEN est compromis, le compte de service ADEN aread_only: truedans la policy OPA ; Trino refuse l'écriture côté serveur.
Les 22 payloads adversariaux dans tests/integration/aden-select-only.py
exercent DDL direct, DML direct, multi-statement, obfuscation par
commentaires, élévation de privilèges (GRANT/REVOKE), CALL
system.runtime.kill_query, variations de casse et wrappers markdown.
À exécuter avant toute release ADEN :
Endpoints API¶
| Méthode | Chemin | Corps | Rôle |
|---|---|---|---|
| POST | /ask |
{question, session_id?, force?, confirm_cost?} |
Question -> SQL -> dashboard |
| POST | /feedback |
{query_id, thumbs, comment?} |
Feedback UX |
| GET | /sessions/{id} |
— | Historique conversationnel |
| GET | /metrics |
— | Prometheus (aden_query_total, aden_query_duration_seconds) |
| GET | /healthz |
— | Liveness |
| GET | /readyz |
— | Readiness (Trino, OPA, OM, LiteLLM) |
Toutes les routes mutantes exigent un JWT Keycloak valide propagé par Cockpit via les en-têtes X-User-Id et X-User-Role.
Événements d'audit structurés¶
ADEN émet une ligne JSON par événement de cycle de vie vers Loki (label app=akko-aden) :
aden_query_received— point d'entréeaden_cache_hitaden_no_tables,aden_opa_deniedaden_sql_generated,aden_cost_gate,aden_trino_executedaden_pii_masked,aden_dashboard_publishedaden_feedback
Grafana livre un dashboard AKKO ADEN SLO avec latences p50 / p95 / p99, burn d'error-budget (1 h / 6 h / 1 j) et top des tables refusées.
Exemples¶
1. Question simple¶
curl -s https://akko.local/api/cockpit/aden/ask \
-H "Content-Type: application/json" \
-H "X-User-Id: alice" \
-H "X-User-Role: akko-analyst" \
-d '{"question": "Top 10 régions par volume transactionnel la semaine dernière"}' | jq
2. Avec mémoire de session¶
curl -s https://akko.local/api/cockpit/aden/ask \
-H "Content-Type: application/json" \
-H "X-User-Id: alice" \
-H "X-User-Role: akko-analyst" \
-d '{"question": "Détaille par jour", "session_id": "abc-123"}' | jq
3. Forcer le coût¶
curl -s https://akko.local/api/cockpit/aden/ask \
-H "Content-Type: application/json" \
-H "X-User-Id: bob" \
-H "X-User-Role: akko-engineer" \
-d '{"question": "Année 2025 complète : transactions jointes aux clients",
"confirm_cost": true}' | jq
Configuration¶
Variables clés (voir helm/akko/charts/akko-aden/values.yaml) :
| Variable | Défaut | Rôle |
|---|---|---|
AKKO_TRINO_URL |
https://trino.akko.svc:8443 |
Coordinateur Trino |
AKKO_OPA_URL |
http://akko-opa:8181 |
Service OPA |
AKKO_OM_URL |
http://akko-openmetadata:8585 |
API OpenMetadata |
AKKO_LITELLM_URL |
http://akko-litellm:4000 |
Gateway LiteLLM |
AKKO_COST_BYTES_LIMIT |
1073741824 |
Seuil EXPLAIN (1 GiB) |
AKKO_CACHE_TTL_SECONDS |
300 |
TTL cache |
AKKO_MAX_TABLES |
5 |
Max tables envoyées au LLM |
Notes opérationnelles¶
- ADEN est stateless hors le PVC des dashboards publiés. Tuer le pod est sûr.
- La mémoire de session vit en Redis si
AKKO_REDIS_URLest défini ; sinon in-process (perdue au restart). - Le sidecar Streamlit monte le PVC
akko-aden-reportsen lecture seule et le sert surreports.<domain>. - Les traces Tempo sortent avec le service name
akko-aden— suivre un request id depuis Cockpit jusqu'à l'exécution SQL.