Structured Output JSON в n8n: как спроектировать схему ответа, которую можно передавать в CRM, БД и API ¶
Обновлено: 2026-05-29
Короткий ответ ¶
Structured Output JSON в n8n — это контракт между AI node и остальным workflow. Он должен быть спроектирован не как “удобный ответ модели”, а как стабильный формат для IF, Switch, CRM, Postgres, HTTP Request, approval и логирования. Хороший JSON содержит schema_version, status, confidence, result, evidence, error_code и поля, которые downstream действительно умеет обработать. Плохой JSON выглядит красиво, но ломает workflow: смешивает null и пустые строки, придумывает enum, меняет типы, отдаёт вложенность без версии и не объясняет, почему результат можно использовать.
Почему схема важнее prompt ¶
Prompt говорит модели, что сделать. JSON-схема говорит workflow, что он получит. Если schema размыта, каждый следующий node вынужден угадывать смысл. Например, поле priority может прийти как high, urgent, important, критично, 1 или true. Человек поймёт, а автоматизация — нет. В production это ведёт к неправильной маршрутизации, падениям API, дублям и ручным исправлениям.
Схема должна проектироваться от downstream. Сначала ответьте: куда пойдёт результат? В CRM? В Postgres? В email draft? В ticketing system? В RAG source filter? В approval card? Потом определите поля, типы, enum, required и error behavior.
Базовый шаблон structured output ¶
Для большинства AI workflow удобно начинать с такой структуры:
{
"schema_version": "1.0",
"status": "ok",
"confidence": 0.82,
"result": {},
"evidence": [],
"warnings": [],
"requires_review": false,
"error_code": null,
"human_message": ""
}
status отвечает на вопрос “можно ли использовать результат”. confidence помогает routing. result содержит бизнес-данные. evidence показывает, на чём основан вывод. warnings не блокируют workflow, но помогают оператору. requires_review переводит кейс в approval. error_code нужен для машинной обработки. human_message — короткое объяснение для пользователя или ревьюера.
Как проектировать result ¶
Не кладите всё в один текст. Если AI извлекает лид, result должен быть структурой:
{
"lead_type": "b2b",
"company_name": "Ромашка ООО",
"contact_name": "Анна",
"email": "anna@example.com",
"phone": null,
"budget_range": "unknown",
"need": "автоматизация заявок из Telegram в CRM",
"urgency": "this_month"
}
Для классификации:
{
"category": "billing",
"subcategory": "invoice_missing",
"priority": "medium",
"language": "ru",
"route_to": "finance_support"
}
Для action request:
{
"action": "create_task",
"target_system": "crm",
"idempotency_key": "ticket_1021_create_task",
"action_preview": "Создать задачу для менеджера по счёту INV-44",
"parameters": {
"task_title": "Проверить счёт INV-44",
"due_date": "2026-05-30"
}
}
В action-схемах idempotency_key обязателен. Иначе повторный execution или retry может создать дубли.
Required fields и nullable values ¶
Главное правило: required означает “workflow не может продолжить работу без этого поля”. Не делайте required всё подряд. Например, email может быть необязательным, если пользователь написал только телефон. Но status, schema_version, confidence и ключевая классификация часто должны быть required.
Определите политику отсутствующих значений:
| Ситуация | Как писать в JSON |
|---|---|
| значение неизвестно | null |
| список пуст | [] |
| действие невозможно | status: no_data или status: blocked |
| поле не применимо | null + warning |
| ошибка обработки | status: error + error_code |
Не используйте одновременно null, "", "unknown", "n/a" и false для одного смысла. Downstream станет хрупким.
Enum: меньше свободы, больше стабильности ¶
Enum защищает workflow от фантазии модели. Вместо свободного priority задайте:
{
"priority": "low | medium | high | urgent"
}
Но в production мало написать enum в prompt. Проверьте его в Code node. Если модель вернула critical, решите заранее: маппить в urgent, отправлять на repair или review. Не позволяйте новым значениям тихо проходить в CRM.
Массивы и вложенность ¶
Массивы нужны для items, evidence, warnings, tasks, extracted_entities. Но они часто ломаются: модель возвращает объект вместо массива, пустую строку вместо [] или добавляет разные структуры внутри одного списка.
Хорошая структура evidence:
{
"evidence": [
{
"source_id": "email_body",
"quote": "не можем оплатить счёт",
"supports_field": "category"
}
]
}
Плохая структура:
{
"evidence": "клиент сказал, что не может оплатить"
}
Почему плохо: downstream не сможет фильтровать evidence, привязать цитату к полю и показать ревьюеру источник.
Error codes для workflow ¶
Не возвращайте только “не получилось”. Ошибка должна быть машинно читаемой:
| Код | Что означает | Что делает workflow |
|---|---|---|
missing_required_input |
не хватает входных данных | уточняющий вопрос |
low_confidence |
модель не уверена | human review |
no_relevant_source |
RAG не нашёл источник | no-answer policy |
invalid_user_permission |
пользователь не имеет права | deny |
schema_validation_failed |
JSON не прошёл проверку | repair или fallback |
unsafe_action |
действие рискованное | approval |
Такой подход позволяет строить Switch node без хаоса.
Версионирование схемы ¶
Добавьте schema_version с первого дня. Менять версию нужно, если вы:
- добавили required field;
- удалили поле;
- изменили enum;
- поменяли смысл поля;
- перенесли данные во вложенный объект;
- изменили error policy.
Старые executions должны оставаться понятными. Если раньше priority был 1/2/3, а теперь low/medium/high, не смешивайте версии в одном отчёте.
Пример валидации схемы в n8n ¶
const x = $json;
const errors = [];
if (x.schema_version !== '1.0') errors.push('unsupported_schema_version');
if (!['ok','no_data','needs_review','blocked','error'].includes(x.status)) errors.push('bad_status');
if (typeof x.confidence !== 'number') errors.push('bad_confidence');
if (x.evidence && !Array.isArray(x.evidence)) errors.push('evidence_not_array');
if (x.warnings && !Array.isArray(x.warnings)) errors.push('warnings_not_array');
if (x.status === 'ok' && !x.result) errors.push('ok_without_result');
if (x.status === 'no_data' && !x.human_message) errors.push('no_data_without_message');
return [{ json: { ...x, validation: { passed: errors.length === 0, errors } } }];
Как не перегрузить схему ¶
Одна из частых ошибок — сделать универсальный JSON для всех AI-задач: классификация, суммаризация, извлечение, scoring, action request и RAG answer одновременно. Такая схема становится длинной, модель путается, а редактору сложно поддерживать её руками. Лучше сделать отдельные схемы под разные типы outputs.
Принцип простой: один output — одна бизнес-задача. Если workflow сначала классифицирует обращение, потом извлекает данные, потом пишет черновик ответа, используйте три маленьких контракта. Это проще тестировать, логировать и чинить.
FAQ ¶
Чем structured output JSON отличается от обычного ответа AI?
Обычный ответ предназначен для человека. Structured output JSON — контракт для следующего node: CRM, БД, API, Router, IF или approval workflow.
Можно ли делать очень большую JSON Schema?
Можно, но это ухудшает стабильность. Лучше делать схему по задаче: extraction, classification, routing, draft generation или action request. Для сложных процессов используйте несколько маленьких схем.
Нужно ли добавлять поле confidence?
Да, но не как единственный критерий. Confidence полезен для routing и review, но его нужно сочетать с validation, source evidence, business rules и тестами.
Как описывать отсутствующие значения?
Не смешивайте null, пустую строку и “unknown”. Выберите политику: null для неизвестного, empty array для отсутствующего списка, status=no_data для невозможного результата.
Как версионировать JSON-схему?
Добавьте schema_version и меняйте её при изменении required fields, enum, вложенности или смысла полей. Старые executions должны оставаться интерпретируемыми.