Приложение для учёта личных финансов, которое я спроектировал и разработал сам: Flutter-приложение, backend на Fastify и AI-функции на Gemini — голос в транзакцию, чат-ассистент по финансам и голосовой помощник для форм приложения.
Моя роль
Самостоятельный продукт: идея, дизайн, Flutter-приложение, backend, интеграция AI и деплой.
Стек
Flutter
Node.js
Fastify
PostgreSQL
Redis
Gemini API
Задача
Вводить расходы вручную утомительно, семьи хотят вести общий бюджет с нескольких телефонов, а приложение должно работать офлайн. Для этого нужна надёжная синхронизация на backend и AI, который превращает свободную речь на нескольких языках в корректные структурированные данные.
Архитектура
Flutter-приложение хранит данные локально и синхронизируется через Fastify API на PostgreSQL: один запрос отправляет локальные изменения с их версиями и забирает всё, что появилось после курсора клиента. AI-запросы идут через backend: он хранит ключ Gemini, проверяет каждый ответ модели по каталогу и схеме приложения, стримит ответы чата через SSE и ограничивает частоту запросов к каждой AI-функции на пользователя в Redis. Развёрнуто с Docker и PM2 за nginx на AWS EC2.
Ключевые решения
01Оптимистичная конкурентность и глобальный курсор изменений
Почему
У каждой записи есть версия; изменение, основанное на устаревшей версии, возвращается как конфликт, который пользователь решает в приложении. Глобальная последовательность упорядочивает все изменения, а строка группы блокируется на время синхронизации — ни одно устройство не пропустит изменение.
Компромисс
Синхронизация внутри одной семейной группы выполняется последовательно.
02Ответам модели не доверяем
Почему
Всё, что возвращает Gemini, проверяется по JSON-схеме и по собственным счетам и категориям пользователя. Некорректные части отбрасываются, а не роняют весь запрос.
03Устойчивые вызовы AI
Почему
При перегрузке модели backend повторяет запрос с паузами, а затем переключается на резервную модель, с таймаутом на каждый запрос — пользователь получает ответ, а не ошибку.
Компромисс
Резервная модель может отвечать чуть хуже основной.
04Качество AI измеряется, а не угадывается
Почему
Отложенная выборка из тысяч голосовых фраз на пяти языках с повторными прогонами для проверки стабильности показывает, действительно ли изменение промпта или модели помогает, ещё до релиза.
Что я сделал
Голос → транзакция: аудио и каталог пользователя на входе, черновик в JSON по схеме на выходе
Чат-ассистент со стримингом через SSE; голосовой помощник, заполняющий около 20 форм приложения
Офлайн-синхронизация семейных групп: версии записей, лента изменений по курсору, блокировка группы
Вход через Google с отзываемыми JWT, удаление аккаунта по требованиям Google Play и автоочистка данных
Набор оценки LLM: около 7 000 кейсов на язык для 5 языков и проверка контракта приложение↔сервер