OpenAI Responses API после Assistants: паттерны миграции и архитектурное руководство
Assistants API прекращает работу 26 августа. Это руководство описывает конкретные паттерны Responses API для команд, которые уже мигрировали: управление состоянием диалога без серверных threads, оркестрация инструментов, streaming и кросс-провайдерный routing через OpenAI-совместимый gateway.
Assistants API прекращает работу 26 августа 2026 года. Если вы читаете этот текст, дедлайн либо завтра, либо уже прошёл. Чеклисты миграции и разбор полётов опубликованы в других статьях блога. Здесь речь пойдёт о другом: вы уже переехали с Assistants, и теперь нужно грамотно строить на Responses API.
Ниже — практическое архитектурное руководство: как работает состояние диалога без серверных Threads, как оркестрация инструментов заменяет Run-объекты, чем отличается streaming и какую роль играет кросс-провайдерный routing через OpenAI-совместимый gateway.
Что Responses API даёт по сравнению с Assistants
Assistants API управлял состоянием за вас: Threads хранили сообщения, Runs опрашивались на завершение, сервер оркестрировал вызовы инструментов по шагам. Удобно, но привязывает. Thread и Run объекты существовали только на серверах OpenAI. Их нельзя было воспроизвести через другого провайдера, кэшировать локально или проинспектировать полное состояние оркестрации в собственной инфраструктуре.
Responses API заменяет этот серверный жизненный цикл stateless (или опционально stateful) моделью запросов. Каждый вызов POST /v1/responses принимает массив input и возвращает массив output. Модель может вызвать несколько инструментов в рамках одного запроса. Состояние — у вас.
Ключевые изменения, влияющие на архитектурные решения:
- Нет серверных threads. Состояние диалога передаётся через
previous_response_id, управляется через Conversations API или воспроизводится вручную вinput. - Встроенный agentic loop. Модель может в одном запросе последовательно вызывать web search, file search, code interpreter, function calls и MCP, без polling.
- Типизированные элементы вывода. Вместо
choices[0].message— массивoutputс отдельными элементамиreasoning,message,function_call,web_search_call. - Лучшая утилизация кэша. OpenAI сообщает об улучшении cache hit rate на 40–80% по сравнению с Chat Completions при эквивалентных нагрузках.
Управление состоянием диалога без серверных Threads
Assistants хранили историю диалога в объектах Thread. Responses API предлагает три подхода к состоянию с разным балансом переносимости и сложности.
Вариант 1: previous_response_id (самый простой, только OpenAI)
Передайте id предыдущего ответа для связывания turns:
first = client.responses.create(
model="gpt-5.6",
input="Объясни теорему CAP.",
)
second = client.responses.create(
model="gpt-5.6",
input="Теперь приведи конкретный пример.",
previous_response_id=first.id,
)
Это ближайший аналог Assistants Threads. OpenAI хранит контекст на сервере и автоматически его воспроизводит. Компромисс: этот параметр специфичен для OpenAI. Responses API DeepSeek не поддерживает previous_response_id (это stateless API). Если нужна кросс-провайдерная переносимость, используйте варианты 2 или 3.
Вариант 2: Conversations API (новый, только OpenAI)
Conversations API обеспечивает persistent именованные диалоги. Полезно, когда несколько сессий должны ссылаться на одну историю. Как и previous_response_id, это только OpenAI.
Вариант 3: Ручное воспроизведение состояния (переносимый)
Добавляйте полный массив output каждого ответа к массиву input следующего запроса:
history = [{"role": "user", "content": "Объясни теорему CAP."}]
response = client.responses.create(
model="gpt-5.6",
input=history,
store=False,
)
# Воспроизводим все output-элементы, включая зашифрованные reasoning
history += response.output
history.append({"role": "user", "content": "Приведи пример."})
next_response = client.responses.create(
model="gpt-5.6",
input=history,
store=False,
)
Этот подход работает с любым провайдером, поддерживающим формат input Responses API. DeepSeek принимает ту же структуру input (message, function_call, function_call_output, reasoning, web_search_call). Цена — управление хранением и воспроизведением ложится на вас, а длинные диалоги увеличивают потребление token на каждый запрос.
Как выбрать: используйте ручное воспроизведение, если маршрутизируете через нескольких провайдеров или хотите полный контроль. Используйте previous_response_id, если работаете только с OpenAI и хотите минимум кода. Не смешивайте подходы в одном диалоге.
Оркестрация инструментов: от polling Run к agentic loop
Assistants API требовал опроса Run-объектов для проверки, хочет ли модель вызвать инструмент, затем отправки вывода инструмента и повторного опроса. Многоинструментный диалог мог потребовать четыре-пять round trip.
Responses API это устраняет. Если в запросе настроены tools, модель может вызвать несколько инструментов и включить их результаты в рамках одного API-вызова (для встроенных инструментов: web search, file search, code interpreter). Для пользовательских function calls вы всё ещё подаёте вывод вручную, но цикл запрос/ответ проще:
import json
response = client.responses.create(
model="gpt-5.6",
input="Какая сейчас погода в Токио и Нью-Йорке?",
tools=[{
"type": "function",
"name": "get_weather",
"description": "Получить текущую погоду для города",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
"additionalProperties": False,
},
"strict": True,
}],
)
tool_outputs = []
for item in response.output:
if item.type == "function_call":
result = get_weather(json.loads(item.arguments)["city"])
tool_outputs.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps(result),
})
final = client.responses.create(
model="gpt-5.6",
input=[
*response.output,
*tool_outputs,
],
tools=[{...}],
previous_response_id=response.id,
)
Встроенные инструменты, заменяющие функции Assistants
| Функция Assistants | Замена в Responses API | Примечания |
|---|---|---|
| Code Interpreter | {"type": "code_interpreter"} | Выполняет Python в sandbox, возвращает текст/изображения |
| File Search (Retrieval) | {"type": "file_search", "vector_store_ids": [...]} | Та же инфраструктура vector store |
| Function calling | {"type": "function", ...} | Формат запроса отличается от Chat Completions |
| Web browsing (beta) | {"type": "web_search"} | Серверное выполнение, цитаты в output |
Кросс-провайдерная поддержка инструментов
Responses API DeepSeek поддерживает типы инструментов function и web_search. Другие встроенные инструменты (file_search, code_interpreter, computer_use, mcp) игнорируются без ошибок. При мультипровайдерном routing для agentic-нагрузок необходимо учитывать доступность инструментов по каждому провайдеру.
OpenAI-совместимость означает, что провайдер предоставляет endpoint chat-completions, чей контракт запроса и ответа достаточно близок к API OpenAI, чтобы немодифицированный вызов OpenAI SDK работал после замены трёх значений: API key, base URL, название модели. Минимальная поверхность на практике —POST /v1/chat/completions с messages, model и потоковым ответом в форме OpenAI.
Streaming: от delta-чанков к семантическим событиям
Chat Completions streaming отправляет delta-объекты с инкрементальным контентом. Responses API использует семантические server-sent events (SSE). Каждое событие имеет поле type, описывающее происходящее:
| Тип события | Значение |
|---|---|
response.created | Запрос принят, начата генерация |
response.output_text.delta | Инкрементальный текст (аналог delta из Chat Completions) |
response.function_call_arguments.delta | Инкрементальный JSON аргументов function call |
response.reasoning_text.delta | Текст цепочки рассуждений (при включённом reasoning) |
response.output_item.added / .done | Элемент output (message, function_call и т.д.) начат/завершён |
response.completed | Финальное событие с полным объектом ответа и usage |
response.failed | Ошибка при генерации |
stream = client.responses.create(
model="gpt-5.6",
input="Подведи итоги последних трендов в AI-исследованиях.",
stream=True,
)
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="")
elif event.type == "response.completed":
print(f"\n\nTokens: {event.response.usage.total_tokens}")
Сообщения data: [DONE] нет. Поток завершается событием response.completed, response.incomplete или response.failed.
Responses API DeepSeek поддерживает ту же структуру SSE-событий, что обеспечивает консистентное поведение streaming при routing между OpenAI и DeepSeek через совместимый gateway.
Кросс-провайдерный routing
Формат input/output Responses API становится кросс-провайдерным стандартом, но степень поддержки различается:
| Провайдер | Поддержка Responses API | Основные ограничения |
|---|---|---|
| OpenAI | Полная | Эталонная реализация |
| DeepSeek | Частичная | Нет previous_response_id, store, background, Conversations API. Инструменты ограничены function и web_search. Неподдерживаемые параметры игнорируются без ошибок. |
| DashScope (Qwen) | Через Chat Completions | DashScope использует OpenAI-совместимый Chat Completions. Формат Responses API нативно не поддерживается, но модели Qwen доступны через routing Chat Completions. |
Архитектура routing
При маршрутизации трафика Responses API через gateway типа TheRouter:
- Stateless-запросы маршрутизируются без проблем. При использовании ручного воспроизведения состояния (вариант 3 выше) один и тот же запрос можно направить любому провайдеру, поддерживающему формат input Responses API.
- Stateful-параметры специфичны для провайдера.
previous_response_id,store: trueи Conversations API работают только когда запросы попадают в OpenAI. Router не может синтезировать серверное состояние для другого провайдера. - Доступность инструментов различается. Запросы с
web_searchилиfunctionможно направлять провайдерам, которые их поддерживают. Запросы сfile_searchилиcode_interpreterдолжны идти в OpenAI. - Fallback требует осторожности. Если OpenAI недоступен и fallback идёт на DeepSeek, приложение должно обрабатывать отсутствие встроенных инструментов, которые DeepSeek не поддерживает.
TheRouter маршрутизирует OpenAI-совместимые запросы через настроенных провайдеров. Для трафика Responses API это означает, что запросы с ручным воспроизведением состояния и инструментами типа function маршрутизируются к любому совместимому endpoint. Запросы, зависящие от специфичных для OpenAI функций (previous_response_id, встроенные инструменты помимо function и web_search), следует фиксировать на OpenAI.
Чеклист production-готовности
Перед выводом интеграции Responses API в production проверьте каждый пункт:
Обработка ошибок
- Обрабатывайте события
response.failedв streaming и проверяйте полеstatusв синхронных ответах - Реализуйте логику retry для ответов 429 (rate limit) и 500+ (серверные ошибки)
- Устанавливайте
max_output_tokensдля предотвращения неконтролируемых расходов на генерацию
Учёт token
- Читайте
usage.input_tokensиusage.output_tokensиз объекта ответа - Для reasoning-моделей
output_tokens_details.reasoning_tokensпоказывает потребление token на цепочку рассуждений - Cache hit token отображаются в
input_tokens_details.cached_tokens
Управление состоянием
- При ручном воспроизведении надёжно сохраняйте массивы
output(база данных, не только in-memory) - Зашифрованные reasoning-элементы нужно воспроизводить as-is для сохранения контекста reasoning-моделей
- Устанавливайте
store: false, если не хотите, чтобы OpenAI сохранял ответы
Поведение при fallback
- Протестируйте каждого провайдера с вашей реальной конфигурацией инструментов
- Логируйте и настройте оповещения, когда fallback-провайдер молча игнорирует неподдерживаемые инструменты
- Рассмотрите отдельные правила routing для agentic (с большим количеством инструментов) и простых генерационных запросов
Валидация миграции
- Убедитесь, что парсинг ответа корректно обрабатывает элементы
output_textиfunction_call - Подтвердите, что structured output работает с
text.formatвместоresponse_format - Запустите интеграционные тесты и с OpenAI, и хотя бы с одним альтернативным провайдером
FAQ
Responses API дороже Chat Completions?
Для эквивалентной генерации текста без встроенных инструментов цена за token одинакова. OpenAI сообщает о лучшей утилизации кэша в Responses, что может снизить фактические затраты. Встроенные инструменты вроде web search тарифицируются отдельно.
Можно ли использовать Responses API с reasoning-моделями?
Да. Начиная с GPT-5.4, reasoning-модели получили улучшенную работу с инструментами в Responses API. Chat Completions не поддерживает вызов инструментов с reasoning_effort, отличным от none, для GPT-5.4 и новее.
Что будет с существующими интеграциями Chat Completions?
Chat Completions остаётся поддерживаемым. Об отказе от него не объявлялось. Однако новые возможности (встроенные инструменты, Conversations API, background mode) доступны только в Responses.
Responses API DeepSeek работает идентично OpenAI?
Нет. DeepSeek поддерживает базовый формат запроса/ответа, function calling и web search, но не поддерживает stateful-функции (previous_response_id, store, Conversations API) и встроенные инструменты file_search, code_interpreter. Неподдерживаемые параметры игнорируются без ошибок.
Можно ли в одном приложении совмещать Chat Completions и Responses API?
Да, но не разделяйте между ними состояние. Chat Completions использует messages/choices, Responses — input/output. Это два отдельных endpoint с разной структурой объектов.
Дополнительные материалы
- OpenAI: Migrate to the Responses API — официальное руководство по миграции с примерами кода
- DeepSeek: Using the Responses API — детали совместимости и ограничения DeepSeek
- OpenAI: Conversation State — три подхода к управлению состоянием диалога
- Assistants to Responses API Migration Routing Guide — пошаговый чеклист миграции
- Assistants API Shutdown Postmortem — архитектурные уроки из deprecation
- Assistants API Sunset: Final Migration Checklist — шаги миграции в последний момент
- Assistants API Alternatives Comparison — Responses vs. Chat Completions vs. сторонние альтернативы