Вызов функций: Responses API и Chat Completions

Сравнение на уровне протокола: определения функций, идентификаторы вызовов, результаты, потоковая передача аргументов, авторизация, идемпотентность и маршруты.

Вызов функций: Responses API и Chat Completions

Responses и Chat Completions могут попросить приложение выполнить функцию, но представляют цикл вызова инструментов по-разному. Chat Completions определяет функции в tools[].function, возвращает message.tool_calls и принимает результаты как сообщения role: "tool". Responses использует плоские определения, типизированные элементы вывода function_call и function_call_output, связанные через call_id.

Модель не выполняет функцию. Код приложения должен проверить аргументы, авторизовать операцию, безопасно выполнить её, вернуть результат и предотвратить повторные побочные эффекты при повторной попытке.

Четыре шага цикла инструментов

1. Объявить разрешённую функцию и схему её аргументов
2. Получить один или несколько предложенных моделью вызовов функции
3. Проверить, авторизовать и выполнить каждый вызов в приложении
4. Вернуть каждый результат с идентификатором корреляции вызова

Только после четвёртого шага модель может сформировать ответ на основе результата. Следующий инструмент запускает новую итерацию с новым идентификатором вызова. Руководство OpenAI по Function Calling также описывает многошаговый обмен.

Сравнить транспортные контракты

Аспект Responses API Chat Completions
Определение Плоский элемент tools[] Поля в tools[].function
Предложенный вызов Элемент вывода function_call tool_calls[] в сообщении ассистента
Корреляция call_id id, результат как tool_call_id
Имя function_call.name tool_calls[].function.name
Аргументы JSON-строка function_call.arguments JSON-строка tool_calls[].function.arguments
Результат function_call_output Сообщение role: "tool"
Потоковая передача Типизированные события аргументов Фрагменты delta.tool_calls[]
Итоговый текст Элементы вывода / метод SDK choices[0].message.content

Нельзя связывать вызовы по позиции в массиве. Параллельные вызовы и фрагменты могут завершиться в другом порядке. Явный идентификатор вызова — стабильный ключ связи.

Определить строгую схему функции

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

strict: true требует от модели соблюдать схему, но приложение всё равно разбирает и проверяет аргументы. Поддержка pattern различается, поэтому применяется та же проверка совместимости, что и для Structured Outputs.

Реализовать цикл через Responses API

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");

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

const tools = [
  {
    type: "function",
    name: "get_delivery_status",
    description: "Возвращает текущий статус доставки заказа.",
    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(`Инструмент не разрешён: ${name}`);
  }

  const args = JSON.parse(rawArguments);
  if (!/^ORDER-[0-9]{4}$/.test(args.order_id)) {
    throw new Error("Некорректный order_id");
  }

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

const first = await client.responses.create({
  model,
  input: "Где находится заказ 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 и call_id результата должны совпадать. В варианте без сохранения состояния предыдущие элементы вывода возвращаются вместе с результатами. Нельзя предполагать, что любой маршрут, совместимый с OpenAI, хранит состояние ответа.

Реализовать цикл через Chat Completions

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");

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

const tools = [
  {
    type: "function",
    function: {
      name: "get_delivery_status",
      description: "Возвращает текущий статус доставки заказа.",
      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(`Инструмент не разрешён: ${name}`);
  }

  const args = JSON.parse(rawArguments);
  if (!/^ORDER-[0-9]{4}$/.test(args.order_id)) {
    throw new Error("Некорректный order_id");
  }

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

const messages = [
  { role: "user", content: "Где находится заказ 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);

Сообщение ассистента с вызовами добавляется в messages до результатов. Его отсутствие или неправильный tool_call_id создаёт некорректную историю.

Считать потоковые аргументы фрагментами

{"order — неполный, а не невалидный JSON. Для каждого идентификатора вызова нужен отдельный буфер: добавляйте приращения, дождитесь завершения аргументов или ответа, один раз разберите полную строку и выполняйте функцию только после валидации. Responses использует типизированные события, Chat Completions — choices[].delta.tool_calls[]. Нельзя запускать функцию на первом фрагменте. См. руководство по потоковым ответам AI API.

Сделать выполнение безопасным и идемпотентным

  • разрешать только зарегистрированные имена функций;
  • ограничивать размер и проверять полную схему;
  • авторизовать пользователя или рабочую нагрузку для доступа к ресурсу;
  • разделять инструменты только для чтения и инструменты с побочными эффектами;
  • удалять секреты из результата;
  • задавать сроки и ограничивать повторные обращения к зависимым сервисам;
  • сохранять безопасную ссылку на операцию.

Для побочного эффекта создаётся ключ идемпотентности из стабильного запроса приложения и идентификатора вызова. Результат сохраняется до ответа модели. Повторная попытка возвращает сохранённый результат вместо второго возврата средств, сообщения, развёртывания или изменения базы данных.

Имя функции и аргументы модели — недоверенные входные данные. Валидная схема не выдаёт прав и не заменяет транзакцию.

Определить правила для нескольких вызовов

parallel_tool_calls: false упрощает автомат состояний. При параллельном режиме связывайте результаты по идентификатору вызова, ограничивайте число вызовов и параллелизм, определяйте влияние ошибки, возвращайте результат для каждого принятого вызова и сериализуйте зависимые побочные эффекты. Параллельность ускоряет независимое чтение, но усложняет авторизацию, повторные попытки и обработку частичных сбоев.

Понимать границу совместимости Modelflare

Modelflare сохраняет строгие определения функций и преобразует поддерживаемые формы между Responses и Chat Completions на допустимых маршрутах OpenAI/Codex. Это преобразование намеренно охватывает меньше, чем полный набор инструментов Responses.

Функции, определённые приложением, представлены в обоих форматах. Размещённые у поставщика инструменты — поиск, выполнение кода и другие — не эквивалентны и не должны считаться автоматически конвертируемыми. Для других семейств, совместимых с OpenAI, Chat Completions передаётся без преобразования до отдельной проверки; контракт поставщика определяет поддержку tools, strict, параллельных вызовов, потоковых аргументов и tool_choice.

Тест Необходимая информация
Вызов только для чтения Имя, аргументы, идентификатор вызова, связь результата, итоговый текст
Невалидные аргументы Явная ошибка без выполнения
Неизвестная функция Отказ по списку разрешённых функций
Инструмент не нужен Обычный текст без выдуманного вызова
Потоковая передача Полные аргументы и корректный идентификатор
Повторный запрос Один побочный эффект или сохранённый результат
Несколько вызовов Связь независимо от порядка
Резервный маршрут Те же модель, протокол, схема и контракт

Надёжная маршрутизация AI API помогает сделать смену маршрута явной, а безопасность ключей AI API — разделить рабочие нагрузки и права.

Выбрать формат по контракту приложения

Responses подходит для типизированных элементов вывода, модели событий Responses, продолжения с сохранением состояния и других проверенных функций. Chat Completions подходит при стабильной истории сообщений и проверенном контракте инструментов поставщика. См. Responses API и Chat Completions.

Стандарт одинаков: явный список разрешённых инструментов, строгая схема аргументов, валидация в приложении, авторизация до выполнения, связь по идентификатору вызова, идемпотентность побочных эффектов и полный результат для модели. Надёжность дают эти меры контроля, а не название конечной точки.