← Retour au portfolio
ENFR
Orchestration d’agents · conception système

Task Router Agent
Un système de routage de tâches à machine à états, piloté par un fichier Markdown

C’est un runtime de tâches piloté par LLM — le routage, l’exécution et l’auto-réparation se font tout seuls. Un simple fichier Markdown sert de plan de contrôle, et l’ensemble tourne sans dépendre d’une session ouverte : on modifie le fichier, le travail continue en arrière-plan. Dans cette étude : comment j’ai conçu l’architecture, les arbitrages que j’ai faits, les résultats mesurés, et un comportement clé que je n’avais pas programmé mais que le système a fait émerger tout seul.

Partie 1

Le Problème

Contexte : en tant qu’opératrice solo, je pilote quatre chantiers en parallèle (production de contenu, brief de revue d’architecture pour un premier client, portfolio, études). L’état des tâches se retrouvait éparpillé entre différents outils, et il me fallait 15 à 30 minutes de « remise en route » à chaque reprise de contexte avant de pouvoir vraiment travailler.

Contrainte produit : je voulais un système de tâches avec deux propriétés : je peux ajouter du travail à tout moment, et les tâches confiées à l’automatisation s’exécutent toutes seules, sans que j’aie besoin de rester devant la machine. Je ferme l’ordinateur, je reviens quelques heures plus tard, et le travail est fait.

Pourquoi c’est un vrai sujet en design d’agents : la plupart des « assistants IA » s’arrêtent dès qu’on ferme la fenêtre de chat. Ils sont liés à une conversation, pas au travail lui-même. Construire un runtime d’agent qui ne dépend pas du cycle de vie de la session est un problème de conception complètement différent — et c’est précisément ce basculement qui fait passer un agent du statut d’« outil » à celui d’« infrastructure ».

La question de conception : un simple fichier texte, couplé à un protocole de machine à états et à une couche de décision LLM, peut-il servir de runtime d’agent persistant ? Si oui, le même pattern se généralise à n’importe quel workflow opérationnel — triage support, pipeline de contenu, automatisation d’opérations.


Partie 2

Architecture du Système

Flux de Routage des Tâches

Déclencheur · task-board.md modifié (source quelconque : moi, un autre agent, un cron)
↓  fswatch · debounce 2s · launchd fallback horaire
Quiescence Gate · 30 secondes d’inactivité requises avant traitement (empêche les collisions de race conditions pendant l’édition)
↓  Acquisition du verrou fichier · primitive atomique mkdir
Couche de Décision LLM · Parsing de la machine à états · classification de chaque tâche : ready / blocked:* / running:* / done / failed:*
ready → exécution parallèle (jusqu’à 3 sous-agents simultanés)
running > 30min → réinitialisé à ready (reprise d’interblocage)
↓  Protocole écriture unique · l’agent principal consolide les mises à jour
Mise à jour d’état + Journal Append-Only · Réécriture task-board.md · journal structuré events.jsonl · deadletter/ pour échecs terminaux

Les 5 Couches

L5Plan de Contrôletask-board.mdMarkdown comme UI & source de vérité
L4Ordonnanceurfswatch + launchdÉvénementiel + fallback temporel
L3Exécuteurbash + claude -pInvocation sans état · one-shot
L2Couche de Décisiontask-check skill (LLM)Routage de machine à états & passage de relais
L1État & Auditevents.jsonl + deadletter/Journal append-only & capture d’échecs

Partie 3

Décisions de Conception Clés

Quatre arbitrages délibérés. Chacun avec une alternative spécifique que j’ai rejetée.

1. Markdown comme plan de contrôle, pas Notion / Airtable / SQL
RejetéBases de données structurées (Notion, Airtable, Postgres). Schéma imposé, requêtable, multi-utilisateur natif.
ChoisiUn unique fichier Markdown avec une front-matter à la YAML par tâche.
RaisonnementLatence d’édition < 1 seconde, zéro dépendance fournisseur, versionné via git, lisible sans outillage, parsable par LLM sans inférence de schéma. Le coût — pas de schéma — est compensé par la couche machine à états (voir décision 2).
À revisiterSi l’édition concurrente multi-opérateurs devient une exigence dure, ou si le volume dépasse ~500 tâches où le parsing Markdown devient goulot.
2. Machine à états typée, pas status en langage naturel
RejetéChaînes de statut libres (« en attente du client », « en cours », « bloqué API »).
ChoisiTokens d’état typés : ready / blocked:siyao:send-brief / running:agent-a4b:2026-04-20T15:25Z / done / failed:exhausted_retries
RaisonnementUn état typé est un protocole, pas une étiquette. Le LLM qui le parse peut raisonner sur les chaînes de dépendances, les conditions d’interblocage, la validité des horodatages — pas juste pattern-match des chaînes. Le plus grand impact unique sur l’intelligence du système (voir Partie 5).
CoûtMigration unique de toutes les tâches existantes (13 cartes) + discipline pour maintenir la taxonomie. Faible au départ, composé ensuite.
3. Concurrence écriture-unique, pas verrous distribués
RejetéÉcritures multi-agents concurrentes sur task-board.md avec verrouillage distribué (flock, patterns d’append, CRDT).
ChoisiProtocole écriture-unique strict : seul l’agent principal écrit l’état. Les sous-agents exécutent en parallèle et renvoient des résultats structurés ; l’agent principal sérialise les mises à jour.
RaisonnementLes fichiers Markdown ne sont pas transactionnels. Les écritures concurrentes causent pertes de mises à jour, corruption structurelle, états non parsables — défaillances en cascade. L’écriture unique limite la bande passante parallèle mais élimine toute une classe de bugs de race conditions à coût d’exécution nul.
ArbitrageLe parallélisme d’exécution est préservé (jusqu’à 3 sous-agents simultanés) ; la sérialisation d’état ne devient un goulot qu’à partir de ~1 tâche/sec soutenue. Pas une contrainte réelle pour cette charge.
4. Quiescence Gate, pas simple debounce
RejetéDebounce fixe de 2 secondes sur le watcher.
ChoisiDebounce watcher (2s) plus une quiescence gate : exiger 30 secondes d’inactivité fichier avant de procéder. Polling toutes les 5s ; réinitialisation à chaque changement détecté.
RaisonnementUn utilisateur en train d’éditer peut déclencher le watcher 5 à 10 fois pour une seule « sauvegarde » logique. Sans quiescence, l’exécuteur court contre l’utilisateur ; les mises à jour d’état entrent en collision avec les éditions en cours. Le gate de 30s aligne le traitement automatique sur le rythme de pause naturel de l’utilisateur.
CoûtPlancher de latence de traitement de 30 secondes. Acceptable pour flux de tâches asynchrones ; inadapté aux agents de chat temps réel.

Partie 4

Résultats Mesurables

Suivi de base initié le 2026-04-20. Chiffres reflétant la première semaine d’exécution ; certains estimés à partir des premières exécutions, avec méthodologie annotée.

~2s
Détection de changement
Mesuré depuis events.jsonl — délai de sauvegarde à l’événement file_changed_triggered
30s
Gate pré-exécution
Exigence d’inactivité délibérée pour prévenir exécution sur édition partielle
~30s
Traitement end-to-end
Pour tâches déterministes (routage, contrôle d’état). Tâches sous-agents variables.
<30min
Reprise d’interblocage
Auto-réparation : états running:* bloqués réinitialisés. Aucune intervention manuelle requise.
~$10-30
Coût mensuel
LLM API uniquement. Aucun coût d’hébergement (tourne sur laptop via launchd).
100%
Validation actions critiques
Toute action destructive/irréversible gatée par validation humaine par conception, pas par bug.

Gains d’efficacité (estimation conservative)

Avant le système : vérification manuelle de la liste de tâches + recherche routinière consommaient environ 8 à 12 heures/semaine. Après : les tâches routinières (recherche concurrentielle, rédaction de documents, scans de marché) sont routées vers des sous-agents parallèles sans supervision temps réel.

Production observée première semaine : 5 tâches substantielles de recherche/rédaction terminées sans supervision active (exemples : analyse concurrentielle de 260 lignes, étude de cas architecturale de 895 lignes, scan marché de 18 JD, évaluation portfolio perspective RH).

Méthodologie : estimations heures économisées sur base auto-rapportée. Quantification précise démarre semaine 2 (en cours).


Partie 5

Comportement Émergent · L’Insight Central

J’ai programmé la couche de décision pour faire une seule chose : trouver les tâches marquées ready et les exécuter. Ignorer tout le reste.

À la première exécution en production, le système a fait ce que je ne lui avais pas programmé :

Aucun de ces comportements n’était dans le prompt. Ils ont émergé de la conception de la machine à états.

Même LLM, même task-board — et pourtant l’agent se comporte de manière radicalement différente selon que le statut est une étiquette ou un protocole. Un statut en langage naturel (« en attente », « en cours ») ne donne au modèle qu’un token à matcher ; un état typé avec sous-champs structurés lui donne un protocole sur lequel raisonner. Le point que j’emporterais pour une équipe produit : ce qui transforme un agent d’un simple exécuteur en véritable partenaire de raisonnement, c’est l’état structuré ; le prompt n’est qu’un réglage final.

La conclusion pratique pour un système d’agents : quand le comportement d’un agent déçoit, la plupart des équipes itèrent sur les prompts. Le levier le plus puissant est souvent ailleurs — dans le système de types au sein duquel l’agent opère.


Partie 6

Généralisation & Prochaines Étapes

Transfert du Pattern

L’architecture n’est volontairement pas spécifique à l’automatisation de tâches. Le même pattern à 5 couches (fichier plan de contrôle + ordonnanceur événementiel + exécuteur sans état + machine à états typée + journal d’audit append-only) se mappe directement à plusieurs catégories de workflows listées dans un brief client en cours (opérateur Shopify) dont je réalise la revue d’architecture :

WorkflowPlan de contrôleTâche de la couche décision
Triage de tickets supportFile d’attente helpdeskClassifier + rédiger réponse + signaler pour revue
Gestion de précommandesTracker ETA fournisseurDétecter delta + générer notifications client
Création produit depuis données fournisseurFile de tableurs entrantsExtraire champs + normaliser + pousser en brouillon
Intake « we buy collections »Journal de demandes entrantesExtraire + scorer + rédiger suivi

La même discipline de machine à états (ready / blocked:* / running:* / done) s’applique à tous. Le seul composant workflow-spécifique est le template de prompt dans la couche de décision. L’infrastructure est partagée.

Évolution Planifiée

Ce Que Je Ferais Différemment

Avoir construit ce système m’a laissé une conclusion que je peux transférer à n’importe quel produit d’agents : la machine à états, c’est le produit lui-même. Prompts, sous-agents, couches d’ordonnancement — tous remplaçables. Ce qui persiste, ce qui passe à l’échelle, et ce qui détermine si le système peut raisonner ou seulement exécuter, c’est le contrat d’état entre l’intention humaine et l’action de l’agent.


Partie 7

Build vs. Buy — Pourquoi je n’ai pas utilisé Coze, Dify ou n8n

Une clarification de périmètre, d’abord : c’est un runtime à opérateur unique, construit pour mon propre flux de travail — pas un produit de plateforme. Il résout la forme très spécifique d’une seule personne qui orchestre une poignée de workflows. La question que j’avais à trancher avant d’écrire la moindre ligne de code n’était pas « construire, ou lancer un concurrent à Coze ? » — c’était : « est-ce qu’un outil existant résout déjà cette forme, et si oui, lequel ? » Ce qui suit, c’est l’analyse que j’ai faite avant de décider de construire.

Les 4 critères que j’optimisais

Les trois options, leurs forces, et pourquoi je les ai écartées

Coze (ByteDance) — builder d’agents visuel, SaaS cloud
Points fortsLe chemin le plus rapide pour qu’une équipe non technique livre un agent. Intégrations d’outils pré-construites riches dans l’écosystème ByteDance.
Pourquoi j’ai renoncéMon état vit déjà en Markdown + git. Utiliser Coze reviendrait à recréer cet état dans un canvas UI et à accepter le verrouillage éditeur. Pour un opérateur solo qui vit dans le terminal, le canvas est une friction, pas une aide.
Dify (open source) — plateforme d’applications LLM, auto-hébergée
Points fortsRAG, agents et gestion de datasets réunis sur une même plateforme. Bon choix pour une équipe qui livre un produit regroupant plusieurs capacités IA.
Pourquoi j’ai renoncéDify vise la livraison d’un produit utilisateur final ; il paie sa complexité en fonctionnalités dont je n’ai pas l’usage (datasets multi-tenant, gestion de conversations, UI embarquée). Pour un runtime de tâches personnel, Docker + Postgres + Redis + UI web, c’est clairement surdimensionné.
n8n (open source) — automatisation de workflows en graphe de nœuds
Points forts400+ intégrations pré-construites. Meilleur choix si l’étape IA n’est qu’un nœud dans un pipeline multi-SaaS plus large.
Pourquoi j’ai renoncéDans mon workflow, la décision IA n’est pas un nœud — elle est la couche de routage elle-même. n8n traite le LLM comme une intégration parmi d’autres ; mon design traite l’état typé comme l’entrée de première classe sur laquelle le LLM raisonne. Centre de gravité différent.

Le raisonnement commun

Ces trois plateformes abstraient le workflow derrière une UI et un schéma que l’on configure via cette UI. C’est le bon choix quand l’opérateur n’est pas technique, quand l’état doit être partagé au sein d’une équipe, ou quand le workflow est le produit. Aucune de ces conditions ne s’applique ici. Pour une opératrice solo dont l’état du workflow est déjà du texte, le coût d’introduire un second système d’état (canvas + stockage éditeur) l’emporte sur le confort des nœuds pré-construits.

Autrement dit : si ce runtime fait environ 300 lignes de shell + une définition de skill plutôt qu’un déploiement Dify, ce n’est pas parce que je pense pouvoir faire mieux. C’est parce que ma forme (une seule opératrice, natif Markdown, natif terminal, zéro éditeur) est précisément la forme que ces plateformes n’optimisent volontairement pas. Pour leur vraie audience — des équipes pluridisciplinaires livrant des agents orientés utilisateurs — elles restent le bon choix, et je les recommanderais.

La compétence transférable ici, ce n’est pas « j’ai construit une meilleure plateforme ». C’est un cadre de décision : avant d’écrire la moindre ligne de code, j’ai nommé les quatre choses dont j’avais réellement besoin, j’ai confronté chaque outil existant à ces quatre critères, et je n’ai choisi de construire que lorsque chaque outil échouait sur un critère précis. C’est exactement le même cadre build-vs-buy que j’appliquerais, dans un poste produit, à toute décision de plateforme d’agents — y compris pour déconseiller une construction sur-mesure lorsqu’un outil existant couvre le périmètre.


Siyao Zhang · Disponible à partir de septembre 2026, Pékin · Contact