diff --git a/EventHubFrontSpec.md b/EventHubFrontSpec.md index 27a072b..dbf0e90 100644 --- a/EventHubFrontSpec.md +++ b/EventHubFrontSpec.md @@ -179,6 +179,10 @@ busy Time Arc += события personal с `mirror_to_default`; ghost/demand/ph `write|admin`; Following не смешивать с share. Confirmed foreign bookings overlay на default personal — только `role=owner` ids в ownSet. +Канон ИИ клиента (подсказки / голос, Future — [PRODUCT-AI.md](PRODUCT-AI.md), см. §7.3): +умность — в ячейках; опциональные hint-ghosts и голос-кнопка mark → ActionPlan → +подтверждение. Не чат; AiRouter вне продукта. + ## 4. Маршруты Публичные: @@ -425,6 +429,15 @@ cold = файлы. UI истории — React, не iframe. Код: Back#76, Fr Пикер адреса/пина; карточка с мини-картой и outbound Google/Яндекс/OSM; Discover ближайшие с опциональным радиусом. Код: Back#77, Front#74, DevOps#16. +## 7.3. Фаза 3 — ИИ клиента (Spec#19 / Spec#22) + +Канон: [`PRODUCT-AI.md`](PRODUCT-AI.md). Позиция «ИИ молчит и делает»: L0 умность в +ячейках Time Arc (shipped, без LLM); L1 опциональные подсказки — ghosts `kind: 'hint'` +с причиной (Front#75); L2 голос-кнопка Time Arc mark, press-to-talk → ActionPlan-чипы +с подтверждением пишущих действий, палитра команд как fallback (Front#76). STT/intent +через Back-прокси `/v1/ai/*`, как GEO/OpenCage. Anti-scope: чат, Composer, AI-home, +авто-booking без подтверждения, подводка AiRouter к Front. Код — после гейтов канона. + ## 8. Источники истины - Маршруты и поведение: `EventHubBack/src/eventhub_app.erl` + handlers diff --git a/PRODUCT-AI.md b/PRODUCT-AI.md new file mode 100644 index 0000000..6d2c8a2 --- /dev/null +++ b/PRODUCT-AI.md @@ -0,0 +1,183 @@ +# ИИ клиента CalenTIQ (Фаза 3, волна 3) + +Канон продуктового ИИ: умная сетка + опциональные предиктивные подсказки + голосовой центр выполнения **без чата**. Трекер: [Spec#22](https://git.sabilin.com/EventHub/EventHubSpec/issues/22), эпик [Spec#19](https://git.sabilin.com/EventHub/EventHubSpec/issues/19). + +Реализация (Future): [Front#75](https://git.sabilin.com/EventHub/EventHubFront/issues/75) подсказки, [Front#76](https://git.sabilin.com/EventHub/EventHubFront/issues/76) голосовой центр. Этот документ — контракт; код подтягивается после него. + +## Позиция: «ИИ молчит и делает» + +ИИ CalenTIQ — не собеседник и не интерфейс. Это слой, который **предлагает действия**, а человек их подтверждает и исполняет. Никакого свободного текста от ИИ, никакого диалога, никаких уточняющих вопросов, никакой «личности ассистента». + +Три следствия: + +1. Результат любой ИИ-операции — **план действий в UI** (чипы, ghosts, навигация), а не текст. +2. Неопределённость разрешается **выбором из кандидатов**, не разговором. +3. Запись в API — только после явного подтверждения человека. + +## Уровни ИИ + +| Уровень | Что | Статус | Где живёт | +|---------|-----|--------|-----------| +| **L0 — умность в ячейках** | детерминированный скоринг Time Arc (без LLM, без Back) | shipped (v1–v4) | ячейки сетки | +| **L1 — предиктивные подсказки** | те же ghosts, `kind: 'hint'` + причина «почему» | Future (Front#75), opt-in | ячейки сетки | +| **L2 — голосовой центр** | голос → ActionPlan → подтверждение | Future (Front#76), opt-in | кнопка Time Arc mark | + +Единый визуальный язык: подсказка — **не** тост и **не** баннер, а ghost прямо в сетке. Интеллект всегда в ячейках — подсказки тоже в ячейках. Новый UI-слой не изобретается: расширяется уже shipped-механика ghosts (`copy` / `demand` / `phrase` / `repeat` → `+hint`). + +## Статус Time Arc v1–v4 (L0, shipped) + +См. FrontSpec §3.3. Кратко: + +- **v1 (Front#50):** клиентский скоринг лучшего часа: свободные слоты студии ∩ занятость пользователя × mood. Черновики владельца из прошлой недели. Без LLM и без Back. +- **v2 (Front#51):** память отказов `eh.timeArc.skips` (штраф часу/мастеру, дуга переезжает); буст мастера с ≥2 confirmed; тепло месяца. +- **v3:** дуга между календарями — `/following` и `/search` баннеры ближайшего часа. +- **v4 (Front#53):** demand-черновики по pending occupancy; локальный разбор фразы → phrase-черновик у владельца (long-press). Без LLM / AiRouter. + +Всё L0 — локальные вычисления Front. Этот канон L0 **не меняет**. + +## Голос-кнопка (L2, контракт Front#76) + +### Кнопка + +- Кнопка = **Time Arc mark** рядом с BrandWordmark в AppShell (см. FrontSpec §3.2) — всегда видна, отдельного слота не занимает. +- **Press-to-talk**: нажал и держишь → говоришь → отпустил → разбор. Отпустил без звука — ничего не происходит. Никакого «зависшего» прослушивания, таймеров VAD и модалок. +- Состояния (4): `idle` / `listening` / `processing` / `done` — анимируются **самим mark** (дуга заполняется, пока держишь). Бренд сам себе индикатор. + +### Палитра быстрых команд (обязательный fallback) + +Голос и тап — **два канала одного движка действий**. Кнопка при недоступности голоса (offline, mic denied, STT упал) открывает палитру тапабельных типовых действий из того же enum интентов. UX голоса не умирает ни в одном degrade-сценарии. + +### Результат — план действий + +После разбора показывается стек action-чипов (план), а не текст. Пишущие действия — с одной кнопкой «Выполнить»; обратимые исполняются сразу (см. классы ниже). + +## ActionPlan (контракт later) + +STT + intent-разбор выдают структурированный план, не текст. Намётка схемы: + +```json +{ + "version": 1, + "locale": "ru", + "confidence": 0.87, + "actions": [ + { "id": "a1", "intent": "open_calendar", "target": { "calendar_hint": "аурора" }, "resolved": null }, + { "id": "a2", "intent": "select_hour", "time": { "hour": 19, "day": "next", "dow": 2 } }, + { "id": "a3", "intent": "book", "confirm": "required" } + ], + "candidates": [] +} +``` + +### Интенты — конечный enum + +Каждый интент исполняется через **уже существующий** UI-флоу. Новых write-эндпоинтов в Back нет. + +| Intent | Глаголы (ru / en) | Исполняется через | +|--------|-------------------|-------------------| +| `navigate` | перейти, открой, покажи / go, open | роуты FrontSpec §4 | +| `open_calendar` | календарь + название | `/c/:id`; fuzzy-match по `GET /v1/calendars` + following | +| `select_hour` | «в 19», «завтра в 11» | курсор `calendarContextStore` + подсветка ячейки | +| `book` | запиши, записаться / book | существующий флоу Book (mandatory confirm + owner pending) | +| `create_slot_draft` | создай слот, поставь окно | механика phrase-черновиков v4: голос — ещё один источник фразы | +| `confirm_booking` / `decline_booking` | подтверди, отклони заявку | `PUT /v1/bookings/:id` через `/bookings` | +| `follow` / `unfollow` | подпишись, отслеживай | `POST/DELETE /v1/calendars/:id/follow` | +| `search` | найди, ищи | `/search` с заполненными фильтрами | + +Всё вне enum — отбрасывается одним тостом, без retry-цикла. + +### Разбор: rule-first + +Двухслойный: словарь глаголов + regex покрывает типовые фразы («запиши к Ане завтра в 19» — глагол + имя + время, детерминированно). LLM-провайдер вызывается **только** при miss и обязан вернуть тот же ActionPlan-JSON — никогда свободный текст. + +`create_slot_draft` у владельца использует локальный разбор фразы из v4 → **полностью офлайн** rule-path. + +### Resolution и кандидаты (вместо вопросов) + +Каждый `target.hint` резолвится **на клиенте** по локальным данным (календари, following, bookings, roster). Не резолвится → пишется в `candidates` и показывается чипами на выбор. Уточняющих вопросов нет по контракту. + +### Классы действий + +| Класс | Примеры | Подтверждение | +|-------|---------|----------------| +| **Обратимые** | `navigate`, `open_calendar`, `select_hour`, `search` | исполняются сразу — навигацию всегда можно отменить «назад» | +| **Пишущие** | `book`, `create_slot_draft`, `confirm/decline_booking`, `follow` | обязательный preview + явный тап «Выполнить» | + +Обязательное подтверждение перед записью в API соблюдается буквально: запись в API — это только пишущие действия. + +### Dry-run preview + +Пишущие действия рендерятся в сетку **до** «Выполнить»: `create_slot_draft` — ghost-ячейкой, `book` — подсветкой целевого часа. Превью плана — тем же визуальным языком, что результат: пользователь видит ровно то, что получит. + +### Исполнение + +Последовательное, стоп на первой ошибке, без авто-продолжения. `book` всегда последний в плане и всегда с confirm; сверху — существующий owner-confirm pending (двойная защита записи). + +## Предиктивные подсказки (L1, контракт Front#75) + +- Подсказка = ghost `kind: 'hint'` в сетке + причина «почему». +- **Причины — шаблоны i18n, не LLM**: «вы обычно по вторникам в 19:00». Объяснимость без провайдера. +- Сигналы — **local-first**: паттерны bookings, `eh.timeArc.skips`, mood; ничего не уходит провайдерам. +- Частотный cap: не более N hint в неделю на экран (N уточняет Front#75). +- Dismiss пишет штраф в `timeArcMemoryStore` — тот же механизм skips; подсказка обучается отказам локально. + +## Opt-in, privacy, degrade + +### Opt-in + +- L1 и L2 — **default off**. Тумблеры в `preferences` (`PATCH /v1/user/me`), как mood; до логина — выключены безусловно. +- Выключенный L1: ghosts v1–v4 работают как сейчас, `hint` не рисуется. Выключенный L2: mark не реагирует на hold, палитра скрыта. + +### Приват-инварианты + +- Транскрипт живёт **только в памяти** на время показа плана; не пишется ни в localStorage, ни в Back. +- Контекст календаря (события, bookings, предпочтения) **никогда** не уходит провайдерам — только аудио/фраза запроса. +- Аудио не сохраняется: стриминг в STT, discard после транскрипта. + +### Degrade-матрица + +| Сценарий | L1 подсказки | L2 голос | +|----------|--------------|----------| +| Offline | работают (local-first) | inert-state + тултип; у owner доступен rule-path `create_slot_draft` | +| Mic denied / нет микрофона | n/a | открывается палитра быстрых команд | +| STT недоступен / 5xx / таймаут | n/a | один тост; палитра как fallback | +| Низкий confidence | n/a | один тост «не расслышал», без retry-цикла | +| Средний confidence | n/a | чипы-кандидаты вместо прямого исполнения | +| Target не найден | подсказка не показывается | кандидаты; пустой список → тост | +| Write-ошибка при исполнении (409/403/402) | n/a | план остановлен; стандартная ошибка флоу (402 → `/subscription`) | +| Opt-in off | ghosts v1–v4 без изменений | mark без реакции | + +**Инвариант:** ни одно состояние ИИ-слоя не блокирует первичные флоу (сетка, запись, навигация живут независимо). + +## Провайдер и Back-прокси (контракт later) + +- STT и intent-разбор — **продуктовый провайдер** (вендор STT + мелкий LLM для intent-JSON при miss rule-слоя). +- Front ходит только в прокси Back (`/v1/ai/*`): ключи провайдера не покидают бэкенд, единая точка rate-limit, аудита и смены провайдера. Паттерн идентичен GEO/OpenCage (см. [GEO.md](GEO.md)). +- Back не персистит аудио и транскрипты. +- **EventHubAiRouter — не продуктовый путь**: это dev-tooling для coding-агентов (Zed). Продуктовый ИИ-клиент о нём не знает; зависимостей Front → AiRouter нет и не будет. + +## Anti-scope + +Вне продукта (зафиксировано): + +- чат с ИИ в любой форме; Composer; отдельная AI-вкладка / AI-home; +- авто-booking и любые записи в API без подтверждения человека; +- свободные текстовые ответы ИИ и уточняющие вопросы; +- подводка EventHubAiRouter / Zed к клиентскому приложению; +- персист аудио/транскриптов, отправка контекста календаря провайдерам. + +## Критерии старта Future-реализаций + +Метрики для решения (собираются в ходе L0/Front#75): + +- hint CTR; plan-confirm-rate; plan-abort-rate; доля rule-path vs provider-path. + +Гейты: + +- **Подсказки (Front#75):** ≥20% активных пользователей с ≥2 confirmed bookings через ghosts за месяц + явное продуктовое решение. +- **Голос (Front#76):** после пилота подсказок; STT-провайдер с RU и приемлемым ToS; пилот на IFT; минимум один сценарий с конверсией лучше ручного пути. + +## Future Stories + +- [Front#75](https://git.sabilin.com/EventHub/EventHubFront/issues/75) — предиктивные подсказки (ghosts `kind: 'hint'`). +- [Front#76](https://git.sabilin.com/EventHub/EventHubFront/issues/76) — голосовой центр: mark press-to-talk + палитра + ActionPlan. diff --git a/README.md b/README.md index 89bba86..4be34e8 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ ## 7. [Карта архитектуры для Zed/агентов](ZED-ARCHITECTURE.md) ## 8. [Архив календаря (Фаза 3)](ARCHIVE.md) ## 9. [Гео и карты (Фаза 3)](GEO.md) +## 10. [ИИ клиента (Фаза 3)](PRODUCT-AI.md) # **Репозитории разработки EventHub** ## 1. [EventHub Backend](https://git.sabilin.com/EventHub/EventHubBack)