Was Function Calling bei LLMs wirklich macht

Fragen Sie ein Modell „wie ist das Wetter gerade in Austin" ohne jede Verbindung zur Außenwelt, und es hat zwei Optionen: sagen, dass es das nicht weiß, oder sich etwas ausdenken. Function Calling gibt ihm eine dritte Option. Es beschreibt, was es braucht, gibt diese Beschreibung an Ihren Code weiter und wartet auf eine echte Antwort, bevor es antwortet.

Der Name ist etwas irrefĂŒhrend. Das Modell ruft nichts auf. Es gibt einen Block strukturierter Daten aus, der sagt, welches Tool es möchte und mit welchen Argumenten, und Ihre Anwendung entscheidet, ob sie es ausfĂŒhrt und was sie mit dem Ergebnis macht. „Tool Use" ist der genauere Begriff und der, den Anthropics Dokumentation verwendet; OpenAI und Google sprechen meist von Function Calling. Der Mechanismus ist unter beiden Namen derselbe. Das Modell erzeugt immer nur eine Anfrage, nie den Seiteneffekt.

Warum das Modell Tools anfragt, sie aber nie selbst ausfĂŒhrt

Stellen Sie sich einen Kunden an einem Tresen vor, der auf Gerichte auf der Speisekarte zeigen und sagen kann, wie viele er möchte, aber nie hinter den Tresen geht. Das Modell ist dieser Kunde: Es liest die Speisekarte, die Sie ihm gegeben haben (die Tool-Definitionen), wÀhlt ein Gericht aus und sagt, wie viele es möchte (die Argumente). Ihr Code ist die Person hinter dem Tresen, die die Bestellung zusammenstellt und eine Bestellung ablehnen kann, die keinen Sinn ergibt.

Diese Trennung ist das gesamte Sicherheitsmodell. Ein Tool-Call ist ein Vorschlag, den Ihr Code validieren oder ablehnen kann, genau wie jede andere Eingabe aus einer nicht vertrauenswĂŒrdigen Quelle. Denn genau das ist er. Ein Modell, das durch Prompt Injection von einer bösartigen Webseite manipuliert wurde, kann trotzdem nur einen Tool-Call anfragen. Es kann Ihre Validierung nicht ĂŒberspringen und direkt auf die Datenbank zugreifen.

Wie man ein Tool mit JSON-Schema definiert

Jeder Anbieter will fĂŒr jedes Tool dieselben drei Dinge: einen Namen, eine Beschreibung, mit der das Modell entscheidet, wann das Tool passt, und ein JSON-Schema fĂŒr die Argumente. Hier ist ein Wetter-Tool fĂŒr die OpenAI Chat Completions API:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
weather_tool = {
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get the current temperature and conditions for a city.",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "City name, e.g. 'Austin, TX'"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
            },
            "required": ["city"],
        },
    },
}

Stecken Sie mehr Zeit in die Beschreibung als in das Schema. Das Modell wĂ€hlt ein Tool, indem es die Anfrage des Nutzers mit den Beschreibungen in der Liste vergleicht, die Sie ihm geschickt haben. Eine vage Beschreibung („Wetterzeug") wird entweder von einem besser beschriebenen Tool verdrĂ€ngt, das etwas anderes macht, oder schlĂ€gt bei Anfragen zu, die es nicht behandeln sollte. Schreiben Sie sie so, als mĂŒssten Sie die Funktion einem Kollegen erklĂ€ren, der genau einen Satz darĂŒber lesen wird.

Wie der Request-Response-Zyklus beim Function Calling funktioniert

Ein Tool-Call braucht mindestens zwei Requests. Hier ist der komplette Ablauf mit der OpenAI API:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from openai import OpenAI
import json

client = OpenAI()
messages = [{"role": "user", "content": "What's the weather in Austin?"}]

response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=[weather_tool],
)
message = response.choices[0].message
messages.append(message)

if message.tool_calls:
    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_weather(args["city"], args.get("unit", "celsius"))
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result),
        })

    final = client.chat.completions.create(model="gpt-4o", messages=messages)
    print(final.choices[0].message.content)

Die erste Antwort beantwortet die Frage nicht. Sie kommt mit finish_reason gleich tool_calls und ohne Textinhalt zurĂŒck, weil das Modell entschieden hat, dass es get_weather braucht, bevor es etwas NĂŒtzliches sagen kann. call.function.arguments kommt als JSON-String an, nicht als bereits geparstes Objekt, also ist json.loads nicht optional. get_weather fĂŒhren Sie selbst aus (es ist eine ganz normale Python-Funktion; das Modell hat keine Möglichkeit, sie auszufĂŒhren), und hĂ€ngen danach das Ergebnis als Nachricht mit role: "tool" und der passenden tool_call_id an.

Erst der zweite Request, der nun dieses Ergebnis enthĂ€lt, liefert Ihnen einen echten Satz zurĂŒck. Wenn Sie die Assistant-Nachricht mit ihren tool_calls anhĂ€ngen, aber das passende Tool-Ergebnis weglassen, lehnt die API den nĂ€chsten Request ab, statt zu raten.

Wie sich Anthropics Tool Use von OpenAIs Function Calling unterscheidet

Gleicher Mechanismus, andere Feldnamen. Wenn Sie zwischen Anbietern wechseln oder beide unterstĂŒtzen, entstehen Ihre Bugs genau hier:

KonzeptOpenAIAnthropic
Feld fĂŒr die Tool-Listetools, jedes Tool verpackt in {"type": "function", "function": {...}}tools, jedes Tool flach: {"name", "description", "input_schema"}
SchlĂŒssel fĂŒr das Argumentschemaparametersinput_schema
Anfrage des Modellstool_calls-Array auf der Nachricht, arguments als JSON-Stringtool_use-Content-Block, input bereits als geparstes Objekt
Ergebnis zurĂŒcksendenneue Nachricht, role: "tool", tool_call_idneue User-Nachricht, Content-Block type: "tool_result", tool_use_id
Fehlgeschlagenen Aufruf signalisierenkein eigenes Feld: Fehler in content ablegenis_error: true am tool_result-Block

Die Falle liegt im Format der Argumente. Anthropic gibt Ihnen input schon als geparstes Python-Dict oder JS-Objekt; OpenAI gibt Ihnen einen String, den Sie selbst durch json.loads schicken mĂŒssen. Code, der fĂŒr den einen Anbieter geschrieben und auf den anderen gerichtet wird, lĂ€sst sich problemlos importieren und scheitert dann beim ersten Tool-Call. Wenn Sie beide unterstĂŒtzen, schreiben Sie eine einzige Normalisierungsfunktion, durch die beide Pfade laufen, bevor Ihre Tool-Implementierungen die Argumente ĂŒberhaupt sehen.

Wie parallele Tool-Calls und mehrstufige Schleifen funktionieren

Beide APIs können in einer einzigen Antwort mehr als einen Tool-Call zurĂŒckgeben. Fragen Sie nach dem Wetter in drei StĂ€dten, und Sie erhalten drei EintrĂ€ge in tool_calls, nicht drei separate Hin- und RĂŒckfahrten. FĂŒhren Sie sie aus (parallel, wenn sie unabhĂ€ngig sind) und hĂ€ngen Sie alle drei Ergebnisse an, bevor der Folge-Request geht. Jede vom Modell ausgegebene Call-ID braucht ein Ergebnis; schicken Sie nur eines von drei zurĂŒck, und der Request schlĂ€gt fehl.

Manche Aufgaben brauchen mehrere Runden: einen Nutzer nachschlagen, dann dessen Bestellverlauf, dann die ĂŒberfĂ€llige Bestellung markieren. Schreiben Sie das als Schleife, die so lange das Modell aufruft und ausfĂŒhrt, was es verlangt, bis eine Antwort ohne Tool-Calls zurĂŒckkommt. Setzen Sie dann eine Obergrenze. Nichts in der API hindert ein Modell daran, neunmal hintereinander dasselbe Tool anzufragen, wenn die Ergebnisse es keiner Antwort nĂ€herbringen. Ein festes Limit, etwa 10 oder 20 Iterationen, macht daraus einen klaren Fehler statt einer außer Kontrolle geratenen Rechnung.

HĂ€ufige Fehler beim Function Calling

Argumente als vertrauenswĂŒrdige Eingabe behandeln. args["city"] stammt aus einem Modell, was bedeutet, dass es aus jedem Text stammt, der die Konversation gefĂŒttert hat, einschließlich einer gescrapten Webseite oder eines Dokuments, das das Modell zusammenfassen sollte. Validieren Sie es, bevor es eine Datenbankabfrage oder einen Shell-Befehl erreicht. Ein Tool, das SQL durch Interpolieren der Modellargumente baut, hat dieselbe Injection-AngriffsflĂ€che wie eines, das SQL aus einem Webformular baut.

Dem Schema wie einer Garantie vertrauen. Das Modell hĂ€lt sich meist an required und enum, aber nicht perfekt. FrĂŒher oder spĂ€ter ĂŒbergibt es "unit": "F" statt "fahrenheit" oder lĂ€sst ein Pflichtfeld weg. Validieren Sie, und geben Sie als Tool-Ergebnis eine klare Fehlermeldung zurĂŒck, statt abzustĂŒrzen. Sagen Sie dem Modell, was falsch war, und es kann in der nĂ€chsten Runde oft mit korrigierten Argumenten erneut versuchen.

Das Modell destruktive Tools allein auswÀhlen lassen. Wenn ein Tool Daten löscht oder eine E-Mail verschickt, ist tool_choice auf automatisch zu lassen die falsche Standardeinstellung. Halten Sie destruktive Tools aus der Liste heraus, bis ein Mensch die konkrete Aktion genehmigt hat, oder machen Sie den Effekt umkehrbar (einen Entwurf statt einer versendeten E-Mail) und lassen Sie einen separaten Schritt ihn bestÀtigen.

Wann man Function Calling nicht einsetzen sollte

Jeder Tool-Call fĂŒgt dem Modell eine zusĂ€tzliche Hin- und RĂŒckfahrt hinzu, plus die Latenz, die das Tool selbst mitbringt. Wenn das „Tool" ohnehin nur eine statische Tatsache zurĂŒckgeben wĂŒrde, legen Sie diese Tatsache in den Prompt oder in den abgerufenen Kontext und sparen Sie sich die Hin- und RĂŒckfahrt; dafĂŒr ist das RAG-Retrieval da. Und wenn die Entscheidung eigentlich eine Klassifikation mit einer kleinen, festen Menge an Ergebnissen ist (dieses Ticket an eine von vier Warteschlangen weiterleiten), antwortet ein typisierter, nicht generativer Aufruf schneller und gĂŒnstiger, als ein allgemeines Chat-Modell ein Tool auswĂ€hlen zu lassen. Genau dafĂŒr sind System-One-Modelle gebaut.

Function Calling rechtfertigt seine Kosten, wenn die Aktion von Laufzeitdaten abhĂ€ngt, die das Modell nicht im Prompt haben kann, und die Menge möglicher Aktionen zu offen ist, um sie als Zustandsmaschine festzulegen. Es ist außerdem die Schleife hinter jedem CLI-Coding-Agenten: eine Datei lesen, einen Test ausfĂŒhren, entscheiden, was als NĂ€chstes kommt, ein weiteres Tool aufrufen.

Beginnen Sie mit einem Tool, einer fest codierten Python-Funktion dahinter und einer Schleife, die nach einer einzigen Hin- und RĂŒckfahrt stoppt. Bringen Sie Argumentvalidierung und Fehlerpfad in Ordnung, bevor Sie ein zweites Tool hinzufĂŒgen. Die meisten Bugs in einem Setup mit mehreren Tools stellen sich als das erste Tool heraus, das Argumente falsch behandelt, mit denen es nicht gerechnet hat.

Verwandte Artikel