Розуміння елементів та повідомлень у API відповідей OpenAI
Пояснює, як API Responses від OpenAI реорганізовує результати роботи моделі у елементи замість повідомлень, та чому ця зміна має значення для виклику інструментів та агентських робочих процесів.
Доповнення до чату: створені навколо повідомлень
Формат Доповнення до чату вже давно є стандартом для спілкування з ШІ. Типовий процес виглядає так:
response = client.chat.completions.create(
model="...",
messages=[
{
"role": "user",
"content": "Explain RAG"
}
]
)
Ви надсилаєте список повідомлень, і модель повертає наступне повідомлення асистента. Усе досить просто. Натомість API Responses відрізняється вже з самого початку.
response = client.responses.create(
model="...",
input="Explain RAG"
)
На перший погляд це може здатися просто іншим кінцевим пунктом, де input замінює messages. Але справжня різниця полягає у абстракції, яку використовує кожен API. Формат Доповнення до чату орієнтований на повідомлення, тоді як API Responses організований навколо елементів та відповідей. Коли до гри долучаються інструменти та агенти, ця різниця починає мати значно більше значення.
Розмова в режимі Chat Completions — це просто масив об’єктів повідомлень.
messages = [
{
"role": "system",
"content": "Act as a helpful AI assistant."
},
{
"role": "user",
"content": "What is a vector database?"
}
]
Кожне повідомлення містить роль та певний вміст.
Типові ролі, які ви побачите, є такими:
systemuserassistant
Ви можете уявити цей процес так:
Messages (list)-> Model -> Assistant Message
Модель отримує наявну розмову та генерує наступне повідомлення асистента. Мінімальний цикл розмови виглядає так:
# Human message appended to the messages list
messages.append({
"role": "user",
"content": user_input
})
response = client.chat.completions.create(
model="...",
messages=messages
)
# AI message appended to the messages list
messages.append(
response.choices[0].message
)
Ваше додатку необхідно стежити за історією повідомлень — чи то в пам’яті, у базі даних, чи будь-яким іншим способом на ваш вибір. Кожне нове повідомлення користувача додається до цього списку, повний список надсилається моделі, а відповідь моделі знову додається у список. Ця схема природним чином відповідає принципам роботи інтерфейсів чату.
User Message (str)->
Message History (list[dict])->
Model (llm)->
Assistant Message (str)->
Message History (list[dict])
Для генерації звичайного тексту та типових сценаріїв використання у стилі чату ця конфігурація є абсолютно достатньою. Однак її обмеження починають проявлятися, коли додаток на основі LLM потребує робити більше, ніж просто вести діалог.
Додатки на основі LLM — це не лише додатки для чату
Розгляньмо такий запит:
Дізнайтеся про останні досягнення в галузі ШІ, підготуйте узагальнення найважливіших з них та надішліть це узагальнення мені на пошту.
Щоб виконати цей запит, моделі потрібно ініціювати пошук в Інтернеті та підключитися до сервісу електронної пошти.
Раптово процес виконання вимагає більшого, ніж просто обмін повідомленнями користувача та асистента.
User Request ->
Model ->
Web Search ->
Search Results ->
Summarize (Model)->
Send Mail ->
Final Response
Більш складні додатки можуть використовувати кілька різних інструментів, поєднаних між собою.
На цьому етапі модель є активним учасником більш широкого процесу виконання, а не просто генератором тексту. Частина її результатів ніколи не призначена для кінцевого користувача — вони існують виключно для внутрішньої обробки. Модель може викликати певний інструмент, а результат цього виклику може потребувати подальшої обробки, що може призвести до ще одного виклику інструменту. Лише після того, як усе це буде вирішено, модель формує остаточну відповідь, причому навіть ця відповідь може не мати форми текстового повідомлення.
Цей процес вже не можна звести до:
Messages (list)-> Model -> Assistant Message
Тепер існують проміжні результати та дії на рівні додатку, які потрібно зберігати як частину контексту, і саме тут абстракція, побудована виключно навколо повідомлень, починає здаватися занадто вузькою.
Кожен результат моделі ≠ повідомлення
Як тільки модель підключена до інструментів, більша частина того, що вона генерує, — це виклик функції, а не текст для розмови.
Візьмемо це як приклад:
Function Call
Name: get_weather
Arguments:
{
"city": "Bengaluru"
}
Це не те, що призначено для читання користувачем — це інструкція, адресована вашому додатку. Ваш код виконує відповідну функцію та передає результат назад до моделі, причому цей результат може бути або не бути призначеним для користувача.
На практиці модель може генерувати принаймні два різних типи вихідних даних під час роботи:
- Виклик функції
- Повідомлення
Об’єднання цих двох типів під єдиною назвою „повідомлення асистента“ не відображає того, що насправді відбувається під час виконання. Ця невідповідність є основною проблемою проектування, яку прагне вирішити API Responses.
API Responses: інша абстракція
Замість того, щоб бути орієнтованим на обмін повідомленнями, API Responses побудований навколо концепції відповіді, яка може об’єднувати кілька елементів вихідних даних.
response = client.responses.create(
model="...",
input="Explain LangGraph"
)
print(response.output)
# response.output is a list of output items.
Для запиту у вигляді звичайного тексту цей вихід може бути лише одним повідомленням. Але для будь-чого, що пов’язано з агентами чи інструментами, відповідь може містити кілька різних типів елементів. Спрощений вигляд цієї структури виглядає так:
Response
| Reasoning Item
| Function Call Item
| Message Item
У цій моделі повідомлення стає лише одним із кількох можливих типів вихідних даних, а не всім тим, що представляє відповідь.
Різниця:
Різниця:
Messages (list)-> Model -> Assistant Message
Доповнення до чату
Input -> Model -> Response
Response:
| Output Item
| Output Item
| Output Item
API Responses
У функції Chat Completions моделюється саме діалог, тоді як API Responses представляє виконання моделі у вигляді відповіді, складеної з елементів виводу. Для простого доповнення тексту ця різниця майже не має значення. Вона стає важливою, коли у гру вступають інструменти, моделі з здатністю до міркувань чи агентські робочі процеси.
Повідомлення проти елементів
Справжня різниця між цими двома API проявляється у тому, як кожен з них структурує свою відповідь.
У функції Chat Completions усе зосереджується навколо повідомлення.
print(response.choices[0].message.content)
# The generated text is inside the assistant message.
# response
# | choices
# | message
# | content
API Responses організовує свій вивід по-іншому.
print(response.output)
# A simplified structure:
# response
# | output
# | reasoning
# | function_call
# | message
# The important difference is that output is not a list of messages,
# but output items.
Повідомлення — це лише один тип елемента; виклик функції — інший тип; а моделі, які підтримують міркування, також можуть генерувати елементи міркувань. Це змінює спосіб розуміння того, що насправді повертає модель.
Chat Completions
Model Output = Assistant Message
Responses API
Model Output = List of Output Items
- The structure which is useful for tool calling.
Виклики інструментів як елементи виводу
Візьмемо просту функцію, яка повідомляє про погоду (класичний приклад, який використовується скрізь).
def get_weather(city: str):
return f"The weather in {city} is 28°C"
# The weather is ofcoure hardcoded.
Ви можете зробити цю функцію доступною для моделі як визначення інструменту.
tools = [
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string"
}
},
"required": ["city"]
}
}
]
Це визначення інструменту додається до вашого запиту.
response = client.responses.create(
model="...",
tools=tools,
input="What is the weather in Bengaluru?"
)
З цього моменту у моделі є два можливих шляхи подальших дій.
Option 1: Generate a message
Option 2: Call get_weather
Оскільки фактичне значення погоди не вбудоване у визначення функції, моделі потрібні актуальні дані, тому вона надсилає елемент виклику функції замість прямої відповіді.
Function Call Item
name: get_weather
arguments:
{
"city": "Bengaluru"
}
Ви можете знайти цей виклик функції, пройшовшись по елементах вихідних даних відповіді.
for item in response.output:
if item.type == "function_call":
print(item.name)
print(item.arguments)
Його виконання дає вам:
get_weather
{"city":"Bengaluru"}
На цьому етапі модель ще не дала остаточної відповіді — вона лише попросила виконати певну дію; саме ваше застосунок виконує функцію.
result = get_weather("Bengaluru")
Цей результат потім потрібно повернути до моделі.
Результат виклику функції
Результат роботи інструменту представляється за допомогою елемента типу function_call_output.
tool_output = {
"type": "function_call_output",
"call_id": item.call_id,
"output": result
}
Поле call_id пов’язує цей результат із конкретним викликом функції, який його звернувся отримати.
Function Call
| call_id: call_123
Application Executes Tool
Function Call Output
| call_id: call_123
Це зіставлення стає необхідним, коли одночасно викликається кілька інструментів, наприклад запит на отримання інформації про погоду у двох різних містах.
Основний цикл виконання інструментів
Додатки, побудовані навколо інструментів, зазвичай дотримуються циклу повторень.
User Input ->
Model ->
Response Output Items ->
Check for Function Calls ->
Execute Functions ->
Create Function Call Outputs ->
Model ->
Final Response
Ось приблизно як це виглядає у коді.
response = client.responses.create(
model="...",
input=user_input,
tools=tools
)
while True:
function_calls = [
item
for item in response.output
if item.type == "function_call"
]
if not function_calls:
break
tool_outputs = []
for call in function_calls:
result = execute_tool(
call.name,
call.arguments
)
tool_outputs.append({
"type": "function_call_output",
"call_id": call.call_id,
"output": result
})
response = client.responses.create(
model="...",
previous_response_id=response.id,
input=tool_outputs,
tools=tools
)
Цей цикл продовжує повторюватися, поки модель продовжує повертати результати викликів функцій. Як тільки вона припиняє звертатися до інструментів, у відповіді міститься кінцевий результат роботи моделі.
Model ->
Function Call ->
Tool Result ->
Model ->
Function Call ->
Tool Result ->
Model ->
Message
Цей цикл є основою для більшості реалізацій агентів, які використовують інструменти.
Висновок
API Responses — це не просто інтерфейс під новою назвою замість Chat Completions. Він пропонує структуру, яка точніше відображає спосіб роботи сучасних додатків на основі LLM, коли використовуються інструменти, логічні міркування та кілька типів вихідних даних. Chat Completions залишається гарним вибором для простих сценаріїв спілкування. Але як тільки ваш робочий процес починає набувати характеру агентського, API Responses є кращим варіантом. Повідомлення відповідають за спілкування; елементи — за виконання. Ось у чому суть.
Пов’язана література
- Розуміння AI-агентів: цілі, інструменти, пам’ять та цикл агента — просте пояснення для початківців про те, чим AI-агенти відрізняються від чат-ботів, з описом основних компонентів, циклу прийняття рішень, рівнів автономії та прикладів використання у реальному світі.
- Структурні механізми контролю для AI-агентів: як це працює у пайплайні ResolveFlow — пояснює, як агент на основі LangGraph забезпечує розділення процесів міркувань та виконання завдань за допомогою перевірок на рівні коду, а не інструкцій у запитах, включаючи помилку отримання даних, яка виникла під час роботи.