Сравнение структурированного вывода среди популярных поставщиков LLM — OpenAI, Gemini, Anthropic, Mistral и AWS Bedrock

Немного отличающиеся API требуют особого подхода.

Содержимое страницы

Вот сравнение поддержки структурированного вывода (получение надежного JSON) среди популярных поставщиков LLM, а также минимальные примеры на Python

цветные столбцы стоящие

Мы уже рассматривали, как запрашивать структурированный вывод от LLM, размещенного на Ollama. Здесь мы рассматриваем, как запросить то же самое от других поставщиков.

Краткая сводка

Поставщик Нативный режим “JSON” Принудительное соблюдение схемы JSON Типичный параметр Примечания
OpenAI Да Да (первоклассный) response_format={"type":"json_schema", ...} Работает через API ответов или Chat Completions; также можно выполнять вызов функций.
Google Gemini Да Да (первоклассный) response_schema= + response_mime_type="application/json" Возвращает строго валидированный JSON при установленной схеме.
Anthropic (Claude) Косвенно Да (через Tool Use со схемой JSON) tools=[{input_schema=...}] + tool_choice Принуждает модель “вызвать” ваш инструмент, определенный схемой; дает аргументы, соответствующие схеме.
Mistral Да Частично (только JSON; нет серверной схемы) response_format={"type":"json_object"} Гарантирует JSON, но вы валидируете его по схеме на стороне клиента.
AWS Bedrock (платформа) Зависит от модели Да (через схему Tool/Converse) toolConfig.tools[].toolSpec.inputSchema API Converse Bedrock валидирует вход инструмента по схеме JSON.

Структурированный вывод LLM - общая информация

Структурированный вывод LLM относится к способности больших языковых моделей (LLMs) генерировать ответы, которые строго соответствуют заранее определенному, конкретному формату или структуре, а не просто производить свободный текст. Такой структурированный вывод может быть в форматах JSON, XML, таблицах или шаблонах, что делает данные машинно-читаемыми, последовательными и легко парсируемыми программным обеспечением для использования в различных приложениях.

Структурированные выводы отличаются от традиционных выходных данных LLM, которые обычно генерируют открытый, естественный язык. Вместо этого структурированные выходы навязывают схему или формат, такой как объекты JSON с определенными ключами и типами значений, или конкретные классы в выходных данных (например, варианты множественного выбора, классы настроения или форматы строк базы данных). Этот подход улучшает надежность, снижает ошибки и галлюцинации, а также упрощает интеграцию в системы, такие как базы данных, API или рабочие процессы.

Генерация структурированных выходов в LLM часто включает техники, такие как:

  • Указание подробных инструкций в запросе для направления модели на создание выходных данных в желаемом формате.
  • Использование инструментов валидации и парсинга, таких как Pydantic в Python, для обеспечения соответствия выходных данных схеме.
  • Иногда навязывание ограничений декодирования на основе грамматики или конечных автоматов для обеспечения соответствия формату на уровне токенов.

Преимущества структурированных выходов LLM включают:

  • Машинную читаемость и легкость интеграции.
  • Снижение вариативности и ошибок.
  • Улучшенную предсказуемость и проверяемость для задач, требующих последовательных форматов данных.

Среди проблем можно выделить проектирование эффективных схем, обработку сложных вложенных данных и потенциальные ограничения в способностях логического мышления по сравнению с генерацией свободного текста.

В целом, структурированный вывод позволяет LLM быть более полезными в приложениях, требующих точных, отформатированных данных, а не только текста, читаемого человеком.

Примеры структурированного вывода на Python

Все фрагменты извлекают информацию об мероприятии в формате JSON: {title, date, location}. Замените ключи/модели по своему усмотрению.

1) OpenAI — JSON Schema (строгий)

from openai import OpenAI
import json

client = OpenAI()

schema = {
    "name": "Event",
    "schema": {
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "date":  {"type": "string", "description": "YYYY-MM-DD"},
            "location": {"type": "string"}
        },
        "required": ["title", "date", "location"],
        "additionalProperties": False
    },
    "strict": True
}

resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "Извлеките мероприятие из: 'PyData Sydney проходит 2025-11-03 в Дарлинг-Харборе.'"}
    ],
    response_format={"type": "json_schema", "json_schema": schema},
)

data = json.loads(resp.choices[0].message.content)
print(data)

Функция Structured Outputs OpenAI навязывает эту схему на стороне сервера.


2) Google Gemini — схема ответа + JSON MIME

import google.generativeai as genai
from google.genai import types

# Настройка с вашим API-ключом
# genai.configure(api_key="your-api-key")

schema = types.Schema(
    type=types.Type.OBJECT,
    properties={
        "title": types.Schema(type=types.Type.STRING),
        "date": types.Schema(type=types.Type.STRING),
        "location": types.Schema(type=types.Type.STRING),
    },
    required=["title", "date", "location"],
    additional_properties=False,
)

resp = genai.generate_content(
    model="gemini-2.0-flash",
    contents="Извлеките мероприятие из: 'PyData Sydney проходит 2025-11-03 в Дарлинг-Харборе.'",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json",
        response_schema=schema,
    ),
)

print(resp.text)  # уже валидный JSON по схеме

Gemini вернет строгий JSON, соответствующий response_schema.


3) Anthropic (Claude) — Использование инструмента с JSON схемой

from anthropic import Anthropic
import json

client = Anthropic()

tool = {
    "name": "extract_event",
    "description": "Возвращает детали мероприятия.",
    "input_schema": {
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "date": {"type": "string"},
            "location": {"type": "string"}
        },
        "required": ["title", "date", "location"],
        "additionalProperties": False
    }
}

msg = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=256,
    tools=[tool],
    tool_choice={"type": "tool", "name": "extract_event"},  # принудительно применяем схему
    messages=[{"role": "user", "content":
        "Извлеките мероприятие из: 'PyData Sydney проходит 2025-11-03 в Дарлинг-Харборе.'"}],
)

# Claude "вызовет" инструмент; получите аргументы (которые соответствуют вашей схеме)
tool_use = next(b for b in msg.content if b.type == "tool_use")
print(json.dumps(tool_use.input, indent=2))

Claude не имеет универсального переключателя “режим JSON”; вместо этого Tool Use с input_schema дает вам валидированные аргументы, соответствующие схеме (и вы можете принудительно применить его использование).


4) Mistral — Режим JSON (валидация на стороне клиента)

from mistralai import Mistral
import json

client = Mistral()

resp = client.chat.complete(
    model="mistral-large-latest",
    messages=[{"role":"user","content":
        "Верните JSON с ключами title, date (YYYY-MM-DD), location для: "
        "'PyData Sydney проходит 2025-11-03 в Дарлинг-Харборе.'"}],
    response_format={"type": "json_object"}  # гарантирует валидный JSON
)

data = json.loads(resp.choices[0].message.content)
print(data)
# Совет: валидируйте `data` по вашей схеме Pydantic/JSON Schema локально.

Режим json_object Mistral навязывает форму JSON (не вашу точную схему) — валидируйте на стороне клиента.


5) AWS Bedrock — Схема инструмента API Converse (независимо от модели)

import boto3, json

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"

tools = [{
    "toolSpec": {
        "name": "extract_event",
        "inputSchema": {
            "json": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "date": {"type": "string"},
                    "location": {"type": "string"}
                },
                "required": ["title","date","location"],
                "additionalProperties": False
            }
        }
    }
}]

resp = bedrock.converse(
    modelId=model_id,
    toolConfig={"tools": tools},
    toolChoice={"tool": {"name": "extract_event"}},  # принудительно применяем схему
    messages=[{"role":"user","content":[{"text":
        "Извлеките мероприятие из: 'PyData Sydney проходит 2025-11-03 в Дарлинг-Харборе.'"}]}],
)

# Получаем содержимое toolUse
tool_use = next(
    c["toolUse"] for c in resp["output"]["message"]["content"] if "toolUse" in c
)
print(json.dumps(tool_use["input"], indent=2))

Bedrock валидирует вход инструмента по вашей схеме JSON, и многие размещенные модели (например, Claude) поддерживают это через Converse.


Практические рекомендации и валидация

  • Если вам нужны самые надежные гарантии на стороне сервера: структурированные выходы OpenAI или схема ответа Gemini.
  • Если вы уже используете Claude/Bedrock: определите Инструмент со схемой JSON и принудительно примените его; читайте аргументы инструмента как ваш типизированный объект.
  • Если вы используете Mistral: включите json_object и валидируйте локально (например, с Pydantic).

Шаблон валидации (работает для всех)

from pydantic import BaseModel, ValidationError

class Event(BaseModel):
    title: str
    date: str
    location: str

try:
    event = Event.model_validate(data)  # `data` от любого поставщика
except ValidationError as e:
    # обработать / повторить / попросить модель исправить с e.errors()
    print(e)

Полезные ссылки