- LLM 0.26 est une version majeure qui permet, depuis le CLI et la bibliothèque Python, de connecter aux modèles locaux OpenAI, Anthropic, Gemini et Ollama des outils créés sous forme de fonctions Python
- Les outils peuvent être installés comme plugins et chargés avec
--tool/-T name_of_tool, ou bien utilisés via --functions en passant directement en ligne de commande des fonctions Python temporaires
- Outre les outils intégrés
llm_version et llm_time, des plugins simpleeval, QuickJS, SQLite et Datasette sont proposés, et le modèle peut réessayer après un appel échoué en consultant le schéma ou en utilisant une autre expression
- Le nouveau
model.chain() de l’API Python détecte les demandes d’appel d’outil, les exécute puis renvoie le résultat au modèle, avec prise en charge à la fois des fonctions synchrones et des outils asyncio
- L’implémentation est devenue possible avec la convergence des approches d’utilisation d’outils et d’appel de fonctions chez plusieurs fournisseurs ; les prochains chantiers portent sur l’extension des plugins, la prise en charge d’un plus grand nombre de plugins de modèles et le support client du Model Context Protocol
Prise en charge de l’exécution d’outils dans LLM 0.26
- LLM 0.26 ajoute la prise en charge des outils, la plus grande fonctionnalité depuis le lancement du projet
- Depuis le CLI LLM et la bibliothèque Python, il est possible de donner aux modèles locaux OpenAI, Anthropic, Gemini et Ollama l’accès à des outils pouvant être représentés par des fonctions Python
- En installant de nouveaux plugins d’outils, on peut ajouter de nouvelles capacités au modèle actuellement utilisé
- Il existe quatre principaux modes d’utilisation
- Installer un outil sous forme de plugin et le charger avec
--tool/-T name_of_tool
- Passer directement du code de fonction Python en ligne de commande avec l’option
--functions
- Transmettre aussi des outils via l’API Python, par exemple avec
tools=[locals]
- Les outils fonctionnent aussi bien dans des contextes asynchrones que synchrones
Exécuter des outils depuis le CLI
- La dernière version de LLM peut être installée avec
uv tool install llm, et une installation existante peut être mise à niveau avec uv tool upgrade llm
- Exemple avec OpenAI : après avoir configuré la clé d’API avec
llm keys set openai, exécuter la commande suivante
llm --tool llm_version "What version?" --td
llm_version est un petit outil de démonstration inclus dans LLM, et --tool llm_version expose cet outil au modèle
--tool peut être indiqué plusieurs fois, et l’option courte -T est également disponible
--td est l’abréviation de --tools-debug et affiche les appels d’outils et les informations de réponse afin de vérifier le fonctionnement interne
- Le modèle par défaut est généralement
gpt-4o-mini ; dans l’exemple, llm models default gpt-4.1-mini définit gpt-4.1-mini comme valeur par défaut
- L’option
-m permet de choisir un autre modèle, et il existe aussi un exemple exécutant o4-mini avec l’outil intégré llm_time
llm --tool llm_time "What time is it?" --td -m o4-mini
- La réponse de l’outil
llm_time inclut des champs comme utc_time, utc_time_iso, local_timezone, local_time, timezone_offset et is_dst
Fonctionnement avec plusieurs modèles et plugins
- Le même schéma de commande peut être utilisé avec les plugins de modèles qui prennent en charge les outils
- L’exemple avec Anthropic Claude Sonnet 4 suit le flux suivant
llm install llm-anthropic -U
llm keys set anthropic
llm --tool llm_version "What version?" --td -m claude-4-sonnet
- L’exemple avec Google Gemini 2.5 Flash suit le flux suivant
llm install llm-gemini -U
llm keys set gemini
llm --tool llm_version "What version?" --td -m gemini-2.5-flash-preview-05-20
qwen3:4b exécuté avec Ollama peut lui aussi lancer un outil simple
qwen3:4b est un petit modèle de 2,6 Go
ollama pull qwen3:4b
llm install 'llm-ollama>=0.11a0'
llm --tool llm_version "What version?" --td -m qwen3:4b
Plugins d’outils pour les maths, JavaScript, SQLite et Datasette
- Les tâches sur lesquelles les LLM sont faibles, comme la multiplication de grands nombres, peuvent être compensées par des appels d’outils
- llm-tools-simpleeval expose la bibliothèque simpleeval de Daniel Fairhead
- simpleeval est une bibliothèque “Simple Safe Sandboxed Extensible Expression Evaluator for Python”
- Elle fournit un sandbox suffisamment robuste pour exécuter de simples expressions Python
- L’exemple de calcul s’exécute ainsi
llm install llm-tools-simpleeval
llm -T simple_eval 'Calculate 1234 * 4346 / 32414 and square root it' --td
- Dans l’exécution de l’exemple, le modèle calcule d’abord
1234 * 4346 / 32414 et obtient 165.45208860368976, puis échoue parce que sqrt(...) n’est pas défini, avant de le remplacer par ** 0.5 et d’obtenir 12.862818066181678
- Quatre plugins d’outils publics sont disponibles
- llm-tools-simpleeval : prise en charge d’expressions simples, notamment mathématiques
- llm-tools-quickjs : accès à un interpréteur JavaScript QuickJS en sandbox ; l’environnement persiste entre les appels, ce qui permet de réutiliser variables et fonctions
- llm-tools-sqlite : accès en lecture seule à des requêtes SQL sur une base de données SQLite locale
- llm-tools-datasette : exécution de requêtes SQL sur une instance Datasette distante
Boîte à outils Datasette et nouvelle tentative après erreur
- Le plugin Datasette fonctionne sous forme de boîte à outils contenant plusieurs outils
- Une boîte à outils est un plugin configurable au moyen d’un constructeur
- Exemple d’utilisation
llm install llm-tools-datasette
llm -T 'Datasette("https://datasette.io/content")' --td "What has the most stars?"
Datasette("https://datasette.io/content") transmet au plugin l’URL de l’instance Datasette à utiliser
- Cet exemple cible la base de données content qui alimente le site web Datasette
- Le modèle effectue trois appels
- Il commence par deviner
SELECT name, stars FROM repos ORDER BY stars DESC LIMIT 1, mais échoue car la colonne stars n’existe pas
- Après avoir reçu l’erreur, il interroge le schéma de la base de données avec l’outil
Datasette_schema()
- À partir du schéma, il compose et exécute la bonne requête
- Selon la réponse finale, le dépôt qui a le plus d’étoiles est
datasette, avec 10 020 stars
Créer des outils temporaires avec --functions
- L’option
--functions offre une manière moins structurée que les plugins d’utiliser des outils temporaires
- Si l’on passe directement un bloc de code Python en ligne de commande, les fonctions qui y sont définies deviennent des outils utilisables par le modèle
- Exemple d’outil de recherche de blog : il appelle une page de recherche avec
httpx et renvoie tel quel le HTML au modèle
llm --functions '
import httpx
def search_blog(q):
"Search Simon Willison blog"
return httpx.get("https://simonwillison.net/search/", params={"q": q}).content
' --td 'Three features of sqlite-utils' -s 'use Simon search'
- L’implémentation se contente de renvoyer le HTML brut de la page de recherche, mais cela fonctionne
- La famille GPT-4.1 étant capable de traiter 1 million de tokens, le HTML brut est considéré comme gérable
- Il faut ajouter
use Simon search au prompt système pour que le modèle utilise l’outil de recherche fourni au lieu de répondre par lui-même
- Un meilleur outil de recherche aurait besoin d’instructions plus détaillées et d’extraits pertinents des résultats
- Dans le résultat de l’exemple, les fonctionnalités de
sqlite-utils citées sont la combinaison d’un CLI et d’une bibliothèque Python, l’ajout automatique de colonnes via alter=True, et la prise en charge des plugins
Prise en charge des outils dans l’API Python
- LLM est à la fois un outil CLI et une bibliothèque Python, et la version 0.26 ajoute aussi la prise en charge des outils à l’API Python
- L’exemple résout avec une fonction outil le problème consistant à compter le nombre de
r dans “strawberry”
import llm
def count_char_in_text(char: str, text: str) -> int:
"How many times does char appear in text?"
return text.count(char)
model = llm.get_model("gpt-4.1-mini")
chain_response = model.chain(
"Rs in strawberry?",
tools=[count_char_in_text],
after_call=print
)
for chunk in chain_response:
print(chunk, end="", flush=True)
after_call=print est une manière de voir les appels d’outils, comme l’option --td ci-dessus
- La nouvelle méthode
model.chain() ressemble à model.prompt(), mais détecte les demandes d’appel d’outil renvoyées, les exécute, puis rappelle le modèle avec les résultats
model.chain() peut exécuter plusieurs réponses avant de fournir la réponse finale
- En itérant sur
chain_response, on peut afficher en streaming les tokens renvoyés sur plusieurs réponses
- Dans le résultat de l’exemple, l’outil
count_char_in_text est appelé avec char='r' et text='strawberry', puis, à partir du résultat 3, répond que strawberry contient trois r
- La bibliothèque Python prend aussi en charge
asyncio, et les outils peuvent être des fonctions async def
- Si le modèle demande plusieurs outils asynchrones à la fois, la bibliothèque les exécute en parallèle avec
asyncio.gather()
- Les boîtes à outils sont également prises en charge : passer
tools=[Datasette("https://datasette.io/content")] à chain() produit le même effet que --tool 'Datasette(...)' dans le CLI
Contexte d’implémentation et schéma d’utilisation des outils
- Le schéma d’utilisation des outils est suivi depuis sa première observation dans le papier ReAcT, publié en octobre 2022
- Le schéma de base est simple
- Indiquer au modèle qu’il dispose d’outils utilisables
- Le modèle demande une action d’outil et s’arrête, via une syntaxe spéciale comme JSON, XML ou
tool_name(arguments)
- Le code analyse cette sortie et exécute l’outil demandé
- Un nouveau prompt incluant le résultat de l’exécution est envoyé au modèle
- Cette approche fonctionne aujourd’hui avec presque tous les modèles
- De nombreux modèles sont entraînés spécifiquement à l’utilisation d’outils, et il existe aussi des classements comme le Berkeley Function-Calling Leaderboard
- OpenAI, Anthropic, Google, Mistral et Meta incluent dans leurs API des fonctionnalités d’utilisation d’outils ou d’appel de fonctions
- Côté modèles locaux, Ollama a ajouté l’an dernier la prise en charge des outils, et elle est également incluse dans le serveur llama.cpp
- En février 2025, LLM a d’abord publié la prise en charge des schémas, qui a servi d’étape vers la prise en charge des outils
- Le défi consistait à concevoir une couche d’abstraction fonctionnant avec plusieurs modèles ; l’implémentation est devenue possible maintenant que les approches des fournisseurs ont nettement convergé
- L’atelier PyCon US Building software on top of Large Language Models a été l’occasion de finaliser l’implémentation alpha, et une section tools du tutoriel est également disponible
Agents et prochaines étapes
- Même si le terme “agents” suscite toujours une certaine réticence, dans l’univers des LLM il converge vers des tools in a loop, et LLM 0.26 s’en rapproche aussi
- Pour qui veut créer un agent, LLM 0.26 peut constituer un bon point de départ
- Les prochains travaux sont regroupés en 13 issues dans le jalon LLM tools v2 milestone
- Il s’agit principalement d’améliorer l’affichage des logs d’exécution des outils et de petits problèmes qui n’ont pas bloqué cette version
- D’autres issues se trouvent sous le label tools
- Le potentiel des plugins est le domaine le plus enthousiasmant
- Le template cookiecutter llm-plugin-tools est déjà utilisé directement
- Un tutoriel associé est prévu
- Il reste aussi à ajouter la prise en charge des outils à davantage de plugins de modèles
- La prise en charge du Model Context Protocol est également prévue
- MCP s’impose rapidement comme une manière standard pour les modèles d’accéder à des outils
- Il y a deux semaines, il n’était pas directement pris en charge par les API des principaux fournisseurs, mais il a été ajouté ces huit derniers jours à OpenAI, Anthropic et Mistral
- L’objectif est que LLM fonctionne comme un client MCP, afin de permettre d’utiliser facilement comme sources d’outils supplémentaires pour LLM les serveurs MCP écrits par les utilisateurs
1 commentaires
Commentaires sur Hacker News
Le moteur de rendu Markdown en streaming que j’ai écrit pour cet outil vaut aussi le détour : https://github.com/day50-dev/Streamdown
Le contexte est sur https://github.com/simonw/llm/issues/12, et j’utilise aussi tous les jours https://github.com/day50-dev/llmehelp, un outil tmux construit au-dessus du
llmde Simonllm: un plugin ZSH qui utilisezlepour transformer de l’anglais en commande shell d’une seule touche : https://github.com/day50-dev/ZummonerAujourd’hui encore, je l’ai utilisé avec quelque chose comme
$ git find out if abcdefg is a descendent of hijklmnop, et lefor i in $(seq 1 6); do printf "%${i}sh${i}\n\n-----\n" | tr " " "#"; done | pv -bqL 30d’un autre commentaire était à l’origine une saisie bien plus proche du langage naturelQuand on appuie sur
ctrl-x x, le buffer est envoyé à OpenRouter et remplacé en moins d’une seconde par la bonne syntaxeJ’ai ouvert une issue : https://github.com/simonw/llm/issues/1112
J’y ai mis quelques options de conception, mais aucune ne me semble encore parfaitement juste, donc j’aimerais avoir des retours
llmavec la coloration syntaxique debatÇa augmente fortement le risque de se tirer une balle dans le pied
La documentation https://llm.datasette.io/en/stable/tools.html met en garde contre l’injection de prompt, mais le scénario le plus plausible me semble être les dégâts que l’on se cause soi-même
Par exemple, si vous donnez à l’outil l’accès à votre compte de courtage pour automatiser des transactions, rien n’empêche le bot de faire des trades stupides, même sans injection de prompt
Dès qu’on commence à brancher des outils sur un LLM, surtout si ces appels d’outils sont authentifiés et peuvent agir à notre place, il y a trop de chemins possibles pour que les choses tournent mal
Le monde MCP est en train de vivre ça à toute vitesse, et l’affaire GitHub MCP d’hier en est un exemple : https://news.ycombinator.com/item?id=44097390
J’ai mis un gros avertissement dans la documentation, et j’ai fait attention à ne publier, parmi les premiers plugins d’outils, que ceux qui ne peuvent pas causer de dégâts. C’est pourquoi le bac à sable QuickJS et le plugin SQLite sont aussi en lecture seule
llmà son compte de courtage, ce n’est pas le bot qui est stupide, c’est la personne qui l’a connectéSur macOS, ce n’est pas extrêmement difficile, mais il manque aujourd’hui de bons outils faciles à utiliser. Claude Code a commencé à utiliser progressivement Seatbelt pour améliorer l’expérience utilisateur
On peut se faire du mal soi-même, et cela arrivera sans doute, mais ne pas évaluer les possibilités est aussi risqué. Le grand public a tendance à sous-estimer la possibilité de se tirer une balle dans le pied, et les techniciens à sous-estimer le risque de ne pas apprendre de nouvelles possibilités
Il y a un an déjà, je faisais exécuter des commandes locales sur mon ordinateur portable par un LLM ; j’estime que c’est risqué dans une certaine mesure, mais rien de dommageable ne s’est produit. Dans un prompt comme
find out where I am and what weather it is going to be, la probabilité qu’il exécuterm -rf /existe, mais elle est très faibleEn revanche, confier des transactions boursières à un LLM sans savoir comment il prend ses décisions est, selon mes critères, beaucoup trop risqué
J’aime bien l’idée d’un agent basé sur le terminal dans le terminal Warp, mais je n’aime pas trop le modèle à la Cursor du genre « faites-nous confiance et payez, on s’occupe des bons prompts et des appels au LLM »
Je cherchais donc un agent de terminal simple basé sur la CLI pour compenser mes lacunes en shell, et la combinaison des outils de terminal avec
llmme paraît être une solution légère intéressanteJe suis bien conscient du gros risque de se tirer une balle dans le pied, donc je me demande s’il est possible de demander une autorisation à chaque appel d’outil, comme le font d’autres agents. Par exemple : «
llms’apprête à appelerrm -rf ./*. Appuyez sur Y pour confirmer » ; cela permettrait de limiter dans une certaine mesure les emballements du LLM dans le terminal--full-autoSi vous utilisez
llm, Gtk-llm-chat vaut aussi le détourIl s’intègre à l’outil en ligne de commande
llmet au bureau, avec une icône de zone de notification et une jolie fenêtre de chatLa version 3.0.0 est sortie récemment, avec des paquets pour les trois principaux systèmes d’exploitation de bureau
Je vois cette release comme un composant clé pour libérer le potentiel des LLM sans les limitations des clients existants
Après la sortie de l’alpha 0.26, j’ai essayé de créer un plugin interagissant avec des serveurs MCP, mais ça a été assez difficile. Jusqu’ici, j’ai réussi à me connecter et à récupérer dynamiquement les outils pour les utiliser, mais je n’arrive pas encore à transmettre les paramètres
La bibliothèque Python officielle
mcpsuppose fortement un flux où l’on exécuteasyncio, se connecte au serveur puis inspecte les outils disponiblesJe maintiens presque entièrement en vibe coding un plugin zsh/omz pour l’autocomplétion par tabulation de la CLI
llm, mais le rythme des nouvelles fonctionnalités est rapide et difficile à suivreCela dit, avec quelque chose comme
llm -f README.md -f llm.plugin.zsh -f completions/_llm -f [https://simonwillison.net/2025/May/27/llm-tools/](<https://simonwillison.net/2025/May/27/llm-tools/>) "implement tab completions for the new tool plugins feature", 90 % du problème est résoluLe dépôt est https://github.com/eliyastein/llm-zsh-plugin et, comme j’essaie d’y mettre autant d’options et de flags que possible, c’est un peu brouillon ; j’aimerais avoir des retours
Les générations futures se demanderont comment nous arrivions à travailler, de la même manière que nous regardons les programmeurs assembleur en nous demandant : « comment faisaient-ils ? »
Je me demandais comment Claude Code connaît et utilise les outils
On peut indiquer à un LLM quels outils existent et comment les utiliser, et un wrapper d’exécution peut gérer cela, mais j’imaginais que Claude Code avait des attentes très précises vis-à-vis de l’API d’appel d’outils utilisée par le wrapper, probablement fortement renforcées par post-entraînement ou fine-tuning
Je me demande donc si les frameworks tiers d’appel d’outils utilisant Claude sont désavantagés par rapport au framework d’Anthropic lui-même
Par ailleurs, comme évoqué dans l’article sur l’« attaque » GitHub MCP, un LLM peut être manipulé pour utiliser toute l’étendue des droits des identifiants. Comme GitHub dispose d’identifiants d’authentification à granularité fine, et que c’est aussi le cas dans mon entreprise, j’aimerais que quelqu’un tente de définir un protocole permettant au wrapper de générer des identifiants à granularité fine à transmettre au LLM
J’imagine une architecture où l’application reçoit des identifiants plus puissants, tandis que le LLM subalterne apprend à faire des « demandes d’autorisation » pour certaines tâches ou ressources. Si l’utilisateur accepte, le framework récupère auprès du service des identifiants à portée limitée et les utilise pour les appels d’outils
Il faudrait une configuration d’outils assez sophistiquée qui expose un outil de « demande d’identifiants supplémentaires » et, lorsqu’il est appelé, interroge l’utilisateur
L’outil doit conserver les identifiants et ne jamais transmettre le vrai token au LLM ; à la place, il peut lui passer un symbole comme
creds1et lui indiquer d’appelercreds1dans les requêtes suivantesJe me demande si vous avez vu cet article. S’il est aussi important qu’il en a l’air, cet indicateur ne devrait-il pas figurer dans toutes les system cards ?
Les auteurs ont évalué 12 LLM populaires affirmant prendre en charge des contextes de 128K tokens ou plus : ils fonctionnent bien sur des contextes courts, mais leurs performances chutent fortement à mesure que la longueur du contexte augmente. À 32K, 10 modèles passent sous les 50 % de la ligne de base en longueur courte, et même GPT-4o, qui fait figure d’exception avec de bonnes performances, passe de 99,3 % à 69,7 %
https://arxiv.org/abs/2502.05167
À ma connaissance, dans les tests needle in a haystack actuels, Gemini 2.5 Pro et Gemini 2.5 Flash sont très loin devant les autres modèles ; ce serait donc bien de les tester aussi
La semaine dernière, j’ai créé avec 0.26a0 une démo de chatbot client basé sur des données propriétaires
Les éléments clés que j’ai dû écrire moi-même étaient le prompt système, l’outil qui récupère les données externes et l’outil qui effectue les calculs ; grâce à cette bibliothèque, les fonctionnalités principales ont été très faciles à réaliser
Dans la démo, l’essentiel de l’effort est allé à la plomberie : créer une belle UI web où la conversation persiste, se rafraîchit naturellement même si le navigateur est rechargé à cause d’un problème de connexion, et permet de démarrer une nouvelle session de chat
Je ne connaissais pas
after_call=print, et j’ai été content de le découvrir en lisant l’article de blogJ’utilise les outils de Simon tous les jours
Les pipes et le passage facile entre Ollama en local et les modèles distants rendent le travail très confortable