Структурированный вывод с использованием LLM: режим JSON, вызов функций и когда использовать каждый из них.
Как получить достоверные и понятные ответы в рамках вашей магистерской программы и как выбрать подходящий инструмент.
Делиться
В моих последних постах мы много говорили о популярных методах оптимизации производительности и стоимости приложений ИИ, таких как потоковая передача ответов или кэширование подсказок. Сегодня я хочу поговорить о чем-то немного другом, но не менее важном для создания реальных приложений ИИ. А именно, о структурированных, машиночитаемых выходных данных .
В большинстве приведенных мною примеров мы имели дело с ответами в свободной текстовой форме от модели ИИ. Пользователь задает вопрос, модель отвечает на естественном языке, и мы просто отображаем этот ответ пользователю каким-либо образом. Довольно просто и понятно. Но что происходит, когда нам нужно, чтобы модель возвращала данные в определенном формате (например, в виде объекта JSON), чтобы мы могли программно обработать их позже? Что если нам нужно, чтобы модель извлекала определенные поля из текста или изображения, заполняла запись в базе данных или запускала последующее действие на основе своего ответа? В таких случаях получение в ответ сплошного текста будет не очень удобно. 🤔
К счастью, существует несколько решений этой проблемы. Для получения структурированных машиночитаемых результатов из LLM-системы есть два основных подхода: режим JSON и вызов функций (также называемый использованием инструментов). Эти два подхода часто путают друг с другом (что неудивительно, поскольку оба работают со структурированными результатами), но они служат совершенно разным целям. Кроме того, OpenAI представила более строгий вариант вызова функций, называемый структурированными результатами , который, как мы увидим, еще больше усиливает контроль за схемой. В этой статье мы более подробно рассмотрим все три подхода, разберемся, как каждый из них работает «под капотом», и выясним, когда следует использовать каждый из них.
Итак, давайте посмотрим!
1. Что такое режим JSON?
Режим JSON — это более простой подход для получения машиночитаемых результатов от LLM. По сути, это параметр, который можно установить в API-запросе, чтобы указать модели всегда возвращать допустимый JSON-объект. И это, собственно, всё! Тем не менее, эта простота имеет свою цену, поскольку нет никаких гарантий относительно структуры или схемы JSON (помните, мы не определяли никаких схем, имен полей, типов и тому подобного), только то, что это будет допустимый, разборчивый JSON.
Например, используя API OpenAI в Python, мы можем включить режим JSON, добавив параметр response_format={"type": "json_object"} к вызову модели. Более конкретно, это будет выглядеть примерно так:
from openai import OpenAI client = OpenAI(api_key="your_api_key") response = client.chat.completions.create( model="gpt-4o-mini", response_format={"type": "json_object"}, messages=[ { "role": "system", "content": "You are a helpful assistant. Always respond in JSON format." }, { "role": "user", "content": "Extract the name, age, and city from this text: 'Maria is 32 years old and lives in Athens.'" } ] ) print(response.choices[0].message.content)
И ответ будет выглядеть примерно так:
{ "name": "Maria", "age": 32, "city": "Athens" }
И вуаля! ✨ Всего лишь одно простое изменение параметра — и мы каждый раз получаем корректный JSON. Нет необходимости в разборе строк или странных ухищрениях с регулярными выражениями.
Однако есть один нюанс. Режим JSON гарантирует, что выходные данные будут корректными в формате JSON, но он не гарантирует конкретную структуру. Если мы запустим один и тот же пример несколько раз, мы можем каждый раз получать немного разные имена полей или немного разную структуру. Например, один запуск может вернуть "name" , а другой — "full_name" . Это проблема, если мы пытаемся надежно извлечь определенные поля программным способом.
Кроме установки response_format={"type": "json_object"} , рекомендуется всегда явно указывать модели, что она должна отвечать в формате JSON, в системном запросе. В приведенном выше примере обратите внимание, как мы добавили в системный запрос также опцию «Всегда отвечать в формате JSON». Без этого модель может иногда возвращать корректный JSON, но не всегда, поскольку ее поведение может стать непредсказуемым.
2. Что такое вызов функции?
Вызов функций (или использование инструментов) — это более продвинутый подход к получению структурированных, машиночитаемых выходных данных от LLM. Вместо того чтобы просто просить модель отформатировать ответ в формате JSON, мы определяем конкретную схему . То есть, мы явно определяем формальное описание структуры, которой должен соответствовать вывод, и таким образом модель более ограничена в возврате данных, точно соответствующих этой схеме. Другими словами, при вызове функций мы заранее определяем, какие поля мы ожидаем, какого типа должны быть эти поля, какие из них обязательны, а какие нет, и так далее.
Вот как бы выглядел тот же пример извлечения данных с использованием вызова функции:
from openai import OpenAI import json client = OpenAI(api_key="your_api_key") # define the schema of the output we expect tools = [ { "type": "function", "function": { "name": "extract_person_info", "description": "Extract personal information from a text", "parameters": { "type": "object", "properties": { "name": { "type": "string", "description": "The full name of the person" }, "age": { "type": "integer", "description": "The age of the person" }, "city": { "type": "string", "description": "The city the person lives in" } }, "required": ["name", "age", "city"] } } } ] response = client.chat.completions.create( model="gpt-4o-mini", tools=tools, tool_choice={"type": "function", "function": {"name": "extract_person_info"}}, messages=[ { "role": "user", "content": "Extract the name, age, and city from this text: 'Maria is 32 years old and lives in Athens.'" } ] ) # parse the structured output tool_call = response.choices[0].message.tool_calls[0] result = json.loads(tool_call.function.arguments) print(result)
В результате получится примерно следующее:
{ "name": "Maria", "age": 32, "city": "Athens" }
Результат выполнения этого примера с вызовом функции идентичен результату, полученному в режиме JSON. Тем не менее, ключевое отличие заключается в том, что, в отличие от режима JSON, при вызове функции результат будет согласованным; он всегда будет точно соответствовать определенной схеме, с согласованными именами полей, типами и любыми другими атрибутами, которые мы в ней определим.
🍨 DataCream — это информационная рассылка, предлагающая статьи и обучающие материалы по искусственному интеллекту, данным и технологиям. Если вас интересуют эти темы, подпишитесь здесь!
Бонус: Еще немного о вызове функций.
Прежде чем перейти к структурированным результатам, стоит остановиться и подробнее остановиться на первоначальной мотивации и применении вызова функций, который выходит далеко за рамки простого получения структурированных результатов. По сути, концепция вызова функций является основой рабочих процессов агентного ИИ. Более конкретно, в агентной среде LLM не просто отвечает на вопрос пользователя, а решает, какое действие предпринять дальше, основываясь на вводимых пользователем данных.
Например, представим себе помощника службы поддержки клиентов, который может либо найти заказ, либо оформить возврат средств, либо передать запрос оператору, в зависимости от запроса пользователя. С помощью вызова функций мы можем определить все три варианта действий как «инструменты» (функции), и выходные данные модели будут определять, какую из них вызвать и с какими аргументами, исходя из входных данных.
tools = [ { "type": "function", "function": { "name": "lookup_order", "description": "Look up the status of a customer order", "parameters": { "type": "object", "properties": { "order_id": {"type": "string", "description": "The order ID"} }, "required": ["order_id"] } } }, { "type": "function", "function": { "name": "issue_refund", "description": "Issue a refund for a customer order", "parameters": { "type": "object", "properties": { "order_id": {"type": "string"}, "reason": {"type": "string"} }, "required": ["order_id", "reason"] } } } ] response = client.chat.completions.create( model="gpt-4o-mini", tools=tools, messages=[ {"role": "user", "content": "I want a refund for order #12345, it arrived broken."} ] ) tool_call = response.choices[0].message.tool_calls[0] print(tool_call.function.name) # "issue_refund" print(tool_call.function.arguments) # '{"order_id": "12345", "reason": "arrived broken"}'
Таким образом, объект ответа API выглядит примерно так:
ChatCompletionMessage( content=None, role='assistant', tool_calls=[ ChatCompletionMessageToolCall( id='call_abc123', type='function', function=Function( name='issue_refund', arguments='{"order_id": "12345", "reason": "arrived broken"}' ) ) ] )
А в результате выполнения этих инструкций по печати, предположительно, будет выведено следующее:
issue_refund {"order_id": "12345", "reason": "arrived broken"}
Итак, что же здесь происходит? Модель возвращает объект tool_calls вместо обычного текстового ответа (обратите внимание, что content равен None ). Внутри объекта tool_calls мы видим, что модель решила вызвать issue_refund (а не lookup_order ) и самостоятельно заполнила аргументы на основе того, что сказал пользователь. Затем мы анализируем эти аргументы и выполняем фактическую логику возврата средств в нашей системе.
Обратите внимание, как модель не просто вернула запрошенные данные, а определила, какое из предложенных действий является наиболее подходящим для выполнения, а затем заполнила соответствующие аргументы в своем ответе. Таким образом, мы можем затем взять эти аргументы и фактически выполнить соответствующее действие в нашей системе. В этом и заключается настоящая сила вызова функций, и именно поэтому он является таким основополагающим компонентом в приложениях агентного ИИ.
Но давайте теперь вернемся к машиночитаемым результатам, а о рабочих процессах агентного ИИ и вызове функций мы поговорим подробнее в другой статье.
3. А что насчет структурированных результатов?
Более строгим вариантом вызова функций являются структурированные выходные данные. Даже если вызов функций направляет модель на предоставление выходных данных в соответствии с определенной схемой, это не является жестким ограничением. На практике это означает, что некоторые отклонения от этой определенной схемы все же могут возникнуть. Такие отклонения могут быть следующими:
- Поле, помеченное как обязательное, фактически опускается, если модель не может определить его значение.
- Добавляются дополнительные поля, не определенные в нашей схеме.
- Поле, определенное как
integerвозвращается в виде строки"32"вместо32«.
…и так далее.
Это происходит потому, что при вызове функции модель пытается следовать схеме, но это всё ещё генерация по принципу «наилучших усилий». Как и в любом другом случае, результат здесь по сути представляет собой предсказание токенов по одному, а схема является лишь приблизительной подсказкой. Всё ещё существует большая вероятность того, что эта пошаговая генерация токенов может быть нарушена на каком-то этапе и привести к результатам, отклоняющимся от заданной схемы.
Структурированные выходные данные, с другой стороны, выводят вызов функций на новый уровень, гарантируя, что каждое поле в определенной схеме всегда будет отображаться в выходных данных точно так, как определено, без неожиданностей, отсутствующих или лишних полей. Ключевое отличие заключается в том, что OpenAI использует ограниченное декодирование в фоновом режиме. Это означает, что на каждом шаге генерации токенов модели разрешается генерировать только те токены, которые обеспечивают корректность выходных данных в соответствии со схемой. Другими словами, схема обеспечивается на уровне генерации, а не просто запрашивается через системную подсказку.
Функция структурированного вывода OpenAI активируется простым указанием параметра strict: true в определении функции:
tools = [ { "type": "function", "function": { "name": "extract_person_info", "strict": True, # enables Structured Outputs "parameters": { "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer"}, "city": {"type": "string"} }, "required": ["name", "age", "city"], "additionalProperties": False } } } ]
Но опять же, это имеет свою цену. Функция структурированного вывода доступна на моделях GPT-4o и более поздних версиях, а более старые модели переключаются в режим JSON. Поддерживаются не все структуры JSON, и это может быть немного медленнее, поскольку OpenAI предварительно обрабатывает результаты.
Тем не менее, это самый строгий и безопасный способ обеспечить соблюдение определенной схемы выходных данных модели без возможности отклонений. Для производственных систем, где надежность и согласованность действительно важны, это, как правило, самый безопасный вариант.
Но разве это не одно и то же?
Режим JSON, вызов функций и структурированные выходные данные могут показаться одинаковыми, поскольку все они, по сути, возвращают вам JSON из модели. Тем не менее, как мы уже видели, они существенно различаются по тому, что гарантируют и для чего предназначены. В частности:
- Принудительное соблюдение схемы : режим JSON возвращает допустимый JSON, но без каких-либо структурных гарантий. Вызов функции возвращает допустимый JSON, соответствующий определенной схеме, с учетом конкретных имен полей, типов и обязательных полей, но отклонения все еще возможны. Структурированные выходные данные идут еще дальше, обеспечивая соблюдение этой схемы на уровне генерации, что делает отклонения невозможными.
- Пример использования : режим JSON предназначен для случаев, когда нам нужен машиночитаемый ответ, но мы можем обойтись переменным форматом. Вызов функций был разработан в первую очередь для случаев, когда модели необходимо запустить действие или передать аргументы внешнему инструменту, поэтому по сути это общий случай машиночитаемых выходных данных. Структурированные выходные данные — это вызов функций с гарантией надежности, что делает его идеальным для производственных конвейеров, где необходима согласованность выходных данных.
- Простота настройки: режим JSON — самый простой в настройке; требуется всего лишь изменение одного параметра без определения схемы. С другой стороны, для вызова функций и структурированных выходных данных нам также необходимо продумать и настроить схему JSON.
Тем не менее, сама компания OpenAI рекомендует по возможности всегда использовать структурированные выходные данные вместо режима JSON, как правило.

В моих мыслях
Получение машиночитаемых результатов от LLM и выбор соответствующего подхода к этому может существенно повлиять на надежность и удобство сопровождения любого приложения ИИ. Ответы в свободной текстовой форме отлично подходят для диалоговых интерфейсов, но как только наш LLM становится компонентом более крупной системы (например, для передачи данных, запуска действий, заполнения баз данных и т. д.), структурированные ответы становятся необходимыми. Режим JSON, вызов функций и структурированные выходные данные могут обеспечить такие результаты, каждый с разным уровнем строгости. Как и во многих решениях в разработке ИИ, правильный выбор зависит от того, что вы создаете и насколько вы готовы мириться с изменчивостью.
Если вы дочитали до этого места, вам может пригодиться платформа pialgorithms — та, которую мы разрабатываем, чтобы помочь командам безопасно управлять организационными знаниями в одном месте.
Понравилась эта статья? Присоединяйтесь ко мне на 💌 Substack и 💼 LinkedIn
Все изображения предоставлены автором, если не указано иное.
Мария Мушуци Посмотреть все от Марии Мушуци
Источник: towardsdatascience.com
Оцените материал:
Похожие записи
5 лучших практик для миграции на новую CRM-систему
31.05.2026
Компания SpaceX официально установила цену акций на уровне 135 долларов, проведя крупнейшее IPO в истории.
12.06.2026
Невидимый след ИИ: как Google прячет метки в изобр…
13.04.2026Присоединяйтесь и подпишитесь на рассылку самых свежих новостей по Email
Получайте свежие новости и идеи на почту. Без спама — только самое интересное.
Нажимая «Подписаться», вы соглашаетесь с политикой конфиденциальности.
