L'interface du contrôleur d'intelligence artificielle (AICI) vous permet de construire des contrôleurs qui contraignent et dirigent la sortie d'un grand modèle de langage (LLM) en temps réel. Les contrôleurs sont des programmes flexibles capables d'implémenter le décodage contraint, l'édition dynamique des invites et du texte généré, et de coordonner l'exécution à travers plusieurs générations parallèles. Les contrôleurs intègrent la logique personnalisée pendant le décodage de jeton par-token et maintiennent l'état pendant une demande LLM. Cela permet diverses stratégies de contrôleur, du décodage programmatique ou basé sur la requête à des conversations multi-agents pour s'exécuter efficacement en intégration étroite avec le LLM lui-même.
Le but de l'AICI est de faciliter la construction et l'expérimentation des stratégies de contrôleur existantes et entièrement nouvelles pour améliorer les générations LLM. En abstraction des détails de mise en œuvre du moteur de l'inférence et du service LLM sous-jacent, l'AICI vise à simplifier le développement de contrôleurs, à faciliter l'écriture de contrôleurs rapides et à faciliter la compatibilité entre les moteurs de déduction et de service LLM.
AICI est conçu pour l'exécution locale et cloud, y compris (éventuellement) les déploiements LLM multi-locataires. Les contrôleurs sont implémentés sous forme de modules WebAssembly (WASM) de poids léger qui fonctionnent sur la même machine que le moteur d'inférence LLM, en utilisant le CPU tandis que le GPU est occupé avec la génération de jetons. AICI est une couche dans la pile d'inférence, et est conçue pour permettre aux bibliothèques de contrôle telles que Guidance, LMQL et autres de se dérouler et de gagner à la fois l'efficacité et les améliorations des performances, ainsi que la portabilité à travers les moteurs de l'inférence et de service LLM.
AICI s'intègre actuellement à Llama.cpp, HuggingFace Transformers et RLLM (moteur d'inférence LLM basé sur TCH personnalisé), avec VLLM en cours.
Aici est:
AICI est un prototype, conçu et construit chez Microsoft Research.
Dans ce quickstart, nous vous guiderons à travers les étapes suivantes:
Pour compiler les composants AICI, vous devez configurer votre environnement de développement pour la rouille. Pour ce QuickStart, vous avez également besoin de Python 3.11 ou ultérieure pour créer un contrôleur.
Note
Utilisateurs de Windows : veuillez utiliser WSL2 ou le DevContainer inclus. L'ajout de support Windows natif est suivi ici.
Utilisateurs de MacOS : veuillez vous assurer que les outils de ligne de commande Xcode sont installés en exécutant xcode-select -p et, s'ils ne sont pas installés, exécutez xcode-select --install .
CUDA : La construction CUDA repose sur une installation libtorch spécifique. Il est fortement recommandé d'utiliser le DevContainer inclus.
Si vous utilisez DevContainer, vous pouvez passer à la section suivante.
À l'aide du System Package Manager, installez les outils nécessaires pour le code de construction dans le référentiel, y compris git , cmake et ccache .
Par exemple dans WSL / Ubuntu en utilisant apt :
sudo apt-get install --assume-yes --no-install-recommends
build-essential cmake ccache pkg-config libssl-dev libclang-dev clang llvm-dev git-lfs
ou en utilisant Homebrew sur macOS:
brew install git cmake ccache
Ensuite, installez la rouille, la rustup et la cargaison , en suivant les instructions fournies ici et ici:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Après l'installation, vérifiez que la commande rustup --version est accessible en l'exécutant à partir du terminal. Si la commande n'est pas reconnue, essayez d'ouvrir une nouvelle session de terminal.
Installation suivante WasM32-Wasi Rust Component:
rustup target add wasm32-wasi
Si vous aviez déjà installé de la rouille ou si vous obtenez des plaintes de fret sur les versions obsolètes, exécutez:
rustup update
Enfin, pour travailler avec les contrôleurs et scripts Python (comme ce tutoriel), exécutez cette commande pour installer les packages requis:
pip install pytest pytest-forked ujson posix_ipc numpy requests
Le serveur RLLM a deux backends, l'un basé sur libtorch et Cuda ( rllm-cuda ), et l'autre basé sur llama.cpp ( rllm-llamacpp ).
Le backend rllm-cuda ne fonctionne que avec les GPU NVIDIA avec Calpept Capability 8.0 ou version ultérieure (A100 et plus tard; RTX 30X0 et plus tard) et nécessite une configuration fidèle de libtorch - il est fortement recommandé d'utiliser le DevContainer inclus. Bien que ce guide se concentre sur le backend rllm-llamacpp , les étapes de construction sont les mêmes pour rllm-cuda , modulo le nom du dossier.
Après la configuration Dev Env ci-dessus, clonez le référentiel AICI et procédez avec les étapes suivantes décrites ci-dessous.
Utilisez la commande suivante pour construire et exécuter aicirt et rllm-llamacpp :
cd rllm/rllm-llamacpp
./server.sh phi2
Vous pouvez transmettre d'autres noms de modèle comme argument (exécuter ./server.sh sans arguments pour voir les modèles disponibles). Vous pouvez également utiliser une URL HuggingFace vers .gguf ou un chemin local vers un fichier .gguf . (Pour rllm-cuda utilisez un modèle de modèle HuggingFace ou un dossier vers le dossier).
./server.sh orca
Vous pouvez trouver plus de détails sur rllm-llamacpp ici.
Le serveur RLLM fournit une interface HTTP, utilisée pour les tâches de configuration et les demandes de traitement. Vous pouvez également utiliser cette interface pour vérifier rapidement son statut. Par exemple, si vous ouvrez http://127.0.0.1:4242/v1/models, vous devriez voir:
{
"object" : " list " ,
"data" : [
{
"object" : " model " ,
"id" : " TheBloke/phi-2-GGUF " ,
"created" : 946810800 ,
"owned_by" : " owner "
}
]
}confirmant que le modèle sélectionné est chargé.
AICI permet d'héberger une logique personnalisée, appelée contrôleurs , qui initient, terminent et interagissent avec la génération de jetons LLMS. Les contrôleurs prennent des arguments d'entrée, les traitent et renvoient un résultat avec des journaux, des jetons LLM et des variables.
Le référentiel comprend quelques exemples, en particulier:
Dans cet exemple, nous utiliserons PYCTRL pour gérer la génération de jetons à l'aide d'un script Python simple. Si vous le souhaitez, vous pouvez créer et télécharger PYCTRL, mais par défaut, le serveur télécharge automatiquement la dernière version de PYCTRL depuis GitHub.
En général, les contrôleurs nécessitent la construction et le déploiement, tandis que les scripts (Python ou JavaScript) sont envoyés à chaque demande.
Ce qui suit illustre la relation entre le serveur RLLM, l'exécution AICI et le contrôleur:
erdiagramme
Hôte || - | {CPU: ""
Hôte || - | {gpu: ""
CPU || - || "Rllm Server": exécuter
CPU || - | {"AICI Runtime": Exécuter
"AICI Runtime" || - || "Contrôleur": instancier
GPU || - | {"Génération de jetons llm": Exécuter
Supposons que nous visons un modèle pour générer une liste, adhérant à un format spécifique et contenant seulement cinq éléments.
En règle générale, la réalisation de cela implique une ingénierie rapide, l'élaboration de l'invite précisément avec des instructions claires, telles que:
What are the five most popular types of vehicles?
Return the result as a numbered list.
Do not add explanations, only the list.
L'invite varierait également en fonction du modèle utilisé, étant donné que chaque modèle a tendance à ajouter des explications et comprend les instructions de différentes manières.
Avec AICI, nous reversons le contrôle au code, et nous pouvons simplifier l'invite pour:
What are the most popular types of vehicles?
Utilisation du code pour:
Créons un fichier list-of-five.py python avec le contenu suivant:
import pyaici . server as aici
# Force the model to generate a well formatted list of 5 items, e.g.
# 1. name 1
# 2. name 2
# 3. name 3
# 4. name 4
# 5. name 5
async def main ():
# This is the prompt we want to run.
# Note how the prompt doesn't mention a number of vehicles or how to format the result.
prompt = "What are the most popular types of vehicles? n "
# Tell the model to generate the prompt string, ie. let's start with the prompt "to complete"
await aici . FixedTokens ( prompt )
# Store the current position in the token generation process
marker = aici . Label ()
for i in range ( 1 , 6 ):
# Tell the model to generate the list number
await aici . FixedTokens ( f" { i } ." )
# Wait for the model to generate a vehicle name and end with a new line
await aici . gen_text ( stop_at = " n " )
await aici . FixedTokens ( " n " )
# Store the tokens generated in a result variable
aici . set_var ( "result" , marker . text_since ())
aici . start ( main ())L'exécution du script n'est pas trop différente de l'envoi d'une invite. Dans ce cas, nous envoyons la logique de contrôle et les instructions toutes ensemble.
Pour voir le résultat final, exécutez la commande suivante:
./aici.sh run list-of-five.py
Résultat:
Running with tagged AICI Controller: gh:microsoft/aici/pyctrl
[0]: FIXED 'What are the most popular types of vehicles?n'
[0]: FIXED '1.'
[0]: GEN ' Carsn'
[0]: FIXED '2.'
[0]: GEN ' Motorcyclesn'
[0]: FIXED '3.'
[0]: GEN ' Bicyclesn'
[0]: FIXED '4.'
[0]: GEN ' Trucksn'
[0]: FIXED '5.'
[0]: GEN ' Boatsn'
[0]: FIXED 'n'
[DONE]
[Response] What are the most popular types of vehicles?
1. Cars
2. Motorcycles
3. Bicycles
4. Trucks
5. Boats
response saved to tmp/response.json
Usage: {'sampled_tokens': 16, 'ff_tokens': 37, 'cost': 69}
Timing: {'http_response': 0.05193686485290527, 'data0': 0.05199289321899414, 'first_token': 0.0658726692199707, 'last_token': 0.1784682273864746}
Tokens/sec: {'prompt': 861.0913072488067, 'sampling': 89.65181217019571}
Storage: {'result': '1. Carsn2. Motorcyclesn3. Bicyclesn4. Trucksn5. Boatsnn'}
Ce référentiel contient un certain nombre de composants et lesquels vous avez besoin dépend de votre cas d'utilisation.
Vous pouvez utiliser un module de contrôleur existant . Nous fournissons PYCTRL et JSCTRL qui vous permettent respectivement de contrôleurs de script à l'aide de Python et JavaScript côté serveur. Le package Pyaici contient l'outil de ligne de commande aici qui vous permet de télécharger et d'exécuter des scripts avec n'importe quel contrôleur (nous fournissons également la définition de l'API REST pour le curieux).
? Python Code échantillons pour le script pyctrl et un monde bonjour javascript pour jsctrl
Nous prévoyons que les bibliothèques seront construites au-dessus des contrôleurs. Nous fournissons un exemple dans PromptLib - une bibliothèque Python côté client qui génère des interagsions avec Decctrl via le package Pyaici.
? Exemple de cahier qui utilise l'intervention pour interagir avec Decctrl.
Les contrôleurs peuvent être exécutés dans un moteur d'inférence LLM Cloud ou local compatible AICI. Vous pouvez exécuter localement le moteur de référence fourni (RLLM) avec le backend libtorch + cuda ou llama.cpp.
Pour développer un nouveau contrôleur , utilisez un projet de démarrage de Rust qui montre l'utilisation de la bibliothèque AICI_ABI, qui simplifie la mise en œuvre de l'interface AICI de bas niveau.
? Sample de code pour un nouveau contrôleur minimal pour vous aider à démarrer
Pour ajouter une prise en charge AICI à un nouveau moteur d'inférence LLM , vous devrez implémenter LLM-Side du Protocole qui parle de l'exécution AICI.
Enfin, vous voudrez peut-être modifier l'un des composants fournis - les PR sont les bienvenus!
AICI Abstracts LLM Inference Engine du contrôleur et vice-versa, comme dans l'image ci-dessous. Les nœuds arrondis sont ambitieux. Des couches supplémentaires peuvent être construites sur le dessus - nous fournissons Promptlib, mais nous pensons fortement que Guidance, LMQL, SGLANG, COUPLIONS, JSONFORMER, LMFE, etc. peuvent également fonctionner sur AICI (soit avec des contrôleurs personnalisés, soit en utilisant PYCTRL ou JSCTRL).
graphique TD
Pyctrl - Aici -> aicirt [aici-runtime]
JSCtrl - AICI -> AICER
Guidance ([Guidancier]) - AICI -> AICIR
LMQL ([LMQL CTRL]) - AICI -> AICIR
AICERT - POSIX SHM -> Rllm
AICERT - POSIX SHM -> LLAMA [LLAMA.CPP]
AICERT - POSIX SHM -> PYAICI
pyaici - python -> vllm (vllm)
pyaici - python -> hf [Hf Transformers]
Le package Pyaici facilite l'intégration de l'AICI avec les moteurs d'inférence LLM basés sur Python. Jetez un œil à l'intégration avec les transformateurs HuggingFace, mais notez qu'il ne prend pas en charge la fouth (génération de séquences multiples en parallèle). Le serveur de repos VLLM est actuellement obsolète. Veuillez utiliser Rllm-Cuda ou Rllm-Llama.cpp pour l'instant.
aicirt s'exécute dans un processus distinct et peut fonctionner sous un utilisateur différent de celui du moteur LLMaici_host_* , implémentées dans hostimpl.rsaicirt expose également une interface WASI partielle; Cependant, presque toutes les fonctions sont sans op fd_writeEn particulier, les modules WASM ne peuvent pas accéder au système de fichiers, au réseau ou à toute autre ressource. Ils ne peuvent pas non plus faire tourner les threads ou accéder à des minuteries (ceci est pertinent pour les attaques Spectre / Meltdown).
La plupart des calculs dans les contrôleurs AICI se produisent sur le CPU, en parallèle avec la génération logit sur le GPU. La génération se produit en étapes, où les logits sont générés en parallèle pour un nouveau jeton pour chaque séquence dans un lot (généralement entre 1 et 50). Cela implique la lecture de l'ensemble du modèle et des caches KV pour les séquences dans le lot de la mémoire GPU. Pour le débit optimal par lots, les caches modèles et KV devraient utiliser une fraction majeure de la mémoire GPU, et la lecture de la mémoire entière prend environ 40 ms sur A100 GPU (80 Go).
Ainsi, chaque étape de génération prend l'ordre de 20 à 50 ms. Avec une ingénierie minutieuse, cela est plus que suffisant pour calculer l'ensemble des jetons autorisés dans la roue compilés à WASM. Ceux-ci peuvent être combinés nativement en rouille, soit via des interprètes Python ou JavaScript que nous fournissons.
Par exemple, l'informatique est perminée par un jeton dans le vocabulaire 32000 du modèle LLAMA prend:
Les nombres ci-dessus sont pour une seule séquence, mais chaque séquence est traitée dans un processus distinct, et donc s'il y a plus de cœurs que de séquences (ce qui est typique), ils ne changent pas. Ils incluent également les frais généraux de l'appel dans l'interprète Python implémenté dans WASM, puis de retour dans le code WASM généré par la rouille pour la contrainte elle-même. Ils sont tous bien dans le budget de 20 à 50 ms, donc n'affecte pas du tout le temps de génération.
Il y a aussi quelques frais généraux dans le chemin critique de l'échantillonnage. Il se résume à environ 0,3 ms par étape de génération lors de l'exécution de 10 séquences en parallèle (c'est quelle que soit la contrainte utilisée). Les frais généraux montent à environ 0,7 ms pour 40 séquences (bien qu'il n'ait pas encore été entièrement optimisé).
WebAssembly est conçu pour avoir des frais généraux minimaux par rapport au code natif. D'après notre expérience, le code de rouille hautement optimisé est inférieur à 2x plus lent lorsqu'il est exécuté dans Wasmtime que natif. C'est 10-100x mieux que JavaScript ou Python.
Toutes les mesures effectuées sur AMD EPYC 7V13 avec GPU NVIDIA A100 avec 80 Go de VRAM.
L'interface de bas niveau fourni par AICI permet:
Il peut être utilisé à partir de n'importe quelle langue qui compile à Wasm.
Ce référentiel fournit une bibliothèque de rouille qui facilite la mise en œuvre des contrôleurs dans la rouille et fournit des implémentations efficaces de contraintes spécifiques (expressions régulières, grammaires YACC, sous-chaînes). Nous fournissons également des interprètes Python et JavaScript qui permettent de coller ces contraintes ensemble. Tous ces éléments peuvent être facilement étendus.
Si vous trouvez l'interface du contrôleur AI et ses idées pour définir une nouvelle couche dans la pile d'inférence LLM utile, veuillez citer le package en utilisant la référence suivante:
Bibtex:
@misc { Moskal2024 ,
author = { Moskal, Michal and Musuvathi, Madan and {Ki ci man}, Emre } ,
title = { {AI Controller Interface} } ,
year = { 2024 } ,
publisher = { {GitHub} } ,
journal = { {GitHub} repository } ,
howpublished = { url{https://github.com/microsoft/aici/} }
}Ce projet accueille les contributions et les suggestions. La plupart des contributions vous obligent à accepter un accord de licence de contributeur (CLA) déclarant que vous avez le droit de faire et en fait, accordez-nous les droits d'utilisation de votre contribution. Pour plus de détails, visitez https://cla.opensource.microsoft.com.
Lorsque vous soumettez une demande de traction, un bot CLA déterminera automatiquement si vous devez fournir un CLA et décorer le RP de manière appropriée (par exemple, vérification d'état, commentaire). Suivez simplement les instructions fournies par le bot. Vous n'aurez besoin de le faire qu'une seule fois sur tous les dépositions en utilisant notre CLA.
Ce projet a adopté le code de conduite open source Microsoft. Pour plus d'informations, consultez le code de conduite FAQ ou contactez [email protected] avec toute question ou commentaire supplémentaire.
Ce projet peut contenir des marques ou des logos pour des projets, des produits ou des services. L'utilisation autorisée de marques ou de logos Microsoft est soumise et doit suivre les directives de marque et de marque de Microsoft. L'utilisation de marques ou de logos de Microsoft dans des versions modifiées de ce projet ne doit pas provoquer de confusion ou impliquer le parrainage de Microsoft. Toute utilisation de marques ou de logos tiers est soumis aux politiques de ces tiers.