docs: перекройка плана спринтов под графовую архитектуру
После Спринта 3 и обсуждения с пользователем зафиксирован разворот
на графовую архитектуру (GRAPH_ARCHITECTURE.md, коммит 907fdbe).
Обновляем SPRINTS.md под новое направление.
Статусы закрытых спринтов уже стояли ✅; здесь главное — план 4–7:
- Вставлена секция «Архитектурный разворот после Спринта 3» сразу
после Спринта 3. В ней зафиксированы принятые решения по 5
открытым вопросам из GRAPH_ARCHITECTURE.md: фреймворк (вручную,
без LangGraph/n8n), модель роутера (DeepSeek + отдельный
RouterClient), exit conditions (свободный текст + роутер на каждой
реплике как подстраховка), эскалация (одна ветка escalate_human с
полем reason), confidence score (не в первый спринт).
- Старые Спринт 4 (сценарии) и Спринт 5 (экспорт) заменены новыми
четырьмя спринтами:
- Спринт 4. Фундамент графа — intents + роутер + переключение веток.
Задачи: таблица intents, миграция agent_configs с intent_id, seed
6 стартовых веток, router_client, оркестрация в chat_service,
UI настроек по веткам, отображение intent в отладке Песочницы.
- Спринт 5. State machine + exit conditions (bouncing). Первая
реальная state machine — в ветке new_booking (6 шагов). Парсер
[INTENT_CHANGE: ...] + роутер на каждой реплике как подстраховка.
UI показывает состояние треда и timeline переходов.
- Спринт 6. Мульти-RAG. Фабрика коллекций в vectorstore, привязка
коллекции к intent, селектор ветки при загрузке документа.
- Спринт 7. Сценарии + экспорт графа. Старый план, но адаптирован:
сценарии фиксируют ожидаемый intent на реплику; экспорт —
снапшот всего графа (intents + активные промпты + коллекции).
- Бэклог дополнен: confidence score, визуализация графа, вынесение
роутера на более дешёвую модель, структурированные exit conditions.
Пункт про «раздельные правила по доменам» помечен как перекрытый
архитектурой.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+130
-32
@@ -142,60 +142,158 @@
|
||||
|
||||
---
|
||||
|
||||
## Спринт 4. Сценарии: сохранение и прогон
|
||||
## Архитектурный разворот после Спринта 3 (2026-04-23)
|
||||
|
||||
### Цель
|
||||
Позволить операторам сохранять отработанные диалоги из песочницы как «сценарии» (с пометкой «эталон / ок / не ок» и заметкой), а потом прогонять их на текущей конфигурации, чтобы сразу увидеть, не сломалось ли что-то после правок.
|
||||
После пилота Спринтов 1–3 решили уходить от одного «мега-промпта» ко графовой архитектуре: **роутер намерений + изолированные ветки + state machine + exit conditions**. Подробности — в `GRAPH_ARCHITECTURE.md`.
|
||||
|
||||
### Статус: ⏳ Запланирован
|
||||
**Принятые решения по открытым вопросам:**
|
||||
- **Фреймворк оркестровки:** пишем вручную на Python. LangGraph/n8n не берём — проект компактный, свой стек работает, не тянем лишних зависимостей.
|
||||
- **Модель для роутера:** остаёмся на DeepSeek, но `RouterClient` делаем отдельным классом от `LLMClient` — потом сменим модель в одном месте, если станет дорого.
|
||||
- **Exit conditions:** свободный текст в промпте ветки + независимый роутер на каждой реплике. Если ветка пропустит триггер — роутер подстрахует.
|
||||
- **Эскалация на человека:** одна ветка `escalate_human` с полем `reason` (`acute_pain` / `surgery` / `angry` / `explicit_request`). Отдельная маршрутизация «куда именно» — задача смежного разработчика при подключении каналов.
|
||||
- **Confidence score:** не тянем в первый спринт. Роутер всегда возвращает один из intent'ов, при сомнении — `general_info`. После первого живого прогона посмотрим на реальные ошибки.
|
||||
|
||||
### Задачи
|
||||
- [ ] Хранилище (SQLite): `scenarios` (id, name, note, label, messages_json, reference_answers_json, config_version_id)
|
||||
- [ ] Эндпоинты:
|
||||
- [ ] `POST /scenarios` — сохранить текущий тред песочницы как сценарий
|
||||
- [ ] `GET /scenarios`, `GET /scenarios/{id}`
|
||||
- [ ] `POST /scenarios/{id}/run` — прогнать реплики пациента на текущей активной конфигурации, вернуть новые ответы агента
|
||||
- [ ] Возможность пометить «правильный ответ оператора» для каждой реплики пациента (эталон)
|
||||
- [ ] Веб-страница «Сценарии»:
|
||||
- [ ] список сценариев с метками и датой
|
||||
- [ ] открытая карточка: реплики пациента, ответы агента при сохранении, опциональные «эталонные ответы»
|
||||
- [ ] кнопка «Прогнать на текущей конфигурации» → показывает side-by-side: старый ответ / новый ответ
|
||||
- [ ] счётчик «сколько сценариев остались в статусе ок» после последнего прогона
|
||||
|
||||
### Критерий готовности
|
||||
- [ ] Оператор может сохранить диалог как сценарий, добавить эталонные ответы, пометить «ок»
|
||||
- [ ] После изменения промпта прогон сценариев показывает, где ответы расходятся
|
||||
- [ ] Виден общий счётчик «ок / изменилось» по всей базе сценариев
|
||||
Старые Спринт 4 (сценарии) и Спринт 5 (экспорт) не удалены — они переехали в Спринт 7 с дополнением под граф (прогон сценариев проверяет маршрутизацию, экспорт — снапшот графа).
|
||||
|
||||
---
|
||||
|
||||
## Спринт 5. Экспорт конфигурации для внешней интеграции
|
||||
## Спринт 4. Фундамент графа — `intents` + роутер + переключение веток
|
||||
|
||||
### Цель
|
||||
Зафиксировать API-контракт и упаковать активную конфигурацию так, чтобы другой разработчик мог подключить чат-канал (приложение / МАКС-бот) без обращений к нам.
|
||||
Заменить «один активный промпт на всё» на «свой промпт на каждую ветку + роутер выбирает ветку на каждой реплике». Это первый шаг к графовой архитектуре из `GRAPH_ARCHITECTURE.md`.
|
||||
|
||||
### Статус: ⏳ Запланирован
|
||||
|
||||
### Задачи
|
||||
- [ ] Документация API: `POST /chat` (с `channel_id`, `user_id`, `thread_id`, `text`), `GET /health`
|
||||
- [ ] Эндпоинт `GET /configs/active/export` — JSON со снапшотом: активный промпт + правила + список документов RAG
|
||||
- [ ] Инструкция «как подключиться» в README (пример curl-запроса + минимальный webhook-адаптер)
|
||||
- [ ] Проверка: внешний разработчик может поднять сервис по docker-compose и получить валидный ответ от `/chat`
|
||||
|
||||
**Данные:**
|
||||
- [ ] Новая таблица `intents` (code, name, description, is_enabled, order_index)
|
||||
- [ ] Миграция Alembic + в `agent_configs` добавить `intent_id` (nullable для обратной совместимости)
|
||||
- [ ] Сид при первом запуске: 6 стартовых веток — `new_booking`, `reschedule`, `price_question`, `medical_question`, `general_info`, `escalate_human`
|
||||
- [ ] Перенос текущего v1 конфига в ветку `general_info` как стартовый промпт
|
||||
|
||||
**Роутер:**
|
||||
- [ ] `services/router_client.py` — отдельный класс под DeepSeek, метод `classify(history, text) → intent_code`
|
||||
- [ ] Короткий промпт-классификатор с фиксированным перечнем категорий
|
||||
- [ ] При сомнении возвращает `general_info` (без confidence score на этом спринте)
|
||||
|
||||
**Оркестрация:**
|
||||
- [ ] В `chat_service.send_message`: сначала `router.classify()` → активный конфиг выбранной ветки → `llm.chat()` с этим промптом
|
||||
- [ ] В таблице `messages` сохраняется `intent_id` каждого обмена
|
||||
|
||||
**API:**
|
||||
- [ ] `GET /intents` — список веток
|
||||
- [ ] `PATCH /intents/{code}` — включить/выключить
|
||||
- [ ] `POST /configs` принимает `intent_id`; создание новой версии — всегда в рамках ветки
|
||||
|
||||
**UI:**
|
||||
- [ ] «Настройки»: слева список веток, справа редактор промпта/правил активной версии выбранной ветки
|
||||
- [ ] В «Песочнице» в отладке показывать: решение роутера + выбранный intent + какая ветка ответила
|
||||
|
||||
### Критерий готовности
|
||||
- [ ] README раздел «Как подключить канал» готов
|
||||
- [ ] Docker-compose поднимается одной командой
|
||||
- [ ] На заданном тестовом запросе `/chat` возвращает ответ, который мы видим и в веб-песочнице
|
||||
- [ ] «У меня острая боль» → `medical_question`
|
||||
- [ ] «Сколько стоит приём» → `price_question`
|
||||
- [ ] «Как доехать» → `general_info`
|
||||
- [ ] В отладочной панели «Песочницы» виден intent и какая ветка дала ответ
|
||||
- [ ] Для каждой ветки можно отдельно править промпт и сохранять версии
|
||||
|
||||
---
|
||||
|
||||
## Спринт 5. State machine + exit conditions (bouncing)
|
||||
|
||||
### Цель
|
||||
Научить ветки вести многошаговые скрипты и бесшовно передавать тред в другую ветку, если пациент сменил тему.
|
||||
|
||||
### Статус: ⏳ Запланирован
|
||||
|
||||
### Задачи
|
||||
|
||||
**Данные:**
|
||||
- [ ] Таблица `thread_state` (thread_id, current_intent, current_step, slots JSON)
|
||||
|
||||
**State machine (первая ветка — `new_booking`):**
|
||||
- [ ] 6-шаговый скрипт: приветствие → перехват инициативы → мини-интервью по симптому → презентация двух слотов → подтверждение → запись
|
||||
- [ ] Модель на каждой реплике видит текущий шаг + собранные слоты (имя, симптом, выбранный слот)
|
||||
- [ ] Переход шагов управляется правилами в промпте ветки («если на шаге 3 пациент назвал время — перейди к шагу 5»)
|
||||
|
||||
**Exit conditions и bouncing:**
|
||||
- [ ] В промпт каждой ветки добавляется блок условий выхода
|
||||
- [ ] Парсер ответа ассистента ловит служебный сигнал `[INTENT_CHANGE: <code>]` → останавливает ветку
|
||||
- [ ] Роутер на каждой реплике: если классификация ≠ текущему `thread_state.current_intent` → `thread_state` сбрасывается, тред идёт в новую ветку с полной историей
|
||||
|
||||
**UI:**
|
||||
- [ ] В «Песочнице» новый блок «состояние треда»: текущий intent, шаг, собранные слоты
|
||||
- [ ] История переходов между ветками в рамках треда (timeline)
|
||||
|
||||
### Критерий готовности
|
||||
- [ ] Сценарий из `GRAPH_ARCHITECTURE.md` («запись → пациент упомянул операцию → хирургия/оператор») проходит без сброса контекста
|
||||
- [ ] Ветка `new_booking` уверенно ведёт 6-шаговый скрипт на 3+ тестовых диалогах
|
||||
- [ ] В отладке видна вся цепочка: начальный intent → шаги → смена ветки → финальный intent
|
||||
|
||||
---
|
||||
|
||||
## Спринт 6. Мульти-RAG
|
||||
|
||||
### Цель
|
||||
Дать каждой ветке свою коллекцию в Chroma, чтобы детская wiki не засоряла ответы общей записи, а скрипты возражений — ответы по ценам.
|
||||
|
||||
### Статус: ⏳ Запланирован
|
||||
|
||||
### Задачи
|
||||
- [ ] Рефакторинг `services/vectorstore.py`: фабрика коллекций, `collection_by_intent(intent_code)` вместо единственной `operators_wiki`
|
||||
- [ ] В `intents` — поле `collection_name` (nullable; если пусто — используется общая `common_wiki`)
|
||||
- [ ] В UI загрузки документа — селектор «в какую ветку залить (или в общую)»
|
||||
- [ ] `POST /documents/upload` принимает `intent_code` как опциональный параметр
|
||||
- [ ] `reindex-all` учитывает коллекции (одна команда — все коллекции)
|
||||
- [ ] В «Отладке» — фильтр по веткам для просмотра документов
|
||||
|
||||
### Критерий готовности
|
||||
- [ ] Документ, загруженный в ветку «детский приём», не появляется в retrieval для других веток
|
||||
- [ ] Общая коллекция `common_wiki` — fallback для веток без собственной базы (например, `general_info`)
|
||||
- [ ] После переключения ветки в диалоге retrieved-чанки берутся из нужной коллекции
|
||||
|
||||
---
|
||||
|
||||
## Спринт 7. Сценарии + экспорт графа
|
||||
|
||||
### Цель
|
||||
То, что изначально планировалось как Спринты 4 + 5 до архитектурного разворота. Теперь встроено в граф: прогон сценария проверяет не только текст ответов, но и правильность маршрутизации; экспорт — снапшот всего графа (intents + промпты + коллекции).
|
||||
|
||||
### Статус: ⏳ Запланирован
|
||||
|
||||
### Задачи
|
||||
|
||||
**Сценарии:**
|
||||
- [ ] Таблица `scenarios` (id, name, note, label, messages_json, expected_intents_json, config_snapshot_id)
|
||||
- [ ] `POST /scenarios` — сохранить текущий тред «Песочницы» как сценарий, зафиксировать ожидаемый intent на каждую реплику пациента
|
||||
- [ ] `POST /scenarios/{id}/run` — прогнать реплики пациента на текущих активных конфигах всех веток; вернуть новые ответы + распознанные intents
|
||||
- [ ] Веб-страница «Сценарии»: список + открытая карточка со side-by-side (старый ответ / новый), подсветка «маршрутизация совпала / разошлась»
|
||||
- [ ] Счётчик «ок / расхождение» по всей базе сценариев после последнего прогона
|
||||
|
||||
**Экспорт:**
|
||||
- [ ] `GET /configs/export` — JSON-снапшот графа: все intents, для каждого — активный промпт и правила, список коллекций RAG и документов в них
|
||||
- [ ] Документация API в README: `POST /chat`, `GET /health`, контракт ответов
|
||||
- [ ] Инструкция «Как подключить канал» + пример curl / минимальный webhook-адаптер
|
||||
- [ ] docker-compose поднимается одной командой, внешний разработчик получает рабочий `/chat`
|
||||
|
||||
### Критерий готовности
|
||||
- [ ] После изменения промпта в одной из веток — прогон сценариев показывает расхождения именно в этой ветке
|
||||
- [ ] Виден общий счётчик «ок / изменилось» по базе сценариев
|
||||
- [ ] В README готов раздел «Как подключить канал», работает docker-compose-запуск
|
||||
|
||||
---
|
||||
|
||||
## Бэклог
|
||||
|
||||
- Раздельные правила по доменам (детский приём / ДМС / взрослый приём)
|
||||
- A/B сравнение двух версий промпта на одном тест-наборе
|
||||
- Раздельные правила по доменам — **перекрыто архитектурой: теперь это ветки (`intents`)**
|
||||
- A/B сравнение двух версий промпта на одном тест-наборе (в рамках одной ветки или между ветками)
|
||||
- Метрики качества ответов (MRR, CSAT по сценариям)
|
||||
- Подсветка цитат источников в ответе агента
|
||||
- Автосинхронизация wiki
|
||||
- Перевод правил из свободного текста в структурированный список (pattern → instruction)
|
||||
- Мультипользовательский режим (несколько операторов одновременно настраивают)
|
||||
- Хранение исходных файлов (`./data/uploads/{document_id}.{ext}` + `source_path` в метаданных Chroma) — чтобы переиндексировать без повторной загрузки и показывать оператору оригинал документа
|
||||
- Confidence score роутера + clarifying question при низкой уверенности — включить после реального прогона, если будет много ошибок классификации
|
||||
- Визуализация графа (веток и переходов между ними) — возможно, в виде отдельной панели
|
||||
- Вынесение роутера на отдельную более дешёвую модель (gpt-4o-mini, локальная Qwen) — когда вызовов станет много
|
||||
- Структурированные exit conditions (список триггеров с keyword-match) — если свободный текст в промпте будет пропускать реальные случаи смены темы
|
||||
|
||||
Reference in New Issue
Block a user