“Tu sais ce qu’il y a de mieux avec Rust ? Il t’empêche de compiler des travaux bâclés. Tu sais ce qu’il y a de pire ? Tout ce que tu codes au début est bâclé.” — M. Krabs, probablement
Quoi de mieux qu’un agent IA ? Un agent IA réécrit en Rust.
Si tu as passé plus de cinq minutes sur internet, tu connais ce mème : peu importe le projet — un éditeur de texte, un serveur DNS, une calculatrice IMC — il y aura toujours quelqu’un dans les commentaires pour dire “tu devrais le réécrire en Rust”. C’est le fameux Rewrite It In Rust — RIIR pour les intimes — aussi inéluctable que la gravité.
Eh bien, je vais le faire. Je vais porter 8,300 lignes d’un agent IA écrit en Python vers Rust. Mais pas seulement parce que le mème me l’ordonne (bon, peut-être un peu). Je le fais parce que j’ai besoin d’un cobaye.
La thèse
Je passe des semaines à écrire sur les échecs silencieux, les cinq défenses contre les hallucinations, et sur la manière dont un LLM peut générer du code qui compile, passe les tests, mais reste incorrect. J’ai même inventé un terme : développement adversarial. Ne jamais faire confiance, toujours vérifier.
Beaucoup de théorie. Maintenant, passons à la pratique.
J’avais besoin d’un projet avec trois critères : portée limitée (pas une nouvelle application avec des spécifications changeantes), une source de vérité claire (le code Python qui marche déjà), et une complexité suffisante pour que les hallucinations LLM puissent se cacher quelque part. Un portage à l’identique coche les trois cases. L’input et l’output attendus existent déjà ; si la version Rust ne se comporte pas exactement comme la version Python, c’est qu’il y a un bug. Simple comme bonjour.
Et puisque je vais porter quelque chose, pourquoi ne pas apprendre Rust pour de bon en même temps ? Le borrow checker, ownership, lifetimes… Ça fait des années que je lis tout mais que je ne teste rien. Ce serait une autre histoire si, au lieu de lire des tutoriels pour la vingtième fois, je me confrontais à un projet réel.
Le patient
Il s’appelle nanobot. C’est un agent IA personnel dérivé de OpenClaw : un outil qui connecte des LLMs (Claude, GPT, DeepSeek, tout ce que tu veux) aux canaux de communication — Telegram, Discord, Slack, e-mail — et leur donne de véritables capacités. Il sait lire et éditer des fichiers, exécuter des commandes, chercher sur internet, programmer des tâches avec cron, et possède une mémoire persistante entre les conversations.
Ça marche. Ça fait des mois que ça fonctionne. En Python.
Le problème ? C’est single-threaded. Il traite un message à la fois. Si tu lui envoies trois messages de suite, il fait la queue comme au supermarché un samedi à midi. Il utilise environ 50 Mo de RAM pour, essentiellement, rediriger du JSON entre des API. Et le code gère les erreurs avec une approche qui fait honte : return f"Error: {str(e)}" partout.
En clair : ça fonctionne, mais c’est un travail bâclé. Candidat parfait.
Pourquoi Rust (en dehors du mème)
Je pourrais résoudre ces problèmes avec Python. Je pourrais intensifier l’utilisation de asyncio, typer les erreurs avec des exceptions sur-mesure, optimiser la mémoire. Ce serait raisonnable.
Mais raisonnable ne me donne pas de terrain expérimental pour le développement adversarial. Un refactoring en Python n’a pas de source de vérité externe — le “avant” et le “après” utilisent le même langage, les mêmes bibliothèques, et les mêmes biais LLM. Un portage vers un langage différent, si. Si l’output de Rust diffère de celui de Python pour le même input, c’est que quelque chose cloche. Et c’est justement le genre de vérification que je souhaite essayer.
En plus, Rust apporte certaines propriétés qui rendent l’expérience encore plus intéressante :
- Le compilateur comme première défense. Nulls, type mismatch, data races : des catégories entières de bugs qui passent silencieusement sous Python mais jamais en Rust. Combien d’hallucinations LLM le compilateur bloque-t-il avant qu’un test ne les détecte ? Je veux mesurer ça.
- Vraie concurrence.
tokiopermet un spawn par conversation. En Python, c’est laborieux. C’est l’amélioration fonctionnelle qui justifie vraiment le portage. - Binaire statique. Un exécutable de 10 Mo au lieu d’un
pip installavec 47 dépendances. Pour la distribution, c’est imbattable. - Parce que c’est cool. Ce n’est pas une raison technique. Je m’en fous.
L’aventure (et l’invitation)
RustyClaw — c’est le nom du portage — sera un projet expérimental documenté en public. Chaque module porté, un article. Avec des données concrètes : combien de jetons ont été consommés, combien ça a coûté, combien de fois l’IA a halluciné, combien de temps j’ai combattu le borrow checker. Sans fioritures.
Si je passe 3 heures sur quelque chose que j’aurais fait en 10 minutes avec Python, je le dirai. Si le LLM invente un crate qui n’existe pas (spoiler : ça arrivera), je le noterai. Si au bout du compte, ce portage ne vaut pas le travail investi, je l’admettrai.
Tout le monde dit “j’ai utilisé une IA pour écrire du code”. Mais personne ne publie à quel point ça lui a coûté, combien de fois elle s’est trompée, et si le résultat est solide. C’est exactement ce que je vais faire.
Et je veux que tu m’accompagnes. Parce que ça va être toute une aventure — avec ses combats contre le compilateur, ses moments de “mais pourquoi ça ne compile pas alors que c’est ÉVIDENT ?”, et ses petites victoires quand un test différentiel passe en vert. Ça va être amusant. Ou au moins honnête.
La pile technique (le mémo)
Si tu es spécialiste Python, la colonne de gauche te semblera familière. Si tu es rustacean, c’est celle de droite. Si tu n’es ni l’un ni l’autre, bienvenue dans le chaos.
| Couche | Python (nanobot) | Rust (rustyclaw) |
|---|---|---|
| Runtime async | asyncio | tokio |
| HTTP | httpx | reqwest |
| Routage LLM | litellm | N’existe pas — router maison |
| Telegram | python-telegram-bot | teloxide |
| Discord | websockets (raw) | tokio-tungstenite (raw) |
| Config | pydantic | serde + figment |
| CLI | typer | clap |
| Gestion des erreurs | str(e) | anyhow + thiserror |
| Journaux | loguru | tracing |
| Copilote IA | — | Claude Code + Codex |
| Lanceur de tâches | make | just |
| Issues | — | linear CLI |
La ligne la plus douloureuse est LiteLLM. En Python, elle permet de jongler entre plus de 100 fournisseurs LLM avec une seule requête. En Rust, aucune solution équivalente. Il faudra construire un routeur sur mesure. Bonne nouvelle : 80% des fournisseurs sont compatibles avec l’API OpenAI, donc avec async-openai combiné à un base_url custom, on couvre presque tout. Anthropic nécessitera une implémentation dédiée.
Environ ~300 lignes de Rust. Ça semble faisable. Semble.
Les trois dernières lignes, c’est le meta-stack : mes outils pour gérer ce portage. Claude Code pour les séances interactives où je réfléchis à voix haute avec le LLM. Codex pour les portes mécaniques où le contexte est clair et je cherche de la rapidité. just à la place de make parce que j’ai envie de tester quelque chose qui ne dépend pas des tabs littéraux (oui, en 2026, on en est encore là). Et linear pour gérer le backlog directement dans le terminal.
La stratégie anti-hallucinations (la partie sérieuse)
Voilà où la théorie du développement adversarial rencontre la réalité. Un LLM assistant un portage de cette taille est essentiellement une machine à inventer des résultats crédibles.
Le principal risque n’est pas que le code ne compile pas. Rust refuse de compiler des travaux bâclés. Le risque, c’est qu’il compile, passe les tests, et fasse silencieusement quelque chose d’incorrect. Exactement le échec silencieux que j’ai documenté deux semaines plus tôt.
Cinq couches de défense :
1. Le compilateur Rust. Neutralise les nulls, type mismatch, data races. Première ligne de défense, gratuite. Mais le fait qu’un code compile ne veut pas dire qu’il est correct.
2. Tests différentiels. Même input → nanobot Python → output. Même input → rustyclaw Rust → output. S’il y a divergence, problème. La source de vérité reste le code Python fonctionnel. C’est la couche clé de l’expérience.
3. Provenance tracking. Chaque fichier porté inclut un header avec le fichier Python d’origine, la session LLM qui l’a généré, et une validation des tests différentiels. Traçabilité complète.
4. Vérification des crates. Chaque crate proposé par le LLM → vérifier manuellement sur crates.io et docs.rs. Les LLM inventent des crates inexistants et des APIs qui ne fonctionnent pas comme ça. Avec aplomb, comme si Sydney était la capitale de l’Australie.
5. Journalisation des incidents. Chaque hallucination identifiée → issue avec label hallucination. Matière pour les articles et éviter les erreurs répétées.
Règle d’or :
Le système de vérification doit être externe au générateur.
Si le LLM génère le code, les tests et les fixtures, on valide un contenu fictif avec un autre contenu fictif. Les tests différentiels contre Python original rompent ce cycle. Et la beauté du portage, c’est que cette couche existe naturellement.
Est-ce que ça vaut le coup ?
Abordons la question qui fâche. Est-ce que ce portage vers Rust a vraiment de l’importance ?
| Métrique | Python | Rust (estimé) | Importance ? |
|---|---|---|---|
| Latence de réponse | ~200ms overhead | ~5ms overhead | Non. Un LLM prend entre 2 et 5 secondes. |
| RAM | ~50MB | ~5MB | Non. Mon serveur a 8 Go. |
| Concurrence | 1 message à la fois | N messages en parallèle | Oui. |
| Temps de démarrage | ~2s | ~50ms | Bof. |
| Binaire | pip install + 47 deps | Exécutable autonome | Oui. |
| Sécurité typée | str(e) partout | Result<T, E> | Oui. |
| Coolitude | Non | Oui | Subjectif. |
Trois sur sept. Quatre, en étendant un peu. L’amélioration de latence et RAM est négligeable puisque le goulot d’étranglement reste l’appel au LLM. La concurrence est indispensable si plusieurs utilisateurs sont connectés. Le binaire statique est un vrai gain. Et les types… après avoir vu comment str(e) masque des bugs pendant des mois, ça compte.
Est-ce que ça justifie des semaines de travail ? Isolé, probablement pas. Comme laboratoire de test pour le développement adversarial avec des données réelles publiées ? Je pense que oui. À la fin de cette série, nous aurons des chiffres pour décider, pas des opinions.
Les chiffres, à nu
Chaque session de travail sera enregistrée dans un fichier CSV sur le repo :
| |
LLM utilisé, jetons consommés, coût, durée, lignes de code portées, hallucinations détectées, tests validés. Tout public. Tout vérifiable.
À la fin de la série, n’importe qui pourra sommer la colonne cost_usd et décider si le RIIR vaut le coup. N’importe qui pourra compter les hallucinations et juger si le développement adversarial marche ou si c’est du vent. Spoiler : je n’ai aucune idée des résultats. Et c’est ce qui rend l’expérience intéressante.
Joins-toi à moi !
- Repo : github.com/frr149/rustyclaw — code, incidents, tracking
- Blog : Chaque étape aura son article ici, dans la série RustyClaw : Rewrite It In Rust
- Backlog : Public sur Linear, visible dans les issues sur GitHub
Quoi de mieux qu’un AGI ? Un AGI réécrit en Rust. C’est ce que dit le mème. Maintenant, testons-le.