Funktionsaufrufe: Responses API und Chat Completions im Vergleich

Vergleich auf Protokollebene: Funktionsdefinitionen, Aufruf-IDs, Ergebnisse, gestreamte Argumente, Autorisierung, Idempotenz und Routenkompatibilität.

Funktionsaufrufe: Responses API und Chat Completions im Vergleich

Responses und Chat Completions können eine Anwendung zur Ausführung einer Funktion auffordern, stellen die Werkzeugschleife aber unterschiedlich dar. Chat Completions definiert Funktionen unter tools[].function, liefert message.tool_calls und nimmt Ergebnisse als role: "tool" entgegen. Responses verwendet flache Funktionsdefinitionen, typisierte function_call-Ausgabeelemente und über call_id verknüpfte function_call_output-Elemente.

Das Modell führt die Funktion nicht aus. Der Anwendungscode muss Argumente validieren, die Operation autorisieren, sie kontrolliert ausführen, das Ergebnis zurückgeben und doppelte Nebenwirkungen bei Wiederholungen verhindern.

Die Werkzeugschleife hat vier Schritte

1. Eine zulässige Funktion und ihr Argumentschema deklarieren
2. Einen oder mehrere vom Modell vorgeschlagene Funktionsaufrufe empfangen
3. Jeden Aufruf in der Anwendung validieren, autorisieren und ausführen
4. Jedes Ergebnis mit der Korrelations-ID des Aufrufs zurückgeben

Erst nach Schritt vier kann das Modell eine Antwort auf Basis des Werkzeugergebnisses erzeugen. Fordert es ein weiteres Werkzeug an, beginnt eine neue Runde mit einer neuen Aufruf-ID. Der OpenAI-Leitfaden zu Function Calling beschreibt denselben mehrstufigen Austausch.

Übertragungsverträge vergleichen

Aspekt Responses API Chat Completions
Definition Flaches Element in tools[] Felder unter tools[].function
Vorgeschlagener Aufruf function_call-Ausgabeelement tool_calls[] der Assistentennachricht
Korrelation call_id id, Ergebnis als tool_call_id
Name function_call.name tool_calls[].function.name
Argumente JSON-String in function_call.arguments JSON-String in tool_calls[].function.arguments
Ergebnis function_call_output Nachricht mit role: "tool"
Streaming Typisierte Argumentereignisse Fragmente in delta.tool_calls[]
Abschlusstext Ausgabeelemente / SDK-Hilfsfunktion choices[0].message.content

Array-Positionen sind keine stabile Zuordnung. Parallele Aufrufe und Fragmente können in einer anderen Reihenfolge eintreffen, als die Anwendung sie beendet. Die explizite ID ist der stabile Zuordnungsschlüssel.

Ein striktes Funktionsschema definieren

{
  "type": "object",
  "properties": {
    "order_id": {
      "type": "string",
      "pattern": "^ORDER-[0-9]{4}$"
    }
  },
  "required": ["order_id"],
  "additionalProperties": false
}

strict: true fordert Schemakonformität, ersetzt aber Analyse und Validierung in der Anwendung nicht. pattern und andere Schlüsselwörter können je Anbieter variieren; deshalb gilt derselbe Test wie für Structured Outputs.

Die Schleife mit Responses API implementieren

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.MODELFLARE_API_KEY,
  baseURL: "https://modelflare.dev/v1",
});

const model = process.env.MODEL_ID;
if (!model) throw new Error("MODEL_ID ist erforderlich");

const orders = new Map([
  ["ORDER-1001", { status: "in_transit", eta: "2026-08-06" }],
]);

const tools = [
  {
    type: "function",
    name: "get_delivery_status",
    description: "Gibt den aktuellen Lieferstatus einer Bestellung zurück.",
    parameters: {
      type: "object",
      properties: {
        order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" },
      },
      required: ["order_id"],
      additionalProperties: false,
    },
    strict: true,
  },
];

function executeTool(name, rawArguments) {
  if (name !== "get_delivery_status") {
    throw new Error(`Werkzeug nicht zulässig: ${name}`);
  }

  const args = JSON.parse(rawArguments);
  if (!/^ORDER-[0-9]{4}$/.test(args.order_id)) {
    throw new Error("Ungültige order_id");
  }

  return orders.get(args.order_id) ?? { status: "not_found" };
}

const first = await client.responses.create({
  model,
  input: "Wo befindet sich die Bestellung ORDER-1001?",
  tools,
  parallel_tool_calls: false,
});

const calls = first.output.filter((item) => item.type === "function_call");
if (calls.length === 0) {
  console.log(first.output_text);
  process.exit(0);
}

const toolOutputs = calls.map((call) => ({
  type: "function_call_output",
  call_id: call.call_id,
  output: JSON.stringify(executeTool(call.name, call.arguments)),
}));

const final = await client.responses.create({
  model,
  input: [...first.output, ...toolOutputs],
  tools,
  parallel_tool_calls: false,
});

console.log(final.output_text);

call.call_id und das call_id des Ergebnisses müssen übereinstimmen. Ein zustandsloser Austausch sendet vorherige Ausgabeelemente und Ergebnisse gemeinsam zurück. Eine zustandsbehaftete Fortsetzung darf nur genutzt werden, wenn Route und Speicherungsrichtlinie sie nachweislich unterstützen.

Die Schleife mit Chat Completions implementieren

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.MODELFLARE_API_KEY,
  baseURL: "https://modelflare.dev/v1",
});

const model = process.env.MODEL_ID;
if (!model) throw new Error("MODEL_ID ist erforderlich");

const orders = new Map([
  ["ORDER-1001", { status: "in_transit", eta: "2026-08-06" }],
]);

const tools = [
  {
    type: "function",
    function: {
      name: "get_delivery_status",
      description: "Gibt den aktuellen Lieferstatus einer Bestellung zurück.",
      parameters: {
        type: "object",
        properties: {
          order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" },
        },
        required: ["order_id"],
        additionalProperties: false,
      },
      strict: true,
    },
  },
];

function executeTool(name, rawArguments) {
  if (name !== "get_delivery_status") {
    throw new Error(`Werkzeug nicht zulässig: ${name}`);
  }

  const args = JSON.parse(rawArguments);
  if (!/^ORDER-[0-9]{4}$/.test(args.order_id)) {
    throw new Error("Ungültige order_id");
  }

  return orders.get(args.order_id) ?? { status: "not_found" };
}

const messages = [
  { role: "user", content: "Wo befindet sich die Bestellung ORDER-1001?" },
];

const first = await client.chat.completions.create({
  model,
  messages,
  tools,
  parallel_tool_calls: false,
});

const assistant = first.choices[0].message;
const calls = assistant.tool_calls ?? [];
if (calls.length === 0) {
  console.log(assistant.content);
  process.exit(0);
}

messages.push(assistant);
for (const call of calls) {
  const result = executeTool(call.function.name, call.function.arguments);
  messages.push({
    role: "tool",
    tool_call_id: call.id,
    content: JSON.stringify(result),
  });
}

const final = await client.chat.completions.create({
  model,
  messages,
  tools,
  parallel_tool_calls: false,
});

console.log(final.choices[0].message.content);

Die Assistentennachricht mit den Aufrufen muss vor den Werkzeugergebnissen in messages stehen. Eine fehlende Nachricht oder eine nicht passende tool_call_id erzeugt einen ungültigen Gesprächsverlauf.

Streaming-Argumente als Fragmente behandeln

{"order ist unvollständiges, nicht ungültiges JSON. Pro Aufruf ist ein eigener Puffer nötig: Inkremente anhängen, auf vollständige Argumente oder den Abschluss warten, die vollständige Zeichenfolge einmal analysieren und erst danach validieren und ausführen. Responses nutzt typisierte Ereignisse, Chat Completions choices[].delta.tool_calls[]. Beim ersten Fragment darf keine Funktion starten. Siehe Leitfaden zum Streaming von KI-APIs.

Werkzeugausführung sicher und idempotent machen

  • ausschließlich registrierte Funktionsnamen erlauben;
  • Argumentgröße begrenzen und vollständiges Schema validieren;
  • Benutzer oder Arbeitslast für die Ressource autorisieren;
  • schreibgeschützte Werkzeuge und Nebenwirkungen trennen;
  • Geheimnisse vor der Rückgabe entfernen;
  • Fristen und begrenzte Wiederholungen nachgelagerter Aufrufe verwenden;
  • eine sichere Vorgangsreferenz statt privater Inhalte speichern.

Für Nebenwirkungen wird ein Idempotenzschlüssel aus einer stabilen Anwendungsanfrage und der Aufruf-ID gebildet. Das Ergebnis muss vor der Modellantwort gespeichert werden. Eine Wiederholung liefert das gespeicherte Ergebnis, statt eine zweite Erstattung, Nachricht, Bereitstellung oder Datenbankänderung auszuführen.

Funktionsname und Argumente aus dem Modell sind nicht vertrauenswürdige Eingaben. Ein gültiges Schema erteilt keine Berechtigung und ersetzt keine Transaktion.

Mehrere Aufrufe bewusst behandeln

Mit parallel_tool_calls: false bleibt der Zustandsautomat übersichtlich. Bei paralleler Ausführung müssen Ergebnisse über Aufruf-IDs zugeordnet, Anzahl und Parallelität begrenzt, Fehlerregeln definiert und reihenfolgeabhängige Nebenwirkungen serialisiert werden. Parallelität ist für unabhängige Lesezugriffe nützlich, erhöht aber die Komplexität von Autorisierung, Wiederholungen und Teilausfällen.

Die Modelflare-Kompatibilitätsgrenze verstehen

Modelflare erhält strikte Funktionsdefinitionen und ordnet unterstützte Aufrufformen auf zulässigen OpenAI/Codex-Routen zwischen Responses und Chat Completions zu. Diese Zuordnung deckt absichtlich weniger als die vollständige Werkzeugoberfläche von Responses ab.

Von der Anwendung definierte Funktionen lassen sich in beiden Formaten ausdrücken. Vom Anbieter betriebene Werkzeuge wie Suche oder Codeausführung sind nicht gleichwertig und dürfen nicht als konvertierbar angenommen werden. Bei anderen OpenAI-kompatiblen Familien werden Chat Completions bis zur Prüfung unverändert durchgereicht; der Anbietervertrag bestimmt die Unterstützung für tools, strict, parallele Aufrufe, gestreamte Argumente und tool_choice.

Test Erforderliche Evidenz
Schreibgeschützter Aufruf Name, analysierte Argumente, Aufruf-ID, Ergebniszuordnung, Abschlusstext
Ungültige Argumente Expliziter Fehler ohne Ausführung
Unbekannte Funktion Ablehnung durch die Positivliste
Kein Werkzeug nötig Normaler Text ohne erfundenen Aufruf
Streaming Rekonstruierte Argumente und korrekte ID
Wiederholte Anfrage Eine Nebenwirkung oder ein gespeichertes Ergebnis
Mehrere Aufrufe Zuordnung unabhängig von der Abschlussreihenfolge
Ersatzroute Gleiches Modell, Protokoll, Schema und Ergebnisvertrag

Zuverlässiges Routing für KI-APIs hält Routenwechsel explizit; Sicherheit von KI-API-Schlüsseln trennt Arbeitslasten und Rechte.

Das Format nach dem Anwendungsvertrag wählen

Responses eignet sich für typisierte Ausgabeelemente, das Responses-Ereignismodell, Zustandsfortsetzung und andere verifizierte Funktionen. Chat Completions passt, wenn die Anwendung einen stabilen Nachrichtenverlauf besitzt und der Werkzeugvertrag des Anbieters geprüft ist. Siehe Responses API und Chat Completions.

Der Standard bleibt gleich: explizite Werkzeug-Positivliste, striktes Argumentschema, anwendungsseitige Validierung, Autorisierung vor der Ausführung, Zuordnung per Aufruf-ID, Idempotenz bei Nebenwirkungen und ein vollständiges Ergebnis für das Modell. Diese Kontrollen schaffen Zuverlässigkeit, nicht der Name des Endpunkts.