25 KiB
Архитектура режимов Talkis: Облако, API и Локально
Статус: актуальное описание реализации на 17 июля 2026 года.
Этот документ отделяет техническую логику Talkis от пользовательского и маркетингового описания. Его можно использовать как основу для архитектурного обсуждения, ревью новых функций и проверки того, что один и тот же сценарий одинаково ведёт себя в режимах Облако, API и Локально.
1. Главный принцип
Talkis имеет единый интерфейс и общие пользовательские сценарии, но три разных способа выполнения вычислений:
| Режим | Где выполняется обработка | Учётные данные | Основное ограничение |
|---|---|---|---|
| Облако | Через сервисы Talkis Cloud и proxy.talkis.ru |
deviceToken после входа в Talkis |
Нужны интернет и активная облачная возможность |
| API | Напрямую через настроенные пользователем endpoints | API-ключи пользователя | Realtime работает только для проверенных конфигураций |
| Локально | В управляемых sidecar-процессах или другом localhost endpoint | Обычно не нужны | Синхронный перевод системного звука недоступен |
Режим определяет маршрут данных, стоимость, приватность и доступность функций. Talkis не должен незаметно переключать пользователя между режимами: такой fallback мог бы изменить место обработки данных или привести к непредвиденным расходам.
2. Источник истины активного режима
Активный режим вычисляется из сохранённых настроек:
useOwnKey = false
-> Облако
useOwnKey = true + STT endpoint указывает на localhost/127.0.0.1
-> Локально
useOwnKey = true + удалённый или пустой STT endpoint
-> API
Переключение вкладки в интерфейсе моделей само по себе не меняет рабочий режим. Новый режим становится активным после явного действия пользователя «Выбрать»/«Использовать». После сохранения публикуется событие обновления настроек, а виджет использует новый snapshot при следующей операции.
Важно: STT и текстовая LLM настраиваются отдельно. Например, в API-режиме распознавание может идти в один endpoint, а очистка текста — в другой. В локальном режиме управляемый STT и локальная LLM также являются разными runtime-процессами.
3. Слои приложения
flowchart TD
UI[React UI: Widget, Settings, Text Overlay]
TS[TypeScript orchestration and domain services]
RUST[Tauri/Rust commands]
CLOUD[Talkis Cloud proxy]
API[Configured provider APIs]
LOCAL[Managed local runtimes]
OS[OS audio, clipboard, paste, permissions]
HISTORY[Local history and audio files]
UI --> TS
TS --> RUST
TS --> CLOUD
RUST --> CLOUD
RUST --> API
RUST --> LOCAL
RUST --> OS
TS --> HISTORY
RUST --> HISTORY
3.1 React/WebView
Widgetпринимает горячие клавиши, управляет записью, синхронным переводом и состояниями плавающих окон.Settingsхранит настройки моделей, разрешений, перевода, файлов и истории.WidgetTextOverlayпоказывает потоковый и финальный текст независимо от основного окна виджета.
3.2 TypeScript orchestration
TypeScript выбирает сценарий и backend по snapshot настроек, управляет отменой, объединяет partial/final результаты и обновляет локальную историю. Здесь находятся маршрутизация диктовки, файлов, перевода выделенного текста, summary и поиска по истории.
3.3 Tauri/Rust
Rust отвечает за нативную запись звука, системный audio capture, media preparation, вызовы STT/Realtime, управление sidecar-процессами, вставку текста и файловое хранение истории.
3.4 Управляемые локальные процессы
talkis-stt: единый OpenAI-совместимыйtranscribe.cppruntime для Whisper, GigaAM, Qwen ASR и NVIDIA Parakeet; базовый endpointhttp://127.0.0.1:8000.talkis-diarize: разделение речи по говорящим; базовый endpointhttp://127.0.0.1:8003.talkis-llm: OpenAI-совместимая GGUF LLM для summary и обработки текста; предпочтительный порт8011.talkis-ffmpeg: конвертация видео и неподдерживаемого аудио, подготовка diarization и чанкинг больших файлов.
Если заняты стандартные порты, Talkis выбирает fallback-порт и сохраняет фактический endpoint.
4. Общий жизненный цикл операции
Для диктовки, файла, перевода или summary используется одна базовая схема:
- Загрузить актуальный snapshot настроек.
- Проверить разрешения и конфигурацию выбранного режима.
- Получить вход: микрофон, системный звук, файл, выделенный текст или запись истории.
- Выбрать backend без скрытой смены режима.
- Запустить операцию с идентификатором сессии/запроса и возможностью отмены.
- Показывать промежуточное состояние, если backend поддерживает streaming.
- Получить финальный результат и при необходимости выполнить текстовую постобработку.
- Показать, вставить или сохранить результат в зависимости от сценария.
- Записать локальную историю и структурированный диагностический лог.
- При ошибке сохранить предыдущую рабочую конфигурацию и показать пользователю понятное действие для восстановления.
5. Техническая матрица возможностей
Обозначения: Да — штатно поддерживается; Условно — зависит от модели, capability или дополнительного локального компонента; Нет — намеренно недоступно.
| Возможность | Облако | API | Локально |
|---|---|---|---|
| Batch-диктовка | Да | Да | Да |
| Потоковая диктовка | Да | Условно: проверенная Realtime-конфигурация | Условно: модель с поддержкой streaming |
| Очистка/переформатирование текста | Да | Да, если настроена LLM | Да, если выбрана локальная LLM |
| Перевод диктовки перед вставкой | Да | Да, если настроена LLM | Да, если выбрана локальная LLM |
| Перевод выделенного текста | Да | Да | Да: локальный переводчик или LLM |
| Транскрибация аудио/видео | Да | Да | Да |
| Разделение файла по говорящим | Да, при доступной cloud capability | Условно: локальный STT + diarization | Да, при установленных STT + diarization |
| Запись созвона: микрофон + системный звук | Да | Да | Да |
| Live-текст созвона | Да | Условно: проверенная Realtime-конфигурация | Условно: streaming-модель |
| Синхронный перевод системного звука | Да | Условно: проверенный OpenAI/Gemini Realtime | Нет |
| Озвучка синхронного перевода | Условно: OpenAI-backed, macOS | Условно: OpenAI, macOS | Нет |
| Summary и пользовательские промпты | Да | Да, если настроена LLM | Да, если выбрана локальная LLM |
| Локальная история и аудиофайлы | Да | Да | Да |
| ИИ-поиск по истории | Экспериментально, dev-сборка | Экспериментально, dev-сборка | Экспериментально, dev-сборка |
6. Диктовка
6.1 Захват
Горячая клавиша/кнопка
-> useWidgetRecording
-> recordingRuntime
-> native_voice_recorder (WAV 16 kHz mono PCM16)
-> WebView MediaRecorder как fallback для выбранных deviceId
Нативный WAV-путь является основным: обычная диктовка не должна зависеть от ffmpeg. MediaRecorder остаётся fallback для устройств, которые WebView адресует надёжнее по deviceId.
6.2 Распознавание по режимам
- Облако: запрос с
deviceTokenотправляется в/api/transcribe; для streaming приложение получает краткоживущий Realtime credential. - API: Rust вызывает настроенный OpenAI-совместимый STT endpoint. Streaming включается только после проверки конкретного adapter/model/endpoint/key fingerprint.
- Локально: запрос идёт в управляемый localhost STT runtime. Streaming используется только моделями, для которых он явно поддержан.
Если потоковый канал недоступен, batch-распознавание остаётся основным финальным путём. Для некоторых realtime-моделей финальный batch может уточнить промежуточный текст.
6.3 Завершение
После STT Talkis может применить стиль, очистку или перевод через выбранный текстовый backend. Финальный текст вставляется в ранее активное приложение. Ошибка очистки не должна уничтожать уже полученную транскрипцию: допустим возврат исходного STT-текста с понятным статусом.
7. Текстовый backend
resolveSummaryBackend(settings) используется не только для summary. Он является общим маршрутизатором текстовой LLM для очистки, промптов, перевода диктовки и LLM-fallback перевода выделенного текста:
- Облачный режим с
deviceToken->proxy.talkis.ru/api/process-text. - Настроенный endpoint -> localhost считается локальным, другой адрес — custom API.
- Собственный API-ключ без endpoint -> стандартный OpenAI-совместимый путь.
- Нет модели или сохранён sentinel
none-> backend недоступен, управляющее действие блокируется с подсказкой выбрать модель.
Управляемый talkis-llm может автоматически перезапуститься только когда сохранён llmLocalModelId. Произвольные Ollama/LM Studio endpoints вызываются как внешние локальные серверы и не управляются Talkis.
Длинные тексты обрабатываются map-reduce чанками. Для локальных моделей применяются меньшие лимиты контекста.
8. Перевод
8.1 Выделенный текст
Отдельная глобальная горячая клавиша копирует выделение из активного приложения и запускает новый запрос. Новый запрос отменяет предыдущий, очищает старое состояние и сразу заменяет содержимое overlay. Финальный результат автоматически скрывается через десять секунд, если пользователь не закрыл его раньше.
Локальный режим сначала использует выбранный специализированный переводчик (NLLB-200 или OPUS для поддерживаемой языковой пары), затем может использовать локальную LLM. Облако и API используют общий текстовый backend.
8.2 Синхронный перевод
Системный звук (+ опционально микрофон)
-> native capture
-> отдельная Realtime-сессия на каждый канал
-> partial/final translated events
-> объединение соседних частей одного говорящего
-> text overlay + local history
-> optional translated voice playback on macOS
- Облако:
deviceTokenобменивается на краткоживущий credential. Постоянный ключ провайдера не попадает в desktop-приложение. - API: поддерживаются только успешно проверенные OpenAI/Gemini Realtime adapters.
- Локально: запуск блокируется до захвата аудио с явным сообщением о недоступности.
Системный звук переводится всегда. Микрофон можно включить как дополнительный канал перевода; при сохранении аудио он всё равно записывается отдельной дорожкой. Озвучка сейчас доступна для OpenAI-backed сессий на macOS, где Talkis исключает собственный процесс из захвата и может приглушить оригинал, чтобы избежать петли.
9. Файлы и говорящие
Файл до 8 ГБ
-> native path, без загрузки всего файла в WebView
-> probe / conversion при необходимости
-> chunking
-> STT
-> optional diarization
-> merge / history
- Готовый WAV
16 kHz mono PCM16может идти напрямую в локальный STT. - Видео, неизвестные форматы, большие файлы и diarization preparation используют
talkis-ffmpeg. - В облаке speaker mode использует облачную capability подписки.
- В локальном режиме используются локальные STT и diarization runtimes.
- В API-режиме speaker mode сейчас является гибридным: он требует локального STT/diarization комплекта, а не произвольной provider diarization API.
Первый найденный говорящий отображается как Вы — это детерминированное правило интерфейса, а не биометрическое распознавание голоса. Остальные получают имена Гость N; пользователь может свободно переименовать их, и новое имя применяется ко всем сегментам этого speaker ID.
10. Созвоны
Созвон записывается в две синхронизированные дорожки:
- микрофон —
Вы; - системный звук —
Созвон.
Нативный system-audio capture:
- macOS: Core Audio system/process tap;
- Windows: WASAPI loopback default output device;
- Linux: PipeWire monitor stream; без PipeWire сценарий завершается понятной ошибкой.
Запись появляется в истории сразу после запуска. Нативные WAV-дорожки и черновик текста фиксируются каждые пять секунд, поэтому после аварийного завершения Talkis восстанавливает доступное аудио и последнюю сохранённую версию текста.
Если текущая STT-модель поддерживает streaming, микрофон и системный звук распознаются параллельно и показываются во вкладке «Транскрибация» во время разговора. Сбой realtime-канала не останавливает запись. После остановки дорожки передаются в файловый pipeline, который уточняет черновик и сохраняет финальный результат. Batch-only модели, включая GigaAM, выполняют только этот финальный этап.
В истории дорожки воспроизводятся синхронно, а не как один заранее смешанный файл.
11. История и ИИ-поиск
История голоса, файлов, созвонов и синхронного перевода хранится локально в app data (history/history.json и связанные аудиофайлы). Выбранный режим обработки не меняет локальный характер истории.
Экспериментальный чат истории сейчас доступен только в dev-сборке. Его путь:
Запрос пользователя
-> локальный индекс истории
-> offline semantic templates + lexical ranking
-> optional embeddings при совместимом embedding backend
-> ограниченный контекст с источниками
-> выбранный Cloud/API/Local text backend
-> ответ со ссылками на записи
Offline-шаблоны покрывают не конкретные фразы запроса, а классы намерений: проблемы/баги, задачи, решения, идеи, обязательства и риски. Embeddings являются усилением поиска, а не обязательным условием работы.
12. Настройки, горячие клавиши и конкурентные запросы
- Настройки загружаются при старте и сохраняются после подтверждённого изменения.
- Горячие клавиши диктовки и перевода проходят одну транзакцию: захват полного аккорда -> нормализация -> проверка конфликта -> регистрация новой -> сохранение -> снятие старой.
- При ошибке новая комбинация откатывается, старая остаётся активной.
requestIdи target не позволяют позднему ответу предыдущего запроса изменить новое состояние.- Для пользовательских операций действует тот же принцип: новый перевод выделения или новый retry не должен ждать исчезновения старого overlay и не должен применять устаревший результат.
13. Данные, безопасность и диагностика
| Данные | Облако | API | Локально |
|---|---|---|---|
| Аудио/текст запроса | Передаётся через Talkis proxy | Передаётся настроенному провайдеру | Остаётся на устройстве |
| Provider key | Хранится на серверной стороне Talkis | Хранится локально в настройках | Обычно отсутствует |
deviceToken |
Хранится локально | Не используется | Не используется |
| История | Локально | Локально | Локально |
Секреты не должны попадать в UI events и логи. Диагностические логи должны содержать режим, endpoint без секрета, adapter/model, session/request ID, этап операции, длительность, chunk index/size, recorder stats и уровни call-capture.
Ошибки классифицируются как минимум на: отсутствие конфигурации, отсутствие разрешения, недоступный runtime/endpoint, timeout, provider authentication, capability unavailable, отмена и ошибка сохранения. Сообщение пользователю должно объяснять следующий шаг, а лог — техническую причину.
14. Карта основных исходников
| Область | Основные файлы |
|---|---|
| Настройки и определение режима | src/lib/store.ts, src/windows/settings/tabs/SettingsTabs.tsx |
| Диктовка и вставка | src/windows/widget/hooks/useWidgetRecording.ts, src/windows/widget/services/transcriptionPipeline.ts |
| Нативная запись | src-tauri/src/native_voice_recorder.rs |
| Потоковая диктовка | src/windows/widget/services/dictationStreamOverlay.ts, src-tauri/src/live_dictation.rs |
| Текстовая LLM | src/lib/summarize.ts, src/lib/cloudTextProcessing.ts |
| Перевод выделения | src/windows/widget/services/selectionTranslation.ts |
| Синхронный перевод | src/windows/widget/services/liveTranslation.ts, src-tauri/src/live_translation.rs, src-tauri/src/realtime.rs |
| Файлы | src/lib/fileTranscription.ts, src-tauri/src/media/mod.rs и модули src-tauri/src/media/ |
| Созвоны | src/lib/callCapture.ts, src-tauri/src/call_capture.rs |
| История | src/lib/store.ts, src-tauri/src/history_storage.rs, src/windows/settings/tabs/MainTab.tsx |
| ИИ-поиск по истории | src/lib/devChatHistoryContext.ts, src/lib/historyEmbeddings.ts, src/lib/offlineHistorySearchTemplates.ts |
15. Решения, которые требуют отдельного обсуждения
- Нужен ли production-доступ к ИИ-чату истории и какие лимиты/источники должны быть у него по умолчанию.
- Должен ли API-режим поддерживать provider-native diarization без локальных моделей.
- Нужна ли озвучка синхронного перевода на Windows/Linux и каким способом исключать собственный звук Talkis из повторного захвата.
- Какие облачные capability и тарифные лимиты должны отображаться до запуска операции.
- Следует ли в будущем разделить единый
useOwnKeyна независимые STT/LLM/Realtime mode selectors, чтобы официально поддерживать гибридные конфигурации.