Aller au contenu

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.

  1. Lookup cache — cache TTL (5 min) indexé par sha256(question | role). Passer force=true pour contourner.
  2. Recherche catalogue — OpenMetadata _search renvoie le top-N des tables candidates.
  3. Vérification OPA par table — pour chaque candidate, POST sur /v1/data/akko/aden/allow avec {user, role, table}. Les tables refusées sont silencieusement retirées du contexte.
  4. Génération LLMqwen2.5-coder:7b reçoit le prompt système, les tables autorisées et les 3 derniers tours de conversation (si session_id est fourni).
  5. Validation sqlglot — la sortie LLM est parsée en dialecte Trino. Tout ce qui n'est pas un unique SELECT / WITH / UNION / EXCEPT / INTERSECT est rejeté. Une denylist (INSERT, UPDATE, DELETE, DROP, etc.) sert de seconde barrière. LIMIT 10000 est injecté s'il est absent.
  6. 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) retour HTTP 413. L'utilisateur peut renvoyer avec confirm_cost=true.
  7. Exécution Trino — avec le token OAuth de l'appelant : row-filters et column-masks en amont s'appliquent toujours.
  8. Caviardage PII — les colonnes taguées PII dans OpenMetadata sont masquées avant la réponse.
  9. Dashboard Streamlit — le résultat est écrit dans le PVC akko-aden-reports et rendu sur https://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 :

  1. Vérification sqlglot — seuls SELECT, WITH, UNION, EXCEPT, INTERSECT sont acceptés. Tout le reste → HTTP 400.
  2. Regex mot-clés avec strip des commentairesINSERT UPDATE DELETE DROP CREATE ALTER TRUNCATE GRANT REVOKE MERGE CALL REPLACE rejetés comme mots isolés (les colonnes deleted_at passent toujours), après suppression des commentaires -- et /* */.
  3. Garde multi-statement — seconde vérification ;<alphanum> attrape tout payload qui contournerait le parser.
  4. Flag OPA read_only sur svc-aden — même si le process ADEN est compromis, le compte de service ADEN a read_only: true dans 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 :

python3 -m pytest tests/integration/aden-select-only.py -v

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ée
  • aden_cache_hit
  • aden_no_tables, aden_opa_denied
  • aden_sql_generated, aden_cost_gate, aden_trino_executed
  • aden_pii_masked, aden_dashboard_published
  • aden_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_URL est défini ; sinon in-process (perdue au restart).
  • Le sidecar Streamlit monte le PVC akko-aden-reports en lecture seule et le sert sur reports.<domain>.
  • Les traces Tempo sortent avec le service name akko-aden — suivre un request id depuis Cockpit jusqu'à l'exécution SQL.

Voir aussi