ChatGPT · Function Calling und Structured Outputs
Der schnellste Weg, aus einem Chatbot ein System zu machen: Sie geben dem Modell eine Reihe von Funktionen, die es aufrufen kann, und verlangen, dass seine Antworten als striktes JSON zurückkommen, dem Ihr Code vertrauen kann. Diese Lektion behandelt beide Mechanismen in der OpenAI APIAPIApplication Programming Interface: eine standardisierte Schnittstelle, über die Anwendungen kommunizieren und Daten austauschen, ohne die interne Funktionsweise der jeweils anderen zu kennen.Vollständige Definition ansehen → im Detail.
- Tool Calling ist der Loop, in dem das Modell Ihren Code bittet, etwas auszuführen.
- Structured Outputs ist die Garantie, dass die Antwort des Modells Ihrem SchemaSchemaEin Schema ist der formale Bauplan, der festlegt, wie Daten in einer Datenbank, einer Datei oder einer Nachricht strukturiert, benannt, typisiert und miteinander verknüpft sind.Vollständige Definition ansehen → entspricht.
Sie lösen unterschiedliche Probleme und funktionieren gut zusammen.
Wir verwenden die Responses API, die primäre Schnittstelle von OpenAI. Chat Completions funktioniert weiterhin und nutzt nahezu identische Konzepte, neue Features erscheinen aber zuerst in Responses. Die Beispiele verwenden gpt-5. Setzen Sie das aktuelle Modell ein, das zu Ihrem Kosten- und Latenzbudget passt, denn die Mechanik ist in der gesamten Modellfamilie gleich.
Zwei Mechanismen, zwei Aufgaben
Halten Sie die beiden gedanklich getrennt:
- Function (Tool) Calling: Das Modell entscheidet, dass es externe Daten oder eine Aktion braucht, und gibt eine strukturierte Anfrage aus, eine *Ihrer* Funktionen aufzurufen. Ihr Code führt sie aus, gibt das Ergebnis zurück, und das Modell macht weiter. So greift das Modell über seinen Kontext hinaus: Datenbanken, Ihre API, ein Taschenrechner, das Wetter.
- Structured Outputs: Sie zwingen die *finale Antwort* des Modells, exakt einem JSON Schema zu entsprechen. Kein Fließtext, keine Markdown-Fences, kein „Klar, hier ist Ihr JSON.“ Nur gültige, parsebare Daten, jedes Mal.
Oft nutzen Sie beides: Tools, um Fakten zu sammeln, Structured Outputs, um das Ergebnis zu verpacken.
Ein Tool definieren
Ein Tool ist eine JSON-Beschreibung einer Funktion: ein Name, eine Beschreibung und ein Parameter-Schema. Die Beschreibung ist keine Dekoration. **Das Modell liest sie, um zu entscheiden, *wann* und *wie* es die Funktion aufruft**, also schreiben Sie sie wie Dokumentation für einen Junior-Entwickler.
from openai import OpenAI
client = OpenAI()
tools = [{
"type": "function",
"name": "get_weather",
"description": "Get the current temperature for a city in Celsius.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. 'Lisbon'"}
},
"required": ["city"],
"additionalProperties": False
},
"strict": True
}]Drei Details machen Aufrufe vorhersehbar:
requiredlistet die Argumente auf, die das Modell liefern muss.additionalProperties: Falseverhindert, dass das Modell zusätzliche Felder erfindet.strict: Truesorgt dafür, dass die Argumente exakt dem Schema entsprechen.
Der Tool-Calling-Loop
Hier liegen viele falsch: Das Modell führt Ihre Funktion nicht aus. Es gibt eine *Anfrage* zurück, sie aufzurufen. Sie führen die Funktion aus, geben das Ergebnis zurück und rufen das Modell erneut auf. Dieser Round Trip ist der Loop, und ererDas Verhältnis von Interaktionen (Likes, Kommentare, Shares) zur Reichweite eines Inhalts. Zeigt, wie stark die Zielgruppe reagiert, gemessen an der Zahl der Personen, die den Inhalt gesehen haben.Vollständige Definition ansehen → liegt in Ihrer Verantwortung.
Der Ablauf:
- Senden Sie die Nutzernachricht plus Ihre Tool-Definitionen.
- Das Modell antwortet mit einem oder mehreren
function_call-Items (oder mit einer normalen Antwort, wenn kein Tool nötig ist). - Ihr Code führt jeden Aufruf aus und hängt ein
function_call_outputmit dem Ergebnis an. - Sie rufen das Modell mit dem aktualisierten Input erneut auf. Jetzt schreibt es die finale Antwort.
import json
def get_weather(city):
# Angenommen, das ruft eine echte API auf.
return {"city": city, "temp_c": 19}
input_list = [{"role": "user", "content": "What's the weather in Lisbon?"}]
response = client.responses.create(
model="gpt-5",
tools=tools,
input=input_list,
)
# Output des Modells weiterreichen (inklusive aller Reasoning-Items).
input_list += response.output
for item in response.output:
if item.type == "function_call":
args = json.loads(item.arguments)
result = get_weather(**args)
input_list.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps(result),
})
final = client.responses.create(
model="gpt-5",
tools=tools,
input=input_list,
)
print(final.output_text)Beachten Sie call_id. Das Modell kann in einem Turn mehrere Tool Calls anfordern (parallel tool calling), und jedes Output muss über die ID dem passenden Call zugeordnet werden. Hängen Sie alle Ergebnisse an, bevor Sie die Folgeanfrage stellen.
Bei Reasoning-Modellen ist es wichtig, response.output vollständig zurückzugeben. Es trägt die Reasoning-Items des Modells in den nächsten Turn, sodass das Modell zwischen Tool Calls nicht den Faden verliert. Wenn Sie den State stattdessen serverseitig verwalten, übergeben Sie previous_response_id.
Praktische Regeln für den Loop
- Loopen Sie, statt von einer einzigen Runde auszugehen. Nachdem Sie Tool-Outputs zurückgegeben haben, ruft das Modell womöglich ein weiteres Tool auf. Packen Sie den Schritt aus Anfrage und Ausführung in eine
while-Schleife, die so lange läuft, bis keinefunction_call-Items mehr zurückkommen. Begrenzen Sie sie (etwa auf 8 Iterationen), damit ein verwirrtes Modell nicht endlos kreist. - Validieren Sie Argumente vor der Ausführung. Das Schema schränkt das Modell ein, trotzdem sollten Sie Tool-Argumente wie jeden nicht vertrauenswürdigen Input behandeln. Geben Sie sie nie direkt an eine Shell, einen SQLSQLSales Qualified Lead: ein Prospect, den das Sales-Team als bereit für direkte Ansprache und ein Angebot validiert hat, weil er klare Qualifizierungskriterien erfüllt.Vollständige Definition ansehen →-String oder einen Dateipfad weiter.
- Halten Sie Funktionen eng gefasst.
get_order_status(order_id)ist besser als eine Mega-Funktion mit einemmode-Flag. Eng gefasste Tools kann das Modell leichter richtig auswählen, und Sie kkDie durchschnittliche Zahl neuer Nutzer, die jeder bestehende Nutzer über Empfehlungen generiert. Über 1,0 verstärkt sich das Wachstum selbst und wird exponentiell.Vollständige Definition ansehen →önnen sie leichter absichern. - Steuern Sie die Auswahl, wenn nötig.
tool_choice="auto"ist der Default."required"erzwingt *irgendein* Tool.- Die Angabe eines bestimmten Tools erzwingt genau dieses eine.
- Die Option
allowed_toolsbeschränkt das Modell für diesen Turn auf eine Teilmenge Ihrer Tools, ohne dass Sie die vollständige Liste umschreiben müssen. Das hält auch das Prompt Caching wirksam. - Setzen Sie
parallel_tool_calls=False, wenn Ihre Tools nacheinander laufen müssen.
Eine neuere Option ist ein Custom Tool ("type": "custom"). Es nimmt Freitext statt JSON-Argumenten entgegen, was zu Inputs wie SQL oder Code-Snippets passt. Optional können Sie es mit einer Grammatik einschränken. Verwenden Sie standardmäßig JSON-Function-Tools und greifen Sie nur dann zu Custom Tools, wenn das JSON-Escaping im Weg ist.
Structured Outputs: Schluss mit dem Parsen von Fließtext
Tool Calling kümmert sich um *Aktionen*. Structured Outputs kümmert sich um die *Form der Antwort*. Wenn Sie strict: true setzen und ein Schema mitgeben, schränkt die API die Generierung so ein, dass das Output Ihrem Schema entspricht. Das ist stärker als der alte Prompt-Trick „antworte in JSON“, der meistens gültiges JSON lieferte und um 2 Uhr nachts kaputtging.
Der sauberste Weg in Python: Definieren Sie Ihr Schema als Pydantic-Modell und überlassen Sie dem SDK das Parsen.
from pydantic import BaseModel
class CalendarEvent(BaseModel):
name: str
date: str
participants: list[str]
response = client.responses.parse(
model="gpt-5",
input=[
{"role": "system", "content": "Extract the event details."},
{"role": "user", "content": "Standup with Ana and Rui on Friday."},
],
text_format=CalendarEvent,
)
event = response.output_parsed
print(event.participants) # ['Ana', 'Rui']output_parsed liefert Ihnen ein typisiertes Objekt, keinen String, den Sie mit json.loads verarbeiten müssen, in der Hoffnung, dass es klappt. Wenn Sie nicht in Python arbeiten, übergeben Sie ein rohes JSON Schema in text.format mit "type": "json_schema" und "strict": true. Zurück bekommen Sie garantiert schemakonformen JSON-Text.
Schema-Design, das wirklich funktioniert
- Jede Property ist faktisch required. Der Strict Mode behandelt alle Keys als required. Um ein Feld „optional“ zu machen, geben Sie ihm eine Union mit null (zum Beispiel
Optional[str]in Pydantic) und prüfen dann in Ihrem Code auf null. - Nutzen Sie Enums, um Auswahlmöglichkeiten einzuschränken. Wenn
statusnuropen,pendingoderclosedsein kann, definieren Sie es als Enum. Das Modell kann dann nichtin-progresszurückgeben und Sie downstream überraschen. - Feldbeschreibungen im Schema steuern, *was* in jedes Feld kommt, nicht nur die Typen. Nutzen Sie sie.
- Achten Sie auf die unterstützte Teilmenge. Structured Outputs unterstützt einen definierten Ausschnitt von JSON Schema, und manche Keywords und
format-Werte werden nicht durchgesetzt. Prüfen Sie den Structured Outputs guide, bevor Sie sich auf eines davon verlassen.
OpenAI Function Calling and Structured Outputs Explained
Beides kombinieren: das realistische Muster
Die meisten Features in Produktion nutzen beides zusammen. Stellen Sie sich einen Support-Assistenten vor, der eine Frage zu einer Rückerstattung beantwortet:
- Das Modell ruft
lookup_order(order_id)auf (Tool Calling), um echte Daten abzurufen. - Sie geben den Bestelldatensatz zurück.
- Das Modell erzeugt eine finale Antwort, die auf ein
RefundDecision-Schema beschränkt ist (Structured Outputs). Das Schema hat ein boolescheseligible, ein Enumreasonund einen Stringcustomer_message.
Ihr Anwendungscode parst nie Freitext. Er liest decision.eligible und verzweigt. Genau darum geht es. Das Modell übernimmt Sprache und Urteilsvermögen, Ihr Code den Kontrollfluss, und dazwischen steht ein typisierter Vertrag.
Tool-*Parameter* und Structured *Outputs* sind getrennte Schema-Slots. Der eine formt den eingehenden Call, der andere die ausgehende Antwort. Sie können jeden allein oder beide gleichzeitig in derselben Anfrage nutzen.
Wissenscheck
1. Was ist der grundlegende Unterschied zwischen Function (Tool) Calling und Structured Outputs?
2. Was passiert im Tool-Calling-Loop tatsächlich, wenn das Modell eine Funktion „aufruft“?
3. Warum betont die Lektion, die „description“ eines Tools wie Dokumentation für einen Junior-Entwickler zu schreiben?
4. Wählen Sie ALLE korrekten Aussagen zu den in der Lektion besprochenen Feldern des Tool-Parameter-Schemas.
Wählen Sie alle richtigen Antworten aus.
5. Wählen Sie ALLE Szenarien, die die in der Lektion beschriebenen Einsatzzwecke korrekt wiedergeben.
Wählen Sie alle richtigen Antworten aus.
Fehler, Kosten und Failure Modes
Reale Systeme brechen auf ganz bestimmte Weise. Planen Sie dafür.
- Halluzinierte Tools oder fehlerhafte Argumente. Mit strikten Tool-Schemas ist das selten. Wenn eine Funktion tatsächlich fehlschlägt, geben Sie einen Fehler *als Tool-Output* zurück (zum Beispiel
{"error": "order not found"}), statt eine Exception zu werfen. Das Modell kann das lesen und sich davon erholen, etwa indem es den Nutzer nach einer korrekten Bestell-ID fragt. - Refusals. Structured Outputs kann aus Sicherheitsgründen trotzdem ablehnen. Das SDK macht das sichtbar, prüfen Sie also auf eine Refusal, bevor Sie
output_parsedvertrauen. Behandeln Sie sie explizit, statt sie als fehlerhafte Daten abzutun. - Truncation. Erreicht das Modell mitten im JSON das Output-TokenTokenEin Token ist die grundlegende Texteinheit, die Sprachmodelle verarbeiten, oft ein Wortfragment, ein ganzes Wort oder ein Satzzeichen und nicht ein einzelnes Zeichen.Vollständige Definition ansehen →-Limit, erhalten Sie selbst im Strict Mode unvollständige Daten. Die Einschränkung garantiert eine *gültige Struktur*, keine *Vollständigkeit*. Prüfen Sie den Response-Status auf
incompleteund erhöhen Siemax_output_tokensbei großen Objekten. Reasoning-Modelle verbrauchen einen Teil dieses Budgets für Reasoning-Tokens, lassen Sie also Spielraum. - Latenz bei der ersten Nutzung eines neuen Schemas. Die erste Anfrage mit einem neuen strikten Schema kann langsamer sein, während die API die Einschränkung vorbereitet. Wiederholt genutzte Schemas sind schnell, verwenden Sie Schemas also wieder, statt sie pro Anfrage dynamisch zu erzeugen.
- Tokens. Tool-Definitionen und Schemas liegen im Kontextfenster und zählen bei jedem Aufruf als Input-Tokens. Lange, ausführliche Schemas über viele Tools summieren sich. Halten Sie Beschreibungen knapp und hängen Sie nur Tools an, die das Modell für diese Anfrage plausibel brauchen könnte.
Wo das im Ökosystem steht
Sie haben jetzt die rohe Mechanik gesehen. Übergeordnete OpenAI-Produkte bauen genau auf diesen Primitiven auf:
- Built-in Tools sind Tool Calls, die die API serverseitig ausführt. Beispiele sind Web Search, File Search und Code Interpreter. Sie aktivieren sie in
tools, ohne den Executor selbst zu schreiben. - Remote MCP Server sind ein Tool-Typ in der Responses API. Sie verweisen
toolsauf einen Model Context ProtocolModel Context ProtocolEin offener Standard, mit dem KI-Assistenten einheitlich und kontrolliert an die Werkzeuge und Daten des Unternehmens angebunden werden, statt jede Integration einzeln zu bauen.Vollständige Definition ansehen → Server (zum Beispiel einen Stripe- oder GitHub-Connector oder Ihren eigenen), und die API findet dessen Tools und ruft sie für Sie auf. MCP ist inzwischen der gängige Weg, ein einziges Set an Tools vielen KI-Clients bereitzustellen. - GPT Actions in Custom GPTs sind Function Calling auf Basis einer OpenAPI-Spezifikation. Das Muster aus Anfrage und Ausführung übernimmt ChatGPT statt Ihres Codes. Für umfangreichere Integrationen in ChatGPT baut das Apps SDK auf MCP auf.
- Das Agents SDK kapselt den Tool-Loop, Retries und Handoffs, sodass Sie die
while-Schleife nicht mehr von Hand schreiben. Wenn die Orchestrierung komplex wird (mehrere Agents, GuardrailsGuardrailsRegeln und Kontrollen, die ein KI-System in sicheren, rechtskonformen und markengerechten Grenzen halten und unerwünschte Ausgaben blockieren.Vollständige Definition ansehen →, Tracing), wechseln Sie dorthin, statt eigenen Loop-Code zu pflegen. Siehe die Agents SDK docs.
Wenn Sie den nackten Loop verstanden haben, wirkt nichts davon wie Magie. Es sind Komfortschichten über dem Vertrag, den Sie gerade kennengelernt haben.
Das Wichtigste in Kürze
- Tool Calling ist ein Loop, den Sie verantworten. Das Modell *fordert* eine Funktion an. Ihr Code führt sie aus, gibt das Output mit
call_idals Schlüssel zurück und ruft das Modell erneut auf. Geben Sie das vollständige Output zurück (inklusive Reasoning-Items), loopen Sie, bis keine Tool Calls mehr übrig sind, und setzen Sie eine harte Obergrenze für Iterationen. - Nutzen Sie Structured Outputs mit
strict: truefür jede Antwort, die Ihr Code parst. Das garantiert die Form und ersetzt fragiles JSON nach dem Prinzip Prompten und Hoffen. In Python definieren Sie ein Pydantic-Modell und lesenoutput_parsed. - Entwerfen Sie Schemas defensiv. Behandeln Sie alle Felder als required (mit null-Unions für optionale Felder), schränken Sie Auswahlmöglichkeiten mit Enums ein und prüfen Sie, ob Ihre Keywords zur unterstützten Teilmenge gehören.
- Geben Sie Tool-Fehler als Daten zurück und prüfen Sie auf Refusals und den Status
incomplete, bevor Sie dem Payload vertrauen. - Wenn der Loop wächst, greifen Sie zu MCP, dem Agents SDK oder GPT Actions. Sie basieren auf denselben Primitiven, das mentale Modell lässt sich also direkt übertragen.
Was Sie aus dieser Lektion umsetzen
Diese Maßnahmen sind im Playbook der Rolle zusammengefasst.
- Neuen Code auf der Responses API starten und response.usage von Tag eins an auslesen