Осьминог, холодильник и 498 тестов: как я за 4 дня навайбкодил бота, который собирает мне корзину в Лавке. DIY или Сделай сам.. DIY или Сделай сам. llm.. DIY или Сделай сам. llm. mcp.. DIY или Сделай сам. llm. mcp. python.. DIY или Сделай сам. llm. mcp. python. telegram mini app.. DIY или Сделай сам. llm. mcp. python. telegram mini app. вайб-кодинг.. DIY или Сделай сам. llm. mcp. python. telegram mini app. вайб-кодинг. ии-агенты.. DIY или Сделай сам. llm. mcp. python. telegram mini app. вайб-кодинг. ии-агенты. искусственный интеллект.
Осьминог, холодильник и 498 тестов: как я за 4 дня навайбкодил бота, который собирает мне корзину в Лавке - 1

Сейчас в тренде подсчёт калорий и БЖУ — скажу честно, сам считаю) А ещё вайбкодинг. Для меня ИИ-агенты стали чем-то вроде 3D-принтера: если есть фантазия, сделать можно что угодно.

Моя фантазия была бытовой. Каждую неделю дома один разговор: «Что едим?» — «А что есть?» — «Полпачки риса и курица, которая скоро умрёт». Потом полчаса в приложении, корзина в которой все что угодно, ведь ты пока кликаешь, невольно добавляешь лишние, а курица всё-таки умирает.

Я захотел так: говорю «3 дня, двое, 5000 ₽, примерно 2000 ккал в день» — и получаю меню с рецептами, где сначала используется то, что уже лежит дома, и собранную корзину в Яндекс Лавке. Оплачиваю сам, одной кнопкой в приложении.

Я не профессиональный разработчик. Пишу на Python для себя и честно называю себя вайбкодером: код пишут ИИ-агенты, а я ставлю задачи, проверяю и говорю «да» или «нет».

Сразу честно: дорога протоптана. Про MCP для Лавки уже есть статья автора yandex-lavka-mcp, про меню под КБЖУ — статья про MCP ВкусВилла. Технику API ищите там. У меня другое: семейный бот в Telegram, который знает, что лежит в холодильнике, и никогда не платит сам. Ниже — как навайбкодить такое и не развалить: процесс, проверки в коде и грабли, которых в тех статьях нет.

Что получилось

Telegram-бот с Mini App. На главном экране — кухня и шеф-осьминог по имени Осьма. Картинку нарисовала нейросеть, а зоны нажатия на ней — настоящие кнопки.

Осьминог, холодильник и 498 тестов: как я за 4 дня навайбкодил бота, который собирает мне корзину в Лавке - 2

Холодильник. /
Пишешь боту обычным текстом: «2 кг картошки, 10 яиц, молоко до 10.11». Модель переводит это в граммы, миллилитры и штуки и ставит типовой срок годности. Сохраняется только после кнопки «✅ Сохранить». Список отсортирован по тому, что раньше испортится. Соль, сахар, масло и специи помечены «всегда есть» и не покупаются никогда.

Осьминог, холодильник и 498 тестов: как я за 4 дня навайбкодил бота, который собирает мне корзину в Лавке - 3

Заказать продукты.
Бюджет, дни, люди, калории, какие приёмы пищи нужны и что можно повторять. Дальше бот:

  1. составляет меню с рецептами — сначала из того, что есть, скоропортящееся в первые дни;

  2. вычитает холодильник из списка покупок;

  3. ищет товары в Лавке и выбирает подходящие;

  4. показывает черновик: меню, товары, сумма, «🧊 Из холодильника»;

  5. по кнопке «🛒 Собрать корзину» кладёт всё в корзину Лавки и проверяет, что каждая позиция на месте.

Осьминог, холодильник и 498 тестов: как я за 4 дня навайбкодил бота, который собирает мне корзину в Лавке - 4

Шеф Осьма.
Тапаешь по осьминогу и пишешь «борщ» или «что-нибудь с курицей и рисом». Шеф выбирает блюдо, пишет рецепт и собирает корзину под него.

Осьминог, холодильник и 498 тестов: как я за 4 дня навайбкодил бота, который собирает мне корзину в Лавке - 5

После заказа — «📦 Привезли» добавляет купленное в холодильник, «🍳 Приготовлено» списывает продукты по дням меню. По желанию бот напоминает, что пора готовить.

Ботом пользуюсь я и семья. У каждого свой профиль, свой холодильник и свой аккаунт Лавки.

Чего бот не умеет — и не будет уметь

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

Как это устроено

Осьминог, холодильник и 498 тестов: как я за 4 дня навайбкодил бота, который собирает мне корзину в Лавке - 6

Сначала был только MCP-сервер — 15 инструментов для ИИ-агента: адреса, поиск, карточка товара с КБЖУ, корзина, холодильник, меню. Я писал в чате агента «собери ужины на 3 дня» — и агент ходил в Лавку сам. Работало, но для семьи «открой терминал и поговори с агентом» — так себе интерфейс. Поэтому появился бот, а MCP остался для меня.

Оба ходят в одно ядро. Ядро держится на одном принципе, который я бы теперь ставил в любой проект с LLM:

Модель предлагает, код проверяет.

Модель придумывает блюда и выбирает товар из найденных кандидатов. Всё, что можно посчитать, считает код: ровно N дней, повторы только по флагу, калории в пределах ±15 % от цели, сумма к оплате не больше бюджета + 15 %, сколько упаковок взять, есть ли товар на складе. Нарушение модели исправляется кодом, а не «доверяем, она же умная».

В модель уходят названия, фасовки и цены товаров. Cookies и адреса — нет.

А где API Лавки?

Публичного API у Лавки нет, а официальный путь я не искал: делал только для себя. Сервер ходит в тот же веб-API, что и сайт lavka.yandex.ru, с моими сессионными cookies. Разбирать CSRF, гонки в корзине и её версии здесь не буду — это подробно сделал автор yandex-lavka-mcp в статье «Как я научил Claude заказывать продукты в Яндекс Лавке». Его эндпоинты и заголовки агент взял за основу — спасибо, — а живой трафик их подтвердил.

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

Как строил: 4 дня, 143 коммита

Осьминог, холодильник и 498 тестов: как я за 4 дня навайбкодил бота, который собирает мне корзину в Лавке - 7

Итог: ~4,4 тыс. строк Python, ~1,3 тыс. строк HTML/JS в Mini App и 498 тестов — тестового кода почти столько же, сколько рабочего.

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

1. Сначала спека. Агент задаёт вопросы по одному: зачем, для кого, что считаем успехом, что точно не делаем. Из ответов получается спека на пару страниц. Для холодильника, например, я сам решил: холодильник у каждого свой, сроки годности в первой версии, списание — ручной кнопкой, без «умного» автосписания.

2. Потом план. Спека разбивается на небольшие задачи, у каждой — файлы, тесты и «что значит готово».

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

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

5. STATE.md — эстафетная палочка. Над проектом работали несколько агентов на разных моделях: дешёвая — на рутину, дорогая — на проектирование и ревью. Память между ними — один файл: что сделано, что дальше, какие ловушки. Каждая сессия начинается с его чтения и заканчивается его обновлением. Вот кусок оттуда:

## Ловушки (не повторяй)

- **Адрес определяет даркстор.** Перед сборкой сверь цены контрольного товара
  с приложением: расхождение в пару рублей = другой склад, и наличие там другое.
- **Поиск плохо понимает «свежие»:** на «огурцы свежие» первым идёт малосольное,
  на «помидоры» — черри. Фильтруй по словам в названии и фасовке.
- **Проверяй факт после добавления:** add_to_cart → пауза 2 с → get_cart;
  позиция должна быть на месте и без флага. Иначе Лавка её молча выбросит.

Без этого файла каждый новый агент наступал бы на те же грабли. С ним — наступает на новые.

Грабли

Самое интересное. Все 498 тестов работают на моках. Моки — это то, как агент представляет себе API. Реальность оказалась изобретательнее.

1. Адрес = склад

Первая собранная корзина выглядела идеально. Я открыл приложение — а там половина позиций «Раскупили»: лук, куриная грудка, морковь, огурец, творог. При этом API честно отвечал «в наличии» и спокойно принимал их в корзину.

Оказалось, у Лавки нет «склада вообще». Есть даркстор, и он определяется адресом. Агент взял первый адрес из избранного, а в приложении у меня был выбран другой. Разные склады — разные цены и разные остатки. Похожую ловушку описывал автор yandex-lavka-mcp, но у него товары подмешивались из «Большой Лавки», а у меня из-за адреса на чужой склад уехала вся корзина.

Осьминог, холодильник и 498 тестов: как я за 4 дня навайбкодил бота, который собирает мне корзину в Лавке - 8

Лечение:

  • адрес и идентификатор места всегда меняются вместе: сменил адрес — старый контекст склада стирается;

  • наличие — только по карточке товара: флаг available в поиске каталожный, ему верить нельзя;

  • после добавления — пауза 2 секунды и перечитать корзину: позиция должна быть на месте и без пометки «нет на складе»;

  • проверка «тот ли склад» — цена контрольного товара (рис, гречка) должна совпасть с приложением до рубля.

Моки этого не поймали бы никогда: в моке склад один.

2. Cookies из браузера с сюрпризом

Чтобы не копировать cookies руками, агент написал выгрузку прямо из профиля Chromium: расшифровка через DPAPI Windows и AES-GCM. Не работало. В новых версиях схемы базы Chromium приписывает к значению 32 байта SHA-256 от домена. Без их отрезания httpx падал на не-ASCII байтах в заголовке.

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

Для бота сделал проще и безопаснее. Человек открывает из бота страницу, вставляет строку Cookie из DevTools, и она уходит по HTTPS прямо на сервер, мимо истории чата. Кто её прислал, сервер проверяет подписью Telegram:

def verify_init_data(init_data: str, bot_token: str, *, max_age: int = 3600) -> int:
    """Проверить подпись Telegram Web App и вернуть Telegram ID пользователя."""
    pairs = dict(parse_qsl(init_data or "", keep_blank_values=True))
    received = pairs.pop("hash", "")
    check = "n".join(f"{k}={v}" for k, v in sorted(pairs.items()))
    secret = hmac.new(b"WebAppData", bot_token.encode(), hashlib.sha256).digest()
    expected = hmac.new(secret, check.encode(), hashlib.sha256).hexdigest()
    if not received or not hmac.compare_digest(expected, received):
        raise InitDataError("подпись не сошлась")
    ...

3. Агент запускал вчерашний сервер

Агент добавил 15-й инструмент, прогнал тесты — зелёные. Запускаю сервер — инструментов 14. Дважды.

Пакет ставился в .venv не в режиме разработки: сборщик клал копию server.py в site-packages, и uv run пересобирал её не всегда. Лечится только явной переустановкой пакета. Теперь это записано в правилах проекта, которые агент читает в начале каждой сессии.

4. Модель думает, пока меню остывает

Модель для меню по умолчанию «рассуждает» перед ответом. Замер показал: ~95 % токенов ответа уходит на рассуждения, и меню на неделю с рецептами не укладывалось в 120 секунд. Для задач «верни JSON по схеме» рассуждения выключил, и меню стало приходить за разумное время.

5. Один и тот же обед пять дней подряд

Прогон «5 дней, двое, 5000 ₽» вернул меню на ~1000 ккал на человека в день из 6 продуктов. Обед все пять дней одинаковый. Формально бюджет соблюдён, придраться не к чему.

Я дал модели приоритеты: калорийность → бюджет → разнообразие → баланс БЖУ. А главное — код проверяет первые три сам: калории ±15 % от цели, обед не повторяется без флага «повторять», обед ≠ ужин в один день, один белок — не больше половины обедов и ужинов. Не прошло — меню уходит модели на правку с конкретной претензией. Баланс БЖУ остался мягким ориентиром: если душить модель всеми ограничениями сразу, она не укладывается ни в одно.

6. Три пачки лука

Черновик на двоих: три упаковки лука и три — говядины. Арифметика была верной. Модель положила в меню 950 г лука, а код округлял упаковки строго вверх: нужно 900 г говядины, упаковка 400 г — значит, 3.

Два фикса. В промпте — «лук и морковь 30–40 г на человека в блюдо». В коде — допуск: если до целой упаковки не хватает не больше 15 % веса, лишнюю не берём. 900 г из 2 × 400 — это недобор 11 %, переживём.

Соседняя проблема: у Лавки есть лимит на количество одного товара в корзине. Меню хотело 2,2 кг голени, а Лавка давала одну упаковку на 750 г. Теперь такой продукт возвращается в правку меню как «не хватает», а не теряется молча.

7. «Творог» — это не «творог 5 %»

В холодильнике лежал «Творог», в меню был «творог 5%». Бот не узнал творог и купил ещё. Сопоставление по словам вместо точного совпадения решило проблему, а тест закрепил.

Почему я не выкладываю код

Логичный вопрос: «А можно себе?» Кода в открытом доступе нет. Причины:

  1. Неофициальный API. Это автоматизация через приватный веб-API с пользовательскими cookies. Как к этому отнесётся Яндекс, решать не мне — и не вам за меня

  2. Cookies = весь аккаунт Яндекса. Готовый бот, поднятый «по инструкции из интернета», с первого же дня хранит самое ценное, что есть у пользователя. Сделать это действительно безопасно сложно

  3. Ответственность. Если чужая копия утечёт, кто-то лишится аккаунта или получит корзину не на тот склад, отвечать я не хочу

Если вы хотите сделать такое для себя, то всё нужное есть в этой статье: принцип «модель предлагает, код проверяет», процесс спека → план → TDD → ревью и список граблей. Агент соберёт остальное за пару вечеров — и вы будете понимать каждую строку, потому что сами принимали каждое решение.

Что я вынес про вайбкодинг

  1. Процесс важнее модели. Спека, план, тесты и ревью делают из «агент что-то написал» предсказуемую работу. Без них 143 коммита за 4 дня превратились бы в 143 коммита хаоса.

  2. Модель предлагает, код проверяет. Всё, что можно посчитать, — считает код. Калории, бюджет, упаковки, наличие. LLM — для того, что посчитать нельзя: придумать ужин.

  3. Моки — это фантазия агента об API. 9 дефектов из первого живого прогона не поймал бы ни один тест. Живой прогон нужен как можно раньше.

  4. Память агента — это файл. STATE.md с разделом «Ловушки» сэкономил мне больше времени, чем любая модель.

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

  6. Решения остаются за человеком. «Не тратить деньги», «ссылки только на Лавку», «списание руками, без автосписания» — это мои решения. Агент их исполняет и охраняет тестами, но не принимает.

Дальше планирую добавить Ленту, Перекресток и Пятерочку. Ведь эра персональных цен возвращается, а платить больше я не хочу.

Автор: karkazi

Источник