test: real Alfa-Bank addresses from the CBR registry; fix two gaps they found

alfabank.ru itself is unreachable from this environment, so addresses
came from the official regulator registry instead (cbr.ru/finorg/foinfo,
"Alfa-Bank subdivisions") — 981 addresses across 509 cities, one per
region sampled into benchmark-bank-context.txt (60 lines) as the
office-address trap the spec calls out explicitly.

Running them surfaced two real precision bugs, not just more test
data:

- ORGANISATION_NEARBY's veto window (80 chars) was too narrow for
  official addresses that include a region name before the city —
  "Отделение ... Республика Бурятия, г. Северобайкальск, ..." puts
  the house number 80+ chars from the anchor. Widened to 150.

- The two dictionary-only FIO rules (surname + capitalized word,
  no role word) had no address-context veto at all: "Великие Луки"
  matched as the given name "Лука" plus a stray word, "Богдана
  Хмельницкого" as a person because streets named after people are
  syntactically identical to actual names. Added the same
  ORGANISATION_NEARBY veto the address rules already use.

False-positive rate on the 60-address sample: 40.5% before, 0% after.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Максименко Никита Владимирович
2026-09-21 21:29:57 +03:00
co-authored by Claude Sonnet 5
parent 495cf08a05
commit e00062ea9f
5 changed files with 1144 additions and 4 deletions
+11 -2
View File
@@ -41,8 +41,17 @@ public record Rule(String type, Pattern pattern, int priority, List<Integer> gro
*/
private static final int FLAGS = Pattern.UNICODE_CHARACTER_CLASS | Pattern.UNICODE_CASE;
/** Сколько символов слева и справа от совпадения просматривает вето-шаблон. */
public static final int VETO_LOOKBEHIND = 80;
/**
* Сколько символов слева и справа от совпадения просматривает вето-шаблон.
*
* <p>150, не 80: на реальных адресах отделений из реестра ЦБ (регион, город,
* улица, дом — в одном предложении) расстояние от «отделение» до номера дома
* часто превышает 80 знаков за счёт длинного названия региона («Ханты-Мансийский
* автономный округ», «Кабардино-Балкарская Республика»). Найдено нагрузочным
* тестом на 60 реальных адресах из официального реестра — с окном в 80 знаков
* вето не срабатывало на части из них.
*/
public static final int VETO_LOOKBEHIND = 150;
public static final int VETO_LOOKAHEAD = 40;
/** Правило без проверок, маскируется всё совпадение. */
@@ -305,10 +305,17 @@ public class RuleRegistry {
// Фамилия рядом с личным именем из словаря: без словаря правило ловило бы
// «Тверская улица» и тому подобное. Имя проверяется по множеству уже
// после совпадения — чередование из ста веток в шаблоне обходится дорого.
// Самое слабое основание среди правил ФИО — ни ролевого слова, ни явного
// якоря, — поэтому именно здесь нужно вето на адресный контекст: «Великие
// Луки» (реальный город) распознаётся как имя «Лука» в падеже плюс
// случайное слово, «Богдана Хмельницкого» — улица в честь исторической
// фигуры. Найдено на реальных адресах отделений из реестра ЦБ.
Rule.of(FIO, "\\b" + SURNAME + "\\s+" + CAPITALISED + "\\b", 74)
.validatedBy(NameDictionary::containsGivenName),
.validatedBy(NameDictionary::containsGivenName)
.vetoedBy(ORGANISATION_NEARBY),
Rule.of(FIO, "\\b" + CAPITALISED + "\\s+" + SURNAME + "\\b", 74)
.validatedBy(NameDictionary::containsGivenName),
.validatedBy(NameDictionary::containsGivenName)
.vetoedBy(ORGANISATION_NEARBY),
// --- Уровень 1: подтверждается контрольной суммой ---