- LLM 0.26 ist ein großes Release, das es ermöglicht, in der CLI und der Python-Bibliothek Tools, die als Python-Funktionen erstellt wurden, mit OpenAI, Anthropic, Gemini und lokalen Ollama-Modellen zu verbinden
- Tools können als Plugins installiert und mit
--tool/-T name_of_tool geladen werden; alternativ lassen sich mit --functions temporäre Python-Funktionen direkt über die Kommandozeile übergeben
- Neben den eingebauten Tools
llm_version und llm_time werden simpleeval-, QuickJS-, SQLite- und Datasette-Plugins angeboten; Modelle können nach fehlgeschlagenen Aufrufen mit einer Schemaabfrage oder einem anderen Ausdruck erneut versuchen
- Das neue
model.chain() der Python-API erkennt Tool-Call-Anfragen, führt sie aus und übergibt die Ergebnisse wieder an das Modell; unterstützt werden sowohl synchrone Funktionen als auch asyncio-Tools
- Da sich die Ansätze verschiedener Anbieter für Tool-Nutzung und Function Calling annähern, wurde die Implementierung möglich; die nächsten Aufgaben sind Plugin-Erweiterungen, Unterstützung für weitere Modell-Plugins und Client-Support für das Model Context Protocol
Tool-Ausführung in LLM 0.26
- LLM 0.26 ergänzt als größte Funktion seit Projektstart Tool-Support
- In der LLM CLI und der Python-Bibliothek können OpenAI-, Anthropic-, Gemini- und lokale Ollama-Modelle Zugriff auf Tools erhalten, die sich als Python-Funktionen ausdrücken lassen
- Durch Installation eines neuen Tool-Plugins lassen sich dem aktuell verwendeten Modell neue Fähigkeiten hinzufügen
- Es gibt vier zentrale Nutzungsweisen
- Ein Plugin-Tool installieren und mit
--tool/-T name_of_tool laden
- Über die Option
--functions Python-Funktionscode direkt an die Kommandozeile übergeben
- Auch in der Python-API lassen sich Tools etwa mit
tools=[locals] übergeben
- Tools funktionieren sowohl in asynchronen als auch in synchronen Kontexten
Tools in der CLI ausführen
- Die aktuelle LLM-Version lässt sich mit
uv tool install llm installieren; bestehende Installationen können mit uv tool upgrade llm aktualisiert werden
- Ein OpenAI-Beispiel: Nach dem Setzen des API-Keys mit
llm keys set openai wird es wie folgt ausgeführt
llm --tool llm_version "What version?" --td
llm_version ist ein einfaches Demo-Tool, das in LLM enthalten ist; --tool llm_version macht dieses Tool dem Modell zugänglich
--tool kann mehrfach angegeben werden; auch die Kurzoption -T ist verfügbar
--td steht für --tools-debug und gibt Tool-Aufrufe und Antwortinformationen aus, um die internen Abläufe nachvollziehbar zu machen
- Das Standardmodell ist in der Regel
gpt-4o-mini; im Beispiel wird mit llm models default gpt-4.1-mini gpt-4.1-mini als Standard gesetzt
- Mit der Option
-m lässt sich ein anderes Modell angeben; es gibt auch ein Beispiel, das o4-mini zusammen mit dem eingebauten Tool llm_time ausführt
llm --tool llm_time "What time is it?" --td -m o4-mini
- Die Antwort des Tools
llm_time enthält Felder wie utc_time, utc_time_iso, local_timezone, local_time, timezone_offset und is_dst
Verhalten mit verschiedenen Modellen und Plugins
- Bei Modell-Plugins, die Tools unterstützen, kann dasselbe Befehlsmuster verwendet werden
- Das Beispiel für Anthropic Claude Sonnet 4 nutzt folgenden Ablauf
llm install llm-anthropic -U
llm keys set anthropic
llm --tool llm_version "What version?" --td -m claude-4-sonnet
- Das Beispiel für Google Gemini 2.5 Flash nutzt folgenden Ablauf
llm install llm-gemini -U
llm keys set gemini
llm --tool llm_version "What version?" --td -m gemini-2.5-flash-preview-05-20
- Auch das über Ollama ausgeführte
qwen3:4b kann einfache Tools ausführen
qwen3:4b ist ein kleines Modell mit 2,6 GB Größe
ollama pull qwen3:4b
llm install 'llm-ollama>=0.11a0'
llm --tool llm_version "What version?" --td -m qwen3:4b
Mathematik-, JavaScript-, SQLite- und Datasette-Tool-Plugins
- Aufgaben, bei denen LLMs schwach sind, etwa Multiplikation großer Zahlen, lassen sich durch Tool-Aufrufe ergänzen
- llm-tools-simpleeval stellt Daniel Fairheads Bibliothek simpleeval bereit
- simpleeval ist eine Bibliothek „Simple Safe Sandboxed Extensible Expression Evaluator for Python“
- Sie bietet eine ausreichend robuste Sandbox zum Ausführen einfacher Python-Ausdrücke
- Ein Rechenbeispiel wird so ausgeführt
llm install llm-tools-simpleeval
llm -T simple_eval 'Calculate 1234 * 4346 / 32414 and square root it' --td
- In der Beispielausführung berechnet das Modell zunächst
1234 * 4346 / 32414 und erhält 165.45208860368976; danach schlägt sqrt(...) fehl, weil es nicht definiert ist, und es wechselt zu ** 0.5, womit es 12.862818066181678 erhält
- Es gibt vier veröffentlichte Tool-Plugins
- llm-tools-simpleeval: Unterstützung für einfache Ausdrücke wie Mathematik
- llm-tools-quickjs: Zugriff auf einen sandboxed QuickJS-JavaScript-Interpreter; die Umgebung bleibt zwischen Aufrufen erhalten, sodass Variablen und Funktionen wiederverwendet werden können
- llm-tools-sqlite: schreibgeschützter SQL-Query-Zugriff auf lokale SQLite-Datenbanken
- llm-tools-datasette: SQL-Queries auf entfernten Datasette-Instanzen ausführen
Datasette-Toolbox und erneuter Versuch nach Fehlern
- Das Datasette-Plugin arbeitet als Toolbox mit mehreren internen Tools
- Eine Toolbox ist ein Plugin, das per Konstruktor konfiguriert werden kann
- Ein Beispiel für die Nutzung sieht so aus
llm install llm-tools-datasette
llm -T 'Datasette("https://datasette.io/content")' --td "What has the most stars?"
Datasette("https://datasette.io/content") übergibt die URL der zu verwendenden Datasette-Instanz an das Plugin
- Dieses Beispiel zielt auf die content database, die die Datasette-Website antreibt
- Das Modell führt drei Aufrufe aus
- Zuerst rät es
SELECT name, stars FROM repos ORDER BY stars DESC LIMIT 1, was fehlschlägt, weil es keine Spalte stars gibt
- Nach Erhalt des Fehlers fragt es mit dem Tool
Datasette_schema() das Datenbankschema ab
- Auf Basis des Schemas setzt es die korrekte Query zusammen und führt sie aus
- Laut endgültiger Antwort ist das Repository mit den meisten Sternen
datasette mit 10.020 stars
Temporäre Tools mit --functions erstellen
- Die Option
--functions bietet eine weniger strukturierte Nutzungsweise für temporäre Tools als Plugins
- Wenn direkt auf der Kommandozeile ein Python-Codeblock übergeben wird, werden die darin definierten Funktionen zu Tools, die das Modell verwenden kann
- Ein Beispiel für ein Blog-Suchtool ruft mit
httpx die Suchseite ab und gibt das HTML unverändert an das Modell zurück
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'
- Die Implementierung gibt das HTML der Suchseite unverändert zurück, funktioniert aber
- Da die GPT-4.1-Familie 1 Million Token verarbeiten kann, wird angenommen, dass sie auch grobes HTML bewältigen kann
- Dem System-Prompt muss
use Simon search hinzugefügt werden, damit das Modell nicht eigenständig antwortet, sondern das bereitgestellte Suchtool verwendet
- Ein besseres Suchtool bräuchte detailliertere Anweisungen und relevante Snippets aus den Suchergebnissen
- Im Beispielergebnis werden als Funktionen von
sqlite-utils die Kombination aus CLI und Python-Bibliothek, das automatische Hinzufügen von Spalten mit alter=True und Plugin-Unterstützung genannt
Tool-Support in der Python-API
- LLM ist sowohl ein CLI-Tool als auch eine Python-Bibliothek; in 0.26 wurde auch der Python-API Tool-Support hinzugefügt
- Das Beispiel löst die Aufgabe, die Anzahl der
r in „strawberry“ zu zählen, mit einem Funktions-Tool
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 ist wie die zuvor genannte Option --td eine Möglichkeit, Tool-Aufrufe zu überprüfen
- Die neue Methode
model.chain() ähnelt model.prompt(), erkennt jedoch zurückgegebene Tool-Call-Anfragen, führt sie aus und ruft das Modell anschließend mit den Ergebnissen erneut auf
model.chain() kann mehrere Antworten ausführen, bevor es die endgültige Antwort liefert
- Wenn
chain_response durchlaufen wird, können die über mehrere Antworten zurückgegebenen Tokens gestreamt ausgegeben werden
- Im Beispielergebnis wird das Tool
count_char_in_text mit char='r' und text='strawberry' aufgerufen; auf Basis des Ergebnisses 3 antwortet es, dass strawberry drei r enthält
- Die Python-Bibliothek unterstützt auch
asyncio; Tools können async def-Funktionen sein
- Wenn das Modell mehrere asynchrone Tools auf einmal anfordert, führt die Bibliothek sie mit
asyncio.gather() gleichzeitig aus
- Auch Toolboxes werden unterstützt: Übergibt man
tools=[Datasette("https://datasette.io/content")] an chain(), hat das denselben Effekt wie --tool 'Datasette(...)' in der CLI
Implementierungshintergrund und Muster der Tool-Nutzung
- Das Muster der Tool-Nutzung wird seit dem im Oktober 2022 erstmals veröffentlichten ReAcT paper verfolgt
- Das Grundmuster ist einfach
- Dem Modell mitteilen, dass es verfügbare Tools gibt
- Das Modell fordert mit spezieller Syntax wie JSON, XML oder
tool_name(arguments) eine Tool-Aktion an und stoppt
- Code parst diese Ausgabe und führt das angeforderte Tool aus
- Ein neuer Prompt mit dem Ausführungsergebnis wird an das Modell gesendet
- Dieser Ansatz funktioniert inzwischen bei nahezu allen Modellen
- Viele Modelle sind speziell für Tool-Nutzung trainiert; es gibt auch Leaderboards wie das Berkeley Function-Calling Leaderboard
- OpenAI, Anthropic, Google, Mistral und Meta enthalten Tool-Nutzung oder Function-Calling-Funktionen in ihren APIs
- Bei lokalen Modellen hat Ollama im vergangenen Jahr Tool-Support hinzugefügt; auch im llama.cpp-Server ist er enthalten
- LLM veröffentlichte im Februar 2025 zunächst Schema-Support und machte ihn zu einem Schritt in Richtung Tool-Support
- Die Herausforderung lag im Design einer Abstraktionsschicht, die über verschiedene Modelle hinweg funktioniert; da sich die Ansätze der Anbieter inzwischen deutlich annähern, wurde die Implementierung möglich
- Der Workshop Building software on top of Large Language Models auf der PyCon US wurde zum Anlass, die Alpha-Implementierung abzuschließen; auch die tools section des Tutorials ist verfügbar
Agents und nächste Pläne
- Gegenüber dem Begriff „agents“ besteht weiterhin Vorbehalt, doch in der LLM-Welt konvergiert er zu tools in a loop, und LLM 0.26 kommt dieser Form nahe
- Wer einen Agent bauen möchte, kann LLM 0.26 als guten Ausgangspunkt nutzen
- Die nächsten Arbeiten sind im LLM tools v2 milestone in 13 Issues zusammengefasst
- Es geht vor allem um bessere Anzeige von Tool-Ausführungslogs und kleinere Issues, die dieses Release nicht blockiert haben
- Weitere Issues finden sich unter dem tools label
- Der Bereich mit dem größten erwarteten Potenzial ist die Erweiterbarkeit durch Plugins
- Das Cookiecutter-Template llm-plugin-tools wird bereits selbst verwendet
- Ein zugehöriges Tutorial ist geplant
- Offen ist außerdem, Tool-Support zu weiteren Modell-Plugins hinzuzufügen
- Der Dokumentation für fortgeschrittene Plugins wurden Details zur Tool-Unterstützung hinzugefügt
- Der Commit, der Tool-Support für Gemini hinzugefügt hat, dient als Beispiel für die nötige Arbeit
- Auch Unterstützung für das Model Context Protocol ist geplant
- MCP entwickelt sich schnell zu einem Standardweg, über den Modelle auf Tools zugreifen
- Vor zwei Wochen wurde es noch nicht direkt von den APIs der großen Anbieter unterstützt, wurde aber in den letzten acht Tagen bei OpenAI, Anthropic und Mistral hinzugefügt
- Ziel ist, dass LLM als MCP-Client arbeitet, damit von Nutzern geschriebene MCP-Server einfach als zusätzliche Tool-Quellen für LLM verwendet werden können
1 Kommentare
Meinungen auf Hacker News
Der Streaming-Markdown-Renderer, den ich für dieses Tool geschrieben habe, ist ebenfalls sehenswert: https://github.com/day50-dev/Streamdown
Der Hintergrund findet sich unter https://github.com/simonw/llm/issues/12, und ich nutze auch täglich https://github.com/day50-dev/llmehelp, ein tmux-Tool, das auf Simons
llmaufsetzt.llmaufsetzt: ein ZSH-Plugin, das mitzleper Tastendruck Englisch in Shell-Befehle umwandelt: https://github.com/day50-dev/ZummonerIch habe es heute wieder etwa in der Form
$ git find out if abcdefg is a descendent of hijklmnopverwendet, und auchfor i in $(seq 1 6); do printf "%${i}sh${i}\n\n-----\n" | tr " " "#"; done | pv -bqL 30aus einem anderen Kommentar war ursprünglich eine viel natürlichsprachlichere Eingabe.Wenn man
ctrl-x xdrückt, wird der Buffer an OpenRouter gesendet und in weniger als einer Sekunde durch die korrekte Syntax ersetzt.Ich habe dazu ein Issue eröffnet: https://github.com/simonw/llm/issues/1112
Ich habe ein paar Designoptionen ergänzt, aber es fühlt sich noch nicht ganz richtig an, daher würde ich mich über Feedback freuen.
llmzusammen mit Syntax-Highlighting vonbat.Das erhöht die Gefahr, sich selbst ins Knie zu schießen, erheblich.
Die Dokumentation https://llm.datasette.io/en/stable/tools.html warnt zwar vor Prompt Injection, aber das plausiblere Szenario ist meiner Ansicht nach selbst verursachter Schaden.
Wenn man einem Tool zum Beispiel Zugriff auf ein Wertpapierdepot gibt, um Trades zu automatisieren, gibt es auch ohne Prompt Injection nichts, was den Bot daran hindert, dumme Trades zu machen.
Sobald man LLMs Tools anhängt, gibt es sehr viele Wege, wie etwas schiefgehen kann – besonders wenn diese Tool-Aufrufe authentifiziert sind und sogar stellvertretend Handlungen ausführen können.
Bei MCP passiert das gerade im Zeitraffer, und der GitHub-MCP-Fall von gestern ist ein Beispiel dafür: https://news.ycombinator.com/item?id=44097390
Ich habe eine große Warnung in die Dokumentation aufgenommen und war bei den ersten Tool-Plugins darauf bedacht, nur solche zu veröffentlichen, die keinen Schaden anrichten können. Deshalb sind auch das QuickJS-Sandbox- und das SQLite-Plugin nur lesend.
llmmit seinem Wertpapierdepot verbindet, ist nicht der Bot dumm, sondern die Person, die das verbunden hat.Unter macOS ist das nicht extrem schwierig, aber im Moment fehlen gute, leicht benutzbare Tools. Claude Code hat begonnen, Seatbelt schrittweise einzusetzen, um die User Experience zu verbessern.
Man kann sich selbst schaden und wird es vielleicht auch tun, aber die Möglichkeiten nicht zu bewerten, ist ebenfalls riskant. Normale Nutzer unterschätzen tendenziell die Möglichkeit, sich ins Knie zu schießen, während Techniker dazu neigen, das Risiko zu unterschätzen, neue Möglichkeiten nicht zu lernen.
Schon vor einem Jahr habe ich ein LLM lokale Befehle auf meinem Laptop ausführen lassen. Ich halte das für bis zu einem gewissen Grad riskant, aber es ist nichts Schädliches passiert. Bei einem Prompt wie
find out where I am and what weather it is going to bebesteht zwar die Möglichkeit, dassrm -rf /ausgeführt wird, aber sie ist sehr gering.Einem LLM allerdings Aktienhandel zu überlassen, ohne zu wissen, wie es entscheidet, wäre nach meinem Maßstab viel zu riskant.
Die Idee eines terminalbasierten Agenten im Warp-Terminal gefällt mir, aber das Modell „Vertrau uns und zahl dafür, wir kümmern uns um gute Prompts und LLM-Aufrufe“ wie bei Cursor mag ich nicht besonders.
Deshalb habe ich nach einem einfachen CLI-basierten Terminal-Agenten gesucht, der meine begrenzten Shell-Kenntnisse ausgleicht, und die Kombination aus Terminal-Tools und
llmsieht nach einer schlanken Lösung aus.Das große Risiko, sich ins Knie zu schießen, ist mir bewusst; daher frage ich mich, ob wie bei anderen Agenten eine Abfrage pro Tool-Aufruf möglich ist. Etwa: „
llmmöchterm -rf ./*aufrufen. Drücken Sie Y zur Bestätigung.“ So ließe sich zumindest teilweise verhindern, dass ein LLM im Terminal Amok läuft.--full-autoübergibt.Wer
llmnutzt, sollte sich auch Gtk-llm-chat ansehen.Es integriert sich mit dem
llm-Kommandozeilentool und dem Desktop und bietet ein Tray-Icon sowie ein ansprechend aussehendes Chatfenster.Kürzlich wurde 3.0.0 veröffentlicht, mit Paketen für die drei großen Desktop-Betriebssysteme.
Ich halte dieses Release für einen zentralen Baustein, der das Potenzial von LLMs ohne die Einschränkungen bestehender Clients erschließt.
Nachdem 0.26 Alpha erschienen war, habe ich versucht, ein Plugin zu bauen, das mit MCP-Servern interagiert, aber das war ziemlich schwierig. Bis jetzt habe ich es geschafft, eine Verbindung herzustellen und Tools dynamisch abzurufen und zu verwenden, aber Parameter kann ich noch nicht übergeben.
Die offizielle Python-Bibliothek
mcpsetzt stark den Ablauf voraus,asynciozu starten, sich mit dem Server zu verbinden und dann die verfügbaren Tools zu inspizieren.Ich pflege ein zsh/omz-Plugin für Tab-Vervollständigung in der
llm-CLI fast ausschließlich per Vibe Coding, aber bei dem schnellen Tempo neuer Feature-Releases ist es schwer, hinterherzukommen.Trotzdem löst so etwas wie
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"schon 90 % des Problems.Das Repository ist https://github.com/eliyastein/llm-zsh-plugin; weil ich versuche, möglichst viele Optionen und Flags unterzubringen, ist es etwas unübersichtlich geworden, daher wäre Feedback willkommen.
Künftige Generationen werden sich fragen, wie wir unsere Arbeit überhaupt erledigt haben, so wie wir auf Assembler-Programmierer schauen und uns fragen: „Wie haben die gearbeitet?“
Ich habe mich gefragt, wie Claude Code Werkzeuge kennt und nutzt.
Man kann einem LLM Werkzeuge und deren Verwendung erklären, und ein Ausführungs-Wrapper kann das verwalten, aber ich hatte den Eindruck, dass Claude Code sehr konkrete Erwartungen an die Tool-Calling-API hat, die der Wrapper verwendet, und dass das wahrscheinlich durch Post-Training oder Fine-Tuning stark verstärkt wurde.
Daher frage ich mich, ob Third-Party-Tool-Calling-Frameworks, die Claude verwenden, gegenüber Anthropics eigenem Framework im Nachteil sind.
Separat davon: Wie in dem Artikel über den GitHub-MCP-„Angriff“ beschrieben, kann ein LLM dazu verleitet werden, die gesamten Berechtigungen von Zugangsdaten auszunutzen. Da es bei GitHub fein granulare Authentifizierungs-Credentials gibt und das auch bei unserem Unternehmen so ist, fände ich es gut, wenn jemand ein Protokoll ausprobiert, bei dem der Wrapper fein granulare Zugangsdaten erzeugt, die er an das LLM weitergibt.
Ich stelle mir eine Architektur vor, in der die Anwendung stärkere Zugangsdaten erhält und das nachgelagerte LLM darauf trainiert ist, für bestimmte Aufgaben oder Ressourcen „Berechtigungen anzufordern“. Wenn der Nutzer zustimmt, holt das Framework vom Dienst Credentials mit eingeschränktem Scope und verwendet sie für den Tool-Aufruf.
Man braucht eine ziemlich ausgefeilte Tool-Konfiguration, die ein Tool „zusätzliche Zugangsdaten anfordern“ exponiert und bei einem Aufruf den Nutzer fragt.
Das Tool sollte die Zugangsdaten speichern und dem LLM niemals den echten Token geben; stattdessen kann es ein Symbol wie
creds1zurückgeben und erklären, dass spätere Anfragen mitcreds1erfolgen sollen.Ich frage mich, ob du dieses Paper gesehen hast. Wenn es so wichtig ist, wie es aussieht, sollte diese Metrik nicht in jede System Card aufgenommen werden?
Es wurden 12 populäre LLMs evaluiert, die behaupten, Kontexte von 128K Tokens oder mehr zu unterstützen. Demnach schneiden sie bei kurzen Kontexten gut ab, aber die Leistung fällt mit zunehmender Kontextlänge stark ab. Bei 32K fielen 10 Modelle unter 50 % der Baseline für kurze Längen; selbst das außergewöhnlich gute GPT-4o sank von 99,3 % auf 69,7 %.
https://arxiv.org/abs/2502.05167
Soweit ich weiß, liegen Gemini 2.5 Pro und Gemini 2.5 Flash in aktuellen Needle-in-a-Haystack-Tests deutlich vor anderen Modellen, daher wäre es spannend, die Tests auch mit diesen Modellen laufen zu lassen.
Letzte Woche habe ich mit 0.26a0 eine Demo eines Kunden-Chatbots auf Basis proprietärer Daten gebaut.
Die Kernelemente, die ich selbst schreiben musste, waren der System-Prompt, ein Tool zum Abrufen externer Daten und ein Tool für Berechnungen; dank dieser Library ließ sich die Kernfunktionalität sehr einfach umsetzen.
Der größte Teil des Aufwands in der Demo steckte in der Infrastruktur: eine ansprechende Web-UI zu bauen, in der die Unterhaltung erhalten bleibt, sich nach Verbindungsproblemen und einem Browser-Refresh sauber aktualisiert und neue Chat-Sessions gestartet werden können.
after_call=printkannte ich nicht; schön, dass ich es durch den Blogpost entdeckt habe.Ich nutze Simons Tools täglich.
Dank Pipes und dem einfachen Wechsel zwischen lokalem Ollama und Remote-Modellen lässt sich damit sehr bequem arbeiten.