Pendant des semaines, j'ai voulu donner à La Commune 2.0 une chose toute simple en apparence : la capacité de citer le droit français de façon fiable, vérifiable, et de relier ces citations à la pile argumentative de Tcollectif. Pas un copier-coller approximatif d'un article : une référence canonique, traçable, interrogeable.
Le résultat ? Un corpus complet de tout LEGI ingéré depuis la DILA, rangé en douze grandes matières, avec pour chacune ses mots-clés discriminants. Une base interrogeable depuis Discord via une commande slash, et bientôt connectée à /fact_check, /reasoning_check, à des outils d'aide juridique citoyenne, voire à un CSA populaire qui notifierait les manquements à la loi des chaînes de télévision.
Ce qui suit est un récit de chantier : ce que j'ai essayé, ce qui a foiré, comment Claude m'a aidé à m'en sortir, et où on en est aujourd'hui.
La République publie son droit consolidé sur Légifrance, mais Légifrance n'est pas conçu pour qu'un développeur citoyen interroge le corpus en masse. La DILA (Direction de l'information légale et administrative) met heureusement à disposition, en open data, les dumps complets de LEGI au format XML. Un fichier de 1,1 Go compressé contient l'intégralité du droit français consolidé en vigueur (et l'historique des abrogés).
Le principe que j'ai poursuivi est en trois temps :
Une fois ces trois étapes franchies, on dispose d'un classificateur sémantique automatique spécialisé en droit français. Branché sur la pile SBDFMS/AXPA/S, il devient un module d'autorité juridique pour les arguments construits dans Tcollectif.
La DILA publie sur echanges.dila.gouv.fr/OPENDATA/LEGI/ un fichier Freemium_legi_global_YYYYMMDD-HHMMSS.tar.gz qui contient tout. Le dernier que j'ai pris : 13 juillet 2025, 1,1 Go compressé, environ 2,55 millions de fichiers XML à l'intérieur.
Chaque article du dump est un petit fichier XML qui suit le format « cetat » de la DILA. Pour chacun, il faut extraire l'identifiant LEGI, le code parent, le numéro d'article, l'état (en vigueur, abrogé…), la date de version, et le contenu. J'ai écrit un parseur tolérant qui ouvre le tarball en streaming (pas besoin de décompresser les 5 Go côté disque), ne consomme qu'une cinquantaine de Mo de RAM, et nettoie le contenu HTML inline (les codes contiennent du <p>, du <br>, de l'<i>… on jette tout ça).
Une seule base : droit_fr.db. Deux tables : texte pour les articles de codes/lois, decision pour la jurisprudence. Une troisième virtuelle : recherche, l'index FTS5 plein-texte. Tokenizer accent-insensible, recherche par préfixe, snippets HTML pour surligner les correspondances.
C'est l'étape la plus longue. Sur 140 000 textes, on ne peut pas tout classer à la main. J'ai donc adopté une approche hybride.
qwen2.5-coder:7b sur Ollama, sur ma machine, gratuit) qui le range dans une des douze catégories. Pour 1 393 textes, j'ai mis environ une heure et demie d'inférence en local.Pour chaque catégorie, je ne veux pas juste « les mots les plus fréquents » (ce serait le, la, des, code, article…). Je veux les mots distinctifs : ceux qui apparaissent beaucoup ici mais peu ailleurs. C'est exactement ce que calcule la statistique TF-IDF classique.
J'ai ajouté deux raffinements importants :
Aucun pipeline réel ne marche du premier coup. Voici la liste des murs que j'ai pris en pleine face, et comment je m'en suis sorti — souvent grâce à une intuition de Claude (l'assistant IA qui m'a accompagné).
J'ai commencé par utiliser INSERT OR REPLACE en SQLite pour mettre à jour les articles. Ça marche très bien… sauf que SQLite ne déclenche pas les triggers AFTER DELETE dans ce cas. Or mon index plein-texte FTS5 est maintenu par triggers : il accumulait silencieusement des doublons. Après quelques heures de fonctionnement, deux entrées de recherche pour chaque article réel.
« Sur SQLite, INSERT OR REPLACE supprime la ligne en conflit mais sans firer le trigger DELETE. Pour les tables FTS5 ou auxiliaires maintenues par trigger, il faut toujours faire un DELETE explicite suivi d'un INSERT plain. » — Documenté désormais dans ma mémoire de projet pour ne plus me faire avoir.
Pour pouvoir lire les résultats à l'œil, j'ai voulu rendre des tableaux Markdown « jolis » : tronqués à 80 caractères. Erreur dramatique. J'ai mis à la main 225 textes dans douze catégories en lisant leur titre tronqué. Une part importante de mes classements étaient pris à l'aveugle, sur des titres dont le mot discriminant était après la coupe.
On ne tronque jamais une donnée affichée pour la prise de décision humaine. Le confort visuel d'un tableau ne justifie pas la perte d'information. Si c'est trop large, on scrolle, on enroule la cellule, on rapatrie dans VSCode — mais on ne jette pas.
Première extraction de mots-clés sur la catégorie 01 (Constitution & fondamentaux). Surprise : les mots-clés top sont affluents, cyprinidés, amont pont, salmonidés dominants, cours eau… Aucun mot constitutionnel.
Diagnostic : ma catégorie 01 ne contenait que 9 textes, dont un — un décret de 1958 sur le classement des cours d'eau en deux catégories piscicoles — pesait à lui seul 99 articles, soit 18 % du corpus de la catégorie. Tous ces articles parlaient évidemment de cours d'eau. Avec une pondération aussi déséquilibrée, le TF-IDF n'avait aucune chance.
J'ai ajouté un filtre simple mais radical : un mot n'est gardé que s'il apparaît dans au moins deux textes distincts de la catégorie. Un mot venant d'un seul texte mal classé est éliminé. « Affluents » est désormais filtré, et les vrais mots constitutionnels — république, souveraineté, assemblée nationale, Sénat, président, électeurs, scrutin, Cour de cassation — remontent au top.
Le VPS hébergeant le bot Tcollectif a 3,6 Go de RAM. Quand j'ai ajouté à mon extracteur de mots-clés le suivi de « dans combien de textes distincts ce mot apparaît-il ? », la consommation mémoire a explosé. Pour chaque mot distinct (et il y en a des dizaines de milliers par catégorie), je stockais un ensemble Python de tous les identifiants de textes le contenant. Multiplié par les bigrammes (deux mots consécutifs), on monte vite à 3 Go de RAM. Le système est passé en swap, puis le kernel a tué le process. Deux heures de calcul perdues.
« Tu veux compter à la fois la fréquence d'un mot ET dans combien de textes il apparaît, et tu veux le faire pour tous les mots distincts. C'est l'origine de ton OOM : tu suis le détail pour des mots qui ne seront jamais retenus de toute façon. Fais-le en deux passes : »
Deux passes au lieu d'une, soit 45 minutes au lieu de 22 — mais qui tiennent. Et qui produisent enfin un résultat propre.
« L'environnement », « d'application », « qu'il »… Selon que la source utilise une apostrophe droite (') ou courbe typographique (’), ma tokenisation produisait soit deux jetons propres (« l » éliminé comme trop court, « environnement » retenu), soit un jeton agglutiné absurde (« lenvironnement »). Solution : avant tout traitement, j'élide systématiquement le préfixe court suivi d'une apostrophe (droite ou courbe), en le remplaçant par un espace. Maintenant « d'application » et « l'environnement » produisent les bons jetons quel que soit le caractère utilisé.
Subset 11 (Fiscalité), top 10 mots-clés discriminants — c'est ce que ma machine a sorti toute seule, sans intervention humaine :
Subset 06 (Libertés publiques & ordre public), top 10 :
C'est la signature juridique en gras de chaque grand domaine. Pour la fiscalité, je vois immédiatement les concepts centraux : impôts, taxes, code des impôts, contribuables, TVA, lois de finances. Pour les libertés publiques : sécurité, défense, gendarmerie, police, armes. Aucun mot parasite dans le top, et chaque mot couvre réellement la majorité des textes de la catégorie (colonne LtxCov).
La même chose pour les douze catégories. Cette signature lexicale sera ensuite enrichie : synonymes (« rassemblement », « cortège » pour « manifestation »), antonymes (« interdiction » pour « autorisation »), tout en filtrant les variantes morphologiques. C'est ce qui me permettra ensuite de classer automatiquement n'importe quel nouveau texte : un courrier reçu de la préfecture, un article de presse, une transcription de débat.
/ref droit français : le documentaliste de Tcollectif propose en direct la citation pertinente d'un article LEGI dans le débat Discord. ELI canonique, traçable, vérifiable./fact_check et /reasoning_check : une affirmation factuelle peut maintenant être vérifiée contre le texte de loi explicite. Un schéma argumentatif (Walton) qui s'appuie sur « la loi dit X » peut être validé en interrogeant droit_fr automatiquement.Tout cela suppose un corpus juridique vivant, accessible programmatiquement, enrichi sémantiquement. C'est ce qu'on vient de construire.
Pour qui voudrait reproduire la démarche sur un autre corpus, voici les briques :
| Action | Commande |
|---|---|
| Initialiser la base SQLite | python -m droit_fr.init_db --db /chemin/droit_fr.db |
| Ingestion complète du dump LEGI | python -m droit_fr.ingest_legi --db ... --dump-url auto |
| Inventaire complet des LEGITEXT du dump | python -m droit_fr.list_legi_codes --dump-path ... |
| Classification automatique par Ollama local | python -m droit_fr.suggest_anchor_from_titles --model qwen2.5-coder:7b --threshold 30 |
| Revue humaine des cas incertains | python -m droit_fr.review_classifications --include-medium |
| Extraction TF-IDF des mots-clés (2 passes) | python -m droit_fr.extract_keywords --top 300 --min-legitext-coverage 2 |
| Sélection humaine interactive | python -m droit_fr.pick_keywords --target 300 --subset 06_libertes_publiques_ordre_public |
| Enrichissement synonymes / antonymes | python -m droit_fr.enrich_keywords_batch --batch-size 20 |
| Dédoublonnage inter-catégorie | python -m droit_fr.dedup_keywords --input ... --output ... |
| Recherche depuis l'API publique | GET /droit-fr/search?q=manifestation&theme=ordre-public |
Tout est en Python pur, sans framework lourd. Ollama local pour l'IA, SQLite pour la base, FastAPI pour exposer l'API à Tcollectif. Le tout tient sur un VPS modeste à 3,6 Go de RAM, à condition de respecter les deux passes pour l'extraction.
À suivre…
Le branchement Discord du /ref droit français arrive dans les prochains jours. La connexion avec /fact_check suit. Et puis viendra le moment où nous tournerons l'outil vers les plateaux télé, les courriers reçus, les contrats qu'on nous fait signer. La République s'écrit en grand — il est temps qu'on la lise tous ensemble.
Un prochain billet racontera la suite. Restez branchés.
La Commune 2.0 : publication, archivage et documentation des travaux issus de Discord.
Contact technique : yannick.mandaba@geomanet.com · Mentions légales · Confidentialité & données
Œuvre propriétaire — Tous droits réservés. Copyright © 2024-2026 Yannick Mandaba-Masseron — SafeZone FPV™. Aucun usage, reproduction, distribution, modification, traduction, ingénierie inverse, exécution publique, intégration ou travail dérivé sans accord écrit préalable du titulaire.
Les termes HUSCI, Action RIC, La Commune Légiférante, GeoMANET, La Commune 2.0, M.S.Y. (Manet Secure You), ainsi que les logos, sigles, emblèmes, chartes graphiques, signatures éditoriales, intitulés de programmes et labels organisationnels, sont des désignations propriétaires réservées de SafeZone FPV™.
Demandes (collaboration, licences, signalements) : ip@safezone-fpv.com.