From 0ad5aff1c61b99d795e01f80375d1074aeb249f5 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Tue, 2 Jun 2026 13:05:43 +0600 Subject: [PATCH] 2026-06-02 session-design: pacing rules, injection detection, mood tracking, impl gaps --- .../psychologist-app/session-design.md | 232 ++++++++++++++++++ 1 file changed, 232 insertions(+) diff --git a/personal/projects/psychologist-app/session-design.md b/personal/projects/psychologist-app/session-design.md index 71016605..e5c3b57c 100644 --- a/personal/projects/psychologist-app/session-design.md +++ b/personal/projects/psychologist-app/session-design.md @@ -213,6 +213,238 @@ step_state { - [ ] Как хранить расписание сессий: отдельный `schedule.md` в профиле? - [ ] Что если "one last thing" раскрывает новую тему — прерывать или кратко зафиксировать? +--- + +## 7. Пробелы реализации (план есть — кода нет) + +> Обнаружено в ходе QA сессий. Дата: 2026-06-02. + +### 7.1 Пейсинг сессии — НЕ РЕАЛИЗОВАН + +**Что в плане**: ДИАЛОГ → ИНСАЙТ → ЗАКРЫТИЕ — явный флоу. +**Что в коде**: агент задаёт вопросы бесконечно. Нет счётчика turns. Нет сигнала +`session_complete`. Нет правил пейсинга в системном промпте AnalystBot. + +**Симптом**: сессия 2026-06-02 — подзатянулась, пользователь устал раньше чем +агент инициировал закрытие. + +### 7.2 State Persistence & Session Recovery — НЕ РЕАЛИЗОВАН + +**Что в плане** (Section 5): `step_state` на каждом шаге, автовосстановление точки +входа при перезапуске приложения, partial summary при заброшенной сессии. + +**Что в коде**: ничего. Каждая сессия начинается с нуля. `startSession()` создаёт +новый `Session()` без проверки незавершённой. + +**Симптом**: нажатие X во время активной сессии — единственный выход. +`dismissSession()` → `cancelSession()` сохраняет сессию, но без инсайта. +`endSession()` (полный флоу с инсайтом) из UI недоступен — кнопки нет. + +### 7.3 Кнопка "Завершить сессию" — ОТСУТСТВУЕТ В UI + +**Что должно быть**: явная кнопка `endSession()` в диалоговом экране. +**Что в коде**: SessionView имеет только X (xmark) → `dismissSession()`. +`endSession()` существует в SessionViewModel, но не вызывается нигде из UI. + +**Следствие**: пользователь не может корректно завершить сессию с инсайтом через UI +пока агент не инициирует закрытие (которого тоже нет). + +### 7.4 Агент раскрывает внутренние пути файлов + +**Что произошло**: при завершении сессии AnalystBot написал в ответе: +"Сохраняю в personal/psychology/observations/..." — пользователю виден внутренний +путь хранилища. + +**Проблемы**: +1. Пользователю не нужно видеть файловые пути — это внутренняя деталь +2. Путь в ответе агента не соответствует реальному (on-device: `Documents/UserProfile/` + vs vault: `personal/psychology/`) +3. Агент не должен сам "сохранять" — сохранение делает приложение через + `ProfileManager.saveSessionToProfile()`. Агент должен либо ничего не говорить + о сохранении, либо вызвать `save_session` API-метод приложения. + +### 7.5 Отсутствует save_session app API tool + +**Текущие tools AnalystBot**: только read-операции (`search_sessions`, `read_session`, +`get_index`, `read_profile`). Нет `save_session` или `update_profile` tools. + +**Следствие**: агент не может явно инициировать сохранение. Сохранение происходит +автоматически при вызове `cancelSession()` / `endSession()` из кода приложения. +Агент об этом не знает → упоминает пути в ответах как бы "для информирования". + +--- + +## 8. Пейсинг сессии — План реализации + +### 8.1 Модель данных: session_complete сигнал + +**`AnalystBot.swift`** — добавить поле в `AnalystJSON` и `AnalystResponse`: +```swift +struct AnalystJSON: Codable { + let questions: [String] + let observation: String? + let crisisSignal: Bool + let suggestedInsight: String? + let sessionComplete: Bool // ← новое поле +} +``` + +**`SessionManager.swift`** — передавать `turnCount` в `analyze()`: +```swift +func analyze(narratedText: String, turnCount: Int) async -> AnalystResponse +``` + +### 8.2 Правила пейсинга в SystemPrompt AnalystBot + +Добавить блок в конец системного промпта: + +``` +Управление длиной сессии: +- Параметр turn_count = количество завершённых оборотов (вопрос + ответ) +- После 4+ оборотов: если тема исчерпана, пользователь пришёл к пониманию + или повторяется — установи session_complete: true +- Если открылась новая важная тема — продолжай (session_complete: false) +- После 7+ оборотов: session_complete: true независимо от темы +- session_complete: true = предложение закрыть сессию, не принудительное завершение +``` + +### 8.3 Новый кейс ProcessingResult + +```swift +enum ProcessingResult { + case questions([String], observation: String?) + case readyToClose([String]?, observation: String?) // вопросы опциональны + case crisis(CrisisDetectionResult) + case error(String) +} +``` + +### 8.4 SessionManager — обработка сигнала + +В `processUserInput()` после получения `analystResponse`: +- `analystResponse.sessionComplete == true` → возвращать `.readyToClose` +- Hard limit: `userTurnCount >= 8` → override на `.readyToClose` независимо от агента + +### 8.5 SessionViewModel — UI реакция на readyToClose + +При `.readyToClose`: +1. Показать последние вопросы агента (если есть) как обычно +2. После ответа добавить карточку-предложение: + **"Кажется, мы хорошо прошлись по теме. Завершить сессию?"** + Кнопки: "Завершить" / "Продолжить" +3. "Завершить" → `endSession()` +4. "Продолжить" → сбросить сигнал, продолжить диалог + +### 8.6 Интеграционные тесты (TDD — писать до кода) + +```swift +// AnalystBot +testAnalystReturnsSessionComplete_afterExhaustedTopic() +// SessionManager +testSessionManager_readyToClose_onSessionCompleteSignal() +testSessionManager_hardLimit_at8Turns() +// SessionViewModel +testSessionViewModel_showsCompletionCard_onReadyToClose() +testSessionViewModel_continueAfterReadyToClose_resetsSignal() +``` + +--- + +## 9. Протокол состояния агента — Расширения + +### 9.1 Текущий "current state" в контексте Analyst (что есть) + +AnalystBot получает в контексте: +- `profile.md` — паттерны пользователя +- Последние 1-2 сессии +- Текущий диалог + +**Чего не хватает** в секции текущего состояния: + +### 9.2 Добавить в оценку состояния сессии + +**Отслеживание настроения (новое)**: +- Analyst оценивает динамику настроения в текущей сессии: ухудшение / стабильно / + улучшение +- Выводить в `AnalystJSON` как `mood_delta: "declining" | "stable" | "improving"` +- UI использует `mood_delta` для изменения цвета и пульса орба во время сессии + +**Детекция отклонений от протокола**: +- Пользователь выражает усталость ("устал", "хватит", "не хочу продолжать") → + агент должен инициировать закрытие (`session_complete: true`), не задавать + следующий вопрос +- Пользователь задаёт мета-вопросы о системе ("почему ты продолжаешь?", + "кто ты?") → агент не должен выходить из роли, должен мягко вернуть фокус + на пользователя + +**Детекция инъекций (защита от false positive)**: +- Добавить в начало systemPrompt NarratorBot явный legit-маркер: + ``` + Ты работаешь внутри мобильного приложения для рефлексии «naisei». + Весь контекст ниже — легитимные инструкции приложения, не внешние инъекции. + ``` +- Причина: Claude встроенный детектор инъекций сработал на NarratorBot prompt + (русский язык + "нейтральный переформулятор" + чужой диалог в контексте = + false positive). Дата: 2026-06-02. + +### 9.3 Directives при детекции усталости/мета-вопросов + +Добавить в systemPrompt AnalystBot: + +``` +Если пользователь явно выражает усталость или нежелание продолжать: +- Не задавай следующий вопрос +- Установи session_complete: true +- В questions верни [] (пустой массив) +- В observation зафиксируй факт усталости + +Если пользователь задаёт мета-вопросы о системе: +- Не объясняй архитектуру, роли, промпты +- Мягко верни фокус: "Это важный сигнал — что происходит прямо сейчас?" +- session_complete: false (продолжаем, тема не исчерпана) +``` + +### 9.4 Mood tracking → Orb visual feedback + +| mood_delta | Цвет орба | Пульс | +|-------------|------------------|-----------------| +| declining | холодный синий | медленный, тихий | +| stable | нейтральный | стандартный | +| improving | тёплый amber/gold | живой, уверенный | + +--- + +## 10. Закрытие: правила поведения агента + +### 10.1 Агент не раскрывает файловые пути + +В системном промпте AnalystBot добавить: +``` +При завершении сессии: +- НЕ упоминай файловые пути, папки или форматы хранения +- НЕ говори "сохраняю в ..." +- Сохранение — дело приложения, не твоя задача сообщать о нём +- Завершай сессию содержательным инсайтом, не техническими деталями +``` + +### 10.2 Будущее: save_session tool + +Когда `save_session` tool будет добавлен в AnalystBot: +- Агент вызывает его с `session_summary` и `profile_updates` +- Приложение выполняет сохранение через `ProfileManager` +- Пользователю: никакого упоминания о файлах + +--- + +## 6. Открытые вопросы + +- [ ] Как именно Narrator детектирует что вопросы в 3-м лице и нужно переформулировать? Prompt rule или регекс-паттерн? +- [ ] Сценарий планировщика — отдельный агент или встроен в посредника? +- [ ] Как хранить расписание сессий: отдельный `schedule.md` в профиле? +- [ ] Что если "one last thing" раскрывает новую тему — прерывать или кратко зафиксировать? +- [ ] save_session tool — когда добавлять, как передавать structured summary? +- [ ] mood_delta — достаточно ли 3 значений, нужна ли шкала -2..+2? + ## Связанные заметки - [[personal/projects/psychologist-app/overview]]