Assistants API вимикають 26 серпня: міграція на Foundry Agent Service

Сьогодні прилетів лист від Microsoft, і це той тип листа, який не варто відкладати «на потім»: 26 серпня 2026 року Azure OpenAI Assistants API перестає працювати. Не «депрекейтиться», не «не рекомендується» — саме перестає працювати. У листі є трекінговий ID LZ_6-Y1Z, за яким те саме повідомлення можна знайти в Azure Portal у розділі Service Health → Health advisories.

Від дати листа до вимкнення — трохи більше тижня. Тому давайте по суті: кого це зачепить, як за дві хвилини зрозуміти, чи ви серед них, і що конкретно міняти в коді.

Що відбувається

Assistants API був спробою OpenAI (а слідом і Azure) дати готову абстракцію для агентів: ви створюєте асистента, до нього тред, у тред кидаєте повідомлення, запускаєте run і чекаєте, поки він допрацює. Ідея хороша, реалізація — надто своєрідна. Полінг статусу run у циклі, окрема сутність під кожен крок, дивна взаємодія з інструментами.

Microsoft вирішила не тягнути цю гілку далі й консолідувати все навколо Foundry Agent Service, який побудований на Responses API. Формулювання з листа доволі відверте: мета — «прибрати фрагментацію і поверхні, що дублюються». Тобто в Azure одночасно існувало кілька схожих способів зробити те саме, і зараз залишається один.

Якщо перекласти на мову структур даних, зміна виглядає так:

БулоСталоЩо змінилося по суті
AssistantsAgentsВерсіонуються, працюють з будь-якою моделлю Foundry
ThreadsConversationsЦе вже не просто список повідомлень, а потік різнотипних елементів
RunsResponsesНемає полінгу: дали вхід — одразу отримали вихід

Найприємніше тут — зникнення циклу очікування статусу run. Цей цикл писала кожна команда, і кожна писала його трохи по-різному й трохи неправильно: хтось забував про requires_action і мовчки виходив на першому ж виклику інструмента, хтось не обробляв expired.

Як перевірити, чи це про вас

Найшвидший спосіб — подивитися, на які URL ходить ваш код.

Вас це зачіпає, якщо в коді або логах трапляються шляхи виду:

https://<resource>.openai.azure.com/openai/assistants/...
https://<resource>.openai.azure.com/openai/threads/...
https://<resource>.openai.azure.com/openai/runs/...

Вас це не зачіпає, якщо ендпоінт виглядає так:

https://<resource>.services.ai.azure.com/api/projects/<project>

Другий орієнтир — SDK. Якщо в коді фігурують client.beta.assistants, threads, runs — ви на Assistants API. Якщо AIProjectClient з пакета azure-ai-projects — швидше за все, ні.

Третій — портал. Агенти, створені через вкладку Agents у Foundry, працюють на новому сервісі. Явне керування асистентами в класичному порталі — на старому.

Пастка з «класичними агентами»

У листі є фраза, яка заспокоює: класичні агенти, схожі на Assistants API, під це вимкнення не потрапляють і продовжать працювати. Формально це правда — у Foundry Agent Service (classic) своя дата виведення, 31 березня 2027 року.

Але покладатися на цю дату я б не став, і це не моя параноя — це прямо сказано в обговореннях від інженерів Microsoft: якщо ваше навантаження спирається на семантику assistant/thread/run або на інфраструктуру, успадковану від Assistants API, воно цілком може відвалитися раніше за березень 2027. Це не дві незалежні колії, а одна, у якої просто по-різному підписані кінці.

Практичний висновок: якщо ви на класичних агентах, у вас не «ще півтора року», а «ще півтора року, якщо пощастить». Планувати варто зараз.

Два шляхи міграції

Перш ніж лізти в код, варто чесно відповісти на одне питання: чи потрібна вам взагалі агентність?

Якщо ваш «асистент» насправді просто відповідає на питання по документах, тримає контекст діалогу й іноді викликає пару функцій — вам не потрібен агентний сервіс. Вам потрібен Responses API, і міграція буде набагато простішою.

Якщо ж у вас справді агент — з інструментами, станом, багатокроковими сценаріями, а можливо, й кількома агентами, які між собою взаємодіють — тоді Foundry Agent Service.

Варіант простіший: Responses API

Було — Assistants з тредом і полінгом:

import time

assistant = client.beta.assistants.create(
    model="gpt-5.1",
    instructions="Ти ввічливо допомагаєш із математикою.",
)
thread = client.beta.threads.create()
client.beta.threads.messages.create(
    thread_id=thread.id, role="user", content="Скільки буде 2+2?"
)
run = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=assistant.id)
while run.status not in ("completed", "cancelled", "expired", "failed"):
    time.sleep(1)
    run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)

Стало — один виклик:

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),
    base_url="https://<resource>.openai.azure.com/openai/v1/",
)

response = client.responses.create(
    model="gpt-5.1",
    input="Скільки буде 2+2?",
)

Багатокроковий діалог теж не потребує тредів — достатньо послатися на попередню відповідь:

second = client.responses.create(
    model="gpt-5.1",
    previous_response_id=response.id,
    input="А поясни це першокласнику.",
)

Три деталі, які варто помітити в цьому коді.

Перша — /openai/v1/ в кінці base URL. Це не косметика: без v1 частина можливостей недоступна. І клієнт тут саме OpenAI, а не AzureOpenAI — Microsoft тепер документує для Responses API звичайний OpenAI-клієнт з підміненим base URL.

Друга — ключ. api_key досі підтримується, але рекомендований шлях — Entra ID. Виглядає він трохи контрінтуїтивно, бо токен передається в те саме поле api_key:

from azure.identity import DefaultAzureCredential, get_bearer_token_provider

token_provider = get_bearer_token_provider(
    DefaultAzureCredential(), "https://ai.azure.com/.default"
)
client = OpenAI(
    base_url="https://<resource>.openai.azure.com/openai/v1/",
    api_key=token_provider(),
)

Третя — модель. Я навмисно пишу gpt-5.1, а не gpt-4.1, який досі трапляється в прикладах документації. gpt-4.1 уже позначено як deprecated з датою виведення 14 квітня 2027 — мігрувати з одного застарілого API на іншу застарілу модель було б дивно. І памʼятайте, що в Azure цей рядок — назва вашого деплойменту, а не назва моделі як такої.

Варіант складніший: Foundry Agent Service

Тут є одна неочевидна річ, на якій легко застрягти на початку: клієнтів два. Проєктний клієнт відповідає за самих агентів та їхні версії, а OpenAI-клієнт, отриманий з нього ж, — за розмови й відповіді.

pip install "azure-ai-projects>=2.4.0"
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient

project = AIProjectClient(
    endpoint=PROJECT_ENDPOINT,
    credential=DefaultAzureCredential(),
)
openai = project.get_openai_client()

У міграційному гайді стоїть >=2.3.0, але актуальна версія на PyPI — 2.4.0, і ставити краще одразу її.

Якщо спробувати робити розмови через project, отримаєте AttributeError: 'AIProjectClient' has no attribute 'conversations' — це найпопулярніша помилка при міграції, і вона навіть винесена окремим рядком у таблицю траблшутингу в документації. У AIProjectClient просто немає атрибута conversations: там живуть agents, connections, datasets, deployments і подібне, а розмови — вже на OpenAI-клієнті.

Ще одна деталь: get_openai_client() повертає звичайний OpenAI, а не AzureOpenAI, і не приймає api_version — у v1-поверхні такого поняття вже немає.

Асистент → агент

Було:

assistant = client.beta.assistants.create(
    model="gpt-5.1",
    name="my-assistant",
    instructions="Ти ввічливо допомагаєш із математикою.",
    tools=[{"type": "code_interpreter"}],
)

Стало — замість словників зʼявилися типізовані обʼєкти, а замість «створити» — «створити версію»:

from azure.ai.projects.models import CodeInterpreterTool, PromptAgentDefinition

agent = project.agents.create_version(
    agent_name="my-agent",
    definition=PromptAgentDefinition(
        model="gpt-5.1",
        instructions="Ти ввічливо допомагаєш із математикою.",
        tools=[CodeInterpreterTool()],
    ),
)

create_version замість create_agent — не просто перейменування. Метод create_agent() видалили ще у 2.0.0, а агент тепер має історію версій, і це те, чого в Assistants API бракувало найбільше: можливість змінити промпт і відкотитися, якщо стало гірше.

Чесна ремарка: у всіх задокументованих прикладах агент викликається за іменем, і як саме прив’язатися до конкретної версії при виклику — з документації незрозуміло. Версіонування є на рівні створення, але керованого «виклич версію 3» я не знайшов.

Тред → розмова

Було:

thread = project_client.agents.threads.create(
    messages=[{"role": "user", "content": "Розкажи смішну історію"}],
)

Стало:

conversation = openai.conversations.create(
    items=[{
        "type": "message",
        "role": "user",
        "content": "Розкажи смішну історію",
    }],
)

Ключова відмінність — поле type. У треді все було повідомленнями, у розмові елементи бувають різні: повідомлення, виклик інструмента, результат виклику. Саме тому це вже не messages, а items.

Run → response

Ось де зникає той самий цикл. Було:

run = project_client.agents.runs.create(thread_id=thread.id, agent_id=agent.id)
while run.status not in ("completed", "cancelled", "expired", "failed"):
    time.sleep(1)
    run = project_client.agents.runs.get(thread_id=thread.id, run_id=run.id)

Дрібниця, яка колись зʼїдала час: в OpenAI-клієнті параметр називався assistant_id, а в Azure-SDK — agent_id, і метод отримання run тут get, а не retrieve. Дві бібліотеки для одного й того самого API з різними іменами — теж одна з причин, чому Microsoft вирішила це все консолідувати.

Стало:

response = openai.responses.create(
    input="Намалюй графік прямої з нахилом 4 і перетином 9.",
    conversation=conversation.id,
    extra_body={
        "agent_reference": {"name": agent.name, "type": "agent_reference"},
    },
)

for item in response.output:
    if item.type == "message":
        for block in item.content:
            print(block.text)

Трохи дивує extra_body — агент передається не окремим параметром, а через додаткове тіло запиту. Це наслідок того, що Responses API лишається сумісним з OpenAI, а прив’язка до агента є специфічним для Azure розширенням.

Що з інструментами

Більшість переїжджає без змін: Azure AI Search, Code Interpreter, File Search, OpenAPI, звичайні функції, Bing Grounding. MCP навіть підвищили в статусі — був preview, став GA.

Три болючих моменти:

  • Azure Functions як інструмент більше немає. Якщо у вас на цьому зав’язана логіка, доведеться переносити її на MCP-сервер або на звичайні OpenAPI-інструменти. Це не механічна заміна, а переробка. Окрема підступність: клас AzureFunctionTool усе ще присутній у пакеті azure.ai.projects.models — але це спадок класичного API, і його наявність у dir() не означає, що воно працює.
  • Connected Agents замінили на A2A. Ідея та сама — агент викликає агента, — але API інший, і сам інструмент поки в public preview.
  • Deep Research теж прибрали. Замість нього пропонують модель Deep Research разом з інструментом веб-пошуку.

Натомість зʼявилося те, чого в класичному API не було взагалі: веб-пошук (одразу GA), генерація зображень (preview) і та сама agent-to-agent взаємодія. Тобто це не «перенесені» інструменти, а нові.

Про що варто знати заздалегідь

Дані не переїдуть. У Microsoft є інструмент міграції, і він справді допомагає — але переносить структуру коду, а не історичний стан. Ваші існуючі треди, повідомлення й історія розмов у новий сервіс не потраплять. Якщо історія діалогів має цінність для бізнесу — вивантажуйте її самі, і робіть це до 26 серпня, бо після цієї дати ви до неї вже не достукаєтеся тим самим API.

Перевірте всі підписки, а не тільки основну. Лист приходить на підписку, а не на людину. Цілком можлива ситуація, коли лист отримав колега з іншого відділу, а зачепить це ваш сервіс.

Порахуйте, скільки у вас насправді коду. Якщо асистент один і він простий — це робота на пів дня. Якщо у вас Assistants API розмазаний по кількох сервісах разом з логікою полінгу, ретраями й обробкою статусів — плануйте тиждень і не сподівайтеся встигнути до 26-го. У такому випадку чесніше тимчасово перевести критичний шлях на Responses API, а повноцінну міграцію на агентів зробити спокійно потім.

Що зробити цього тижня

  1. Знайти в коді всі згадки assistants, threads, runs і beta.assistants — це і є ваш обсяг робіт. До речі, самі ці методи з пакета openai нікуди не зникли й досі викликаються, але позначені як deprecated — тож увімкнений mypy чи pyright покаже їх вам сам.
  2. Перевірити Service Health в порталі за трекінговим ID LZ_6-Y1Z, щоб побачити повний список зачеплених підписок.
  3. Вирішити по кожному сценарію: це справді агент чи достатньо Responses API.
  4. Вивантажити історію тредів, якщо вона потрібна бізнесу.
  5. Мігрувати, починаючи з найпростішого сервісу — щоб набити руку на дешевому, а не на критичному.

І ще одне, вже не технічне. Ця історія — гарна ілюстрація того, чому варто тримати роботу з моделлю за власною тонкою абстракцією, а не розсипати виклики вендорського SDK по всьому коду. Ті команди, у яких доступ до LLM схований за одним інтерфейсом, зараз міняють один файл. Ті, у яких client.beta.threads зустрічається у двадцяти місцях, — читають цю статтю з відчутно гіршим настроєм.

Sources: Migrate to Foundry Agent Service, Foundry Agent Service overview, Azure OpenAI Responses API, Model retirement schedule, How to create Assistants (classic), azure-ai-agents for Python, [Will azure-ai-agents SDK continue working after the retirement?](https://learn.microsoft.com/en-us/answers/questions/5934023/will-azure-ai-agents-sdk-(foundry-agent-service-cl), How do I check if my agent uses the Assistants API, Azure Service Health advisories