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

<Callout type="info" title="От прозы к данным">
Структурированный вывод преобразует ответы ИИ из свободного текста в обрабатываемые, парсируемые данные.
</Callout>

## Почему структура важна

<StructuredOutputDemo />

## Базовые техники форматирования

### Списки

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

<TryIt 
  compact
  title="Форматирование списка"
  prompt={`Предоставьте 5 советов для улучшения сна.

Формат: Нумерованный список с кратким объяснением для каждого пункта.
Каждый совет должен быть выделен жирным шрифтом, за которым следует тире и объяснение.`}
/>

<Callout type="tip" title="Лучшие практики для списков">
Указывайте точное количество элементов, нужны ли объяснения, и должны ли элементы быть выделены жирным шрифтом или иметь определённую структуру.
</Callout>

### Таблицы

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

<TryIt 
  compact
  title="Форматирование таблицы"
  prompt={`Сравните 4 лучших веб-фреймворка для Python.

Оформите в виде markdown-таблицы со столбцами:
| Фреймворк | Лучше всего для | Кривая обучения | Производительность |`}
/>

<Callout type="tip" title="Лучшие практики для таблиц">
Указывайте названия столбцов, ожидаемые типы данных (текст, числа, рейтинги) и количество необходимых строк. Для сложных сравнений ограничивайтесь 4-6 столбцами для удобства чтения.
</Callout>

### Заголовки и разделы

Заголовки создают чёткую структуру документа, делая длинные ответы удобными для просмотра и организованными. Используйте их для отчётов, анализов или любых многокомпонентных ответов. Иерархические заголовки (##, ###) показывают связи между разделами.

```
Проанализируйте это бизнес-предложение.

Структурируйте свой ответ по следующим разделам:
## Краткое резюме
## Сильные стороны
## Слабые стороны
## Рекомендации
## Оценка рисков
```

<Callout type="tip" title="Лучшие практики для разделов">
Перечисляйте разделы в нужном порядке. Для единообразия указывайте, что должен содержать каждый раздел (например, «Краткое резюме: только 2-3 предложения»).
</Callout>

### Акцентирование с помощью директив в верхнем регистре

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

**Распространённые директивы в верхнем регистре:**

<InfoGrid items={[
  { label: "NEVER", description: "Абсолютный запрет: «NEVER include personal opinions»", color: "red" },
  { label: "ALWAYS", description: "Обязательное требование: «ALWAYS cite sources»", color: "green" },
  { label: "IMPORTANT", description: "Критическая инструкция: «IMPORTANT: Keep responses under 100 words»", color: "amber" },
  { label: "DO NOT", description: "Строгий запрет: «DO NOT make up statistics»", color: "red" },
  { label: "MUST", description: "Обязательное действие: «Output MUST be valid JSON»", color: "blue" },
  { label: "ONLY", description: "Ограничение: «Return ONLY the code, no explanations»", color: "purple" },
]} />

```
Обобщите эту статью.

IMPORTANT: Держите резюме в пределах 100 слов.
NEVER добавляйте информацию, отсутствующую в оригинале.
ALWAYS сохраняйте оригинальный тон и точку зрения.
DO NOT включайте собственные мнения или анализ.
```

<Callout type="warning" title="Используйте умеренно">
Если всё написано в верхнем регистре или помечено как критическое, ничего не выделяется. Сохраняйте эти директивы для действительно важных ограничений.
</Callout>

## Вывод в формате JSON

JSON (JavaScript Object Notation) — самый популярный формат для структурированного вывода ИИ. Он машиночитаем, широко поддерживается языками программирования и идеально подходит для API, баз данных и автоматизированных рабочих процессов. Ключ к надёжному JSON — предоставление чёткой схемы.

### Базовый запрос JSON

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

<TryIt 
  title="Извлечение JSON"
  description="Извлечение структурированных данных из неструктурированного текста."
  prompt={`Извлеките информацию из этого текста и верните в формате JSON:

{
    "company_name": "string",
    "founding_year": number,
    "headquarters": "string",
    "employees": number,
    "industry": "string"
}

Текст: "Apple Inc., основанная в 1976 году, имеет штаб-квартиру в Купертино, Калифорния. Технологический гигант насчитывает около 164 000 сотрудников по всему миру."`}
/>

### Сложные структуры JSON

Для вложенных данных используйте иерархический JSON с объектами внутри объектов, массивами объектов и смешанными типами. Чётко определяйте каждый уровень и используйте аннотации в стиле TypeScript (`"positive" | "negative"`) для ограничения значений.

```
Проанализируйте этот отзыв о продукте и верните JSON:

{
  "review_id": "string (сгенерировать уникальный)",
  "sentiment": {
    "overall": "positive" | "negative" | "mixed" | "neutral",
    "score": 0.0-1.0
  },
  "aspects": [
    {
      "aspect": "string (например, 'price', 'quality')",
      "sentiment": "positive" | "negative" | "neutral",
      "mentions": ["точные цитаты из отзыва"]
    }
  ],
  "purchase_intent": {
    "would_recommend": boolean,
    "confidence": 0.0-1.0
  },
  "key_phrases": ["массив строк с примечательными фразами"]
}

Верните ONLY валидный JSON, без дополнительного текста.

Отзыв: "[текст отзыва]"
```

### Обеспечение валидности JSON

Модели иногда добавляют пояснительный текст или markdown-форматирование вокруг JSON. Предотвратите это явными инструкциями о формате вывода. Вы можете запросить чистый JSON или JSON внутри блоков кода — выбирайте в зависимости от ваших потребностей парсинга.

Добавьте явные инструкции:

```
IMPORTANT:
- Верните ONLY объект JSON, без markdown-блоков кода
- Убедитесь, что все строки правильно экранированы
- Используйте null для отсутствующих значений, не undefined
- Проверьте, что вывод является парсируемым JSON
```

Или запросите блоки кода, попросив модель обернуть вывод:

````
Верните результат как блок кода JSON:
```json
{ ... }
```
````

## Вывод в формате YAML

YAML более удобочитаем для человека, чем JSON, используя отступы вместо скобок. Это стандарт для конфигурационных файлов (Docker, Kubernetes, GitHub Actions) и хорошо работает, когда вывод будет читаться людьми или использоваться в DevOps-контекстах. YAML чувствителен к отступам, поэтому будьте конкретны в требованиях к форматированию.

<TryIt 
  compact
  title="Генерация YAML"
  prompt={`Сгенерируйте workflow GitHub Actions для проекта на Node.js.

Верните как валидный YAML:
- Включите: этапы install, lint, test, build
- Используйте Node.js 18
- Кэшируйте npm-зависимости
- Запускайте при push в main и pull requests`}
/>

## Вывод в формате XML

XML всё ещё требуется для многих корпоративных систем, SOAP API и устаревших интеграций. Он более многословен, чем JSON, но предлагает такие функции, как атрибуты, пространства имён и секции CDATA для сложных данных. Указывайте названия элементов, структуру вложенности и где использовать атрибуты, а где дочерние элементы.

```
Преобразуйте эти данные в формат XML:

Требования:
- Корневой элемент: <catalog>
- Каждый элемент в теге <book>
- Используйте атрибуты где уместно
- Используйте CDATA для текста описания

Данные: [данные о книгах]
```

## Пользовательские форматы

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

### Формат структурированного анализа

Используйте разделители (===, ---, [SECTION]) для создания легко просматриваемых документов с чёткими границами между разделами. Этот формат отлично подходит для код-ревью, аудитов и анализов.

```
Проанализируйте этот код, используя именно этот формат:

=== АНАЛИЗ КОДА ===

[SUMMARY]
Обзор в одном абзаце

[ISSUES]
• CRITICAL: [проблема] — [файл:строка]
• WARNING: [проблема] — [файл:строка]  
• INFO: [проблема] — [файл:строка]

[METRICS]
Сложность: [Low/Medium/High]
Поддерживаемость: [оценка]/10
Покрытие тестами: [примерный %]

[RECOMMENDATIONS]
1. [Рекомендация приоритета 1]
2. [Рекомендация приоритета 2]

=== КОНЕЦ АНАЛИЗА ===
```

### Формат заполнения пропусков

Шаблоны с пропусками (___) направляют модель на заполнение определённых полей при сохранении точного форматирования. Этот подход отлично работает для форм, брифов и стандартизированных документов, где важна согласованность.

```
Заполните этот шаблон для данного продукта:

БРИФ ПРОДУКТА
─────────────
Название: _______________
Слоган: _______________
Целевой пользователь: _______________
Решаемая проблема: _______________
Ключевые функции:
  1. _______________
  2. _______________
  3. _______________
Отличительная особенность: _______________

Продукт: [описание продукта]
```

## Типизированные ответы

Типизированные ответы определяют категории или типы сущностей, которые модель должна распознавать и маркировать. Эта техника необходима для распознавания именованных сущностей (NER), задач классификации и любого извлечения, где нужно последовательно категоризировать информацию. Чётко определяйте ваши типы с примерами.

<TryIt 
  compact
  title="Извлечение сущностей"
  prompt={`Извлеките сущности из этого текста.

Типы сущностей:
- PERSON: Полные имена людей
- ORG: Названия организаций/компаний
- LOCATION: Города, страны, адреса
- DATE: Даты в формате ISO (YYYY-MM-DD)
- MONEY: Денежные суммы с валютой

Форматируйте каждую как: [TYPE]: [значение]

Текст: "Тим Кук объявил, что Apple инвестирует 1 миллиард долларов в новый объект в Остине к декабрю 2024 года."`}
/>

## Многокомпонентные структурированные ответы

Когда вам нужен всеобъемлющий вывод, охватывающий несколько аспектов, определите отдельные части с чёткими границами. Точно указывайте, что входит в каждую часть — формат, длину и тип содержимого. Это предотвращает смешивание разделов или пропуск частей моделью.

```
Исследуйте эту тему и предоставьте:

### ЧАСТЬ 1: КРАТКОЕ РЕЗЮМЕ
[Обзор в 2-3 предложениях]

### ЧАСТЬ 2: КЛЮЧЕВЫЕ ВЫВОДЫ
[Ровно 5 пунктов списка]

### ЧАСТЬ 3: ТАБЛИЦА ДАННЫХ
| Метрика | Значение | Источник |
|---------|----------|----------|
[Минимум 5 строк]

### ЧАСТЬ 4: РЕКОМЕНДАЦИИ
[Нумерованный список из 3 практических рекомендаций]

### ЧАСТЬ 5: ДОПОЛНИТЕЛЬНОЕ ЧТЕНИЕ
[3 рекомендуемых ресурса с краткими описаниями]
```

## Условное форматирование

Условное форматирование позволяет определять различные форматы вывода в зависимости от характеристик входных данных. Это мощный инструмент для систем классификации, сортировки и маршрутизации, где формат ответа должен варьироваться в зависимости от того, что обнаружила модель. Используйте чёткую логику if/then с явными шаблонами вывода для каждого случая.

<TryIt 
  compact
  title="Классификация тикетов"
  prompt={`Классифицируйте этот тикет поддержки.

Если URGENT (система не работает, проблема безопасности, потеря данных):
  Вернуть: 🔴 URGENT | [Категория] | [Предлагаемое действие]

Если HIGH (затрагивает нескольких пользователей, влияет на выручку):
  Вернуть: 🟠 HIGH | [Категория] | [Предлагаемое действие]

Если MEDIUM (затронут один пользователь, есть обходной путь):
  Вернуть: 🟡 MEDIUM | [Категория] | [Предлагаемое действие]

Если LOW (вопросы, запросы функций):
  Вернуть: 🟢 LOW | [Категория] | [Предлагаемое действие]

Тикет: "Я не могу войти в свой аккаунт. Я дважды пытался сбросить пароль, но всё равно получаю ошибку. Это блокирует работу всей моей команды с панелью управления."`}
/>

## Массивы и списки в JSON

Извлечение нескольких элементов в массивы требует тщательного определения схемы. Укажите структуру массива, что должен содержать каждый элемент и как обрабатывать граничные случаи (пустые массивы, единичные элементы). Включение поля count помогает проверить полноту.

```
Извлеките все пункты действий из этой стенограммы встречи.

Верните как массив JSON:
{
  "action_items": [
    {
      "task": "строка, описывающая задачу",
      "assignee": "имя человека или 'Не назначено'",
      "deadline": "дата, если упомянута, иначе null",
      "priority": "high" | "medium" | "low",
      "context": "релевантная цитата из стенограммы"
    }
  ],
  "total_count": number
}

Стенограмма: "[стенограмма встречи]"
```

## Инструкции по валидации

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

```
Сгенерируйте отчёт, затем:

ЧЕКЛИСТ ВАЛИДАЦИИ:
□ Все обязательные разделы присутствуют
□ Не осталось текста-заполнителя
□ Все статистики включают источники
□ Количество слов в пределах 500-700
□ Заключение связано с введением

Если какая-либо проверка не пройдена, исправьте перед ответом.
```

## Обработка необязательных полей

Реальные данные часто имеют отсутствующие значения. Явно инструктируйте модель, как обрабатывать необязательные поля — использование `null` чище, чем пустые строки, и проще обрабатывается программно. Также предотвращайте «галлюцинацию» отсутствующих данных, подчёркивая, что модель никогда не должна придумывать информацию.

```
Извлеките контактную информацию. Используйте null для отсутствующих полей.

{
  "name": "string (обязательно)",
  "email": "string или null",
  "phone": "string или null", 
  "company": "string или null",
  "role": "string или null",
  "linkedin": "URL string или null"
}

IMPORTANT: 
- Никогда не придумывайте информацию, отсутствующую в источнике
- Используйте null, а не пустые строки, для отсутствующих данных
- Номера телефонов в формате E.164, если возможно
```

## Резюме

<Callout type="tip" title="Ключевые техники">
Будьте явными относительно формата, используйте примеры, указывайте типы, обрабатывайте граничные случаи с помощью null-значений и просите модель валидировать свой вывод.
</Callout>

<Quiz 
  question="Каково главное преимущество структурированного вывода перед неструктурированным текстом?"
  options={[
    "Он использует меньше токенов",
    "ИИ легче его генерировать",
    "Его можно программно парсить и валидировать",
    "Он всегда выдаёт корректную информацию"
  ]}
  correctIndex={2}
  explanation="Структурированные выводы, такие как JSON, могут парситься кодом, сравниваться между запросами, интегрироваться в рабочие процессы и валидироваться на полноту — вещи, которые сложны или невозможны с произвольным текстом."
/>

Структурированные выводы необходимы для создания надёжных приложений на основе ИИ. В следующей главе мы рассмотрим промптинг с цепочкой рассуждений для сложных задач логического мышления.
