MODÈLE DE RÉFÉRENCE
Architecture des outils
Le modèle conceptuel derrière la couche outils de Roxabi Factory — cinq couches, deux taxonomies, un vocabulaire runtime précis, et les deux discriminants qui gardent l'ensemble cohérent.
La pile
Chaque capacité que la Factory expose à un agent s'inscrit dans une pile à couches. À la base, un worker — une instance de calcul — fait tourner un workerEngine : un pipeline codé, déterministe. Ce moteur appelle trois sortes d'étapes : le harness (un tour agentique pur, où le modèle décide), les outils eux-mêmes, et du code interne simple. Au-dessus du moteur, les outils sont portés par des providers ; quand un provider est un processus auto-hébergé sur le bus avec un heartbeat, c'est un satellite.
Le harness est un appelé du moteur, pas une de ses saveurs. De l'agentique là où l'on veut une décision, du code là où l'on veut une garantie — les deux sont empilés, jamais unifiés.
Vocabulaire runtime
Six termes, définis avec précision, empêchent le modèle de dériver. L'ambiguïté historique de worker (autrefois utilisé à la fois pour les instances de calcul et les providers d'outils) est résolue ici.
| Terme | Nature | Définition |
|---|---|---|
workerEngine |
plomberie · runtime | Pipeline codé, déterministe ; orchestre le harness, les outils et le code interne. |
harness |
plomberie · agentique | Un tour agentique pur — le LLM décide, ses outils du tour s'exécutent. Pas de logique de pipeline. Appelé par le workerEngine. |
worker |
plomberie · calcul | Une instance déployée qui fait tourner un workerEngine. Consomme des outils. Ni outil, ni provider. |
tool |
nature outil | Une surface de capacité invoquée par le harness ou le workerEngine. |
provider |
plomberie · backend | Le rôle qui porte un ou plusieurs outils. Indépendant du transport. Un provider expose plusieurs outils. |
satellite |
plomberie · déploiement | Un provider déployé en processus NATS auto-hébergé avec un heartbeat. Tout satellite est un provider ; tout provider n'est pas un satellite. |
Un terme délibérément exclu : un backing service — une application auto-hébergée complète comme un fork Postiz (serveur web, base de données, Redis) — est de l'infrastructure, comme Postgres. Un conteneur qui tourne n'est pas un heartbeat. Le heartbeat vient de notre adapter NATS placé devant. Backing service, provider, outil : trois couches, jamais confondues.
Taxonomie A — Cinq couches
Les outils ne sont pas uniformément atomiques. Le modèle compte cinq couches de composition, d'un appel compilé unique jusqu'au sous-agent récursif — et un dispatcher uniforme les atteint toutes.
| Couche | Nom | Ce que c'est | Transport |
|---|---|---|---|
| L0a | Atomique intégré | Compilé dans le moteur, en-processus, toujours disponible. bash, lecture/écriture de fichier, web fetch. |
— |
| L0b | Atomique distant | Un provider sur le bus, découvert par heartbeat. image.generate, voice.tts. |
NATS / stdio / HTTP |
| L1 | Macro | Une chaîne déterministe de primitives — sans second modèle dans la boucle. | en-proc ou distant |
| L2 | Skill | Un composite de couche-instruction (un document SKILL.md) qui guide le modèle à travers une séquence métier. |
injection de prompt |
| L3 | Sous-agent | Un appel d'agent imbriqué et isolé qui remonte un résumé. | harness NATS |
Le choix décisif est un dispatcher uniforme : une seule porte de contrôle d'accès, un seul type de résultat, le transport variant en dessous. Pour la boucle de raisonnement du modèle, un image.generate distant et un bash intégré sont identiques — même forme, même type de résultat. La différence de transport est invisible.
Taxonomie B — Nature des domaines
Les contrats wire NATS sont découpés par nature, pas par fonction. C'est l'axe qui organise le plan d'outils.
Un domaine est de nature outil quand sa seule raison d'être est d'exposer une capacité que le modèle invoque — voix, image, et les satellites à venir. Tout le reste est de la plomberie : le transport d'inférence, le bus de jobs, la persistance, la sécurité, l'observabilité, la livraison.
| Nature | Domaines | Préfixe sujet |
|---|---|---|
| outil | voice · image · +futur : postiz, vault, scrape, camoufox |
factory.tool.<cap>.* |
| plomberie | cli · llm · jobs · gh · turns · blob · verify · audit · event · outbound |
factory.<domain>.* |
Les sujets de nature outil portent un infix tool. pour que le côté consommateur puisse s'abonner au plan entier d'outils d'un seul coup avec factory.tool.>, et découvrir ce qui est disponible via un registre unique — sans logique d'abonnement par type.
La forme des packages reflète ce découpage : roxabi-contracts/tool/{voice,image,…} contient les schémas wire de chaque domaine outil ; roxabi-contracts/tools/ contient la couche protocole — InProcessTool, RemoteTool, ToolManifest.
Une fois que les outils forment un plan, le harness et les workers les découvrent et les adressent comme un tout. Cette adressabilité est le gain de la séparation.
Les deux discriminants
Deux règles maintiennent la précision du modèle. Sans elles, les frontières dérivent.
Règle 1 — surface d'outil ≠ nature d'outil
Un domaine de plomberie peut exposer un outil sans être de nature outil. Le domaine gh expose un gh.list_prs intégré ; le domaine llm expose llm.generate — les deux restent de la plomberie. Exposer un outil ne promeut pas le domaine wire.
surface d'outil ≠ nature d'outil
plomberie github expose un outil intégré → reste plomberie
substrat d'inférence expose un appel generate → reste plomberieC'est cette règle qui garde la séparation honnête, au lieu de laisser tout dériver vers « outil ». Le test n'est pas de savoir si un domaine expose un appel en forme d'outil — c'est de savoir si la raison d'être entière du domaine est l'exposition de capacités.
Règle 2 — provider ⊃ satellite
Tout provider n'est pas un satellite. Un provider n'est un satellite que lorsqu'il est un processus auto-hébergé qu'on fait tourner sur le bus NATS avec un heartbeat. Une API cloud qu'on appelle, ou un outil en ligne de commande one-shot, est un provider sans l'empreinte d'un satellite.
provider ⊋ satellite
processus auto-hébergé sur le bus → satellite (heartbeat : voiceCLI, imageCLI, notre adapter Postiz)
API cloud / CLI stdio one-shot → provider (pas de heartbeat : X, GitHub, un outil bash)Et la troisième couche à ne jamais confondre : un backing service — une application entière comme notre fork Postiz (serveur web, base de données, Redis) — est de l'infrastructure, comme Postgres. Le backing service n'est pas le provider ; le provider, c'est notre adapter NATS placé devant.
Backing service → provider → outil : trois couches. Un conteneur qui tourne n'est pas un heartbeat.
Invariants clés
Six invariants tiennent le modèle ensemble. Si l'un est violé, la taxonomie devient incohérente.
- Worker ≠ outil. Un worker consomme des outils et tourne sur un workerEngine. Ce n'est ni un outil, ni un provider.
- Le harness est purement agentique. Le harness n'a aucune logique de workflow. Il est appelé par le workerEngine — empilé, jamais unifié avec lui.
- Dispatch uniforme. Une seule porte de contrôle d'accès, un seul
ToolResult, indépendant du transport. Pas de pipeline ramifié par type. - surface d'outil ≠ nature d'outil. Un domaine de plomberie qui expose un appel en forme d'outil reste de la plomberie. Exposer un outil ne promeut pas le domaine wire.
- provider ⊋ satellite. Auto-hébergé sur le bus avec heartbeat → satellite. Cloud / one-shot → provider non-satellite. Un conteneur backing qui tourne n'est pas un heartbeat.
- worker = calcul-sur-moteur. Le mot « worker » désigne cela, pas un outil ou un provider. Les choses qui portent les outils sont des providers ; la variante déployée sur NATS est un satellite.
Ce modèle est verrouillé (validé le 2026-06-02). L'implémentation est parkée en attente de la migration des sujets du bus en cours — le design est arrêté pour pouvoir construire proprement une fois la migration terminée.