Макеты — один sketches/index.html; approved/ удалён
- sketches/index.html: все экраны одним документом в порядке 1·… (14–19 — бывшие approved) - approved/ (4 файла групп + 3 макета + README) удалён как дубль - стили вынесены в sketches/style.css - README, THEMES, BORROW-FROM-ASSISTENT, MARKDOWN-SOURCE, MIC-ASR-SEARCH приведены к фактическому состоянию
This commit is contained in:
@@ -1,131 +1,153 @@
|
||||
# agentik-desktop — дизайн десктопного клиента
|
||||
# agentik-desktop — десктопный клиент агента agentik
|
||||
|
||||
Десктопный клиент агента **agentik** (Jetpack Compose Desktop). UI общается с агентом
|
||||
через опубликованную библиотеку `pw.binom.agentik:client` — она даёт готовые
|
||||
`Agent` / `Conversation`, живой поток событий и `interrupt()` для кнопки «Стоп».
|
||||
Клиент агента **agentik** на Kotlin Multiplatform + **Compose Desktop** (цель сборки —
|
||||
только JVM). UI общается с агентом через готовую библиотеку `pw.binom.agentik:client`:
|
||||
она даёт `Agent` / `Conversation`, живой поток событий (SSE), авто-reconnect,
|
||||
кэш списка диалогов и `interrupt()` для кнопки «Стоп».
|
||||
|
||||
Это пока **только дизайн**, не код приложения.
|
||||
Проект уже **собирается и запускается** (`./gradlew run`), а не только спроектирован.
|
||||
|
||||
## Что где лежит
|
||||
|
||||
**`approved/` — принятое. Это решение, на него опираемся в реализации.**
|
||||
|
||||
```
|
||||
approved/001-sidebar-utility.html # ОСНОВА: привычный мессенджер
|
||||
approved/004-settings-agents.html # настройки агентов + проверка связи
|
||||
approved/005-new-chat-picker.html # модалка «Новый диалог»
|
||||
approved/006-groups-manage/ # группы: 4 файла по состояниям
|
||||
approved/006-groups-manage/1-list.html # Группы: изменить, удалить, добавить
|
||||
approved/006-groups-manage/2-add.html # новая группа
|
||||
approved/006-groups-manage/3-edit.html # изменение и удаление
|
||||
approved/006-groups-manage/4-delete.html # удаление: диалоги остаются
|
||||
approved/README.md # что одобрено и когда
|
||||
sketches/index.html # ВСЕ экраны одним документом: список диалогов, чат, запись,
|
||||
# группы, агенты, проверка связи, пустые состояния.
|
||||
# Это и черновик, и «одобренное» — отдельного approved/ больше нет.
|
||||
sketches/style.css # палитра и стили макетов (цвета числами — так и надо, это макет)
|
||||
src/jvmMain/kotlin/... # код приложения
|
||||
src/jvmTest/kotlin/... # тесты
|
||||
```
|
||||
|
||||
**`sketches/` — предложения. Не решение, а варианты и черновики.**
|
||||
Документы рядом:
|
||||
- `REQUIREMENTS.md` — требования (накиданы, ни один пункт не обязателен в текущей форме).
|
||||
- `STORAGE.md` — что и где хранится на клиенте (SQLite + JSON).
|
||||
- `CACHE.md` — кэш сообщений: что есть в библиотеке, схема «только новое».
|
||||
- `THEMES.md` — цвета не пишем в коде, только из семантической схемы.
|
||||
- `MARKDOWN-SOURCE.md` — откуда взята отрисовка Markdown.
|
||||
- `BORROW-FROM-ASSISTENT.md` — что берём из `ai/assistent`, а что нет.
|
||||
- `MIC-ASR-SEARCH.md` — библиотеки микрофона/распознавания (`mic-kmp`, `asr-kmp`, `vad-kmp`).
|
||||
|
||||
## Структура кода
|
||||
|
||||
```
|
||||
sketches/002-rail-voice-first/index.html # голос — главное действие
|
||||
sketches/003-three-pane-command/index.html # три панели + состояние агента
|
||||
sketches/004-settings-agents/index.html # черновик настроек агентов
|
||||
sketches/005-new-chat-picker/index.html # черновик модалки нового диалога
|
||||
pw.binom.agentik.desktop
|
||||
├── Main.kt # точка входа: окно, реестр, состояние
|
||||
├── settings/ # AppPaths, AppSettings/AgentConfig, SettingsRepository
|
||||
├── persistence/ # ConversationMetaRepository (группы/lastSeen/preview/watermark)
|
||||
├── session/ # LiveTurn, ChatSession, AgentConnection, AgentRegistry
|
||||
├── model/ # UiModels + Mappers (журнал/стрим → UI)
|
||||
├── media/ # AvatarStorage (копия картинки агента)
|
||||
├── net/ # ConnectionChecker (проверка связи)
|
||||
├── markdown/ # вендоренный парсер Markdown (из assistent)
|
||||
└── ui/ # App, AppState, theme/, components/, screens/
|
||||
```
|
||||
|
||||
`BORROW-FROM-ASSISTENT.md` — что берём из `ai/assistent` (приёмы, плагины, версии),
|
||||
а что не берём (архитектура, экраны, дизайн — своё).
|
||||
`MIC-ASR-SEARCH.md` — библиотеки для микрофона и распознавания: своё готовое
|
||||
(`mic-kmp`, `asr-kmp`, `vad-kmp`), вместо копирования кода из assistent.
|
||||
`STORAGE.md` — где что хранится: **хранилище делаем абстракцией, реализация —
|
||||
SQLite, настройки — JSON.** Три отдельные абстракции (сообщения / настройки /
|
||||
снимок списка), почему SQLite, что менять в API (превью и число непрочитанных).
|
||||
`CACHE.md` — кэш сообщений на клиенте: что для него есть в библиотеке, схема
|
||||
загрузки «только новое», где может порваться.
|
||||
`THEMES.md` — цветовые темы: цвета не пишем в коде, берём из схемы. Тем будет
|
||||
несколько, поэтому ни одного цвета числом.
|
||||
`MARKDOWN-SOURCE.md` — откуда брать готовую отрисовку Markdown (файлы и адреса).
|
||||
`REQUIREMENTS.md` — требования. Статус: накидываем, ни один пункт не обязателен
|
||||
к исполнению в том виде, как записан.
|
||||
Ключевые решения по слоям — в `STORAGE.md`; кратко:
|
||||
|
||||
## Одобренное
|
||||
- **Транспорт целиком в `:client`.** HTTP/JSON/SSE/Bearer, reconnect, кэш списка
|
||||
диалогов — не переписываем.
|
||||
- **Сообщения** — `KsqliteJournalStore` из `:journal-ksqlite` (offline-история).
|
||||
- **Группы, `lastSeen`, превью, watermark синхронизации** — наш
|
||||
`ConversationMetaRepository` (сервер про это не знает).
|
||||
- **Отдельная SQLite-база на агента**: id диалогов серверные, у двух агентов
|
||||
могут совпасть; плюс группы локальны для агента.
|
||||
|
||||
`approved/` — макеты, которые пользователь **принял**. Это решение, на него
|
||||
опираемся в реализации. Отличие от `sketches/`: там предложения, здесь принятое.
|
||||
## Текущее состояние реализации
|
||||
|
||||
- **`001-sidebar-utility.html`** — **основа дизайна**: привычный мессенджер,
|
||||
список диалогов + чат, папки («Все чаты / Работа / Дом»). Выбран как основа.
|
||||
- **`004-settings-agents.html`** — настройки агентов: список, форма нового,
|
||||
проверка связи отдельным окном поверх. Одобрена 2026-09-19.
|
||||
- **`005-new-chat-picker.html`** — модалка «Новый диалог» при нескольких
|
||||
ассистентах. Одобрена 2026-09-19.
|
||||
- **`006-groups-manage/`** — управление группами, четыре файла по состояниям.
|
||||
Одобрена 2026-09-19. Пояснительной панели над окном в ней нет
|
||||
(в `sketches/005` была — это была заметка для проверки, не часть интерфейса).
|
||||
Готово и покрыто тестами:
|
||||
|
||||
**Правило:** одобренное живёт **только** в `approved/`. Если макет приняли —
|
||||
он переезжает туда, а не остаётся «выбранным» среди вариантов.
|
||||
- **Настройки** — `settings.json` (агенты, тема, выбранная группа, последний диалог),
|
||||
`clientId` генерируется один раз. Атомарная запись.
|
||||
- **Живой список диалогов** — `AgentConnection.conversationsFlow` опрашивает
|
||||
встроенный кэш либы (`conversationStore.listFlow`, warmup-цикл на старте) и
|
||||
дополнительно подписан на SSE-поток агента: диалог, созданный с другого
|
||||
клиента (или самим сервером), появляется в списке сам; свои создания и
|
||||
удаления отражаются сразу. Ошибки сервера не считаются «пустым списком» —
|
||||
последний известный список сохраняется.
|
||||
- **Реестр агентов** — добавление/правка/удаление, подключение, деградация в
|
||||
«не отвечает» без падения приложения.
|
||||
- **Стриминг** — `LiveTurn` применяет события `Working → StartReasoning →
|
||||
AppendText → ToolCall/ToolResult → End/Interrupted/Error`; `ChatSession`
|
||||
догоняет историю (`after = synced_at`), пишет в локальный кэш, склеивает
|
||||
live + history, обновляет превью и `lastSeen`.
|
||||
- **История/UI-модели** — мапперы `MessageRecord → UiMessage` со склейкой
|
||||
`ToolCall`+`ToolResult` в одну карточку.
|
||||
- **Группы** — CRUD групп + раскладка диалогов (каскад: удаление группы не
|
||||
удаляет диалоги).
|
||||
- **Тема** — семантические цвета (`AgentikTheme.colors`), тёмная + светлая
|
||||
схемы; отдельно пользовательские цвета агентов.
|
||||
- **Markdown** — вендоренный рендер из `ai/assistent` (см. `MARKDOWN-SOURCE.md`).
|
||||
- **Аватары** — копирование выбранного файла в `~/.agentik-desktop/avatars/`.
|
||||
- **Проверка связи** — «ответ сервера» + создание/удаление временного диалога,
|
||||
с таймингами.
|
||||
- **UI (Compose)** — рейка агентов, список диалогов (поиск, папки, бейджи),
|
||||
чат с живым стримингом, композер, окна «Агенты», «Группы» и «Новый диалог»
|
||||
(при нескольких агентах — выбор, при одном — сразу), пустые состояния,
|
||||
адаптивная раскладка (узкое окно — список или чат).
|
||||
- **Голос** — микрофон → VAD → распознавание (`voice/`): кнопка микрофона
|
||||
или `Ctrl+M` из чата запускает запись (`mic-api`), Silero VAD (`vad-kmp`) отмечает наличие речи,
|
||||
Vosk (`asr-vosk`, модель внутри артефакта) отдаёт промежуточный текст над
|
||||
полем ввода, финал дописывается в черновик. Тишина текстом не становится.
|
||||
Нативные модели считаются на одном выделенном потоке. На старте `main()`
|
||||
делает [`VoiceNative.warmUp()`] — обязательный прогрев Vosk/ONNX ДО
|
||||
инициализации skia/Compose (иначе создание модели после отрисовки окна
|
||||
роняет JVM в C++-логах; подробности в `MIC-ASR-SEARCH.md`).
|
||||
- **Реальная интеграция** — интеграционные тесты гоняются против живого
|
||||
`:standalone`-агента (Qwen через vLLM), а не только in-memory-фейков:
|
||||
`RealServerIT` (диалог → отправка → живой ответ LLM → локальный кэш) и
|
||||
`RealAppStateIT` (весь `AppState`: реестр → подключение → живой список →
|
||||
создание диалога → стриминг → превью/lastSeen), `RealSpeechIT` (речь:
|
||||
прогрев нативных ASR → инициализация Compose → распознавание реального wav
|
||||
→ «раз два три четыре пять»). См. `AGENTIK_E2E_URL`.
|
||||
|
||||
Проверено вживую (окно приложения + живой агент): список диалогов подтягивается
|
||||
с сервера, кнопка «+» создаёт диалог, сообщение уходит и ответ приходит в UI,
|
||||
`Ctrl+M` переводит микрофон в запись и обратно без падения JVM.
|
||||
|
||||
Не начато / заглушки:
|
||||
|
||||
- **Свёрнутый вид хода работы агента** — сейчас живой ход показывается
|
||||
развёрнуто (reasoning, карточки ToolCall/ToolResult, спиннер). Как его
|
||||
складывать — вопрос дизайна, не решён (REQUIREMENTS §9).
|
||||
- **Кусок прерванного ответа в кэше** — R39 выполнен (прерванный ответ в
|
||||
историю не пишется), а вот хранить ли его обрывок в кэше — вопрос не решён
|
||||
(REQUIREMENTS §9).
|
||||
- **Светлая тема визуально не проверялась глазами** (`THEMES.md`); в
|
||||
`ui/ThemeRenderTest.kt` проверено программно: фон код-блоков берётся из
|
||||
слота темы, порядок яркостей в светлой палитре корректный.
|
||||
- **Голос: движок зашит на Vosk** (`VoskVoiceRecognizer`) — Qwen3/Whisper из
|
||||
`asr-kmp` подключаются заменой `VoiceRecognizerFactory`; выбор движка/модели
|
||||
в настройках не сделан.
|
||||
|
||||
## Как запустить
|
||||
|
||||
```bash
|
||||
./gradlew run # окно приложения
|
||||
./gradlew jvmTest # тесты (реальная SQLite, фейковый транспорт)
|
||||
|
||||
# Интеграционные тесты против живого агента (по умолчанию пропускаются):
|
||||
AGENTIK_E2E_URL=http://127.0.0.1:8080/agentik ./gradlew jvmTest \
|
||||
--tests '*RealServerIT' --tests '*RealAppStateIT'
|
||||
./gradlew jvmJar # собрать jar
|
||||
```
|
||||
|
||||
## Что решено
|
||||
|
||||
- **Сначала десктоп, затем Android — по тем же лекалам.** Десктопный клиент
|
||||
делаем образцом: понятный, удобный и **читаемый по коду**. Когда получится —
|
||||
Android повторяет те же решения. Поэтому цель — максимально простой код,
|
||||
который пользователь открывает и понимает.
|
||||
- **Основа — вариант 1** (`approved/001-sidebar-utility.html`). Остальные остаются рядом как
|
||||
источник идей.
|
||||
- Стиль — тёмный, из уже принятой темы клиента assistent: фон `#121218`,
|
||||
панели `#17212B`, акцент `#6AB2F2`, текст `#EBEBEB`.
|
||||
- **Тем будет несколько — цвета берём из цветовой схемы, не пишем числом в коде.**
|
||||
Имена по смыслу («фон», «панель», «акцент»), чтобы смена темы ничего не ломала.
|
||||
В макетах числа — это нормально. Разбор и список того, что забывают: `THEMES.md`.
|
||||
- **Узкое окно** — на экране что-то одно: либо список диалогов, либо чат.
|
||||
Переключение кнопкой «Назад», как в Телеграме. Широкое — обе части сразу.
|
||||
- **Подсказка о горячих клавишах** под полем ввода убрана.
|
||||
- **Счётчиков токенов и кольца заполнения не будет.** В библиотеке `client`
|
||||
таких данных нет вообще: ни модели, ни отправлено/получено, ни предела
|
||||
контекста. Правило: нет данных — нет цифры.
|
||||
- **Несколько агентов** — предусмотреть. Различение: **цвет или картинка** на выбор.
|
||||
Цвет помечает диалоги агента (полоска у строки, точка у группы); картинка
|
||||
показывается кружком вместо цвета. **Картинку клиент копирует себе**, а не берёт
|
||||
по исходному адресу — иначе значок пропадёт вместе с файлом.
|
||||
- **Markdown** — показывать разметкой, а не сырыми символами. Готовый рендер
|
||||
лежит в репозитории `ai/assistent` — писать свой не надо, брать оттуда.
|
||||
Точные координаты файлов — в `MARKDOWN-SOURCE.md`.
|
||||
- **Папки над списком диалогов** («Все чаты», «Работа», «Дом») — часть выбранного
|
||||
варианта 1, не терять их при доработках.
|
||||
- **Проверка связи с агентом** — в отдельном окне поверх настроек: идёт → отвечает
|
||||
→ не отвечает, с подробностями и подсказкой. Добавить агента можно только
|
||||
после успешной проверки.
|
||||
- **Новый диалог** — при нескольких ассистентах спрашиваем, в каком создавать
|
||||
(диалог живёт внутри ассистента, отдельно его не создать). Один ассистент —
|
||||
окна нет, создаётся сразу. Ноль ассистентов — кнопки «+» нет вообще.
|
||||
|
||||
## Заимствования: только приёмы, не архитектура
|
||||
|
||||
Из репозитория `ai/assistent` берём **приёмы отрисовки, плагины и версии**.
|
||||
Архитектуру, слои, готовые экраны и окна — **не берём**: дизайн у нас свой,
|
||||
нарисованный (см. `sketches/`). Разбор по пунктам — в `BORROW-FROM-ASSISTENT.md`,
|
||||
там же таблица «берём / не берём».
|
||||
|
||||
## Как смотреть
|
||||
|
||||
Открыть в браузере любой из файлов — каждый самодостаточный, без сборки.
|
||||
В четвёртом сверху переключатель состояний: список агентов, форма, проверка
|
||||
связи (успех и ошибка), список диалогов с двумя агентами.
|
||||
- **Сначала десктоп, затем Android — по тем же лекалам.** Десктоп делаем
|
||||
образцом: понятный и читаемый по коду. Потом Android повторяет решения.
|
||||
- **Несколько агентов** — различение цветом или картинкой (выбирает пользователь).
|
||||
Картинку клиент **копирует себе**, а не берёт по адресу.
|
||||
- **Markdown** — показывать разметкой. Готовый рендер взят из `ai/assistent`.
|
||||
- **Папки над списком диалогов** — сохранены.
|
||||
- **Проверка связи** — отдельным окном, с таймингами шагов.
|
||||
- **Ориентир по размеру:** ~9 небольших кусков логики поверх библиотеки, а не
|
||||
собственная инфраструктура (см. `STORAGE.md`, `CACHE.md`).
|
||||
|
||||
## Что предстоит решить
|
||||
|
||||
1. **Запись** — по нажатию (нажал — говоришь — нажал) или на удержание.
|
||||
1. **Запись** — по нажатию или на удержание.
|
||||
2. Форма показа хода работы агента в свёрнутом виде.
|
||||
3. Различение агентов: одного цвета может оказаться мало.
|
||||
4. Где хранится список агентов.
|
||||
5. Нужны ли папки для диалогов, как в мобильном клиенте, или хватит поиска.
|
||||
6. **Где хранится кэш сообщений** — файл или лёгкая база (см. `CACHE.md`).
|
||||
7. **Что делать с куском прерванного ответа** в кэше — иначе схема даст сбой
|
||||
на первом же нажатии «Стоп» (`CACHE.md`, п. 3.2).
|
||||
|
||||
## Что сознательно не решается здесь
|
||||
|
||||
- **Откуда берётся распознавание речи.** Отдельная тема, к интерфейсу не
|
||||
относится: в дизайне показано только место кнопки и что происходит на экране
|
||||
во время записи. Кто именно превращает голос в текст — решается потом.
|
||||
Готовые библиотеки уже найдены, см. `MIC-ASR-SEARCH.md`.
|
||||
4. **Что делать с куском прерванного ответа** в кэше (`CACHE.md`, п. 3.2).
|
||||
5. Где показывать несколько агентов в одном списке диалогов (сейчас список —
|
||||
у активного агента).
|
||||
|
||||
Reference in New Issue
Block a user