Премиум
AI Chat
Локальный AI ассистент для транзакций, кошельков, категорий и аналитики.
Обзор
В Finapp встроен локальный AI ассистент на Ollama и @tanstack/ai. Чат живёт в slide-over панели (AiChatPanel.vue) и предоставляет ~24 tool'а, оборачивающих мутации и запросы Pinia-сторов. Данные не покидают машину пользователя.
Точки входа
- FAB-кнопка (
AiFab.vue) - плавающая кнопка на всех авторизованных страницах - Горячая клавиша
Cmd/Ctrl+I(см.shortcuts.ts) - Slide-over панель справа,
sm:max-w-md
Архитектура
User input → AiChatPanel
├── useAiChat.send(text)
│ ├── pruneContext(messages) - скользящее окно + сжатие старых tool results
│ ├── chat() через @tanstack/ai + адаптер Ollama
│ ├── стрим TOOL_CALL_START / TOOL_CALL_ARGS / TOOL_CALL_END / TEXT_MESSAGE_CONTENT
│ ├── autoRetry - детект неудачных мутаций + незавершённых chain
│ ├── client-side fallback - клиент напрямую исполняет мутации если модель тупит
│ ├── toast - визуальный фидбек для мутаций
│ └── lastAction tracker - для undo
├── AiToolCallsList.vue - rich cards для каждого tool
└── AiMessageContent.vue - markdown-текст ассистента
Каталог tools
Все tools живут в app/components/ai/tools/ и регистрируются через createAllTools().
Transactions
create_trn,create_adjustment,create_transferupdate_trn,delete_trn,duplicate_trn,bulk_delete_trnslist_trns,search_trns,undo_last_action
Wallets
create_wallet,update_wallet,delete_wallet,list_walletsarchive_wallet,unarchive_wallet,set_wallet_exclude_in_total,reorder_wallets
Categories
create_category,update_category,delete_category,list_categories
Analytics
get_summary,get_wallet_stats,compare_periods
Settings
set_base_currency,set_locale,get_settings,set_theme
Системный промпт
В useSystemPrompt.ts. Ключевые правила:
- Tool-first: любой фактический вопрос о кошельках/категориях/транзакциях ВСЕГДА через tool, не из памяти
- Multi-turn: если не хватает полей - задать ОДИН уточняющий вопрос; на следующем turn смержить данные
- Chain-паттерн:
delete_trn/update_trn/duplicate_trnтребуют сначалаlist_trnsдля получения id - FORBIDDEN: выдумывать id, просить у пользователя id, переиспользовать старые tool results
- DATE RULE: не передавать
dateесли пользователь не сказал явно
Сжатие контекста
pruneContext.ts - скользящее окно + сжатие tool results:
maxContextTurns(default 6) - в модель идут только последние N пар user+assistantcompressToolsAfterTurns(default 1) - старые tool_calls сжимаются в строку[tool_name: summary]
Это убирает "я уже сделал X" галлюцинации в длинных сессиях.
AutoRetry + client fallback
useAiChat.ts:
- autoRetry - если tool вернул error /
ok: false, отправить retry с реальным id изlist_trns - executeFallback - если модель всё равно тупит на chain'ах, клиент САМ выполняет мутацию и инжектирует результат как синтетический tool_call
Auto-clear при простое
idleMinutesBeforeClear (default 30) - если чат был неактивен N минут, история очищается на следующем send().
Рекомендации моделей
| Модель | Скорость | Tools | Заметки |
|---|---|---|---|
| gemma4:latest | fast | excellent | По умолчанию. Баланс скорость/точность |
| gemma4:26b | medium | good | Для RAM-богатых, выше точность |
| qwen3:8b | fast | excellent | Альтернатива с похожими характеристиками |
См. modelMeta.ts.
Настройки
Все настройки в aiStore.settings:
model,baseUrl,think,autoRetry,debugmaxContextTurns,compressToolsAfterTurns,idleMinutesBeforeClear
Доступны через popover в шапке чата.
Известные ограничения
- Multi-turn
create_trnзависит от модели. Наgemma4:latestсборка транзакции через уточнения (юзер: "купил кофе" → ассистент спрашивает сумму/кошелёк → вызываетcreate_trn) работает примерно в 70% попыток. Модель иногда кладёт ответ не в то поле (например "наличными" вdescriptionвместоwallet) или забываетtype. Для надёжного multi-turn -qwen3:8bили давать все поля сразу. - Chain-tools (
duplicate_trn,delete_trn,update_trn) наgemma4:latestв длинных сессиях - модель иногда отвечает текстом и просит id вместоlist_trns→ мутация. Это компенсировано client-side fallback'ом (executeFallbackвuseAiChat.ts), который напрямую исполняет мутацию и инжектирует синтетический tool_call. - Галлюцинация "я уже это сделал" может вернуться при большом
maxContextTurns. DefaultcompressToolsAfterTurns: 1сжимает старые результаты для предотвращения; выше 8-10 turns риск возвращается. - Зависимость от локальной сети. Если Ollama не запущена на
baseUrl, чат не работает. Remote fallback отсутствует по дизайну - всё inference локально. - Validation errors из
@tanstack/aiприходят сstate: 'done'(неerror) и{ error: "..." }в результате.autoRetryраспознаёт их через helperisTcFailedпо JSON результата. - Нет массовой перестановки категорий -
useCategoriesStoreне предоставляетsaveCategoriesOrder, AI не может менять порядок категорий.
Добавление нового tool
- Создать
app/components/ai/tools/<name>.tsсcreate*Tools() - Использовать
toolDefinition({ description, inputSchema, name })из@tanstack/ai - Вернуть полную форму результата (с
ok: trueна успех) - Зарегистрировать в
tools/index.ts - Добавить rich card в
AiToolCallsList.richKind() - Обновить
useSystemPrompt.tsправилом - Добавить bench case в
/tmp/ai-bench.mjs