Галоўная / Артыкулы / Розумэнне элементаў і паведамленняў у API адпаведзяў OpenAI

Розумэнне элементаў і паведамленняў у API адпаведзяў OpenAI

Пасвячаецца таму, як API Responses адказоў OpenAI пераканструюе выходы модэля ў элементы заместо паведамленняў, і чаму гэта змяненне мае значэнне для вызывання інструментаў та рабочых праграм на адной стороне.

1735 слоў

Дапраўленне чату: створана абоў’язкова на адной з прыемакоў

Дапраўленне чату ўжо давно являецца стандартным форматам для спілкування з LLM-амі. Тыповы процес выглядае так:

response = client.chat.completions.create(
    model="...",
    messages=[
        {
            "role": "user",
            "content": "Explain RAG"
        }
    ]
)

Вы адправляеце спіс прыемакоў, і модель вяртае наступны прыемак ад асістента. Усё досыта проста. Апошней, API для адпаведзяў відрóżнаецца ад самага пачатку.

response = client.responses.create(
    model="...",
    input="Explain RAG"
)

На першы погляд, гэта можа здавацца проста іншым канцэнтрам, дзе input заменяе messages. Але справжняя разніца крыўціцца ў абстракцыі, якую выкарыстоўвае кожны API. Дапраўленне чату абоў’язкова базуецца на прыемакох, тады як API для адпаведзяў організавана ўакол элементаў і адпаведзяў. Калі ў гэты прыцэп увайшаюць інструменты і агенты, гэтае разлікаванне стае ўжо дужа значным.

Размова пад рэжымом Chat Completions — это простае масавец объектаў паведамленняў.

messages = [
    {
        "role": "system",
        "content": "Act as a helpful AI assistant."
    },
    {
        "role": "user",
        "content": "What is a vector database?"
    }
]

Кожнае паведамленне мае ролю і певны контэнт.

Тыповыя ролі, якія вы пабачыце, ўключаюць:

  • system
  • user
  • assistant

Вы можете уявіць сабе прайом так:

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 = не толькі прыкладнасці для чатавання

Возьмем такую запит:

Знайсці апошнія досягнення ў галузі AI, стварыць падсумак тых, якія маюць значэнне, і аправіць гэты падсумак у маю ящык для пісьма.

Ёжыце гэты запит значыць, што модель павінна запусканы пошук у інтэрнете і працаваць з сервісам электронной пошты.

Раптам процес выканання включае больш, чым простую абмянку паведамленняў між корыстнікам і асистэнтам.

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"
}

Гэта не тое, што павінна чытаць аб’ектар — гэта інструкцыя, надасвечаная вашай працоўнай програме. Ваш код запускае адпаведную функцыю і падае рэзультат назад модэлі, і гэты рэзультат, зноў жа, можа быць адкрываны для аб’ектара, а можа і не будзе.

У практыцы модэль можа ствараць пры ўрабоцеўкі як мінімум два разныя відэры выходных даных:

  1. Вызов функцыі
  2. Паведамленне

Аб’еднаванне гэтых двух пад адной азначэннем „паведамленне асістента“ не відбівае таго, што на самай працэ ўрабоцкі вадзіцца. Гэта несаходствае ёсць ключовай проблемай дызайну, яку прагне рашыць 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 ёсць кращым рашэнням. Паведамленні адпавядаюць за дыялог; элементы – за выконання задач. Гэта і є сутнасць.

Спаднёйшая літэратура