30 KiB
Диагностика релевантности поиска — 3 ноября 2026 года
Решения и границы
Эта заметка фиксирует исходные измерения от 2 ноября, узкое исправление терминологии, условный multi-index scoring и контракт для закрытия пробела корпуса. Выдачи и AI-аннотации не являются юридической оценкой. Целевой A/B прогон и полный benchmark для итогового SHA описаны ниже; они не заменяют экспертную оценку.
Изменён backend 0.13.3 → 0.13.4, frontend внутренней лаборатории
0.3.3 → 0.3.4, версия алгоритма search-6 → search-8. search-7
относится к предшествующему составу ranking semantics; новые подписанные
снимки этого PR помечаются search-8. Сохранённые отзывы не переписываются.
Финальный runtime/batch прогон кода выполнен на 1c7ddf2; последующий коммит
этого отчёта и changelog меняет только документацию.
Исходный benchmark
Зафиксированный набор содержит 7 500 русских формулировок: 15 тематических
групп, по 500 запросов в каждой. Это 750 тем с десятью вариантами формулировки,
а не 7 500 независимых правовых ситуаций. Исходный индекс
akyldash-fragments-v2 содержал около 42,6 млн фрагментов и занимал 11,2 ГБ.
| Показатель Акылдаш, исходный прогон | Результат |
|---|---|
| Собранные выдачи | 7 500 / 7 500 |
| Пустые выдачи | 6 |
| Медиана / P95 ответа | 21,67 / 77,36 мс |
| Совпадение основ темы в видимых названии/фрагменте, top 1 | 40,57% |
| То же, top 3 / top 10 | 47,05% / 51,40% |
Сравнение с КонсультантПлюс в исходном отчёте — только диагностическое: юрисдикции и корпуса различаются. Совпадение основ темы не измеряет Recall, MRR, полноту ответа или юридическую правильность; российская выдача не является эталоном права Кыргызстана. Для вывода о качестве нужны экспертные релевантные коды, контрольный набор и отдельные метрики.
Подтверждённые классы ошибок
14 коротких документов прочитаны целиком; Codex (ИИ) подготовил по ним 79 предварительных оценок пар «запрос—документ» в 71 сессии: 59 помечены как нерелевантные и 20 как косвенно полезные. Это тематическая AI-разметка для триажа, а не заключение юриста или независимая экспертная проверка. Она не подтверждает полноту правового ответа. Неоценённые результаты не считались ошибками.
| Тема | Наблюдавшийся неверный приоритет | Вывод |
|---|---|---|
| Расторжение договора аренды | 135066 | Решение по одной сделке с землёй не даёт общих оснований расторжения |
| Международный коммерческий арбитраж | 26411 | Продажа земельных участков не регулирует арбитраж и отказ в признании решения |
| Испытательный срок работника | 94603 | Земельный участок под завод/испытательный полигон совпадает словами, но не предметом |
| Место жительства ребёнка | 50760 | Финансирование приюта не устанавливает порядок спора родителей |
| Перевод студента в другой вуз | 14165 | Разовое финансирование студентов не устанавливает процедуру перевода |
| Таможенные пошлины | 41854 | Индивидуальная отсрочка одному предприятию не задаёт общие правила |
| Общий порядок предоставления земли | 54735, 54571 | Решения по отдельным участкам не являются общей процедурой |
| Защита критической инфраструктуры | 59082 | Предоставление земли под завод не устанавливает защитные требования |
| Купля-продажа / расторжение договора | 60053 | Расторжение одной сделки с госакциями не является общей нормой |
| Подача искового заявления | 18516, 7115 | Отдельный спор и узкая льгота по пошлине не дают общего порядка подачи иска |
| Принятие нормативных актов | 77190 | Первое чтение одного законопроекта не устанавливает общий процесс |
| Подключение к водоснабжению | 135061 | Целевая помощь отдельным домохозяйствам не задаёт технические условия |
Проверенные варианты глобального повышения веса кодексов отвергнуты: вес 2 не исправил аренду и подачу иска, а вес 4 поднял Налоговый кодекс выше профильных источников по воинской обязанности и подаче иска. Общие веса или жёсткие приоритеты кодексов не добавлялись.
Узкая терминологическая поправка
В исследованной редакции ГК КР II (код 5, редакция 460) глава 27 и статья 542 связывают договор аренды с имущественным наймом; статьи 558–559 используют термин «имущественный наем». В полном тексте этой редакции буквальная фраза «расторжение договора аренды» не встречалась. Исходный запрос возвращал код 135066 на первом месте, а формулировка «расторжение договора имущественного найма» — код 5.
Backend теперь сохраняет исходную строку и добавляет только для точного
сочетания «договор [арендная форма]» вариант «договор [та же форма]
имущественного найма». В частности, договора аренды превращается в
договора имущественного найма. Расширение не срабатывает для аренды квартиры
или земельного участка, поднайма, найма работника или лизинга. Точный запрос
остаётся в строгой лексической ветке, поэтому его профильный муниципальный акт
не исключается из поиска.
В read-only пробе предыдущей версии aa6fabe для общего запроса код 5 поднялся
с позиции за пределами top 10 на #5, но нерелевантный индивидуальный акт 135066
оставался первым. На SHA 726b1ee isolated code-only прогон на исходном индексе
поставил код 5 первым для общего запроса; из 7 500 запросов изменились 19
выдач, 7 481 совпала с baseline, число пустых осталось 6. Этот результат
проверяет search-7 с match_phrase boost 12 для синтетической юридической
фразы, исходный phrase boost 3; он не описывает multi-index DFS. После
подключения трёх актов в отдельном supplement на e653f3b изменились 184
выдачи против code-only варианта и 38 top-1; все шесть пустых выдач
сохранились. Разные наборы индексов нельзя считать одним benchmark. Итоговый
conditional-DFS прогон на 1c7ddf2 приведён ниже. Контроль запросов об аренде
жилья/земли, поднайме, трудовых отношениях, международном праве и KY
формулировках выполнялся отдельно; rank-метрики не являются юридической
оценкой.
Условный scoring для явного multi-index scope
При поиске по явно настроенному comma-separated scope (base,supplement) API
передаёт search_type=dfs_query_then_fetch как URL query parameter. Запросы по
одному настроенному индексу или alias сохраняют прежний query_then_fetch;
одиночные wildcard и alias не классифицируются по результату их expansion.
DFS включается только при запятой в настроенной строке scope.
Тело DSL, фильтры, query weights, сортировка, индексы и документы не меняются.
Параметр применяется только к /search; фильтры и остальные API вызовы
используют прежний режим.
OpenSearch описывает query_then_fetch как scoring по локальным term/document
frequencies шардов, а DFS — по глобальным frequencies через шарды; DFS обычно
точнее, но может быть медленнее. search_type поддерживается именно как
параметр Search API URL (OpenSearch Search API).
Поэтому ограниченный режим использует глобальные statistics для явной
multi-index конфигурации и не меняет behavior одиночного поиска.
На изолированном OpenSearch 3.7.0 оба режима сравнили на одинаковых DSL bytes и одном неизменном наборе данных: 300 заранее выбранных запросов, максимум 300. Порядок менялся в 93 случаях, top-1 — в 29; из 9 именованных риск-кейсов изменились 3, все 5 выбранных positive controls сохранили top-1. Контроль с индивидуальным актом 135066 сохранил этот акт первым. В пяти запросах о педагогических/научных работниках и одном запросе о болезнях животных top-1 не изменился. Эти шесть случаев требуют предметной оценки и не объявляются автоматически нерелевантными: статья 150(8) закона об охране здоровья связывает аттестацию педагогических/научных работников с медицинской деятельностью, а случай с болезнями животных остаётся без экспертной оценки. Новые оценки пар не выставлялись; специальные веса или document IDs не добавлялись. Сравнение ограничено выборкой и не является разметкой юридической правильности. Медианы 22,955 мс для default и 16,115 мс для DFS не используются как оценка скорости: default всегда исполнялся первым, возможен cache/order bias.
Frozen experiment metadata is stored outside the repository in
reports/search-benchmark-2026-11-02/dfs-probe-e653-summary.json. The paired
response-capture artifact dfs-probe-e653.jsonl has SHA-256
4a9373ba5236c5c5a1d85139b6dbe67bcf01df02216f8b2f292e5c451e6432c8.
Выборка содержала base index (42 624 501 fragments) и read-only supplement
(8 934 fragments); UUID/count guards прошли, основной индекс и сохранённые
отзывы во время эксперимента не менялись. В PR не включены сырые ответы,
персональные данные или выдачи документов.
Кодовый тест проверяет URL, encoded multi-index path, RU/KY, пустую выдачу, прежнее тело запроса и неизменность single alias/index и filters. Read-only integration gate для root-owned runtime:
AKYLDASH_OPENSEARCH_URL=http://127.0.0.1:8081 \
AKYLDASH_OPENSEARCH_INDEX='akyldash-fragments-v2,akyldash-benchmark-supplement-20261103-r1' \
PYTHONPATH=backend python3 -m unittest \
backend.test_search_api_opensearch.SearchMultiIndexDfsOpenSearchTest.test_global_statistics_mode_accepts_read_only_multi_index_search -v
Root выполнил этот read-only gate на 8081 для 1c7ddf2: фактический multi-index
URI использовал DFS и подписанный token сохранял полный scope. Gate выполняет
только поиск и не создаёт/удаляет индексы. Полный benchmark на том же code SHA
и frozen baseline приведён ниже; 300-query experiment и более ранние прогоны
остаются отдельными сравнительными срезами.
Откат к предшествующему приложению возвращает query_then_fetch для multi-index
поиска. Индекс, документы, saved reviews и старые подписанные токены при этом
не требуют изменений или миграции; новые отзывы, сохранённые до rollback,
остаются помечены search-8. Для supplement действует отдельный rollback
контракт ниже: отключение его подключения скрывает только добавленные редакции,
не удаляя подготовленные файлы и базовый индекс.
Итоговый pinned прогон search-8
Полный isolated прогон для executable SHA 1c7ddf2fcbd7c55920470e7ac52339fdab0366db
собрал 7 500 уникальных запросов с версией search-8. SHA-256 выдачи:
fc02e22e5b3d612813ffc0bdaf9892bb16ee4c29af3e1b77f79e58a0a45c8ec1. Итог
сопоставлен с frozen baseline и двумя явно указанными промежуточными режимами:
| Сравнение | Изменён порядок | Изменён top-1 |
|---|---|---|
Исходный baseline search-6 |
537 | 103 |
Code-only 726b1ee, search-7 |
521 | 93 |
Code + три акта, e653f3b, без DFS |
425 | 63 |
Это число изменений порядка, а не улучшений. Все шесть пустых выдач сохранились во всех четырёх прогонах. Среди ранее проверенных Codex (ИИ) пар с нулевой оценкой top-1 стало 26 вместо 28 в baseline; остальные 26 не объявлены исправленными, и новые выдачи автоматически не оценивались.
| Добавленный акт | Наблюдался в top-1 / top-3 / top-10 |
|---|---|
Трудовой кодекс КР 230021083 |
24 / 58 / 105 |
Земельный кодекс КР 230029465 |
4 / 11 / 26 |
Закон об охране здоровья 230000918 |
34 / 51 / 92 |
Все шесть точных названий новых актов на RU/KY дали ожидаемый текущий акт первым. Все 284 запроса, общие с целевым DFS экспериментом, совпали с ним по полному порядку кодов и редакций. Проверки dataset guard подтвердили неизменность исходного индекса, manifest, mappings и UUID/count, а также 115 подключённых файлов. 7 503 записи локальной лаборатории совпали с backup; обе SQLite integrity checks прошли. Записи оценок, комментарии и пользователи не менялись.
Шесть межтематических контекстов нельзя автоматически считать нерелевантными: медицинский закон остался первым в пяти запросах о педагогических/научных работниках и в одном запросе о болезнях животных. Для педагогических случаев проверенная статья 150(8) связывает аттестацию этих работников с медицинской деятельностью; новые оценки пар не выставлялись. Вопрос о животных также остался без экспертной оценки. ИИ-аннотации не доказывают полноту правового ответа или юридическую правильность ранжирования.
Режим и scope дополнительно проверены на runtime 8081: для multi-index /search
уходит DFS, для одиночного индекса и filters — прежний default; тело DSL
одинаково. Alias проверен transport mock без создания alias. Runtime mode/API
smoke включал HMAC/TTL, signed scope, сохранение пустого результата и
отрицательные rank/code сценарии. Browser UI fixture прошёл на 726b1ee; UI
побайтно не менялся в следующих SHA, но отдельный browser прогон на 1c7ddf2
не выполнялся. Медиана backend 23.685 мс описательна и не является SLO или
доказательством ускорения.
Эксперимент подключал только три акта в отдельном supplement и не доказывает полноту всего корпуса, актуальность всех редакций, наличие вложений или production-готовность. Отключение overlay/supplement скроет эти новые редакции из локального поиска/API, но сохранит подготовленные файлы, review-записи и исходный индекс. Production не менялся. Подробный технический отчёт и raw benchmark остаются во внешнем рабочем наборе и в PR не включены.
Пустые выдачи в лаборатории
Исходный контракт блокировал сохранение шести подписанных пустых снимков:
API отвергал results: [], а интерфейс читал поля из пустого placeholder.
Теперь signed snapshot может сохранить пустую оценку с обязательным общим
комментарием. В записи top_result_code остаётся null, список результатов
пустой. Непустой снимок по-прежнему обязан получить оценку каждого ранга ровно
один раз, совпадающую с подписанным кодом документа. HMAC, TTL и текущая
аутентификация не меняются.
Подписанный снимок теперь сохраняет scope запроса при явно настроенном
multi-index выражении (A,B): provenance не сужается до индекса, который
случайно вернул единственный результат, и сохраняется при пустой выдаче. Для
одного настроенного индекса или alias остаётся прежнее поведение: единственный
concrete _index записывается как provenance; если OpenSearch не сообщил его,
сохраняется настроенный scope. Новые отзывы получают эту метку при сохранении.
SQLite-схема, исторические записи и формат подписанных токенов не меняются.
Откат к старому коду не требует миграции и не переписывает существующие
отзывы; пока работает откатанная версия, новые multi-index отзывы снова могут
получить суженную метку, поэтому исправление provenance следует вернуть вместе
с основным выпуском.
Существующая отдельная база локальной лаборатории содержала 7 494 сохранённых сессии, 79 AI-оценок и 55 516 диагностических комментариев; 74 715 оценок результатов остались пустыми намеренно. Integrity check прошёл, порядок кодов и редакций baseline не менялся. Эти сохранённые оценки не переписывались. В последующем изолированном trial шесть реальных подписанных пустых отзывов с общими комментариями были сохранены как записи 7498–7503; итоговые 7 503 строки лаборатории совпали с backup, integrity check прошёл. Пустые query-level отзывы не являются оценками отдельных документов.
Пробел актуального корпуса и безопасный контракт закрытия
Отдельно от ранжирования, в исходном base index не было трёх
подтверждённых действующих актов (все три canonical ID имели _count = 0):
| Акт | Canonical ID | Последняя проверенная редакция |
|---|---|---|
| Трудовой кодекс КР от 23.01.2025 № 23 | 230021083 | 57548 от 28.07.2026 |
| Земельный кодекс КР от 18.07.2025 № 149 | 230029465 | 34746 от 18.07.2025 |
| Об охране здоровья граждан КР от 12.01.2024 № 14 | 230000918 | 47553 от 23.01.2026 |
Это разрыв между нормализованными документами и индексом, а не полное
отсутствие актов в локальном архиве: в data/minjust-normalized уже есть
canonical-каталоги всех трёх документов, manifest сообщает 209 958 успешно
обработанных документов. Однако трудовой акт там отстаёт от официальной
редакции 57548 от 28.07.2026 (последняя подготовленная — № 60 от 23.06.2026);
земельный акт нормализован до редакции № 10 от 18.07.2025; для акта о здоровье
последняя подготовленная — № 60 от 23.01.2026. В base index эти три canonical
ID не попали. Прежние отменённые трудовой и земельный кодексы нельзя переименовать
или поднять как замену актуальным нормам.
Для локальной изолированной проверки подготовлены точные официальные RU/KY редакции: 3 документа и 8 934 фрагмента. На runtime 8081 они были временно подключены отдельным supplement-index и доступны API через read-only overlay catalogues. Это не изменение base index или production и не доказательство полноты корпуса. Вложения и полная история новых canonical ID отдельно не подтверждены.
Закрытие пробела требует отдельного контролируемого обновления данных:
- Заново сверить официальные canonical ID, статус, edition ID и дату; сохранить исходный ответ, хеши и provenance в отдельном staging-каталоге.
- Проверить полноту обеих языковых версий, ссылки на редакции, историю и вложения; не импортировать метаданные с ненужными персональными полями.
- Нормализовать документы отдельно и проверить exact-count/hash. Для ограниченного дополнения собрать отдельный версионный индекс только из этих актов; не пересобирать и не перезаписывать индекс из 42,6 млн фрагментов.
- Изолированный read-only trial уже проверил отдельный supplement-index,
подключённые каталоги
data_root, RU/KY выдачу, current-edition routes, collapse и контрольные запросы. Перед новым подключением нужно повторно сверить official source и dataset guards; trial не записывал новые документы в base index и не менял alias. - Публикация требует отдельного владельческого разрешения и проверенного rollback. При отключении локального supplement-index поиск и просмотр новых редакций из него прекращаются; подготовленные файлы и уже сохранённые отзывы сохраняются, базовый индекс не удаляется. PR фиксирует код и результаты локального испытания; production, base index и исходные данные не менялись.
Что остаётся проверить
- Финальный code/data эксперимент и full 7 500 run относятся к executable SHA
1c7ddf2; текущий follow-up commit меняет только этот report и changelog. CI и полное независимое ревью должны повторно пройти на новом PR head. - Browser fixture пустой выдачи прошёл на
726b1ee; UI побайтно неизменён в следующих commit. Для1c7ddf2выполнены реальные API/runtime smokes, но новый полный browser run не заявляется. - Три supplement-акта не подтверждают полноту всего корпуса, полную историю, вложения, лицензирование или production readiness. Для новой публикации нужен свежий source/edition check и отдельное разрешение владельца.
- Измерения top-k, AI-разметка и технические smoke checks не заменяют независимую экспертную оценку юридической релевантности.
Исходные измерения и хеши хранятся вне репозитория в рабочем наборе
search-benchmark-2026-11-02. В этот репозиторий не включаются сырые ответы
MinJust, диагностические логи, внешние пользовательские данные или выгрузка
лабораторных комментариев.