Construire des agents d’analyse de données fiables : leçons de la KDD Cup
Les enseignements tirés de la KDD Cup montrent que des agents d’analyse de données fiables dépendent d’une évaluation rigoureuse, d’une orchestration robuste des outils et d’un traitement attentif de données ambiguës du monde réel. Les benchmarks de type compétition exposent précocement les modes de défaillance, encourageant l’amélioration itérative, un raisonnement transparent et des boucles de validation qui transforment les prototypes en assistants d’analyse fiables.
Tags
Résumé rapide
Les enseignements tirés de la KDD Cup montrent que des agents d’analyse de données fiables dépendent d’une évaluation rigoureuse, d’une orchestration robuste des outils et d’un traitement attentif de données ambiguës du monde réel. Les benchmarks de type compétition exposent précocement les modes de défaillance, encourageant l’amélioration itérative, un raisonnement transparent et des boucles de validation qui transforment les prototypes en assistants d’analyse fiables.
Construire des agents d’analyse de données fiables : leçons de la KDD Cup
Les agents d’analyse de données occupent une place inconfortable dans la vague actuelle des systèmes d’IA. Un chatbot qui produit un paragraphe plausible a réussi si le paragraphe est utile. Un agent d’analyse qui produit un nombre plausible n’a réussi que si le nombre est correct — et l’exactitude dans le travail sur les données n’est pas une question de style. C’est une question de jointures, de filtres, de types, de nullabilité et de fuseaux horaires.
La KDD Cup, la longue série de compétitions de fouille de données associée à la communauté SIGKDD, est un cadre utile pour ce problème. Les tâches de compétition sont adverses d’une manière très spécifique : elles vous donnent un jeu de données que vous n’avez pas conçu, une question que vous n’avez pas écrite et une fonction de score avec laquelle vous ne pouvez pas négocier. Le billet de blog IA de NVIDIA, « Building Reliable Data Analytics Agents: Lessons from the KDD Cup », utilise ce cadre pour examiner ce qu’il faut pour qu’un agent soit digne de confiance pour un vrai travail d’analyse plutôt que pour seulement sembler le faire.
Cet article reprend le même thème — la fiabilité comme problème d’ingénierie central — et le transforme en guide de construction : quoi installer, comment structurer le pipeline et comment le tester. Une clarification d’emblée : la source est un article d’ingénierie d’un fournisseur, et les modèles d’implémentation ci-dessous sont des pratiques générales et reproductibles motivées par ce thème. Ce ne sont pas des affirmations sur les détails internes d’un système spécifique qui y serait décrit.
Pourquoi les agents d’analyse échouent différemment
La plupart des défaillances d’agents dans des contextes conversationnels sont agaçantes. La plupart des défaillances d’agents dans des contextes analytiques sont silencieuses. Un agent qui hallucine un nom de colonne obtient généralement une erreur. Un agent qui joint deux tables sur la mauvaise clé ne le fait souvent pas. Il renvoie un nombre, le nombre semble raisonnable, et le nombre est faux.
Trois modes de défaillance dominent :
Dérive de schéma et hallucination. Le modèle suppose une colonne appelée user_id alors que la table contient uid. Sans introspection, le modèle comble les lacunes avec des noms plausibles.
Requêtes sémantiquement fausses mais syntaxiquement valides. Une jointure interne supprime silencieusement des lignes qu’une jointure gauche aurait conservées. Une clause WHERE sur une colonne nullable supprime des enregistrements que l’analyste s’attendait à voir.
Non-reproductibilité. La même question posée deux fois produit deux requêtes différentes et deux réponses différentes. Il n’y a aucun artefact à auditer.
Chaque pratique ci-dessous vise à combler l’une de ces lacunes. L’idée unificatrice est simple : traiter l’agent comme un pipeline avec des étapes vérifiables, pas comme un prompt qui émet des réponses.
Prérequis
Pour suivre, vous avez besoin de :
- Python 3.11 ou version ultérieure.
- Un shell de type Unix (macOS, Linux ou WSL). Les commandes supposent
bashouzsh. - Accès à n’importe quel LLM suivant les instructions via une API HTTP ou un serveur local. Les exemples ci-dessous sont neutres vis-à-vis du fournisseur et font référence à un identifiant de modèle que vous fournissez.
- Suffisamment de RAM pour contenir votre jeu de données de travail, ou un moteur de base de données capable de déborder sur disque. Aucun GPU n’est nécessaire pour l’échafaudage lui-même.
La pile est délibérément banale : Pandas pour les données en mémoire, DuckDB pour le SQL sur fichiers sans serveur, Pydantic pour des contrats d’outils typés, Tenacity pour la logique de nouvelle tentative, et pytest pour les tests de régression.
Installation étape par étape
Commencez par créer un environnement isolé pour que les dépendances de l’agent n’entrent pas en collision avec votre Python système.
python3 -m venv .venvActivez-le. Sous Windows, utilisez .venv\Scripts\activate à la place.
source .venv/bin/activateMettez pip à niveau avant d’installer quoi que ce soit, car les versions plus anciennes résolvent les dépendances de manière moins prévisible.
python -m pip install --upgrade pipInstallez les dépendances principales. DuckDB exécute du SQL analytique directement sur des fichiers CSV et Parquet, ce qui élimine toute une classe de bugs « l’agent pointait vers la mauvaise base de données » pendant le développement.
pip install "pandas>=2.0" duckdb "pydantic>=2" pyyaml tenacity pytest openaiCréez la structure du projet. Garder les outils, les contrôles de validation et les évaluations dans des répertoires séparés rend évident l’endroit où une nouvelle pièce de logique doit aller.
mkdir -p analytics_agent/tools analytics_agent/checks analytics_agent/evals data artifacts
touch analytics_agent/__init__.pyÉcrivez un fichier de configuration plutôt que de coder en dur les noms de modèles et les chemins. C’est le changement unique qui améliore le plus la reproductibilité, car chaque exécution est décrite par un artefact que vous pouvez committer.
cat > config.yaml <<'YAML'
model:
id: "<your-model-id>"
temperature: 0
max_repair_attempts: 3
data:
source: "data/orders.csv"
sample_rows: 20
limits:
max_rows_returned: 5000
statement_timeout_seconds: 30
YAMLExportez vos identifiants d’API en variables d’environnement afin qu’ils n’entrent jamais dans le dépôt. Ne committez jamais de clés dans un fichier de configuration.
export ANALYTICS_AGENT_API_KEY="<your-key>"Définissez temperature sur 0 dans la config. Cela ne garantit pas le déterminisme entre fournisseurs, mais cela supprime une source de variance et rend les régressions plus faciles à attribuer.
Ancrer l’agent dans le schéma réel
La première règle d’un agent d’analyse fiable : il ne doit jamais deviner à quoi ressemblent les données. Avant qu’une question ne soit répondue, profilez la source et placez le profil dans le contexte du prompt.
import duckdb
con = duckdb.connect()
con.execute("CREATE VIEW t AS SELECT * FROM read_csv_auto('data/orders.csv')")
print(con.execute("DESCRIBE t").df())
print(con.execute("SUMMARIZE t").df())DESCRIBE donne les noms et types de colonnes ; SUMMARIZE ajoute les décomptes de nulls, les décomptes distincts et les distributions de base. Transmettre les deux au modèle coûte quelques centaines de tokens et élimine la plupart des défaillances d’hallucination de colonnes.
Échantillonnez aussi de vraies lignes, mais échantillonnez de manière déterministe — une graine fixe ou un ORDER BY avec un LIMIT — afin que la même question produise le même contexte à chaque exécution.
Contraindre la surface des outils
Un agent n’est fiable que dans la mesure des actions qu’il peut entreprendre. Définissez un petit ensemble typé d’outils et refusez tout le reste.
from pydantic import BaseModel, Field
class DescribeTable(BaseModel):
table: str = Field(..., description="Name of a registered view.")
class SampleRows(BaseModel):
table: str
limit: int = Field(20, ge=1, le=100)
class RunSQL(BaseModel):
sql: str = Field(..., description="A read-only SELECT or WITH statement.")
rationale: str = Field(..., description="Why this query answers the sub-question.")Quatre outils — décrire, échantillonner, exécuter du SQL et un outil de réponse finale — couvrent une quantité surprenante de travail analytique. Chaque outil supplémentaire multiplie l’espace des façons dont l’agent peut mal tourner.
Rendre l’exécution sûre et déterministe
Le SQL généré doit être en lecture seule et borné. Le filtrage par mots-clés est une première barrière utile, mais soyez honnête sur ses limites : ce n’est pas une frontière de sécurité. Associez-le à une connexion à la base de données en lecture seule.
FORBIDDEN = ("insert", "update", "delete", "drop", "alter", "create", "attach", "copy")
def safe_sql(sql: str) -> str:
stripped = sql.strip().rstrip(";")
lowered = stripped.lower()
if not lowered.startswith(("select", "with")):
raise ValueError("Only SELECT and WITH statements are permitted.")
if any(kw in lowered for kw in FORBIDDEN):
raise ValueError("Statement contains a forbidden keyword.")
return strippedMême un SELECT peut être coûteux, alors définissez un délai d’expiration de requête et un plafond strict de lignes. Un agent qui renvoie 500 000 lignes n’a pas répondu à la question ; il a déplacé le problème en aval.
Valider chaque résultat avant qu’il n’atteigne l’utilisateur
C’est l’étape que la plupart des équipes sautent, et c’est celle qui compte le plus. Après chaque exécution de requête, lancez des assertions programmatiques sur le résultat. Les échecs doivent déclencher une boucle de réparation, pas une réponse confiante.
def assert_sane(df, min_rows=1, max_null_fraction=0.2, unique_key=None):
assert len(df) >= min_rows, f"Expected >= {min_rows} rows, got {len(df)}"
if unique_key:
assert df[unique_key].is_unique, f"{unique_key} is not unique after the join"
for col in df.columns:
frac = df[col].isna().mean()
assert frac <= max_null_fraction, f"{col} is {frac:.0%} null"La vérification unique_key est particulièrement précieuse. Elle attrape la classique jointure plusieurs-à-plusieurs qui gonfle les comptages de lignes et double silencieusement chaque somme dans une agrégation en aval.
Enveloppez la séquence générer-valider-réparer dans une boucle de nouvelle tentative bornée. Donnez au modèle l’erreur d’assertion exacte et laissez-le réviser ; plafonnez les tentatives pour qu’un agent confus ne boucle pas indéfiniment.
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=8),
retry=retry_if_exception_type(ValueError),
)
def generate_and_execute(question, schema_context):
sql = safe_sql(propose_sql(question, schema_context))
df = con.execute(sql).df()
assert_sane(df)
return dfTraçage et rejeu
Pour chaque exécution, ajoutez un enregistrement structuré à un fichier JSONL : la question, le hachage du contexte de schéma, le SQL généré, le résultat de la validation, la réponse finale et le minutage.
import json, time, hashlib
def log_event(path, **fields):
fields["ts"] = time.time()
with open(path, "a") as f:
f.write(json.dumps(fields) + "\n")
log_event("artifacts/trace.jsonl", question=q, sql=sql, rows=len(df))Ce n’est pas glamour, et c’est la différence entre déboguer en raisonnant et déboguer en devinant. Quand une réponse est contestée trois semaines plus tard, la trace est l’artefact qui résout le litige.
Exemples d’utilisation
Un point d’entrée minimal en ligne de commande rend l’agent testable en CI et dans un terminal. Gardez l’interface étroite.
python -m analytics_agent.run \
--config config.yaml \
--question "What was the monthly revenue trend for the top three regions in the last year?"Exécutez sur un fichier Parquet local au lieu d’un CSV en modifiant data.source dans la config — les couches de profilage et d’outillage ne changent pas.
python -m analytics_agent.run --config config.local.yaml --question "Which customers have not ordered in 90 days?"Pour l’exploration interactive, gardez l’étape de profilage à chaud et réutilisez la connexion DuckDB afin que les questions répétées ne relisent pas le fichier.
Évaluer la fiabilité plutôt que le ressenti
La fiabilité est une propriété mesurable, pas un ressenti. Construisez un ensemble de référence de vingt à cinquante questions avec des réponses que vous avez vérifiées à la main ou par une requête indépendante. Notez chaque exécution selon trois axes : a-t-elle produit la bonne réponse, l’a-t-elle produite avec une requête valide, et l’a-t-elle produite dans le budget de coût.
pytest analytics_agent/evals -q --tb=shortUn test utile affirme explicitement le déterminisme — exécutez la même question deux fois et comparez les DataFrames résultants.
def test_repeatable(agent, question):
a = agent.ask(question)
b = agent.ask(question)
assert a.equals(b), "Agent output is not reproducible at temperature 0"Suivez le taux de réussite dans le temps. Une baisse après un changement de prompt ou de modèle est un signal ; sans l’ensemble d’évaluation, c’est une régression invisible qui se manifeste sous la forme d’un chiffre erroné dans un tableau de bord des mois plus tard.
Limites ouvertes qu’il vaut la peine de nommer
Trois contraintes résistent aux corrections faciles. Premièrement, le SQL généré par LLM peut être correct sur votre échantillon et faux sur des cas limites que l’échantillon ne contenait jamais, donc la couverture de l’ensemble d’évaluation compte plus que sa taille. Deuxièmement, le filtrage SQL par mots-clés relève de la défense en profondeur, pas de la défense ; l’isolation appartient au niveau de la base de données. Troisièmement, « fiable » est relatif au domaine — un taux de réussite de 95 % est excellent pour l’analyse exploratoire et inacceptable pour le reporting réglementaire. Décidez du seuil avant de construire, pas après.
Conclusion
La leçon que le cadre de la KDD Cup rend concrète est que les agents d’analyse sont évalués comme des soumissions de science des données, pas comme des réponses de chat : sur des questions mises de côté, sans crédit partiel pour la confiance. La fiabilité vient de l’échafaudage autour du modèle — l’ancrage dans le schéma avant la génération, une surface d’outils typée et étroite, une exécution bornée en lecture seule, une validation programmatique de chaque résultat, des boucles de réparation bornées et une suite de régression qui attrape la dérive. Aucun de ces composants n’est exotique, et aucun n’est facultatif si la sortie est un nombre sur lequel quelqu’un va agir. Commencez par l’étape de profilage et les assertions de validation ; elles apportent le plus grand gain de fiabilité pour le moindre effort, et tout le reste se construit par-dessus.
Source : « Building Reliable Data Analytics Agents: Lessons from the KDD Cup », NVIDIA AI Blog — https://developer.nvidia.com/blog/building-reliable-data-analytics-agents-lessons-from-the-kdd-cup



