4. Stratégie de solution
Cinq décisions structurantes traduisent les objectifs de qualité dans le code. Chacune est consignée sous forme de décision d'architecture ; cette page énonce le choix retenu et son raisonnement, puis renvoie vers la suite.
Acteurs Tokio à passage de messages, sans état partagé
Le cœur du runtime est un ensemble d'acteurs Tokio. Chaque acteur possède son
état de façon exclusive et ne communique qu'au travers de canaux mpsc
bornés, exposés derrière un handle clonable. Aucun Arc<Mutex<T>> n'est
partagé entre acteurs. Cela rend structurellement impossible pour un acteur de
modifier l'état d'un autre, ce qui transforme le principe « un acteur, une
responsabilité » en propriété imposée par la structure du code plutôt qu'en
simple convention. Le prix à payer est davantage de plomberie de messages ; le
gain est l'absence de toute une classe d'interblocages asynchrones et de
courses de données.
Voir le modèle d'exécution, qui s'appuie sur cette base.
L'inférence comme sidecar supervisé
L'inférence LLM locale s'exécute dans un processus séparé, pas dans le
daemon : le llama-server embarqué (upstream llama.cpp), que le daemon lance
et avec lequel il dialogue via son API HTTP compatible OpenAI. L'appel
d'outils natif est piloté par le template de chat du modèle (--jinja), pas
par un chemin de grammaire personnalisé. Isoler l'inférence évite qu'un
plantage du modèle ou une saturation mémoire n'emporte le runtime, et
cantonne le dialogue entre le runtime et le moteur à une interface étroite.
Le batching continu décode plusieurs requêtes dans une même passe, et suivre
l'upstream llama.cpp élargit la gamme d'architectures de modèles prises en
charge.
Le daemon supervise ce processus : il sonde la vie de chaque instance du moteur et relance celle qui est morte, avec un délai d'attente qui croît d'une seconde à trente. Un modèle évincé volontairement n'est pas relancé. Voir l'inférence locale.
Un pont PyO3 avec des services découplés par traits
Le code qu'un paquet apporte est en Python ; le runtime est écrit en Rust. Le
pont est PyO3, avec pyo3-async-runtimes pour l'interopérabilité asynchrone. Le
runtime ne livre pas ses structures internes brutes à Python : il expose un
ensemble de services (l'objet ctx) derrière des traits Rust, ce qui découple
le contrat de son implémentation et le rend simulable (mock) pour les tests. Le
contrat de types nomme dix-sept services, et ce qu'un appel reçoit dépend de ce
qu'il est : un outil de paquet s'exécute avec un contexte qui ne porte ni
modèle, ni proxy d'outils, ni mémoire, et un workflow de paquet y ajoute
ctx.llm, ctx.agents, ctx.workflows et ctx.user.
Voir socle technique et runtime et le contrat d'agent ; ctx est
est documenté dans la référence du SDK.
Un contrat machine pour l'intégration hôte
Le runtime est conçu pour être piloté par un produit hôte : sa surface HTTP
est donc un contrat stable de premier ordre, pas une réflexion après coup. La
spécification OpenAPI est générée à partir du code, servie par le daemon, et
versionnée (/api/v1, les changements cassants étant réservés à un futur
/api/v2). Des SDK hôtes typés en TypeScript et en Python sont générés à
partir de cette spécification. Un intégrateur pilote un daemon réel sans
avoir à rétro-ingénierer quoi que ce soit.
Voir intégration hôte. La surface générée est la référence de l'API HTTP ; le guide pratique est Intégrer via le contrat pilote.
La gouvernance vit dans le runtime, pas dans l'agent
Le budget d'étapes est appliqué par le runtime autour de chaque agent, et non implémenté par chaque agent individuellement, et il borne chaque appel d'outil quel que soit le chemin sur lequel il s'exécute. L'enregistrement va dans un seul registre : un appel d'outil qu'une exécution annonce est écrit, par ses événements rattachés à l'exécution, dans le journal chaîné par hachage. Le palier d'autonomie atteint une exécution orchestrée de la même façon que le budget, par le moteur qui la planifie et l'exécute.
Les règles de permission et l'approbation humaine vivent au même endroit. Chaque appel d'outil d'un agent est tranché avant de s'exécuter par une seule décision de gouvernance, dans une conversation, dans une tâche, dans une étape de plan comme dans un élément de lot : la politique de l'organisation et une règle de refus le refusent, un outil qui ne fait que lire s'exécute, et tout ce qui est sensible s'exécute sur un accord permanent de l'agent ou demande à une personne. Un agent est une fiche et ne porte aucun code : il n'a donc aucun chemin pour contourner cette décision. Ce que la décision ne voit pas, c'est le Python d'un paquet : une fois qu'un outil de paquet est autorisé à s'exécuter, son code tourne dans le processus du runtime avec les droits de ce processus, et c'est pourquoi installer un paquet est l'acte de confiance. Le modèle de confiance des agents énonce cette frontière. Le tableau complet se trouve dans Concepts transversaux et dans le modèle de responsabilité.
Les décisions qui encadrent cela sont le modèle de permissions, l'humain dans la boucle, et secrets et authentification API.