Месяц назад крупный B2B-поставщик выкатил внутреннего ИИ-ассистента для сейлзов. На второй день система выдала VIP-клиенту жесткий отказ в кредитной линии. Ассистент подтянул в контекст профиль ИП с похожим названием и миллионными долгами вместо нужного головного АО. Сделку спасли чудом. Проект заморозили на аудит.
Это классическая иллюстрация того, как отсутствие MDM-слоя убивает генеративный поиск на старте. Дедупликация сущностей для RAG — не опциональный тюнинг, а фундаментальный этап архитектуры. Именно здесь кроется ответ на вопрос, почему RAG галлюцинирует данные, а не модель. Вы не можете винить эмбеддинги в том, что один контрагент размазан по вашим базам в пяти разных написаниях.
Векторный индекс прямолинеен. Он просто мапит текст в многомерное пространство. Для него «ООО Ромашка», «Ромашка-Трейд» и криво заведенный в биллинге «Romashka LLC» — это три разные вселенные. Когда менеджер спрашивает статус по клиенту, ретривер тащит в контекст топ-5 чанков. В них лежат противоречивые статусы, разные балансы и конфликтующие договоры. LLM честно пытается синтезировать этот шизофренический контекст и выдает франкенштейна. Бизнес называет это галлюцинацией нейросети. Бизнес ошибается. Вы скормили системе мусор. Она вернула мусор. Точка.
Детерминированный матчинг против вероятностного: битва за Entity Resolution
Как решать проблему? Первый порыв любого бэкендера — детерминированный матчинг. Просто сджоинить всё по ИНН и ОГРН. Идеально на бумаге. Катастрофа в проде.
В любой унаследованной CRM около трети записей не имеют корректного ИНН. Его вбили с опечаткой, вставили КПП вместо него или вообще оставили поле пустым, потому что сейлз торопился закрыть лид. Детерминированный подход здесь слепнет. Вы отбрасываете грязные данные, и они оседают мертвым грузом, порождая те самые дубли при индексации. Точность ответов падает, потому что половина фактов просто не склеилась с нужным профилем.
Настоящий бой выигрывает вероятностный entity resolution. Мы говорим о фреймворках уровня Splink или Zingg. Здесь в игру вступает концепция блокировки и вероятностного сравнения. Вы не гоняете декартово произведение всех записей на все — это вычислительный суицид. Вы создаете правила блокировки. Например, берете первые четыре буквы нормализованного названия компании и код региона. Это ваш жесткий блок. Внутри блока алгоритм считает метрики схожести: расстояние Левенштейна для названий, Jaro-Winkler для адресов, косинусное сходство для текстовых описаний.
from splink.duckdb.linker import DuckDBLinker
from splink.duckdb.plugin import comparison_library as cl
settings = {
"link_type": "dedupe_only",
"blocking_rules_to_generate_predictions": [
"l.region_code = r.region_code and substr(l.name_norm, 1, 4) = substr(r.name_norm, 1, 4)",
"l.inn = r.inn"
],
"comparisons": [
cl.exact_match("inn", term_frequency_adjustments=True),
cl.jaro_winkler_at_thresholds("name_norm", [0.9, 0.8]),
cl.levenshtein_at_thresholds("address", [2, 5])
],
"retain_matching_columns": True
}
linker = DuckDBLinker(df_raw_entities, settings)
df_predictions = linker.predict(threshold_match_probability=0.85)Этот код находит неочевидные связи, которые прямолинейная SQL-логика пропустит. А как же LLM для матчинга сущностей? Чистый хайп. Прогонять тяжелую языковую модель по миллионам пар строк слишком долго и неоправданно дорого. LLM-ассист уместен исключительно для сумеречной зоны — тех самых пар, где вероятность совпадения болтается между сорока и шестьюдесятью процентами. Остальное должна молотить суровая математика на CPU.
Сборка Golden Record для базы знаний и смерть near-duplicates
Найти дубли — только половина дела. Скармливать сырой граф связей в RAG категорически нельзя. Вам нужен golden record для базы знаний. Это единый канонический профиль сущности, собранный из всех разрозненных систем по правилам выживания атрибутов. Самый свежий баланс берем из биллинга, официальное название из ЕГРЮЛ, контактные лица из CRM. Именно этот очищенный слепок мы векторизуем и кладем в индекс.
Но профили юрлиц — это только метаданные. Есть еще неструктурированные тексты: договоры, допники, корпоративные переписки. Здесь возникает проблема near-duplicate в векторном индексе. Если юристы штампуют типовые договоры, у вас в базе копятся тысячи почти идентичных документов. Ретривер забивает окно контекста пятью одинаковыми абзацами из разных файлов. Места для полезных фактов не остается. Ассистент тупеет на глазах.
Решение — жесткий дедуп чанков до эмбеддинга. Использовать косинусное сходство на лету слишком тяжело. Прагматичный путь — алгоритмы MinHash и Locality-Sensitive Hashing. Вы хешируете шинглы каждого чанка. Если Jaccard similarity зашкаливает за девяносто пять процентов, чанк объявляется дублем. В индекс летит только один канонический текст, а в его метаданные дописываются ссылки на все документы-источники. Канонизация экономит токены и делает выдачу плотной.
Если вы строите Graph RAG, ставки удваиваются. Графовый поиск опирается на связи. Если одна компания представлена четырьмя изолированными узлами, ваш алгоритм обхода графа уткнется в тупик. Транзакции привязаны к одному узлу, руководство — к другому. Любой запрос про связи сущностей вернет пустоту. MDM-слой здесь действует как клей, стягивающий фрагментированный граф в единую рабочую топологию.
Human-in-the-loop, 152-ФЗ и аудит слияний
В регулируемых отраслях цена ошибки слишком высока. Когда мы в Morana Labs катили on-prem RAG для крупного финтеха, главным блокером стала не настройка LLM, а комплаенс. Банковская тайна и 152-ФЗ запрещают слепое алгоритмическое слияние профилей. Если скрипт ошибочно склеит счета двух разных однофамильцев, компания получит иск и штраф.
Поэтому для пограничных вероятностей встраивается интерфейс human-in-the-loop. Оператор дата-офиса видит спорные мёрджи и принимает решение вручную. Каждое действие пишет жесткий аудит-лог. Архитектура обязана поддерживать мгновенный откат слияния, который каскадом обновит векторный индекс и инвалидирует старые чанки. Без этого механизма вас просто не пустят в промышленную эксплуатацию.
До запуска системы в бой необходимо замерять метрики чистоты данных. Мы смотрим на precision и recall пайплайна матчинга на размеченной выборке. Считаем долю дублей в сырых данных и сравниваем с золотым датасетом. Только когда precision переваливает за девяносто девять процентов, можно открывать доступ пользователям.
Подготовка данных — грязная, сложная и абсолютно необходимая инженерия. Вы обязаны провести аудит источников, определить правила разрешения конфликтов, настроить вероятностный матчинг и собрать канонические профили. Иначе вы индексируете помойку. Если вашему бизнесу нужен рабочий инструмент, а не игрушка для демо, начните с фундамента. Построение MDM-слоя, дедупликация и запуск защищенных on-prem RAG систем под ключ — это то, чем мы занимаемся каждый день. Никакой магии, только инженерная дисциплина.