Les applications d’intelligence artificielle développées en Rust passent progressivement des premières expérimentations à des modèles plus pratiques, comme l’a montré une présentation en direct organisée par JetBrains en collaboration avec la Rust Foundation. La première session s’est concentrée sur la bibliothèque open source Rig et sur la manière de l’utiliser pour créer des applications reposant sur de grands modèles linguistiques ainsi que des agents capables d’appeler des outils et d’exécuter des tâches au sein de l’application.
Orhun Parmaksız, Developer Advocate, a présenté un petit agent logiciel qu’il a construit avec Rig et Ratatui, tandis que Stephen Korzeniewski, responsable principal de Rig chez 0xPlaygrounds, a expliqué l’architecture interne du projet et les choix qui sous-tendent ses interfaces de programmation.
Une interface unifiée pour les fournisseurs de modèles
Rig traite un problème pratique courant dans les applications de modèles linguistiques : OpenAI, Anthropic, Gemini et les autres disposent chacun d’interfaces de programmation différentes, même lorsque certains services annoncent leur compatibilité avec l’interface d’OpenAI. Au lieu de relier directement certaines parties de l’application à l’interface d’un seul fournisseur, Rig propose une interface Rust unifiée permettant de changer de fournisseur sans réécrire les parties liées au modèle.
La bibliothèque organise l’application autour de plusieurs composants essentiels, notamment le client du fournisseur, le modèle de complétion, l’agent et les outils dont il dispose. L’agent fonctionne comme une couche supérieure à la requête directe adressée au modèle : il ajoute les instructions, les limites de jetons et les capacités nécessaires à l’exécution de tâches qui dépassent le simple flux texte-entrée et texte-sortie.
Les instructions de l’agent sont transmises dans ce que Rig appelle un preamble, c’est-à-dire des instructions qui jouent le rôle d’une invite système et sont insérées au début de chaque requête. Les communications réseau avec les fournisseurs de modèles sont gérées par des interfaces asynchrones, tandis qu’une grande partie des détails du fonctionnement asynchrone reste à l’intérieur de la bibliothèque, afin que le code de l’application se concentre sur la configuration et le comportement souhaités.
Les outils font de l’agent une partie de l’application
Rig définit un outil au moyen de l’interface Tool trait de Rust. La définition de l’outil comprend son nom, les types de ses entrées, de ses sorties et de ses erreurs, ainsi que la fonction exécutée lorsqu’il est appelé. Elle décrit également les arguments attendus à l’aide de JSON Schema, afin que le modèle reçoive une description structurée des données qu’il peut fournir.
Après l’enregistrement de l’outil auprès de l’agent, un modèle prenant en charge les appels d’outils peut déterminer quand l’utiliser et intégrer son résultat à la réponse. La fonction peut effectuer un calcul, interroger une base de données, récupérer des informations ou se connecter à un autre service. L’outil peut également recevoir un contexte conservant un état, comme le nombre d’appels ou les résultats mis en cache. L’idée s’étend aux agents eux-mêmes : un agent peut être proposé comme outil à un autre agent afin de leur permettre de se déléguer des tâches.
De la démonstration expérimentale à une application testable
Le projet Rat Code a réuni ces composants dans un petit agent logiciel fonctionnant dans le terminal avec Rig et Ratatui. Le fichier main.rs initialise le client du fournisseur, le modèle et l’agent, tandis que les capacités de lecture et d’écriture de fichiers ainsi que d’exécution de commandes shell sont définies séparément, puis enregistrées auprès de l’agent via l’interface des outils de Rig.
L’application utilise également l’interface de diffusion en continu de Rig pour traiter la réponse par lots et mettre à jour l’interface du terminal pendant que le modèle génère sa réponse. La présentation a également abordé le système de hooks, le mécanisme de récupération après les appels d’outils et les choix de conception qui sous-tendent les abstractions fournies par la bibliothèque. La présentation du code complet commence dans l’enregistrement à 27:28.
RAG, modèles locaux et tests
Rig ne se limite pas aux fournisseurs de modèles hébergés. Elle prend en charge les applications de génération augmentée par récupération, dans lesquelles les documents et les requêtes des utilisateurs sont convertis en représentations vectorielles, puis les documents sémantiquement les plus proches sont recherchés afin d’être ajoutés au contexte du modèle. La bibliothèque fournit des intégrations avec des bases de données et des abstractions pour les magasins de vecteurs, avec la possibilité d’implémenter l’interface d’un magasin de vecteurs lorsqu’une base de données n’est pas prise en charge directement.
Il est également possible d’exécuter des modèles locaux via Ollama et llama.cpp, ou d’utiliser l’intégration rig-candle pour effectuer l’inférence directement dans une application Rust. Cette option permet d’intégrer les poids du modèle à l’application et d’exécuter des modèles pris en charge via WebAssembly, sans dépendre d’une interface de modèle hébergée ni d’un serveur d’inférence local distinct.
L’importance de ces options apparaît dans le lieu d’exécution des modèles et de stockage des données, mais elles posent en même temps un défi pour les tests des intégrations. Rig s’appuie principalement sur un système d’enregistrement : les tests sont exécutés avec les fournisseurs réels et le trafic HTTP est conservé dans des fichiers YAML, puis un serveur simulé rejoue les requêtes et les réponses dans les tests d’intégration continue. Le projet comprend environ 1 700 interactions enregistrées, qui sont rejouées à chaque demande de fusion en quelques secondes.
Ces tests vérifient que l’intégration logicielle continue de fonctionner, mais ils ne mesurent pas la qualité des sorties du modèle, qui peut changer lorsque le fournisseur met à jour son modèle. La présentation a donc évoqué les tests planifiés sur des modèles actifs comme une méthode distincte pour les applications dépendant de la qualité de la réponse.
Lecture éditoriale : qu’est-ce qui compte pour les développeurs ?
La valeur pratique de Rig ne réside pas seulement dans l’ajout d’un nouveau fournisseur, mais dans la séparation de la couche applicative des détails des interfaces de modèles, tout en conservant les outils, la récupération et l’inférence locale au sein d’une même architecture Rust. Cela peut réduire le coût du changement de fournisseur ou de méthode d’exécution, mais n’élimine pas les différences de comportement entre les modèles et ne résout pas à lui seul le problème de l’évaluation de la qualité des sorties. Le mécanisme de test du projet montre que garantir le fonctionnement de la connexion est une chose, tandis que vérifier que le modèle produit le résultat attendu en est une autre, qui nécessite des tests en conditions réelles et des critères d’évaluation indépendants.