Par l'équipe Duke Analytics. Niveau technique : SQL, algèbre relationnelle, un peu de théorie de la décision. Il y a deux semaines, modus publiait « Why a Semantic Layer is Not Enough for AI ». Le diagnostic est juste : une couche sémantique dit à l'IA ce que vos métriques signifient, pas comment l'entreprise fonctionne. Elle sait joindre users à orders, elle ignore que users contient trois mille comptes de test. La conclusion de l'article, elle, ne suit pas : aspirer Slack, Git et Notion dans une « couche de contexte » ne répond pas à la vraie question. Cette question tient en un mot : identifiabilité.
1 · Le problème n'est pas le contexte, c'est l'identifiabilité
Notons E l'ensemble de l'évidence accessible au système au moment de répondre : schémas, statistiques de tables, code de transformation, définitions sémantiques, documentation, messages, historique de commits, requêtes passées. Notons M(E) l'ensemble des interprétations métier compatibles avec E, et A(m) la réponse produite par l'interprétation m. La question est identifiable à partir de E si, et seulement si, pour toute paire m1, m2 de M(E) on a A(m1) = A(m2). Sinon, aucun système déterministe alimenté par E ne peut être correct dans tous les cas, quelle que soit sa capacité de raisonnement.
L'expérience de pensée. Deux entreprises A et B. Même schéma, mêmes lignes, même pipeline dbt, mêmes requêtes historiques, même documentation. Toutes deux ont S = 200 € de ventes et R = 30 € de remboursements. Chez A, « chiffre d'affaires » signifie brut, soit 200 €. Chez B, net, soit 170 €. Cette décision n'a jamais été écrite nulle part : E(A) = E(B), donc toute fonction f(E) renvoie la même valeur aux deux, et se trompe chez l'une. Si les deux cas sont équiprobables, l'espérance de précision plafonne à 0,5. Un modèle plus gros n'y change rien. Une couche de contexte non plus, sauf si elle ajoute un élément d tel que M(E ∪ {d}) soit strictement plus petit que M(E).
« L'objectif n'est pas de donner au modèle tout ce que l'entreprise sait. C'est de lui donner juste assez d'évidence pour éliminer toute interprétation qui changerait matériellement la réponse. Le second objectif est mesurable, le premier ne l'est pas. »
2 · Le contexte n'a de valeur que s'il réduit M(E)
Ce que modus a raison de dire, chiffres à l'appui : le benchmark BIRD attache à chaque question une phrase d'évidence externe, par exemple « account.type = 'OWNER' désigne le titulaire principal ». Retirez-la et GPT-4 passe de 54,9 % à 34,9 %. Vingt points : c'est la mesure la plus propre de la valeur du savoir tribal. Mais observez la forme de cette évidence. C'est une phrase qui sépare deux interprétations, pas un fil de discussion.
Un document d est utile si M(E ∪ {d}) est plus petit que M(E), et neutre au mieux s'il ne change rien. Au mieux seulement : un LLM n'est pas indifférent à la taille du prompt. Dans une fenêtre de 32 768 tokens, chaque document non discriminant concurrence le schéma pour l'attention, et la précision se dégrade là où elle était bonne. L'effet lost-in-the-middle a été mesuré jusqu'à moins 30 % sur des informations placées au milieu du contexte. D'où notre premier principe : la pertinence n'est pas le critère, la discrimination l'est. Un exemple vérifié, question puis SQL, vaut plus que vingt documents connexes parce qu'il tranche d'un coup le chemin de jointure, le filtre de population et le grain. DAIL-SQL l'a mesuré : de 72,3 % à 83,5 % sur Spider par la seule organisation des exemples.
3 · Le SQL peut être valide et mathématiquement faux : l'algèbre du fan-out
Avant même de parler d'interprétation, il existe une classe d'erreurs que ni le schéma, ni la couche sémantique, ni le contexte ne préviennent, et qu'aucun parseur SQL ne détecte. Avec les erreurs de jointure et de littéraux, elle représente 60 à 80 % des erreurs observées dans toutes les taxonomies publiées. Soit une commande o de valeur v, avec I lignes d'articles et R événements de remboursement. Aplatir les deux tables enfants sous la commande avant d'agréger produit I × R lignes pour cette seule commande : chaque ligne porte v, et chaque remboursement est répété I fois.
SELECT SUM(o.total) - SUM(r.amount)
FROM orders o
JOIN order_items i ON i.order_id = o.id
JOIN refunds r ON r.order_id = o.idApplication numérique. v = 100, I = 2, R = 2, remboursements de 10 et 20. Le calcul naïf donne 2 × 2 × 100 moins 2 × 30, soit 340, vs 100 moins 30, soit 70. Ajoutez une seconde commande de 100 € à une ligne, sans remboursement : le total naïf donne 440 € pour un vrai 170 €. La base n'a rien fait de faux, le parseur non plus, la jointure a suivi le modèle relationnel à la lettre. L'erreur est une erreur de grain.
La rustine réflexe, SUM(DISTINCT o.total), est une autre erreur : elle confond les valeurs et les entités. Deux commandes légitimes de 100 € valent 200 € en somme sur les entités, et 100 € en somme sur les valeurs uniques. La seule réparation correcte est de restaurer le grain avant de combiner : pré-agréger les enfants au grain du parent, puis soustraire, puis sommer sur les commandes éligibles.
WITH ref AS (
SELECT order_id, SUM(amount) AS refunded
FROM refunds GROUP BY order_id
)
SELECT SUM(o.total - COALESCE(ref.refunded, 0))
FROM orders o
LEFT JOIN ref ON ref.order_id = o.id
WHERE o.customer_id IN (SELECT id FROM customers WHERE kind <> 'internal');L'invariant que la machine peut prouver est simple : pour toute commande éligible o, le nombre de lignes portant o dans la relation intermédiaire juste avant l'agrégation finale doit valoir exactement 1. Au-delà, il y a multiplication de grain. Un moteur d'exécution peut le vérifier, un EXPLAIN peut l'estimer, une inflation supérieure à dix fois le nombre de commandes est un signal, et une analyse d'AST peut le prédire à partir des cardinalités des arêtes de jointure : deux jointures 1:n issues du même parent, sans agrégation intermédiaire, sous une somme portant sur une colonne au grain du parent. Le modèle n'a pas besoin de sentir que la requête est suspecte. Le système prouve qu'un invariant est violé.
4 · Le grain compte plus que le schéma : le contrat de mesure
Les relations entre clés étrangères disent que les tables peuvent se joindre. Elles ne disent pas comment les quantités se comportent à travers ces jointures. Pour chaque mesure, le système doit connaître le grain auquel la quantité existe : order_total existe au grain order_id et s'additionne sur des commandes distinctes ; refund_amount existe au grain refund_event_id, avec order_id pour parent, et exige une pré-agrégation obligatoire avant toute combinaison.
- Mesure : net_sales · Grain d'entité : order · Unité : EUR.
- Population : clients externes, soit customers.kind <> 'internal'.
- Calcul : order_total moins les remboursements agrégés au grain order.
- Attribution : segment client à la date de commande, sur l'historique effectif-daté.
- Provenance : approuvée, finance, 2026-07-01. Validité : transactions à partir du 2026-07-01. Décisions ouvertes : aucune.
Deux champs sont non négociables. La provenance, approuvée, observée dans les données ou mentionnée dans une discussion, ne s'écrase jamais dans un score de confiance unique : ce sont trois autorités différentes. La validité porte sur la période où la règle s'applique aux transactions, pas sur la date où le système l'a apprise. Une politique apprise en septembre peut gouverner les ventes depuis juillet, et une requête vérifiée l'an dernier peut légitimement encoder l'ancienne politique.
La sémantique temporelle mérite d'être explicite. Un client passe de SMB (janvier à juin) à Enterprise (juillet et après). Il achète 100 € en mars. Avec le segment courant, la commande historique migre vers Enterprise ; avec le segment à la date d'achat, elle reste SMB. Le contrat fixe l'attribution et la jointure doit l'implémenter, avec un prédicat h.valid_from <= o.ordered_at et o.ordered_at < h.valid_to. Sans ce prédicat, le modèle génère une jointure structurellement valide sur l'enregistrement courant, et réécrit silencieusement l'histoire. Notez que l'absence de chevauchement des fenêtres de validité est une propriété observée sur l'échantillon, pas garantie par le schéma : le contrat conserve cette distinction.
5 · Trois couches d'erreur, trois remèdes, trois coûts
- L1 · Implémentation. SQL valide, comptabilité fausse : fan-out, chemin de jointure, littéral, grain. 60 à 80 % des cas. Remède : schéma compact annoté de valeurs, arêtes avec cardinalité, contrats de grain, validateur structurel, fixtures. Coût : de la milliseconde à la seconde.
- L2 · Interprétation. Lecture plausible mais pas celle voulue, alors que l'évidence existe. 15 à 30 % des cas, les vingt points de BIRD. Remède : récupération d'évidence discriminante. Coût : 100 ms à 2 s.
- L3 · Inidentifiable. Deux interprétations donnent deux réponses et rien dans E ne tranche. 2 à 5 % des questions, mais les pires erreurs. Remède : une question précise, ou une réponse double qualifiée. Coût : un tour de dialogue.
C'est l'impasse du débat couche sémantique contre couche de contexte contre plus gros modèle : chacun défend une seule couche. La discipline qui en découle est simple : ne jamais payer le prix de la couche 3 pour un problème de couche 1, et ne jamais faire passer un problème de couche 3 pour de la couche 1. Un fan-out ne mérite pas une question à l'utilisateur ; une politique brut/net ne se résout pas avec trois sondes de jointure.
6 · Le désaccord est un signal gratuit, et nous le jetions
Prenons « chiffre d'affaires par segment client ». Avant toute ligne de SQL, le système doit résoudre trois décisions indépendantes : le revenu est brut ou net de remboursements ; la population inclut ou exclut les comptes internes ; le segment est celui d'aujourd'hui ou celui de la date de commande. Soit jusqu'à huit interprétations, dont trois typiquement plausibles.
Notre pipeline génère déjà trois candidats SQL par question, en divide-and-conquer, plan de requête et synthèse en ligne, à la manière de CHASE-SQL. Quand deux candidats sont équivalents après normalisation, on parle de consensus ; quand ils divergent, nous baissions la confiance, point. C'était gaspiller le signal le plus riche du système. Trois candidats qui divergent sont souvent trois hypothèses distinctes, et la forme de leur désaccord dicte l'action suivante. Nous comparons désormais les plans, pas les textes : ensemble de tables, chemin de jointure, prédicats de population, base temporelle, grain d'agrégation, expression de mesure. Un désaccord structurel, même sémantique de mesure mais chemin ou grain différents, se tranche dans la base par une sonde bornée ou dix lignes synthétiques. Un désaccord politique, brut contre net, attribution courante contre historique, population, ne se tranche pas dans la base : il faut une définition approuvée, ou une question.
7 · Récupérer pour discriminer, pas pour ressembler
La récupération classique maximise la similarité entre la question et le document. Avec des hypothèses explicites, l'objectif change : on cherche le document qui réduit le plus M, pondéré par l'autorité de sa source. Notre score compte, pour chaque paire d'hypothèses survivantes, si le document en invalide exactement une, avec un poids d'autorité (approuvée, puis observée, puis discussion) et un poids de validité nul hors de la fenêtre. Un document de score zéro n'entre jamais dans le prompt, quelle que soit sa similarité.
Exemple. Une note finance approuvée, « le reporting revenu est toujours net des remboursements », invalide l'hypothèse brute et pas les deux autres : score élevé. Une spécification approuvée, « le reporting historique conserve le segment à la date de transaction », invalide l'hypothèse du segment courant. Il reste une seule interprétation : la question est identifiable, on exécute. Cent messages sur « le revenu » qui n'invalident rien : score zéro, prompt inchangé, M inchangé. C'est la différence entre accumuler du contexte et acquérir de l'évidence.
8 · Choisir l'action suivante par sa valeur, pas par réflexe
Pour chaque action possible, consulter une métadonnée, profiler une colonne, échantillonner, lire une définition approuvée, exécuter une fixture adverse ou poser une question, nous évaluons la réduction attendue de perte de décision divisée par le coût total : latence, calcul et coût humain. En prototype, nous approximons le numérateur par le désaccord entre candidats survivants que l'action est susceptible de réduire, et nous ne traitons jamais une probabilité verbalisée par le LLM comme calibrée. Cette fonction empêche un agent puissant d'explorer toute l'entreprise avant de répondre à une question simple : pour une métrique gouvernée, la meilleure action est de répondre ; pour une ambiguïté politique sans définition, c'est de demander, avec un coût humain assumé, parce que toute autre action a une valeur nulle. Une limite connue : l'accord entre candidats peut refléter une erreur partagée, d'où les fixtures adverses, exécutées même en cas de consensus sur les questions multi-tables enfants.
9 · Une échelle à quatre barreaux, pas un cerveau d'entreprise
- E0, voie rapide, moins d'une seconde. Question vérifiée ou mesure gouvernée avec une seule interprétation possible : réponse déterministe. La couche sémantique n'est pas obsolète, c'est le chemin sûr pour tout ce qu'elle couvre, et quand elle ne couvre pas, elle refuse au lieu d'inventer.
- E1, générer et vérifier, plus 150 ms. Trois candidats, un pack de schéma budgété en tokens, fini les 37 500 tokens envoyés à un modèle qui en accepte 32 768, et le validateur structurel déterministe : fan-out, pré-agrégation, SUM(DISTINCT), population, attribution temporelle. Accord et validation, on exécute. 70 à 80 % du trafic sort ici.
- E2, discriminer, plus 2 s en médiane. Désaccord ou échec : classification de l'axe. Structurel, une sonde ou une fixture. Politique, récupération par score de discrimination.
- E3, clarifier, plus un tour. Axe politique sans évidence approuvée : une seule question, deux options concrètes, avec la conséquence chiffrée quand elle est disponible. En messagerie, on attend ; sur un rapport planifié, on livre les deux valeurs étiquetées. La décision est persistée avec provenance et validité : la prochaine fois, la question sort en E0.
Le budget sous dix secondes survit parce que E2 et E3 ne sont atteints que par une minorité de questions. C'est ce qui rend la vérification systématique abordable : on garde la machinerie, on la déclenche sur le désaccord.
10 · Mémoriser les échecs : le contre-exemple comme documentation native
Une requête réussie enregistre ce qui a marché une fois. Un contre-exemple enregistre quand une interprétation tentante casse, sous une forme exécutable : pattern rejeté, agrégation après jointure de deux relations 1:n ; fixture, une commande de 100, deux lignes de 60 et 40, deux remboursements de 10 et 20 ; attendu 70 ; obtenu par la version fautive 340 ; propriété violée, une seule ligne par commande avant agrégation ; réparation, agréger les mesures enfants au grain parent avant de combiner. Dix lignes communiquent une distinction de calcul plus précisément qu'un paragraphe d'avertissement, et la machine peut les exécuter.
Nous livrons une bibliothèque générique, fan-out, rustine DISTINCT, attribut historique ou courant, fuite de population, brut/net, calendaire ou fiscal, jointure sur clé nulle, jour de frontière de fuseau, et chaque tenant enrichit la sienne. Un sélecteur simple choisit la fixture qui sépare le plus de paires de candidats survivants. Sur nos six implémentations de test, quatre fixtures ont suffi à éliminer les cinq fautives : deux remboursements, une seconde commande de même montant, un changement de segment postérieur, une commande interne. Ce que cela ne démontre pas : que le système découvre le contrat seul. Le candidat brut n'est faux que parce que le contrat dit net. Les tests vérifient l'implémentation contre une signification, ils ne décident pas de la signification.
11 · Entraîner le modèle à distinguer : invariants et récompense multi-fixtures
Notre fine-tuning GRPO/GSPO de Qwen3.6-35B-MoE suivait la recette Arctic-Text2SQL-R1 : récompense de 1 si le SQL s'exécute et renvoie le bon résultat sur une base. Cette récompense est aveugle à tout ce qui donne le bon chiffre sur les lignes d'aujourd'hui et le mauvais demain, c'est-à-dire à toute la couche 1. Nous générons désormais des familles de fixtures en trois classes : préservantes, scinder une ligne de 100 en 60 plus 40, scinder un remboursement de 30 en 10 plus 20, réordonner, renommer de façon cohérente, avec la contrainte que le résultat ne bouge pas ; modifiantes, passer une relation de 1:1 à 1:n, déplacer un événement au-delà d'une frontière d'historique, changer la population ou l'attribution, avec la contrainte que le résultat bouge dans la direction prédite par le contrat ; irréductiblement ambiguës, retirer l'énoncé brut/net en laissant les deux lectures exécutables, la sortie attendue étant une clarification ou deux réponses qualifiées, jamais un SQL unique.
La récompense devient : 1,0 si le SQL s'exécute et reste correct sur toutes les fixtures de la famille, au moins trois ; 0,4 s'il n'est correct que sur la fixture de base ; 0,1 s'il s'exécute avec une syntaxe valide et un résultat faux ; 0 sinon. Sur les prompts ambigus, 1,0 pour une clarification bien formée, 0 pour un SQL unique confiant, avec 10 à 15 % de prompts ambigus dans le mix. Le modèle apprend une capacité à trois faces : préserver ce qui doit rester invariant, changer ce que la spécification impose, signaler ce que l'évidence ne peut pas trancher. Notre hypothèse, à tester sur des schémas jamais vus : cela transfère mieux entre organisations que la mémorisation de milliers de templates réussis.
12 · La confiance vient des alternatives éliminées
Aujourd'hui, un modèle est confiant parce qu'il a produit deux fois la même chose. Pour l'analytique d'entreprise, une notion plus défendable : la confiance croît à mesure que la part d'interprétations restantes diminue par rapport aux interprétations initiales. Ce n'est pas une probabilité calibrée, c'est un principe. La confiance doit monter parce que des interprétations ont été réfutées par l'évidence, pas parce que l'échantillonnage a convergé. Et chaque réponse porte sa trace : barreau de sortie, axe de désaccord, identifiants d'évidence, sondes, clarification. Si le système dit 170 €, il doit pouvoir dire que ce chiffre dépend d'un revenu net, d'une population hors comptes internes et d'un segment à la date de commande, et que changer l'une de ces règles peut changer le résultat.
« Le test le plus dur pour un agent data n'est pas « génère-t-il le bon SQL ? ». C'est : sait-il ce qui devrait changer dans le monde pour que sa réponse cesse d'être vraie ? »
13 · Ce qui nous ferait changer d'avis
Une architecture non réfutable est un pitch. Nous mesurons quatre systèmes à modèle et accès identiques : S0, la base actuelle ; S1, plus la récupération de contexte ; S2, plus les contrats et le validateur structurel ; S3, plus les sondes discriminantes, les contre-exemples et le clarificateur. La métrique principale n'est pas la précision mais le taux de réponses fausses avec assurance, toujours affiché à côté de la couverture : un agent qui refuse tout ne doit jamais paraître meilleur.
- Au moins 70 % des questions sortent en E1 avec moins de 150 ms de surcoût.
- Le validateur seul divise par deux les faux chiffres silencieux sur les questions multi-tables enfants, à couverture égale.
- La classification d'axe route au moins 85 % des cas L2 sans question, et au moins 85 % des cas L3 en une seule question.
- La récompense multi-fixtures bat l'exécution unique d'au moins 5 points sur un jeu de test tenu à l'écart. Si la deuxième ou la quatrième affirmation échoue, nous réduisons le validateur au fan-out et à la population, et nous gardons le clarificateur seul.
Conclusion
Modus a raison sur le diagnostic : les schémas et les formules omettent des preuves nécessaires. Modus a tort sur l'implication : collecter ce qui manque ne suffit pas, parce qu'une partie de ce qui manque n'existe nulle part, et parce que le reste doit devenir un calcul vérifiable avant d'être utile. La progression n'est donc pas schéma plus sémantique plus contexte vers le LLM, mais évidence, contraintes, contre-exemples et incertitude explicite.
Un bon agent data fait trois choses distinctes : trouver l'évidence, établir ce qu'elle prouve, et vérifier que son calcul suit ce qui est prouvé. Nous ne demandons plus au modèle de décrire ce que la base veut dire. Nous lui demandons de distinguer les significations concurrentes par leurs conséquences exécutables, et de montrer quand aucune preuve disponible ne permet de les distinguer. Comprendre, pour une machine, ce n'est pas savoir. C'est distinguer.
La spécification complète, Meaning Resolution Loop v1 (contrats de mesure, validateur structurel, analyseur de désaccord, récupération discriminante, mémoire de contre-exemples, récompense multi-fixtures), est disponible sur demande. Résultats de l'évaluation à quatre systèmes au T4 2026. Duke Analytics · Let's make data swing.

