Sommaire
Salut les moule·e·s
Introduction
Je suis en train de passer un nouveau step dans mon parcours initiatique sur l'IA en jouant plus sérieusement avec les IA locales. Après mes balbutiements avec ollama, j'ai été convaincu par les différents journaux de Jérôme (merci !), notamment celui-ci de passer à l'étape d'après : llama.cpp et llama-swap.
On peut commencer à découvrir et apprendre sans aucun setup particulier : un modèle comme Qwen2.5-0.5B pèse quelque chose comme 500Mo en RAM et peut tourner sur un CPU ordinaire. L'avantage d'un modèle aussi petit c'est qu'il est bourré de défauts, et que c'est via les paramètres qu'on donnera à llama.cpp qu'on va essayer de les contenir au mieux. C'est vraiment intéressant à faire, je conseille à tous les curieux de l'IA de faire l'expérience.
Le standard OpenAI
Les façons de parler à un LLM (localement hébergé ou grassement payé à distance) sont plus ou moins standardisées (ce sont plutôt des standards de fait apportés par les acteurs les plus endettés principaux du domaine), et llama-swap propose l'API de OpenAI (ChatGPT). En gros tu prépares un fichier JSON avec la requête et quelques paramètres, tu envoies ça via une requête HTTP POST, et tu récupères un JSON de résultat. Pas plus compliqué que ça.
Par exemple une simple commande curl permet d'interroger son LLM :
curl http://localhost:8080/v1/chat/completions\
-H "Content-Type: application/json"\
-d '{
"model": "small",
"messages": [
{"role": "user", "content": "récite un poème à propos d'\''une moule et d'\''un clavier BEPO"}
]
}'
En retour on va recevoir quelque-chose comme (j'ai abrégé la réponse, ne vous inquiétez pas vous ne ratez rien):
{"choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"Dans les bras de la machine qui ne sait pas de langue,\nL'oreille s'épanouit sous l'action du clavier.\n[...]"}}],"created":1784911562,"model":"/disk/ia/models/Qwen2.5-0.5B-Instruct-Q4_K_M.gguf","system_fingerprint":"b10099-1a064ab09","object":"chat.completion","usage":{"completion_tokens":232,"prompt_tokens":48,"total_tokens":280,"prompt_tokens_details":{"cached_tokens":47}},"id":"chatcmpl-7tqzmDMzb8SKBr7xjSxUE4yvUrxvUhIz","timings":{"cache_n":47,"prompt_n":1,"prompt_ms":11.56,"prompt_per_token_ms":11.56,"prompt_per_second":86.50519031141869,"predicted_n":232,"predicted_ms":1101.727,"predicted_per_token_ms":4.748823275862069,"predicted_per_second":210.5784826912656}}
Script Python
Toujours dans le but de mettre les mains dans le cambouis, j'ai eu l'idée de faire un petit script Python qui gérerait ce petit manège question/réponse, et qui s’insérerait dans un flot de pipe shell. Il prend du contenu sur le stdin, il prend un prompt en paramètre, envoie tout ça à l'inférence locale, et affiche le résultat. C'est tellement simple que j'ai pu le faire sans aucune dépendance Python, au prix d'un code un poil plus moche toutefois : il existe des modules python dédiés qui vous feront faire la requête en une seule ligne, alors que là je la monte à la mimine (tout comme la mayonnaise) (j'ai d'ailleurs une méthode inratable, faudra que j'en fasse un journal tiens).
L'idée c'est de se faire un alias avec le petit nom que vous voulez. Par exemple alias gégé="python <chemin>/ia.py" et voilà, vous avez gégé dans votre shell qui vous résoudra tous vos problèmes, travaillera à votre place, et fera de vous une personne riche et respectée, achetez mon livre.
Exemples d'utilisation
Et le pire c'est que j'ai trouvé des exemples (presque) utiles:
cat l_albatros.txt | gégé "traduis en Espagnol" > el_albatros.txtcurl -fsSL https://random.malware.com/install.sh | gégé "est-ce que ce script est dangereux ?"cat /proc/cpuinfo | gégé "quelles sont les failles connues de mon CPU ?"cat ia.py | gégé "écris un script bash équivalent"
Même avec le tout petit Qwen2.5-0.7B on arrive à quelque chose. Certaines de ces requêtes marchent plutôt bien, d'autres ont un résultat farfelu, je vous laisse expérimenter ;).
Le script
import sys
import json
import argparse
import urllib.request
import urllib.error
def main():
parser = argparse.ArgumentParser(description='Send stdin and a prompt to an OpenAI-compatible API')
parser.add_argument('prompt', nargs='?', help='The prompt to send')
parser.add_argument('--model', default='small', help='Model name to use')
args = parser.parse_args()
if not args.prompt:
print("Usage: cat <file> | ai [--model MODEL] '<prompt>'", file=sys.stderr)
sys.exit(1)
prompt = args.prompt
input_content = sys.stdin.read()
model = args.model
try:
payload = {
"model": model,
"messages": [{"role": "user", "content": f"{input_content}\n\n{prompt}"}],
"stream": False
}
request = urllib.request.Request(
"http://localhost:8080/v1/chat/completions",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
method="POST"
)
with urllib.request.urlopen(request, timeout=600) as response:
data = json.load(response)
print(data["choices"][0]["message"]["content"])
except (urllib.error.URLError, urllib.error.HTTPError) as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Et c'est tout.
Pour tout dire mon script actuel est beaucoup plus évolué que ça, mais je ne le partage pas, ça n'a pas vraiment d'intérêt, il vaut vraiment mieux se le bricoler au fur-et-à-mesure des idées (et si vous avez les moyen de faire tourner un Qwen3.6-9B ou mieux en 27B, il pourra même vous y aider !). Par exemple dans les dernières versions je cherche des magic en début de flot stdin pour détecter des images. À ce moment je bascule sur un autre LLM qui sait prendre des images en entrée, et j'ai donc accès à des commandes comme cat chat_qui_fait_de_la_formule1.png | gégé "est-ce que cette photo a été générée par IA ?". Je gère un prompt système (du style "réponds brièvement"), j'affiche aussi quelques statistiques de la requête sur stderr etc.
La prochaine étape est d'entrer dans l'agentique : le script va pouvoir exécuter les commandes envoyées par le LLM (avec tout le danger qui va avec bien évidemment !). Mais du coup je pourrai bien voir comment marche un agent au moins pour la base.
Conclusion
Héberger une IA locale c'est aujourd'hui facile, et ça permet vraiment de toucher du doigt toutes les horreurs imperfections du domaine. On comprend aussi très vite le côté "potion magique" des différents paramètres utilisés : tu essaies un truc parce que tu l'as lu sur Reddit, tu regardes si c'est mieux (c'est quoi au juste "mieux" ?), tu essaies autre chose etc.
Je n'ai pas donné non plus la configuration llama-swap associée (pas vraiment d'intérêt non plus), mais je dois en être à une dizaine de paramètres que je comprends, sans toutefois les maîtriser. Mais pour ça j'attends les prochains journaux de Jérôme :)
En attendant si vous voulez vous amuser et comprendre, bidouiller ce petit bout de script Python est, je pense, une bonne entrée en matière.

Envoyer un commentaire
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.