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.

Version 1.0 · Mis à jour

01

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.

WORKER (instance de calcul) workerEngine pipeline déterministe · orchestre toutes les étapes harness tour agentique pur le modèle décide appelé, jamais orchestrateur outils surfaces de capacité portés par des providers dispatch uniforme code interne logique directe déterministe pas de modèle outils → portés par des providers (satellites si auto-hébergés sur le bus)
La pile Factory. Un worker fait tourner un workerEngine qui appelle trois sortes d'étapes — le harness, les outils et le code interne. Les outils sont portés par des providers ; un provider qui tourne en processus auto-hébergé sur le bus NATS avec un heartbeat 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.

02

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.

03

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.

COMPOSITION ↑ L3 — Sous-agent agent imbriqué isolé · remonte un résumé · harness NATS L2 — Skill composite couche-instruction (SKILL.md) · guide le modèle · injection de prompt L1 — Macro chaîne déterministe · pas de second modèle · en-proc ou distant L0b — Atomique distant provider sur le bus · découvert par heartbeat · NATS / stdio / HTTP L0a — Atomique intégré compilé en-processus · toujours disponible · bash · fichier · web fetch DISPATCH UNIFORME
Cinq couches, un dispatcher. D'une primitive intégrée à un sous-agent récursif, chaque outil franchit la même porte de contrôle d'accès et renvoie le même type de résultat — seul le transport change en dessous.
04

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 protocoleInProcessTool, 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.

05

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 plomberie

C'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)
Satellite provider · auto-hébergé sur le bus NATS voiceCLI · imageCLI · adapter Postiz heartbeat ✓ conteneur qui tourne ≠ heartbeat — le heartbeat vient de notre adapter Provider non-satellite provider · pas de processus bus · pas de heartbeat API X · API GitHub · CLI one-shot heartbeat ✗ HTTP en-proc ou stdio — pas d'identité NATS
Deux formes de provider. Un satellite tourne sur le bus, émet un heartbeat, et a une identité NATS — le moteur le découvre automatiquement. Un provider non-satellite (API cloud, CLI one-shot) est appelé directement, sans empreinte sur le bus.

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.

06

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.