Zum Inhalt springen

Tool Calling (Function Calling)

Mit Tool Calling (auch Function Calling genannt) geben Sie dem Modell die Möglichkeit, strukturierte Anfragen an von Ihnen definierte Funktionen zu stellen, statt selbst zu raten. Das Modell entscheidet anhand des Nutzerwunsches, welches Werkzeug sinnvoll ist, und liefert die Argumente als wohlgeformtes JSON zurück. Ihr Code führt das Werkzeug aus und reicht das Ergebnis wieder ans Modell, das daraus die finale Antwort formuliert.

Typische Einsatzgebiete sind Taschenrechner, Datenbankabfragen, Wetter-APIs, interne Mikroservices oder Suchsysteme. Das Modell “weiß” dabei nichts über die konkrete Implementierung, es kennt nur Name, Beschreibung und Parameterschema.

Tools werden im Feld tools des Requests als Liste übergeben. Jedes Tool hat den Typ "function" und enthält ein verschachteltes function-Objekt mit folgenden Feldern:

FeldBedeutung
nameFunktionsname, den das Modell im Aufruf verwendet.
descriptionKlartextbeschreibung, wann und wofür das Modell das Werkzeug einsetzen soll.
parametersJSON-Schema (Draft 07) der erwarteten Argumente: Typen, Properties, Required-Liste.

Je präziser description und parameters formuliert sind, desto seltener wählt das Modell das falsche Werkzeug oder übergibt ungültige Argumente.

Wir definieren ein einzelnes Werkzeug calculate, das die vier Grundrechenarten unterstützt. Das Schema beschreibt einen Operator-Parameter mit Enum-Werten sowie zwei numerische Operanden.

Terminal-Fenster
curl -X POST https://ai.noris.de/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "vllm/release/gpt-oss-120b",
"messages": [
{"role": "user", "content": "Was ist 17 mal 24?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "calculate",
"description": "Führt eine Grundrechenart aus.",
"parameters": {
"type": "object",
"properties": {
"operator": {
"type": "string",
"enum": ["add", "subtract", "multiply", "divide"],
"description": "Die auszuführende Operation."
},
"operand_a": {"type": "number", "description": "Erster Operand."},
"operand_b": {"type": "number", "description": "Zweiter Operand."}
},
"required": ["operator", "operand_a", "operand_b"]
}
}
}
]
}'

Ist das Modell überzeugt, dass ein Werkzeugaufruf sinnvoll ist, antwortet es nicht mit Text, sondern mit tool_calls. Die Antwort sieht dann verkürzt so aus:

{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "calculate",
"arguments": "{\"operator\":\"multiply\",\"operand_a\":17,\"operand_b\":24}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}

Achten Sie auf finish_reason: "tool_calls", dies signalisiert, dass das Modell auf die Ausführung des Werkzeugs wartet. Die Argumente liegen als JSON-kodierter String in function.arguments vor und müssen vor der Ausführung geparsed werden.

Jetzt kommt Ihr Code ins Spiel: Parsen Sie die Argumente, führen Sie die Funktion lokal aus und schicken Sie das Ergebnis als neue Nachricht mit der Rolle "tool" zurück. Hängen Sie außerdem die ursprüngliche Assistant-Nachricht inklusive tool_calls an den Verlauf an, damit das Modell weiß, was es zuvor angefordert hat.

Terminal-Fenster
curl -X POST https://ai.noris.de/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "vllm/release/gpt-oss-120b",
"messages": [
{"role": "user", "content": "Was ist 17 mal 24?"},
{"role": "assistant", "content": null, "tool_calls": [
{"id": "call_abc123", "type": "function", "function": {"name": "calculate", "arguments": "{\"operator\":\"multiply\",\"operand_a\":17,\"operand_b\":24}"}}
]},
{"role": "tool", "tool_call_id": "call_abc123", "content": "408"}
],
"tools": [
{
"type": "function",
"function": {
"name": "calculate",
"description": "Führt eine Grundrechenart aus.",
"parameters": {
"type": "object",
"properties": {
"operator": {"type": "string", "enum": ["add","subtract","multiply","divide"]},
"operand_a": {"type": "number"},
"operand_b": {"type": "number"}
},
"required": ["operator","operand_a","operand_b"]
}
}
}
]
}'

Das Modell nutzt das Ergebnis und formuliert die finale natürlichsprachliche Antwort, beispielsweise: “17 mal 24 ergibt 408.”

Im openai-SDK lässt sich der gesamte Zyklus kompakt abbilden:

import json
from openai import OpenAI
client = OpenAI(base_url="https://ai.noris.de/v1", api_key="YOUR_API_KEY")
tools = [{
"type": "function",
"function": {
"name": "calculate",
"description": "Führt eine Grundrechenart aus.",
"parameters": {
"type": "object",
"properties": {
"operator": {"type": "string", "enum": ["add", "subtract", "multiply", "divide"]},
"operand_a": {"type": "number"},
"operand_b": {"type": "number"}
},
"required": ["operator", "operand_a", "operand_b"]
}
}
}]
messages = [{"role": "user", "content": "Was ist 17 mal 24?"}]
response = client.chat.completions.create(
model="vllm/release/gpt-oss-120b",
messages=messages,
tools=tools
)
assistant_msg = response.choices[0].message
messages.append(assistant_msg)
for tool_call in assistant_msg.tool_calls:
args = json.loads(tool_call.function.arguments)
ops = {"add": lambda a, b: a + b, "subtract": lambda a, b: a - b,
"multiply": lambda a, b: a * b, "divide": lambda a, b: a / b}
result = ops[args["operator"]](args["operand_a"], args["operand_b"])
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
final = client.chat.completions.create(
model="vllm/release/gpt-oss-120b",
messages=messages,
tools=tools
)
print(final.choices[0].message.content)

Wer mehr als nur einzelne Funktionen anbinden möchte, greift zum Model Context Protocol (MCP). Dabei stellt ein separater Server eine Sammlung von Tools bereit, die vom LLM-Client dynamisch entdeckt und genutzt werden können. MCP eignet sich besonders für komplexe Agenten-Setups mit vielen Werkzeugen, da es das manuelle Pflegen langer tools-Listen überflüssig macht.