Blog • Open Responses

Nachdem Open Responses der AAIF beigetreten ist: welche type-, arguments- und status-Prüfungen vor dem Replay eines Tool-Call-Items?

Am 22. Oktober 2026 machte die Agentic AI Foundation Open Responses zum gehosteten Projekt. Die Spec fasst eine Runde in Items: message, function_call und function_call_output sind der portable Kern; ein type mit Anbieter-Präfix gehört zu dieser Implementierung.

Bevor ein geloggtes Item an ein anderes Modell geht, drei Felder lokal prüfen. Ein Doppelpunkt in type heißt: kein portables function_call. In den Spec-Beispielen ist arguments ein JSON-String: erst parse, dann Tool-JSON-Schema. Ist status nicht completed, kein function_call_output anhängen.

Der gestrige Text Bedrock Structured Outputs behandelt die Form des Modelltexts. Chat Completions speichert arguments oft auch als String, in einem anderen Umschlag—siehe AI Agent und JSON Schema. Schema-Bestanden heißt nicht ausführen—siehe die Rechteentscheidung. Dieser Artikel behandelt nur das Open-Responses-Item.

Zuerst dies: Die AAIF hat eine gemeinsame Item-Form übernommen. Ein JSON-Datensatz kann weiter nur einem Anbieter gehören. Unten der Reihe nach: type, arguments, status, und welches Feld das Replay stoppt.

Am 22. Oktober wechselte das gemeinsame Item in offene Governance

Bei AGNTCon und MCPCon kündigte die AAIF vier Projekte an. Open Responses, beigetragen von OpenAI, steht neben MCP und A2A unter Linux-Foundation-Governance. Die Meldung: AAIF-Ankündigung. Am selben Tag kamen Anthropic Agent Skills, Hugging Face OpenEnv und Alibaba OpenSandbox dazu. Dieser Text behandelt weder deren Kataloge noch das Skill-Format, nur das Item-JSON von Open Responses.

Ein AWS-Open-Source-Beitrag vom selben Tag schreibt, Amazon Bedrock unterstütze die Responses API und beteilige sich an der Spec. Siehe den AWS-Open-Source-Beitrag. Feldbedeutungen folgen der Open-Responses-Spezifikation. Nicht gestreamte Körper sind application/json, Items unterscheiden sich über type. Die Frage: ein function_call liegt schon im Log; welche drei Felder stoppen die nächste Anfrage vor Anbieterwechsel oder Replay am nächsten Tag.

Welche Felder braucht ein replaybares function_call mindestens?

Ein function_call für den nächsten Check trägt mindestens type, id, name, call_id, status und arguments. Der Wert von arguments ist ein String; die Tool-Parameter stecken darin. Unten ist region apac, geprüft mit demselben Schema.

function_call für die nächsten Checks. arguments ist ein String.
{
  "type": "function_call",
  "id": "fc_001",
  "name": "get_latest_sales_report",
  "call_id": "call_123",
  "status": "completed",
  "arguments": "{\"region\":\"apac\"}"
}

Nach der lokalen Tool-Ausführung hängt die nächste Anfrage function_call_output an, kein tool_calls-Array aus Chat Completions. Das Fortsetzungsbeispiel der Spec behält dieselbe call_id und setzt output. Kommt das Ergebnis später als Item zurück, folgen id und status.

function_call_output der nächsten Anfrage, gleiche call_id.
{
  "type": "function_call_output",
  "call_id": "call_123",
  "output": "sales report for apac"
}

Warum blockiert ein Doppelpunkt in type den Wechsel des Anbieters?

Die Spec erlaubt eigene Items, doch type braucht ein Anbieter-Slug-Präfix, zum Beispiel openai:web_search_call. Der Datensatz kann in der ursprünglichen Sitzung bleiben und zum selben Anbieter zurück. Portabilität auf einen anderen Open-Responses-Server wird nicht angenommen. Lokal: Doppelpunkt heißt namespaced, kein function_call_output.

Item mit Anbieter-Präfix. Nicht erneut als get_latest_sales_report ausführen.
{
  "type": "openai:web_search_call",
  "id": "ws_001",
  "status": "completed",
  "action": { "type": "search", "query": "apac sales report" }
}
Sichtbarer typeReplay bei anderem Anbieter?Lokale Aktion
function_callWeiter zum nächsten CheckDanach arguments und status prüfen.
messageKann in inputAls Gesprächstext, nicht als Tool-Ergebnis.
openai:web_search_callNicht portabelBeim Originalanbieter lassen oder aus dem anbieterübergreifenden Log nehmen.

Warum muss arguments erst ein String sein, dann das Schema?

Das Streaming-Event response.function_call_arguments.delta hängt Text an, bevor das Objekt fertig ist. Gespeichertes arguments ist daher ein String, kein Objekt. Ein Log mit Objekt in arguments scheitert beim nächsten Parse oder wird von einer anderen Implementierung abgelehnt. Erst String prüfen, dann json.loads, dann das Tool-JSON-Schema. Das Schema unten erlaubt nur apac, emea und amer.

Tool-Parameter-Schema. region nur apac, emea oder amer.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "additionalProperties": false,
  "required": ["region"],
  "properties": {
    "region": {
      "type": "string",
      "enum": ["apac", "emea", "amer"]
    }
  }
}

Der String unten lässt sich parse, aber region ist global und nicht im enum. Das Gate soll arguments_schema liefern, das Tool läuft nicht.

Parse gelingt, Schema scheitert. Keine Region raten.
{
  "type": "function_call",
  "id": "fc_009",
  "name": "get_latest_sales_report",
  "call_id": "call_129",
  "status": "completed",
  "arguments": "{\"region\":\"global\"}"
}
Sichtbares argumentsGate-ErgebnisLokale Aktion
String, Objekt mit region apacokTool ausführen, dann function_call_output schreiben.
arguments ist selbst ein Objektarguments_typeAblehnen. Erst wieder zum String, dann parse.
Parse gelingt, region ist globalarguments_schemaNicht ausführen. Validierungsfehler zurückschreiben, keine Region raten.

Warum kein Tool-Ergebnis, wenn status nicht completed ist?

Die Spec macht jedes Item zu einer Zustandsmaschine. in_progress heißt, die Ausgabe läuft noch. incomplete ist terminal, muss das letzte Item dieser Response sein, und die Response selbst muss incomplete sein. Nach completed ändert sich das Item nicht mehr. Bei incomplete kann arguments abgeschnitten sein; halbes JSON darf nicht laufen.

statusfunction_call_output schreiben?
in_progressNein. Warten, bis der Strom endet.
incompleteNein. Terminal, Argumente können abgeschnitten sein.
completedJa. Danach: kein Doppelpunkt in type, arguments besteht das Schema, call_id stimmt mit dem nächsten Item.
Jeder andere Wert, auch failedNein. Keinen Seiteneffekt ausführen.

Das Gate unten ist derselbe Code, der die Beispiele dieses Artikels prüft. Es liefert den Grund in dieser Reihenfolge: Doppelpunkt, kein function_call, status, Typ von arguments, Parse, Schema. Nur ok darf das Tool ausführen und function_call_output anhängen.

Lokales Gate vor dem Replay. Alles außer ok führt das Tool nicht aus.
def actual_type(data: object) -> str:
    if data is None:
        return "null"
    if isinstance(data, bool):
        return "boolean"
    if isinstance(data, list):
        return "array"
    if isinstance(data, int):
        return "integer"
    if isinstance(data, float):
        return "number"
    if isinstance(data, str):
        return "string"
    if isinstance(data, dict):
        return "object"
    raise TypeError(type(data))

def validate(data: object, schema: dict) -> list[str]:
    errors: list[str] = []
    if not isinstance(schema, dict):
        return errors
    types = schema.get("type")
    if isinstance(types, str):
        types = [types]
    actual = actual_type(data)
    if types and not any(t == actual or (t == "number" and actual == "integer") for t in types):
        errors.append("type")
    if "enum" in schema and not any(value == data for value in schema["enum"]):
        errors.append("enum")
    if isinstance(data, dict) and schema.get("properties"):
        for key in schema.get("required") or []:
            if key not in data:
                errors.append(f"required:{key}")
        if schema.get("additionalProperties") is False:
            for key in data:
                if key not in schema["properties"]:
                    errors.append(f"additional:{key}")
        for key, sub in schema["properties"].items():
            if key in data:
                errors.extend(validate(data[key], sub))
    if isinstance(data, list) and schema.get("items"):
        for item in data:
            errors.extend(validate(item, schema["items"]))
    return errors

def replay_gate(item: dict, schema: dict) -> str:
    item_type = item.get("type")
    if not isinstance(item_type, str) or ":" in item_type:
        return "namespaced"
    if item_type != "function_call":
        return "not_function_call"
    if item.get("status") != "completed":
        return "status"
    arguments = item.get("arguments")
    if not isinstance(arguments, str):
        return "arguments_type"
    try:
        parsed = json.loads(arguments)
    except json.JSONDecodeError:
        return "arguments_parse"
    if validate(parsed, schema):
        return "arguments_schema"
    return "ok"

Wie prüft man die drei Felder in JSONNote?

  1. 1
    Zuerst: ist arguments ein String?

    Öffnen Sie JSON formatieren. Das ganze Item einfügen. Entfaltet sich arguments zu einem Objekt statt einem String, ist die Speicherform schon falsch und das Gate liefert arguments_type.

  2. 2
    Dann die geparsten Parameter prüfen

    Das aus dem arguments-String geparste Objekt in JSON Schema. Als Schema das region-Enum von oben. global wird markiert; vor der Ausführung nicht auf apac ändern.

  3. 3
    Zuletzt die Items beider Anbieter vergleichen

    Öffnen Sie JSON Diff. Links das Item des Originalanbieters, rechts das zum Replay. Wechselt type von function_call zu einer Erweiterung mit Doppelpunkt, oder call_id stimmt nicht, die nächste Anfrage nicht senden.

Häufige Fragen

Kann dasselbe JSON nach dem AAIF-Beitritt direkt zu Bedrock oder einer anderen Implementierung?

Nein. Portabel sind message, function_call und function_call_output. Ein Item mit Anbieter-Präfix gilt nicht als serverübergreifend replaybar. Claude Structured Outputs auf Bedrock betreffen eine andere Schicht: JSON Schema für Modelltext, nicht diesen Item-Umschlag.

Warum wird arguments nicht als Objekt gespeichert?

Die Spec-Beispiele speichern arguments als JSON-String. Streaming hängt per function_call_arguments.delta an, und vor completed kann der String unvollständig sein. Nicht vorzeitig parse und ausführen.

Darf ich das Tool ausführen, wenn status incomplete ist?

Nein. incomplete ist terminal. Die Spec verlangt, dass es das letzte Item dieser Response ist und die Response selbst incomplete ist. Abgeschnittenes arguments scheitert meist am Schema; selbst bei zufälligem Bestehen nicht als fertigen Aufruf ausführen.

Brauche ich ein lokales Schema, wenn allowed_tools die Tool-Namen schon begrenzt?

Ja. allowed_tools begrenzt, welche Tool-Namen in dieser Anfrage wirklich laufen dürfen; die Definitionen können weiter vollständig in tools stehen. Es prüft nicht, ob region in arguments im Enum liegt. Ein erlaubter Name braucht weiter Parse und Schema.

Dürfen tool_calls aus Chat Completions im selben Array wie Open-Responses-Items stehen?

Nein. Open Responses nutzt type, call_id und arguments als String. Chat Completions nutzt ein tool_calls-Array. In einem Log vermischt, schickt das Replay den falschen Umschlag zum nächsten Anbieter. Die zwei Dokumente per Diff getrennt vergleichen.

Fazit

Nachdem Open Responses der AAIF beigetreten ist, teilen Anbieter die Item-Form, nicht jede Logzeile.

Entscheidung: Doppelpunkt in type heißt kein portables function_call; arguments erst als String bestätigen, dann mit dem Tool-Schema prüfen; ist status nicht completed, kein function_call_output.

Als Nächstes: formatieren, ob arguments ein String ist, die geparsten Parameter per Schema prüfen, die beiden Items per Diff vergleichen.

Zurück zum Blog