Files
obsidian-vault/personal/projects/psychologist-app/session-design.md
T

14 KiB
Raw Blame History

title, aliases, created, updated, tags, related
title aliases created updated tags related
Session Design — AI Psychologist App
Дизайн сессии
Session design
архитектура сессий
2026-05-19 2026-06-02
project
app
psychology
ux
architecture
personal/projects/psychologist-app/overview
personal/projects/psychologist-app/character-design
personal/projects/psychologist-app/onboarding-ux

Session Design — AI Psychologist App

Детальный дизайн сессии: UX-флоу, механика вопросов, открытие/закрытие. Связан с: overview (раздел 5-6 — базовая структура и архитектура ботов)


1. Механика вопросов (Question Delivery)

Pipeline вопросов

Бот 2 (Analyst / "Психолог")
  → генерирует 1-2 вопроса в 3-м лице ("пользователь избегает...")
        ↓
Бот 1 (Narrator / "Посредник")
  → переформулирует в 1-е лицо ("ты избегаешь?")
  → на выходе: структурированный массив вопросов для UI
        ↓
UI алгоритм (не агент!)
  → показывает вопросы по одному в карточках
  → ждёт ответа → следующий

Ключевой принцип: раздача вопросов — детерминированный алгоритм, не LLM-вызов. Агент генерирует батч вопросов один раз → посредник переформулирует → UI сам управляет порядком.

Narrator детектирует 3-е лицо: через prompt rule ("переформулируй в 1-е лицо") — регекс не нужен.

Завершение батча вопросов → подбивка

Когда все вопросы в батче отвечены, UI передаёт агенту нумерованную подбивку ответов. Это входные данные для следующей итерации (следующий батч или инсайт).


2. Открытие сессии

Структура intro

  1. Персонаж-активация (анимация, 1-2 сек)
  2. Короткое введение — 1-2 предложения, сразу к делу, без "Давай начнём с..."
  3. "Крючки-активаторы" — max 2 из profile.md / последних сессий. Если нет истории — пропустить.
  4. Переход к теме — предложение агента или выбор пользователя

Что получает Analyst при старте

  • profile.md — паттерны пользователя
  • последние 2 сессии из observations/
  • задача: intro + max 2 крючка

3. Закрытие сессии

Последовательность (3 этапа)

Этап 1 — Structured session close (backend, не видно пользователю)

Analyst генерирует SessionCloseResult:

  • insight — ключевое наблюдение (1-2 предложения, 3-е лицо)
  • sessionSummary — аналитическое резюме всей сессии (не первые реплики)
  • profileUpdates — новые паттерны для добавления в profile.md
  • promptUpdates — обновления для prompts.md (если применимо)

Приложение раскладывает результат по файлам через ProfileManager.

Статус: частично реализован. insight генерируется (Sonnet + tools + полный диалог). sessionSummary, profileUpdates, promptUpdates — НЕ реализованы (см. §12.2.G).

Этап 2 — Посредник: человечный итог (видно пользователю)

  • Тёплая живая фраза, без психолого-языка
  • Не повторять аналитика, не хвалить избыточно
  • Упомянуть дату следующей сессии если есть расписание

Этап 3 — "One last thing" + завершение

  • Открытый вопрос: "Хочешь что-то добавить?"
  • Маленький текстовый input, неакцентная кнопка "Завершить"
  • Если написал → агент добавляет в заметки сессии, отвечает коротко (не перезапускает сессию)
  • Если упомянул перенос → сценарий планировщика (EventKit)

4. Флоу сессии целиком

ОТКРЫТИЕ
├── Анимация персонажа (1-2 сек)
├── Intro: 1-2 предложения + max 2 крючка из истории
└── Выбор темы

ДИАЛОГ
├── Analyst генерирует 1-2 вопроса (3-е лицо)
├── Narrator переформулирует → карточки (1-е лицо)
├── UI раздаёт по одному (one-card UX)
├── Пользователь отвечает → нумерованная подбивка
└── Следующий батч / инсайт
    └── session_complete: true → карточка "Завершить / Продолжить"
        └── hard limit: 8 ходов → принудительный readyToClose

ЗАКРЫТИЕ
├── [backend] Analyst: insight + sessionSummary + profileUpdates
├── Посредник: тёплая фраза + дата следующей сессии
├── "One last thing"
│   ├── Написал что-то → в заметки, короткий ответ
│   ├── Упомянул перенос → сценарий планировщика
│   └── Завершить (кнопка) → сессия закрыта

5. State Persistence & Session Recovery

Текущая реализация (актуально)

resumeOrStart() при открытии SessionView:

  • Проверяет незавершённую сессию (detectIncompleteSession())
  • .resumed → восстанавливает карточки из session.messages (видимые .analyst)
  • .startedAfterPartialSummary → была стale сессия >20h → показывает partial summary, затем чистый старт
  • .started → обычный старт

IncompleteSessionState.isFresh — сессия считается свежей если < 20 часов. Stale → generatePartialSummary()completed_partial.

Что не реализовано (Stage 2)

Retention protocol: при N пропущенных сессиях подряд → реактивационный флоу с push-уведомлением.


6. Что реализовано сейчас

Актуально на 2026-06-02, коммит 4f02d565.

Пейсинг сессии

  • AnalystJSON.sessionComplete: Bool (backward-compatible)
  • ProcessingResult.readyToClose([String]?, observation:)
  • SessionManager.userTurnCount, consecutiveShortAnswerCount
  • Hard limit: 8 ходов → принудительный .readyToClose
  • continueSession() — сброс флага после "Продолжить"
  • Пейсинг-правила и директивы усталости в systemPrompt AnalystBot

Session Recovery

  • detectIncompleteSession(), resumeOrStart(), generatePartialSummary()
  • FatigueContext, IncompleteSessionState, SessionResumeResult
  • SessionViewModel: восстановление карточек при .resumed

Завершение сессии

  • endSession()generateSessionInsight() (Sonnet + tools + полный диалог, без обрезки)
  • Агент вызывает search_sessions, read_session, read_profile для сравнения с историей
  • finalInsightProfileManager.addInsight() немедленно
  • suggestedInsight по ходу диалога → addInsight() немедленно (не теряется при X)

Агент / промпты

  • NarratorBot: legit-маркер, injection guard, off-topic директива, детекция усталости/настроения
  • AnalystBot: turnCount/duration/messageCount/fatigueCtx в user message; no-filepath директива

UX

  • One-card UX: CardData.isActive, appendCard(), activeCard, historyCards
  • История — dimmed compact cards (read-only)
  • TextEditor с placeholder, auto-grow (36→160pt), кнопка dismiss клавиатуры
  • CardData.isCompletion → quickReply "Завершить" → endSession(), "Продолжить" → continueAfterCompletion()

Тесты

  • 202 теста, 0 фейлов
  • Покрыто: пейсинг, усталость, injection guard, crisis, session recovery, one-card UX, insights, snapshot prompts

7. Что не реализовано (план)

7.1 Structured session close [ПРИОРИТЕТ]

Что в плане (§3, §4): Analyst возвращает структурированный объект — insight + sessionSummary + profileUpdates.

Что в коде: generateSessionInsight() возвращает только одну строку-инсайт.

  • profile.md после сессии не обновляется новыми паттернами
  • prompts.md не обновляется
  • sessions/YYYY-MM-DD.md заполняется через saveSessionToProfile() где summary = первые 3 реплики пользователя, не аналитическое резюме

Решение: SessionCloseResult { insight: String, sessionSummary: String, profileUpdates: [String], promptUpdates: [String]? }. Агент возвращает структуру → приложение раскладывает по файлам.

7.2 mood_delta → Orb visual feedback

AnalystJSON → добавить moodDelta: "declining" | "stable" | "improving". UI: declining = холодный синий/медленный пульс, stable = нейтральный, improving = amber/живой.

7.3 Планировщик как tool

EventKit-интеграция как tool у Narrator — аналогично search_sessions / read_profile. Дизайн-решение отложено.

7.4 Retention protocol

При N пропущенных сессиях подряд → реактивационный флоу (§5).

7.5 Android: TextEditor в Skip Fuse

Проверить поддержку TextEditor + fixedSize(horizontal:vertical:) в Skip Fuse на Android. При необходимости заменить на TextField(axis: .vertical). .scrollContentBackground(.hidden) уже огорожен #if os(iOS).


8. Открытые вопросы

  • Планировщик — tool у Narrator (EventKit). Когда и как реализовывать?
  • mood_delta — 3 значений достаточно? (declining/stable/improving)
  • Android: TextEditor в Skip Fuse — нужна проверка на устройстве

9. Протокол агента — действующие правила

NarratorBot systemPrompt содержит:

  • Legit-маркер (защита от injection false positive)
  • Injection guard директива
  • Off-topic директива (мета-вопросы → вернуть фокус)
  • Детекция усталости → передать в metadata для Analyst
  • Детекция настроения → sentimentObservation в metadata

AnalystBot systemPrompt содержит:

  • Пейсинг-правила (turn_count, 4+ оборотов → оценка exhaustion, 7+ → принудительно)
  • Директива при усталости: session_complete: true, questions: []
  • Директива при мета-вопросах: не объяснять архитектуру, вернуть фокус
  • No-filepath директива: не упоминать пути файлов

AnalystBot tools (read-only):

  • search_sessions — поиск по архиву
  • read_session — читать конкретную сессию
  • get_index — индекс всех сессий
  • read_profile — профиль пользователя
  • list_files / read_file — дополнительные файлы профиля

10. Хранение данных (on-device)

Всё хранится через ProfileManager в Documents/UserProfile/:

Файл Что содержит Кто пишет
profile.md Паттерны пользователя, наблюдения updateProfile() — пока не вызывается автоматически после сессии
insights.md Инсайты по сессиям addInsight() — пишется немедленно
prompts.md Промпты/темы пользователя не обновляется автоматически
observations/Www.md Запись сессии за неделю appendSession()
index.md Индекс сессий updateSessionIndex()

Расписание: через системный календарь (EventKit), не отдельный файл.


Связанные заметки