L’assistant sur les données
On pose sa question en français. Le modèle écrit la requête, la base fournit les chiffres, et la réponse cite ses sources.
En bref
L’assistant répond en français à des questions sur les données de sortants.fr. Le modèle écrit la requête et la base fournit les chiffres : un métier à qui l’on propose un assistant demande d’abord d’où sort le chiffre. Il est monté comme chez un client, avec un périmètre de données limité à ce qu’il a le droit de voir, des réponses vérifiables une par une, et des questions de référence qui mesurent la qualité à chaque modification.
- Nature
- Projet personnel
- État
- En service sur mon poste depuis septembre 2026
- Rôle
- Conception, code, évaluation
- Pile
- Python, DuckDB, Ollama, Qwen2.5-Coder 7B quantifié, RTX 4060 de 8 Go
- Données
- Celles de sortants.fr, lues en place. Aucune donnée envoyée à un service extérieur
Mots-clésLLM · IA générative · inférence locale · Ollama · Qwen2.5-Coder · quantification Q4_K_M · text-to-SQL · DuckDB · Python · prompt engineering · injection de prompt · OWASP LLM Top 10 · garde-fous · évaluation · jeu de test · traçabilité des sources
Une question, une réponse
QUESTION
Quel est le taux d'acces du master MIAGE
de Bordeaux en 2025 ?
REQUÊTE
SELECT f.mention, f.etablissement,
a.taux_acces
FROM v_formation f
JOIN v_admission a USING (ifc)
WHERE f.mention ILIKE '%MIAGE%'
AND f.academie ILIKE '%Bordeaux%'
AND a.session = '2025'
RÉSULTAT
etablissement taux_acces
Université de Bordeaux 30.09
BASE DE CALCUL ET SOURCE
taux_acces
rang_dernier_appele_pp / n_can_pp
MonMaster, millésime 2026_S1
Licence Ouverte 2.0
MESURE
premier jeton 481 ms
débit 51.7 jetons/s
total 1.72 s
SQL exécuté 3 msComment ça marche
- L’utilisateurUne question en français
- Le modèle, sur la machineÉcrire la requête SQLle modèle traduit la question, et s’arrête là
- La porte et la basegardeLa porterelit la requête avec l’analyseur de la base, onze règlesRefusée : la requête s’arrête ici.
- La porte et la basegardeLa base, privée d’accès au disqueexécute la requête et fournit les chiffres
- L’utilisateurLa réponsela requête, le résultat, la base de calcul et la source de chaque colonne
Le modèle écrit la requête et s’arrête là. Lui donner les lignes à résumer rouvrirait la porte par laquelle un chiffre inventé entre.
Ce que j’ai décidé
La défense se place après le modèle
- Retenu
- Traiter la requête produite comme une chaîne fournie par un tiers non fiable.
- Écarté
- Écrire dans le prompt que le modèle doit ignorer les consignes de l’utilisateur.
Une question hostile retourne précisément le prompt. Une consigne écrite dedans ne protège de rien.
L’analyseur de la base, plutôt qu’une liste de mots interdits
- Retenu
- Onze règles appliquées à l’arbre syntaxique que la base produit elle-même.
- Écarté
- Chercher des mots comme DROP ou un point-virgule dans le texte.
Une liste de mots perd contre la tolérance du moteur qu’elle prétend protéger. Un cas de test vérifie d’ailleurs qu’un point-virgule placé dans un commentaire passe, puisqu’il est inoffensif.
Rendre la requête fausse impossible à écrire
- Retenu
- Des vues qui figent les filtres de dénombrement.
- Écarté
- Exposer la table source et compter sur le modèle pour se souvenir de la règle.
La table porte une ligne de total en plus des lignes de détail. Une somme naïve produit une phrase juste et un chiffre faux, sans aucun signal d’erreur.
Comment la qualité se mesure
Quinze questions de référence servent à régler le prompt. Huit autres sont tenues à l’écart et ne servent qu’à mesurer : un écart qui se creuse entre les deux jeux signale un réglage trop ajusté à ses propres exemples.
- Questions de réglage
- 12 justes sur 15
- Questions témoins
- 5 justes sur 8
- Questions hostiles
- 18 essayées, aucune donnée sortie du périmètre
- Temps de réponse
- 236 ms de médiane jusqu’au premier jeton, 48 jetons par seconde
Ce qui a raté
- Le test d’injection se trompait de méthode. Il cherchait des mots interdits dans le texte, et signalait comme faille une requête saine où le mot figurait dans une chaîne. Un contrôle se fait sur ce que la requête désigne.
- Le débit était mesuré dix fois trop bas. 4,8 jetons par seconde affichés pour 47 réels : la mesure incluait la fermeture du flux.
- Quatre consignes en prose ont échoué. Elles laissaient le score à 10 sur 15 en déplaçant les échecs. Cinq exemples dans le prompt l’ont porté à 12.
- La documentation du schéma trompait le modèle sur la casse de quatre colonnes, et ses égalités ne rendaient rien.
Outils
- Python 3.12, DuckDB en mémoire, appels HTTP directs au moteur.
- Ollama, modèle ouvert quantifié, température nulle et graine fixe pour que deux passages soient comparables.
- Une abstraction de moteurs compatible OpenAI, prête pour vLLM ou une API hébergée.
Ce que ça prouve
Que je sais monter un assistant sur des données d’entreprise comme on le ferait chez un client : un périmètre borné, des réponses vérifiables, une défense placée là où elle tient, et une mesure de qualité à chaque modification.