DEV Community

Uig
Uig

Posted on

1,5 mètre : le jour où mon robot a refusé de réparer la machine

mon robot répare la machine

Je tire le levier, la machine casse. Mon robot s'approche, pose un diagnostic et la répare. Quatre lignes dans un terminal. Trois jours pour en arriver là.

1,5 mètre

Mon robot était planté là, à 1,5 m pile de la machine. Il l'inspectait. Il avait son diagnostic. Et il ne la réparait pas. Jamais.

Pas de crash. Pas d'exception. Pas une ligne d'erreur. Juste un robot en contemplation devant une machine en panne, pour l'éternité.

La règle était simple : à 1,5 m ou moins, le robot peut s'occuper de la machine. Sauf que je l'avais écrite deux fois, dans deux états différents, et je me suis planté sur le signe. D'un côté <=, de l'autre >=. Deux versions d'une même règle, qui ont divergé pile là où personne ne regarde : sur la borne.

Je l'ai repéré en relisant le code pendant une revue, puis reproduit avec un test à 1,5 m pile. La correction : la règle n'existe plus qu'à un seul endroit, et le test reste posé sur la borne, comme un vigile.

Deux copies d'une même règle finissent toujours par se fâcher.

Un robot, une machine, un levier

Ce robot est le premier habitant d'un monde que je construis. Un monde où des agents décident. À terme, ce monde sera mon laboratoire. Une capsule temporelle où ce que j'apprends et ce que je construis restent au même endroit. Quelque chose qui m'appartient, mais que je partage. Avec des habitants dedans.

Pour l'instant, tout se passe dans le terminal, et rien n'est laissé au hasard. Le moteur est déterministe, et c'est voulu. Plus tard, un LLM proposera les actions du robot, et le moteur décidera de ce qui est permis. Le LLM propose, le moteur dispose.

Pourquoi je m'inflige ça

J'ai un cimetière de side projects. Vous aussi, probablement. Et avec la GenAI, les idées germent vite et pourrissent tout aussi vite.

Celui-ci, je le construis lentement. À contre-courant d'une industrie qui consomme toujours plus, toujours plus vite. Et en ralentissant, j'ai retrouvé un truc que je croyais perdu : le plaisir de coder. Chaque ligne. Chaque test.

Il réunit mes trois obsessions du moment : le creative coding (Three.js, shaders, animation), les agents IA (LLM, mémoire, tool calling) et l'IA de jeu (PNJ, simulation, comportements). Bref : des LLM-powered NPCs dans un monde 3D.

Je viens de PHP. Puis Black October est arrivé : deux RCE critiques au cœur du langage (CVE-2026-109618 et CVE-2026-109619), en plein raz-de-marée de rapports de sécurité assistés par IA. J'ai tourné ma veste et je suis passé à Python. J'y reviendrai dans un article dédié.

Une classe de plus, évidemment

Revenons à nos moutons.

Mon robot passe par quatre états : au repos, en déplacement, en inspection, en réparation. Pour coder ses transitions, deux options.

La table de transitions : un dictionnaire (état, événement) → handler. Plus proche de ma spec, plus simple à tracer. Raisonnable.

État initial Événement Précondition Action État final
idle machine.broke Robot à plus de 1,5 m de la machine move_to vers le POI de la machine moving

Ma spec pour modéliser les états du robot

Le state pattern : une classe par état.

J'ai pris le state pattern. Évidemment. Presque dix ans de PHP, ça ne se soigne pas en une semaine : quand on me tend une classe de plus, je la prends.

Ma justification officielle tient quand même la route : la logique de chaque état va grossir. D'autres états arriveront, comme thinking quand le LLM réfléchira. Un nouvel état, une nouvelle classe, sans toucher aux autres.

Une classe par état, donc. Et mon cerveau PHP a enchaîné tout seul : une classe par fichier. Des années de PSR-4, ça laisse des traces. En PHP, l'autoloader fait correspondre chaque classe à un fichier : App\Robot\IdleState vit dans src/Robot/IdleState.php, point. Alors quand je relis robot_fsm.py avec ses huit classes en 152 lignes, j'ai des sueurs froides.

Sauf qu'en Python, le fichier, c'est le module. Et un module regroupe ce qui va ensemble : aucune règle n'impose une classe par fichier, pas même la PEP 8. Huit classes qui parlent toutes de la même machine à états, c'est un module cohérent, pas un dépotoir. J'ai quand même sorti Action de events.py, parce qu'une action n'est pas un événement. Le reste attendra que ça fasse vraiment mal.

Excusez la digression, mais le prix à payer pour le choix du state pattern : si chaque état décide dans son coin, la trace des décisions s'éparpille. Exactement ce que je veux éviter, puisque tout le projet repose sur des décisions qu'on peut relire. Alors chaque état renvoie sa décision, et un seul endroit, la machine à états, l'applique et la journalise. Les états proposent, la machine dispose. Ça ne vous rappelle rien ?

Verdict quand thinking arrivera. Si je me suis trompé, vous le lirez ici.

Quatre claques Python

Python n'a pas de mot-clé interface. J'ai cherché. Pour donner un contrat à mes états, j'ai atterri sur ABC et @abstractmethod : une classe de base abstraite, RobotStateHandler, et une méthode handle que chaque état doit implémenter. Sauf que le contrat ne se vérifie qu'à l'instanciation. Oubliez handle dans une sous-classe, et Python vous laisse la déclarer tranquillement ; il ne râle qu'au moment où tu essaies de créer l'objet. PHP, lui, refuse la classe dès sa déclaration.

default_factory attend une fonction. Avec default_factory=list, mon robot est devenu une liste vide. Pas un robot cassé : une liste. Vide. Et Python n'a rien dit, parce qu'il ne vérifie pas les annotations de type à l'exécution.

Deux de mes tests portaient le même nom. Le second écrasait le premier, en silence. Un test qui passe parce qu'il n'existe plus. C'est ruff qui a cafté.

Les bugs vivent sur les bornes, alors mes tests y vivent aussi : 1,5 m pile pour la distance, et 0, 69, 70, 89, 90 et 100 pour les seuils d'usure du diagnostic.

J'ai noté une vingtaine d'autres notions. Je les garde pour une série « Un dev PHP apprend Python ».

La suite

Prochaine étape : tirer le levier depuis une page web et voir le robot réagir en direct.

Le code est sur GitHub : github.com/Arawnn/the-little-workshop. Et vous, vous faites quoi en ce moment ?

Top comments (0)