Блог • Open Responses
После входа Open Responses в AAIF: какие проверки type, arguments и status нужны перед повтором tool-call item?
22 октября 2026 Agentic AI Foundation приняла Open Responses как проект. Спека складывает ход в item: message, function_call и function_call_output — переносимое ядро; type с префиксом провайдера принадлежит этой реализации.
Перед отправкой item из журнала другой модели проверьте три поля локально. Двоеточие в type значит, что это не переносимый function_call. В примерах спеки arguments — JSON-строка: сначала parse, затем JSON Schema инструмента. Если status не completed, не добавляйте function_call_output.
Вчерашний текст structured outputs Bedrock про форму текста модели. В Chat Completions arguments тоже часто строка, но конверт другой — см. AI Agent и JSON Schema. Прохождение schema не разрешает выполнение — см. решение о правах. Эта статья только про item Open Responses.
- Переносимые type: message, function_call или function_call_output.
- type с двоеточием — расширение провайдера, не переносимый вызов.
- arguments должен быть строкой; разобранный объект сверьте со schema инструмента, включая region.
- Останавливайтесь на in_progress или incomplete. function_call_output только при completed, и call_id должен совпасть.
Сначала это: AAIF приняла общую форму item. Одна JSON-запись всё ещё может принадлежать одному провайдеру. Дальше по порядку: type, arguments, status и какое поле останавливает повтор.
22 октября общий item перешёл под открытое управление
На AGNTCon и MCPCon AAIF объявила четыре проекта. Open Responses от OpenAI стоит рядом с MCP и A2A под управлением Linux Foundation. Анонс: анонс AAIF. В тот же день вошли Agent Skills от Anthropic, OpenEnv от Hugging Face и OpenSandbox от Alibaba. Здесь не каталоги и не формат skill, только JSON item Open Responses.
В тот же день пост AWS Open Source написал, что Amazon Bedrock поддерживает Responses API и участвует в спеке. См. пост AWS Open Source. Смысл полей — по спецификации Open Responses. Тела без стриминга — application/json, item различаются по type. Вопрос: function_call уже в журнале; какие три поля должны остановить следующий запрос перед сменой провайдера или повтором на следующий день.
Какие поля минимум нужны replayable function_call?
function_call для следующей проверки несёт как минимум type, id, name, call_id, status и arguments. Значение arguments — строка, параметры инструмента внутри. Ниже region равен apac и снова проверяется той же schema.
{
"type": "function_call",
"id": "fc_001",
"name": "get_latest_sales_report",
"call_id": "call_123",
"status": "completed",
"arguments": "{\"region\":\"apac\"}"
}
После выполнения инструмента у вас следующий запрос добавляет function_call_output, не массив tool_calls из Chat Completions. Пример продолжения в спеке сохраняет тот же call_id и output. Если сервер вернёт результат как item, тогда появятся id и status.
{
"type": "function_call_output",
"call_id": "call_123",
"output": "sales report for apac"
}
Почему двоеточие в type не даёт выполнить вызов у другого провайдера?
Спека разрешает свои item, но type обязан нести префикс slug провайдера, например openai:web_search_call. Запись может остаться в исходной сессии и вернуться тому же провайдеру. Переносимость на другой сервер Open Responses не предполагается. Локально: двоеточие значит namespaced, function_call_output не создаётся.
{
"type": "openai:web_search_call",
"id": "ws_001",
"status": "completed",
"action": { "type": "search", "query": "apac sales report" }
}
| Видимый type | Повтор у другого провайдера? | Локальное действие |
|---|---|---|
function_call | Дальше к следующей проверке | Дальше arguments и status. |
message | Можно положить в input | Как текст диалога, не как результат инструмента. |
openai:web_search_call | Не переносимо | Оставить у исходного провайдера или вынуть из общего журнала. |
Почему arguments сначала строка, и только потом schema?
Событие response.function_call_arguments.delta дописывает текст до готовности объекта. Сохранённый arguments — строка, не объект. Журнал с объектом в arguments упадёт на следующем parse или будет отклонён другой реализацией. Сначала убедитесь, что это string, затем json.loads, затем JSON Schema инструмента. Schema ниже допускает только apac, emea и amer.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["region"],
"properties": {
"region": {
"type": "string",
"enum": ["apac", "emea", "amer"]
}
}
}
Строка ниже разбирается, но region равен global и его нет в enum. Шлюз должен вернуть arguments_schema, инструмент не выполняется.
{
"type": "function_call",
"id": "fc_009",
"name": "get_latest_sales_report",
"call_id": "call_129",
"status": "completed",
"arguments": "{\"region\":\"global\"}"
}
| Видимый arguments | Результат шлюза | Локальное действие |
|---|---|---|
| Строка, в объекте region равен apac | ok | Выполнить инструмент, затем записать function_call_output. |
| arguments сам является объектом | arguments_type | Отклонить. Сначала вернуть строку, потом parse. |
| Строка разбирается, но region равен global | arguments_schema | Не выполнять. Вернуть ошибку проверки, регион не угадывать. |
Почему не писать результат инструмента, если status не completed?
Спека делает каждый item конечным автоматом. in_progress значит, вывод ещё идёт. incomplete терминален, обязан быть последним item этого ответа, и сам response тоже incomplete. После completed item больше не меняется. При incomplete arguments может быть обрезан; неполный JSON выполнять нельзя.
| status | Писать function_call_output? |
|---|---|
in_progress | Нет. Ждать конца потока. |
incomplete | Нет. Терминально, аргументы могут быть обрезаны. |
completed | Да. Затем: в type нет двоеточия, arguments проходит schema, call_id совпадает со следующим item. |
| Любое другое значение, включая failed | Нет. Побочный эффект не выполнять. |
Шлюз ниже — тот же код, который проверяет образцы этой статьи. Он возвращает причину по порядку: двоеточие, не function_call, status, тип arguments, parse, schema. Только ok позволяет выполнить инструмент и добавить function_call_output.
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"
Как разобрать три поля в JSONNote?
- 1Сначала: arguments — строка?
Откройте JSON Формат. Вставьте item целиком. Если arguments раскрывается в объект, а не в одну строку, форма хранения уже неверна, и шлюз вернёт arguments_type.
- 2Затем проверить разобранные параметры
Объект из строки arguments вставьте в JSON Schema. Schema — enum region сверху. global будет отмечен; не меняйте его на apac до выполнения.
- 3В конце сравните item двух провайдеров
Откройте JSON Diff. Слева item исходного провайдера, справа тот, что собираетесь повторить. Если type сменился с function_call на расширение с двоеточием или call_id не совпал, следующий запрос не отправляйте.
Вопросы
После входа Open Responses в AAIF тот же JSON можно сразу отдать Bedrock или другой реализации?
Нет. Переносимое ядро — message, function_call и function_call_output. Item с префиксом провайдера не считается повторяемым на другом сервере. Structured outputs Claude в Bedrock — другой слой: JSON Schema текста модели, не этот конверт item.
Почему arguments не хранят объектом?
В примерах спеки arguments хранится JSON-строкой. Поток дописывает её через function_call_arguments.delta, и до completed строка может быть неполной. Не разбирайте раньше времени и не выполняйте.
Если status равен incomplete, можно ли всё же выполнить инструмент?
Нет. incomplete терминален. Спека требует, чтобы это был последний item ответа и сам response был incomplete. Обрезанный arguments обычно не проходит schema; даже если случайно пройдёт, не считайте его законченным вызовом.
Если allowed_tools уже ограничивает имена инструментов, нужна ли локальная schema?
Да. allowed_tools ограничивает, какие имена могут реально выполниться в этом запросе; определения могут целиком оставаться в tools. Он не проверяет, есть ли region внутри arguments в enum. Разрешённое имя всё равно требует parse и schema.
Можно ли класть tool_calls из Chat Completions в один массив с item Open Responses?
Нет. Open Responses использует type, call_id и arguments-строку. Chat Completions использует массив tool_calls. В одном журнале повтор отправит неверный конверт следующему провайдеру. Сравните два документа отдельно через Diff.
Итог
После входа Open Responses в AAIF провайдеры делят форму item, а не каждую строку журнала.
Решение: двоеточие в type значит, что это не переносимый function_call; сначала подтвердите, что arguments — строка, затем schema инструмента; если status не completed, function_call_output не пишите.
Дальше: форматированием проверьте, что arguments — строка, Schema проверит разобранные параметры, Diff сравнит два item.