Перейти к содержимому
Все проекты

Мобильный продукт · AI · Синхронизация

Finlens — приложение для личных финансов

Приложение для учёта личных финансов, которое я спроектировал и разработал сам: Flutter-приложение, backend на Fastify и AI-функции на Gemini — голос в транзакцию, чат-ассистент по финансам и голосовой помощник для форм приложения.

Моя роль
Самостоятельный продукт: идея, дизайн, Flutter-приложение, backend, интеграция AI и деплой.
Стек
  • Flutter
  • Node.js
  • Fastify
  • PostgreSQL
  • Redis
  • Gemini API

Задача

Вводить расходы вручную утомительно, семьи хотят вести общий бюджет с нескольких телефонов, а приложение должно работать офлайн. Для этого нужна надёжная синхронизация на backend и AI, который превращает свободную речь на нескольких языках в корректные структурированные данные.

Архитектура

Finlens — приложение для личных финансов — Схема архитектурыFlutter appoffline-firstFastify APIsync · auth · AIRedisrate limitsPostgreSQLrecords · changesGoogle Sign-Inid tokensGeminiJSON · SSEEval suite5 languages
Flutter-приложение хранит данные локально и синхронизируется через Fastify API на PostgreSQL: один запрос отправляет локальные изменения с их версиями и забирает всё, что появилось после курсора клиента. AI-запросы идут через backend: он хранит ключ Gemini, проверяет каждый ответ модели по каталогу и схеме приложения, стримит ответы чата через SSE и ограничивает частоту запросов к каждой AI-функции на пользователя в Redis. Развёрнуто с Docker и PM2 за nginx на AWS EC2.

Ключевые решения

  1. 01Оптимистичная конкурентность и глобальный курсор изменений

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

    Почему
    Всё, что возвращает Gemini, проверяется по JSON-схеме и по собственным счетам и категориям пользователя. Некорректные части отбрасываются, а не роняют весь запрос.
  3. 03Устойчивые вызовы AI

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

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

Что я сделал

  • Голос → транзакция: аудио и каталог пользователя на входе, черновик в JSON по схеме на выходе
  • Чат-ассистент со стримингом через SSE; голосовой помощник, заполняющий около 20 форм приложения
  • Офлайн-синхронизация семейных групп: версии записей, лента изменений по курсору, блокировка группы
  • Вход через Google с отзываемыми JWT, удаление аккаунта по требованиям Google Play и автоочистка данных
  • Набор оценки LLM: около 7 000 кейсов на язык для 5 языков и проверка контракта приложение↔сервер
  • 254 теста backend на PostgreSQL в памяти (PGlite)

В цифрах

кейсов оценки распознавания голоса на язык
~7,000
языков в наборе оценки AI
5
автотестов backend
254