# Пример 04 · Простые информационные запросы (general_info) > ⚠️ **Эта версия устарела.** Актуальная — [`04_general_info_simple_v2.md`](04_general_info_simple_v2.md). У ветки `general_info` в коде **нет** машины состояний, шагов (`answer`, `done`) и слотов (`info_topic`, `branch_mention`, `needs_followup`) — всё это в v1 было как иллюстрация будущего. Также нет confidence threshold для RAG и per-intent фильтров по путям вики (Спринт 7). Список изменений — внизу v2 в Changelog. > Связано с [`GRAPH_ARCHITECTURE_v3.md`](../architecture/GRAPH_ARCHITECTURE_v3.md), разделы 1, 2, 6. Демонстрирует **простейший случай**: ветка `general_info` без машины состояний — одна реплика пациента, ретривер, ответ, `done`. Никаких слотов, никаких tool calls, никаких guard'ов. Это та точка, с которой реально удобно запускать систему: классификатор + RAG + одна шаблонная ветка. ## О чём этот пример Это сборник коротких самостоятельных диалогов по одному типу запросов — общая информация (часы, адрес, проезд, телефоны, что брать с собой, чего клиника не делает). У всех у них одна и та же траектория: 1. **Роутер** классифицирует реплику как `general_info`. 2. Запускается ветка `general_info`, шаг `answer` (он же единственный). 3. **Ретривер** достаёт чанки из соответствующего раздела вики (`/wiki/contacts/**`, `/wiki/branches/**`, `/wiki/hours/**`, `/wiki/preparation/**`, `/wiki/services/_general/**`). 4. Модель формирует ответ строго по найденным чанкам. 5. `state_after = done`. Цель — на каждой реплике показать: - **решение роутера** (router) — какое намерение распознано; - **работу ретривера** (retriever) — какие чанки достаются и из каких разделов вики; - **структурированный ответ модели** — `reply`, `state_after`, `slots_updated`; - **итоговое состояние треда** (thread_state). Слоты у `general_info` практически не используются — одно поле `info_topic` модель проставляет, чтобы ретривер при последующих репликах в том же треде не лез в посторонние разделы. Если в ходе ответа возникает запрос на запись («а можно тогда записаться?»), это уже **жёсткий переход** в `new_booking` (см. примеры 05+). ## Конфигурация ветки general_info ```yaml intent: general_info steps: answer: wiki_sources: - /wiki/contacts/** - /wiki/branches/** - /wiki/hours/** - /wiki/preparation/** - /wiki/services/_general/** tools: [] next: done done: wiki_sources: [] tools: [] ``` ## Начальное состояние (общее для всех примеров ниже) Перед каждым диалогом в `thread_state` пусто — это первый ход свежего треда: ```json { "thread_id": "T-9XXX", "current_intent": null, "current_step": null, "slots": {}, "handoff_count": 0, "suspended_intent": null, "resumable_step": null, "resumable_slots": null } ``` После ответа `current_intent = "general_info"`, `current_step = "done"`, и тред физически не закрывается — пациент может задать следующий вопрос. Если он задаст ещё один информационный вопрос, тот пройдёт по той же траектории заново (роутер его классифицирует, ветка отработает и снова уйдёт в `done`). --- # A · Часы работы ## A.1 · «Во сколько вы открываетесь?» **Решение роутера:** `general_info`. Чёткий вопрос про режим работы, никаких других намерений. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/hours/**')`. - top-k: 2. - Достались чанки: - `hours/clinic-czetkina-9.md` (score 0.93) — режим работы филиала на Клары Цеткин 9. - `hours/clinic-pirogova.md` (score 0.71) — режим работы филиала «Пирогова». Модели достаточно первого чанка. Второй пригодится, если пациент уточнит про другой адрес. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Здравствуйте! Клиника на Клары Цеткин 9 открывается в 8:00 в будни и в 9:00 в выходные. Закрываемся в 21:00 в будни и в 18:00 в выходные. Если интересует другой филиал — подскажу отдельно.", "state_after": "done", "slots_updated": { "info_topic": "hours" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "hours" } } ``` --- ## A.2 · «До скольки работает клиника на Цеткина?» **Решение роутера:** `general_info`. Конкретика про филиал — это всё ещё информационный вопрос, не запись. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/hours/**', '/wiki/branches/**')`. - top-k: 2. - Достались чанки: - `hours/clinic-czetkina-9.md` (score 0.94) — режим работы. - `branches/czetkina-9.md` (score 0.78) — карточка филиала с адресом. Реплика содержит «Цеткина» — в индексе совпадает по названию филиала, поэтому ретривер уверенно вытаскивает нужную карточку. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Клиника на Клары Цеткин 9 работает до 21:00 в будни и до 18:00 в выходные.", "state_after": "done", "slots_updated": { "info_topic": "hours", "branch_mention": "Клары Цеткин 9" } } ``` Заметим: `branch_mention` модель кладёт в слоты, чтобы при возможном следующем вопросе («а проезд как?») ретривер мог сразу дать чанк именно про этот филиал, а не про другой. **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "hours", "branch_mention": "Клары Цеткин 9" } } ``` --- ## A.3 · «У вас по выходным открыто?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/hours/**')`. - top-k: 2. - Достались чанки: - `hours/clinic-czetkina-9.md` (score 0.86). - `hours/clinic-pirogova.md` (score 0.81). Здесь пациент не уточнил филиал, поэтому в ответе разумно перечислить оба. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Да, мы работаем и в субботу, и в воскресенье. На Клары Цеткин 9 — с 9:00 до 18:00, в Клинике Пирогова — с 9:00 до 17:00. Расписание конкретного врача может отличаться, при записи подскажу.", "state_after": "done", "slots_updated": { "info_topic": "hours" } } ``` Замечание: фраза «расписание конкретного врача может отличаться» — это **зашитая в промпт ветки оговорка** (canned disclaimer), не результат RAG. Часы работы клиники и слоты конкретного врача — разные вещи, и модель должна на этом не зависать. **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "hours" } } ``` --- ## A.4 · «Завтра праздник, вы работаете?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/hours/**')`. - top-k: 3. - Достались чанки: - `hours/clinic-czetkina-9.md` (score 0.74) — обычный режим. - `hours/holidays-2026.md` (score 0.42) — праздничный календарь, низкий score (документ короткий, неточное совпадение по датам). - `hours/clinic-pirogova.md` (score 0.71). Score праздничного чанка — ниже порога уверенности (0.50). По правилу шага: **если ни один чанк не покрывает запрос полностью — отвечаем шаблоном «уточним и перезвоним»**, а не выдумываем расписание праздников. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "В обычные дни мы работаем с 8:00 до 21:00. В праздничные дни режим может меняться. Подскажите, пожалуйста, на какую дату вы планируете визит — я уточню у регистратуры и сообщу вам в течение дня.", "state_after": "done", "slots_updated": { "info_topic": "hours", "needs_followup": true } } ``` Слот `needs_followup: true` — сигнал для оператора в инбоксе, что тред нужно дозакрыть человеком. На дашборде такие треды попадают в очередь «требуется уточнение». **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "hours", "needs_followup": true } } ``` --- # B · Адрес и филиалы ## B.1 · «Где вы находитесь?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/branches/**')`. - top-k: 3. - Достались чанки: - `branches/_index.md` (score 0.88) — общий перечень филиалов. - `branches/czetkina-9.md` (score 0.71). - `branches/pirogova.md` (score 0.69). **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "У нас два адреса в Перми: ул. Клары Цеткин, 9 (основная клиника, ЛОР, сурдология, отоневрология) и Клиника Пирогова, ул. Героев Хасана 24 (терапия, аллергология, телемед). Подсказать, какой ближе к вам или какая специальность нужна?", "state_after": "done", "slots_updated": { "info_topic": "branches" } } ``` Финальный встречный вопрос — лёгкий, без обязательного ответа. Если пациент проигнорирует, тред просто останется в `done`. Если ответит — пойдёт ещё один проход через роутера. **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "branches" } } ``` --- ## B.2 · «Какой у вас точный адрес на Цеткина?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/branches/**')`. - top-k: 1. - Достались чанки: - `branches/czetkina-9.md` (score 0.96) — карточка филиала: адрес, ориентиры, вход. Высокий score, точный матч — достаточно одного чанка. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Адрес: г. Пермь, ул. Клары Цеткин, 9. Вход со стороны двора, ориентир — голубая вывеска «Клиника Ухо, Горло, Нос». Регистратура на первом этаже.", "state_after": "done", "slots_updated": { "info_topic": "branches", "branch_mention": "Клары Цеткин 9" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "branches", "branch_mention": "Клары Цеткин 9" } } ``` --- ## B.3 · «Сколько у вас филиалов в городе?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/branches/**')`. - top-k: 1. - Достались чанки: - `branches/_index.md` (score 0.90) — общий перечень. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "В Перми у нас два филиала: основная клиника на ул. Клары Цеткин, 9, и Клиника Пирогова на ул. Героев Хасана, 24. Других филиалов и онлайн-клиник в других городах у нас нет — приём только в этих двух.", "state_after": "done", "slots_updated": { "info_topic": "branches" } } ``` Замечание: пациенты иногда спрашивают «вы же есть в Москве?», путая клинику с одноимёнными. Ответ намеренно явно говорит «других филиалов нет» — чтобы не оставить надежду на не-существующее. **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "branches" } } ``` --- ## B.4 · «Как вас на Яндекс Картах искать?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/branches/**', '/wiki/contacts/**')`. - top-k: 2. - Достались чанки: - `contacts/online-presence.md` (score 0.84) — карточка организации, ссылки на 2ГИС / Я.Карты. - `branches/_index.md` (score 0.66). **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "На Яндекс Картах ищите по запросу «Клиника Ухо Горло Нос Пермь» — найдутся обе наших точки: на Клары Цеткин 9 и на Героев Хасана 24. Можно сразу проложить маршрут оттуда.", "state_after": "done", "slots_updated": { "info_topic": "branches" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "branches" } } ``` --- # C · Как добраться и парковка ## C.1 · «На метро как до вас доехать?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/branches/**')`. - top-k: 2. - Достались чанки: - `branches/czetkina-9.md` (score 0.62) — раздел «как добраться». - `branches/pirogova.md` (score 0.59). Ни один чанк не упоминает метро — потому что **в Перми метро нет**. Ретривер вытащил то, что про транспорт ближе всего по теме. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "В Перми метро нет, поэтому удобнее всего добираться на автобусе, троллейбусе или такси. До Клары Цеткин 9 — остановка «Площадь Восстания» (5 минут пешком), маршруты 14, 32, 36, 67. До Клиники Пирогова — остановка «Героев Хасана», маршруты 19, 27. Подсказать что-то конкретнее, если знаете, откуда поедете?", "state_after": "done", "slots_updated": { "info_topic": "transit" } } ``` Это полезный пограничный кейс: вопрос содержит **ложное допущение** (метро). Промпт ветки требует мягко поправить пациента, не оставляя его в недоумении, и сразу предложить рабочую альтернативу. **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "transit" } } ``` --- ## C.2 · «Парковка у вас есть?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/branches/**')`. - top-k: 2. - Достались чанки: - `branches/czetkina-9.md` (score 0.81) — раздел «парковка». - `branches/pirogova.md` (score 0.78) — раздел «парковка». **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Да, парковка есть на обоих филиалах. На Клары Цеткин 9 — небольшая бесплатная парковка во дворе клиники, обычно есть свободные места. У Клиники Пирогова — общая платная парковка торгового центра рядом, въезд с ул. Героев Хасана. Подсказать, какой филиал вам удобнее?", "state_after": "done", "slots_updated": { "info_topic": "parking" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "parking" } } ``` --- ## C.3 · «Как пешком от центра дойти до Цеткина?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/branches/**')`. - top-k: 1. - Достались чанки: - `branches/czetkina-9.md` (score 0.79) — раздел «как добраться» → подраздел «пешком». **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "От Комсомольского проспекта дойти можно за 12–15 минут: вниз по Сибирской до Клары Цеткин, направо до дома 9. Удобнее всего ориентироваться на Яндекс Карты — там же увидите маршрут.", "state_after": "done", "slots_updated": { "info_topic": "transit", "branch_mention": "Клары Цеткин 9" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "transit", "branch_mention": "Клары Цеткин 9" } } ``` --- # D · Контакты ## D.1 · «Дайте телефон регистратуры» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/contacts/**')`. - top-k: 1. - Достались чанки: - `contacts/registratura.md` (score 0.95) — телефоны регистратуры по филиалам. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Регистратура на Клары Цеткин 9 — +7 (342) 204-00-00. Регистратура Клиники Пирогова — +7 (342) 204-67-16. Звонят с 8:00 до 21:00 в будни. Если удобнее, можно записаться прямо здесь — я помогу.", "state_after": "done", "slots_updated": { "info_topic": "contacts" } } ``` Хвостовая фраза «можно записаться прямо здесь» — это soft-cross-sell, разрешённая в ветке `general_info`. Пациент свободен её проигнорировать; если откликнется — следующая реплика уйдёт в `new_booking` через роутера. **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "contacts" } } ``` --- ## D.2 · «У вас WhatsApp есть?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/contacts/**')`. - top-k: 1. - Достались чанки: - `contacts/messengers.md` (score 0.93) — мессенджеры, в которых отвечает клиника. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Да, мы отвечаем в WhatsApp по номеру +7 (342) 204-00-00 — это тот же телефон регистратуры. Также есть Telegram: @ugn_clinic. Уведомления о приёме приходят на тот номер, который вы оставите при записи.", "state_after": "done", "slots_updated": { "info_topic": "contacts" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "contacts" } } ``` --- ## D.3 · «А сайт у вас какой?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/contacts/**')`. - top-k: 1. - Достались чанки: - `contacts/online-presence.md` (score 0.91). **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Сайт клиники: ugn-clinic.ru. Там можно посмотреть прайс, расписание врачей и записаться. Если хотите — могу записать вас прямо сейчас, без перехода на сайт.", "state_after": "done", "slots_updated": { "info_topic": "contacts" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "contacts" } } ``` --- # E · Документы и подготовка к приёму ## E.1 · «Что нужно с собой принести?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/preparation/**')`. - top-k: 2. - Достались чанки: - `preparation/what-to-bring.md` (score 0.92) — общий список. - `preparation/лор-первичный.md` (score 0.65) — для ЛОРа отдельно. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "На приём возьмите паспорт (для пациентов с 14 лет) и, если есть, выписки или результаты предыдущих обследований по теме визита. Полис ОМС не нужен — клиника частная. Если есть конкретная специальность, к которой записываетесь, могу подсказать, что взять именно для неё.", "state_after": "done", "slots_updated": { "info_topic": "preparation" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "preparation" } } ``` --- ## E.2 · «Полис ОМС у вас принимают?» **Решение роутера:** `general_info`. Это ещё не вопрос про цену в чистом виде — пациент уточняет принцип, не считает деньги. Но граница тонкая, см. замечание ниже. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/preparation/**', '/wiki/services/_general/**')`. - top-k: 2. - Достались чанки: - `preparation/what-to-bring.md` (score 0.74) — там есть строка про ОМС. - `services/_general/payment-options.md` (score 0.81) — формы оплаты, ДМС. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Нет, мы частная клиника и по полису ОМС не работаем. Принимаем оплату наличными, картой и переводом. По ДМС — работаем не со всеми страховыми, нужно уточнять по конкретной компании.", "state_after": "done", "slots_updated": { "info_topic": "preparation" } } ``` Замечание: следующая реплика «а с какими страховыми работаете?» — это уже **жёсткий переход** в `price_question` (там есть отдельная ветвь про ДМС-покрытие). На текущей реплике мы аккуратно касаемся темы, но не углубляемся. **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "preparation" } } ``` --- ## E.3 · «Сыну 16, можно без меня прийти?» **Решение роутера:** `general_info`. Вопрос про порядок приёма несовершеннолетних — это организационный, не медицинский и не запись. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/preparation/**', '/wiki/_faq/**')`. - top-k: 2. - Достались чанки: - `_faq/teenagers-without-parents.md` (score 0.94) — соответствует в точности. - `preparation/what-to-bring.md` (score 0.61). Чанк `teenagers-without-parents.md` — это статья из раздела ЧаВо (см. источник `vrachi-kliniki-...md`, пункт «Если пациенту 16-17 лет»), индексированная в общий пул `general_info`. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "С 15 лет подросток может прийти на приём один и сам подписать договор — нужен паспорт. Если родители смогут позже подъехать или прислать документы — это тоже подойдёт. Записать сына на удобное время?", "state_after": "done", "slots_updated": { "info_topic": "preparation" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "preparation" } } ``` --- # F · Что клиника не делает ## F.1 · «У вас можно сделать бронхоскопию?» **Решение роутера:** `general_info`. По формальному признаку — пациент спрашивает про услугу. Но это не запись и не цена, это уточнение «делаете ли вы такое в принципе». Промпт роутера явно относит подобные формулировки к `general_info`, чтобы не запускать механизм записи на услугу, которой нет. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/services/_general/**')`. - top-k: 2. - Достались чанки: - `services/_general/not-performed.md` (score 0.97) — список процедур, которые НЕ проводятся. - `services/_general/scope.md` (score 0.62) — общий профиль клиники. Чанк `not-performed.md` собран из соответствующего раздела вики (см. источник «Процедуры, которые в клинике НЕ проводятся» в `skripty-...md`). Бронхоскопия там есть в явном виде. **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Бронхоскопию у нас не делают — это исследование с эндоскопом по слизистым трахеи и бронхов, его нужно искать в специализированных пульмонологических центрах. Если вас беспокоит кашель или дыхание — мы можем записать к пульмонологу на консультацию, он уже подскажет, куда дальше.", "state_after": "done", "slots_updated": { "info_topic": "scope_of_services" } } ``` Это **важный сценарий для honesty-eval'а**: модель не должна выдумывать «да, конечно, запишем» в надежде, что пациент придёт и разберётся на месте. Прямой отказ + полезная альтернатива. **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "scope_of_services" } } ``` --- ## F.2 · «Криодеструкцию миндалин делаете?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/services/_general/**')`. - top-k: 1. - Достались чанки: - `services/_general/not-performed.md` (score 0.94). **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Нет, криодеструкцию миндалин в нашей клинике не проводят. Если вас беспокоит хронический тонзиллит — можем записать на приём к ЛОР-врачу: он осмотрит, предложит варианты лечения из тех, что у нас доступны, и подскажет, куда обратиться, если понадобится именно эта процедура.", "state_after": "done", "slots_updated": { "info_topic": "scope_of_services" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "scope_of_services" } } ``` --- ## F.3 · «Электрофорез носа можно у вас?» **Решение роутера:** `general_info`. **Активная ветка / шаг:** `general_info` / `answer`. **Ретривер:** - Фильтр: `doc_path STARTS WITH any('/wiki/services/_general/**')`. - top-k: 1. - Достались чанки: - `services/_general/not-performed.md` (score 0.91) — электрофорез в списке «не проводится». **Вызовы инструментов:** нет. **Структурированный ответ модели:** ```json { "reply": "Нет, электрофорез у нас не делают. Из физиопроцедур по ЛОР-профилю мы предлагаем промывания, орошения, лечебные ингаляции — если интересно, могу подробнее рассказать или сразу записать на приём.", "state_after": "done", "slots_updated": { "info_topic": "scope_of_services" } } ``` **`thread_state` после хода:** ```json { "current_intent": "general_info", "current_step": "done", "slots": { "info_topic": "scope_of_services" } } ``` --- ## Что показал этот пример - **Одна реплика — один проход.** Ветка `general_info` не имеет машины состояний: единственный «осмысленный» шаг `answer` сразу уводит в `done`. Это самый дешёвый путь в системе и логичная точка запуска первой версии бота. - **Слоты почти не используются.** Поле `info_topic` нужно ровно для того, чтобы при следующем вопросе в том же треде ретривер знал, в какую сторону смотреть в первую очередь. Опционально — `branch_mention`, чтобы помнить, про какой филиал шла речь. - **Ретривер делает основную работу.** Все факты в ответе должны быть из чанков, не из памяти модели. Если score лучшего чанка ниже порога — модель уходит в шаблон «уточним и перезвоним» с `needs_followup=true` (см. A.4). - **Пограничные кейсы важнее, чем happy path.** Метро в Перми (которого нет — C.1), услуги, которых клиника не предоставляет (F), праздничные дни без чанка (A.4) — именно на них модель ломается чаще всего и именно их полезно держать в eval-наборе с самого начала. - **Soft cross-sell разрешён, но мягкий.** Хвост «могу записать прямо сейчас» — нормальная практика для информационных ответов. Жёстко продавать запись — нет. ## Что важно проверять в eval-наборе на этом примере - Роутер **не уводит** информационные вопросы в `new_booking` или `price_question` (классическая ошибка — на «сколько у вас стоит» в чистом виде это уже `price_question`, а на «какие у вас услуги» — `general_info`; границы должны быть чёткими). - Все факты в `reply` находимы в одном из чанков, попавших в контекст. Хорошая метрика — `groundedness` (доля утверждений, для которых есть прямое подтверждение в источниках). - При отсутствии релевантных чанков модель отвечает шаблоном «уточним», а не выдумывает (см. A.4 и логика `needs_followup`). - Ответы не превышают 3–4 предложений. `general_info` — не место для лекций. - Слот `info_topic` проставлен корректно (`hours`, `branches`, `transit`, `parking`, `contacts`, `preparation`, `scope_of_services`) — эта же таксономия используется в дашборде «о чём чаще всего спрашивают». - На пограничных кейсах (метро в Перми, услуги, которых нет, праздничные дни) ответ не выдаёт ложных утверждений — это критичный безопасный минимум для запуска.