TL;DR
EnsembleLink — метод сопоставления записей между датасетами, когда данные "грязные": опечатки, сокращения, разные форматы одного и того же. Работает в два этапа: сначала широкий поиск кандидатов комбинацией семантического и лексического поиска (ensemble retrieval), затем точное ранжирование каждой пары через cross-encoder.
Предобученные языковые модели уже знают семантические связи из обучения на огромных текстовых корпусах — что "NYC" = "New York City", что "Dick" — сокращение от "Richard", что "Lutte ouvrière" (фр.) = "Workers' Struggle" (англ.). Модель умеет ловить опечатки — "Montgomery" vs "Montgomery" отличаются одной буквой, но большинство символьных n-грамм совпадают. Проблема: при прямом запросе "сопоставь эти списки" модель может упустить неочевидные совпадения — сокращение "НАЦП" и полное "Национальная ассоциация" не имеют общих слов.
Метод из трёх шагов в одном конвейере: (1) Retrieval — найти топ-K кандидатов двумя способами (embeddings для семантики + character n-граммы для лексики), взять объединение (~50-80 кандидатов). (2) Rerank — cross-encoder оценивает каждую пару (query, candidate) вместе, не отдельно, посимвольно сравнивая и выдавая score 0-1. (3) Select — выбрать топ-1 по score. Для задач требующих world knowledge (кросс-языковые переводы, исторические названия) опционально добавить LLM-reranker.
Схема метода
ЭТАП 1: Retrieval (широкий поиск кандидатов)
├─ Dense retrieval: embeddings + cosine similarity → топ-K кандидатов
├─ Sparse retrieval: TF-IDF character n-grams → топ-K кандидатов
└─ Union (объединение) → ~50-80 уникальных кандидатов
ЭТАП 2: Rerank (точная оценка каждой пары)
└─ Cross-encoder: (query, candidate) → score [0, 1]
↳ посимвольное сравнение, аббревиатуры, опечатки
ЭТАП 3: Select (выбор лучшего)
└─ arg max(score) → топ-1 кандидат
ОПЦИОНАЛЬНО (для world knowledge):
└─ LLM reranker выбирает среди топ cross-encoder кандидатов
↳ только если нужны знания: переводы, историч. названия
Все три этапа выполняются в одном запросе, если адаптируешь принцип для чата.
Пример применения
Задача: У тебя список клиентов из CRM с опечатками и сокращениями. Нужно сопоставить их с чистым списком контрагентов из 1С, чтобы проставить ИНН и юридические адреса.
Промпт:
У меня два списка компаний. Нужно сопоставить записи из первого (CRM с опечатками) со вторым (чистая база 1С).
СПИСОК 1 (CRM):
1. "НАЦП пром и офис недв"
2. "ТехНет"
3. "Авиакомпании России"
4. "Service Employees"
СПИСОК 2 (база 1С):
- Национальная ассоциация промышленной и офисной недвижимости
- Технологическая сеть
- Профсоюз работников сферы услуг
- Ассоциация авиакомпаний России "Аэрофлот - Российские авиалинии"
- Технологии будущего
- Российская ассоциация производителей
Используй двухэтапный подход:
ШАГ 1 — RETRIEVAL (широкий поиск):
Для каждой записи из CRM найди топ-3 кандидатов из базы 1С по ДВУМ критериям:
• Семантическая близость: похожий смысл, даже если слова разные (синонимы, переводы, сокращения)
• Лексическая близость: совпадение букв, частей слов, с учётом опечаток 1-2 символа
Объедини результаты обоих критериев (union).
ШАГ 2 — RERANK (точная оценка):
Для каждого кандидата оцени пару (CRM, база) очень внимательно:
- Сравни каждое слово посимвольно
- Проверь аббревиатуры и сокращения ("НАЦП" → "Национальная ассоциация")
- Учти опечатки (1-2 символа)
- Переводы и синонимы
- Оценка совпадения по шкале 0-1
ШАГ 3 — SELECT:
Выбери лучшего кандидата для каждой записи.
Формат вывода: таблица
CRM | Совпадение из 1С | Score | Объяснение
Результат:
Модель покажет таблицу с сопоставлениями. Для каждой записи из CRM — лучший матч из базы 1С со score и объяснением. Метод поймает что "НАЦП" = аббревиатура "Национальная ассоциация", "ТехНет" близко к "Технологическая сеть" (совпадение корня + опечатка), "Авиакомпании России" = часть полного названия, "Service Employees" = перевод "Профсоюз работников сферы услуг". Покажет на каком этапе (семантика или лексика) нашёлся кандидат и почему он был выбран на rerank.
Почему это работает
Слабость LLM: При прямом запросе "сопоставь эти списки" модель работает в один проход — она сразу пытается выбрать совпадение. Если сокращение неочевидное ("НАЦП" не содержит слов из "Национальная ассоциация") или есть несколько похожих вариантов, модель может ошибиться или упустить правильный вариант. Это проблема recall — не все правильные кандидаты рассматриваются.
Сильная сторона LLM: Модели обучены на огромных текстовых корпусах и выучили паттерны мира — что "OKC" = "Oklahoma City" из миллионов упоминаний в веб-текстах, что "Dick" — это "Richard" из биографий, что "ААРП" = "American Association of Retired Persons" из статей. Они умеют находить семантическую близость даже при нулевом лексическом совпадении. И умеют ловить опечатки — "Монгомери" vs "Монтгомери" сохраняют большинство биграмм (мо-он-нг-го-ом-ме-ер-ри).
Как метод использует это: Two-stage подход разделяет recall и precision. На первом этапе (Retrieval) комбинация двух критериев гарантирует что правильный кандидат попадёт в список — даже если это чистая аббревиатура без лексического совпадения (поймает семантика) или опечатка в незнакомом слове (поймает лексика). На втором этапе (Rerank) пристальное посимвольное сравнение пары позволяет отличить "Montgomery" от "Montgomery" и понять что "AARP" = "American Association of Retired Persons" (каждая буква — первая буква слова).
Рычаги управления промптом:
- K (число кандидатов на Retrieval) — уменьши до 3-5 для простых задач (экономия времени/токенов), увеличь до 10-15 для сложных (больше шансов поймать неочевидный match)
- Два критерия поиска — убери лексический если данные очень разные (разные языки, только аббревиатуры); убери семантический если нужна строгая лексическая близость
- Инструкция "объясни" — добавь "объясни логику" в ШАГ 2 чтобы видеть рассуждения; убери для краткого вывода
- Порог отсечения — добавь "если score < 0.6 → отметь 'нет совпадения'" чтобы не генерировать ложные match
- Опциональный LLM-этап — добавь только для задач где нужны знания мира (кросс-языковые переводы не-буквальные, исторические названия, культурный контекст)
Шаблон промпта
У меня два списка: {описание_списка_1} и {описание_списка_2}.
Задача: сопоставить записи из первого списка со вторым.
СПИСОК 1:
{данные_списка_1}
СПИСОК 2:
{данные_списка_2}
Используй двухэтапный подход:
ШАГ 1 — RETRIEVAL (широкий поиск):
Для каждой записи из СПИСОК 1 найди топ-{K} кандидатов из СПИСОК 2 по ДВУМ критериям:
• Семантическая близость: похожий смысл, даже если слова разные (синонимы, переводы, сокращения, аббревиатуры)
• Лексическая близость: совпадение символов, частей слов, с учётом опечаток 1-2 символа
Объедини результаты обоих критериев (union).
ШАГ 2 — RERANK (точная оценка):
Для каждого кандидата оцени пару (запись, кандидат) очень внимательно:
- Посимвольное сравнение
- Аббревиатуры и сокращения
- Опечатки (1-2 символа разницы)
- Переводы и синонимы
- Оценка совпадения по шкале 0-1
ШАГ 3 — SELECT:
Выбери лучшего кандидата для каждой записи (максимальный score).
{формат_вывода}
Плейсхолдеры:
- {описание_списка_1} и {описание_списка_2} — что за данные (клиенты, города, товары, имена)
- {данные_списка_1} и {данные_списка_2} — сами списки (скопируй-вставь)
- {K} — число кандидатов на retrieval (обычно 3-10 для малых датасетов до 100 строк, 10-20 для средних до 500)
- {формат_вывода} — таблица / JSON / список с объяснениями
🚀 Быстрый старт — вставь в чат:
Вот шаблон двухэтапного сопоставления данных (retrieval-rerank). Адаптируй под мою задачу: [твоя задача].
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит какие у тебя списки, сколько записей, какие особенности данных (опечатки, сокращения, разные языки, аббревиатуры) — чтобы настроить K и критерии поиска. Она возьмёт паттерн retrieval-rerank из шаблона и адаптирует под задачу.
Ограничения
⚠️ Размер данных: Для датасетов больше ~500-1000 записей упираешься в лимит контекста чата. Оригинальный метод разработан для batch processing с кодом (Python/R) — там датасеты до 64,000 записей обрабатываются за минуты. В чате работает только для небольших объёмов. Если данных больше — нужен код или API.
⚠️ Субъективные критерии: Метод хорош для объективных совпадений (одно и то же название/имя/место с вариациями написания). Если критерий субъективен ("похожие компании по профилю бизнеса") — нужна другая логика, не string matching.
⚠️ Множественные совпадения: Метод выбирает топ-1 кандидата. Если одной записи соответствует несколько правильных совпадений (например, одна организация сменила название и есть обе версии в базе) — нужно явно указать "найди все совпадения score ≥ 0.8".
⚠️ Вычислительная точность: Оригинал использует специализированные модели (cross-encoders обучены на задачах сопоставления текстов). В чате LLM симулирует эту логику через инструкции — это менее точно для edge cases. Автор сообщает 90-99% accuracy на бенчмарках с кодом; в чате ожидай ~80-95% для хороших данных.
⚠️ World knowledge только через LLM: Для задач требующих энциклопедических знаний (кросс-языковые переводы не-буквальные: "Podemos" → "We Can") базовый cross-encoder не справляется. В оригинале добавляют LLM-reranker (Qwen3-8B). В чате GPT-4/Claude автоматически используют свои знания — но для o1/o3-mini с ограниченным knowledge может не сработать.
Как исследовали
Команда протестировала на четырёх бенчмарках разной природы: города США (7,118 грязных названий из PPP-заявок → 28,889 официальных из Census), кандидаты-избиратели в Калифорнии (494 пары: имена с опечатками, отчествами, прозвищами), организации (242 из судебных документов → база спонсоров кампаний: вариации в суффиксах, порядке слов, аббревиатурах), и мультиязычные партии (названия на родных языках → английские переводы, 32 страны включая нелатинские скрипты: японский, греческий, турецкий).
Сравнили с двумя baseline: fastLink (вероятностный метод Fellegi-Sunter без обучения) и fuzzylink (supervised: LLM генерирует training labels через GPT-4o-mini, обучает логрег). Все методы тестировали одинаково: 40% записей в тест, каждый метод выбирает топ-1 для каждого запроса из полного корпуса. Accuracy = процент правильных.
Результат шокировал: Zero-shot метод без единого примера обучения обогнал supervised метод на трёх из четырёх задач. На городах 90.0% vs 71.3% (fastLink) и 71.7% (fuzzylink). На кандидатах 99.0% vs 23.0% и 96.5%. На организациях 96.1% vs 28.2% and 92.2%. Единственное исключение — мультиязычные партии: там zero-shot cross-encoder дал 84.3%, supervised fuzzylink только 39.1%. Но с добавлением LLM-reranker (Qwen3-8B для world knowledge) — 89.4%, превысив всех.
Почему так получилось? Предобученные cross-encoder модели уже выучили паттерны из веб-корпусов — что "OKC" = "Oklahoma City" из миллионов упоминаний, что "Dick" = "Richard" из биографических текстов, что "AARP" расшифровывается из контекстов где встречались обе формы. Supervised методы переобучаются на конкретных примерах — если в training data не было примера "South Ozone Park" → "Queens", они не generalize. Предобученные модели видели эти связи в обучающем корпусе.
Ablation experiments показали что ensemble retrieval критичен. Убери dense (embeddings) — потеряешь "OKC" → "Oklahoma City" (нет лексического совпадения). Убери sparse (n-grams) — потеряешь "Montgomery" → "Montgomery" (одна опечатка смещает embedding). С обоими retrieval methods правильный кандидат попадает в топ-50 почти всегда, cross-encoder только выбирает.
Scalability тест: На DBLP-Scholar бенчмарке (64,263 библиографических записи) метод показал 97.9% top-1 accuracy и обработал всё за ~12 минут на consumer GPU (RTX 4090). Это обогнало GPT-4 zero-shot (89.8%) и почти догнало fully supervised методы требующие тысячи labels: Ditto 94.3%, DeepMatcher 94.7%. На CPU inference в 30× медленнее но всё равно feasible — 2 секунды на запрос.
Ключевой инсайт для практики: Для задач не требующих world knowledge (города, имена, организации на одном языке) — cross-encoder быстрее и точнее LLM. LLM-reranker нужен только если семантическое совпадение не буквально: "Podemos" (исп. "мы можем") → "We Can" или "Lutte ouvrière" (фр. "рабочая борьба") → "Workers' Struggle". Для таких случаев trade-off: cross-encoder ранжирует ~50 пар/сек, LLM — ~2 пары/сек. Выигрыш в accuracy +5-7%, проигрыш в скорости 25×.
Адаптации и экстраполяции
🔧 Техника: пороговая фильтрация → отсеять ложные match
В оригинале метод всегда выбирает топ-1. Но иногда правильного совпадения нет в базе — тогда любой выбор будет ошибкой.
ШАГ 3 — SELECT:
Если максимальный score ≥ {порог} → выбери этого кандидата
Если максимальный score < {порог} → отметь "НЕТ СОВПАДЕНИЯ"
Формат вывода: таблица
Запрос | Лучший кандидат | Score | Статус (Match / No match) | Объяснение
Где {порог} зависит от задачи: 0.7-0.8 для строгих критериев, 0.5-0.6 для мягких. Модель в "Объяснении" покажет почему score низкий (сильные отличия в ключевых словах, разные домены).
🔧 Техника: инвертировать для дедупликации → найти дубли внутри одного списка
Вместо сопоставления двух списков можно искать дубликаты внутри одного.
У меня список клиентов с возможными дубликатами (одна компания записана по-разному).
СПИСОК:
{данные}
Задача: найти группы записей, которые относятся к одной компании.
ШАГ 1 — RETRIEVAL:
Для каждой записи найди топ-5 самых похожих **других** записей из этого же списка (исключая саму себя) по двум критериям: семантика + лексика.
ШАГ 2 — RERANK:
Оцени каждую пару на вероятность что это одна компания (score 0-1).
ШАГ 3 — CLUSTER:
Сгруппируй записи где score ≥ 0.75 друг с другом.
Формат: Группы дубликатов | Записи в группе | Score внутри группы | Объяснение
Это даёт дедупликацию — ценно для очистки CRM, баз контактов, товарных каталогов.
🔧 Техника: добавить контекстные поля → multi-field matching
Исследование сравнивало одно поле (название). Но часто есть дополнительный контекст: город, регион, дата, категория.
СПИСОК 1 (CRM):
Название | Город | Категория
ТехНет | Москва | IT
МегаТех | Санкт-Петербург | IT
...
СПИСОК 2 (база 1С):
Название | Регион | Отрасль
Технологическая сеть | Московская область | Информационные технологии
МегаТехнологии | Ленинградская область | Промышленность
...
ШАГ 1 — RETRIEVAL:
Найди кандидатов по названию (ensemble: семантика + лексика).
ШАГ 2 — RERANK multi-field:
Оценивай пару учитывая ВСЕ поля:
- Совпадение названия (вес 0.5)
- Совпадение географии: город/регион (вес 0.3)
- Совпадение категории/отрасли (вес 0.2)
Итоговый score = взвешенная сумма. Объясни вклад каждого поля.
ШАГ 3 — SELECT top-1.
Это повышает точность когда названия похожи (много "МегаТех" в разных городах) — география разделяет.
🔧 Техника: batch с summarization → обработка больших списков
Для списков больше лимита контекста — разбей на батчи, обработай каждый, собери результаты.
[В первом сообщении:]
Задача: сопоставить 800 записей из CRM с 1200 из базы 1С. Разобьём на батчи по 100.
Вот СПИСОК 2 (база 1С) целиком — {1200 записей}.
Запомни его для следующих шагов.
[Во втором сообщении:]
BATCH 1: записи 1-100 из CRM.
{100 записей}
Используй двухэтапный подход retrieval-rerank для каждой записи.
Формат: таблица с match.
[Повторить для batch 2, 3... batch 8]
[В финальном сообщении:]
Собери результаты из всех 8 батчей в один CSV.
Отметь случаи где score < 0.7 как "требует проверки".
Это обходит лимит контекста — модель держит эталонную базу в памяти цепочки, обрабатывает батчами.
Ресурсы
EnsembleLink: Accurate Record Linkage Without Training Data (Dasanaike, January 2026)
Упомянутые методы: - fastLink (Enamorado et al., 2019) — вероятностный Fellegi-Sunter framework без обучения - fuzzylink (Ornstein, 2025) — supervised с LLM-generated training labels - DeepMatcher (Mudgal et al., 2018) и Ditto (Li et al., 2020) — deep learning supervised методы - Классика: Fellegi-Sunter (1969), Jaro-Winkler similarity (Jaro, 1989), Levenshtein distance (1966)
Автор: Noah Dasanaike, PhD Candidate, Department of Government, Harvard University
Бенчмарки: - Города: Kaufman and Klevs (2022) PPP loans → U.S. Census places - Кандидаты: California Elections Data Archive → L2 voter file - Организации: Abi-Hassan et al. (2023) amicus briefs → DIME campaign finance (Bonica, 2016) - Партии: ParlGov database (Döring and Manow, 2012), 32 страны - Scalability: DBLP-Scholar (Köpcke et al., 2010)
Модели в оригинале: Qwen3-Embedding-0.6B (dense retrieval), Jina Reranker v2 Multilingual (cross-encoder), Qwen3-8B (optional LLM reranker)
