Мультиклиника: 40 клиник · 4 страны
Ночь 16→17.07.2026 · анализ кода трёх репо + ресёрч (методички колл-центров, multi-location продукты, регуляторика ЕС). Материал для обсуждения — здесь развилки с вариантами, а не решения. Пометки: код = проверено по коду, вывод = моя оценка, [источник] = внешний ресёрч.
Звонок сегодня vs звонок в целевой картине
Сейчас — весь SIP-трафик идёт в одну клинику:
…CLINIC=trent)Надо — номер страны, выбор клиники в диалоге:
Карта модулей и связей
Девять модулей. Синие — рантайм звонка, жёлтые — данные/справочники, нижний ряд — опорные слои. Пунктирная рамка — M9 (комплаенс): ограничивает всё.
Стрелки: M1→M2 (телефония даёт вход каскаду), M2→M5 (каскад ведёт к слотам), M3→M2 и M4→M2 (реестр и пациент кормят каскад), M3→M5 (реестр говорит слотам, какие клиники в городе), M6→M5 (слоты живут в CRM). Нижний ряд — опоры: языковой стек нужен диалогу, hub ведёт реестр, роли и метрики.
Модули
Телефония: номера стран, DID, перевод
Камни
- Испания: Ley 11/2022 запрещает 902 как канал обслуживания для «protección de la salud»; CNMC штрафует. Geo-DID → proof of address зоны номера; провайдер зарегистрирован в CNMC. Германия: geo-номер только с адресом в городе, иначе 032.
- Гео по caller ID — лишь подсказка: мобильные не привязаны к городу; анонимные звонки.
- Наш transfer — SIP REFER без возврата: «не дозвонились в клинику» = потерянный пациент. Нужен warm transfer + очередь + callback-обещание.
- Если агент лёг — маршрут failover на прямые номера клиник должен жить у телеком-оператора, не у нас.
- Портирование существующих номеров клиник в ЕС — процедура на недели–месяцы (страна-зависимо), закладывать в план пилота.
- Конфиг транка/dispatch-правил сейчас не в репо (руками через CLI) — для 4 стран обязателен IaC.
Каскад выбора клиники в диалоге
Камни
- Дефолт ≠ навязывание: право пациента выбирать клинику/врача — закон (РФ 323-ФЗ ст.21; Испания Ley 41/2002). Формулировки через выгоду, на «а какие ещё есть?» — честный список.
- Перед оффером соседней клиники проверить: там есть услуга, врач, страховка/цены сопоставимы. «Есть слот» ≠ «есть услуга».
- Смена филиала при переносе — только с явным подтверждением вслух (грабля «приехал не туда» задокументирована у всех PMS).
- Повторный пациент привязан к врачу сильнее, чем к клинике: «вам важнее ваш врач или время?» Врач ездит по филиалам по дням — искать его слоты по всем его локациям.
- Пациент с планом лечения (орто/импланты) не переливается автоматом.
- Минимум вопросов до брони; вопрос локации — последний fallback, не первый шаг.
- Острая боль: фикс-скрипт red-flags, без триажа; в какой клинике города сегодня принимают острых — таких данных у нас нет вообще.
Реестр сети: страна → город → клиника
Камни
- «Дефолтная клиника города» — чьё правило? Загрузка/маржа/своя-франчайзи/территория — бизнес-политика, из кода не выводится. Главный вопрос созвона.
- Конфиг-дрейф: уже сегодня 26 YAML руками; на 40 — умрёт без валидатора и наследования конфигов.
- Праздники: в Испании календарь на уровне автономий; TZ: Канары ≠ материк.
- Таймзона Москвы захардкожена в 5+ местах ядра (4 модуля + api + SQL напоминаний) — поле timezone клиники по факту не применяется к слотам и напоминаниям.
Пациент уровня сети
Камни
- GDPR: клиники — разные юрлица (тем более франчайзи) → «карта видна во всех клиниках» = передача special-category данных; нужен joint-controller/консент. Может юридически урезать фичу.
- Миграция: дубли телефонов, «unassigned» пациенты (яма Open Dental при включении мультилокации на живой базе), конфликты ФИО из разных CRM.
- Secondary-gate/история визитов клиник-соседей: «постоянный в X» = «новый в Y» — правила надо определить.
- Снимки: переиспользовать свежие рентгены (не переоблучать), при разных PMS — задача на передачу, а не обещание «видно везде».
Слоты и cross-clinic бронирование
Камни
- Floating providers: врач в двух клиниках по дням; «свободное» окно = дорога между площадками; расписание врача — per клиника + блок drive-time.
- Гонки: слот увели, пока пациент думал; бронь только живым чтением/записью PMS, не кэшем.
- Опрос «всех клиник города» = N×M запросов к CRM (у нас уже N+1 по врачам) — без агрегированного кэша с инвалидацией CRM ляжет.
- Перелив звонков «в соседний офис» не работает (там тоже занято) — правильный паттерн: агент отвечает параллельно везде, cross-clinic — это слоты, не звонки.
CRM / PMS-интеграции
Камни
- Неизвестная PMS = самый большой неоценённый объём работ. Если зоопарк систем (норма после M&A) — нужен middleware-слой с трансляцией типов приёмов.
- «Поверхностная» интеграция (читать календарь без правил провайдеров/операторных) — задокументированный источник ошибочных броней.
- Квоты/лимиты API их PMS при 40 клиниках × агрегатор доступности.
Языки и голосовой стек
Камни
- Качество STT на телефонном 8kHz с акцентами — лабораторный WER не переносится; пилотный замер на реальном трафике обязателен.
- Испания: кастильский vs каталанский — уточнить требование.
- Языки персонала клиники — атрибут реестра: предупреждать, если в выбранной клинике не говорят на языке пациента.
- Определение конца реплики (turn-detector) уже мультиязычное и работает локально; а вот диктовка номеров и нормализация текста — переписывать per язык.
Операционка: hub, роли, деплой, метрики
Камни
- Саботаж регистратур — причина №1 провала AI-ресепшенов в сетях [ainora]: вовлекать в пилот, позиционировать «забирает звонки, которые вы ненавидите».
- Единая таксономия типов звонков на 4 страны, иначе аналитика рассыпается.
- KPI-рамка рынка: answer rate 95%+, конверсия новых 70–80%, no-show <5%; ~30–35% эскалаций на живого оператора — норма, а не провал.
Комплаенс (накрывает всё)
Камни
- Расползание функций в сторону медицинского триажа (оценка симптомов/срочности) = medical device (MDR) + high-risk по AI Act. Держать агента в административной рамке жёстко промптом и тестами.
- Передача карт между юрлицами сети — см. M4.
- Пилот юридически не может стартовать до DPIA заказчика — влияет на сроки продажи.
Развилки — что реально обсуждать
Развилка №1 · Как получить cross-clinic: сравнение за 30 секунд
Сегодня «1 клиника = 1 контейнер + своя БД» — это и сила (изоляция, простые инциденты), и потолок (никакого «посмотри слоты соседа»). Сначала матрица, ниже — детали вариантов.
| Критерий | A роутер | B multi-tenant | C федерация | D через PMS |
|---|---|---|---|---|
| Скорость до пилота | быстро | долго | средне | быстро, если PMS одна |
| Риск для текущего прода | минимальный | высокий | средний | минимальный |
| Cross-clinic слоты | живой опрос + кэш | нативно | через кэш | нативно в PMS |
| Identity сети | отдельная задача | нативно | сетевой индекс | если есть в PMS |
| Зависимость от ответа «какая PMS» | средняя | средняя | средняя | полная |
| Радиус инцидента | роутер = страна | страна+ | клиника | PMS = сеть |
Отдельный инстанс «hotline-ES»: знает реестр (M3), ведёт диалог, ходит в клиники по новому read-API слотов (+бронь в целевой), переводит звонок на локальный номер. Обычные клиники не меняются.
- Изоляция клиник сохраняется (и как продукт-фича для он-премиса)
- Не трогаем синглтоны каналов/кэши ядра
- Можно катить по стране за раз; откат тривиален
- Ложится на готовый transfer-механизм
- Новый межсервисный API + авторизация между инстансами
- Latency: опрос N клиник в реальном времени → нужен кэш доступности
- Identity пациента всё ещё врозь (решается в развилке №2)
- Роутер — новая точка отказа на всю страну
Ядро уже умеет грузить все клиники циклом и держит per-clinic реестры графов/CRM — но каналы (Telegram и др.) — глобальные синглтоны, кэши и контекст-переменные местами без clinic_id, «одна БД на клинику» становится «одна на всех».
- Cross-clinic запросы = обычные запросы в одну БД
- Единая identity сети «бесплатно»
- Меньше контейнеров/инфраструктуры на 40 клиник
- Проще он-премис поставка (один стек)
- Чинить синглтоны каналов, глобальные кэши, fallback-граф, contextvars — ровно тот класс багов, что уже кусал (протечки между ходами)
- Радиус поражения инцидента — вся страна, не одна клиника
- Миграция данных 40 баз → одну
- Дольше и рискованнее всего
Клиники не меняются; рядом появляется общий сервис сети: реестр клиник + сетевой индекс пациентов + агрегированный кэш доступности (клиники периодически публикуют слоты). Диалог остаётся в клинике-инстансе, который спрашивает сетевой слой.
- Кэш доступности решает latency и щадит PMS
- Сетевой индекс пациентов = аккуратная зона для GDPR-контроля (консент в одном месте)
- Инстансы можно апгрейдить постепенно
- Свежесть кэша слотов ↔ гонки броней (нужна финальная проверка перед бронью)
- Два источника правды (клиника vs сетевой слой) — дисциплина синхронизации
- Больше движущихся частей, чем в A
Если у сети одна PMS на все 40 клиник с локациями внутри (как у Denticon/Ascend/Curve: единая БД, локация — атрибут) — cross-clinic слоты и единый пациент уже существуют у заказчика. Тогда наш «агрегатор» = один CRM-адаптер с фильтром по локации, и мотивация роутера/федерации сильно ослабевает.
- Не строим то, что уже есть у заказчика
- Единый пациент и слоты — из коробки PMS
- Меньше всего нового кода
- Работает только если PMS действительно одна и с API
- Полная зависимость от возможностей/лимитов чужой системы
- Наша архитектура «инстанс на клинику» всё равно требует решения (чей контейнер ведёт диалог линии страны)
Моя гипотеза (спорьте): сначала ответ про PMS (может выбрать D), иначе — эволюция A→C: роутер сначала, кэш доступности вторым шагом, консолидация ядра — только если припрёт.
Развилка №2 · Identity пациента сети
- Ноль миграций; карты остаются в клиниках
- Для пилота достаточно: роутер спрашивает клиники по телефону звонящего
- GDPR-вопрос не исчезает: номер звонящего уходит во все клиники, факт «пациент клиники X» раскрывается — объём передачи минимальный, но юр. основание всё равно нужно
- Нет единой истории/семейных связок между клиниками
- Мгновенный «домашний» лукап на звонке; карта остаётся в клинике
- Одно место для консента и правил видимости
- Новый критичный сервис (PII), нужна синхронизация при регистрации пациента
- Всё ещё нет слияния карт — только указатели
- Опыт «карта доступна везде» — сильный продукт
- Фундамент под всю аналитику сети
- Требует юр. основания между юрлицами (joint controller / консент) — может быть невозможно для франчайзи
- Миграция/мердж карт — самый тяжёлый проект из всех
- По сути дублирует PMS заказчика — а «источник правды» о пациенте обычно PMS, не мы
Моя гипотеза (спорьте): если PMS одна — identity сети уже существует у них, нам хватит варианта B поверх неё.
Развилка №3 · Топология телефонии
- Один маркетинговый номер, простая аналитика
- Агент один на страну — ровно наш сценарий каскада
- Все звонки, включая «перенесите мне на завтра» постоянных, идут через каскад выбора города
- Локальный номер вызывает больше доверия (местные привыкли к geo-номерам)
- Единая точка отказа страны
- Постоянные попадают сразу в свою клинику (без каскада) — короче звонки
- DID клиники = автоматический контекст (паттерн Weave/Mango)
- Маркетинг всё равно получает единый номер
- Портирование/сохранение 40 локальных номеров — недели–месяцы, страна-зависимо
- Сложнее объяснить сети, где чей номер публикуется
- Минимальное вмешательство в привычки пациентов
- Агент продаётся как «ни один звонок не потерян» — легко мерить ценность
- «Один номер на страну» из постановки не выполняется буквально
- Маршруты overflow на стороне их операторов — зависимость от телекома заказчика
Моя гипотеза (спорьте): гибрид B закрывает и маркетинг, и постоянных пациентов — но проверить, что заказчик вообще готов держать локальные номера.
Сводный каталог подводных камней
Полные списки — внутри модулей выше. Здесь — топ-12 по способности сорвать проект.
- Неизвестная PMS заказчика — до ответа на это оценивать сроки нельзя (M6, развилка №1-D).
- AI Act Art.50 с 02.08.2026 — бот обязан представляться; это про прод уже сейчас (M9).
- Передача карт между юрлицами — может юридически запретить «единого пациента сети» (M4).
- Правило дефолтной клиники — политика, а не техника; без владельца правила фича заглохнет (M3).
- Запись звонков в Испании — locución + retention + special category (M9).
- «Есть слот» ≠ «есть услуга/страховка/врач» — главный источник ошибочных cross-clinic предложений (M2/M5).
- Гонки слотов и floating providers — брони мимо реальности (M5).
- Телефонные номера: 902 запрещён, DID требуют юрадрес — выбор номера = регуляторный риск (M1).
- STT на телефонном звуке 4 языков — качество проверять пилотом; готовой локальной замены нет (M7 / onpremis).
- Саботаж регистратур — главный нетехнический риск внедрения (M8).
- Конфиг-дрейф 40 клиник — без схемы/наследования утонем (M3/M8).
- Blind transfer без возврата — потерянные звонки на самом ценном шаге (M1).
Шпаргалка: как это делают другие
| Паттерн | Кто | Берём? |
|---|---|---|
| Единая БД сети; локация — атрибут записи | Denticon, Dentrix Ascend, Curve, Open Dental | как ориентир модели |
| Default clinic per patient (домашняя) | Open Dental | да |
| DID = контекст локации; вопрос — последний fallback | Weave, Mango, Arini (zip-вопрос), Viva | да |
| Cross-location offer «остаёмся в сети» | Viva, CareStack | ядро фичи |
| Подтверждение при смене локации | Dentrix Ascend | да, вслух |
| Новые → центральный 24/7; свои → локальный | Aspen Dental (сеть 1000+ офисов) | развилка №3-B |
| Missed call → SMS со ссылкой на запись | Doctoralia Phone (Испания), Weave | запасной канал |
| Провайдер привязан к локациям расписанием + дни | Ascend, Open Dental | в модель врача |
| Один workflow на сеть + scorecard per клиника | Arini, Dental Intelligence | да |
| AI отвечает параллельно во всех локациях (не перелив) | Viva | архитектурно |
Вопросы, без которых не проектировать (на созвон)
Первые три — критичные: их ответы выбирают варианты в развилках.
- топ PMS: какая система расписаний у 40 клиник (одна? зоопарк?), есть ли API и кто даст доступ? → размер M6, может схлопнуть развилку №1 в вариант D.
- топ Юрструктура: собственные клиники или франчайзи, одно юрлицо на страну? → M4 identity, балансировка, «чей пациент».
- топ «1 номер на страну» — дословно? Что с существующими номерами клиник? → развилка №3.
- Правило дефолтной клиники: кто владелец, какой критерий (загрузка/маржа/территория)? → M3.
- Языки по странам: Испания — кастильский/каталанский/английский? Какие остальные 3 страны? → M7.
- Единая карта пациента — требование или nice-to-have? Готовы ли юристы к joint-controller? → развилка №2.
- Врачи ездят между клиниками? → модель расписаний M5.
- Fallback: есть ли живой колл-центр (часы?), куда падаем при отказе агента? → M1/M8.
- Запись звонков: писать ли аудио, сроки, где хранить? → M9 + onpremis.
- Пилот: какие 2–3 клиники, какие метрики успеха, кто со стороны сети отвечает? → M8.
Грубая оценка объёмов (для калибровки ожиданий)
| Блок | A роутер | B multi-tenant | C федерация |
|---|---|---|---|
| M3 реестр сети | нужен во всех вариантах — средне | ||
| M2 каскад в диалоге (промпты+тулы) | одинаково — средне | ||
| M5 слоты cross-clinic | средне (read-API+кэш) | крупно (ядро+БД) | средне+ (кэш-сервис) |
| M4 identity | мини (обзвон) / отдельно | крупно (миграции) | средне (индекс) |
| M1 телефония (DID, транки, warm transfer) | одинаково — средне, но с внешними зависимостями (номера, операторы, порт — недели–месяцы) | ||
| M7 языки | одинаково — средне→крупно (STT/TTS — см. /onpremis) | ||
| M6 PMS-адаптер | объём не оценить до ответа на вопрос №1 | ||
| Вариант D (через PMS) | объём ≈ PMS-адаптер M6 + каскад M2 — целиком зависит от ответа на вопрос №1 | ||
| Разблокирующие мелочи | E.164-телефоны, таймзона вместо MSK-хардкода (5+ мест), i18n es, валидатор YAML — мелко, делать в любом случае | ||
вывод Самая тяжёлая пара — identity/изоляция БД и канальные синглтоны; LangGraph-граф и промпт-слой почти не пострадают. Куда идти — см. развилку №1 и её вопрос.
См. также: /onpremis — как это продать в контуре заказчика; /adjustment — улучшения, многие из которых — пререквизиты отсюда. Источники: код трёх репо, Open Dental / Dentrix Ascend docs, ainora.lt, Viva AI, Arini, intavia.ai, mybcat, flexbone.ai, BOE (Ley 11/2022, Ley 41/2002, RD 8/1996), artificialintelligenceact.eu, практика AEPD (Conversia/Adaptalia), методрекомендации ЦНИИОИЗ N 12-22.