- BrainTools - https://www.braintools.ru -

AI Sous-chef: как мы собрали приложение с облачными моделями и вынесли промпты из кода

Представьте: открываете холодильник — морковь, йогурт, яблоко. На полке арахисовое масло. Продукты есть, но что из этого приготовить? Можно искать рецепты и проверять, хватает ли ингредиентов. А можно сфотографировать продукты и попросить приложение предложить варианты.

Такой сценарий мы выбрали для командного проекта AI Sous-chef. Пользователь фотографирует продукты или вводит их списком, а сервис предлагает блюда и шаги приготовления. За два с половиной месяца мы выпустили Android-приложение и Telegram-бота.

После запуска получили отзыв: приложение предложило салат из сырой картошки и баклажана. Рецепт появился на экране, но в инструкции не было этапа приготовления этих ингредиентов.

Основной репозиторий AI Sous-chef опубликован под лицензией GNU AGPL v3.0. В нём находятся бэкенд, промпты, конфигурации и материалы проверок. Покажем, как устроены загрузка инструкций и обработка ответов, какие решения могут пригодиться в похожем проекте и что нам ещё предстоит доработать.

Что вошло в первую версию

Команда собралась на платформе командной практики geek-tik.tech [1]. Над первым релизом работали шесть человек: два бэкенд-разработчика, мобильный разработчик, дизайнер, проектный менеджер и промпт-инженер. Начали 9 июля 2026 года, приложение и бота выпустили 21 сентября.

Для минимальной рабочей версии (MVP) выбрали один сценарий: «Что приготовить из того, что уже есть?» Пользователь отправляет фотографию, получает список распознанных продуктов, подтверждает его и переходит к вариантам блюд. Можно обойтись без фотографии и написать ингредиенты текстом.

Рецепты приходят сразу с пошаговыми инструкциями. Когда пользователь выбирает блюдо, приложение показывает уже полученный текст — отдельного обращения к модели за шагами нет. При этом модель составляет инструкции и для тех блюд, которые человек не откроет. Расходы такой схемы мы пока не сравнивали с генерацией подробного рецепта по запросу.

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

Две модели и общий бэкенд

У Android-приложения на React Native и Telegram-бота общий бэкенд на Python и FastAPI. В инфраструктуре используются PostgreSQL, Redis, nginx и Docker Compose.

Проект продолжает меняться после первого релиза. Далее описываем реализацию AI-модуля, которую изучили при подготовке статьи. В ней задачи разделены между двумя моделями:

AI Sous-chef: как мы собрали приложение с облачными моделями и вынесли промпты из кода - 1

Текстовый ввод в этом модуле тоже проходит через Qwen: модель получает сообщение и возвращает список ингредиентов. Затем для генерации рецептов используются названия продуктов. Во второй версии API предусмотрена передача количества и единиц измерения, когда они заполнены.

Оба запроса отправляются в API Яндекса. Для обращения используется библиотека openai с заданным адресом сервера. Вызов клиента выглядит так:

client = AsyncOpenAI(
    api_key=cls.YANDEX_API_KEY,
    base_url=cls.AI_API_URL,
    project=cls.YANDEX_FOLDER_ID,
)

В AI_API_URL задан адрес https://ai.api.cloud.yandex.net/v1 [3]. API Яндекса поддерживает совместимые с OpenAI вызовы, поэтому мы используем библиотеку openai, указав адрес сервиса Яндекса. Запросы при этом идут к выбранным моделям в облаке Яндекса.

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

В адресах моделей используется latest, а не закреплённая версия. Это важно при повторении [4] экспериментов: одинаковые код и промпт не гарантируют прежнего поведения [5] после обновления модели.

Промпты отдельно от кода

Инструкции для моделей хранятся в отдельных файлах. Для распознавания и генерации рецептов предусмотрены системный и пользовательский промпты:

prompts/
  pin.json
  recognize/v1/system.md
  recognize/v1/user.md
  recipes/v1/system.md
  recipes/v1/user.md

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

Выбранные версии промптов задаются в pin.json:

{
  "recognize": "v1",
  "recipes": "v1"
}

Загрузчик читает нужные файлы и подставляет данные вместо выражений вроде {{ products }}. Для этого используется простая замена по шаблону. Условных конструкций Jinja здесь нет. Если необходимый файл или значение для подстановки отсутствует, загрузчик выдаёт ошибку.

Так можно редактировать инструкции, не переписывая функцию обращения к модели, и независимо выбирать версии для распознавания и рецептов.

У переключения есть особенность: содержимое pin.json кешируется в памяти [6] процесса. После изменения указателя работающему процессу нужен перезапуск или вызов функции очистки кеша. Сами тексты промптов читаются при каждом обращении к загрузчику.

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

Отдельное хранение промптов не означает, что их можно менять без учёта бэкенда. Например, можно уточнить формулировку шага приготовления, сохранив прежнюю структуру ответа. Но если попросить модель возвращать другие поля, нужно проверить, сможет ли код их обработать. Поэтому после изменения промпта важно проверять не только ответ модели, но и то, как с ним работает приложение.

Что мы просим у модели

В промпте генерации указаны несколько требований:

  • предложить два-три простых рецепта;

  • использовать преимущественно переданные продукты;

  • явно указывать базовые добавки — соль, масло, воду;

  • включать в шаги время, температуру или визуальный признак готовности;

  • отвечать на русском и возвращать JSON без Markdown-обёрток.

Формулировка «используй в основном переданные продукты» оставляет модели свободу. Это не строгий запрет на дополнительные ингредиенты. При оценке рецептов нужно учитывать, какие добавки допустимы и как сообщать о них пользователю.

Что проверяет бэкенд в ответе модели

Для генерации рецептов JSON запрашивается не только текстом промпта, но и параметром API:

response_format=ResponseFormatJSONObject(type="json_object")

Затем бэкенд разбирает ответ через json.loads и вызывает model_validate, проверяя соответствие ожидаемой структуре данных.

В этом участке кода нет удаления пояснений до первой и после последней фигурной скобки. Если ответ с рецептами не удаётся разобрать как JSON, возникает ошибка.

При распознавании также проверяется, получен ли непустой список продуктов. Для отсутствия продуктов и недоступности внешнего AI-сервиса предусмотрены отдельные типы ошибок. При этом некоторые причины пока объединены: ошибка разбора JSON при распознавании превращается в исключение «продукты не найдены». По нему нельзя определить, действительно ли список пуст или ответ пришёл в неверном формате.

У этих проверок есть конкретные границы:

AI Sous-chef: как мы собрали приложение с облачными моделями и вынесли промпты из кода - 2

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

Как проверять приложение без генерации

В AI-модуле предусмотрен режим stub. При настройке AI_MODE=stub вместо обращения к моделям выбираются реализации с фиксированными ответами: тестовым списком продуктов и заранее записанными рецептами.

Этот режим позволяет проверять работу приложения с подготовленными данными без запросов к облачным моделям. Он не предназначен для оценки качества генерации — рецепты уже записаны в коде.

В README описан локальный запуск через Docker Compose. В состав входят бэкенд, бот, PostgreSQL, Redis, nginx и фронтенд. Для запуска бота нужен токен Telegram, а для реальных запросов к моделям — настройки доступа к Яндексу.

Десять сценариев, но пока без общей метрики

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

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

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

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

Что изменилось после обратной связи

После релиза мы распространяли ссылки на приложение и бота, собирали отзывы и сами пользовались сервисом.

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

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

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

Что можно взять из репозитория

Основной репозиторий опубликован под GNU AGPL v3.0. Использовать, изменять и распространять код можно на условиях этой лицензии; полный текст приведён в LICENSE [7].

Для похожего приложения с облачными моделями здесь можно изучить:

  • загрузку системных и пользовательских промптов из файлов;

  • независимый выбор версий для распознавания и генерации;

  • разбор и проверку структуры ответов;

  • режим с фиксированными ответами вместо вызовов моделей.

Эти компоненты реализованы для AI Sous-chef, а не оформлены как отдельная универсальная библиотека. При адаптации нужно учитывать структуры данных приложения, кеширование указателя версий и настройки внешнего API.

Лицензия репозитория не предоставляет доступ к облачным моделям: для реальной генерации по-прежнему нужны учётные данные Яндекса и соблюдение условий используемого сервиса. Код мобильного приложения размещён в отдельном репозитории. Указанная здесь лицензия относится к основному репозиторию teamsync-labs/ai-sous-chef.

Сейчас AI Sous-chef доступен в RuStore и Telegram. Ближайшая доработка — правка и добавление ингредиентов после фотографии. Параллельно продолжаем работу над промптами: приложение уже выдаёт пошаговые инструкции, но нам ещё предстоит добиться более надёжного результата для человека, который будет по ним готовить.


Материал подготовлен при активном участии команды AI Sous-chef и Натальи Жосан [12] — автора идеи и junior Python-разработчика.

Автор: IrinaLevko

Источник [13]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/38255

URLs in this post:

[1] geek-tik.tech: http://geek-tik.tech

[2] ошибку: http://www.braintools.ru/article/4192

[3] https://ai.api.cloud.yandex.net/v1: https://ai.api.cloud.yandex.net/v1

[4] повторении: http://www.braintools.ru/article/4012

[5] поведения: http://www.braintools.ru/article/9372

[6] памяти: http://www.braintools.ru/article/4140

[7] LICENSE: https://github.com/teamsync-labs/ai-sous-chef/blob/main/LICENSE

[8] Бэкенд и промпты: https://github.com/teamsync-labs/ai-sous-chef

[9] Мобильное приложение: https://github.com/teamsync-labs/ai-sous-chef-app

[10] Android-приложение в RuStore: https://www.rustore.ru/catalog/app/com.eugnazaroff.aisouschef

[11] Telegram-бот: https://t.me/ai_sous_chef_bot

[12] Натальи Жосан: https://setka.ru/users/f9cee860-2892-40b9-9b89-ab3c057650f7

[13] Источник: https://habr.com/ru/articles/1105496/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1105496

www.BrainTools.ru

Rambler's Top100