Zurück zum Blog

API & SDKAnleitung

Function Calling in Python mit einer OpenAI-kompatiblen API

Sende Tools, lies tool_calls und gib das Ergebnis mit role tool zurück: eine zweistufige Function-Calling-Schleife in Python mit MiniMax-M2.7.

Von CheapTokens TeamVeröffentlicht am Aktualisiert am 5 Min. Lesezeit

Diagramm einer zweistufigen Tool-Call-Schleife: Tools senden, tool_calls lesen, das Ergebnis als Tool-Nachricht zurückgeben
Auf dieser Seite
  1. So funktioniert Function Calling
  2. Client einrichten
  3. Eine Funktion deklarieren und die erste Anfrage senden
  4. Was du sehen solltest
  5. Mehrere Aufrufe und längere Schleifen
  6. Warum MiniMax-M2.7 für Tool-Schleifen geeignet ist
  7. Wenn es nicht funktioniert
  8. Nächste Schritte

Function Calling (Tool Calling) ermöglicht es einem Modell, deinen Code aufzufordern, eine Funktion auszuführen. Du sendest eine tools-Liste mit der Anfrage, das Modell antwortet mit tool_calls statt mit Text, du führst die Funktion selbst aus und sendest ihr Ergebnis in einer Nachricht mit role: tool zurück. Das Modell schreibt dann die finale Antwort. Diese Anleitung zeigt die gesamte Schleife in Python gegen den OpenAI-kompatiblen Endpoint https://api.cheaptokens.dev/v1/chat/completions mit MiniMax-M2.7 (id MiniMaxAI/MiniMax-M2.7, 180K Kontext, bis zu 8.192 Ausgabe-Tokens pro Antwort).

Tool Calling ist für jedes Modell im Katalog als unterstützt aufgeführt (CheapTokens docs, geprüft am 22. Oktober 2026). Du brauchst einen API-Key; falls du noch keine Anfrage gestellt hast, beginne mit deiner ersten API-Anfrage.

So funktioniert Function Calling#

Das Modell führt nie etwas aus. Es erzeugt nur eine strukturierte Anfrage: "rufe get_weather mit {\"city\": \"Paris\"} auf". Dein Programm erledigt die eigentliche Arbeit und meldet zurück. Daraus ergibt sich eine Schleife aus zwei Anfragen:

  1. Anfrage 1. Du sendest die User-Nachricht plus die tools-Liste. Das Modell antwortet entweder mit Text oder gibt einen oder mehrere tool_calls zurück.
  2. Dein Code. Du parst jeden Aufruf, führst die passende Funktion aus und sammelst das Ergebnis.
  3. Anfrage 2. Du sendest die gesamte Konversation erneut: die ursprünglichen Nachrichten, die Assistant-Nachricht mit tool_calls und eine role: tool-Nachricht pro Aufruf. Das Modell nutzt die Ergebnisse, um die finale Antwort zu schreiben.

Jede Anfrage sendet den gesamten Verlauf erneut, daher verbrauchen Tool-Schleifen mehr Input-Tokens als eine einzelne Frage. Falls das neu für dich ist, lies was ein Token ist.

Client einrichten#

Installiere das OpenAI SDK (pip install openai) und lege deinen Key in einer Umgebungsvariable statt im Quellcode ab:

bash
export CHEAPTOKENS_API_KEY="your-key-here"

Die Base-URL ist https://api.cheaptokens.dev/v1. Nimm die Modell-ID aus der CheapTokens-Dokumentation: verschiedene Anbieter benennen dasselbe Modell unterschiedlich. Diese Anleitung verwendet MiniMaxAI/MiniMax-M2.7.

Eine Funktion deklarieren und die erste Anfrage senden#

Eine Funktion wird im tools-Array beschrieben. Jeder Eintrag hat type: function und ein function-Objekt mit einem name, einem description und parameters, geschrieben als JSON Schema. Die Beschreibung ist das, was das Modell liest, um zu entscheiden, wann es die Funktion aufruft, also schreibe sie als kurzen, konkreten Satz.

Unten steht ein vollständiges Skript. Die Wetterfunktion ist ein Stub mit Fake-Daten, damit das Beispiel keine zusätzlichen Dienste benötigt:

python
import json
import os

from openai import OpenAI

client = OpenAI(
    base_url='https://api.cheaptokens.dev/v1',
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'MiniMaxAI/MiniMax-M2.7'


def get_weather(city: str) -> dict:
    # Stub with fake data: replace with a real lookup
    fake = {'Paris': 14, 'Tokyo': 19}
    return {'city': city, 'temp_c': fake.get(city, 10)}


FUNCTIONS = {'get_weather': get_weather}

tools = [
    {
        'type': 'function',
        'function': {
            'name': 'get_weather',
            'description': 'Get the current temperature in Celsius for a city.',
            'parameters': {
                'type': 'object',
                'properties': {
                    'city': {'type': 'string', 'description': 'City name, for example Paris'},
                },
                'required': ['city'],
            },
        },
    }
]

messages = [{'role': 'user', 'content': 'What is the weather in Paris right now?'}]

# Step 1: the model decides whether to call a function
first = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
message = first.choices[0].message

if not message.tool_calls:
    print(message.content)
    raise SystemExit

# Step 2: run every requested call and send the results back
messages.append(message)
for call in message.tool_calls:
    args = json.loads(call.function.arguments)
    result = FUNCTIONS[call.function.name](**args)
    messages.append({
        'role': 'tool',
        'tool_call_id': call.id,
        'content': json.dumps(result),
    })

final = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
print(final.choices[0].message.content)

Drei Details entscheiden, ob die Schleife funktioniert:

  • call.function.arguments ist ein JSON-String, kein Dictionary. Parse ihn mit json.loads.
  • Jede role: tool-Nachricht muss die tool_call_id des Aufrufs tragen, den sie beantwortet, also call.id.
  • Der content einer Tool-Nachricht ist ein String. Verwende json.dumps für strukturierte Ergebnisse.

Was du sehen solltest#

Wenn du die Felder nach der ersten Anfrage ausgibst, sehen sie so aus (die Call-ID unterscheidet sich bei jedem Lauf):

text
finish_reason: tool_calls
call.id: call_abc123
call.function.name: get_weather
call.function.arguments: {"city": "Paris"}

Das Skript gibt dann die finale Antwort aus, einen einfachen Satz wie "It is currently 14 °C in Paris." Der Wortlaut ändert sich von Lauf zu Lauf; die Zahl stammt aus deiner Funktion.

Mehrere Aufrufe und längere Schleifen#

Ein Modell kann mehr als einen Aufruf in einer einzigen Antwort zurückgeben, zum Beispiel wenn du nach dem Wetter in zwei Städten fragst. Deshalb iteriert das Beispiel über message.tool_calls und beantwortet jeden Aufruf mit seiner eigenen ID. Der Function-Calling-Leitfaden von OpenAI beschreibt dasselbe Verhalten: Mit dem Standard-tool_choice von auto kann das Modell null, eine oder mehrere Funktionen aufrufen (OpenAI docs, geprüft am 22. Oktober 2026; die Seite hat kein Datum der letzten Aktualisierung und beschreibt die neuere Responses-API-Form, während diese Anleitung die Chat-Completions-Form des /v1/chat/completions-Endpoints verwendet).

Agenten wiederholen die beiden Schritte, bis das Modell aufhört, nach Tools zu fragen. Wickle die Anfragen in eine Schleife und setze immer ein Schrittlimit:

python
for _ in range(5):  # safety limit against endless loops
    response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
    message = response.choices[0].message
    if not message.tool_calls:
        print(message.content)
        break
    messages.append(message)
    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        result = FUNCTIONS[call.function.name](**args)
        messages.append({'role': 'tool', 'tool_call_id': call.id, 'content': json.dumps(result)})

Wenn du die Antwort Token für Token erscheinen lassen willst, kombiniere dies mit Streaming in Python.

Warum MiniMax-M2.7 für Tool-Schleifen geeignet ist#

Eine Tool-Schleife sendet viele Anfragen mit wachsendem Verlauf. Für dieses Muster bietet CheapTokens MiniMax-M2.7: 180K Kontext, bis zu 8.192 Ausgabe-Tokens pro Antwort, ein Preis zu jeder Tageszeit ohne Peak-Zeiten und ein Guthaben für alle Modelle. Input und Output zusammengerechnet ist es günstiger als OpenRouters Referenzpreis (CheapTokens Preisvergleich, geprüft am 31. Oktober 2026).

Aktuelle Preise pro 1M Tokens: Input $0.05, Output $0.15. Als Beispiel-Workload (keine Messung) kostet der obige Round Trip mit zwei Anfragen und insgesamt etwa 2.700 Input- und 350 Output-Tokens $0.00019. Die vollständige Preisliste steht auf der Preisseite.

Um ein anderes Modell auszuprobieren, ändere nur die ID; die Liste der IDs steht auf der Modellseite.

Wenn es nicht funktioniert#

  • message.tool_calls ist None. Das Modell hat mit Text geantwortet. Formuliere die Funktion description spezifischer und stelle sicher, dass die Frage die Funktion tatsächlich benötigt.
  • json.JSONDecodeError bei arguments. Das Modell hat ungültiges JSON erzeugt. Wickle json.loads in try/except und sende den Fehlertext als Tool-Ergebnis zurück, damit das Modell es mit korrigierten Argumenten erneut versuchen kann.
  • Die zweite Anfrage schlägt fehl oder das Modell ignoriert das Ergebnis. Prüfe, dass jeder Aufruf genau eine role: tool-Nachricht mit passender tool_call_id hat und dass die Assistant-Nachricht mit tool_calls davor im Verlauf steht.
  • KeyError in FUNCTIONS[...]. Das Modell hat eine Funktion benannt, die du nicht registriert hast. Gib einen kurzen Fehler-String als Tool-Ergebnis zurück, statt abzustürzen.
  • Die Antwort ist abgeschnitten. Prüfe finish_reason: Der Wert length bedeutet, dass das Ausgabe-Token-Limit erreicht wurde. Siehe warum eine KI-Antwort mitten im Satz stoppt.
  • 401 oder Authentifizierungsfehler. Prüfe, dass der Key in CHEAPTOKENS_API_KEY steht und die Base-URL mit /v1 endet.

Nächste Schritte#

Ersetze den Stub durch eine echte Funktion: eine Datenbankabfrage, einen Suchaufruf oder einen Taschenrechner. Füge weitere Einträge zu tools und zu FUNCTIONS hinzu und halte jedes Funktionsergebnis kurz, denn es geht bei jeder folgenden Anfrage wieder in den Verlauf ein. Um einen Key zu erhalten und den ersten Aufruf zu machen, öffne die CheapTokens docs.