From 4867e4cebccbc7e180672bee26570e20a8221490 Mon Sep 17 00:00:00 2001 From: Maksim Totmin Date: Tue, 23 Jun 2026 12:37:48 +0700 Subject: [PATCH] =?UTF-8?q?docs:=20GUIDE.md=20=E2=80=94=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=BD=D1=8B=D0=B9=20=D0=B3=D0=B0=D0=B9=D0=B4=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8E=20?= =?UTF-8?q?=D0=B0=D0=B2=D1=82=D0=BE=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2=20?= =?UTF-8?q?=D1=81=20=D0=BD=D1=83=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GUIDE.md | 303 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 4 + 2 files changed, 307 insertions(+) create mode 100644 GUIDE.md diff --git a/GUIDE.md b/GUIDE.md new file mode 100644 index 0000000..c45af03 --- /dev/null +++ b/GUIDE.md @@ -0,0 +1,303 @@ +# Как создать автотесты для другого проекта + +Этот гайд описывает полный цикл создания автотестов для SEE и SMC на любом проекте — от первого запуска до полного набора asserts. + +## Архитектура тестов + +Тесты **универсальные**: не содержат хардкода имён моделей. Всё привязывается динамически: + +| Компонент | Где | Что делает | +|-----------|-----|-----------| +| `TEXT_RULES` | В начале каждого теста | По ключевым словам в имени модели подбирает текст для запроса | +| `DEFAULT_HOST` / `DEFAULT_PORT` | В начале каждого теста | Куда стучаться (меняешь под свой проект) | +| `asserts.json` | Отдельный файл | Опциональные проверки — какой ответ ожидается от каждой модели | + +## Шаг 1: Подготовка тестов к новому проекту + +Скопируй `test_see_models.py` и/или `test_smc_models.py` в репозиторий нового проекта. + +### 1.1 Укажи адрес сервера + +В начале каждого тестового файла поменяй `DEFAULT_HOST` и `DEFAULT_PORT`: + +```python +DEFAULT_HOST = "10.101.60.27" # IP вашего SEE/SMC +DEFAULT_PORT = 6184 # порт SEE: 6184, SMC: 6181 +``` + +### 1.2 Настрой TEXT_RULES для своих моделей + +`TEXT_RULES` — список `(keyword, text)`. **Первое совпадение побеждает**, поэтому специфичные правила должны идти раньше общих. + +**Принцип**: для каждой модели нужен текст, который гарантированно вернёт непустой ответ. + +Пример для SEE (`test_see_models.py`): + +```python +TEXT_RULES = [ + # специфичные — раньше + ("fio", "Иванов Иван Иванович"), # модель ФИО → имя + ("date", "на завтра"), # модель даты → фраза с датой + ("phone", "8 913 456 78 90"), # модель телефона → номер + ("number", "пятьсот двадцать три"), # модель числа → число прописью + ("weekday", "в пятницу"), # модель дня недели → день + ("mobilizac", "оператор"), # конкретная модель + ("trud", "зарегистрироваться в цзн"), # конкретная модель + # общие — позже + ("mfc", "записаться в мфц"), # fallback для всего MFC + ("test", "test message"), # fallback для тестовых +] +``` + +**Как подобрать правила для нового проекта:** + +1. Посмотри список моделей: `curl http://HOST:PORT/SEE_OR_SMC/models` +2. Для каждой модели подбери текст, который точно вернёт ответ +3. Сгруппируй по ключевым словам (одно правило может покрыть несколько моделей) +4. Расположи специфичные правила **выше**, общие — **ниже** + +Пример для SMC (`test_smc_models.py`): + +```python +TEXT_RULES = [ + ("филиал", "родники"), # филиалы → название филиала + ("offices", "родники"), + ("мфц", "записаться в мфц"), # модели МФЦ + ("mfc", "записаться в мфц"), + ("ekc", "записаться в екц"), # модели ЕКЦ + ("екц", "записаться в екц"), + ("minsoc", "какие выплаты"), # минсоц + ("техническая", "подключите оператора"), # техподдержка + ("тест", "тестовый запрос"), +] +``` + +## Шаг 2: Первый запуск без asserts + +Запусти тест с `-v` (verbose), чтобы увидеть каждую модель и её ответ: + +```bash +python3 test_see_models.py --host 10.101.60.25 --port 6184 -v +``` + +Вывод будет примерно таким: + +``` +── PRETRAINED ────────────────────────────────────────────── + ✓ date → 2026-06-24 + ✓ number → 523 + ✓ fio → Ф:"Иванов" И:"Иван" О:"Иванович" + ─── 6 passed, 0 empty + +── CUSTOM ────────────────────────────────────────────── + ✓ zags → отдел_центрального_о_новосибирска + ✓ weekday → пятница + ✗ fio2 → empty + ✗ Начало диалога → empty + ─── 200 passed, 9 empty +``` + +Из вывода ты видишь: +- Какие модели работают (`✓ модель → результат`) +- Какие всегда пустые (`✗ модель → empty`) +- Какие есть хеш-варианты и бэкапы (у них тот же префикс, но с `~hash` или `-backup`) + +## Шаг 3: Создание asserts + +### 3.1 Правило `min_found` + +| Тип модели | `min_found` | Почему | +|------------|-------------|--------| +| Базовая (без суффикса) | `1` | Должна всегда отвечать | +| Хеш-вариант (`~4f2d37c9...`) | `0` | Копия базы для тестирования, может не работать | +| `-backup` | `0` | Бэкап, может не работать | +| `-planned` | `0` | Запланированная, ещё не готова | +| `-new` | `0` | Экспериментальная, может не работать | + +### 3.2 Формат для SEE (entity-модели) + +SEE возвращает сущности с полями `text`, `calculated`, `confidence`: + +```json +{ + "asserts": [ + { + "model": "fio", + "text": "Иванов Иван Иванович", + "checks": { + "min_found": 1, + "text_contains": "иван", + "calculated_contains": "Ф:", + "confidence_min": 0.3 + } + } + ] +} +``` + +| Поле checks | Что проверяет | Пример | +|-------------|--------------|--------| +| `min_found` | Минимум сущностей в ответе | `1` | +| `text_contains` | Подстрока в `text` первой сущности | `"иван"` | +| `calculated_contains` | Подстрока в `calculated` первой сущности | `"Ф:"` | +| `confidence_min` | Минимальный confidence первой сущности | `0.3` | + +**Где брать значения**: из вывода `-v`. Например: + +``` +✓ fio → Ф:"Иванов" И:"Иван" О:"Иванович" + ↑ берём отсюда подстроки для text_contains и calculated_contains +``` + +### 3.3 Формат для SMC (classifier-модели) + +SMC возвращает классы с полями `class` и `confidence`: + +```json +{ + "asserts": [ + { + "model": "МФЦ НСК", + "text": "записаться в мфц", + "checks": { + "min_found": 1, + "text_contains": "Запись", + "confidence_min": 0.5 + } + } + ] +} +``` + +| Поле checks | Что проверяет | Пример | +|-------------|--------------|--------| +| `min_found` | Минимум классов в ответе | `1` | +| `text_contains` | Подстрока в `class` первого класса | `"Запись"` | +| `confidence_min` | Минимальный confidence первого класса | `0.5` | + +**Где брать значения**: из вывода `-v`: + +``` +✓ МФЦ НСК → Предварительная запись (1.000) + └── text_contains: "Запись" + └── confidence_min: 0.5 +``` + +### 3.4 Полный assert для каждой строки `-v` + +Проходишь вывод `-v` и для каждой строки создаёшь запись: + +| Вывод `-v` | Запись в asserts | +|-------------|-----------------| +| `✓ fio → Ф:"Иванов" И:"Иван" О:"Иванович"` | `{"model": "fio", "text": "Иванов Иван Иванович", "checks": {"min_found": 1, "calculated_contains": "Ф:", "confidence_min": 0.3}}` | +| `✓ weekday → пятница` | `{"model": "weekday", "checks": {"min_found": 1, "text_contains": "пятница"}}` | +| `✓ zags~ffb403 → ...` (хеш) | `{"model": "zags~ffb403", "checks": {"min_found": 0}}` | +| `✗ fio2 → empty` | `{"model": "fio2", "checks": {"min_found": 0}}` | + +### 3.5 Поле `text` в assert + +Поле `text` в assert — опционально. Если оно есть, оно **переопределяет** `pick_text()`. + +**Когда указывать `text`:** +- Модель работает с конкретным текстом, отличным от того что подбирает `pick_text` +- Например: модель `service_mfc` возвращает услуги, но `pick_text` даёт "записаться в мфц" — лучше явно указать "хочу получить водительское удостоверение" + +**Когда НЕ указывать `text`:** +- `pick_text()` уже подбирает правильный текст +- Для хеш-вариантов и бэкапов — используй тот же текст что у базовой модели (укажи явно, чтобы не зависеть от порядка `pick_text`) + +**Важно**: если указываешь `text` для хеш-варианта или бэкапа, используй тот же текст, что для базовой модели. Разные тексты могут не работать для копий. + +Пример: + +```json +// Базовая модель +{"model": "zags", "text": "загс центрального района", "checks": {"min_found": 1}} + +// Хеш-вариант — тот же текст! +{"model": "zags~ffb403fff9f5802d53aac8a5c844af26", "text": "загс центрального района", "checks": {"min_found": 0}} +``` + +## Шаг 4: Запуск с asserts + +```bash +python3 test_see_models.py --host 10.101.60.25 --port 6184 --asserts see_asserts.json -v +``` + +Теперь для каждой модели: +- `✓` — ответ непустой, проверки asserts пройдены +- `✗ → empty` — ответ пустой (но `min_found: 0`, тест проходит) +- `✗ → assert fail` — ответ есть, но не прошёл проверки (тест падает) + +## Шаг 5: Исправление проблем + +### Модель возвращает пустой ответ, а должна работать + +1. Проверь текст в assert — попробуй другой текст напрямую через curl: + ```bash + curl "http://HOST:PORT/see/entities/MODEL?text=ТЕКСТ&similarity=70" + ``` +2. Если другой текст работает — обнови `text` в assert +3. Если никакой текст не работает — модель действительно пустая, оставь `min_found: 0` + +### Модель проходит без asserts, но падает с asserts + +Проверь значения в `checks`: +- `text_contains` — проверь, что подстрока действительно есть в ответе +- `calculated_contains` — только для SEE entity-моделей +- `confidence_min` — возможно завышен, поставь 0.1 для ненадёжных моделей + +### Хеш-вариант работает без asserts, но с asserts — пустой + +Это значит assert для хеш-варианта переопределяет `text` на неправильный. Убедись, что `text` совпадает с базовой моделью. + +## Полный пример: с нуля до готового набора + +Допустим, у тебя новый проект с SEE на `10.10.10.10:6184`. + +```bash +# 1. Первый запуск — смотрим что есть +python3 test_see_models.py --host 10.10.10.10 --port 6184 -v + +# 2. Вывод показывает 50 моделей. Создаём see_asserts.json: +# - Для каждой базовой модели → min_found: 1 + проверки из ответа +# - Для хеш-вариантов и бэкапов → min_found: 0 +# - Для пустых → min_found: 0 + +# 3. Запускаем с asserts +python3 test_see_models.py --host 10.10.10.10 --port 6184 --asserts see_asserts.json + +# 4. Если есть ✗ assert fail — правим checks в asserts +# 5. Повторяем 3-4 пока все не пройдут +``` + +## Структура репозитория после настройки + +``` +мой-проект/ +├── test_see_models.py # тест SEE (скопирован, изменён DEFAULT_HOST/PORT и TEXT_RULES) +├── test_smc_models.py # тест SMC (опционально) +├── see_asserts.json # проверки для SEE +├── smc_asserts.json # проверки для SMC (опционально) +└── README.md # краткое описание +``` + +## Важные замечания + +1. **Порядок правил в TEXT_RULES важен** — специфичные ключевые слова (`"fio"`, `"phone"`) должны быть выше общих (`"mfc"`, `"test"`). + +2. **Не указывай `text` в assert без необходимости** — если `pick_text()` уже подбирает правильный текст, не переопределяй. Каждый лишний `text` в assert — потенциальный источник расхождений. + +3. **Хеш-варианты и бэкапы** — всегда `min_found: 0`. Они могут не работать, и это нормально. + +4. **confidence_min для ненадёжных моделей** — ставь `0.1`, не `0.5`. Даже минимальный confidence лучше чем пустой ответ. + +5. **`_find_assert` ищет сначала точное совпадение** — модель `ekc_smc-backup` не перепутается с `ekc_smc`. Порядок записей в asserts не критичен. + +6. **Проверяй на всех серверах** — если у тебя несколько инсталляций (dev/stage/prod), гоняй тесты на каждой: + +```bash +for host in 10.101.60.25 10.101.60.26 10.101.60.27; do + python3 test_see_models.py --host $host --port 6184 --asserts see_asserts.json +done +``` diff --git a/README.md b/README.md index 02fad36..63c0568 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,7 @@ python3 test_smc_models.py --host 10.101.60.27 --port 6181 - `text_contains` — подстрока в тексте/классе первой сущности - `calculated_contains` — подстрока в `calculated` (только entity) - `confidence_min` — минимальный confidence + +## Как адаптировать под другой проект + +См. [GUIDE.md](GUIDE.md) — полный гайд: от первого запуска до готового набора asserts.