Files
agentik-desktop/REQUIREMENTS.md
T

273 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Требования к десктопному клиенту agentik
**Статус:** накидываем. Ни один пункт не обязателен к исполнению в том виде, как
записан. Позже пересматриваем.
**Основа дизайна:** вариант 1 (`approved/001-sidebar-utility.html`) — выбран,
лежит в `approved/`.
**Сейчас описываем только поведение.** Кода не пишем, пока требования не устоятся.
---
## 1. Общее
- **R1.** Стиль — тёмная тема, уже принятая в клиентах пользователя
(фон `#121218`, панели `#17212B`, текст `#EBEBEB`). **Это текущая тема, а не
единственная** — см. R1.1.
- **R1.1.** **Цвета берём из цветовой схемы, а не пишем в коде числом.**
Предусматриваем, что тем будет **несколько** (тёмная, светлая и другие).
В коде не должно быть цвета вида `#121218` — вместо этого берём цвет из
текущей схемы по смыслу: «фон», «панель», «текст», «акцент», «ошибка»,
«успех». Смена темы тогда ничего не ломает — меняется схема, код остаётся.
- **R1.2.** **Имена берём по смыслу, а не по виду.** Не «тёмно-серый» и не
«синий», а «фон», «панель», «акцент». Смысл не меняется от смены темы, а
название цвета — меняется.
- **R1.3.** В макетах (`sketches/`, `approved/`) цвета стоят числами — это
нормально, макет должен выглядеть как задумано. **На этом основан и способ
проверки темы:** имена в коде — из R1.1, числа — только в макетах.
- **R1.4.** Отдельно проверяем, что при смене темы **ничего не осталось
вписанным числом**: ни текст, ни фон, ни границы, ни тени, ни цвет
«наведения» курсора. Если цвет не из схемы — он не переключится и будет
выбиваться.
- **R1.5.** Особый случай — **цвета для различения ассистентов** (цвет агента из
настроек). Они **не из схемы**: это пользовательские цвета, они заданы
сознательно и в другой теме остаются собой. Их надо отличать от цветов темы,
чтобы не перепутать при смене.
- **R2.** Что видно на экране в любой момент: список диалогов, выбранный диалог
и поле ввода. Больше ничего обязательного нет.
## 2. Раскладка и адаптивность
- **R3.** **Широкое окно** — две части: список диалогов слева, открытый диалог справа.
- **R4.** **Узкое окно** — на экране что-то одно: либо список диалогов, либо
конкретный диалог. Переключение — как в Телеграме.
- **R5.** В узком окне у открытого диалога сверху слева — кнопка «Назад».
Нажатие возвращает к списку диалогов на всю ширину.
- **R6.** Порог «широкое/узкое» — по ширине окна, одна величина. Какой именно
размер считать порогом — решим позже.
## 3. Список диалогов
- **R7.** Группировка диалогов со временем появится — «Работа», «Дом» и подобное.
Сейчас это накидка, состав групп ещё не определён.
- **R8.** В строке диалога: название, последнее сообщение, время, счётчик
непрочитанных.
- **R9.** Есть поиск по диалогам.
- **R9.1.** Над списком — **папки** (круглые кнопки): «Все чаты», «Работа», «Дом»
и «+» для новой. Это часть одобренного варианта 1, не забывать при доработках.
- **R9.2.** В строке диалога — круглый значок непрочитанных, если есть.
## 4. Открытый диалог
- **R10.** Показ сообщений: мои и ассистента, с временем.
- **R11.** Ответ ассистента можно прервать — на видном месте кнопка «Стоп».
- **R12.** Сообщения приходят вживую, по мере генерации: текст дотекает куском
за куском, а не появляется целиком в конце. Отображать именно так.
- **R13.** **Разметка (markdown).** Если я или агент пишем текст, похожий на
разметку, показывать его разметкой, а не сырыми символами: списки, выделение,
код, таблицы, ссылки. Код для этого писать не надо — готовый рендер уже есть
в репозитории `ai/assistent`, брать оттуда.
- **R13.1.** **Откуда брать — точно.** См. `MARKDOWN-SOURCE.md`: репозиторий
`ai/assistent`, ветка `main`, коммит `2c5b3f9`. Копировать 4 файла:
модель разметки, парсер, отрисовку и десктопную обёртку открытия ссылок.
Одна внешняя зависимость — `ru.otpbank.ai:markdown:0.51.0` (есть в Nexus).
## 5. Панель ввода
- **R14.** Поле ввода, кнопка «Отправить» и **отдельная кнопка записи микрофона**.
- **R15.** Подсказка о горячих клавишах под полем **убирается** — её не должно быть.
- **R16.** Пока идёт запись, должно быть понятно, что сейчас пишется голос.
- **R17.** Отмена записи — явная.
- **R18.** Голос: кнопка включает микрофон и останавливает его. Точная механика
(по нажатию или на удержание) — решим позже.
- **R19.** Распознавание речи — **отдельная тема, здесь не решается.** Важно только:
где кнопка, что видно во время записи, что происходит после.
- **R19.1.** **Как читать микрофон — решено, пишем не мы.** Подключаем свою готовую
библиотеку `mic-kmp` (`pw.binom.mic:mic-api-jvm`, версия `1.0.0`, есть в Nexus).
Отдаёт звук потоком кусочков, а не файлом: можно распознавать сразу, не дожидаясь
конца записи. Микрофон освобождается сам при остановке. Формат — 16 кГц, моно.
Код из `ai/assistent` для этого **не берём**. Подробности — `MIC-ASR-SEARCH.md`.
## 6. Несколько агентов
- **R20.** Предусмотреть возможность подключать **несколько агентов** и видеть их
в одном клиенте. Не «много людей», а один клиент — несколько агентов.
- **R21.** Возможный способ различения — **по аватару**: у каждого агента свой
аватар, по нему и отличаем. Тема аватаров не продумана, это накидка.
- **R22.** Другие возможные способы (на выбор, обсудить):
- **Цветовой акцент** — у каждого агента свой цвет: полоса у пузыря ответа,
цвет имени в шапке;
- **Группировка в списке** — диалоги сгруппированы по агентам, с заголовком
группы (как секции каналов);
- **Переключатель агента** — как выбор аккаунта в Телеграме: все диалоги
одного агента разом;
- **Метка в строке диалога** — маленький значок/буква агента рядом с названием.
- **R23.** У библиотеки уже есть всё для этого: агент создаётся вызовом с двумя
значениями — имя и адрес. Имя клиент знает сам, сервер его не хранит. Каждому
агенту — своё имя и адрес.
- **R23.1.** Экран настроек агентов (`sketches/004-settings-agents`) — **окно поверх
чата**, не отдельная страница. Два состояния: список агентов и форма нового.
- **R23.2.** В строке агента видно: имя, адрес, состояние («на связи» / «не отвечает»),
кнопки изменить и удалить.
- **R23.3.** Форма нового агента: название, адрес сервера, и **чем отличать в списке —
цвет или картинка**. Под каждым полем — пояснение простым языком.
- **R23.3.1.** Переключатель «Цвет / Картинка»: выбирается что-то одно.
**Цвет** — простой выбор из готовых, им помечаются диалоги агента в списке
(полоска у строки, точка у группы).
**Картинка** — файл с диска; показывается круглым превью, рядом «Выбрать файл…»
и «Убрать».
- **R23.3.2.** **Картинку копировать себе, а не брать по исходному адресу.**
Выбранный файл сохраняется внутрь клиента (в свою папку), и дальше используется
только эта копия. Ссылку на исходный путь не хранить: файл могут удалить,
переименовать, флешку вынуть — и значок у агента пропадёт. Работает и для
картинки на диске, и для картинки по ссылке: скачать и положить себе.
- **R23.3.3.** Выбранная картинка показывается кружком там же, где был бы цвет:
в списке агентов, в списке диалогов, в шапке чата. Убрать её можно в любой
момент — тогда снова становится цвет.
- **R23.4.** **Проверка связи до сохранения** — в **отдельном окне поверх настроек**,
не строкой внутри формы. Нажал «Проверить связь» — открылось второе окно, в нём
идёт проверка. Пока идёт: «спрашиваю агента…» с крутящимся значком. Результат —
зелёный значок «отвечает» или красный «не отвечает», и под ним подробности:
ответ на запрос, адрес, число диалогов, отклик в миллисекундах.
Кнопка «Добавить агента» становится доступной только при успехе.
Есть «Проверить снова». Закрытие окна возвращает к настройкам.
Основа: агент отвечает на запрос состояния и отдаёт список диалогов; оба запроса
ничего не меняют.
- **R23.5.** Цвет агента работает не только в настройках: тем же цветом помечаются
его диалоги в списке (полоска у строки, точка у группы).
- **R23.6.** Список агентов сохраняется между запусками. Где именно — не решено.
- **R23.7.** Доступа по паролю/ключу у библиотеки нет — поля для него не делать.
- **R23.8.** **Новый диалог: выбор ассистента** — **МАКЕТ ОДОБРЕН**,
лежит в `approved/005-new-chat-picker.html`. Черновик со всеми состояниями —
`sketches/005-new-chat-picker`.
По «+» спрашиваем, **в каком ассистенте** создать диалог — потому что диалог
живёт внутри агента, отдельно его не создать.
- **R23.9.** **Окно показывается только если ассистентов больше одного.**
Один — диалог создаётся сразу в нём, спрашивать не о чем, окна нет и в помине.
- **R23.10.** **При нуле ассистентов кнопки «+» нет вообще.** Создавать диалог
не в ком. Вместо списка — объяснение, что нужно сначала добавить ассистента.
- **R23.11.** В списке выбора видно: значок (цвет или картинка), имя, адрес
сервера, состояние связи. Отмечен первый по умолчанию — создать диалог можно
одним нажатием.
- **R23.12.** **Ассистента без связи выбирать можно** — окно предупреждает словами,
но не мешает. Связь может пропасть на секунду, а диалог человек создаёт осознанно.
Ассистентов без связи из списка не выкидываем.
## 7. Состояние агента: что показывать
Сверка с исходниками здесь важнее всего. Ниже — **по факту из кода**, не по догадкам.
### 7.1. Что есть в самой библиотеке `client` (что клиент реально получает)
- **У каждого события и каждого сообщения есть дата.** Значит доступно: длительность
хода, сколько времени агент думал, когда пришёл ответ.
- **Есть события:** начало ответа, рассуждение, текст по мере генерации, картинка,
вызов инструмента (имя, аргументы, заголовок), результат инструмента, конец хода,
прерывание, ошибка.
- **Есть события о диалогах:** создан, удалён, переименован.
- **По диалогу:** название, время последнего изменения, умеет ли принимать и отдавать
картинки, временный он или постоянный.
- **Токенов, модели, цены, предела контекста — в библиотеке НЕТ.** Ни одного числа.
Проверено сплошным поиском: числовых полей в протоколе не существует вообще.
### 7.2. Решение: счётчиков токенов и кольца в интерфейсе НЕ будет
- **R24.** Показывать израсходованные токены, долю от предела и кольцо заполнения
**не нужно**. Данных для этого в библиотеке нет — значит и рисовать нечего.
Кольцо из макета удалено.
- **R25.** Никаких полей наугад: **нет данных — нет цифры.** Не подставлять
правдоподобные числа вместо отсутствующих.
- **R26.** Если когда-нибудь агент начнёт отдавать такие числа — вернёмся к вопросу.
До тех пор тема закрыта.
### 7.3. Ход работы агента
- **R27.** Показывать не только ответы ассистента, но и происходящее вокруг них:
вызовы инструментов, рассуждения, ошибки.
- **R28.** Свёрнутый вид — одна строка: кратко, что происходит
(например, «вызов инструмента: такой-то»).
- **R29.** Развёрнутый вид — подробности шага: аргументы, результат, время.
- **R30.** Сворачивание и разворачивание — по нажатию на строку.
## 8. Простота кода и кэш сообщений
### 8.1. Цель: клиент должен читаться
- **R31.** **Главная цель — чтобы клиент был максимально прост по коду.** Я должен
открыть исходники и понять, что там происходит. Это важнее, чем сэкономить
строки или вынести лишнее.
- **R31.1.** **Сначала десктоп, затем Android по тем же лекалам.** Десктопный клиент
— образец. Когда он получится удобным и понятным, Android делается по его
решениям. Поэтому простота кода здесь — не пожелание, а условие.
- **R32.** Распознавание голоса, микрофон и детекция речи — **готовые библиотеки**,
свои (`mic-kmp`, `asr-kmp`, `vad-kmp`). Писать заново ничего не надо, и это уже
не «вынос в отдельное место» — библиотеки и есть отдельное место.
- **R33.** Материал найден, разобран: `MIC-ASR-SEARCH.md`. Там же сказано, почему
код микрофона из `ai/assistent` брать **не** надо.
- **R34.** Из `ai/assistent` берём только приёмы отрисовки, плагины и версии —
не архитектуру, не экраны, не дизайн. Разбор: `BORROW-FROM-ASSISTENT.md`.
### 8.2. Кэш сообщений на клиенте — решение принято
**Задача:** не тянуть всю историю диалога заново при каждом открытии окна.
**Схема:**
1. Открыли диалог — смотрим свой кэш.
2. Берём из кэша **последнее сообщение** и его дату.
3. Спрашиваем библиотеку `client`: есть ли сообщения **новее** этой даты.
4. Были — забираем только новые, добавляем в кэш, рисуем.
5. Не были — ничего не делаем.
- **R35.** **Это работает: в библиотеке `client` всё нужное есть.** Функция
`Conversation.getMessages(after, offset, limit)` отдаёт сообщения **строго новее**
указанной даты (проверено: в отборе `created_at > ?`). Пустой ответ = после
нашего сообщения ничего не появилось. Есть и версия потоком, страницами по 100
(`PAGE_SIZE = 100`). Отдельно создавать диалог для проверки не нужно — только
если открываем новый.
- **R36.** **Дубли отсекаются по `id` сообщения.** Идентификатор один и тот же
и в живом потоке, и в истории (`Message.id`). То есть при догрузке не нужно
угадывать, что уже нарисовано — сверяем по идентификатору.
- **R37.** **Самого кэша в библиотеке нет** — это пишем мы. Библиотека даёт только
«спроси, что новее».
**Три места, где схема может порваться (найдено в коде, не предположения):**
- **R38.** **Одинаковые даты.** Отбор строго «новее» (`created_at > ?`), поэтому
если два сообщения получили **ровно одну и ту же дату** — второе в ответ не
попадёт. Порядок внутри одной даты сервер задаёт сам (сортировка по времени,
затем по `id`). Реальный риск невелик (дата с точностью до миллисекунд), но
при сверке по `id` (R36) он не страшен вовсе.
- **R39.** **Прерванный ответ в историю не попадает.** Если ответ прервали кнопкой
«Стоп», кусок ответа остаётся только в живом потоке, в историю он не пишется
(`Event.Interrupted`). В кэше окажется то, чего на сервере нет.
- **R40.** **Живой поток тоже надо просить «с этого момента».** У подписки на события
есть тот же параметр «после»; без него после переподключения пропустим события.
- **R41.** **Решение:** кэш делаем. Схема — из R35. Где хранится (файл или лёгкая
база) — **не решено**, см. раздел 9.
### 8.3. Что не выносим
- **R42.** Экраны, пузырь сообщения, хранение списка агентов — **остаются в клиенте**.
Это и есть клиент, выносить их некуда.
- **R43.** Полезный признак, что что-то не вынесено: **в экране накопилась логика
длиннее пары десятков строк.** Значит, это место жить в экране не должно.
## 9. Решения, которые ещё не приняты
- Запись: по нажатию или на удержание.
- Нужны ли вложения (картинки) в этом клиенте.
- Как именно различать агентов (см. R21–R22): цвета или картинки достаточно?
- Нужен ли картинке размер/форма — показывать кружком без изменений или обрезать.
- Где хранится список агентов — в файле на диске или спрашивать сервер.
- Как выглядит показ хода работы агента в свёрнутом виде.
- **Где хранится кэш сообщений** — обычный файл на диске или лёгкая база (R41).
- Сколько держать в кэше и когда чистить (старые диалоги).
- Что делать с куском прерванного ответа в кэше (R39): хранить или выбрасывать.
## 10. Как проверяем
Проверка — **живой прогон**, а не «собралось». Правило пользователя: перед показом
гонять сценарий руками; на экран смотреть, а не верить описанию.