Un wiki local qui coûte des tokens à chaque mise à jour ? C’est fini. Découvrez comment un compilateur Python transforme vos notes en un wiki structuré, sans appel à l’IA.

Un wiki personnel basé sur l’intelligence artificielle, c’est pratique. Mais quand on y réfléchit, c’est aussi un gouffre à tokens : à chaque nouvelle note ajoutée, l’IA relit tout, décide des changements et réécrit les pages. Résultat ? Des coûts qui explosent et des résultats qui varient à chaque exécution. Un développeur a trouvé mieux : un compilateur Python qui fait le travail en quelques secondes, sans appel à l’IA, sans dépendance externe, et avec des résultats toujours identiques.

LE PROBLÈME : LES WIKIS IA COÛTENT TROP CHER

Le principe est simple : on part d’un dossier de fichiers markdown mal formatés, on y ajoute quelques notes personnelles, et l’IA se charge de tout organiser. Sauf que… chaque fois qu’on ajoute une nouvelle note, l’IA relit l’ensemble du dossier, décide ce qui a changé, et réécrit les pages concernées. Ce n’est pas de la synthèse, c’est de l’organisation pure. Et ça, ça coûte cher en tokens.

Pour 5 000 fichiers, l’IA passe plus de temps à relire qu’à écrire. Résultat : des coûts qui s’accumulent et une latence qui explose.

Autre problème : l’indéterminisme. Un même dossier passé deux fois dans un wiki basé sur l’IA ne donne pas forcément les mêmes liens entre les pages. Rien n’a changé dans les fichiers, pourtant la structure diffère. Pour du code, c’est parfois amusant. Pour un wiki utilisé comme source de vérité, c’est un vrai souci.

LA SOLUTION : UN COMPILATEUR PYTHON, PAS UN AGENT IA

Le développeur a donc tout remplacé par un compilateur Python utilisant uniquement la bibliothèque standard. Pas de sentence-transformers, pas de base de données vectorielle, pas d’appel API. Juste du code qui analyse, structure et génère un wiki markdown parfaitement lié et vérifié.

Le pipeline se décompose en quatre étapes :

  • Un extracteur par expressions régulières
  • Un constructeur de graphe qui détecte les références croisées
  • Un réécrivain conscient des sections qui préserve les notes manuelles
  • Un linteur qui vérifie les liens et les pages orphelines

Chaque étape est testée séparément, et le tout est conçu pour être déterministe : deux exécutions identiques donnent toujours le même résultat.

ÉTAPE 1 : EXTRAIRE LES DONNÉES MÊME SI ELLES SONT MAL FORMATÉES

Les fichiers sources sont un vrai capharnaüm. Certains utilisent des en-têtes avec # Titre, d’autres des lignes en majuscules, et certains n’ont aucun en-tête. Les métadonnées comme « created: » ou « aliases: » peuvent être manquantes ou cachées n’importe où dans le fichier.

Le compilateur ne s’arrête pas pour si peu. Il cherche d’abord un en-tête avec #. Si rien n’est trouvé, il cherche une ligne en majuscules. Si toujours rien, il utilise le nom du fichier. Pour les métadonnées, il scanne l’ensemble du fichier au lieu d’exiger une position précise.

Résultat : le pipeline ne plante pas sur un fichier mal formaté. Il s’adapte et nettoie l’essentiel du désordre. C’est la partie la plus ennuyeuse du projet, mais c’est aussi celle qui a demandé le plus de travail.

ÉTAPE 2 : CONSTRUIRE UN GRAPHE DE RÉFÉRENCES SANS EXPLOSER LE TEMPS

Cette étape gère les liens entre les notes. Problème : la première version était trop lente.

L’approche initiale consistait à lancer une expression régulière distincte pour chaque entité contre chaque autre fichier. Avec 100 fichiers, ça marchait. Avec 1 000 fichiers, ça prenait 4,4 secondes. Avec 5 000 fichiers, ça montait à 107 secondes. L’idée que « pas d’appel API » rimait avec « rapide » était fausse : l’algorithme compte plus que l’absence de réseau.

La solution ? Un indexeur de mots qui tokenise chaque fichier une seule fois. Au lieu de tester chaque nom d’entité à chaque position du texte, il ne vérifie que les candidats qui commencent par le mot actuel. Résultat : le temps passe de 4,4 secondes à moins de 50 millisecondes pour 1 000 fichiers, et de 107 secondes à moins d’une seconde pour 5 000 fichiers.

L’approche initiale était en O(n²). La nouvelle version passe en temps quasi linéaire. Une différence de 107 secondes à moins d’une seconde.

Une tentative intermédiaire a combiné tous les noms d’entités en un seul motif alternatif (Name1|Name2|Name3.). Résultat : le moteur regex devait toujours vérifier la liste complète à chaque position. Avec 5 000 entités, c’était toujours un comportement quadratique déguisé en linéaire. Seul l’indexeur de mots a vraiment résolu le problème.

ÉTAPE 3 : RÉÉCRIRE LES SECTIONS SANS EFFACER LES NOTES MANUELLES

Cette étape écrit le markdown final. Elle ne parse pas d’arbre syntaxique abstrait, elle ne parcourt pas de graphe. Elle fait simplement des remplacements de chaînes ciblés entre des balises ## spécifiques. D’où son nom : réécrivain conscient des sections.

Le but n’est pas de tout écraser et de repartir de zéro. Il faut préserver les notes personnelles ajoutées dans la section Notes de chaque page. La logique est simple : avant d’écrire une page, le compilateur vérifie si le fichier existe déjà. Si oui, il récupère tout ce qui se trouve sous l’en-tête ## Notes. Ensuite, les sections gérées par le compilateur — Métadonnées, Liens associés, Référencé par, et Corps — sont effacées et régénérées à partir de la source, tandis que le contenu des Notes est réécrit tel quel.

Pour s’assurer que ça fonctionne, le développeur a ajouté manuellement une note à une page générée, modifié la source, et relancé le compilateur. La note est restée en place tandis que les autres sections étaient mises à jour. Testé à nouveau quelques jours plus tard sur Windows, le résultat était identique.

EXEMPLE CONCRET : UNE NOTE PASSE EN WIKI STRUCTURÉ

Prenons un exemple réel avec la note attention_mechanism.txt :

RAW SOURCE (rawnotes/attentionmechanism.txt)
------------------------------------------------
ATTENTION MECHANISM
created: 2026-02-27

A common mistake is tuning Attention Mechanism without first
checking Learning Rate Schedule.

This section needs a cleaner example before it is considered final.

Le compilateur génère la page suivante :

COMPILED OUTPUT (compiledwiki/attentionmechanism.md)
------------------------------------------------
# Attention Mechanism

## Metadata
- created: 2026-02-27
- aliases: none
- source: rawnotes/attentionmechanism.txt

## Related
- [[Learning Rate Schedule]]

## Referenced By
- [[Gradient Descent]]

## Body
A common mistake is tuning Attention Mechanism without first
checking Learning Rate Schedule.

This section needs a cleaner example before it is considered final.

## Notes
(add your own notes here -- preserved on recompile)

Le lien vers Gradient Descent a été ajouté automatiquement, car la note source de ce dernier mentionne « Attention Mechanism » dans son corps. C’est tout le pipeline en action : entrée désordonnée, sortie structurée et liée, sans aucun lien manuel.

ÉTAPE 4 : LE LINTEUR QUI VÉRIFIE SES PROPRES ERREURS

Le linteur parcourt la sortie, signale les liens cassés et identifie les pages sans référence. Rien à voir avec un jugement de l’IA : c’est une vérification structurelle avec des règles fixes.

Première version du linteur : un bug majeur. Il comptait les liens entrants en scannant chaque [[lien]] dans le fichier. Problème : la section Référencé par contient aussi des [[liens]]. Ces liens indiquent les pages qui pointent vers le fichier courant, pas l’inverse. Le linteur comptait donc des liens sortants en trop, ce qui faussait complètement le décompte des pages orphelines.

Résultat ? Sur un corpus de 100 fichiers avec 13 pages orphelines confirmées, le linteur buggé affichait zéro. Il ne sous-comptait pas, il affirmait que le wiki était parfaitement lié alors qu’il ne l’était pas. Sans test croisé, le bug serait parti en production.

La correction a consisté à limiter le décompte à la section Liens associés uniquement. C’est le seul endroit où se trouvent les vrais liens sortants.

relatedtext = extract_section(text, "Related")
for match in LINKRE.finditer(relatedtext):
    targetslug = slugify(match.group(1))
    if targetslug in incomingcount:
        incomingcount[targetslug] += 1

Après cette modification, le décompte des pages orphelines du linteur correspondait exactement à celui du constructeur de graphe. Toujours. Quel que soit la taille du corpus. Un test de régression nommé d’après le bug a été ajouté pour éviter toute récidive.

17 TESTS POUR UNE MACHINE À LA FIABILITÉ MAXIMALE

Le projet inclut 17 tests, tous basés sur la bibliothèque standard unittest. Chaque étape a ses tests isolés, utilisant des objets Entity créés à la main plutôt que des générateurs synthétiques. L’objectif ? Si un test échoue, il pointe directement sur la cause.

testlinterdoesnotmiscountreferencedby . ok
testhumannotespreservedacross_recompile . ok
testrecompileisidempotentoncompilerowned_sections . ok
testdeterministicoutput . ok

Ran 17 tests in 0.020s
OK

Le test testlinterdoesnotmiscountreferencedby est le test de régression pour le bug décrit précédemment. Son nom est laid, mais c’est le plus important du fichier.

Les tests de pipeline complet capturent les problèmes d’intégration que les tests unitaires ne voient pas. Par exemple, le test d’idempotence recompile le même corpus deux fois et vérifie que la sortie est identique au byte près. Si le réécrivain avait introduit une non-déterminisme, comme un horodatage fantaisiste, le test l’aurait immédiatement détecté.

Mieux vaut 17 tests qui échouent chacun pour une raison précise qu’un seul test d’intégration qui plante et laisse le développeur chercher pendant des heures.

BENCHMARKS : WINDOWS VS LINUX, 5 000 FICHIERS EN 12 SECONDES

Le pipeline complet a été testé sur trois tailles de corpus, sur deux machines différentes (un conteneur Linux et un PC Windows 10), en utilisant la même graine pour garantir l’identité des données sources.

FULL PIPELINE TIME BY STAGE AT 5,000 FILES (12.44s total)
============================================================

extract  [==]                                    1.40s  (11%)
graph    [=]                                      0.63s  ( 5%)
rewrite  [=======]                                3.45s  (28%)
lint     [==============]                         6.97s  (56%)

============================================================
Le linteur est de loin l’étape la plus lente : à 5 000 fichiers, il prend plus de temps que les trois autres étapes réunies.

Ce résultat a surpris le développeur. Le linteur a la logique la plus simple du pipeline : il ouvre chaque fichier une fois pour scanner deux petites sections. Le goulot d’étranglement n’est pas le code, mais les entrées/sorties disque. Le linteur lit chaque fichier à chaque exécution, et Windows est significativement plus lent que Linux sur ce point. Windows Defender contribue probablement à ce ralentissement en vérifiant chaque fichier à l’ouverture, bien que cela n’ait pas été vérifié directement dans les logs.

Le constructeur de graphe n’a aucun accès disque, donc il scale le mieux, mais pas de manière parfaitement linéaire.

Passer de 100 à 1 000 fichiers (×10) prend 15,2 fois plus de temps. Passer de 1 000 à 5 000 fichiers (×5) prend 13,3 fois plus de temps.

C’est le coût mesurable de l’indexeur de mots qui doit vérifier plus de noms de candidats par token à mesure que la liste d’entités grandit. Cela signifie que le scaling n’est pas tout à fait linéaire.

Les comptes de pages orphelines (13, 133, 644) sont restés identiques sur chaque exécution, quel que soit le système d’exploitation. Ce n’est pas un hasard : c’est la preuve que le compilateur produit des résultats déterministes. Seuls les temps d’exécution changent, en fonction du Matériel et du système, mais pas de l’algorithme.

Ces chiffres d’orphelins proviennent d’un corpus synthétique ensemencé, pas d’une mesure de « qualité ». Ils ont été vérifiés indépendamment : le constructeur de graphe et le linteur calculent le statut d’orphelin via des chemins de code complètement différents, et ils ont toujours été d’accord à chaque échelle testée.

CE QUE ÇA CHANGE POUR VOUS : 12 SECONDES POUR 5 000 NOTES

En pratique, pour un wiki personnel de taille moyenne (quelques centaines à quelques milliers de notes), une recompilation complète prend moins de deux secondes. À 5 000 notes, ça tourne en 12 secondes sur du matériel Windows standard, sans coût en tokens, sans appel réseau.

Pour un usage réel, les limites du compilateur sont claires :

  • Données sources non structurées ou chaotiques : si vos fichiers mélangent des styles d’en-têtes, des métadonnées dispersées et des langues différentes, les expressions régulières seules ne suffiront pas. Il faudra une couche d’extraction bien plus sophistiquée.
  • Liens sémantiques : le constructeur de graphe utilise des correspondances de noms exacts. Si une note parle de « descente de gradient » et une autre de « l’étape d’optimisation » sans utiliser la phrase littérale, elles ne seront pas liées. Rien dans ce pipeline ne comprend le sens. C’est la limite honnête d’un compilateur déterministe. Si cette fonctionnalité est nécessaire, il faudrait ajouter une couche sémantique en option, sans l’intégrer au cœur du système.

Le message n’est pas « l’IA est un mauvais outil pour construire un wiki ». C’est « l’IA est le mauvais outil pour les 90 % du travail qui sont purement mécaniques, et probablement le bon outil pour les 10 % restants ».

LE CODE COMPLET, LES TESTS, ET TOUT CE QU’IL VOUS FAUT POUR REPRODUIRE

Le projet est open source. Tout le code, les 17 tests, et les sorties terminales non arrondies sont disponibles pour que vous puissiez tout relancer vous-même :

https://github.com/Emmimal/wiki-compiler/

Si vous cherchez un compilateur markdown 100 % Python, une alternative déterministe aux outils de base de connaissances basés sur des agents, ou une analyse pratique pour construire une alternative locale au RAG, c’est ici que ça se passe.

POURQUOI CELA MARCHE : LE COMPILATEUR GARANTIT CE QUE L’AGENT DÉCIDE

Un agent décide à quoi pourrait ressembler votre wiki. Un compilateur garantit à quoi il doit ressembler. Le développeur voulait un wiki prévisible. Contrairement à une IA qui varie ses sorties, un compilateur donne toujours le même résultat à chaque exécution. Cette cohérence est essentielle pour des notes de référence personnelles.

Le système est conçu pour que les pages markdown agissent comme des fichiers objets. Ils sont générés à partir de la source et peuvent être reconstruits à volonté. Le contenu édité à la main est séparé des sections générées par la machine. C’est la garantie que vos notes restent fiables, même après des années de modifications.

Et surtout, c’est gratuit. Pas de tokens à dépenser, pas de dépendances à installer, juste du Python standard qui fait son travail.

Sources :
  • Towards Data Science

L'indépendance de CLODCO est votre garantie.

Pour que l'actualité de l'IA reste sans filtre et sans concession, votre soutien est indispensable. Votre contribution est le seul moteur de notre liberté éditoriale.

Soutenir CLODCO