Lomo Agent : architecture et ingénierie d’un agent créatif qui collabore dans la durée
Comment nous avons conçu Lomo Agent : organisation des tâches, routage des messages, exécution durable, génération asynchrone, contexte et mémoire.
isboyjc112 min de lectureSur cette page
- Lomote AI et Lomo Agent
- Point de départ : une tâche créative sans cesse retouchée
- 1. Organisation des tâches : de la requête utilisateur à des tâches qui avancent dans la durée
- 2. Vue d’ensemble du système : couche de coordination, couche d’exécution et espace de travail partagé
- 2.1 Architecture globale
- 2.2 Toutes les requêtes ne méritent pas un Worker dédié
- 2.3 Le déroulé d’une collaboration
- 3. Routage des messages : amener chaque message à la bonne unité d’exécution
- 3.1 Routage déterministe et routage sémantique
- 3.2 Message livré ne veut pas dire message exécuté
- 3.3 S’inspirer des outils collaboratifs pour l’attribution des messages
- 4. Exécution durable : reprise après interruption, nouvelles tentatives et contrôle de la concurrence
- 4.1 Persister la décision d’abord, exécuter l’appel d’outil ensuite
- 4.2 Reprise par un autre Worker et Fencing Token
- 4.3 Traiter les nouveaux messages de l’utilisateur en cours d’exécution
- 4.4 Journal d’exécution et limites de cohérence de l’ordonnancement
- 5. Génération asynchrone : cycle de vie des tâches et réveil par événement
- 5.1 Soumettre, attendre, reprendre l’exécution
- 5.2 Les événements asynchrones exigent eux aussi un traitement fiable
- 5.3 Quand faut-il réveiller le Worker ?
- 6. Ingénierie du contexte : maintenir un état utile dans une fenêtre limitée
- 6.1 Contexte en couches et Prompt Cache
- 6.2 Chargement des outils à la demande : plus de capacités, un contexte plus coûteux
- 6.3 Compaction de l’historique : au-delà du résumé, garder la possibilité de relire
- 6.4 Garder la tâche en cours dans le champ de vision du modèle
- 6.5 Les arbitrages concrets de l’ingénierie du contexte
- 7. Ingénierie de la mémoire : provenance, versions et révocabilité
- 7.1 Des messages passés à la mémoire à long terme
- 7.2 Ce que nous ont appris les architectures de mémoire existantes
- 7.3 Pourquoi « oublier » est plus difficile que « se souvenir »
- 7.4 Fiabilité de la mémoire et dégradation gracieuse
- 8. Progression autonome : plan, conditions d’arrêt et autorisation de l’utilisateur
- 8.1 Transformer le plan en une liste d’exécution maintenue dans la durée
- 8.2 Le modèle a cessé de répondre : la tâche doit-elle continuer ?
- 8.3 Human-in-the-Loop : associer l’utilisateur aux décisions clés
- 8.4 Niveau d’autonomie, budget et limites d’autorisation
- 9. Shared Workspace : l’utilisateur et l’Agent modifient la même œuvre
- 9.1 Donner aux œuvres un cycle de vie propre
- 9.2 Rendre l’Agent attentif aux changements dans l’espace de travail
- 9.3 Conflits de version et contrôle de concurrence optimiste
- 9.4 Des Artifacts et de Canvas à un espace de création partagé
- 10. Orchestration des capacités créatives : de l’appel de modèle à la livraison d’une œuvre complète
- 10.1 Faire comprendre à l’Agent le processus créatif et les limites des capacités
- 10.2 La production vidéo multi-agents vue à travers ViMax
- 10.3 Graphe de dépendances de production : de l’exécution linéaire à la gestion des dépendances
- 11. Validation technique : de la reprise après panne à la collaboration de bout en bout
- 11.1 Valider la fiabilité du runtime
- 11.2 Simulations de collaboration de bout en bout
- 11.3 Comment mesurer la qualité d’une collaboration
- 12. Questions ouvertes et pistes à explorer
- Conclusion
- Références et lectures complémentaires
Réaliser un court film produit, c’est souvent enchaîner discussion du besoin, collecte de références, storyboard, génération des images, génération de la vidéo et allers-retours de corrections. Certaines étapes prennent quelques secondes, d’autres plusieurs minutes d’attente ; certaines décisions peuvent être laissées au modèle, d’autres doivent impérativement être validées par l’utilisateur. Et une fois la première version terminée, le travail est encore loin d’être fini.
Depuis l’arrivée de l’ère de l’IA, j’écris rarement des articles techniques : l’essentiel de mon temps passe à explorer la façon dont l’IA peut se concrétiser dans des cas d’usage variés.
Beaucoup pensent que les grands gagnants de l’ère de l’IA sont les product managers (PM), parce que la distance entre une idée et sa réalisation s’est considérablement réduite. Pour ma part, je crois que l’IA donne peut-être encore plus de pouvoir aux ingénieurs. Elle desserre peu à peu l’étau que la mise en œuvre technique imposait à la créativité, et leur offre l’occasion de sortir du code et des détails d’ingénierie pour repenser l’essence du produit, de l’architecture, voire de l’interaction homme-machine.
Autrefois, nous passions énormément de temps à chercher comment faire exécuter correctement des instructions à une machine. Aujourd’hui, nous pouvons enfin consacrer davantage d’énergie à une question bien plus passionnante : comment amener la machine à vraiment comprendre l’intention humaine, et à collaborer avec nous de façon plus naturelle ?
Nous avons récemment lancé Lomo Agent sur Lomote AI. J’en profite pour partager quelques réflexions, choix d’architecture et pratiques d’ingénierie qui ont jalonné son parcours, de l’idée à la mise en production.
L’article aborde l’organisation des tâches, le routage des messages, l’exécution durable, les tâches asynchrones, l’ingénierie du contexte, la mémoire à long terme, la progression autonome et l’espace de travail créatif partagé. Certaines de ces conceptions sont déjà en production, d’autres encore en cours d’exploration et de validation. Je m’appuierai aussi sur d’excellents produits et travaux techniques, comme DeepSeek, Manus ou Claude Code, pour expliquer comment j’ai arbitré entre différentes approches.
Lomote AI et Lomo Agent
Lomote AI est la plateforme de création IA tout-en-un que nous construisons. Fait intéressant, sur ce projet, j’ai surtout endossé le rôle de product manager (PM), tout en participant à la conception et à la construction de l’infrastructure sous-jacente. Cela m’a permis de repenser, sous l’angle à la fois du produit et de la technique, la forme et l’expérience qu’un outil de création IA devrait offrir.
Aujourd’hui, Lomote AI intègre de nombreux modèles de génération et d’amélioration d’images, de vidéos et de 3D, et propose une bibliothèque de ressources, des Moodboards ainsi que des workflows visuels Flows. Notre objectif : permettre aux créateurs de gérer leurs ressources, de combiner des modèles et d’orchestrer leurs processus créatifs dans un espace de travail unifié, pour que la création IA passe d’appels de modèles épars à une expérience complète et cohérente.
Ces fonctionnalités règlent en grande partie le problème de l’éparpillement des outils de création IA. Mais à mesure que l’usage s’approfondissait, nous avons compris que l’intégration des outils n’était qu’une première étape : la vraie question, c’est de les faire réellement fonctionner ensemble pour libérer la productivité des utilisateurs.
Dans un processus de création IA classique, l’utilisateur doit encore planifier lui-même les étapes, choisir les modèles, organiser ses ressources, et faire transiter sans cesse le contexte et les résultats intermédiaires d’une tâche à l’autre. Dès que le processus se complique, il dépense une énergie considérable rien qu’à coordonner les différentes étapes.
C’est dans ce contexte qu’est né Lomo Agent. Je voulais que l’IA cesse d’être un simple outil de création pour devenir un agent capable de comprendre une intention, de planifier des tâches et de collaborer dans la durée. L’utilisateur se contente de décrire son objectif ; l’Agent mobilise les modèles et outils déjà disponibles sur la plateforme, organise le processus créatif et s’ajuste en continu selon les retours de l’utilisateur pendant l’exécution.
Pour offrir une telle expérience, un simple appel d’outil en un seul tour ne suffit évidemment pas. Organiser les tâches, gérer le contexte, coordonner l’exécution asynchrone, permettre à l’Agent de poursuivre le travail une fois l’utilisateur parti : autant de problèmes d’architecture qu’il nous a fallu résoudre.
Point de départ : une tâche créative sans cesse retouchée
Imaginons que, lundi, un utilisateur demande : « Fais-moi une série d’affiches de marque au style rétro. » L’Agent clarifie le besoin et commence à générer des images de référence. Entre-temps, l’utilisateur glisse une autre demande : « Au passage, transforme la photo de chat d’hier en vidéo de cinq secondes. » Puis mercredi, il revient à la charge : « La série d’affiches de l’autre jour, rends-la un peu plus chaude, et déplace aussi le texte. »
Entre humains, c’est la collaboration la plus banale qui soit. Pour un Agent, en revanche, cela suppose de gérer plusieurs tâches en parallèle, la génération asynchrone, le rattachement du contexte et la recherche dans les ressources passées. Et à mesure que le travail avance, s’ajoutent l’annulation, les nouvelles tentatives, le contrôle du budget, l’évolution des préférences de l’utilisateur ou encore la collaboration multi-appareils.
Il y a environ un mois, j’ai commencé à me poser une question : pourquoi un Agent devrait-il forcément être centré sur la session ?
La plupart des produits d’Agent actuels reprennent encore le paradigme d’interaction du chatbot : l’utilisateur doit créer, retrouver et entretenir ses sessions lui-même. Pour un utilisateur intensif, les sessions s’accumulent, et la gestion du contexte, qui devrait incomber à l’IA, devient au contraire un fardeau pour lui.
Dans la vraie vie, personne n’entretient délibérément des « sessions ». On change de sujet quand on veut, et l’on reprend naturellement une discussion là où on l’avait laissée, plusieurs jours plus tard. Un agent idéal devrait sans doute en être capable, lui aussi.
Nous avons donc tenté, dans Lomo Agent, d’estomper la notion de session côté utilisateur, en confiant au système l’identification des tâches, l’isolation du contexte et le rattachement à l’historique. Ce n’est bien sûr qu’un maillon de l’architecture d’ensemble. Pour réellement soutenir une création dans la durée, il faut encore résoudre toute une série de problèmes d’ingénierie : reprise de l’exécution, ordonnancement asynchrone, mémoire à long terme, autorisation budgétaire, gestion des productions partagées.
Pendant la réalisation de la démo, je suis justement tombé sur la vision d’architecture Super-agent de l’équipe DeepSeek. Fait intéressant, ils s’étaient heurtés à des problèmes similaires en matière d’organisation des tâches et de gestion du contexte. Le partage des responsabilités Router / Worker décrit dans leur article « Explorer la conception d’un Super-agent : vers des systèmes d’Agents plus autonomes », ainsi que leur conception ultérieure du routage des messages, m’ont beaucoup inspiré et m’ont aidé à affiner l’architecture globale de Lomo Agent.
Voici donc, à partir de la conception et de l’implémentation réelles, mes réflexions et mes arbitrages sur un certain nombre de questions.
1. Organisation des tâches : de la requête utilisateur à des tâches qui avancent dans la durée
Revenons à notre exemple : l’utilisateur peut changer plusieurs fois de sujet en quelques minutes, ou ne revenir sur une tâche qu’au bout de plusieurs jours. Pour l’Agent, recevoir un message n’est qu’un début ; l’essentiel est de comprendre à quel objectif ce message se rattache, et comment retrouver l’avancement précédent et les ressources associées.
Cela impose un arbitrage d’architecture. Si toutes les tâches partagent un même contexte, les informations liées à des objectifs différents se parasitent facilement, et la fenêtre de contexte du modèle enfle rapidement. Si l’on isole tout strictement par session, on coupe les liens historiques entre les tâches.
Dans Lomo Agent, nous avons donc séparé le point d’entrée de l’interaction, le contexte d’exécution et la mémoire à long terme en trois couches relativement indépendantes :
| Concept | Problème résolu | Approche de Lomo Agent |
|---|---|---|
| Point d’entrée de l’interaction | Où l’utilisateur exprime-t-il son besoin ? | Un fil principal unique côté utilisateur, sans créer ni changer de session manuellement |
| Contexte d’exécution | Quelles informations le modèle doit-il raisonner ensemble ? | Le système crée une tâche distincte par objectif, chacune avec sa propre frontière de contexte |
| Mémoire à long terme | Qu’est-ce qui mérite d’être conservé d’une tâche à l’autre et dans le temps ? | Profil, faits et résumés épisodiques à la provenance traçable, rappelés à la demande |
L’utilisateur n’a qu’à exprimer naturellement son intention dans un point d’entrée unique ; le système se charge d’identifier la tâche à laquelle se rattache chaque message, et crée une tâche distincte pour tout travail qui doit avancer dans la durée. Chaque tâche possède son propre contexte, son état d’exécution et ses références vers ses productions, et peut, si nécessaire, obtenir des informations d’autres tâches via des mécanismes contrôlés de recherche et de partage.
Pour cette conception, nous nous sommes aussi penchés sur les pratiques des produits existants.
Par exemple, ChatGPT renforce la continuité entre conversations grâce à une mémoire inter-sessions ; Claude, avec des mécanismes comme Projects, permet d’organiser connaissances et contexte autour d’un projet donné. Chacune de ces approches a ses atouts : des frontières explicites de session et de projet sont faciles à comprendre, à gérer et à partager, et donnent à l’utilisateur un contrôle plus direct sur la portée du contexte.
Mais dans les scénarios créatifs où l’on jongle souvent entre des tâches sur de longues périodes, l’utilisateur doit encore assumer une part du travail d’organisation du contexte. La mémoire inter-sessions aide l’Agent à connaître les préférences et l’historique de l’utilisateur, mais elle ne peut pas représenter pleinement une tâche en cours d’exécution ni son avancement.
Lomo Agent choisit donc de confier davantage l’organisation des tâches au système, pour libérer autant que possible l’utilisateur de la gestion quotidienne des sessions. Bien sûr, l’identification automatique implique aussi des erreurs de rattachement possibles. C’est pourquoi nous avons conservé un point d’entrée explicite vers les tâches et des mécanismes de correction, et, lorsque la cible est claire, nous privilégions le routage déterministe pour limiter les jugements superflus du modèle.
flowchart TD
U["Utilisateur"] --> E["Point d'entrée d'interaction unifié"]
E --> R["Identification et routage des tâches"]
R --> A["Tâche A : affiches de marque"]
R --> B["Tâche B : production vidéo"]
R --> C["Requête simple : traitée directement par le fil principal"]
A <--> M["Mémoire à long terme"]
B <--> M
C <--> M
A --> S["Ressources et productions partagées"]
B --> S
Figure 1 : le modèle d’organisation des tâches de Lomo Agent
2. Vue d’ensemble du système : couche de coordination, couche d’exécution et espace de travail partagé
Une fois l’organisation des tâches arrêtée, restait à les faire réellement tourner.
Un Agent capable de collaborer dans la durée doit à la fois traiter les nouvelles requêtes de l’utilisateur, faire avancer les tâches existantes, attendre des résultats de génération externes et réagir aux modifications que l’utilisateur apporte à ses œuvres. Confier tout cela à un seul Agent fait rapidement enfler le contexte, et les tâches se parasitent ; créer un Agent indépendant pour chaque requête introduit en revanche un surcoût de coordination.
Partant de ce constat, j’ai conçu pour Lomo Agent une structure à deux niveaux Main / Task, et introduit un runtime durable dans la couche d’exécution : les différentes tâches avancent ainsi de façon indépendante, tout en accédant aux productions créatives et en les modifiant via un espace de travail partagé.
2.1 Architecture globale
Le système se décompose en quatre grandes parties :
-
Main (fil principal) : le point d’entrée avec lequel l’utilisateur interagit en continu. Il comprend les nouvelles requêtes, les rattache aux tâches existantes, traite les questions simples, et coordonne l’exécution et les retours des différentes tâches.
-
Task (tâche) : un espace d’exécution indépendant qui porte un objectif créatif précis. Chaque tâche maintient son propre brief, son contexte, son plan d’exécution, sa boîte de réception de messages et son état d’exécution.
-
Agent Runtime (runtime) : gère l’inférence du modèle, les appels d’outils, les attentes asynchrones, la persistance de l’état et la reprise de l’exécution, et vérifie à chaque étape le budget, les permissions et les conditions d’arrêt.
-
Shared Workspace (espace de travail partagé) : stocke les productions créatives (images, vidéos, 3D, documents, collections de ressources, workflows Flows…) et permet à l’utilisateur comme aux différentes tâches d’y accéder et de les modifier dans la limite de leurs permissions.
Côté rôles, Main assume principalement celui de Router, tandis qu’au sein de chaque Task, un Worker fait avancer le travail concret. Router et Worker sont des rôles d’exécution logiques : ils n’exigent pas des modèles de base différents.
flowchart TB
U["Utilisateur : langage naturel / actions sur cartes / modifications dans l'espace de travail"] --> UI["Interface de collaboration unifiée"]
UI --> GW["Couche d'accès : API interne / API ouverte / événements temps réel"]
GW --> CMD["Réception des commandes et messages"]
subgraph CORE["Agent Core"]
ROUTER["Main / Router : compréhension et coordination"]
ROUTING["Attribution des objectifs et routage déterministe"]
W1["Task A / Worker : série d'affiches"]
W2["Task B / Worker : vidéo courte"]
CTX["Orchestration du contexte / compaction / outils à la demande"]
RT["Moteur d'exécution durable"]
GUARD["Autorisation / budget / niveau d'autonomie"]
end
CMD --> ROUTING
ROUTING --> ROUTER
ROUTING --> W1
ROUTING --> W2
ROUTER --> RT
W1 --> RT
W2 --> RT
RT --> CTX
RT --> GUARD
RT --> TOOLS["Outils créatifs : image / vidéo / audio / 3D / canevas / ressources"]
RT <--> DB[("Base de données persistante : faits et état d'exécution")]
RT <--> M["Mémoire à long terme : profil / faits / résumés épisodiques"]
TOOLS --> ASSET["Productions partagées et espace de travail"]
ASSET --> UI
DB --> EVENTS["Files d'attente / publication d'événements / push temps réel"]
EVENTS --> UI
Figure 2 : l’architecture en couches de Lomo Agent
L’une des caractéristiques importantes de cette architecture est que l’exécution des tâches et les productions créatives ont chacune leur propre cycle de vie.
Par exemple, lorsque l’utilisateur lance la création d’affiches dans le fil principal, le système crée une tâche dédiée. Les images de référence, l’historique des générations et des modifications qui suivent sont gérés par cette tâche, tandis que les productions finales sont stockées dans l’espace de travail partagé. Même si l’utilisateur passe à une autre tâche, celle des affiches peut continuer d’attendre ses résultats de génération et reprendre son exécution au besoin.
Le fil principal n’a qu’à conserver un résumé et l’état courant de chaque tâche, sans garder durablement la trace complète de chaque appel de modèle et de chaque exécution d’outil. On évite ainsi que son contexte enfle indéfiniment au fil de l’usage.
Ce que nous a inspiré le Super-agent de DeepSeek
Pendant la conception, la vision d’architecture Super-agent de l’équipe DeepSeek nous a beaucoup inspirés.
Elle aussi sépare coordination et exécution : elle organise les différents objectifs via un découpage Router / Worker, et maintient l’attribution des tâches grâce à des mécanismes comme Ticket / Executor. Cette conception convient particulièrement aux Agents qui tournent longtemps et reçoivent régulièrement de nouvelles instructions.
Toutefois, le prototype de recherche publié par DeepSeek vise surtout les dépôts de code. Une tâche sur un dépôt a généralement un périmètre, des modifications de fichiers et des livrables relativement clairs, alors que les frontières d’une tâche de création IA sont bien plus floues.
Par exemple, une simple génération d’image ne prend parfois que quelques dizaines de secondes, mais quelques retouches demandées ensuite par l’utilisateur peuvent la transformer peu à peu en projet créatif au long cours. Dans le même temps, une même ressource peut être référencée et modifiée par plusieurs tâches.
Nous avons donc conservé la répartition de base Router / Worker, en y ajoutant des mécanismes taillés pour la création : tâches légères, identité partagée des productions et attente asynchrone.
Cette approche introduit aussi une nouvelle complexité : déterminer l’attribution des tâches, gérer les permissions sur les objets partagés, synchroniser l’état entre tâches. Nous y reviendrons dans les sections suivantes.
2.2 Toutes les requêtes ne méritent pas un Worker dédié
Les architectures multi-agents tombent facilement dans un piège : puisqu’on peut découper les tâches, autant confier chaque chose à un Agent indépendant.
Mais un espace d’exécution indépendant a un coût. Chaque Worker créé implique de maintenir un contexte, un état d’exécution et des relations de routage de messages supplémentaires, et les appels au modèle comme le surcoût de coordination risquent d’augmenter d’autant.
Prenons les scénarios créatifs de Lomo Agent :
si l’utilisateur demande « Quelles résolutions ce modèle vidéo prend-il en charge ? », Main peut répondre directement ;
s’il demande « Génère une image de chat roux », une seule tâche de génération peut suffire ;
mais « Réalise une série d’affiches de marque au style homogène » implique planification, gestion des ressources, plusieurs cycles de génération et des retouches ultérieures : mieux vaut alors créer une tâche dédiée.
Nous distinguons donc deux types de tâches d’exécution : les tâches légères (Lightweight Task) et les tâches persistantes (Persistent Task).
Les tâches légères portent essentiellement une génération ponctuelle, son état d’exécution et sa carte de résultat, sans occuper durablement la liste des tâches de l’utilisateur. Lorsque l’utilisateur continue de retoucher le résultat, ou que la tâche se mue progressivement en objectif créatif complexe, le système peut la promouvoir en tâche persistante, en conservant le contexte et l’historique d’exécution nécessaires à la suite de la collaboration.
Les tâches persistantes disposent quant à elles d’un cycle de vie relativement complet : elles peuvent recevoir des messages en continu, exécuter un plan, attendre des travaux externes, et reprendre le travail d’un jour à l’autre, voire après bien plus longtemps.
Ce découpage complète aussi la conception « sans session ». Nous voulons alléger la charge de gestion des sessions, et certainement pas la remplacer par une autre liste de tâches qu’il faudrait sans cesse ranger.
2.3 Le déroulé d’une collaboration
Revenons à l’exemple des affiches de marque et de la vidéo du chat.
L’utilisateur lance d’abord la création d’affiches dans Main. Le Router juge qu’il s’agit d’un objectif qui doit avancer dans la durée : il crée la tâche correspondante, dont un Worker prend en charge la planification et la génération.
Pendant la création des affiches, l’utilisateur demande de transformer en vidéo la photo de chat précédente. Le système reconnaît un travail indépendant et lui crée un nouvel espace d’exécution. Les deux tâches maintiennent chacune leur contexte et peuvent avancer indépendamment selon leur propre état.
Lorsqu’une proposition d’affiche doit être validée, l’Agent présente les options candidates à l’utilisateur via une carte de tâche. L’utilisateur choisit directement sur la carte, et le message est renvoyé au Worker concerné grâce à l’identité explicite de la tâche, sans nouveau jugement sémantique.
Quelques jours plus tard, l’utilisateur dit dans le fil principal : « La série d’affiches de l’autre fois, un peu plus chaude. » Le Router s’appuie alors sur l’index des tâches et l’historique pour rattacher cette nouvelle instruction à la tâche d’affiches d’origine.
sequenceDiagram
participant U as Utilisateur
participant R as Main / Router
participant A as Task A / affiches
participant B as Task B / vidéo
U->>R: Fais une série d'affiches de marque
R->>A: Créer la tâche et le brief
R-->>U: Afficher la carte de tâche dans le fil principal
U->>R: Au passage, fais une vidéo du chat d'hier
R->>B: Créer une autre tâche
Note over A,B: Contextes indépendants, avancée en parallèle
A-->>U: Propositions d'affiches à valider
U->>A: Choisir l'option B sur la carte
B-->>U: Génération vidéo terminée
U->>R: Les affiches de la dernière fois, un peu plus chaudes
R->>A: Renvoyer la relance vers la tâche existante
Figure 3 : diagramme de séquence de la collaboration multi-tâches Main / Task
Un détail de conception mérite ici l’attention : l’attribution d’un message et son exécution sont deux étapes distinctes.
Le Router peut assigner un message à la tâche correspondante, sans que le Worker le traite immédiatement. Celui-ci peut par exemple être en train d’attendre une génération vidéo, ou en pause sur un point de validation par l’utilisateur. Les nouveaux messages entrent d’abord dans une boîte de réception persistante, puis le runtime planifie leur consommation selon l’état courant.
Cela signifie aussi que l’organisation des tâches n’est qu’une première étape. À mesure que les tâches se multiplient, identifier précisément les intentions de suivi de l’utilisateur, éviter les erreurs de routage et gérer les messages qui arrivent en cours d’exécution deviennent des questions centrales pour toute l’architecture.
3. Routage des messages : amener chaque message à la bonne unité d’exécution
Le point d’entrée unifié permet à l’utilisateur de changer de tâche naturellement, mais il transfère aussi au système un travail que l’utilisateur faisait lui-même : décider à qui confier chaque nouveau message.
Par exemple, l’utilisateur vient de générer une image, puis lance soudain : « Passe celle de tout à l’heure en bleu. »
« Celle de tout à l’heure » peut désigner la dernière image générée, ou une ressource d’une tâche d’affiches antérieure.
Si l’attribution se trompe, au mieux on crée une tâche en double, au pire l’Agent modifie la mauvaise œuvre.
La précision du routage des messages conditionne donc directement la fiabilité de tout le système. Plutôt que de laisser le modèle deviner à nouveau l’attribution de chaque message, nous avons adopté un principe assez simple : tout ce qui peut être déterminé par une relation explicite est traité directement par le code ; ce n’est qu’en l’absence d’attribution explicite qu’on s’en remet au jugement sémantique du modèle.
3.1 Routage déterministe et routage sémantique
La réception des messages dans Lomo Agent passe grosso modo par trois étapes.
Premier niveau : routage direct pour les cibles explicites.
Lorsque l’utilisateur répond dans le panneau d’une tâche, clique sur un bouton d’action d’une carte de tâche, ou demande une modification sur une œuvre explicitement rattachée, le système sait déjà à quel objectif appartient le message : il peut le distribuer directement d’après le Task ID ou la relation avec l’objet.
Dans ce cas, solliciter encore le modèle pour juger de l’attribution ne ferait qu’ajouter de la latence et un risque d’erreur.
Deuxième niveau : jugement sémantique par le Router.
Pour les messages envoyés depuis le fil principal sans cible explicite, le Router doit trancher en tenant compte de la requête courante, des interactions récentes et de l’index des tâches existantes.
Les issues possibles sont les suivantes :
-
une requête simple, traitée directement par Main ;
-
une tâche légère, qui ne demande qu’une seule génération ;
-
une instruction de suivi, rattachable à une tâche existante ;
-
un nouvel objectif, qui justifie la création d’une tâche persistante ;
-
une attribution clairement ambiguë, qui nécessite une confirmation de l’utilisateur.
Un point est facile à négliger : le Router n’a pas besoin de lire l’historique complet de toutes les tâches. Il suffit généralement de lui fournir l’objectif, le résumé, l’état récent et les références d’objets associées de chaque tâche ; une fois un candidat identifié, il peut lire les détails à la demande.
On évite ainsi que le routage lui-même devienne, à mesure que les tâches se multiplient, un nouveau contexte qui enfle sans cesse.
Pour l’instant, nous fournissons en priorité un certain nombre de tâches récemment actives, avec des informations d’index comme leur objectif, leur résumé et leur état ; si l’utilisateur évoque un travail plus ancien, le système effectue une recherche par mots-clés et par similarité vectorielle des résumés, puis confie un petit nombre de candidats pertinents au jugement du Router. Ainsi, l’augmentation du nombre de tâches pèse surtout sur le périmètre de recherche, et non sur la longueur du contexte que chaque inférence doit embarquer.
Une autre erreur fréquente consiste à prendre pour un nouvel objectif un travail que l’utilisateur voulait en réalité poursuivre. Avant de créer une tâche persistante, le système effectue donc aussi une vérification de similarité avec les 50 tâches en cours les plus récentes. S’il repère un objectif potentiellement en double, il confie les quelques candidats les plus proches au modèle, qui juge s’il faut reprendre une tâche existante ou confirmer la création d’une nouvelle. Cette vérification intercepte une partie des créations en double, mais ne remplace pas la clarification de toutes les ambiguïtés sémantiques. Lorsque les éléments sont insuffisants, il faut toujours laisser l’utilisateur choisir explicitement.
Troisième niveau : vérification de l’attribution et de l’état avant exécution.
Une fois la cible du message déterminée, le système doit encore vérifier que la tâche est valide, qu’elle accepte l’opération en cours, et que les objets concernés sont toujours accessibles.
Par exemple, l’utilisateur peut faire référence à une tâche déjà archivée, ou tenter de modifier une ressource déjà supprimée. Que le Router ait correctement attribué le message sur le plan sémantique ne garantit pas que l’opération qui suit puisse s’exécuter.
flowchart TD
M["Message utilisateur ou événement d'interaction reçu"] --> A{"Porte-t-il une identité de tâche explicite ?"}
A -->|Oui| V["Vérifier la cible existante et les droits d'accès"]
A -->|Non| B{"Existe-t-il une relation explicite avec un objet ?"}
B -->|Oui| V
B -->|Non| R["Jugement sémantique du Router"]
R --> Q["Requête simple : traitée directement par Main"]
R --> L["Génération en une étape : créer une tâche légère"]
R --> E["Objectif existant : rattacher à une tâche existante"]
R --> N["Objectif indépendant : préparer la création d'une tâche persistante"]
R --> C["Ambiguïté : demander confirmation à l'utilisateur"]
N --> S{"Doublon détecté parmi les tâches en cours ?"}
S -->|Oui| H["Fournir les candidats similaires au jugement du modèle"]
H -->|Rattacher à une tâche existante| E
H -->|Confirmer la création| CREATE["Créer la tâche persistante"]
S -->|Non| CREATE
CREATE --> NV["Vérifier la nouvelle tâche et les droits d'opération"]
L --> NV
NV --> I["Initialiser l'état d'exécution et livrer l'entrée"]
E --> V
V --> D{"L'état de la cible existante est-il valide ?"}
D -->|Oui| T["Écrire dans l'Inbox de la tâche concernée"]
D -->|Non| F["Prévenir l'utilisateur ou choisir une autre cible"]
C --> U["Réévaluer après la réponse de l'utilisateur"]
U --> R
Figure 4 : le flux de routage des messages de Lomo Agent.
3.2 Message livré ne veut pas dire message exécuté
Le routage des messages comporte un autre détail souvent négligé : le moment où un message est attribué à un Worker et celui où ce Worker commence réellement à le traiter peuvent différer.
Par exemple, l’Agent est en pleine génération vidéo quand l’utilisateur ajoute : « Mets le décor en intérieur. »
La requête de génération vidéo a peut-être déjà été soumise au service en amont et ne peut plus être modifiée sur-le-champ. Le système doit d’abord enregistrer cette nouvelle instruction, puis, lorsque le Worker atteint une frontière d’exécution appropriée, décider de la suite en fonction de l’avancement en cours.
Nous maintenons donc pour chaque tâche une boîte de réception de messages (Inbox) persistante. Le Router se charge de déposer le message dans la bonne boîte ; le runtime décide quand la lire, comment fusionner son contenu dans le contexte, et s’il faut ajuster le plan existant.
Il faut encore distinguer plusieurs cas :
-
un message pas encore consommé par le Worker peut, sous certaines conditions, être retiré ou remplacé ;
-
un message déjà entré dans l’inférence du modèle doit attendre la frontière d’exécution en cours ;
-
pour une requête de génération externe déjà soumise, aucun retrait direct n’est possible à ce jour. La nouvelle instruction doit attendre que le Worker atteigne une frontière d’exécution appropriée ; il décide alors, selon l’avancement de la tâche, s’il faut régénérer ou appliquer la nouvelle contrainte aux étapes suivantes.
Cette séparation pose aussi les bases de la reprise après interruption, des tâches asynchrones et de la possibilité pour l’utilisateur d’intervenir à tout moment.
3.3 S’inspirer des outils collaboratifs pour l’attribution des messages
Côté conception de l’interaction, nous nous sommes aussi inspirés de la façon dont des outils collaboratifs comme Slack ou Feishu (Lark) organisent les messages.
Prenons les Threads (fils de réponses) : quand l’utilisateur répond dans un fil donné, le système sait naturellement à quelle discussion appartient le message, sans avoir à réanalyser toute la conversation. L’avantage de ce rattachement explicite : il est stable, prévisible, et ne coûte quasiment rien en reconnaissance sémantique.
Les scénarios d’interaction de Lomo Agent sont toutefois plus souples. L’utilisateur peut répondre directement sur une carte de tâche, ou revenir dans le fil principal et évoquer en passant un travail d’il y a quelques jours.
Nous avons donc conservé les deux approches : pour les opérations explicites sur une tâche, un routage direct par identité structurée ; pour le langage naturel du fil principal, un rattachement aux tâches passées effectué par le Router.
Cette stratégie hybride réduit le nombre de fois où le modèle intervient dans le routage, sans pouvoir éliminer toute ambiguïté. Si l’utilisateur travaille par exemple sur deux séries d’affiches en même temps et se contente de dire « Modifie la série de la dernière fois », le système n’a pas forcément assez d’éléments pour identifier la cible. Poser la question est alors généralement plus fiable que de choisir soi-même la tâche qui semble la plus pertinente.
À mesure que le nombre de tâches croît, trouver le bon équilibre entre rappel, précision du routage et coût d’interaction reste un chantier à optimiser en continu à partir des usages réels.
L’attribution n’est toutefois pas le dernier contrôle de la chaîne. Une tâche créée en trop peut généralement être fusionnée ou fermée ; en revanche, si un message est rattaché par erreur à une modification d’œuvre, à une suppression de ressource ou à une génération payante, le coût peut être bien plus lourd. Nous séparons donc le routage des messages de l’autorisation d’exécution proprement dite : le premier détermine à quelle tâche confier le travail, la seconde vérifie toujours, de façon indépendante, les permissions et les limites de coût.
Par exemple, les opérations irréversibles comme la suppression de ressources ou l’oubli de souvenirs exigent à chaque fois la confirmation de l’utilisateur ; pour une génération payante, le système présente d’abord un devis, et ne demande confirmation qu’en cas de dépassement du seuil sans confirmation, du plafond quotidien sans confirmation ou du budget du plan approuvé. Les opérations payantes lancées via l’API ouverte sont elles aussi confirmées une à une. Même lorsque le Router a identifié la bonne tâche, cela ne confère aucun droit d’exécution au-delà de ce que l’utilisateur a autorisé.
Même une fois le message arrivé dans la bonne tâche, l’exécution peut s’étendre sur plusieurs minutes, voire plusieurs jours, et subir un redémarrage de processus, un timeout réseau ou une annulation par l’utilisateur. La reprise fiable relève du mécanisme d’exécution durable.
4. Exécution durable : reprise après interruption, nouvelles tentatives et contrôle de la concurrence
Une fois les messages correctement routés vers les tâches, il faut garantir la fiabilité de l’exécution elle-même.
Pour un Chat Completion ordinaire, une requête aboutit généralement en quelques secondes à quelques dizaines de secondes. Le fonctionnement d’un Agent créatif est bien plus complexe : une production vidéo peut impliquer plusieurs cycles d’inférence, des dizaines d’appels d’outils et plusieurs générations externes de plusieurs minutes chacune. Pendant ce temps, l’utilisateur peut à tout moment modifier sa demande ou arrêter la tâche.
Prenons un scénario de panne très courant : l’Agent soumet une requête au service de génération vidéo, la tâche est bel et bien créée en amont, mais le processus du service redémarre juste avant de recevoir la réponse.
Que faire à la reprise ? Relancer directement la requête risque de générer en double et de facturer une seconde fois ; considérer qu’elle a réussi risque de faire attendre indéfiniment une tâche qui n’a en réalité jamais été créée.
Ce type de problème nous a fait comprendre que l’exécution d’un Agent a besoin d’un état durable, indépendant du contexte du modèle et du processus de service.
J’ai donc introduit dans Lomo Agent un mécanisme d’exécution durable (Durable Execution). La base de données enregistre les états d’exécution clés, les décisions du modèle et les résultats des appels d’outils ; la file de tâches assure l’ordonnancement et la reprise ; les événements temps réel servent à informer l’utilisateur de l’avancement. Même si le processus d’exécution est interrompu, le système peut déterminer la suite à partir des faits enregistrés.
Sur le plan conceptuel, nous nous sommes inspirés de l’approche Durable Execution de Temporal. Grâce à l’historique des workflows, à l’ordonnancement des tâches et aux mécanismes de nouvelle tentative, Temporal permet aux processus métier de survivre au cycle de vie d’un processus unique, un atout particulièrement net pour les workflows complexes et de longue durée.
Un moteur de workflow générique a toutefois ses propres coûts de modélisation et d’exploitation. L’exécution de Lomo Agent doit en outre gérer l’inférence du modèle, des appels d’outils dynamiques, l’état de la création et les interactions avec l’utilisateur ; nous avons donc implémenté un journal d’exécution et un mécanisme d’ordonnancement par événements autour des structures de données propres à l’Agent. Nous avons repris l’idée d’ingénierie de l’exécution durable, ce qui ne veut pas dire que nous utilisons directement la solution de Temporal.
4.1 Persister la décision d’abord, exécuter l’appel d’outil ensuite
Une boucle d’Agent (Agent Loop) classique suit généralement un enchaînement simple :
lire le contexte → inférence du modèle → appel d’outil → récupération du résultat → poursuite de l’inférence.
Pour des tâches courtes, cette implémentation est relativement directe. Mais dès que l’exécution traverse plusieurs processus, services externes et systèmes de facturation, une interruption à n’importe quelle étape peut produire un état incohérent.
Nous découpons l’exécution en une série de Steps. Chaque Step peut contenir une inférence du modèle et l’ensemble des appels d’outils qu’il propose.
Avant d’exécuter les outils, le runtime commence par persister la décision du modèle et les appels en attente. Chaque appel d’outil possède un identifiant métier stable, et son état, ses entrées et son résultat sont consignés dans le journal d’exécution.
Ainsi, à la reprise, le Worker peut déterminer d’après le journal quelles opérations sont terminées, lesquelles peuvent être retentées, et lesquelles exigent d’abord de vérifier l’état externe, sans avoir à rappeler le modèle pour regénérer les mêmes décisions d’outils.
flowchart TD
S["Lire l'Inbox et l'état d'exécution courant"] --> L["Inférence du modèle : réponse et intentions d'appels d'outils"]
L --> P["Valider atomiquement le résultat du modèle et les appels en attente"]
P --> X{"L'appel a-t-il déjà un résultat persisté ?"}
X -->|Oui| REUSE["Réutiliser le résultat existant"]
X -->|Non| K{"A-t-il pu être exécuté auparavant ?"}
K -->|Non| EXEC["Soumettre l'appel d'outil"]
K -->|Oui| I{"Une nouvelle tentative idempotente est-elle possible ?"}
I -->|Oui| EXEC
I -->|Non| UNKNOWN["Marquer le résultat inconnu, rendre la main au modèle"]
EXEC --> SAVE["Persister le résultat de l'outil et l'état métier"]
REUSE --> NEXT["Passer à l'appel ou au Step suivant"]
SAVE --> NEXT
UNKNOWN --> CHECK["Interroger d'abord l'état courant, puis décider d'une nouvelle tentative"]
CHECK --> NEXT
Figure 5 : le flux d’exécution durable de l’Agent.
Un concept clé entre ici en jeu : l’idempotence (Idempotency).
Supposons que l’utilisateur approuve la génération d’une vidéo, et que le système soumette deux fois la même requête à cause d’un timeout réseau. Idéalement, les deux soumissions doivent correspondre à une seule et même commande de génération : pas deux vidéos identiques, et encore moins une double facturation.
Pour les services en aval qui prennent en charge une clé d’idempotence, on peut dédoublonner à l’aide d’un identifiant d’appel stable ; pour les commandes et la facturation internes à la plateforme, l’ID d’appel, l’ID de commande et des contraintes d’unicité garantissent qu’une requête répétée ne produit aucun nouvel effet métier.
Mais si l’API de génération en amont ne prend pas en charge de clé d’idempotence et ne permet pas non plus d’interroger le résultat d’une requête, le système n’a en réalité aucun moyen de savoir, après un timeout réseau, si la tâche a été créée.
Dans ce cas, l’exécution durable ne peut pas fournir par magie une garantie Exactly-once (exécution exactement une fois).
Il faut encore distinguer deux situations. Pour les quelques outils non idempotents, si le journal d’exécution a déjà enregistré started sans jamais obtenir de résultat, le runtime ne réexécute pas directement l’appel : il transmet au modèle un résultat marqué outcome_unknown, pour qu’il vérifie d’abord l’état courant avant de décider de la suite. Les appels idempotents, eux, peuvent être rejoués en conservant l’identifiant d’appel d’origine.
Une autre couche d’incertitude se situe entre le service de génération et l’API en amont. Nous y adoptons une stratégie plus prudente : si le résultat de la soumission est incertain, la soumission est considérée comme un échec, et aucune nouvelle requête n’est envoyée automatiquement en amont. Les deux cas semblent relever du même « résultat inconnu », mais l’un concerne la reprise des appels d’outils de l’Agent, l’autre vise à éviter de créer en double des tâches de génération réellement facturées.
Temporal se heurte au même problème avec les nouvelles tentatives d’Activity : un moteur de workflow sait ordonnancer et relancer l’exécution de façon fiable, mais les effets de bord des API externes exigent toujours un mécanisme d’idempotence côté métier.
Pour les services de génération payants, cette frontière est particulièrement importante : un échec d’exécution se rattrape, une double facturation ne se compense pas forcément automatiquement.
4.2 Reprise par un autre Worker et Fencing Token
L’exécution durable règle la reprise après une interruption de processus, mais il existe un cas plus insidieux : l’ancien Worker n’est peut-être pas vraiment arrêté.
Par exemple, le Worker A perd temporairement la connexion en cours d’exécution à cause d’une instabilité réseau. Le système détecte le timeout et confie la tâche au Worker B, qui poursuit l’exécution. Quelques secondes plus tard, le Worker A retrouve la connexion et tente de valider le résultat qu’il vient d’obtenir.
Deux Workers peuvent alors coexister dans le système, chacun persuadé de détenir le droit d’exécution. Sans garde-fou, l’ancien Worker risque d’écraser le nouvel état, voire de relancer dans le circuit d’exécution une tâche déjà arrêtée.
Nous utilisons pour cela un mécanisme de génération d’exécution comparable à un Fencing Token (jeton de clôture).
Chaque fois qu’une tâche est reprise par un nouvel exécutant, le runtime incrémente la génération d’exécution de cette tâche. Pour valider un changement d’état, un Worker doit présenter la génération qu’il détient, qui est alors vérifiée.
Si la génération enregistrée en base a déjà changé, la validation de l’ancien Worker est rejetée.
On peut résumer ce mécanisme ainsi : le système autorise un nouvel exécutant à reprendre le travail, tout en retirant à l’ancien le droit de continuer à modifier l’état interne.
Lorsque l’utilisateur arrête volontairement une tâche, l’état d’exécution et la vérification de la génération empêchent de même l’ancien Worker de reprendre sa progression une fois rétabli.
Le Fencing Token protège cependant la cohérence de l’état propre au système. Si l’ancien Worker a déjà envoyé une requête de génération vidéo à une plateforme externe, l’invalidation de son droit d’exécution interne n’annule pas automatiquement l’opération distante.
C’est pourquoi, même après l’arrêt d’une tâche par l’utilisateur, une génération externe soumise auparavant peut continuer de tourner. Le service de génération continue de suivre son résultat et procède au règlement des frais correspondants, mais un événement de fin retardataire ne réveille pas la tâche arrêtée et ne modifie pas l’état d’exécution déjà annulé.
4.3 Traiter les nouveaux messages de l’utilisateur en cours d’exécution
Une autre catégorie de problèmes vient de l’intervention en temps réel de l’utilisateur.
Supposons que l’Agent soit en train d’appeler le modèle pour générer une série d’affiches. L’utilisateur envoie alors : « Pas de fond blanc, mets du bleu nuit. »
Ce message peut arriver sur le serveur avant la fin de l’inférence, sans que la requête en cours au modèle l’ait réellement lu.
Si le système se contentait d’assembler l’historique selon l’heure de création des messages, on pourrait aboutir à un état incohérent : le modèle semblerait avoir pris sa décision après la modification demandée par l’utilisateur, alors qu’il ignorait tout de cette nouvelle instruction.
Pour résoudre ce problème, nous traitons séparément la réception et la consommation des messages.
Un nouveau message entre d’abord dans l’Inbox persistante de la tâche concernée. Le Worker lit ces entrées à la prochaine frontière d’exécution sûre, et les intègre au contexte du modèle avec l’état courant de la tâche.
Le système enregistre le Step au cours duquel chaque message a effectivement été consommé, au lieu de déduire l’ordre des événements de la seule heure d’envoi.
On distingue ainsi clairement plusieurs étapes : l’instruction de l’utilisateur a été livrée, le modèle l’a comprise, l’opération correspondante a été exécutée.
Bien sûr, si une requête de génération a déjà été soumise en amont, une modification ultérieure ne change pas automatiquement le contenu en cours de génération. La version actuelle ne permet pas non plus de retirer ce type de requête : le Worker ne peut que décider, à la prochaine frontière d’exécution sûre, s’il faut régénérer ou appliquer la nouvelle contrainte aux étapes suivantes. Si l’utilisateur a déjà arrêté la tâche, l’ancienne génération, même si elle se termine ensuite, ne relancera pas l’exécution de la tâche.
Côté utilisateur, l’interface doit elle aussi refléter fidèlement cet état, pour éviter de confondre « message reçu » avec « génération en cours ajustée selon la nouvelle demande ».
Pour les générations longues (vidéo, audio, 3D), un Worker doit en outre libérer ses ressources d’exécution pendant qu’il attend un résultat externe, puis reprendre précisément à l’arrivée de l’événement.
4.4 Journal d’exécution et limites de cohérence de l’ordonnancement
Nous avons vu comment persister les décisions du modèle, et comment la génération d’exécution empêche un ancien Worker de continuer à modifier l’état après la reprise de la tâche. Mais en pratique, un autre problème passe facilement inaperçu : si la base de données a déjà enregistré un travail à exécuter, mais que le message de file chargé de réveiller le Worker s’est perdu, comment la tâche reprend-elle ?
Dans Lomo Agent, l’état d’exécution est maintenu par les tours (Turn) et le journal des appels d’outils. Un tour enregistre l’état d’exécution, la génération d’exécution, le bail et le point de reprise (Checkpoint) ; chaque appel d’outil a pour identifiant stable « tour + numéro de Step + numéro d’appel ». Au retour du modèle, le runtime enregistre dans une même transaction le résultat de la décision, les appels en attente et les messages de l’Inbox consommés ; avant d’exécuter un outil, il consigne started, puis, une fois l’appel terminé, enregistre le résultat avec l’état done. À la reprise, il peut ainsi réutiliser les résultats ou retenter sans risque d’après les enregistrements existants, sans demander au modèle de deviner ce qui s’est passé. Pour les appels qui ne peuvent pas être retentés sans risque, l’état « résultat inconnu » est conservé et laissé aux décisions suivantes.
Pour l’ordonnancement, nous n’avons pas introduit d’Outbox transactionnel pour la mise en file des tours : la base de données fait foi pour les faits d’exécution, et la file sert surtout à réveiller le Worker. Le message d’ordonnancement est envoyé après la validation du tour ; si la mise en file échoue, un chien de garde (watchdog) en arrière-plan redécouvre la tâche à partir de l’état de la base. Aujourd’hui, il effectue une vérification toutes les 10 secondes, en surveillant surtout les baux expirés, les attentes prolongées en file et les tâches qui ont des messages non traités sans tour actif. Les jobs de la file sont identifiés par l’ID du tour et la génération d’exécution, ce qui limite les mises en file en double pour une même génération ; et même en cas d’ordonnancement en double, le runtime empêche toute écriture périmée grâce à la vérification de l’état d’exécution et de la génération. Nous acceptons ainsi un délai de reprise limité en échange d’un mécanisme d’ordonnancement plus simple ; la diffusion des événements vers l’interface et les tâches de génération externes conservent chacune leurs propres mécanismes de livraison fiable et de compensation.
Un autre risque vient de l’exécution concurrente d’un ancien et d’un nouveau Worker. Le Fencing Token empêche l’ancien Worker d’écraser l’état interne, mais ne peut pas annuler une requête externe déjà envoyée. Nous fixons donc la clé d’idempotence des générations payantes à « tour + numéro de Step + numéro d’appel », sans y inclure la génération d’exécution. Même si l’ancien et le nouveau Worker soumettent deux fois le même appel, la couche de génération de la plateforme le rattache à une seule et même commande grâce à une contrainte d’unicité sur « espace de travail + clé d’idempotence », et dédoublonne le débit interne. Cela permet de maîtriser les commandes et la facturation en double au sein de la plateforme, sans pour autant que le service externe en amont offre de lui-même une garantie Exactly-once. En revanche, une panne survenue avant que la décision du modèle soit persistée peut entraîner une nouvelle inférence de ce Step. Compte tenu de notre structure de coûts actuelle, dominée par la génération de médias, nous avons choisi d’accepter ce coût d’inférence en double, relativement limité.
Le cas le plus délicat est celui où le résultat de la soumission en amont est incertain. Un timeout de requête peut signifier que la soumission a échoué, ou bien que la tâche a été créée en amont sans que la réponse ait pu revenir. Pour éviter les générations en double, le service de génération actuel ne resoumet pas automatiquement ce type de requête ; dès qu’il obtient l’ID de tâche en amont, il le persiste immédiatement, pour pouvoir reprendre le polling après une interruption. Mais si le processus plante précisément entre l’obtention de l’ID et son enregistrement, il n’est pas encore possible de retrouver cette tâche. Le système ne peut alors que procéder au remboursement selon les règles après le timeout, la plateforme prenant à sa charge les frais en amont éventuellement engagés.
Ces choix n’éliminent pas toutes les fenêtres de panne, mais ils fixent clairement les limites de traitement selon les cas : ce dont l’achèvement est confirmé est réutilisé directement, ce qui peut être retenté sans risque continue de s’exécuter, et ce qui ne peut être confirmé n’est pas resoumis à l’aveugle. Pour un Agent qui tourne dans la durée, la fiabilité ne signifie pas qu’aucune étape n’échoue jamais, mais qu’après une interruption, le système peut encore prendre des décisions de reprise maîtrisées à partir des faits existants.
5. Génération asynchrone : cycle de vie des tâches et réveil par événement
L’exécution durable présentée plus haut répond essentiellement à la question de savoir comment l’Agent enregistre son état, gère les pannes et reprend son exécution. Mais la création IA présente une autre caractéristique incontournable : une grande partie des appels d’outils sont asynchrones par nature.
Par exemple, une génération d’image peut ne prendre que quelques secondes, quand une tâche vidéo ou 3D peut durer plusieurs minutes, voire davantage. Après soumission, le service en amont ne renvoie généralement qu’un ID de tâche ; le résultat réel s’obtient par polling ou par callback.
Laisser le Worker attendre la fin de la tâche externe immobiliserait des ressources d’exécution, et couplerait l’inférence du modèle à des durées de génération externes incontrôlables.
Nous gérons donc séparément le cycle de vie de l’exécution de l’Agent et le cycle de vie des tâches de génération externes.
5.1 Soumettre, attendre, reprendre l’exécution
Lorsque le Worker appelle un outil de génération, l’outil soumet d’abord la requête au service concerné et renvoie l’identifiant de la tâche de génération. Le runtime enregistre ensuite la relation entre cette tâche et la tâche Agent en cours : ID de la tâche, état d’attente et informations nécessaires à la suite de l’exécution.
Une fois la soumission faite, le Worker peut suspendre son exécution : nul besoin d’appeler le modèle en continu ni de maintenir une requête d’inférence de longue durée.
Pendant l’attente, l’avancement du service de génération peut être poussé directement au front-end sous forme d’événements métier, et la carte de tâche affiche l’état courant.
Une fois la génération terminée, le runtime décide de réveiller ou non le Worker, selon que les étapes suivantes dépendent ou non du résultat.
sequenceDiagram
participant W as Task Worker
participant G as Service de génération
participant S as Runtime durable
participant UI as Carte de tâche
W->>G: Soumettre la requête de génération (identifiant métier stable)
G-->>W: Renvoyer l'ID de tâche
W->>S: Enregistrer le lien de tâche et l'état d'attente
Note over W,S: Ci-dessus le chemin normal, pour un échec d'enregistrement après soumission voir 4.4
S-->>UI: Passer à « génération en cours »
Note over W: Worker suspendu, ressources d'exécution libérées
G-->>S: Événement de progression
S-->>UI: Mettre à jour la progression (sans inférence du modèle)
G-->>S: Événement de fin de génération
S->>S: Mettre à jour l'état de la tâche et enregistrer les productions
S-->>UI: Afficher le résultat de génération
alt Les étapes suivantes dépendent du résultat
S->>W: Envoyer l'événement de reprise
W->>W: Lire le dernier état et poursuivre
else Aucune inférence nécessaire pour l'instant
Note over UI: Mettre à jour seulement le résultat et l'état de la tâche
end
Figure 6 : le flux d’exécution d’une tâche de génération asynchrone (chemin normal).
Premier bénéfice direct de cette conception : une meilleure utilisation des ressources. Pendant les quelques minutes d’attente d’une génération vidéo, le système n’a qu’à maintenir l’état durable et traiter les événements nécessaires, sans mobiliser en continu des ressources d’inférence de l’Agent.
Par ailleurs, la progression de la génération et l’interface utilisateur peuvent être mises à jour indépendamment : l’utilisateur voit l’état de la tâche en temps réel, sans attendre que le Worker rappelle le modèle.
5.2 Les événements asynchrones exigent eux aussi un traitement fiable
Une fois les tâches de génération passées en mode événementiel, encore faut-il se soucier de la fiabilité des événements eux-mêmes.
Par exemple, un service en amont peut envoyer plusieurs fois la même notification de fin à cause d’un problème réseau, ou être incapable de réessayer immédiatement après l’échec d’un callback. Quand plusieurs générations tournent en parallèle, les événements de progression peuvent en outre arriver dans le désordre.
Le traitement des événements ne peut donc pas se résumer à « callback reçu, on continue ».
Il faut s’appuyer sur l’état persisté de la tâche pour mettre à jour les tâches de génération, et garantir qu’un événement en double ne provoque ni écriture en double, ni double facturation, ni double réveil du Worker.
Pour les événements susceptibles d’arriver dans le désordre, il faut aussi empêcher qu’une ancienne progression écrase le résultat final. Si une tâche est déjà terminée et qu’arrive ensuite une notification « génération en cours » retardée, l’état ne doit pas revenir en arrière.
Par ailleurs, les callbacks externes ne sont pas toujours fiables. Pour une tâche restée longtemps sans résultat, on peut vérifier son état à l’aide de l’ID de tâche et de l’API de consultation en amont, et, au besoin, rétablir l’avancement par un polling compensatoire.
Tout cela fonctionne de concert avec la Durable Execution décrite plus haut : le runtime maintient un état d’exécution fiable, tandis que le système de tâches asynchrones transforme les changements survenus pendant la génération externe en événements métier récupérables.
5.3 Quand faut-il réveiller le Worker ?
Une autre question touche directement au coût des appels au modèle : faut-il rappeler le modèle à chaque fin de génération ?
Tout dépend de ce qui reste à faire, et de la part qui exige une décision de l’Agent.
Si l’utilisateur a seulement demandé « Génère une image de chat », il suffit d’afficher le résultat une fois la génération terminée : inutile de réveiller le Worker pour que le modèle décrive une image déjà générée.
Mais si l’utilisateur demande « Réalise une série d’affiches de marque au style homogène », le résultat courant peut servir d’entrée aux étapes suivantes. L’Agent doit alors examiner les productions existantes, lire la liste d’exécution et décider, au regard de l’objectif courant, s’il poursuit la génération ou attend la validation de l’utilisateur.
Nous traitons donc séparément l’événement de fin de génération et la décision de relance de l’Agent.
Le service de génération rapporte le résultat ; le runtime juge, d’après le plan de la tâche, les relations d’attente, les autorisations et l’état d’exécution courant, s’il faut reprendre le Worker.
On évite aussi de confier au modèle une masse d’événements de progression. Une tâche vidéo de plusieurs minutes peut générer de nombreux changements d’état en amont, mais l’immense majorité ne demande qu’une mise à jour du front-end et ne justifie pas une nouvelle inférence.
Cette conception événementielle est proche des mécanismes d’attente asynchrone des moteurs de workflow génériques et des files de tâches. Pour Lomo Agent, nous nous sommes surtout attachés à la combiner avec l’objectif créatif, la liste d’exécution et le processus de relecture par l’utilisateur.
Par exemple, une fois un plan vidéo généré, s’il reste d’autres plans à produire et que l’utilisateur a autorisé la poursuite, le runtime peut reprendre la tâche correspondante ; si l’étape en cours exige une relecture par l’utilisateur, la tâche doit rester en attente de validation.
La fin d’une génération signifie seulement qu’un appel d’outil s’est achevé. Quant à savoir si toute la tâche créative doit se poursuivre, il faut en juger à la lumière du plan, du budget et des autorisations.
Ce qui nous amène au mécanisme de progression autonome : lorsque l’Agent termine une étape, voire s’apprête à clore son cycle d’inférence, comment le système juge-t-il si la tâche est vraiment terminée et s’il faut poursuivre l’exécution ?
6. Ingénierie du contexte : maintenir un état utile dans une fenêtre limitée
À mesure que l’Agent prend en charge des tâches créatives longues et en plusieurs phases, la gestion du contexte devient un facteur déterminant pour la qualité d’exécution comme pour le coût.
Une tâche créative qui s’étend sur plusieurs jours peut accumuler des centaines de messages, des dizaines d’appels d’outils, ainsi qu’une foule d’états d’images, de vidéos et de workflows. Même avec des fenêtres de contexte toujours plus grandes, tout faire entrer dans chaque inférence n’est pas réaliste.
D’un côté, un contexte long augmente le coût en tokens et la latence d’inférence ; de l’autre, un excès d’historique peut brouiller le jugement du modèle sur l’objectif courant. Surtout lorsque l’utilisateur a modifié plusieurs fois sa demande : des contraintes anciennes et devenues caduques, si elles traînent encore dans le contexte, peuvent facilement fausser les générations suivantes.
Nous traitons donc la gestion du contexte comme un problème d’ingénierie à part entière : à chaque étape d’inférence, comment fournir au modèle des informations suffisantes, pertinentes et toujours valides ?
6.1 Contexte en couches et Prompt Cache
Lomo Agent organise le contexte du modèle en plusieurs couches : un préfixe système relativement stable, un historique compacté, les interactions récentes, et l’état dynamique requis par l’étape d’exécution en cours.
flowchart LR
A["Préfixe stable : règles système / outils de base / instantané du profil"] --> B["Résumés d'historique par segments"]
B --> C["Messages récents et résultats d'outils clés"]
C --> D["Queue dynamique : brief / liste / budget / changements d'objets"]
D --> E["Entrée de l'étape et rappel à la demande"]
Figure 7 : l’organisation du contexte dans Lomo Agent.
Un facteur pèse directement sur le coût réel de l’inférence : le Prompt Caching (mise en cache des prompts). Dans la suite, « Prompt Cache » désigne la capacité de mise en cache des prompts que les fournisseurs de modèles proposent au niveau de l’API ; sa mise en œuvre concrète et ce qui peut être réutilisé dépendent de chaque fournisseur.
Les services de modèles qui prennent en charge cette capacité peuvent réutiliser les préfixes de prompt conformes à leurs règles de mise en cache, et réduire ainsi le coût des traitements redondants. Nous gardons donc autant que possible les règles système, les outils de base et les informations de profil relativement stables en tête de contexte, et plaçons en fin de contexte l’état de la tâche, susceptible de changer à chaque étape.
Insérer des informations très dynamiques au début du préfixe empêcherait le contenu stable qui suit de profiter efficacement du cache.
Le taux de succès réel du Prompt Cache dépend toutefois aussi du fournisseur de modèle, du routage des requêtes et de la politique de cache. La disposition du contexte ne fait que créer les conditions de la réutilisation ; le gain final doit être validé sur des données d’appels réels.
Par ailleurs, un préfixe stable ne signifie pas que le profil peut rester longtemps sans mise à jour. Lorsque l’utilisateur modifie ses préférences ou demande d’oublier certaines informations, l’instantané de profil concerné doit être invalidé sans délai, pour éviter que la réutilisation du cache ne réinjecte des informations périmées dans l’inférence.
6.2 Chargement des outils à la demande : plus de capacités, un contexte plus coûteux
Lomote AI intègre de nombreux modèles et outils de création couvrant des domaines variés : génération d’images, génération de vidéos, 3D, gestion des ressources, édition de documents, workflows Flows, etc.
Placer le nom, les paramètres et le mode d’emploi de tous les outils dans chaque requête au modèle augmenterait la consommation de tokens, et pourrait en plus pousser le modèle à se tromper parmi de nombreux outils similaires.
Nous répartissons donc les outils en deux catégories : des outils de base, visibles en permanence, et des outils chargés dynamiquement par domaine de capacité.
Par exemple, quand l’Agent planifie un storyboard, il n’a peut-être besoin que des outils liés aux documents et aux ressources ; en passant à la production vidéo, il charge alors les capacités de génération vidéo et de workflow.
Pour les domaines d’outils devenus inactifs, on peut retirer leurs définitions détaillées à une frontière de compaction du contexte, afin de limiter l’influence d’informations non pertinentes sur les inférences suivantes.
Cette conception a aussi un prix. Quand l’ensemble d’outils change, la structure du contexte du modèle peut changer avec lui, ce qui affecte le taux de succès du Prompt Cache ; et les appels d’outils déjà présents dans l’historique peuvent ne plus correspondre exactement aux définitions d’outils visibles à cet instant.
Sur ce point, la pratique de Context Engineering de Manus nous offre un contre-exemple qui mérite réflexion.
Manus préfère garder des définitions d’outils stables, et réduire la complexité du choix en restreignant les actions disponibles à un instant donné. Cela aide à limiter l’invalidation du préfixe causée par les changements de définitions d’outils, et facilite la cohérence entre les appels d’outils passés et le contexte courant. Il s’agit ici de l’effet d’un contexte stable sur la réutilisation du cache ; le Prompt Caching au niveau de l’API n’est pas assimilé au mécanisme de KV Cache interne à l’inférence du modèle.
Si Lomo Agent a choisi un chargement par domaine, c’est surtout parce que les types d’outils et les structures de paramètres d’une plateforme de création sont assez volumineux : conserver en permanence toutes les définitions d’outils risquerait d’alourdir sensiblement le contexte.
Chaque stratégie a ses conditions d’application : un ensemble d’outils stable favorise le cache et la cohérence du comportement, tandis que le chargement dynamique peut réduire le coût en tokens et les interférences de choix dues aux outils non pertinents.
Pour notre approche, l’effet réel doit être évalué en continu en croisant fréquence de changement d’outils, taux de succès du cache, précision des appels d’outils et coût global d’inférence. Par ailleurs, la présence d’un outil dans le contexte du modèle n’affecte que ce que le modèle peut choisir à cet instant. Le véritable droit d’exécuter un outil reste contrôlé côté serveur : la visibilité des outils dans le prompt ne peut pas servir de frontière de sécurité.
6.3 Compaction de l’historique : au-delà du résumé, garder la possibilité de relire
Au fil d’une exécution longue, les messages et résultats d’outils s’accumulent. Même avec une grande fenêtre de contexte, il faut compacter à un moment opportun.
L’approche courante consiste à faire résumer sans cesse l’historique par le modèle, puis à poursuivre la conversation à partir du dernier résumé. Elle pose un problème : à force de compactions successives, le résumé perd peu à peu des détails, voire fige en faits historiques les propres suppositions du modèle.
Nous procédons donc par résumés segmentés : l’historique plus ancien est compacté par tranches, tandis que les interactions récentes sont conservées intégralement.
Les nouveaux résumés sont autant que possible ajoutés à la suite de la séquence existante, au lieu de réécrire à chaque fois tout l’historique.
Pour les sorties d’outils volumineuses (listes de ressources, informations sur les nœuds d’un workflow, résultats de génération complexes…), le système peut ne garder dans le contexte courant qu’un résumé utile, et stocker le contenu complet à l’extérieur.
Lorsque l’Agent a ensuite besoin de vérifier un paramètre précis ou un détail de l’historique, il récupère l’information pertinente par recherche ou par lecture paginée.
On retrouve là une idée commune avec Manus, qui préconise d’utiliser le système de fichiers comme support de contexte externe : le modèle n’a pas à transporter en permanence toutes les informations, mais doit pouvoir retrouver, au besoin, une source d’origine fiable.
Une distinction mérite d’être soulignée : le résumé sert à alléger le contexte de l’inférence courante ; il ne doit pas devenir l’unique source de vérité sur l’historique. En particulier, lorsque l’utilisateur a corrigé une information ou qu’une contrainte passée n’est plus valable, le système doit pouvoir en localiser la source, pour éviter qu’un ancien résumé continue d’influencer la suite de l’exécution.
6.4 Garder la tâche en cours dans le champ de vision du modèle
La compaction du contexte règle le problème d’un historique trop long, mais une autre question est tout aussi importante : à mi-parcours, l’Agent sait-il encore clairement quel objectif il poursuit ?
Sur les tâches longues, le modèle peut, après de nombreux appels d’outils, dériver peu à peu de l’objectif initial. Par exemple, l’utilisateur demande une série complète d’affiches de marque, et l’Agent consacre l’essentiel de ses efforts à peaufiner encore et encore une seule image de référence, au détriment du livrable d’ensemble.
Nous fournissons donc en continu, dans le contexte dynamique du Worker, le brief courant, la liste d’exécution, les contraintes clés, l’état d’attente et les dernières modifications des œuvres concernées.
Ces états proviennent du runtime durable, et non d’un simple résumé, par le modèle, des échanges précédents.
Par exemple :
-
le brief consigne la direction visuelle finalement approuvée par l’utilisateur ;
-
la liste d’exécution indique quelles étapes sont terminées et lesquelles restent à faire ;
-
l’état de l’espace de travail indique au modèle si une ressource a été remplacée par l’utilisateur, et dans quelle version se trouve le document courant.
Lorsque l’utilisateur modifie directement un workflow Flows ou un document partagé, le cycle d’exécution suivant reçoit également une notification de changement d’objet, et peut lire la dernière version au besoin.
Ainsi, même si une partie de l’historique a été compactée, l’Agent peut continuer à travailler en s’appuyant sur un état de tâche fiable.
Sur ce point, Manus nous a aussi beaucoup inspirés en réaffichant en permanence une liste de choses à faire pour maintenir l’attention du modèle sur l’objectif. Lomo Agent va plus loin en couplant ces listes à l’état du runtime : elles fournissent du contexte au modèle, mais servent aussi à décider de la relance automatique et de l’arrêt. Nous y reviendrons en détail dans le chapitre sur la progression autonome.
6.5 Les arbitrages concrets de l’ingénierie du contexte
Au bout du compte, l’ingénierie du contexte de Lomo Agent poursuit trois objectifs :
-
réduire les informations non pertinentes ;
-
maîtriser le coût d’inférence ;
-
garantir que les informations sur lesquelles repose la décision courante sont toujours valides.
Mais ces objectifs ne vont pas toujours dans le même sens.
Une compaction plus agressive réduit la consommation de tokens, mais risque d’omettre des détails importants. Le chargement dynamique des outils réduit l’espace de choix, mais peut nuire au taux de succès du cache. Conserver durablement un profil stable renforce la continuité, mais impose de gérer les changements de préférences et la révocation de la mémoire.
Il est donc difficile de tout régler avec une stratégie unique et figée. La gestion du contexte doit s’ajuster de concert avec l’état des tâches, le système de mémoire, les permissions des outils et le comportement réel des modèles.
Pour Lomo Agent, ce qui compte avant tout, c’est que le modèle trouve la bonne information au bon moment et poursuive son travail sur la base d’un état de tâche valide. Mais lorsque la collaboration s’étend sur plusieurs tâches, plusieurs jours, voire plusieurs mois, la gestion du contexte interne à chaque tâche ne suffit plus. Les préférences que l’utilisateur a forgées au fil du temps, ses projets passés et les décisions qu’il a prises doivent être organisés et retrouvés par un autre mécanisme.
7. Ingénierie de la mémoire : provenance, versions et révocabilité
Le chapitre précédent traitait de l’organisation de l’information dans une fenêtre de contexte limitée. Mais pour un Agent qui collabore dans la durée, une question plus profonde se pose : lorsqu’un échange est sorti du contexte courant, voire que la tâche associée est terminée depuis longtemps, de quoi le système doit-il se souvenir ?
Dans les scénarios créatifs de Lomo Agent, un utilisateur peut se servir de la plateforme pendant des mois et se forger peu à peu des préférences visuelles assez stables, des processus de création habituels et une charte de marque. On ne devrait pas lui demander de les redécrire à chaque fois.
En même temps, les préférences évoluent. Un style adopté temporairement pour un projet ne vaut pas forcément pour toutes les créations suivantes ; une charte de marque valable hier peut devenir totalement caduque après une refonte.
Nous voulons donc que la mémoire à long terme assure une continuité d’une tâche à l’autre et dans le temps, tout en conservant la provenance et le domaine de validité de chaque fait.
7.1 Des messages passés à la mémoire à long terme
Le premier défi de la mémoire à long terme est le tri de l’information.
Par exemple, si l’utilisateur dit « En général, j’aime les palettes peu saturées », c’est sans doute une préférence qui mérite d’être conservée durablement ; mais s’il dit « Cette fois, essayons un rouge très saturé », il s’agit plus probablement d’une exigence ponctuelle pour l’œuvre en cours.
Les deux phrases parlent de couleur, mais n’ont pas la même portée. Si le système écrivait tous les messages de l’utilisateur tels quels dans une base vectorielle, puis s’en remettait à la recherche sémantique pour les réinjecter, il confondrait facilement préférences durables, exigences ponctuelles et informations passées devenues caduques.
Nous avons donc organisé la mémoire de Lomo Agent en plusieurs couches :
| Couche de mémoire | Contenu conservé | Usage principal |
|---|---|---|
| Preuves d’origine (Evidence) | Messages de l’utilisateur, provenance, date et état de validité | Fonder l’extraction des faits, leur correction et leur révocation |
| Mémoire atomique (Atomic Memory) | Préférences de l’utilisateur, faits de projet, habitudes, etc., avec provenance et version | Permettre une recherche à la demande d’une tâche à l’autre |
| Mémoire épisodique (Episodic Memory) | Résumé par étapes d’un échange ou d’une tâche | Aider l’Agent à retrouver les projets passés et leur avancement |
| Profil utilisateur (User Profile) | Un petit nombre d’informations relativement stables et à forte valeur | Fournir à moindre coût un contexte de fond pour la collaboration au quotidien |
Ces couches sont liées entre elles, mais n’ont pas les mêmes responsabilités.
Les messages d’origine constituent des preuves traçables. Le système peut en extraire des souvenirs candidats, puis décider, selon leur provenance, leur date, leur portée et les enregistrements existants, s’il faut les enregistrer, les compléter ou les mettre à jour.
La mémoire épisodique s’intéresse plutôt à ce qui s’est passé au cours d’une collaboration. Par exemple : la semaine dernière, l’utilisateur a réalisé une série d’affiches de marque, a finalement retenu une déclinaison aux tons chauds, et a décidé de reporter la production vidéo. Ces informations ne méritent pas toutes d’entrer dans le profil à long terme, mais peuvent s’avérer utiles la prochaine fois que l’utilisateur évoquera le projet.
Le profil utilisateur doit être plus sobre. Il ne doit conserver qu’un petit nombre d’informations relativement stables, pour éviter de devenir, au fil de l’usage, une nouvelle conversation historique qui enfle sans fin.
Au moment du rappel, le système choisit les souvenirs à fournir au modèle en fonction de l’objectif et des permissions de la tâche courante, sans charger tout l’historique à chaque tour.
Un principe important s’ajoute : la mémoire doit distinguer les faits exprimés explicitement par l’utilisateur des suppositions du modèle.
Par exemple, si l’Agent, voyant l’utilisateur générer plusieurs images cyberpunk d’affilée, en déduit une préférence durable pour le style cyberpunk, cela peut constituer une hypothèse candidate, mais pas l’équivalent d’une préférence explicitement exprimée par l’utilisateur.
De même, le contenu de pages web, les résultats renvoyés par des outils et les résumés générés par l’Agent lui-même ne doivent pas, sans fondement fiable, être reconditionnés en faits personnels concernant l’utilisateur.
7.2 Ce que nous ont appris les architectures de mémoire existantes
Pour concevoir la mémoire à long terme, nous avons étudié plusieurs approches techniques.
Les pratiques de mémoire d’OpenClaw mettent l’accent sur un stockage inspectable et maintenable, et sur le lien entre la mémoire et le contexte d’origine. Leur atout : une grande transparence, qui permet à l’utilisateur ou au développeur de vérifier ce qui a été enregistré ; mais lorsque la mémoire ne cesse de grossir, la recherche, la maintenance et la gestion des conflits exigent des mécanismes supplémentaires.
Mem0 se concentre davantage sur l’extraction des informations utiles d’une conversation et sur leur récupération par recherche lors des interactions suivantes. Cette approche convient aux agents utilisés longtemps et sur des sujets variés, mais la qualité de la mémoire dépend fortement de la précision de l’extraction et de la stratégie de rappel. Si une instruction ponctuelle est prise à tort pour une préférence durable, les recherches ultérieures risquent au contraire d’amplifier l’erreur en continu.
Zep / Graphiti propose une autre voie : maintenir explicitement dans la mémoire les relations entre entités et l’information temporelle. C’est particulièrement précieux pour des faits qui évoluent. Par exemple, une marque utilisait autrefois le bleu comme couleur principale, puis est passée au vert : ces deux enregistrements ne se contredisent pas forcément, ils peuvent correspondre à des périodes de validité différentes.
Les relations temporelles et les graphes de connaissances introduisent toutefois une complexité de maintenance plus élevée : normalisation des entités, mise à jour des relations, gestion des conflits et coût de recherche.
Au regard des scénarios créatifs de Lomo Agent, trois questions nous importent avant tout :
-
Les préférences de l’utilisateur ont-elles une source fiable ?
-
Les faits passés sont-ils toujours valides ?
-
Lorsque l’utilisateur corrige ou révoque une information, les souvenirs concernés sont-ils mis à jour en conséquence ?
Ainsi, en plus du stockage en couches et de la recherche sémantique habituels, nous maintenons pour chaque souvenir sa provenance, ses versions et ses relations de dérivation.
7.3 Pourquoi « oublier » est plus difficile que « se souvenir »
La mémoire à long terme pose encore une question souvent négligée : que doit faire le système lorsque l’utilisateur demande explicitement d’oublier quelque chose ?
Supposons que l’utilisateur dise d’abord : « La couleur principale de notre marque est le bleu. »
Puis qu’il ajoute : « Le bleu associé à un blanc cassé, ça donne un ensemble plus doux. »
Le système de mémoire peut fusionner ces deux déclarations en une nouvelle préférence de marque :
« La marque privilégie l’association du bleu et du blanc cassé, avec une tendance visuelle générale douce. »
Si l’utilisateur demande ensuite d’oublier la première consigne sur le bleu, supprimer le premier souvenir atomique ne suffit évidemment pas, car la version fusionnée contient toujours l’information.
À mesure que le système tourne, cette information peut aussi passer dans la mémoire épisodique, dans le profil utilisateur, voire être fusionnée dans d’autres souvenirs dérivés.
Nous maintenons donc pour chaque souvenir ses relations de provenance et de dérivation, qui décrivent de quelles preuves historiques et de quelles versions antérieures il est issu.
flowchart TD
U["L'utilisateur désigne ce qu'il faut oublier"] --> A["Retirer aux preuves concernées le droit d'alimenter la mémoire"]
A --> B["Localiser les versions de souvenirs affectées"]
B --> C["Suivre les relations de dérivation jusqu'aux versions ultérieures"]
C --> D["Désactiver le contenu affecté, vérifier les preuves encore valides"]
D --> E["Réextraire à partir des preuves valides restantes"]
E --> F["Mettre à jour souvenirs atomiques, résumés et profil utilisateur"]
F --> G["Invalider les index de recherche et caches de contexte concernés"]
Figure 8 : le processus de révocation de la mémoire.
Dans l’implémentation, nous maintenons l’évolution de la mémoire à l’aide de chaînes de versions. Pour reprendre l’exemple de la couleur principale de la marque : la première déclaration sur le bleu forme une version valide v1, associée à sa preuve d’origine (ORIGIN) ; lorsque l’utilisateur ajoute l’association avec le blanc cassé, le système crée une nouvelle version v2, marque l’ancienne comme SUPERSEDED, et enregistre la nouvelle preuve de correction (REFINE). Si l’utilisateur se contente ensuite de confirmer une conclusion existante, on peut l’enregistrer comme CONFIRM, sans avoir à générer un nouveau souvenir.
Lorsque l’utilisateur demande d’oublier la consigne initiale sur le bleu, le système retire à la preuve correspondante le droit d’être utilisée, et suit les relations entre versions pour localiser les souvenirs affectés. Les versions qui dépendent de cette preuve sont supprimées et leur contenu effacé, puis réextraites à partir des preuves valides restantes ; une preuve qui ne jouait qu’un rôle de confirmation peut simplement être dissociée, sans affecter les souvenirs qui tiennent par eux-mêmes.
Cette révocation se propage aussi au profil utilisateur et aux résumés épisodiques. Une fois les entrées de profil concernées invalidées, le système met à jour la version du profil, pour que les inférences suivantes n’utilisent plus l’ancien instantané ; les résumés affectés doivent quant à eux être reconstruits. Le système supprime également les vecteurs de recherche correspondants et empêche que le contenu supprimé soit de nouveau extrait, ce qui réduit le risque qu’une ancienne information revienne dans le contexte.
Il faut noter qu’une requête déjà envoyée au modèle ne peut pas être révoquée instantanément. Nous suivons donc aussi les messages et les souvenirs sur lesquels reposent les réponses et les résultats d’outils, afin d’exclure les contenus dérivés affectés des inférences suivantes, et d’éviter qu’une information oubliée ne soit réintroduite par les propres sorties passées de l’Agent.
L’« oubli » dont il est question ici porte principalement sur le droit d’utiliser la mémoire à long terme et le contexte qui en dérive ; il ne revient pas à supprimer tout l’historique de conversation, les ressources générées ou les données de transaction. Ces dernières relèvent d’autres cycles de vie des données et de la gestion de la vie privée, et doivent être traitées séparément.
7.4 Fiabilité de la mémoire et dégradation gracieuse
Le suivi de provenance et la gestion des versions rendent le système de mémoire plus maîtrisable, sans pour autant éliminer complètement les erreurs de compréhension du modèle.
Par exemple, un même échange peut contenir plusieurs faits candidats : l’un remplit les conditions pour être enregistré, l’autre n’est qu’une instruction ponctuelle mal interprétée par le modèle. Si l’on traite tout un lot de résultats d’extraction comme un bloc indivisible, une seule anomalie peut compromettre d’autres souvenirs valides.
Les souvenirs candidats doivent donc être validés et traités un par un, en distinguant les cas : source qui ne les étaye pas, conflit de contenu, format de sortie anormal, etc.
De même, une panne du service de mémoire ne doit pas paralyser complètement les tâches créatives normales.
Par exemple, en cas de timeout du rappel, on peut provisoirement se passer de l’enrichissement par la mémoire ; un échec d’extraction en arrière-plan peut être retraité plus tard ; un candidat au format anormal ne doit pas entraîner la perte des autres enregistrements valides.
Cette stratégie de dégradation gracieuse implique que l’Agent ne pourra pas toujours exploiter l’intégralité des préférences passées, mais qu’il pourra continuer à travailler sur la base du brief courant et de l’état d’exécution existant.
Plutôt que de laisser un résultat de mémoire défaillant compromettre toute une tâche, nous préférons préserver le chemin d’exécution normal, et réinjecter le contexte pertinent une fois la mémoire rétablie.
La valeur de la mémoire à long terme doit, au bout du compte, se vérifier dans la collaboration réelle. Au-delà de la précision du rappel, nous voulons savoir si la mémoire est périmée, si sa provenance peut être expliquée, et si, une fois une correction ou une révocation effectuée par l’utilisateur, l’ancienne information continue d’influencer les décisions suivantes.
À mesure que la mémoire et le contexte des tâches s’affinent, l’Agent dispose d’un contexte qui traverse le temps. Mais savoir ce que l’utilisateur a fait par le passé ne permet pas de juger si la tâche en cours doit se poursuivre.
Une tâche comportant plusieurs étapes créatives exige aussi un plan d’exécution explicite, des conditions d’arrêt et des limites d’autorisation fixées par l’utilisateur. C’est précisément le problème que résout le mécanisme de progression autonome, objet du chapitre suivant.
8. Progression autonome : plan, conditions d’arrêt et autorisation de l’utilisateur
Les mécanismes précédents permettent à l’Agent de maintenir des tâches longues, de reprendre une exécution interrompue et de retrouver l’information passée au besoin. Mais pour un objectif créatif qui doit avancer dans la durée, une question reste entière : comment l’Agent juge-t-il s’il doit continuer à travailler ?
Par exemple, l’utilisateur demande un court film de marque. Une fois le scénario et le storyboard terminés, l’Agent peut livrer un récapitulatif puis clore le tour en cours. Le modèle n’a pas commis d’erreur : il a bien terminé le travail de cette étape. Mais on est encore loin du film fini que l’utilisateur attend.
Dans nos premières démos et nos simulations de scénarios, nous avons rencontré ce genre de situation : l’utilisateur devait envoyer « continue » à répétition pour que l’Agent passe à l’étape suivante.
À l’inverse, se contenter d’exiger du modèle qu’il « ne s’arrête pas avant d’avoir tout terminé » crée un autre problème : l’Agent risque de retenter en boucle des opérations déjà échouées, de générer sans fin des ressources inutiles, ou d’élargir de lui-même le périmètre créatif sans confirmation de l’utilisateur. Il faut donc rendre l’objectif de la tâche et l’avancement de l’exécution indépendants de chaque réponse du modèle, et laisser au runtime le contrôle des limites de la progression automatique.
8.1 Transformer le plan en une liste d’exécution maintenue dans la durée
Nous avons introduit une liste d’exécution structurée (Execution Checklist), qui consigne les principales étapes d’un objectif créatif, du plan jusqu’à la livraison.
Contrairement à une TodoList ordinaire générée par le modèle dans la conversation, la liste d’exécution est un état structuré persisté et maintenu par le système. Chaque étape peut être associée à un état d’exécution, à des productions, à des preuves d’achèvement et, au besoin, à une exigence de confirmation par l’utilisateur.
Par exemple, la réalisation d’un court film de marque peut s’organiser ainsi :
| Phase d’exécution | Tâches principales | Critère d’achèvement |
|---|---|---|
| Scénario et storyboard | Définir la narration et le découpage des plans | Document de storyboard et validation de l’utilisateur |
| Personnages et décors | Préparer les références visuelles | Ressources correspondantes et historique des versions |
| Génération des plans | Appeler un modèle vidéo pour générer les plans | Tâches de génération externes et leurs résultats |
| Relecture | Vérifier les plans et la direction d’ensemble | Retour de l’utilisateur ou résultat de recette autorisé |
| Livraison finale | Rassembler les productions à livrer | Œuvre finale et ressources associées |
Une fois le plan d’exécution approuvé par l’utilisateur, la liste devient la référence pour la suite. Le Worker peut planifier et appeler des outils en fonction de l’étape en cours, et le runtime peut vérifier l’avancement à la fin de chaque cycle d’exécution.
Attention : la liste d’exécution n’est pas un plan figé et immuable.
Le processus créatif change souvent. Par exemple, pendant la génération des plans, l’utilisateur décide de modifier un personnage : les étapes prévues et une partie des productions doivent alors être revues. Le système doit mettre à jour le plan selon la nouvelle instruction de l’utilisateur, et maintenir le lien entre étapes et productions.
La liste d’exécution assure la continuité de l’objectif et de l’avancement. Elle permet à l’Agent, après une compaction du contexte, une attente asynchrone ou une reprise de processus, de retrouver où en est l’exécution de la tâche courante.
8.2 Le modèle a cessé de répondre : la tâche doit-elle continuer ?
Une fois le plan persisté, reste une question plus concrète : que doit faire le runtime quand le modèle termine son tour ?
Pour concevoir ce mécanisme, nous nous sommes inspirés de Codex Goals et des Claude Code Hooks.
Codex Goals associe un objectif à un état d’exécution continu, ce qui permet à l’Agent de poursuivre son travail tant que l’objectif n’est pas atteint ; le Stop Hook de Claude Code permet, lui, d’exécuter des vérifications supplémentaires au moment où le modèle s’apprête à s’arrêter, et de bloquer cet arrêt selon le résultat.
Ce que j’en retiens : la fin d’une réponse du modèle ne signifie pas forcément que toute la tâche est terminée. Le runtime a besoin d’un état d’objectif et d’un mécanisme de vérification de l’arrêt qui lui soient propres.
Les critères d’achèvement d’un Agent de code et d’un Agent créatif diffèrent toutefois nettement.
Pour une tâche de code, des tests qui passent, un build réussi ou des fichiers modifiés comme prévu fournissent généralement des preuves d’exécution assez claires. Une tâche créative, elle, peut difficilement s’appuyer sur un critère objectif unique : qu’une vidéo ait été générée avec succès ne dit rien sur la cohérence des personnages, le rythme des plans ou le rendu visuel d’ensemble au regard des attentes de l’utilisateur.
Nous traitons donc séparément l’avancement du processus et la recette de l’œuvre.
Le premier peut s’évaluer à partir de la liste d’exécution, de l’historique des appels d’outils et de l’état des productions ; la seconde requiert une compréhension multimodale, les exigences précises de la tâche et, si nécessaire, une relecture humaine.
À la fin de chaque cycle d’exécution, le runtime vérifie s’il reste des étapes à faire avancer, s’il y a une attente externe, si une confirmation de l’utilisateur est nécessaire, et si les limites d’exécution sont atteintes.
flowchart TD
E["Fin du cycle d'exécution en cours"] --> A{"La liste d'exécution est-elle terminée ?"}
A -->|Oui| END["Fin de l'exécution, affichage des résultats ou recette"]
A -->|Non| B{"Tâche en pause, bloquée ou hors limites ?"}
B -->|Oui| STOP["Arrêter la progression automatique et en consigner la raison"]
B -->|Non| C{"Attente d'un événement externe ?"}
C -->|Oui| WAIT["Suspendre jusqu'à l'événement attendu"]
C -->|Non| D{"Point de contrôle humain atteint ?"}
D -->|Oui| GATE["Afficher une carte de confirmation ou de choix"]
D -->|Non| F{"Y a-t-il une avancée concrète ?"}
F -->|Non| LIMIT["Arrêter la relance automatique pour éviter de tourner à vide"]
F -->|Oui| NEXT["Planifier le cycle suivant du Worker"]
NEXT --> E
Figure 9 : le runtime décide de poursuivre ou non l’exécution d’après l’état persisté de la tâche, et non sur le seul fait que le modèle a cessé de répondre.
Il faut ici prêter une attention particulière à l’avancée concrète (Meaningful Progress).
Par exemple, l’ajout d’une production exploitable, l’achèvement d’une étape de la liste ou la soumission réussie d’une nouvelle tâche de génération peuvent servir d’indicateurs d’avancement.
Mais si l’Agent se contente de réécrire la description du plan, ou fait sans cesse passer des étapes de « à faire » à « en cours » sans produire de nouveau résultat utile, il ne doit pas être relancé indéfiniment.
Grâce aux contrôles d’avancement et aux limites d’exécution, nous réduisons autant que possible le coût d’inférence de ces cycles à vide.
Ce jugement a bien sûr ses limites. Générer une nouvelle ressource prouve seulement que le système a exécuté une opération, pas que sa qualité est au rendez-vous. Une recette qualité multimodale plus complète reste une piste à explorer.
8.3 Human-in-the-Loop : associer l’utilisateur aux décisions clés
Au cours d’une création, l’utilisateur souhaite souvent confier les opérations répétitives à l’Agent, tout en gardant la main sur les orientations créatives clés.
Par exemple, la direction visuelle de la marque, la conception des personnages, le storyboard et le choix final des plans sont généralement très subjectifs. Si l’Agent tranche seul à ces moments et poursuit la génération, il risque d’engendrer ensuite beaucoup de coûts inutiles.
Nous avons donc introduit des points de contrôle humains (Human-in-the-Loop Checkpoint), qui suspendent la progression automatique aux étapes nécessitant l’intervention de l’utilisateur.
Lorsqu’un point de contrôle est atteint, l’Agent peut présenter, via une carte structurée, la proposition en cours, les résultats candidats et les actions suggérées. L’utilisateur choisit directement « Confirmer et continuer » ou « Ajuster la proposition », et le système met à jour l’état de la tâche d’après cette action explicite.
Ce type d’interaction s’inspire aussi de la confirmation des permissions dans des produits comme Claude Code : pour une opération aux effets de bord notables, le système doit informer l’utilisateur à l’avance de ce qui va être exécuté, et c’est l’utilisateur qui décide de poursuivre ou non.
Dans un contexte créatif, toutefois, le contrôle humain ne porte pas seulement sur les permissions, mais aussi sur l’esthétique et la direction créative. L’utilisateur peut avoir autorisé l’Agent à générer des vidéos tout en voulant choisir lui-même la proposition une fois le découpage des plans terminé. C’est pourquoi le point de contrôle doit être enregistré comme un état d’exécution à part entière, et non reposer sur le seul fait que le modèle écrive « en attente de confirmation » dans sa réponse.
Lors de nos simulations pendant le développement, nous avons justement rencontré ce problème : l’Agent avait atteint une étape exigeant la confirmation de l’utilisateur, mais se contentait de répondre « Merci de confirmer si je dois continuer », sans fournir de carte de confirmation actionnable, si bien que l’utilisateur devait taper à nouveau « continue ».
Nous avons donc ajouté un filet de sécurité dans le runtime : lorsque la tâche est arrêtée sur un point de contrôle sans que le modèle ait posé la question correspondante, le système ajoute automatiquement une carte de confirmation ; pour une génération exécutée après approbation de l’utilisateur, si la liste d’exécution comporte encore des étapes à réaliser, la tâche est automatiquement réveillée à la fin de la génération. Ainsi, l’attente de confirmation et la reprise de l’exécution ne dépendent plus entièrement du comportement interactif du modèle : c’est le runtime qui les coordonne en fonction de l’état de la tâche.
Bien sûr, contrôler ne veut pas dire faire intervenir l’utilisateur à chaque étape. Les opérations irréversibles ou qui dépassent le périmètre autorisé exigent toujours une confirmation explicite ; les étapes répétitives et peu risquées, elles, peuvent s’enchaîner dans les limites de l’autorisation, pour éviter que trop de confirmations n’interrompent le processus créatif.
8.4 Niveau d’autonomie, budget et limites d’autorisation
Sur une plateforme de création IA, l’Agent peut faire appel à des capacités de génération de texte, d’image, de vidéo, d’audio, de 3D, etc., fournies par différents éditeurs de modèles, et ces opérations s’accompagnent souvent de frais réels. Le niveau d’autonomie et le contrôle du budget doivent donc être conçus séparément.
Que l’utilisateur autorise l’Agent à exécuter automatiquement ne veut pas dire qu’il consent à des appels illimités à des modèles payants ; de même, approuver un budget ne signifie pas que toutes les opérations peuvent se passer de relecture.
Le runtime maintient séparément trois dimensions :
| Dimension de contrôle | Problème résolu | Mécanismes typiques |
|---|---|---|
| Niveau d’autonomie (Autonomy Level) | Jusqu’où l’Agent peut-il avancer d’une traite ? | Confirmation à chaque étape, confirmation aux nœuds clés, exécution automatique dans le périmètre autorisé |
| Contrôle du budget (Budget Control) | Combien de ressources la tâche courante peut-elle consommer ? | Plafond par opération, budget du plan, vérification du solde, confirmation d’un budget supplémentaire |
| Périmètre autorisé (Execution Scope) | Quels travaux l’Agent est-il autorisé à réaliser ? | Brief et plan approuvés, modifications explicites de l’utilisateur |
Par exemple, l’utilisateur approuve la réalisation de trois vidéos et autorise l’Agent à les terminer automatiquement dans les limites du budget.
Si, en cours d’exécution, il s’avère nécessaire de produire des images de référence de personnages supplémentaires, l’Agent peut évaluer si ces opérations relèvent du plan et du budget initiaux. S’il faut élargir le périmètre d’exécution ou engager des frais supplémentaires, il doit à nouveau demander la confirmation de l’utilisateur.
Une frontière de sécurité essentielle entre ici en jeu : ni le plan proposé par le modèle, ni l’autorisation qu’il prétend avoir obtenue ne peuvent devenir d’eux-mêmes une véritable autorisation de l’utilisateur.
Le système doit se fonder sur les actions explicites de l’utilisateur dans l’interface et sur l’état d’autorisation enregistré côté serveur.
Selon les outils, il faut aussi adopter des stratégies de contrôle différentes, en fonction de leurs effets de bord réels :
-
les requêtes en lecture seule présentent généralement peu de risques ;
-
l’écriture de documents doit gérer les versions et les permissions ;
-
les générations payantes exigent une vérification du budget ;
-
les opérations irréversibles, comme la suppression de ressources ou la révocation de souvenirs, requièrent un mécanisme de confirmation plus strict.
Par rapport à un Coding Agent, l’Agent d’une plateforme de création est soumis à une contrainte particulière : une fois soumises, de nombreuses générations externes ont peut-être déjà engendré des frais, et même si l’utilisateur arrête ensuite la tâche, elles ne sont pas forcément annulables intégralement. Les vérifications de budget et d’autorisation doivent donc intervenir avant que l’effet de bord ne se produise réellement, en lien avec le mécanisme d’exécution durable présenté plus haut.
D’après notre pratique actuelle, la difficulté de l’exécution autonome réside, là encore, dans le fait de savoir quand s’arrêter. Solde insuffisant, pause demandée par l’utilisateur, attente d’une tâche externe, relecture humaine nécessaire, ou exécution continue sans avancée utile : dans tous ces cas, le runtime doit conserver l’état courant et attendre que les conditions soient réunies pour reprendre.
Cela signifie aussi qu’un niveau d’autonomie plus élevé n’est pas forcément meilleur. Risque, coût et vérifiabilité varient sensiblement d’une tâche créative à l’autre : l’Agent doit, dans les limites autorisées par l’utilisateur, réduire autant que possible les interventions inutiles, tout en préservant le contrôle de l’utilisateur aux nœuds clés.
À plus long terme, nous espérons aussi, grâce à l’évaluation multimodale de la qualité, permettre au système de juger plus précisément de la cohérence des personnages, de la continuité des plans et du rendu visuel. À mesure que la recette automatique progressera, certaines étapes qui exigeaient une validation humaine pourront être confiées à l’Agent en toute autonomie.
Bien sûr, la collaboration dans la durée soulève une autre question, au-delà de l’exécution des tâches : l’utilisateur peut modifier une œuvre directement dans l’espace de travail, sans passer par la conversation. Lorsque l’Agent et l’utilisateur éditent en même temps le même document ou le même workflow, comment le système maintient-il la cohérence de l’état ?
Cela exige un espace de travail partagé qui, au-delà de l’exécution des tâches, garantit l’accès et la modification conjoints de l’œuvre par l’humain et l’Agent.
9. Shared Workspace : l’utilisateur et l’Agent modifient la même œuvre
Les mécanismes évoqués jusqu’ici expliquent surtout comment l’Agent comprend les tâches, maintient le contexte et fait avancer le travail dans la durée. Mais dans la pratique, la collaboration entre l’utilisateur et l’Agent ne se limite pas à la fenêtre de discussion.
Par exemple, pendant que l’Agent génère une vidéo d’après le storyboard, l’utilisateur peut, directement dans l’espace de travail à droite, ajuster le prompt d’un plan, remplacer une ressource de référence ou modifier un nœud de modèle dans un workflow Flows.
Ces opérations ne passent pas par la conversation, mais modifient bel et bien l’œuvre. Si l’Agent continuait à s’appuyer sur son contexte antérieur, il risquerait d’utiliser des paramètres périmés, voire d’écraser les modifications que l’utilisateur vient de faire.
Dans Lomo Agent, nous traitons donc les productions créatives comme des objets persistants, indépendants de la conversation et du contexte des tâches, et organisons la collaboration entre l’humain et l’Agent via le Shared Workspace (espace de travail partagé).
9.1 Donner aux œuvres un cycle de vie propre
Dans un Agent conversationnel classique, le résultat d’une génération est souvent attaché à un message de la conversation. L’utilisateur peut le consulter, le télécharger ou poser d’autres questions, mais à mesure que la conversation s’allonge, la gestion et la modification des œuvres se heurtent peu à peu aux limites de la structure en sessions.
Le processus créatif de Lomo, lui, comporte de nombreux objets à maintenir dans la durée : images, vidéos, ressources 3D, documents de storyboard, collections de ressources ou encore workflows visuels Flows.
Ces objets ont besoin d’une identité, de versions et de droits d’accès stables ; ils doivent pouvoir être modifiés directement par l’utilisateur, et référencés d’une tâche à l’autre.
Nous gérons donc séparément le contexte de conversation et les productions créatives :
-
le contexte de conversation et de tâche conserve l’intention de l’utilisateur, le déroulement de l’exécution et les informations dont dispose l’Agent à un instant donné ;
-
l’espace de travail partagé conserve les œuvres qui doivent réellement être éditées dans la durée et livrées, avec leur identité, leur contenu, leurs versions et leurs références.
Par exemple, l’utilisateur discute d’abord dans Main du storyboard d’un court film de marque, puis une tâche de production vidéo prend le relais. Les deux espaces d’exécution peuvent, dans la limite des permissions, référencer le même document de storyboard, sans avoir à en créer chacun une copie.
De même, une fois une tâche de génération vidéo terminée, ses productions peuvent toujours être référencées par un nouveau workflow, que le Worker d’origine tourne encore ou non.
Cette conception permet aux œuvres de survivre au-delà d’un tour de conversation et du cycle de vie d’une tâche, et pose les bases de la réutilisation des ressources et de la collaboration multi-tâches.
9.2 Rendre l’Agent attentif aux changements dans l’espace de travail
Les objets partagés règlent la question du stockage des œuvres, mais il en reste une autre : comment l’Agent sait-il qu’une œuvre a été modifiée ?
Supposons que l’Agent ait lu un document de storyboard et s’apprête à poursuivre la génération d’après ses descriptions de plans. Au même moment, l’utilisateur modifie directement le contenu du deuxième plan dans l’espace de travail.
Si le système n’enregistrait que les messages de la conversation, le cycle d’inférence suivant de l’Agent ignorerait cette modification.
Nous maintenons donc des informations de version pour chaque objet partagé, et enregistrons la version que chaque tâche concernée a vue lors de sa dernière lecture ou modification de l’objet.
Lorsqu’un document, une ressource ou un workflow change, le système peut laisser aux tâches concernées une notification de changement persistante. Par exemple :
Le document de storyboard est passé de la v3 à la v4 : la description du deuxième plan a changé. Avant de poursuivre, lisez la dernière version.
Nous n’avons pas choisi de déclencher une inférence de l’Agent à chaque modification dans l’espace de travail.
La raison est simple : en ajustant un workflow, l’utilisateur peut déplacer des nœuds, modifier des paramètres ou remplacer des ressources à la chaîne. Réveiller le Worker à chaque changement provoquerait une foule d’appels inutiles au modèle, et pourrait faire intervenir l’Agent à répétition alors que l’utilisateur n’a pas fini d’éditer.
Les notifications de changement servent donc surtout à ce que l’Agent, lors de sa prochaine exécution, s’aperçoive que ses informations sont périmées. Ce n’est que lorsque la tâche concernée doit réellement avancer que le runtime décide, selon l’état courant, de reprendre ou non l’exécution.
Pour un workflow visuel comme Flows, l’Agent n’a pas non plus besoin de lire l’intégralité des données des nœuds à chaque inférence. Le système peut d’abord fournir un bref aperçu des changements, puis récupérer la dernière version à la demande lorsque le modèle doit effectivement traiter les nœuds concernés.
On retrouve là le lien avec l’ingénierie du contexte décrite plus haut : l’espace de travail conserve l’état complet de l’œuvre, le contexte ne fournit que les informations nécessaires à la décision courante.
9.3 Conflits de version et contrôle de concurrence optimiste
Percevoir les changements ne suffit pas à garantir la sûreté des modifications concurrentes.
Prenons un scénario plus concret :
l’Agent lit la v3 du document de storyboard et s’apprête à retoucher le troisième plan. Au même moment, l’utilisateur modifie directement ce même document et l’enregistre en v4.
Si l’Agent soumettait ensuite sans précaution sa modification fondée sur la v3, il risquerait d’écraser le contenu que l’utilisateur vient de rédiger dans la v4.
Nous appliquons donc un contrôle de concurrence optimiste (Optimistic Concurrency Control).
En lisant un objet, l’Agent obtient sa version courante ; lorsqu’il soumet une modification, il doit fournir la version sur laquelle elle se fonde. Avant l’écriture, le serveur vérifie que l’objet est toujours dans la version attendue.
Si la version correspond, la modification est acceptée normalement ; si l’objet a déjà été mis à jour, le système refuse toute opération susceptible d’écraser le nouveau contenu, et demande à l’Agent de relire le dernier état.
Toutefois, selon le type d’objet créatif, la granularité de détection des conflits de version n’est pas la même.
Pour un document, une réécriture complète doit indiquer la version sur laquelle elle se fonde : si l’utilisateur a déjà modifié le document, le système refuse l’écrasement ; une modification partielle, elle, se localise par un fragment de texte exact et unique, et n’est validée que si tous les remplacements attendus s’appliquent.
Pour les workflows Flows, nous avons adopté une détection des conflits plus fine. L’utilisateur peut être en train d’ajuster un nœud de plan pendant que l’Agent modifie un autre nœud. Si toute modification du workflow entraînait le refus de la soumission, les sauvegardes automatiques fréquentes rendraient l’édition en parallèle très difficile pour les deux parties. Le système vérifie donc surtout si les nœuds que l’Agent s’apprête à modifier ont changé : si l’utilisateur a déjà modifié le même nœud, l’Agent doit le relire ; si le changement porte sur d’autres nœuds, les modifications non conflictuelles peuvent être appliquées sur la dernière version du workflow, et l’Agent est informé des changements concernés.
Il ne s’agit pas de fusionner automatiquement des modifications divergentes d’un même contenu, mais, grâce à une granularité de version adaptée, de permettre aux modifications non conflictuelles d’aboutir aussi indépendamment que possible, tout en empêchant un ancien état d’écraser un nouveau contenu.
sequenceDiagram
participant A as Agent
participant O as Œuvre partagée
participant U as Utilisateur
A->>O: Lire le document de storyboard v3
U->>O: Modifier le storyboard et enregistrer en v4
O-->>A: Consigner une notification de changement de version
Note over O,A: La notification attend la prochaine exécution pour être lue
A->>O: Soumettre une modification fondée sur la v3
O-->>A: Conflit de version, écrasement refusé
A->>O: Relire la v4
A->>A: Ajuster la modification d'après la dernière version
A->>O: Soumettre à nouveau sur la base de la v4
Figure 10 : le contrôle de version des œuvres partagées.
Deux mécanismes fonctionnent ici de concert : la notification de changement permet à l’Agent de percevoir à temps la mise à jour d’une œuvre, et la vérification de version apporte une protection finale au moment de la soumission. Même si une notification arrive en retard, l’Agent ne peut pas écraser les modifications de l’utilisateur avec un état périmé.
Le contrôle de concurrence optimiste règle bien sûr la cohérence de l’état, et non les conflits d’intention créative. Par exemple, l’utilisateur veut des tons froids pour un plan, tandis que l’Agent continue d’appliquer des tons chauds selon le plan initial. Même sans aucun conflit de version, le choix final doit encore être tranché au regard de la dernière intention de l’utilisateur et des contraintes de la tâche.
9.4 Des Artifacts et de Canvas à un espace de création partagé
Côté conception de l’interaction, nous nous sommes inspirés de produits comme Claude Artifacts et ChatGPT Canvas.
Ces produits sortent le contenu généré des simples messages de conversation : l’utilisateur peut consulter et éditer directement un document ou du code à côté de la conversation, ce qui lui évite de devoir décrire chaque modification en langage naturel.
Ce mode d’interaction est très instructif pour Lomo Agent. Bien souvent, un créateur préfère déplacer directement un nœud, modifier un passage du storyboard ou remplacer une image, plutôt que de tout redécrire à l’Agent.
L’espace de travail partagé de Lomo Agent doit toutefois gérer des scénarios différents de l’édition de documents classique.
D’une part, les objets créatifs sont de types plus complexes : outre le texte, ils comprennent des images, des vidéos, des ressources 3D et des workflows Flows dont les nœuds dépendent les uns des autres. D’autre part, une même œuvre peut être référencée par plusieurs tâches, tout en faisant l’objet à la fois de générations asynchrones et de modifications directes par l’utilisateur. Le système doit maintenir non seulement le contenu de l’œuvre, mais aussi ses versions, les tâches associées et la connaissance que chaque exécution a de l’état de l’œuvre.
Nous relions donc l’espace de travail éditable à l’état d’exécution de l’Agent : l’utilisateur peut manipuler directement les œuvres, et le Worker récupère un état d’objet valide lors de ses exécutions suivantes.
D’après notre pratique actuelle, l’espace de travail partagé apporte un changement important : l’utilisateur peut participer à un même processus créatif à la fois en langage naturel et par édition directe. Mais cela fait aussi peser sur le système davantage de responsabilités en matière de synchronisation d’état et de contrôle de la concurrence. En particulier, lorsqu’une ressource influe sur plusieurs tâches à la fois, la gestion des dépendances entre objets, et la question de savoir quelles productions en aval doivent être régénérées après une modification en amont, restent des pistes que nous continuons d’explorer.
10. Orchestration des capacités créatives : de l’appel de modèle à la livraison d’une œuvre complète
Sur une plateforme de création comme Lomote AI, l’Agent ne doit pas seulement savoir choisir et appeler des outils : il doit aussi comprendre les relations entre les différentes capacités créatives.
Générer une image est relativement simple ; réaliser un court film complet peut en revanche impliquer de nombreuses étapes : scénario, storyboard, conception des personnages, références de décors, génération des plans, post-production. Chaque phase peut recourir à un modèle différent, et le résultat d’une phase influe directement sur la suite. Surtout, la réussite d’une génération isolée ne garantit pas que l’ensemble du processus créatif aboutira.
Par exemple, l’Agent planifie une production : générer d’abord personnages et arrière-plans, puis détourer les personnages, enfin les incruster dans un plan vidéo donné. Si la plateforme ne dispose pas actuellement des capacités de détourage ou de composition vidéo nécessaires, le plan ne peut pas aboutir à une œuvre finale, aussi réussies que soient les ressources générées en amont.
Il faut donc que l’Agent comprenne, dès la planification, les capacités dont la plateforme dispose réellement, ainsi que les dépendances entre outils et entre productions.
10.1 Faire comprendre à l’Agent le processus créatif et les limites des capacités
Lomote AI intègre déjà de nombreux modèles de génération et d’amélioration d’images, de vidéos et de 3D, et propose la gestion des ressources ainsi que les workflows visuels Flows.
Chaque outil a ses cas d’usage, et les modèles diffèrent par leurs conditions d’entrée, leur qualité de génération, leur coût et leur format de sortie.
Pour l’Agent, choisir un outil suppose de prendre en compte à la fois l’objectif créatif courant, les ressources existantes et les exigences des étapes suivantes.
Par exemple, pour un court film avec un personnage récurrent, l’Agent doit réfléchir à la façon d’établir une référence du personnage, aux modèles qui acceptent une image de référence en entrée, à la manière de préserver la cohérence visuelle d’un plan à l’autre, et à la possibilité d’intégrer les résultats dans la suite de la production. S’il se contentait d’appeler des modèles d’après l’objectif final de l’utilisateur, il produirait facilement une foule de ressources intermédiaires séduisantes mais inutilisables pour l’œuvre finale.
Nous avons donc rassemblé notre savoir-faire créatif et les contraintes des outils sous forme de guides consultables à la demande : cohérence des personnages, planification du storyboard, utilisation des ressources de référence, orchestration des workflows, et capacités de production actuellement prises en charge par la plateforme. Ces guides sont fournis à l’Agent dans les tâches concernées, pour l’aider à planifier des étapes créatives réalisables, sans devoir inscrire durablement toutes les connaissances métier dans le prompt système.
Cette conception s’articule aussi avec l’ingénierie du contexte présentée plus haut : l’Agent obtient les outils et connaissances créatives nécessaires selon la phase de production en cours, puis planifie et exécute en tenant compte des capacités réelles.
S’en remettre à la compréhension des descriptions d’outils par le modèle a bien sûr ses limites : le modèle peut proposer un plan de production apparemment raisonnable, mais en réalité irréalisable. Pour les capacités clés des outils, les contraintes d’entrée-sortie et les conditions d’exécution, le système doit donc toujours fournir des critères de validation explicites.
10.2 La production vidéo multi-agents vue à travers ViMax
En explorant l’orchestration des capacités créatives, je me suis aussi intéressé à des systèmes multi-agents dédiés à la génération vidéo, comme ViMax.
ViMax organise scénario, personnages, storyboard, génération des plans et assemblage vidéo en une chaîne de production unique, et traite les tâches interdépendantes de la création vidéo grâce à la collaboration de différents rôles ou phases d’exécution.
Cette approche est très instructive. La production vidéo est par nature séquencée en phases, chacune avec ses propres enjeux : la planification du scénario porte sur la structure narrative, la conception des personnages sur la cohérence visuelle, et la génération des plans doit tenir compte des conditions d’entrée et du rendu de chaque modèle.
Une division du travail spécialisée permet à chaque phase de s’articuler autour de responsabilités relativement claires, et facilite l’organisation de processus de production complexes.
Ce type d’architecture appelle toutefois aussi des arbitrages. D’un côté, la collaboration multi-agents alourdit la transmission du contexte, la synchronisation d’état et la coordination ; de l’autre, une chaîne de production prédéfinie ne couvre pas forcément des besoins créatifs qui évoluent sans cesse.
Les scénarios de Lomo Agent sont plus ouverts. L’utilisateur peut n’avoir besoin que d’une affiche, vouloir générer une vidéo à partir de ressources existantes, ou demander directement à l’Agent de modifier un workflow Flows. Imposer une chaîne multi-agents figée à chaque besoin créatif risquerait au contraire d’ajouter des niveaux d’exécution inutiles.
À ce stade, nous préférons donc nous appuyer sur un système d’exécution des tâches unifié, combiné à des guides créatifs et des capacités d’outils chargés à la demande, pour que le Worker organise les étapes de production selon l’objectif précis. Pour les tâches créatives complexes à maintenir dans la durée, nous explorerons progressivement une spécialisation plus explicite et une gestion des dépendances entre tâches. Cet arbitrage reste cohérent avec la conception Main / Task décrite plus haut : un niveau de coordination supplémentaire ne se justifie que si un espace d’exécution indépendant apporte un bénéfice réel.
10.3 Graphe de dépendances de production : de l’exécution linéaire à la gestion des dépendances
Aujourd’hui, la liste d’exécution structurée aide déjà l’Agent à suivre l’avancement de la création ; mais lorsqu’une tâche implique de nombreuses ressources et étapes de génération interdépendantes, une liste linéaire montre ses limites.
Par exemple, un court film comporte plusieurs plans, qui peuvent partager la même conception de personnage tout en utilisant des décors et des modèles de génération différents.
Certains plans peuvent être générés indépendamment, d’autres doivent attendre que les ressources de référence en amont soient prêtes. Si l’utilisateur modifie l’apparence d’un personnage en cours de route, les plans concernés devront peut-être être régénérés, sans que cela touche les décors sans rapport.
Ce type de relation s’exprime mieux sous forme de Production DAG (graphe orienté acyclique de production).
flowchart LR
BR["Besoin créatif"] --> SCRIPT["Scénario / storyboard"]
SCRIPT --> CHAR["Conception des personnages"]
SCRIPT --> SCENE["Conception des décors"]
CHAR --> SHOT1["Plan 1"]
CHAR --> SHOT2["Plan 2"]
SCENE --> SHOT1
SCENE --> SHOT2
SHOT1 --> REVIEW["Recette et assemblage"]
SHOT2 --> REVIEW
REVIEW --> FINAL["Livraison finale"]
Figure 11 : le graphe de dépendances de production envisagé.
Dans une telle structure, chaque nœud représente une étape de production ou une production intermédiaire, et les arêtes entre nœuds décrivent leurs dépendances.
Une fois ces relations structurées, le système peut envisager plusieurs capacités :
-
Ordonnancement des dépendances : n’exécuter une étape en aval qu’une fois les ressources nécessaires en amont prêtes.
-
Génération parallèle : exécuter en parallèle les plans sans dépendance directe, dans la limite des ressources et du budget.
-
Propagation des changements : lorsque la conception d’un personnage ou d’un décor change, repérer les résultats en aval potentiellement affectés, plutôt que de tout régénérer sans distinction.
-
Production incrémentale : réutiliser autant que possible les ressources et résultats de génération encore valides, pour limiter les appels redondants et les coûts de production inutiles.
Le graphe de dépendances ajoute toutefois une complexité d’ingénierie. En particulier, les dépendances d’une tâche créative ne sont pas toujours parfaitement déterminées : après le changement d’une ressource en amont, faut-il obligatoirement régénérer les résultats en aval ? Cela dépend de l’étendue de la modification et des exigences concrètes de la production.
Par ailleurs, même avec des dépendances correctes, rien ne garantit que le rendu visuel des résultats soit à la hauteur des attentes. La cohérence des personnages, la continuité des plans et le rythme d’ensemble exigent toujours un mécanisme d’évaluation de la qualité distinct.
Le Production DAG reste donc pour l’instant surtout une piste d’évolution que nous explorons. À ce stade, notre priorité est de garantir que l’Agent mène à bien des processus créatifs réalisables avec les modèles et outils dont dispose déjà Lomote AI, tout en maîtrisant le coût et la validité des productions intermédiaires.
À mesure que la gestion des dépendances entre tâches, la recette automatique et la compréhension multimodale progresseront, nous pourrons combiner ces mécanismes pour que l’Agent passe de l’exécution étape par étape de tâches créatives à la gestion de processus de production complets. Mais pour juger de l’efficacité réelle de ces conceptions, une démo fluide est loin de suffire : les tâches créatives complexes connaissent aussi des échecs d’outils, des modifications de l’utilisateur, des reprises d’état et des générations en double.
Reste à vérifier l’efficacité de ces choix par des exercices de reprise après panne et des simulations de collaboration de bout en bout.
11. Validation technique : de la reprise après panne à la collaboration de bout en bout
Les systèmes d’Agents ont une particularité intéressante : que chaque module fonctionne correctement ne garantit pas que l’ensemble de la collaboration soit fiable.
Par exemple, le message a bien été routé vers la bonne tâche et l’appel d’outil a réussi, mais l’Agent oublie de poursuivre une fois la génération terminée ; ou bien chaque étape se déroule comme prévu, mais on se retrouve au final avec plusieurs documents en double, sans que l’utilisateur sache lequel est le plus récent.
Ce genre de problème est difficile à détecter avec des tests unitaires classiques ou quelques échanges simples.
Pendant le développement de Lomo Agent, en plus de vérifier la justesse de chaque module, nous avons donc fait parcourir à l’Agent des processus complets dans des scénarios d’usage proches de la réalité, pour observer le comportement réel des mécanismes une fois combinés.
11.1 Valider la fiabilité du runtime
Pour des mécanismes comme l’exécution durable, l’Inbox, la génération asynchrone ou l’autorisation de l’utilisateur, beaucoup de problèmes n’apparaissent que lors de transitions d’état bien précises.
Par exemple :
-
la requête de génération vidéo a été soumise, mais le Worker redémarre avant d’enregistrer le résultat ;
-
un ancien Worker perd la connexion, un nouveau processus prend le relais, puis l’ancien reprend son exécution ;
-
au moment où l’utilisateur clique sur Arrêter, un événement de fin de génération arrive justement.
Ces situations surviennent rarement lors d’une démo classique, mais peuvent affecter directement l’état d’exécution, les œuvres de l’utilisateur, voire les frais réels. Pendant le développement, nous avons donc eu recours à des tests d’injection de fautes, pour vérifier en priorité la façon dont le système gère ces situations anormales.
Les tests couvrent les frontières d’exécution clés suivantes :
| Scénario de validation | Point d’attention |
|---|---|
| Interruption et reprise de l’exécution | Peut-on reprendre depuis l’état persisté sans exécution en double ? |
| Livraison en double et timeout réseau | Les requêtes idempotentes et les résultats inconnus sont-ils correctement gérés ? |
| Reprise concurrente par plusieurs Workers | Empêche-t-on l’ancien exécutant d’écrire un état périmé ? |
| Course entre annulation et événements asynchrones | Une tâche arrêtée peut-elle être réveillée par un événement retardataire ? |
| Changements de budget et d’autorisation | Les permissions courantes sont-elles revérifiées à la reprise ? |
| Modification concurrente d’objets partagés | Une ancienne version peut-elle écraser une modification récente de l’utilisateur ? |
| Correction et révocation de la mémoire | Une information invalidée peut-elle encore être utilisée via un résumé ou le profil ? |
Pour les sorties du modèle reproductibles de façon stable, nous pouvons rejouer des résultats figés, en nous concentrant sur la validation des transitions d’état du runtime. Les services de génération externes peuvent, eux, être simulés avec des timeouts, des callbacks en double et des événements dans le désordre, sans dépendre à chaque fois de vrais modèles payants. L’enjeu de ces tests est le déterminisme du comportement du système : même si le modèle comporte une part d’aléatoire, l’état des tâches, les droits d’exécution et les contraintes de facturation doivent obéir à des règles claires et vérifiables.
L’un des tests les plus représentatifs simule un Worker qui se « fige » brusquement pendant l’exécution d’un outil. Dans ce test, le modèle utilise une sortie figée et décide d’abord d’appeler un outil de consultation ; le premier Worker a déjà enregistré la décision du modèle et consigné l’appel d’outil comme started, puis il est suspendu artificiellement. Une fois son bail expiré, un second Worker prend le relais : la génération d’exécution passe à 2, et il reprend depuis le checkpoint. Comme la décision du modèle pour ce Step est déjà enregistrée, il n’a pas besoin de relancer l’inférence et termine l’appel d’outil avec la même clé d’idempotence. Enfin, on réactive le premier Worker pour qu’il tente de soumettre son résultat retardataire.
Les résultats sont conformes aux attentes : le tour finit à l’état completed, avec une génération d’exécution égale à 2 ; le modèle n’a été appelé qu’une seule fois, il n’existe qu’un seul résultat d’outil, et l’écriture d’état de l’ancien Worker est rejetée. Nos tests d’intégration couvrent aussi la relivraison après perte d’un message de file, la reprise idempotente d’une génération après approbation, le blocage de la création de nouvelles tâches par un tour annulé, les limites d’autorisation des frais et la révocation de la mémoire.
11.2 Simulations de collaboration de bout en bout
Les tests du runtime permettent de vérifier que le système gère correctement les états anormaux, mais ne reflètent pas pleinement l’expérience réelle de collaboration avec l’Agent.
Par exemple, l’Agent termine avec succès la planification du storyboard, puis s’arrête net à la phase suivante, et n’attaque la génération vidéo que lorsque l’utilisateur tape à nouveau « continue ». Du point de vue de l’exécution du programme, ce n’est peut-être pas une panne ; pour l’utilisateur, le déroulement n’est clairement pas assez naturel.
J’ai donc introduit des simulations de scénarios.
Dans un environnement isolé, avec de vrais modèles de texte, des services de génération simulés et un utilisateur simulé au comportement prédéfini, l’Agent mène à bien un processus créatif relativement complet.
Les scénarios couvrent une série d’affiches, la production d’un court film, la modification d’un workflow Flows, la création 3D, le passage d’une tâche à l’autre, la reprise de tâches passées, ou encore la correction et l’oubli de souvenirs. Ils combinent des appels successifs au modèle et aux outils, des confirmations de l’utilisateur, des attentes asynchrones et des liens de contexte entre tâches.
Par exemple, dans le scénario de court film, l’utilisateur simulé énonce d’abord son objectif créatif ; l’Agent doit organiser le storyboard, préparer les ressources et soumettre les requêtes de génération. En cours d’exécution, l’utilisateur peut ajuster la proposition, choisir des plans ou insérer à l’improviste une autre tâche. Nous ne cherchons pas seulement à savoir si une étape donnée réussit, mais si l’Agent continue, après ces changements, à faire avancer le travail autour de l’objectif initial.
J’observe en particulier :
-
si l’Agent se met en pause ou poursuit au bon moment ;
-
si l’utilisateur doit envoyer « continue » à plusieurs reprises pour faire avancer la tâche ;
-
s’il y a des confirmations superflues ou des générations en double ;
-
si un nouveau message est correctement rattaché à une tâche existante ;
-
si, après une modification de l’œuvre par l’utilisateur, l’Agent parvient à obtenir le dernier état ;
-
si une même œuvre se retrouve scindée par inadvertance en plusieurs copies ;
-
si, après une correction ou une révocation de la mémoire, l’ancienne information influence encore la suite de la collaboration.
Par rapport à l’appel direct de vrais services de génération de médias, l’environnement simulé permet de maîtriser le coût des tests et de reconstruire plus facilement des scénarios précis à répétition, pour observer les problèmes susceptibles d’apparaître dans les interactions entre modules.
Cette approche a toutefois des limites nettes. Des résultats de génération simulés ne permettent pas d’évaluer la qualité visuelle d’œuvres réelles, et un utilisateur simulé à stratégie prédéfinie ne peut pas refléter toute la complexité des comportements créatifs réels. Ces simulations servent donc surtout à vérifier la continuité de l’organisation des tâches, de l’état d’exécution et du processus de collaboration ; elles ne représentent pas directement le taux de réussite des tâches d’utilisateurs réels. La qualité des œuvres finales et l’expérience d’usage réelle doivent encore être évaluées à partir de vrais résultats de génération et des retours des utilisateurs.
11.3 Comment mesurer la qualité d’une collaboration
Au fil de ces simulations, j’ai compris qu’il est difficile d’évaluer complètement un Agent qui tourne dans la durée à la seule aune de la fluidité de ses réponses ou de la réussite de ses appels d’outils.
L’utilisateur se soucie peut-être davantage d’autres questions :
-
Combien de fois dois-je répéter mes explications ?
-
L’Agent s’est-il souvenu des exigences déjà arrêtées ?
-
Peut-il reprendre naturellement le travail quelques jours plus tard ?
-
Quand je modifie une œuvre, sait-il ce qui s’est passé ?
Au-delà de la justesse d’exécution classique, je me suis donc mis à suivre des dimensions comme la continuité des tâches, le coût d’intervention de l’utilisateur, les opérations répétées et la cohérence des productions.
Les simulations ont aussi mis au jour des problèmes difficiles à repérer en testant les modules isolément.
Par exemple, lors de la création d’une série d’affiches en anglais, Main comprenait correctement la demande de l’utilisateur et créait la tâche, mais le Worker basculait en chinois dans la planification suivante. Le problème ne venait pas du routage : la tâche ne recevait que la description d’objectif reformulée par le fil principal, sans l’information de langue contenue dans la formulation originale de l’utilisateur. Nous avons ensuite modifié la transmission de la langue de l’utilisateur, pour que la tâche maintienne sa langue de réponse d’après ce que l’utilisateur a réellement saisi, au lieu de s’en remettre entièrement à la déduction du modèle à partir de la description de la tâche.
Un autre problème est apparu à la jonction entre contrôle humain et génération asynchrone. L’Agent atteignait une étape exigeant la confirmation de l’utilisateur sans fournir de carte de confirmation actionnable ; ou bien, après l’approbation d’une génération par l’utilisateur, la tâche de génération externe se terminait normalement, mais la tâche de l’Agent n’avançait plus. Pour ces cas, nous avons ajouté dans le runtime un filet de sécurité pour les cartes de confirmation et un réveil automatique à la fin des générations, afin de moins dépendre du comportement interactif du modèle.
Pris isolément, ces problèmes ne se manifestent pas forcément comme des pannes d’exécution évidentes, mais ils affectent directement la continuité de la collaboration ressentie par l’utilisateur. Ils m’ont aussi permis de clarifier un principe : le modèle se charge de comprendre l’intention et de planifier les étapes, tandis que les états clés comme l’attente, l’autorisation et la reprise de l’exécution doivent être garantis de façon déterministe par le runtime.
Bien sûr, ces dimensions ne mesurent pas à elles seules le niveau d’intelligence de l’Agent. Sur une plateforme de création, l’achèvement du processus et la qualité de l’œuvre doivent toujours être évalués séparément. L’Agent peut générer tous les plans conformément au plan sans garantir pour autant que la vidéo finale réponde aux attentes de l’utilisateur en matière de style visuel, de cohérence des personnages et de rythme narratif. Une recette multimodale plus fiable reste une capacité que nous voulons continuer à améliorer.
D’après notre pratique actuelle, les simulations de bout en bout nous offrent un moyen de validation relativement maîtrisé. Elles nous aident non seulement à repérer les problèmes de collaboration entre modules, mais soumettent aussi certains principes de l’architecture à l’épreuve d’un processus d’exécution complet. Au-delà du simple renforcement des capacités des modèles, faire en sorte que ces capacités s’enchaînent de façon stable dans une collaboration au long cours mérite tout autant un investissement continu.
12. Questions ouvertes et pistes à explorer
Lomo Agent dispose aujourd’hui des capacités de base : organisation multi-tâches, exécution durable, gestion du contexte et espace de travail partagé. Mais de nombreuses questions restent à explorer avant d’atteindre l’agent créatif dont je rêve.
D’abord, la collaboration entre tâches.
La structure Router / Worker actuelle permet à plusieurs tâches d’avancer indépendamment et d’accéder au besoin aux productions partagées. Mais si l’on autorisait les Workers à s’entraider, à se déléguer des tâches, voire à négocier un budget partagé, il faudrait résoudre des problèmes bien plus complexes de permissions, de synchronisation d’état et de cohérence concurrente.
La collaboration multi-agents est séduisante, mais davantage d’Agents ne signifie pas naturellement davantage d’efficacité : le surcoût de coordination et de transmission du contexte peut annuler le bénéfice de la spécialisation. Je préfère d’abord repérer, à partir des besoins réels, les travaux qui méritent d’être découpés, avant d’élargir progressivement le champ de la collaboration.
Ensuite, la gestion des dépendances du processus créatif.
Le Production DAG évoqué plus haut est une piste qui m’intéresse particulièrement. Pour des productions complexes comportant plusieurs plans, personnages et décors, des dépendances explicites facilitent l’exécution parallèle, les mises à jour incrémentales et la réutilisation des résultats.
Toutefois, les dépendances d’une tâche créative ne sont pas toujours déterminées : quand l’utilisateur modifie un personnage, savoir quels plans doivent être régénérés et lesquels peuvent être conservés exige souvent d’examiner le contenu réel.
La fiabilité de la génération asynchrone peut, elle aussi, encore être améliorée.
Aujourd’hui, le système ne peut pas annuler de lui-même une tâche de génération déjà soumise en amont ; et si une tâche a été créée mais qu’une panne survient avant l’enregistrement de l’ID de tâche en amont, il ne dispose pas non plus de capacités de recherche inverse ni de rétablissement du lien.
Ces cas sont déjà couverts par une gestion d’état et des mécanismes de garantie financière, mais peuvent encore engendrer des coûts de génération inutiles. Nous souhaitons intégrer progressivement les capacités d’annulation et de consultation des tâches des services en amont, pour que le système gère plus précisément les interruptions d’exécution et les modifications de dernière minute demandées par l’utilisateur.
La recette qualité multimodale est elle aussi un problème difficile.
Aujourd’hui, un outil qui renvoie un succès ou une étape de la liste marquée terminée prouvent seulement que le système a effectué l’opération correspondante. La qualité réelle d’une œuvre fait intervenir la cohérence visuelle, la continuité du mouvement, la synchronisation image-son et l’esthétique subjective de l’utilisateur.
Avec les progrès des modèles multimodaux, j’espère intégrer peu à peu ces capacités d’évaluation au processus d’exécution, pour que l’Agent juge plus précisément quand ajuster, quand régénérer, et quand rendre la décision à l’utilisateur.
Par ailleurs, la précision de la mémoire à long terme, la fiabilité du routage des tâches et les limites du suivi proactif doivent elles aussi être validées en continu.
Le suivi de provenance et la gestion des versions rendent la mémoire plus maîtrisable, sans éviter totalement les erreurs d’extraction du modèle ni les rappels erronés d’informations passées. De même, bien que le mécanisme de routage actuel combine déjà l’index des tâches, la recherche dans l’historique et la détection des tâches similaires pour limiter les erreurs, il faudra encore, face au grand nombre de tâches accumulées sur la durée, mesurer à partir de données d’usage réelles la précision du rattachement des tâches et la fréquence à laquelle l’utilisateur doit corriger lui-même.
Quant au suivi proactif, le système peut savoir qu’il reste du travail sur une tâche sans savoir si l’utilisateur souhaite être dérangé à ce moment-là. Nous voulons que les Agents de demain soient plus proactifs, mais cette proactivité doit reposer sur l’autorisation de l’utilisateur, la valeur de la tâche et les habitudes d’usage réelles.
Ces questions n’auront pas toutes une réponse mûre à court terme. À mesure que Lomo Agent s’ouvrira à davantage de scénarios créatifs réels, nous réexaminerons sans cesse l’architecture actuelle, pour vérifier quelles conceptions sont réellement efficaces et lesquelles doivent être ajustées.
Conclusion
En repensant au parcours de Lomo Agent, de l’idée initiale à la mise en production, j’ai de plus en plus le sentiment que l’essor des Agents confronte l’architecture logicielle à de nouvelles questions passionnantes.
Autrefois, pour concevoir un logiciel, il fallait d’abord définir clairement les fonctionnalités, les processus et les frontières d’interaction, puis laisser l’utilisateur opérer selon ces règles. Maintenant que l’IA commence à savoir comprendre une intention, appeler des outils et décider de façon autonome, une bonne partie du travail d’organisation et de coordination qui incombait à l’utilisateur peut progressivement être confiée au système.
Mais les progrès des modèles ne sont qu’une partie de l’histoire.
Un Agent capable de collaborer dans la durée doit aussi savoir ce qu’il est en train d’accomplir, ce qu’il a déjà fait, quelles informations sont encore valides, et dans quels cas il doit continuer ou s’arrêter. Il doit faire face aux interruptions, à la concurrence, aux coûts et aux permissions d’un système réel, et permettre à l’utilisateur de changer d’avis à tout moment.
L’architecture de Lomo Agent est justement une mise en pratique autour de ces questions. Beaucoup d’idées viennent d’excellents produits et travaux techniques comme DeepSeek, Manus, Claude Code ou Temporal ; d’autres ont été trouvées à tâtons dans mes propres scénarios de création.
L’implémentation actuelle n’en est encore qu’au stade de l’exploration : il reste un long chemin vers la forme idéale, et de nombreuses conceptions doivent encore passer l’épreuve de vrais utilisateurs.
Je reste néanmoins convaincu qu’à mesure que les modèles progressent, la manière dont les humains interagissent avec les logiciels offre encore un immense champ des possibles.
Peut-être qu’un jour, nous n’aurons plus besoin d’apprendre à utiliser un logiciel : il nous suffira de lui dire ce que nous voulons accomplir.
C’est la direction vers laquelle nous espérons que Lomo Agent se rapprochera toujours davantage.
Si vous aussi construisez des Agents, n’hésitez pas à échanger avec nous sur vos expériences et vos questions d’architecture et d’ingénierie ;
si vous êtes créateur, venez essayer Lomo Agent avec vos vrais besoins créatifs.
Essayer Lomo Agent : lomo agent
Références et lectures complémentaires
Les liens ci-dessous servent surtout à illustrer les sources d’inspiration architecturales et le contexte de recherche ; ils ne signifient pas que chaque projet partage, sur toutes ses capacités, la même implémentation ou les mêmes objectifs de conception que Lomo Agent.
-
Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory
-
LongMemEval: Benchmarking Chat Assistants on Long-Term Interactive Memory
Note : dans cet article, nous avons distingué autant que possible les « mécanismes existants » des « pistes d’évolution ». Pour les garanties d’idempotence des services externes, le périmètre des données couvert par l’oubli, le comportement des caches des modèles et l’efficacité de la recette automatique, les capacités réelles dépendent de l’implémentation et des conditions propres à chaque dépendance.