Files
AR 15 M4 60f8a7b398 feat(sprint7.6): оптимизация воронки new_booking до 4 шагов (вариант 2)
Воронка сжата с 6 шагов до 4: intro → qualify → book → close.
Спецификация: docs/OPTIMIZATION_CONVERSION_v1.md.
Цель: сравнимая с конкурентом (NEXTBOT/Александра) конверсия — ≤3 реплик
бота до запроса телефона, содержательный ответ на жалобу в первом
осмысленном сообщении.

Промпты шагов:
- intro.md — переписан. Приветствие + открытый вопрос «что беспокоит?».
  Имя НЕ спрашиваем (слот name со шага снят), оно собирается на book
  вместе с телефоном. Если пациент сразу написал жалобу — не зацикливаемся,
  переходим в qualify.
- qualify.md — переписан. Обязательный 5-пунктовый шаблон ответа на жалобу:
  эмпатия (одна фраза) → 2-3 ЛОР-гипотезы из RAG-выдержек («может быть
  связано с») → специалист → услуга/цена («при необходимости назначит») →
  бинарный CTA «записать?». Если в выдержках нет гипотез/цен — пункт
  пропускается, не сочиняем. Если жалоба не описана (пациент сразу
  «хочу записаться к ЛОРу») — пропускаем гипотезу/услугу, оставляем
  эмпатию-формальность + специалист + CTA.
  Три особые ситуации сохранены: ребёнок (require_legal_rep), конкретный
  врач (waitlist_flag), первичная жалоба на слух (needs_surgologist_first).
- book.md — переписан. Одной репликой: подтверждение плана с
  использованием {specialist}/{reason} + запрос телефона + имени (если
  ещё не было в истории). При is_child=true — обращение к родителю,
  legal_rep_phone используется, если уже собран.
- present.md — DEPRECATED. Файл оставлен в репо на случай отката
  (вариант 1 спецификации). Внутри — заглушка «попал по ошибке —
  выходи на book».
- close.md и offer_time.md не тронуты (offer_time станет актуален с
  реальным календарём).

allowed_next в SEED_INTENT_STEPS:
- intro: [intro, qualify] (без изменений)
- qualify: [qualify, book] (раньше: [qualify, present])
- present: [book] (изоляция; раньше: [present, qualify, offer_time])
- offer_time: [offer_time, book] (deprecated, без изменений)
- book: [book, qualify, close] (раньше: [book, qualify, offer_time, close])
- close: [close] (без изменений)

migrate_new_booking_allowed_next_v2(session) — одноразовая миграция в
services/intent_step_service.py. При старте для каждого шага
new_booking сравнивает текущий allowed_next_json с дореформенным
значением (_PRE_SPRINT_7_6_ALLOWED_NEXT). Если совпадает — обновляет
на новое из SEED. Если оператор правил вручную — пропускает,
warning в лог. Идемпотентна (на повторных запусках ничего не делает).
Подключена в main.py lifespan после ensure_seed_guards.

Защитное условие require_legal_rep на qualify сохранено. Теперь блокирует
переход qualify → book (раньше qualify → present). Логика та же:
при is_child=true и пустых legal_rep_name/legal_rep_phone валидатор
отклоняет переход.

eval/MANUAL_CASES.md — markdown-чеклист для ручных прогонов:
- §A: 5 конверсионных кейсов (храп+уши, боль в горле, тугоухость,
  насморк >месяца, звон в ушах) с чеклистом 5 пунктов на первый ответ
  и проверкой ≤3 реплик до телефона.
- §B: регрессия 8 ручных сценариев из блока H Спринта 6b со ссылками
  на docs/examples/*_v2.md.

SPRINTS.md: Спринт 7.6 →  Закрыт по коду. Применение промптов в БД
и ручная регрессия — за оператором (через UI «Настройки → Шаги»
для каждого из 4 шагов new_booking).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 21:04:09 +05:00

120 lines
8.8 KiB
Markdown
Raw Permalink 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.
# Ручные кейсы для регрессии
Чеклист для прогонов в Песочнице. **Не автоматизирован** — это `markdown`-чеклист, по которому оператор/разработчик прогоняет сценарии руками. Полная подсистема прогона (`eval/run.py`) — Спринт 8.
Раздел A — конверсионные кейсы Спринта 7.6 (новые). Раздел B — регрессия 8 ручных сценариев из блока H Спринта 6b (должны проходить как раньше).
---
## A · Конверсионные кейсы (Спринт 7.6)
Все 5 кейсов — про оптимизацию воронки `new_booking` до 4 шагов: `intro → qualify → book → close`. Цель проверки — сжатие воронки и содержательность первого ответа.
### Что проверяем на каждом кейсе
**Структура первого ответа бота на жалобу пациента** (5-пунктовый шаблон, см. `prompts/intents/new_booking/steps/qualify.md`):
- [ ] **Эмпатия** — одна короткая фраза (одна, не больше).
- [ ] **Гипотеза** — 2–3 ЛОР-причины формулировкой «может быть связано с…», без диагноза. Если в RAG-выдержках причин нет — пункт допустимо пропустить.
- [ ] **Специалист** — рекомендация по профилю (зафиксирован в слот `specialist`).
- [ ] **Услуга и цена** — формулировкой «при необходимости назначит». Если в RAG нет — пункт пропускается.
- [ ] **CTA** — бинарный вопрос «записать?».
**Сжатие воронки:**
- [ ] До запроса телефона — **≤ 3 реплики бота** (раньше было 5–6).
- [ ] Имя на `intro` **не спрашивается** — спрашивается на `book` вместе с телефоном.
- [ ] Граф работает по `intro → qualify → book → close`. На `present` модель не попадает (в Песочнице бейдж шага не показывает `present`).
**RAG:**
- [ ] В отладочной панели «Найденные фрагменты» видно, что чанки пришли из подписанных документов ветки `new_booking`.
- [ ] Если ветка не подписана ни на один документ — гипотеза/услуга/цена пропускаются (5-пунктовый шаблон деградирует до эмпатия + специалист + CTA).
### Кейсы
#### A.1 · «Очень сильно храплю, иногда закладывает уши»
Контрольный кейс из `docs/OPTIMIZATION_CONVERSION_v1.md` §1 (сравнение с конкурентом «Александра»).
- Ожидаемый специалист: ЛОР.
- Ожидаемые гипотезы (из вики): искривление перегородки, аденоиды, ринит.
- Ожидаемая услуга: эндоскопия, 1 000 ₽ (если в подписанных документах есть).
- Слоты после `qualify`: `reason="храп + заложенность ушей"`, `specialist="ЛОР"`.
#### A.2 · «Болит горло уже неделю, не проходит»
- Ожидаемый специалист: ЛОР.
- Ожидаемые гипотезы: тонзиллит, фарингит.
- Слоты: `reason="боль в горле, неделя"`, `specialist="ЛОР"`.
#### A.3 · «Стал плохо слышать на одно ухо, и звон»
Особая ситуация 3 (`needs_surgologist_first`).
- Ожидаемое поведение: сначала уточнить «вас уже обследовал сурдолог?», при первичном — `specialist=ЛОР`, `needs_surgologist_first=true`.
- Объяснение: «обычно начинают с ЛОР-врача, при необходимости направит к сурдологу».
#### A.4 · «Насморк больше месяца, не проходит»
- Ожидаемый специалист: ЛОР.
- Ожидаемые гипотезы: хронический ринит, синусит.
#### A.5 · «Звон в ушах, какой-то непонятный»
Аналог A.3, проверка устойчивости.
- Ожидаемое поведение: уточнение «были у сурдолога?», при первичном — ЛОР с пометкой про сурдолога.
---
## B · Регрессия 8 ручных сценариев (блок H Спринта 6b)
После переписки воронки в Спринте 7.6 — все 8 сценариев должны продолжать работать. Сравниваем с разобранными примерами в `docs/examples/*_v2.md`.
### B.1 · Базовая запись к ЛОР-врачу
- См. `docs/examples/01_basic_booking_v2.md`.
- Ожидание: путь `intro → qualify → book → close` (3 реплики бота до телефона), без особых ситуаций.
### B.2 · Soft-insertion цена в середине записи
- См. `docs/examples/02_price_during_booking_v2.md` Вариант A.
- Ожидание: на короткое «а сколько стоит?» — ответ в-line, шаг не меняется, `soft_insertion_count++`.
### B.3 · Hard-handoff в `reschedule` и возврат
- См. `docs/examples/02_price_during_booking_v2.md` Вариант B (там `price_question`, для reschedule аналогично).
- Ожидание: `suspended_intent=new_booking`, после возврата — восстановление `current_step_code` и `slots`.
### B.4 · Возврат из `suspended_intent`
- Подразумевается в B.3.
- Ожидание: при возврате `handoff_count` сбрасывается в 0.
### B.5 · Упоминание хирургии → escalate с `reason=surgery`
- Пациент в любом месте говорит «у меня уже была операция, надо перенести» — должен сработать `[INTENT_CHANGE: escalate_human]` с `reason=surgery`.
### B.6 · Петля роутера → автоэскалация с `reason=routing_loop`
- Искусственно: чередовать `new_booking ↔ price_question` 4+ раза.
- Ожидание: на 4-м переключении автоматически уйти в `escalate_human` с `reason=routing_loop` без вызова LLM.
### B.7 · Запись ребёнка с защитным условием `require_legal_rep`
- См. `docs/examples/03_child_patient_guard_v2.md`.
- Ожидание: при `is_child=true` и пустых `legal_rep_*` валидатор блокирует переход `qualify → book`. **Внимание:** в Спринте 7.6 переход теперь `qualify → book` (раньше было `qualify → present`). Защитное условие должно продолжать работать на новом переходе.
### B.8 · Конкретный врач → лист ожидания
- Пациент: «хочу к доктору Иванову».
- Ожидание: `requested_doctor="Иванов"`, `waitlist_flag=true`, фраза «администратор свяжется для уточнения даты».
---
## Как прогонять
1. Открой Песочницу (`http://localhost:8000/sandbox.html`).
2. Создай новый тред для каждого кейса (чтобы счётчики `handoff_count` и `soft_insertion_count` были чистыми).
3. Веди диалог как пациент, проставляй галочки в чеклисте по факту.
4. Если что-то не так — отметь словесно, приложи скрин/реплику. Возвращаемся в код, правим, прогоняем снова.
## Что НЕ делает этот документ
- Не запускается автоматически. Для автозапуска — Спринт 8 (`eval/run.py`).
- Не покрывает все возможные граничные случаи маршрутизатора. Для них есть `eval/router_cases_*.jsonl` (тоже к Спринту 8).
- Не сравнивает с baseline по метрикам. Это всё прогоны «глазами».