sequenceDiagram
autonumber
actor Analyst as Аналитик / Шеф-повар
participant APP as Админ-панель (Dart/Web)
participant API as Бэкенд-шлюз (recipe-mdm-service)
participant DB as Реляционная СУБД (PostgreSQL)
participant AI as Локальный/Облачный LLM-Шлюз (gpt-4o)
Analyst->>APP: Вставляет название и сырой текст шагов, нажимает "Синтезировать"
APP->>API: HTTP POST /api/v1/recipes/synthesize (JSON Payload)
activate API
note over API: Шаг 3: Декодирование JWT, проверка роли пользователя (ANALYST)
note over API: Шаг 4: Первичная Pydantic-валидация непустых строк
API->>AI: Запрос деконструкции текста по Pydantic-схеме RecipeSchema
activate AI
note over AI: Сегментация на шаги, выделение ингредиентов и перевод мер в метрическую систему (г, мл)
AI-->>API: Валидный JSON (массив шагов, веса, точные имена продуктов)
deactivate AI
Note over API, DB: Старт распределенной атомарной транзакции (BEGIN)
API->>DB: Проверка и маппинг имен ингредиентов с food_elements (Триграммный поиск)
API->>DB: INSERT INTO master_recipes (title, total_calories, macro_totals) VALUES (...) RETURNING id
activate DB
DB-->>API: Возврат recipe_id
deactivate DB
API->>DB: Блочный INSERT INTO recipe_ingredients (связывание recipe_id и element_id)
API->>DB: Блочный INSERT INTO recipe_steps (порядковые шаги, тайминги, температура)
Note over API, DB: Фиксация всех изменений (COMMIT)
API-->>APP: HTTP 201 Created (Рецепт успешно деконструирован и добавлен в Master Data)
deactivate API
note over APP: Экран обновляется, аналитик видит готовую граф-карточку блюда с БЖУ
Метод POST /api/v1/recipes/synthesize
Документация API: Синхронно-асинхронный гибридный метод деконструкции, валидации и зачисления кулинарных рецептов в Master Data каталог
- INVENTORY-2XX Backlog — Краткое Описание задачи 1.
- INVENTORY-1XX Refinement (Уточнение) — Краткое Описание задачи 2.
1 Функциональное назначение
Метод является основным оркестратором для пополнения базы данных корпоративных рецептов. Он принимает сырой, неструктурированный текст рецепта от аналитиков, шеф-поваров или пользователей (парсинг кулинарных блогов) и раскладывает его на строго типизированные связи с элементами Периодической таблицы продуктов.
Метод решает три критические задачи:
- ИИ-деконструкция и маппинг ингредиентов: Встроенный контур анализа преобразует свободный текст (например, “две щепотки соли, пол-литра молока”) в строгие веса, метрические единицы измерения и атомарные ID продуктов из таблицы
food_elements. - Расчет системного уравнения Майяра (Калорийность): Базовые нутриенты (БЖУ) всех дочерних ингредиентов автоматически суммируются с учетом весовых коэффициентов и уваривания, формируя итоговый энергетический профиль готового “соединения” (блюда).
- Изоляция транзакционной целостности (Multi-Table Upsert): Зачисление блюда происходит в рамках единой распределенной транзакции PostgreSQL, одновременно создавая шаги приготовления, карточку блюда и реберные связи в графе зависимостей.
2 Протокол взаимодействия (HTTP Контракт)
- Метод:
POST - Маршрут:
/api/v1/recipes/synthesize - Формат данных:
application/json
2.1 Спецификация заголовков (HTTP Headers)
| Заголовок | Обязательный | Описание | Пример значения |
|---|---|---|---|
Content-Type |
Да | Указывает на передачу строго типизированного JSON-пакета | application/json |
Authorization |
Да | Токен авторизации (Access Token). Метод доступен только ролям ANALYST и ADMIN. |
Bearer eyJhbGciOiJIUzI1Ni... |
X-Request-ID |
Да | Сквозной ID для трассировки логов парсинга и ИИ-деконструкции рецептурной структуры | req-recipe-synth-10cc |
2.2 Спецификация тела запроса (Request Body)
| Поле | Тип | Обязательный | Описание | Пример значения |
|---|---|---|---|---|
title |
String | Да | Название кулинарного соединения (блюда) | "Классический Капучино" |
raw_instructions |
String | Да | Неструктурированный текст рецепта, подлежащий ИИ-сегментации | "Взять 60 мл эспрессо, взбить 120 мл молока паром до микропены..." |
2.2.1 Пример сырого JSON-запроса (Payload):
{
"title": "Классический Капучино",
"raw_instructions": "Взять 60 мл эспрессо, взбить 120 мл молока паром при 60 градусах до получения плотной микропены, влить в чашку."
}3 Схема обработки запроса пользователя (Mermaid)
4 Расшифровка шагов работы метода
Шаг 1 (
Analyst -> APP): Аналитик вносит название блюда ("Классический Капучино") и копирует сырой текст процесса приготовления в текстовое поле админ-панели, инициируя команду отправки.Шаг 2 (
APP -> API): Клиентское веб-приложение отправляет запросHTTP POST /api/v1/recipes/synthesize. В заголовках передаются авторизационные данные для проверки RBAC-прав.Шаг 3 (
API -> API): Микросервисrecipe-mdm-serviceдекодирует токен. Если роль пользователя отличается отADMINилиANALYST, выполнение немедленно блокируется статусомHTTP 403 Forbidden.Шаг 4 (
API -> API): Pydantic-модель проверяет входящие текстовые блоки. Если длина строк стремится к нулю, шлюз возвращаетHTTP 422 Unprocessable Entity.Шаг 5 (
API -> AI): Сервис передает сырой текст кулинарного процесса в LLM-модель. Запрос оборачивается в системный контекст Structured Outputs. Нейросеть обязана разложить неструктурированную речь на массив объектов, содержащих точные физические веса в граммах/миллилитрах, температурные режимы, и очищенные базовые имена продуктов.Шаг 6 (
AI -> API): Модель возвращает валидный структурированный JSON-документ. Шлюз проверяет его внутреннюю структуру на соответствие контракту данных.Шаг 7 (
API -> DB): Сервер открывает атомарную транзакцию в PostgreSQL. Первым этапом запускается внутренний цикл сопоставления выделенных ИИ имен продуктов с базовой таблицей Периодической Системы (food_elements) через оператор похожести строкsimilarity.Шаг 8 (
API -> DB): Выполняется вставка головной карточки рецепта с автоматическим перерасчетом суммарных калорий и БЖУ, полученных из связанных карточек продуктов:INSERT INTO master_recipes (title, total_calories, total_proteins, total_fats, total_carbs) VALUES (\$1, \$2, \$3, \$4, \$5) RETURNING id;Шаг 9 (
API -> DB): Используя полученныйrecipe_id, микросервис выполняет множественные вставки в таблицу связейrecipe_ingredients, формируя ребра графа нашего блюда, а также фиксирует хронологическую последовательность в таблицеrecipe_steps.Шаг 10 (
DB -> API): База данных подтверждает успешность выполнения всех связанных инсертов. Транзакция закрывается командойCOMMIT. Если на любом из этапов 7–9 произошел сбой (например, критический разрыв связи или нарушение внешнего ключаFOREIGN KEY), транзакция полностью откатывается (ROLLBACK).Шаг 11 (
API -> APP): Сервер возвращает статусHTTP 201 Createdи отдает полную собранную сущность рецепта, готовую к рендерингу в интерфейсе аналитика.
5 Спецификация ответов сервера (Response Body) и ошибок
5.1 Успешные ответы (Success Responses)
5.1.1 HTTP 201 Created (Ответ на Шаге 11)
Возвращается после успешного завершения ИИ-анализа и транзакционного сохранения рецепта. Поле recipe_graph содержит полную раскладку связей.
- Заголовки ответа (Response Headers):
Content-Type: application/json
- Тело ответа (Response Body):
{
"status": "RECIPE_SYNTHESIZED",
"recipe_id": 1042,
"title": "Классический Капучино",
"totals": {
"calories": 84.6,
"proteins": 4.1,
"fats": 4.5,
"carbs": 6.8
},
"recipe_graph": {
"ingredients": [
{
"element_symbol": "C",
"name": "Эспрессо",
"amount_ml": 60.0,
"role": "Вкусовая основа"
},
{
"element_symbol": "Ml",
"name": "Молоко 3.2%",
"amount_ml": 120.0,
"role": "Текстурный стабилизатор / Микропена"
}
],
"steps": [
{
"step_number": 1,
"action": "Экстракция кофейного зерна под давлением 9 бар",
"target_temperature_c": 92.0,
"duration_seconds": 25
},
{
"step_number": 2,
"action": "Взбивание молока сухим паром до создания коллоидной микропены",
"target_temperature_c": 60.0,
"duration_seconds": 15
}
]
}
}5.2 Спецификация интеграционной Pydantic/JSON-Схемы Обогащения
Структура валидации ответа от LLM (RecipeSchema), используемая внутри Python-слоя для контроля качества ИИ-синтеза.
{
"\$schema": "https://json-schema.org",
"title": "MasterRecipeSynthesis",
"type": "object",
"properties": {
"extracted_ingredients": {
"type": "array",
"items": {
"type": "object",
"properties": {
"raw_name": {"type": "string"},
"metric_amount": {"type": "number"},
"unit": {"type": "string", "enum": ["г", "мл", "шт"]}
},
"required": ["raw_name", "metric_amount", "unit"]
}
},
"chronological_steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"step_index": {"type": "integer"},
"instruction": {"type": "string"},
"temperature_celsius": {"type": "number"},
"seconds_duration": {"type": "integer"}
},
"required": ["step_index", "instruction"]
}
}
},
"required": ["extracted_ingredients", "chronological_steps"]
}5.3 Вилки исключений и обработка ошибок
5.3.1 Ошибка: Отсутствие прав доступа (HTTP 403 Forbidden — Шаг 3)
Выбрасывается на Шаге 3, если JWT-токен валиден, но пользователь обладает ролью USER, не имеющей административного права изменять глобальные мастер-данные.
{
"error_code": "ERR-INSUFFICIENT-PRIVILEGES",
"message": "Доступ отклонен: У вашего аккаунта недостаточно прав для выполнения ИИ-синтеза рецептов.",
"details": {
"required_roles": ["ADMIN", "ANALYST"],
"current_role": "USER",
"action": "Contact data governance team to elevate your role profile."
}
}5.3.2 Ошибка: Сбой лингвистического маппинга ингредиентов (HTTP 422 Unprocessable Entity — Шаг 7)
Выбрасывается на Шаге 7, если ИИ успешно выделил ингредиенты, но поисковый индекс PostgreSQL не смог сопоставить одно из имен (например, “корень мандрагоры”) ни с одним зарегистрированным элементом Периодической Системы. Транзакция атомарно откатывается.
{
"error_code": "ERR-ELEMENT-MAPPING-FAILED",
"message": "Синтез прерван: обнаружен неизвестный химико-продуктовый элемент, отсутствующий в Периодической Системе.",
"details": {
"unmapped_ingredient": "корень мандрагоры",
"action": "Сначала зарегистрируйте данный продукт как элемент через базовый метод POST /api/products."
}
}