Sommaire de page

Droit français vivant : un corpus juridique citoyen

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.

~643 000articles en vigueur dans LEGI
140 040textes (LEGITEXT) distincts
1 393textes classés à la main + IA
12grandes matières

Le grand principe

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 :

  1. Ingérer tout le corpus. Ne rien filtrer au stockage. La DILA me donne 643 000 articles en vigueur, je les prends tous, je les range dans une base SQLite locale avec un index plein-texte FTS5.
  2. Le classer en grandes matières. Pour pouvoir interroger « cherche dans le pénal » ou « cherche dans les libertés publiques », je dois rattacher chaque texte à une catégorie. Douze catégories, calquées sur l'organisation officielle de Légifrance : Constitution, droit privé, commercial, travail-sécu, pénal, libertés publiques, administration territoriale, contentieux administratif, santé-éducation-recherche-sport, économie sectorielle et environnement, fiscalité, régalien spécialisé.
  3. Extraire les mots-clés discriminants de chaque catégorie. Un mot qui caractérise vraiment la fiscalité (« impôt », « contribuable », « CGI »). Un autre qui caractérise les libertés publiques (« manifestation », « presse », « défense »). Ces mots-clés serviront à classer automatiquement de nouveaux textes, à comprendre la matière dominante d'un courrier reçu, d'un contrat, d'un extrait de débat télévisé.

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.

Le chantier, étape par étape

1. Le dump de la DILA

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.

# Sur le VPS hébergeant le bot Tcollectif $ wget https://echanges.dila.gouv.fr/OPENDATA/LEGI/Freemium_legi_global_20250713-140000.tar.gz --2026-05-11 03:14:22-- https://echanges.dila.gouv.fr/... 100%[==========================================>] 1.1G 12.4MB/s in 1m 30s 2026-05-11 03:15:52 (12.4 MB/s) - sauvegardé [1228291245/1228291245]

2. Le parseur XML cetat

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).

3. Le schéma SQLite + FTS5

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.

4. La classification en 12 catégories

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.

  • Une ancre manuelle : pour les ~130 codes/lois principaux (Code civil, Code pénal, CGCT, Code général des impôts, Loi 1881 sur la presse, Code de la sécurité intérieure, etc.), je mappe à la main vers la bonne catégorie.
  • Une classification IA pour le reste : chaque texte de plus de 30 articles est soumis à un modèle local (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.
  • Une revue humaine sur les cas incertains : pour les 67 textes en confidence « low » ou rangés en « divers », j'ai relu et tranché manuellement (j'ai mis cinq minutes pour les soixante-sept).
# Le tri rapide humain en action ====================================================================== [ 2/67] LEGITEXT : LEGITEXT000006071318 Articles courants : 1,895 Classif AI actuelle : subset 10 (confidence: medium) Raison AI : Le code du sport pourrait faire partie de la catégorie environnement, mais il est également lié à l'éducation et au divertissement. La catégorie 10 est la plus appropriée. TITRE : Code du sport Choix > 9 -> Reclassé en 09

5. L'extraction des mots-clés discriminants

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 :

  • Pondération des titres : le titre du code (par exemple « Code de la sécurité intérieure ») compte 200 fois plus qu'un mot du contenu d'un article, parce que le titre c'est dense en signal sémantique. Le titre de section (« Manifestations sur la voie publique ») compte 20 fois plus.
  • Filtre anti-monoculture : si un mot n'apparaît que dans un seul texte de la catégorie, je le rejette. Il n'est pas représentatif. C'est ce qui m'a sauvé de m'imaginer que le subset « Constitution » tournait autour des « affluents » et des « cyprinidés » (lire plus bas : c'est une vraie anecdote).

Les galères, les erreurs, et les astuces de Claude

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é).

Galère n°1 : les triggers SQLite qui ne se déclenchent pas

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.

Astuce Claude

« 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.

Galère n°2 : les titres tronqués

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.

Règle d'or apprise dans la douleur

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.

Galère n°3 : le subset « Constitution » pollué par un décret sur les cours d'eau

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.

Le filtre anti-monoculture

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.

Galère n°4 : le Out of Memory qui tue tout

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.

# Le log de l'agonie [tar-heartbeat] t+1338s membres=5,100,067 (3812/s) xmls_yield=2,491,826 current=.../legi/global/eli/arrete/2022/3/8/EAEA2206565A/ jo/texte/versions.xml # puis... silence. Plus de heartbeat. ps -ef | grep extract retourne vide. → OOM kill confirmed via free -h : swap saturé, available 216 MB.

L'astuce 2-passes proposée par Claude

« 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 : »

  1. Une première passe légère qui ne compte que la fréquence des mots. RAM bornée à quelques centaines de Mo.
  2. Avec ce résultat, on identifie les 1 000 mots les plus prometteurs par catégorie (12 × 1 000 = 12 000 mots à suivre, pas 500 000).
  3. Une seconde passe qui re-parcourt le dump mais ne suit la couverture que pour ces 12 000 mots. RAM bornée à ~40 Mo.

Deux passes au lieu d'une, soit 45 minutes au lieu de 22 — mais qui tiennent. Et qui produisent enfin un résultat propre.

Galère n°5 : les apostrophes Unicode

« 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é.

Le résultat aujourd'hui

Subset 11 (Fiscalité), top 10 mots-clés discriminants — c'est ce que ma machine a sorti toute seule, sans intervention humaine :

# Score Freq DF LtxCov LtxN Terme 1 2.1046 5,606 2 65.66% 65 finances rectificative 2 1.8311 8,688 4 56.57% 56 décembre finances 3 1.7503 5,627 3 67.68% 67 rectificative 4 1.0304 5,789 6 73.74% 73 impôt 5 0.7192 8,592 9 81.82% 81 code impôts 6 0.6298 1,913 2 57.58% 57 finances décembre 7 0.6271 10,830 10 88.89% 88 impôts 8 0.5978 2,726 5 72.73% 72 imposition 9 0.4604 1,773 4 69.70% 69 impositions 10 0.4048 7,787 10 79.80% 79 taxe

Subset 06 (Libertés publiques & ordre public), top 10 :

# Score Freq DF LtxCov LtxN Terme 1 0.2707 11,437 11 67.01% 65 sécurité 2 0.2343 1,756 2 20.62% 20 corps officiers 3 0.2026 7,623 11 75.26% 73 défense 4 0.1910 3,262 8 36.08% 35 gendarmerie 5 0.1736 3,275 7 24.74% 24 police nationale 6 0.1641 1,619 6 37.11% 36 code défense 7 0.1639 1,864 5 25.77% 25 gendarmerie nationale 8 0.1514 1,290 4 27.84% 27 armée 9 0.1376 1,319 4 24.74% 24 ministère intérieur 10 0.1300 1,775 6 26.80% 26 armes

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.

Ce qu'on va pouvoir en faire

Court terme — branchement Tcollectif

  • /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.
  • Filtrage par projet : chaque tenant ou projet Discord peut activer un sous-ensemble du corpus (« manifestations & libertés publiques » pour un dossier de droit social, « administration territoriale » pour un dossier d'urbanisme). On charge moins, on cherche plus vite, l'IA voit moins de bruit.
  • Connexion avec /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.

Moyen terme — outils d'aide juridique citoyenne

  • Lecture de courriers administratifs : tu reçois un arrêté préfectoral, une mise en demeure, un avis d'imposition. Tu colles le texte. L'outil identifie la matière (fiscalité ? police administrative ? droit social ?), pointe les articles de loi pertinents, t'explique tes voies de recours.
  • Pré-montage de dossiers à destination d'avocats : avant même de prendre rendez-vous avec un avocat, tu arrives avec un dossier déjà structuré : les faits, les articles de loi qui s'appliquent, la jurisprudence pertinente, les pièces à fournir. Tu fais gagner deux heures à l'avocat (et tu paies moins).
  • Détection d'articles qui enfreignent la loi : un article de presse, un post de blog, une publication suspecte ? L'outil le passe contre le corpus pénal et presse, identifie les passages potentiellement problématiques (diffamation, provocation, atteinte à la vie privée…), produit un rapport.

Long terme — la République en regard

  • Calcul d'impact d'un programme politique : un parti propose une réforme. Combien d'articles de loi devraient être modifiés, abrogés, créés pour rendre la mesure applicable ? L'outil croise le programme avec le corpus, mesure l'écart, produit un chiffre : « cette mesure nécessite la modification de 47 articles répartis sur 6 codes ». La promesse cesse d'être abstraite.
  • Transcription d'émissions de télévision : les transcriptions audio s'analysent contre le corpus. Une affirmation contraire à la loi, un appel à la haine, une diffamation publique ? Le système le repère, génère un rapport, transmet à qui de droit (médiateur, signalement Pharos, etc.).
  • Le CSA populaire : une instance citoyenne indépendante qui agrège ces rapports, les rend publics, les notifie aux médias concernés. Pas pour censurer : pour informer le public et faire vivre la loi qui existe déjà. Les chaînes savent que leurs propos sont écoutés, vérifiés, et que les manquements seront documentés et rendus visibles. Pas de magistrature détournée : un système de signalement transparent et réversible.

Tout cela suppose un corpus juridique vivant, accessible programmatiquement, enrichi sémantiquement. C'est ce qu'on vient de construire.

Récap des commandes principales

Pour qui voudrait reproduire la démarche sur un autre corpus, voici les briques :

ActionCommande
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.

Footer

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.