3 points par GN⁺ 2025-05-28 | 1 commentaires | Partager sur WhatsApp
  • 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

 
GN⁺ 2025-05-28
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 llm de Simon

    • J’en ai oublié un autre que j’ai construit au-dessus de llm : un plugin ZSH qui utilise zle pour transformer de l’anglais en commande shell d’une seule touche : https://github.com/day50-dev/Zummoner
      Aujourd’hui encore, je l’ai utilisé avec quelque chose comme $ git find out if abcdefg is a descendent of hijklmnop, et le for i in $(seq 1 6); do printf "%${i}sh${i}\n\n-----\n" | tr " " "#"; done | pv -bqL 30 d’un autre commentaire était à l’origine une saisie bien plus proche du langage naturel
      Quand on appuie sur ctrl-x x, le buffer est envoyé à OpenRouter et remplacé en moins d’une seconde par la bonne syntaxe
    • La bibliothèque a vraiment l’air excellente, et ce serait bien qu’il y ait un hook permettant à un plugin de prendre en charge la façon dont le contenu de l’outil est affiché
      J’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
    • J’ai déjà créé moi-même, dans plusieurs langages, des moteurs de rendu Markdown en streaming pour afficher rapidement la sortie d’un LLM en streaming ; je suis content de voir que je ne suis pas le seul
    • Ça a l’air intéressant, je vais regarder ; pour l’instant, j’utilise llm avec la coloration syntaxique de bat
  • Ç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

    • Oui, ça augmente vraiment le risque
      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
    • Si quelqu’un connecte llm à son compte de courtage, ce n’est pas le bot qui est stupide, c’est la personne qui l’a connecté
    • Le sandboxing va être essentiel
      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
    • Je pense que le débat sur le fait de se tirer une balle dans le pied passe à côté de l’essentiel
      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écute rm -rf / existe, mais elle est très faible
      En revanche, confier des transactions boursières à un LLM sans savoir comment il prend ses décisions est, selon mes critères, beaucoup trop risqué
    • N’importe quel outil peut être mal utilisé
  • 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 llm me paraît être une solution légère intéressante
    Je 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 : « llm s’apprête à appeler rm -rf ./*. Appuyez sur Y pour confirmer » ; cela permettrait de limiter dans une certaine mesure les emballements du LLM dans le terminal

    • Je me demandais pourquoi ce terminal faisait de la pub partout et sponsorisait plusieurs projets ; je viens de comprendre qu’il n’est pas entièrement gratuit
    • Il me semble que c’est le comportement par défaut de codex CLI quand on ne lui passe pas --full-auto
  • Si vous utilisez llm, Gtk-llm-chat vaut aussi le détour
    Il s’intègre à l’outil en ligne de commande llm et au bureau, avec une icône de zone de notification et une jolie fenêtre de chat
    La version 3.0.0 est sortie récemment, avec des paquets pour les trois principaux systèmes d’exploitation de bureau

    • Je suis curieux de savoir à quoi il sert en dehors du chat général
  • 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

    • Ce matin, j’ai essayé de voir si je pouvais faire une démo rapide de plugin avec MCP, et c’était assez délicat
      La bibliothèque Python officielle mcp suppose fortement un flux où l’on exécute asyncio, se connecte au serveur puis inspecte les outils disponibles
  • Je 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 à suivre
    Cela 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ésolu
    Le 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

    • Ce qui est assez surprenant, c’est que ce n’est pas de la science-fiction, mais simplement notre façon de coder actuelle
      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

    • Ce mécanisme d’identifiants est déjà possible aujourd’hui avec la prise en charge des outils par les LLM
      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 creds1 et lui indiquer d’appeler creds1 dans les requêtes suivantes
  • Je 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

    • Je n’avais pas vu cet article, mais il est vraiment intéressant
      À 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 blog

  • J’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