Вайбкодинг — это не только трекеры финансов и калорий. React Native.. React Native. вайб-кодинг.. React Native. вайб-кодинг. Программирование.

3,5 месяца, 2 071 коммит, своя пекарня, два ИИ-ассистента и VPS с 2 ГБ RAM


У нас своя небольшая пекарня «D&K Sourdough». Заказы на хлеб долгое время принимались вручную — клиент пишет в Telegram‑группу, мы записываем, следим за тем, чтобы не набрать больше, чем сможем отпечь, потом вручную уведомляем по готовности. Хотелось это автоматизировать. У меня самого есть небольшой опыт в программировании — делал несколько приложений для себя на Delphi в лохматых 2000-х, писал Access приложения, даже напрограммировал систему автополива для теплицы на Arduino, которая работает вот уже 7 лет. И тут, узнав каких способностей уже достигли LLM, 19 января сделал initial commit: один файл bot.py, SQLite, три кнопки. Меню, корзина, клиенты делают заказ, мы получаем оповещение. Сначала все писал с чатом Deepseek и виндовым Copilotom, но потом узнал про агентов и все заверте…

Сейчас это ~91 000 строк Python, 143 React‑компонента, схема БД версии v105 и два работающих ИИ‑ассистента — всё на том же VPS с 2 ГБ RAM.


Неделя первая: система слотов

На следующий день после initial commit выяснилась первая нетривиальная вещь: ёмкость печи ограничена. Большой хлеб занимает 2 слота, маленький — 1, максимум 20 слотов на дату. Если 12 человек возьмут большой — ни печь ни мы не справимся физически.

Нужна oven_limits таблица с max_slots и used_slots, причём бронирование должно быть атомарным — иначе два человека могут одновременно взять последний слот, оба получат подтверждение и будут ждать хлеб, которого не будет.

Это первая конкурентная задача в проекте, и она сразу заставила думать о транзакциях.

За неделю бот обзавёлся корзиной с мульти-заказом, регистрацией пользователей, отдельной admin-панелью со сменой статусов заказов, уведомлениями в Telegram-группу («осталось 2 слота!», «всё разобрали!») и автовотчером для перезапуска при изменении файлов. Всё это был один класс BreadOrderBot на 1500 строк, но работало.


Февраль: из бота в продукт

REST API + React PWA

В начале февраля стало понятно, что Telegram — не единственный нужный интерфейс. Клиенты хотят видеть каталог хлеба как нормальный сайт, оформлять заказ без Telegram. Началась параллельная разработка: FastAPI поверх той же SQLite-базы и React 18.3 + TypeScript 5.6 + Vite 6.0 + Tailwind — фронтенд. JWT сначала в localStorage, потом аудит безопасности сказал «нет» — переехало в httpOnly cookie.

Деплоить vite build на сервер с 2 ГБ RAM было нельзя — OOM при активном боте и API. Выработали правило: всегда билдить локально, деплоить через scp. Правило соблюдается до сих пор.

cd Copilot/frontend && npm run build
scp -r dist/* root@server:/root/bread_bot/frontend/dist/
ssh root@server "systemctl restart bread-api"

PWA получила: каталог хлеба с фотографиями, корзину, историю заказов, личный кабинет, Web Push уведомления (VAPID), fullscreen-лайтбокс для фото хлеба, splash screen с реальным progress bar.

Аналитика и учёт затрат

Захотелось наконец понять, зарабатывает ли пекарня деньги или просто занята. Добавили модуль учёта затрат: FIFO-инвентаризация ингредиентов (закупки, расход, остатки), техкарты с рецептами, производственные партии от замеса до выпечки с разбивкой себестоимости на материалы + косвенные + амортизация, оборудование с линейной амортизацией, несколько кассовых счетов с переводами, кредиты с аннуитетными графиками.

Схема БД к концу февраля: v41 → v67. Каждая миграция — Python-функция migrationvNN().

Аудит безопасности

Когда появился полноценный API, провели первый аудит — 170 находок, 14 критических. Самые важные:

  • Race condition в бронировании слотов — два пользователя одновременно берут последний слот. Решение: BEGIN IMMEDIATE + db_transaction() с передачей conn= в sub-функции. Паттерн save_order_atomic() стал эталоном для всех похожих операций.

  • 27 мест с date.today() вместо today_local() — пекарня в UTC+11, сервер в UTC, разница 11 часов. В полночь по серверному времени ещё утро по сахалинскому, и заказы падали не в ту дату.

  • TOCTOU в инвентаризации — чтение остатка и его списание должны быть в одной транзакции.

  • SQL injection через f-string в названиях таблиц — исправлено через whitelist.


Март: плагины и телефонная авторизация

Плагиновая архитектура

К марту бот разросся до 10 миксинов в BreadOrderBot и 6 в AdminPanel. Добавление новой фичи требовало редактировать несколько файлов одновременно. Сделали plugin system:

class Plugin(ABC):
    @property
    @abstractmethod
    def name(self) -> str: ...

    def register_handlers(self, dp, bot_context): ...
    def register_admin_routes(self) -> dict: ...
    def register_admin_menu_items(self) -> list: ...
    def register_admin_text_handlers(self) -> dict: ...
    def on_startup(self, bot_context): ...
    def on_shutdown(self): ...

Сейчас активных плагинов 6: аналитика, отзывы, управление затратами, исторические заказы, инструменты разработчика, сообщения в группу.

Авторизация

OTP через Telegram-бот (6 цифр, TTL 5 минут), Telegram Login Widget для PWA, обычный пароль через bcrypt, rate limiting с lockout (5 попыток → блокировка на 5 минут).

Активность и когорты

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


Апрель: три ИИ-ассистента

Голосовой ассистент для администратора

Голосом текст набирается в три раза быстрее, чем печатанием. Хотелось голосом узнать статус заказов или изменить лимит печи. Пайплайн получился такой:

Голосовое сообщение (OGG) → Groq Whisper Large v3 Turbo (STT, ru)
    → Claude Haiku (intent parse с tool_use + снимок БД)
    → Кнопка «Да/Нет»
    → Атомарное выполнение в БД

Конфигурация минимальна — два API-ключа в .envGROQ_API_KEY и ANTHROPIC_API_KEY.

Главная инженерная проблема: сервер с 2 ГБ RAM. Claude CLI-бинарник весит ~600 МБ и вызывает OOM при запуске рядом с работающими ботом и API. Решение: вместо CLI — Anthropic Python SDK напрямую, без лишнего процесса, прямо на VPS.

Василиса — клиентский чат-ассистент

Чат-бот «Василиса» в PWA для клиентов. Знает весь каталог хлеба с КБЖУ, ингредиентами и описаниями, видит доступные даты и остатки слотов, знает историю заказов конкретного клиента, может предложить хлеб в корзину через специальный маркер CART_PROPOSAL:{...}.

VASSILISA_PERSONA = (
    "Ты Василиса — дружелюбный ассистент пекарни D&K Sourdough. 🍞n"
    "Характер: говоришь как человек, которая сама влюблена в этот хлеб — тепло, "
    "с искренним интересом, иногда с юмором. Не как справочник, а как друг-пекарь.n"
    "Пол: ты девушка. ВСЕГДА говори о себе в женском роде."
)

Василиса ведёт долгосрочную память клиентов (customer_ai_memory_repo.py) — сводка по каждому пользователю, которая обновляется автоматически. Если клиент писал три недели назад и упоминал, что любит ржаной с тмином — Василиса это помнит.

Самое тонкое место — не вызов API, а системный промпт: 200 строк ограничений, которые предотвращают выдачу CART_PROPOSAL без явного называния хлеба, добавление в корзину на первом сообщении сессии, раскрытие себестоимости, prompt injection через текст меню.

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

Стив — проактивный AI-агент

Стив – мой личный помощник в Телеграм. Работает по расписанию cron и сам инициирует действия. Не отвечает на вопросы — наблюдает и докладывает.

0 22 * * *   python3 steve_proactive.py morning      # Утренний брифинг
0 10 * * *   python3 steve_proactive.py evening      # Вечерняя сводка
0 23 * * 0   python3 steve_proactive.py weekly       # Еженедельный аудит
0 19 1  * *  python3 steve_proactive.py monthly      # Месячная рефлексия
0 20,2,9 * * python3 steve_proactive.py code_review  # Аудит кода 3 раза в день

Утром присылает сколько заказов, выручку и остатки слотов. Еженедельно анализирует тренды и сравнивает с прошлой неделей. Code review ищет bare except, прямые os.getenv(), большие файлы — присылает отчёт в Telegram. Раз в месяц анализирует рост продаж и предлагает изменения в ассортименте.

У Стива есть настроение, которое рассчитывается из сигналов бизнеса (растут продажи — energized, падают — concerned) и инжектируется в каждый промпт:

MOOD_TONES = {
    "energized": "Ты полон энергии, наблюдения острее, язык живее",
    "calm":      "Ты спокоен и методичен, фокус на деталях",
    "concerned": "Ты обеспокоен — что-то идёт не так, нужна честность",
    "curious":   "Ты любопытен, хочется разобраться глубже",
}

Стив ведёт журнал — записывает мысли после каждого сеанса. У него есть убеждения (beliefs), которые формируются из наблюдений и могут быть подтверждены или опровергнуты реальными данными. Есть DeepSeek fallback: если Anthropic отдаёт rate limit, запросы автоматически переходят на DeepSeek API с совместимым интерфейсом.

В апреле добавили вкладку AdminSteve в React-приложении — можно видеть записи в журнале, текущее настроение, убеждения и предложения по развитию характера.


Май: тюнинг

Работа над UX каталога: новая тема «Cinematic» разработанная в Claude Design — полноэкранный hero с анимированным паром над хлебом (фича еле видная, но пусть будет), cinematic ticker с реальными названиями из каталога. Активно пробуем DeepSeek v4 Pro Max в качестве агента и все выглядит очень многообещающе.


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

Стек

Слой

Технология

Telegram-бот

Python, python-telegram-bot 13.15 (sync), SQLite WAL

API

FastAPI, Pydantic v2, Uvicorn

БД

SQLite, 67 репозиторий-модулей, v105 схема

Фронтенд

React 18.3, TypeScript 5.6, Vite 6.0, Tailwind CSS 3.4

PWA

Service Worker, Web Push (VAPID, pywebpush)

ИИ

Claude Sonnet/Haiku (Anthropic), DeepSeek (fallback), Groq Whisper (STT)

Сервер

VPS, 2 ГБ RAM, Python 3.8.10, systemd, Nginx

Авторизация

JWT (httpOnly cookie), Telegram OTP, Telegram Login Widget

Цифры

Метрика

Значение

Первый коммит

19 января 2026

Всего коммитов

2 071

Python файлов (без venv)

301

Строк Python-кода

~91 000

React-компонентов

143

API-роутеров

31

Модулей БД

67

Версия схемы БД

v105

Тестов

628+

Активных плагинов

6

ИИ-ассистентов

2 (Стив – мой личный помощник, Василиса работает с клиентами)

Время разработки

~3,5 месяца

Архитектура БД

67 отдельных репозиторий-модулей, каждый отвечает за свою область:

db/
├── order_repo.py               # Заказы
├── bread_repo.py               # Каталог хлеба
├── oven_repo.py                # Слоты печи
├── inventory_*.py              # FIFO-инвентаризация (7 модулей)
├── analytics_*.py              # Аналитика (6 модулей)
├── account_repo.py             # Кассовые счета
├── loan_repo.py                # Кредиты
├── production_repo.py          # Производственные партии
├── steve_repo.py               # «Душа» Стива
├── customer_ai_memory_repo.py  # Память Василисы
├── voice_memory_repo.py        # Контекст голосового ИИ
└── ... ещё 55 модулей

Атомарность критических операций — через db_transaction() с поддержкой вложенных транзакций через SQLite SAVEPOINT.

Архитектура бота

Бот построен на mixin-архитектуре: 10 миксинов в BreadOrderBot, 6 в AdminPanel. MRO Python делает всё остальное — методы одного миксина спокойно вызывают self.send() из другого. Плюс plugin system с 9 lifecycle hooks.


Что в продакшне

Сервер: VPS 46.17.106.35, 2 ГБ RAM. Два systemd-сервиса: bread-bot.service (Telegram) и bread-api.service (FastAPI + статика React).

Автоматические бэкапы БД каждую ночь: WAL checkpoint + gzip + email. Стив запускается через cron три раза в день для code review, утром и вечером для сводок. Его мысли приходят прямо в Telegram.


Несколько вещей, которые стали очевидны в процессе

SQLite нормально справляется с нагрузкой небольшого бизнеса. При правильном использовании — WAL mode, BEGIN IMMEDIATE для конкурентных записей — 67 модулей, 105 миграций, FIFO, аналитика работают на одном .db файле без проблем.

105 версий схемы за 3,5 месяца — это процесс, а не проблема. Каждая новая фича начинается с migrationvNN(). Откат в крайнем случае — через резервную копию.

ИИ на продакшне с ограниченными ресурсами требует осторожности. 2 ГБ RAM означает: никаких heavy-weight процессов рядом с лёгкими. Claude CLI (600 МБ) + бот + API = OOM. Anthropic Python SDK напрямую + DeepSeek fallback — нормально работает. Groq Whisper без GPU — просто HTTP-запрос.

Race condition — самый неочевидный баг. Не OOM и не падение, а два пользователя, которые одновременно берут последний слот и оба получают подтверждение. BEGIN DEFERRED + два отдельных соединения != транзакция. BEGIN IMMEDIATE + один conn=, пробрасываемый в sub-функции, — транзакция.

Инварианты кодовой базы важнее красоты. «Добавим эту фичу прямо сейчас» — нормально, но только если следовать правилу: один способ открыть соединение с БД, одна конфигурация, один способ получить текущее время в правильном часовом поясе. Когда эти инварианты начинают нарушаться — хаос нарастает быстро.

Автор: ZdenniZ

Источник