Обзор модуляЧасть V · ~8 ч · Сложность: (средний)→(продвинутый) · Пререквизиты: Модуль 12, 13
К этому моменту мы знаем, как один корпус документов превращается в упорядоченный список результатов: каскад ранжирования L0–L3 (Модуль 12) отбирает кандидатов и выстраивает их по убыванию релевантности, а распределённая инфраструктура (Модуль 13) обслуживает это на тысячах машин с приемлемой задержкой. Но настоящая поисковая система — это не один индекс и не один ранжирующий стек. Это множество источников: основной веб-индекс, индекс картинок, видео, новостей, товарных карточек, карт, ответов-фактов, локального бизнеса, переводов, погоды. Каждый источник — это, по сути, отдельная мини-поисковая система со своим индексом, своей моделью релевантности и своими ограничениями по задержке. Задача этого модуля — объяснить, как из ответов многих таких источников собирается одна страница выдачи (search engine results page, SERP).
В сквозном конвейере «обход → индекс → факторы → ранжирование → выдача → постобработка → измерение» мы вступаем в стадию формирования выдачи. Метапоиск (metasearch) — это архитектурный приём: вместо одного монолитного ответа система выполняет запрос параллельно по многим источникам (scatter), собирает их частичные ответы (gather) и сливает в единый ранжированный список. Федерация (federation) — это политика: какие вертикали (картинки, видео, новости и т.д.) уместны для данного запроса, в какой форме их показать и в какую позицию SERP вставить. Метапоиск отвечает на вопрос «как технически собрать ответ из частей», федерация — «что и куда вставить, чтобы страница была полезной».
Мы разберём трёхуровневую архитектуру метапоиска (верхний/средний/базовый), паттерн scatter-gather и его отказоустойчивость; алгоритмы слияния частичных ответов и протоколы обмена между уровнями; механику федерации вертикалей — предсказание интента, блендинг (blending) и выбор позиции вставки блока; наконец — формирование финального ответа: размер выдачи, предельная глубина, пагинация и сборка SERP. Это модуль о том, почему страница выглядит именно так, как выглядит, и почему «десять синих ссылок» давно стали лишь одним из многих блоков.
Как читать по трекамВнимание. Сложность модуля растёт от главы к главе: 14.1 и 14.4 — средний уровень ((средний)), 14.2 и 14.3 — продвинутый ((продвинутый)). Главы 14.2 (слияние, отказоустойчивость) и 14.3 (федерация вертикалей) требуют понимания вероятностного ранжирования и оценки качества выдачи; если устойчивость распределённых систем для вас нова — освежите Модуль 13.
- Студент — обязательны 14.1 (архитектура, scatter-gather) и 14.2 (алгоритмы слияния: round-robin, взвешенное, на основе обучения). Федерацию вертикалей (14.3) разберите на уровне идей — это прикладная инженерия. 14.4 — обзорно.
- Инженер — всё обязательно. Особое внимание: инженерные заметки про бюджеты задержек, тайм-ауты, частичные ответы, деградацию (graceful degradation), кэширование на каждом уровне, идемпотентность фан-аута. Это ядро serving-стека выдачи.
- SEO — главы 14.3 и 14.4 критичны: как контент попадает в вертикали (картинки/видео/новости/карточки), почему органический результат «сдвигается вниз» блоками вертикалей, что определяет триггер вертикали для запроса. SEO-врезки разбросаны по модулю.
- Смешанный — последовательно весь модуль; формальные части слияния (14.2) можно бегло просмотреть при первом проходе, вернувшись к ним из Модуля 15.
- 14.1. Архитектура метапоиска и scatter-gather — три уровня (верхний/средний/базовый), фан-аут запроса, сбор частичных ответов, бюджет задержки. (средний)
- 14.2. Слияние ответов источников — алгоритмы merge, протоколы обмена, нормализация скоров, отказоустойчивость слияния (тайм-ауты, частичные ответы, деградация). (продвинутый)
- 14.3. Федерация вертикалей — предсказание интента, блендинг, выбор вертикалей и позиции вставки блока на SERP. (продвинутый)
- 14.4. Формирование финального ответа (SERP) — размер выдачи, предельная глубина, пагинация, дедупликация на стыке, сборка страницы. (средний)
Цели обучения
После главы студент сможет:
- Объяснить, зачем поисковая система разбивается на множество источников и почему ответ собирается из частей, а не из одного индекса.
- Описать трёхуровневую архитектуру метапоиска (верхний/средний/базовый узел) и роль каждого уровня.
- Реализовать паттерн scatter-gather: параллельный фан-аут запроса по источникам и сбор частичных ответов.
- Рассчитать бюджет задержки (latency budget) для дерева фан-аута и объяснить, почему задержка определяется самым медленным узлом (tail latency).
- Сравнить scatter-gather с последовательным опросом источников и обосновать выбор.
Почему один индекс не справляется
Веб-индекс, индекс картинок и индекс новостей различаются всем: единицей документа (страница против изображения против события), темпом обновления (новости живут минуты, веб-страницы — недели), моделью релевантности (для картинок важны визуальные признаки и подпись, для новостей — свежесть и авторитет издания), требованиями к задержке. Слить их в один монолит невозможно и не нужно. Поэтому система строится как набор источников (sources / backends), каждый из которых — самостоятельная мини-поисковая система с собственным индексом и ранжированием (тем самым каскадом из Модуля 12).
Метапоиск (metasearch) в нашем (внутреннем) смысле — это опрос множества собственных индексов одной организации и слияние их ответов. (Исторически словом «метапоиск» называли и опрос чужих поисковиков, но в инженерии современной ПС это в первую очередь внутренняя федерация.)Интуиция. Представьте справочное бюро вокзала. Один оператор не знает всего: про поезда спрашивают у диспетчера, про погоду — у метеослужбы, про такси — у стоянки. Координатор бюро принимает ваш вопрос, рассылает его всем профильным службам сразу, собирает ответы и складывает в один связный ответ. Метапоиск — это и есть такой координатор, а службы — это источники.
Три уровня: верхний, средний, базовый
Дерево фан-аута имеет три логических уровня.
Код: Выделить всё
┌─────────────────────────┐
│ ВЕРХНИЙ узел (root) │ ← принимает запрос, федерация,
│ mixer / blender / root │ финальная сборка SERP
└────────────┬────────────┘
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ СРЕДНИЙ │ │ СРЕДНИЙ │ │ СРЕДНИЙ │ ← агрегатор источника
│ (web mid) │ │(images mid)│ │ (news mid) │ слияние шардов
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘
┌───┴───┐ ┌───┴───┐ ┌───┴───┐
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
[b][b][b]… [b][b][b]… [b][b][b]… ← БАЗОВЫЕ узлы (leaf),
шарды индекса шарды шарды держат часть индекса
- Верхний узел (root / mixer / blender) — точка входа запроса после его понимания (Модуль 5). Решает, какие источники опросить (политика федерации, глава 14.3), рассылает запрос вниз, дожидается ответов, выполняет финальное слияние и блендинг и собирает SERP (глава 14.4). Логики данных не хранит — он координатор.
- Средний узел (intermediate / source aggregator) — «голова» одного источника. Принимает запрос от верхнего узла, разворачивает его по всем шардам своего индекса, собирает их частичные ответы, сливает в единый ранжированный список своего источника и возвращает наверх «top-k от веба» / «top-k от картинок». Здесь живёт ранжирование источника (каскад L0–L3 из Модуля 12).
- Базовый узел (leaf / index server) — держит один шард индекса (часть документов) и умеет искать только в нём. Возвращает локальный top-k своего шарда. Это то, что Модуль 13 называет шардом по документам (document-partitioned index): каждый leaf отвечает за поднабор документов, и запрос обязан обойти все шарды, чтобы покрыть индекс.
Scatter-gather: рассеять и собратьИнженерная заметка. Уровней может быть и больше: при тысячах шардов между средним и базовым вставляют дополнительный слой агрегации, чтобы один узел не собирал ответы от тысяч leaf одновременно (степень ветвления, fan-out degree, держат в пределах десятков). Логически это всё тот же scatter-gather, просто рекурсивно вложенный. Глубокое дерево уменьшает нагрузку на каждый агрегатор, но добавляет «прыжки» (hops) и увеличивает хвостовую задержку.
Базовый паттерн на каждом уровне — scatter-gather (рассеять-собрать), он же fan-out/fan-in:
- Scatter (рассеивание). Узел отправляет запрос всем своим дочерним узлам параллельно (не последовательно!).
- Gather (сбор). Узел собирает ответы по мере поступления, пока не выполнится условие останова (получены все ответы либо истёк дедлайн).
- Merge (слияние). Полученные частичные ответы сливаются в один (как именно — глава 14.2) и передаются выше.
Код: Выделить всё
def scatter_gather(query, children, deadline_ms, k):
deadline = now() + deadline_ms
futures = [async_send(child, query, k) for child in children] # SCATTER (параллельно)
partials = []
for f in as_completed(futures, until=deadline): # GATHER до дедлайна
partials.append(f.result())
cancel_pending(futures) # отменяем опоздавших
return merge(partials, k) # MERGE → top-k
Бюджет задержки и хвостовая задержкаЗаблуждение. «Раз источников много, надо опрашивать их по очереди — сначала веб, если мало результатов, потом картинки.» Последовательный опрос складывает задержки: T = Σ tᵢ. Параллельный (scatter) берёт максимум: T ≈ max tᵢ. При десяти источниках разница — порядок величины. Источники опрашиваются одновременно.
У всей выдачи есть жёсткий бюджет задержки (latency budget) — например, верхний узел обязан ответить за 300 мс. Этот бюджет делится по дереву: если на блендинг и сетевые накладные расходы уходит 50 мс, источникам остаётся 250 мс; средний узел из них тратит часть на собственное слияние и оставляет шардам, скажем, 200 мс.
Ключевое свойство scatter-gather: узел не быстрее своего самого медленного ребёнка.
Код: Выделить всё
ответ узла ≈ max(t₁, t₂, …, t_n) + время_слияния
Интуиция. Колонна машин едет со скоростью самой медленной. Если 999 шардов ответили за 10 мс, а один — за 200 мс, агрегатор ждёт 200 мс. Это и есть хвостовая задержка (tail latency): при большом числе узлов почти всегда найдётся «отстающий», и медианная скорость шарда не спасает.
Разделение индекса: по документам vs по термамПример. Пусть у источника 100 шардов, задержка каждого распределена так, что 99-й перцентиль = 120 мс, а медиана = 30 мс. Вероятность, что все 100 ответят быстрее медианы, ничтожна. Ожидаемая задержка агрегатора близка к высоким перцентилям отдельного шарда. Поэтому борьба за хвост (а не за медиану) — главная тема serving-задержки. Приёмы (hedged requests, дублирование «отстающих», тайм-ауты) детально разбираются в Модуле 13; здесь важно, что они применяются на каждом уровне дерева фан-аута.
Почему запрос обязан идти во все шарды? Потому что индекс почти всегда разбит по документам (document partitioning): шард i содержит документы из подмножества Dᵢ, и любой из них может оказаться релевантным. Альтернатива — разбиение по термам (term partitioning), где шард держит постинг-листы части словаря; тогда запрос идёт только в шарды нужных термов, но слияние сложнее и хвост по «горячим» термам тяжелее. На практике почти всегда выбирают разбиение по документам — отсюда обязательный полный фан-аут (подробности — Модуль 13).
Частые заблуждения
Заблуждение. «Метапоиск — это про опрос чужих поисковых систем.» В современной инженерии это в первую очередь про опрос своих источников (веб, картинки, видео, новости…) и их слияние. Опрос внешних систем — частный и сегодня редкий случай.
Заблуждение. «Верхний узел сам ищет документы.» Нет. Верхний узел не хранит индекс. Он маршрутизирует запрос, ждёт частичные ответы и собирает страницу. Поиск происходит на базовых узлах.
Лаба / практикаЗаблуждение. «Чем глубже дерево фан-аута, тем быстрее.» Глубина снижает нагрузку на отдельный агрегатор, но добавляет сетевые «прыжки» и шансы поймать хвост на каждом уровне. Глубина — компромисс, а не ускорение.
Цель: реализовать scatter-gather-агрегатор и измерить эффект хвостовой задержки.
Вход: функция source(i, query), имитирующая i-й источник: спит случайное время по заданному распределению (например, медиана 30 мс, 99-й перцентиль 150 мс) и возвращает список (doc_id, score).
Шаги:
- Реализуйте scatter_gather(query, n_sources, deadline_ms, k): запустите все источники параллельно (потоки/корутины), соберите ответы до дедлайна, отмените опоздавших.
- Слейте частичные ответы в общий top-k простым способом (объединить, отсортировать по score, взять k).
- Прогоните 1000 запросов при n_sources ∈ {1, 5, 50, 500} и постройте распределение задержки агрегатора. Зафиксируйте медиану и 99-й перцентиль.
- Сравните с последовательной реализацией при n_sources = 5.
Критерий «сделано»: scatter-версия параллельна (а не последовательна), дедлайн соблюдается (опоздавшие отменяются), численно показан рост 99-го перцентиля агрегатора с ростом n_sources. Время ~60 мин.
Контрольные вопросы
- Чем различаются роли верхнего, среднего и базового узлов? Кто из них хранит индекс?
- Почему источники опрашиваются параллельно, а не последовательно? Запишите задержку для обоих случаев.
- Что такое хвостовая задержка и почему она растёт с числом шардов даже при быстрой медиане?
- Как бюджет задержки в 300 мс распределяется по трёхуровневому дереву? Приведите пример раскладки.
- Почему при разбиении индекса по документам запрос обязан обойти все шарды?
- Зачем вставляют дополнительные уровни агрегации между средним и базовым узлами? Какой ценой?
- Что произойдёт с задержкой и с полнотой выдачи, если один шард из 1000 «завис» и дедлайн истёк?
Цели обучения
После главы студент сможет:
- Сравнить алгоритмы слияния: round-robin, по нормализованному скору, ранговые методы (Borda, обратный ранг), обучаемое слияние.
- Объяснить проблему несопоставимости скоров разных источников и способы их нормализации/калибровки.
- Описать протокол обмена между уровнями: что узел передаёт вверх (top-k, скоры, метаданные, флаги полноты).
- Спроектировать отказоустойчивое слияние: тайм-ауты, частичные ответы, флаги деградации, graceful degradation.
- Оценить влияние выпавшего источника на качество и полноту итоговой выдачи.
Зачем вообще «сливать», а не «склеить»
Каждый источник вернул свой top-k, упорядоченный по своей шкале релевантности. Наивно склеить их подряд нельзя: скор 0.8 от веб-источника и скор 0.8 от источника картинок измеряют разные вещи и несопоставимы напрямую. Слияние (merge) — это построение единого порядка из нескольких частичных, разнородных списков. Это классическая задача IR — слияние результатов (results merging / data fusion).
Алгоритм 1. Round-robin (чередование)Интуиция. Три эксперта оценили кандидатов по своим критериям и по своим шкалам (один по 100-балльной, другой по буквам, третий — рангами без баллов). Чтобы получить общий список, нужно либо привести оценки к общей шкале, либо забыть про баллы и работать с рангами. Оба пути — это разные алгоритмы слияния.
Берём по одному элементу из каждого источника по кругу: 1-й от A, 1-й от B, 1-й от C, 2-й от A, … Игнорирует скоры полностью, опирается только на ранг внутри источника.
- Плюс: прост, не требует сопоставимости скоров, гарантирует присутствие каждого источника.
- Минус: игнорирует уверенность — слабый результат от слабого источника встаёт вровень с сильным от сильного. Хорош как baseline и как способ «гарантировать разнообразие источников», но не как качественный мерж.
Приводим скоры всех источников к общей шкале, затем сортируем по нормализованному скору. Нормализация (по источнику):
Код: Выделить всё
score_norm = (score - min_s) / (max_s - min_s) # min-max в [0,1]
score_z = (score - μ_s) / σ_s # z-нормализация
Код: Выделить всё
final_score(d) = w_s · score_norm(d, s)
Алгоритм 3. Ранговые методы (rank fusion)Внимание. Min-max и z-нормализация лечат разницу масштабов, но не разницу смысла. Если источник A систематически выдаёт по запросу слабые результаты, нормализация всё равно растянет их в [0,1] и «подравняет» к сильным результатам источника B. Поэтому одной нормализации мало — нужна калибровка: приведение скоров к общей интерпретируемой величине (например, к оценочной вероятности релевантности). Калиброванный скор сопоставим между источниками по смыслу, а не только по масштабу.
Работают с рангами, а не со скорами — устойчивы к несопоставимости шкал.
- Borda count. Элементу на позиции r из списка длины L начисляют L − r баллов; баллы по всем источникам, где элемент встретился, суммируют.
- Reciprocal Rank Fusion (RRF). Каждый источник даёт элементу вклад 1 / (k₀ + rᵢ), где rᵢ — ранг в источнике, k₀ — сглаживающая константа (часто ≈ 60):
Код: Выделить всё
RRF(d) = Σ_s 1 / (k₀ + rank_s(d))
Интуиция RRF. Верхние позиции ценны нелинейно: разница между 1-й и 2-й позицией куда важнее, чем между 50-й и 51-й. 1/(k₀+r) отражает это — и притом не требует ни скоров, ни их нормализации. RRF любят за устойчивость и простоту: ему достаточно порядка.
Алгоритм 4. Обучаемое слияние (learned merge)Пример. Документ d стоит 1-м в источнике A и 3-м в источнике B, k₀=60. Его RRF = 1/61 + 1/63 ≈ 0.0164 + 0.0159 = 0.0323. Документ e стоит 2-м только в A: RRF = 1/62 ≈ 0.0161. d обгоняет e за счёт присутствия в двух источниках высоко.
Финальное слияние можно сформулировать как задачу обучения ранжированию (learning-to-rank, Модуль 9), где признаки — это позиция и скор элемента в каждом источнике, идентичность источника, признаки запроса, флаги полноты ответов. Модель учится на разметке/кликах оптимальному порядку. Это самый мощный, но и самый дорогой подход; на верхнем узле он часто и реализует блендинг вертикалей (глава 14.3) — слияние и федерация смыкаются в одной модели.
Код: Выделить всё
Алгоритм | Нужны сопоставимые скоры? | Учитывает уверенность? | Сложность | Когда применять
----------------------+-----------------------------+--------------------------+---------------+----------------------------------------------
Round-robin | нет | нет | тривиальная | baseline, гарантия присутствия источника
Нормализованный скор | да (после калибровки) | да | низкая | однородные источники, есть калибровка
Borda / RRF | нет | частично (через ранг) | низкая | разнородные источники, нет калибровки
Обучаемое слияние | как признаки | да | высокая | продакшн-блендинг, много источников/сигналов
Что именно узел передаёт вверх по дереву — это протокол слияния (merge protocol). Минимальный полезный пакет частичного ответа:
Код: Выделить всё
PartialResponse {
source_id // кто ответил (web / images / news …)
results: [ {doc_id, score, rank, snippet_ref, features…} ] // top-k
complete: bool // успел ли источник обойти все свои шарды
shards_answered: int // сколько шардов из total ответило
shards_total: int
latency_ms: int
cost_hint: float // во сколько обошёлся ответ (для бюджетирования)
}
Инженерная заметка. Флаг complete и пара shards_answered/shards_total — не косметика, а вход для алгоритма слияния. Источник, ответивший «я успел обойти лишь 60% своих шардов», менее надёжен: его top-k неполон, и слияние должно дисконтировать его вес или пометить выдачу как «частичную». Передавать наверх калиброванные скоры (а не сырые) сильно упрощает мерж: верхний узел не обязан знать внутренние шкалы источников.
Отказоустойчивость слиянияИнженерная заметка. Объём передаваемого top-k на каждом уровне — параметр настройки. Снизу (от шардов) обычно поднимают больше кандидатов, чем нужно финально (over-fetch), чтобы слияние имело запас; вверх к корню — меньше. Глубина запроса к источнику (k) связана с предельной глубиной SERP (глава 14.4): нет смысла тянуть 1000 кандидатов, если покажем 10 на странице плюс запас на дедупликацию и пагинацию.
Распределённая система частично выходит из строя постоянно: шарды тормозят, узлы падают, сети моргают. Слияние обязано быть устойчивым.
- Тайм-ауты и дедлайны. Каждый узел ждёт детей лишь до дедлайна (глава 14.1). Опоздавшие отменяются; их вклад теряется.
- Частичные ответы (partial results). Лучше показать выдачу из ответивших источников, чем не показать ничего. Слияние работает с тем, что пришло, и помечает результат флагом полноты.
- Graceful degradation (плавная деградация). При выпадении источника система не падает, а деградирует предсказуемо: пропадает вертикаль картинок — SERP остаётся без блока картинок, но веб-результаты на месте. Деградация по приоритету: дешевле потерять «новости», чем основной веб-источник.
- Тайм-аут как сигнал, а не ошибка. Истёкший дедлайн источника — это не исключение, а штатное событие: слияние просто продолжает без него.
- Согласованность дедлайнов по дереву. Дочерний дедлайн должен быть строго меньше родительского с запасом на сетевую дорогу обратно, иначе ответ ребёнка прилетит, когда родитель уже ответил без него — потраченная зря работа.
Внимание. Тонкая ловушка — недетерминизм выдачи из-за тайм-аутов. Если на грани дедлайна источник то успевает, то нет, одна и та же выдача «мерцает» между запросами. Это бьёт по воспроизводимости A/B-экспериментов (Модуль 19) и по кэшированию. Лечится стабилизацией дедлайнов, кэшированием на уровне источника и пометкой «частичности» ответа.
Пример (деградация). Бюджет 300 мс. Веб-источник ответил за 180 мс (полно), картинки — за 120 мс (полно), новости — не уложились (>250 мс). Верхний узел собирает SERP из веба и картинок, блок новостей не показывает, выставляет complete=false для подсистемы новостей. Пользователь получает полезную страницу за бюджет; отсутствие новостного блока заметно лишь там, где он был бы уместен.
Частые заблужденияSEO-врезка. Из-за частичности и тайм-аутов состав SERP по одному и тому же запросу не детерминирован на коротком горизонте: блок вертикали может то появляться, то нет в зависимости от того, успел ли источник. Поэтому однократное наблюдение «у меня нет блока картинок» ничего не доказывает — нужны повторные замеры. Стабильность позиции оценивайте по агрегату, а не по одному снимку выдачи.
Заблуждение. «Чтобы слить источники, достаточно отсортировать всё по скору.» Скоры разных источников несопоставимы по смыслу. Без калибровки сортировка по сырому скору систематически перекашивает выдачу в пользу источника с более «щедрой» шкалой.
Заблуждение. «Если источник не ответил — надо вернуть ошибку.» Нет. Штатная реакция — частичный ответ и плавная деградация. Полный отказ выдачи из-за одного медленного источника недопустим.
Лаба / практикаЗаблуждение. «RRF хуже слияния по скору, ведь он игнорирует баллы.» Наоборот, на разнородных источниках без надёжной калибровки RRF часто устойчивее: ему не нужны сопоставимые скоры, и он не ломается от перекоса шкал.
Цель: сравнить алгоритмы слияния и проверить устойчивость к выпадению источника.
Вход: 3 источника, каждый возвращает ранжированный список (doc_id, score) по тестовому запросу; есть «золотая» разметка релевантности для оценки nDCG (см. Модуль 19). У источников разные шкалы скоров (например, A в [0,1], B в [0,100], C — только ранги).
Шаги:
- Реализуйте слияние: (а) round-robin, (б) по min-max-нормализованному скору, (в) RRF (k₀=60).
- Посчитайте nDCG@10 для каждого метода на наборе запросов.
- Введите вес доверия источнику и повторите для нормализованного скора.
- Тест отказа: уроните источник B (вернул пустой/частичный ответ, complete=false). Повторите слияние всеми методами, измерьте падение nDCG и проверьте, что выдача не пустая.
Критерий «сделано»: три метода реализованы и сравнены численно; продемонстрирована graceful degradation при отказе источника; объяснено, почему сырой скор без нормализации даёт перекос. Время ~75 мин.
Контрольные вопросы
- Почему нельзя слить источники простой сортировкой по сырому скору? Чем нормализация отличается от калибровки?
- Запишите формулу RRF и объясните роль константы k₀. Почему RRF устойчив к несопоставимости шкал?
- Что входит в PartialResponse? Зачем нужны complete и shards_answered/shards_total?
- Перечислите четыре приёма отказоустойчивого слияния. Что такое graceful degradation?
- Почему дочерний дедлайн должен быть строго меньше родительского?
- Как тайм-ауты порождают недетерминизм выдачи и чем он вредит экспериментам (Модуль 19)?
- В каких условиях обучаемое слияние оправдывает свою стоимость по сравнению с RRF?
- Источник вернул complete=false, shards_answered=60/100. Как это должно повлиять на его вес в слиянии?
Цели обучения
После главы студент сможет:
- Различать слияние однородных источников (глава 14.2) и федерацию вертикалей — разнородных блоков (картинки, видео, новости, карточки) на одной странице.
- Объяснить триггер вертикали через предсказание интента запроса (query intent prediction).
- Описать блендинг (blending): выбор, какую вертикаль показать и в какую позицию SERP вставить блок.
- Сформулировать выбор позиции как задачу ранжирования с разнородными элементами и оценить компромисс «полезность блока vs вытеснение органики».
- Связать федерацию вертикалей с группировкой/разнообразием (Модуль 15) и антиспамом (Модуль 16).
Вертикаль — это не просто ещё один источник
В главе 14.2 мы сливали однородные списки документов в один ранжированный список. Федерация вертикалей — задача другого рода. Вертикаль (vertical) — это специализированный источник со своим типом контента и своей формой представления: блок картинок (сетка/карусель), видео-карусель, новостной блок (заголовки с датами), товарные карточки (цена, рейтинг, фото), карта с точками, блок-ответ (factbox), локальный бизнес. Вставить их в общий список «по скору» нельзя: карусель картинок и текстовая ссылка несопоставимы, и сама их уместность зависит от запроса.
Предсказание интента: триггер вертикалиИнтуиция. На запрос «как выглядит сатурн» блок картинок уместен и должен стоять высоко. На запрос «закон о защите прав потребителей» картинки скорее мешают, а уместны текстовые источники и, может, factbox. Федерация — это решение «стоит ли вообще показывать вертикаль и, если да, куда её поставить», а не «как отсортировать однотипное».
Решение начинается с предсказания интента запроса (query intent prediction) — оценки вероятности, что пользователю по этому запросу нужен данный тип контента:
Код: Выделить всё
P(хочет_картинки | запрос), P(хочет_видео | запрос), P(хочет_новости | запрос), …
- Лексические: слова-маркеры («фото», «видео», «купить», «цена», «рецепт», «новости», «рядом»).
- Сущностные: класс сущности из понимания запроса (Модуль 5) — персона/фильм/товар/место.
- Поведенческие: на похожих запросах пользователи кликали в блок картинок/видео (клик-модели, Модуль 11) — сильнейший сигнал.
- Свежестные: всплеск запроса + свежие документы → вероятен новостной интент (связь с Модулем 17).
- Геосигналы: «рядом», локальная сущность → карта/локальный блок (Модуль 18).
Пример. Запрос «смартфон модели X2». Интент-классификатор видит товарную сущность + коммерческие клики на похожих запросах → высокая P(хочеткарточки), умеренная P(хочеткартинки) и P(хочетвидео) (обзоры), низкая P(хочетновости) вне периода анонса. Триггерятся товарный и картиночный блоки; новостной — нет (если нет всплеска).
Блендинг: что показать и куда вставитьЗаблуждение. «Вертикаль показывается, если у источника есть результат.» Нет: у источника картинок почти всегда что-то найдётся. Решающее — не наличие результата, а предсказанный интент и оценочная полезность блока для этого запроса.
Блендинг (blending) — это финальный шаг на верхнем узле: вплести (или не вплести) блоки вертикалей в список органических результатов и выбрать позицию вставки каждого блока. Решается одновременно «да/нет» (показывать ли блок) и «где» (на какую позицию).
Концептуально блендинг — это ранжирование разнородных элементов: кандидаты — это и органические документы, и блоки-вертикали целиком. Каждому кандидату нужна сопоставимая оценка ценности на позиции. Современный подход — обучаемая модель (learning-to-rank, Модуль 9), предсказывающая «whole-page»-полезность: ожидаемую пользу всей страницы при данной раскладке блоков.
Упрощённая модель оценки места блока на позиции p:
Код: Выделить всё
gain(блок, p) ≈ P(интент) · ожидаемая_польза(блок | клик)
cost(блок, p) ≈ вытеснение_органики(p) · ценность_вытесненного
вставить блок на лучшую позицию p*, где gain − cost максимален и > порога
Типичные позиции вставки (slots):Интуиция (вытеснение). Блок наверху сдвигает органику вниз. Если блок очень полезен (пользователь явно хочет картинки) — выгода перекрывает вытеснение, блок ставят высоко. Если польза сомнительна — блок либо ниже, либо не показывается вовсе. Это всегда компромисс между полезностью блока и вытеснением органических результатов.
- Топ-позиция (top slot) — блок над всей органикой (factbox, очень релевантная карусель). Сильнейшее вытеснение — порог полезности высокий.
- Промежуточные позиции (inline slots) — блок вплетён между органическими результатами (например, после 3-го). Умеренное вытеснение.
- Боковая панель (side panel) — карточка сущности сбоку, почти без вытеснения основной колонны.
Инженерная заметка. Блендинг и финальное слияние (14.2) на верхнем узле часто сливаются в одну обучаемую модель: признаки органических результатов, признаки блоков, скоры интента, позиция — всё подаётся в whole-page-ранкер, который выдаёт финальную раскладку. Разделение «сначала мерж органики, потом вставка блоков» — упрощение для понимания; в проде граница размыта.
Калибровка между разнородными типамиВнимание (взаимовлияние блоков). Полезность блока зависит от соседей: два визуальных блока подряд избыточны; новостной блок рядом с factbox о том же событии дублирует смысл. Поэтому whole-page-оптимизация смотрит на страницу целиком, а не на каждый блок изолированно. Это прямой мост к Модулю 15: разнообразие и борьба с дублированием касаются не только органики, но и набора вертикалей.
Чтобы сравнивать «ценность картинки» и «ценность текстовой ссылки» на одной позиции, нужна общая шкала полезности. Её дают:
- единая целевая метрика (например, ожидаемая удовлетворённость / клик с длинным дочиткой — Модуль 11);
- оценочные сессии и разметка «помог ли блок» (Модуль 19);
- онлайн-эксперименты на позицию и форму блока (Модуль 19).
SEO-врезка. Чтобы контент попал в вертикаль, он должен (1) индексироваться соответствующим источником (правильный тип/разметка: подписи и alt у картинок, разметка видео, структурированные данные у карточек), и (2) запрос должен триггерить эту вертикаль по интенту. Можно идеально оптимизировать карточку товара, но если запрос не коммерческий — товарный блок не появится. И обратно: появление блока вертикали наверху вытесняет органику вниз — поэтому при коммерческих/визуальных интентах борьба идёт не только за топ органики, но и за попадание в сам блок вертикали. Манипуляции с разметкой ради ложного триггера ловятся антиспамом (Модуль 16).
Частые заблужденияSEO-врезка. Триггер вертикали — запросо-зависимый. Один и тот же сайт по запросу A видит товарный блок над собой, по запросу B — нет. Анализируйте SERP-«фичи» по классам запросов, а не вообще: что триггерит блок и какова доля кликов, уходящая в блок, а не в органику.
Заблуждение. «Федерация — это то же слияние, что в 14.2.» Нет. Слияние строит порядок из однородных списков. Федерация решает про разнородные блоки: показывать ли вертикаль и в какую позицию вставить — с учётом интента и вытеснения органики.
Заблуждение. «Если вертикаль доступна, её надо показать.» Показ определяется предсказанным интентом и оценочной полезностью минус вытеснение, а не самим наличием результата.
Лаба / практикаЗаблуждение. «Блоки можно оценивать независимо друг от друга.» Полезность блока зависит от соседей и от страницы целиком; два похожих блока подряд избыточны. Нужна whole-page-оптимизация.
Цель: построить простой блендер вертикалей с предсказанием интента и выбором позиции.
Вход: набор запросов с разметкой «уместная вертикаль» (картинки/видео/новости/карточки/нет); для каждого запроса — органический top-10 и кандидат-блок каждой вертикали с оценкой P(интент).
Шаги:
- Триггер: показывать блок вертикали v, только если P(интент_v) > порог_v.
- Позиция: для сработавших блоков оцените gain − cost на позициях {top, после 3-го, side} по упрощённой модели; выберите лучшую (или «не показывать», если максимум ниже порога).
- Соберите финальную раскладку SERP (органика + вплетённые блоки).
- Оцените раскладки против разметки: доля запросов, где уместная вертикаль показана на разумной позиции; доля ложных срабатываний (показан ненужный блок).
- Подвигайте пороги и постройте кривую «полнота вертикали vs ложные срабатывания».
Критерий «сделано»: на визуальном запросе блок картинок встаёт высоко, на сугубо текстовом — не показывается; продемонстрирован компромисс «полезность блока vs вытеснение» и зависимость от порога интента. Время ~90 мин.
Контрольные вопросы
- Чем федерация вертикалей отличается от слияния однородных источников (14.2)?
- Что такое предсказание интента запроса и какие сигналы в него входят? Какой сигнал сильнейший и почему?
- Почему наличия результата у источника недостаточно для показа вертикали?
- Запишите упрощённую модель «вставить блок на позицию p». Что такое вытеснение органики?
- Перечислите типы позиций вставки и порядок их «агрессивности» по вытеснению.
- Почему блоки нельзя оценивать изолированно? Что такое whole-page-оптимизация и как это связано с Модулем 15?
- Что нужно, чтобы контент попал в вертикаль, и почему оптимизация бессмысленна без триггера интента (взгляд SEO)?
- Как федерация смыкается с финальным слиянием в единую обучаемую модель?
Цели обучения
После главы студент сможет:
- Объяснить, почему система не ранжирует «весь индекс», а ограничивает глубину top-k на каждом уровне.
- Связать размер запрашиваемого top-k с пагинацией, дедупликацией и предельной глубиной выдачи.
- Описать дедупликацию и схлопывание на стыке источников при сборке страницы.
- Спроектировать финальную сборку SERP: порядок шагов от частичных ответов до отрисованной страницы.
- Оценить компромисс «глубина выдачи vs стоимость/задержка».
Почему не «весь индекс»: глубина top-k
Поисковая система никогда не выстраивает полный порядок по всему индексу. На каждом уровне дерева запрашивается лишь top-k — несколько десятков/сотен лучших кандидатов. Причины:
- Стоимость. Полный порядок по миллиардам документов нереализуем в бюджете задержки (глава 14.1).
- Бесполезность хвоста. Пользователь почти никогда не уходит дальше первых страниц; точный порядок документа на позиции 10 000 никому не нужен.
- Каскад уже отсёк лишнее. L0 (отбор кандидатов) из Модуля 12 свёл миллиарды к тысячам до дорогого ранжирования.
Глубины top-k уменьшаются к корню («воронка»):Интуиция. Незачем идеально сортировать всю библиотеку, если читателю нужны 10 книг с верхней полки. Сортируем хорошо лишь верхушку, а до хвоста доходим, только если пользователь явно листает дальше.
Код: Выделить всё
шард (leaf): top-1000 ──┐
├─ merge на среднем узле → top-200 источника
шард (leaf): top-1000 ──┘
средний узел: top-200 ──┐
├─ merge+blend на верхнем узле → top-50
средний узел: top-200 ──┘
верхний узел: top-50 → пагинация → страница 1: позиции 1–10
Предельная глубина выдачиИнженерная заметка. Финальный запас (top-50 при показе 10) нужен под три вещи: (1) дедупликацию на стыке источников (часть кандидатов схлопнется), (2) блендинг вертикалей (часть позиций займут блоки), (3) пагинацию на пару страниц вперёд без повторного дорогого ранжирования. Тянуть глубже — лишняя стоимость и задержка без пользы.
Существует предельная глубина выдачи (result depth cap) — максимальная позиция, до которой система вообще готова отдать результаты (например, несколько сотен). Дальше:
- ранжирование на больших глубинах ненадёжно (сигналов мало, разреженность);
- стоимость хранения и пагинации хвоста не оправдана почти нулевым спросом;
- глубокий хвост уязвим к спаму (мало конкуренции — легче «всплыть»), что важно для Модуля 16.
Дедупликация на стыке источниковЗаблуждение. «Если документ есть в индексе, его можно долистать до любой позиции.» Нет. Существует предельная глубина: за ней система просто не отдаёт результатов, даже если документы формально проиндексированы.
Один и тот же материал может прийти от разных источников: статья — из веба и из новостей; страница товара — из веба и из карточек. На сборке нужна межисточниковая дедупликация (cross-source dedup): схлопнуть дубликаты, оставив представление в наиболее уместной форме/вертикали.
Финальная сборка SERPВнимание. Это не та же дедупликация, что внутри одного индекса (каноникализация, Модуль 3, near-duplicate detection). Здесь схлопываются записи, пришедшие из разных подсистем, по идентификатору документа/сущности или по контентному отпечатку. Сложность — выбрать, в какой форме показать схлопнутую сущность (текстовая ссылка или карточка), и не показать её дважды в разных блоках. Группировка и схлопывание по хосту/теме — отдельная большая тема Модуля 15; здесь речь именно про стык источников.
Порядок шагов на верхнем узле от частичных ответов до страницы:
Код: Выделить всё
1. Собрать частичные ответы источников (gather, 14.1), с флагами полноты.
2. Слить органические источники в единый ранжированный список (merge, 14.2).
3. Триггер вертикалей по интенту + блендинг блоков с выбором позиций (14.3).
4. Межисточниковая дедупликация и схлопывание (стык источников).
5. Применить постранжирование/антиспам-фильтры (Модуль 16) — снять/понизить нежелательное.
6. Применить ограничения разнообразия/группировки (Модуль 15) — например, лимит на хост.
7. Обрезать до размера страницы (пагинация: позиции 1–10), оставив запас на пагинацию.
8. Обогатить представление: сниппеты, заголовки, превью, разметка блоков.
9. Отдать SERP + метаданные (флаги полноты, диагностика) на рендеринг.
Инженерная заметка. Порядок шагов важен: антиспам и лимиты разнообразия применяются до финальной обрезки до 10 позиций — иначе удалённый спам-результат оставит «дыру» в выдаче или лимит на хост сработает уже после показа. Поэтому обрезка до размера страницы — почти последний шаг, а не первый.
Пример (пагинация). Верхний узел держит финальные top-50 после слияния, блендинга, дедупликации и фильтров. Страница 1 — позиции 1–10, страница 2 — 11–20 и т.д. Пока пользователь в пределах 50, дорогое ранжирование не повторяется — листание дёшево. Запросил позицию 60 (за пределами буфера) — система либо до-ранжирует следующий пласт, либо упирается в предельную глубину и не отдаёт результатов.
Частые заблужденияSEO-врезка. Размер первой страницы и предельная глубина задают «экономику видимости». Почти весь трафик собирают позиции 1–10 первой страницы; блоки вертикалей и factbox дополнительно вытесняют органику вниз и могут давать ответ прямо на SERP без перехода (zero-click). Реальная «полезная глубина» для трафика — единицы позиций, а не «есть ли я в индексе на 200-й».
Заблуждение. «Система ранжирует весь индекс и берёт лучшие 10.» Полный порядок не строится никогда. Каскад отсекает кандидатов, дальше работают только с top-k, который сужается к корню.
Заблуждение. «Дедупликация уже сделана на индексации — на сборке она не нужна.» Каноникализация (Модуль 3) убирает дубли внутри источника. На сборке нужен ещё один проход — между источниками, иначе один материал придёт и текстовой ссылкой, и карточкой.
Лаба / практикаЗаблуждение. «Обрезать до 10 надо сразу, чтобы не тащить лишнее.» Обрезка — почти последний шаг: антиспам и лимиты разнообразия должны отработать раньше, иначе в выдаче появятся «дыры» и нарушенные лимиты.
Цель: реализовать финальную сборку SERP из частичных ответов с правильным порядком шагов.
Вход: частичные ответы 3 органических источников (top-k со скорами и флагами полноты) + 2 блока-вертикали с P(интент); список «спам»-doc_id (для антиспам-шага); лимит «не более 2 результатов с одного хоста» (разнообразие).
Шаги:
- Слейте органику (RRF из 14.2) в единый список.
- Вплетите сработавшие вертикали на выбранные позиции (упрощённый блендер из 14.3).
- Сделайте межисточниковую дедупликацию по doc_id/отпечатку.
- Примените антиспам-фильтр и лимит на хост — до обрезки.
- Обрежьте до страницы (позиции 1–10), оставив буфер до 30 для пагинации.
- Соберите финальную структуру SERP с метаданными полноты.
Критерий «сделано»: шаги идут в правильном порядке (фильтры — до обрезки); страница 1 содержит ровно 10 элементов без «дыр»; пагинация на страницу 2 работает из буфера; продемонстрировано влияние предельной глубины. Время ~75 мин.
Контрольные вопросы
- Почему система никогда не строит полный порядок по всему индексу? Назовите три причины.
- Зачем держать финальный запас (top-50 при показе 10)? Перечислите три применения.
- Что такое предельная глубина выдачи и почему она существует (включая связь с антиспамом)?
- Чем межисточниковая дедупликация отличается от каноникализации (Модуль 3)?
- Запишите порядок шагов финальной сборки SERP. Почему обрезка до 10 — почти последний шаг?
- Почему антиспам и лимиты разнообразия применяются до обрезки, а не после?
- Как буфер top-k обеспечивает дешёвую пагинацию и где он упирается в предельную глубину?
- Что такое zero-click и как блоки вертикалей меняют «полезную глубину» для трафика (взгляд SEO)?
- Поисковая система — это федерация источников, а не один индекс. Каждый источник (веб, картинки, видео, новости, карточки) — самостоятельная мини-ПС со своим индексом, ранжированием и темпом обновления. Ответ собирается из частей.
- Метапоиск устроен трёхуровнево: верхний узел (root/mixer) маршрутизирует, федерирует и собирает SERP; средний узел (агрегатор источника) сливает шарды и ранжирует источник; базовый узел (leaf) держит один шард индекса.
- Scatter-gather — базовый паттерн: запрос рассылается всем детям параллельно, ответы собираются до дедлайна. Задержка узла ≈ задержка самого медленного ребёнка — отсюда борьба с хвостовой задержкой, а не с медианой.
- Слияние строит единый порядок из разнородных списков. Сырые скоры источников несопоставимы: нужны нормализация и калибровка. Ранговые методы (RRF, Borda) устойчивы без калибровки; обучаемое слияние сильнее, но дороже.
- Протокол обмена передаёт вверх top-k со скорами, флагами полноты (complete, shards_answered/total) и стоимостью — это вход для слияния и для решения о деградации.
- Отказоустойчивость слияния: тайм-ауты как штатное событие, частичные ответы, graceful degradation по приоритету источников. Цена — недетерминизм выдачи на грани дедлайнов.
- Федерация вертикалей ≠ слияние однородного. Решает «показывать ли блок» (по предсказанному интенту) и «в какую позицию вставить» (блендинг) с учётом компромисса «полезность блока vs вытеснение органики». Оптимизация — на уровне всей страницы (whole-page), а не блока.
- Финальная сборка SERP работает с top-k, который сужается к корню (воронка), не строя полного порядка. Порядок шагов: слияние → блендинг → межисточниковая дедупликация → антиспам/разнообразие → обрезка до страницы. Фильтры — до обрезки. Есть предельная глубина выдачи; почти весь трафик — на первых позициях первой страницы.
- Метапоиск (metasearch) — опрос множества источников и слияние их частичных ответов в единую выдачу (в инженерном смысле — опрос собственных источников организации).
- Федерация (federation) — политика выбора, какие вертикали и в какой форме/позиции показать на странице.
- Источник (source / backend) — самостоятельная мини-ПС со своим индексом и ранжированием (веб, картинки, видео, новости, карточки…).
- Вертикаль (vertical) — специализированный источник со своим типом контента и формой представления (блок картинок, видео-карусель, новостной блок, товарные карточки, карта, factbox).
- Верхний узел (root / mixer / blender) — точка входа запроса; федерация, финальное слияние и сборка SERP; индекса не хранит.
- Средний узел (intermediate / source aggregator) — агрегатор одного источника: слияние его шардов и ранжирование.
- Базовый узел (leaf / index server) — держит один шард индекса; возвращает локальный top-k.
- Scatter-gather (fan-out/fan-in) — параллельная рассылка запроса детям и сбор их ответов до дедлайна.
- Бюджет задержки (latency budget) — допустимое время ответа, делящееся по дереву фан-аута.
- Хвостовая задержка (tail latency) — высокие перцентили задержки; растёт с числом узлов из-за «отстающих».
- Слияние результатов (results merging / data fusion) — построение единого порядка из нескольких частичных списков.
- Round-robin — слияние чередованием по рангу, без учёта скоров.
- Нормализация скоров (min-max / z) — приведение скоров источника к общему масштабу.
- Калибровка скоров — приведение скоров к общей интерпретируемой величине (напр. вероятности релевантности), сопоставимой по смыслу между источниками.
- Reciprocal Rank Fusion (RRF) — ранговое слияние с вкладом 1/(k₀+rank); устойчиво к несопоставимости шкал.
- Borda count — ранговое слияние суммированием баллов L−rank.
- Обучаемое слияние (learned merge) — слияние как задача learning-to-rank; часто совмещено с блендингом.
- Протокол слияния (merge protocol) — формат частичного ответа: top-k, скоры, флаги полноты, стоимость.
- Частичный ответ (partial results) — выдача из ответивших источников при выпадении части источников.
- Плавная деградация (graceful degradation) — предсказуемое снижение полноты выдачи при отказе источника без полного сбоя.
- Предсказание интента запроса (query intent prediction) — оценка вероятности нужды пользователя в данном типе контента; триггер вертикали.
- Блендинг (blending) — выбор, какие блоки вертикалей показать и в какую позицию SERP вставить.
- Вытеснение органики — сдвиг органических результатов вниз из-за вставки блока вертикали.
- Позиция вставки (slot) — место блока на SERP: топ-позиция, промежуточная (inline), боковая панель.
- Whole-page-оптимизация — оценка полезности всей страницы целиком, а не блоков по отдельности.
- Top-k / глубина выдачи — число лучших кандидатов, запрашиваемое на уровне; сужается к корню.
- Предельная глубина выдачи (result depth cap) — максимальная позиция, до которой система отдаёт результаты.
- Межисточниковая дедупликация (cross-source dedup) — схлопывание одного материала, пришедшего от разных источников.
- SERP (search engine results page) — финально собранная страница выдачи.
- Zero-click — получение ответа прямо на SERP (factbox/блок) без перехода на источник.
- Опирается на Модуль 12 (каскад ранжирования и serving) — каждый источник внутри себя реализует каскад L0–L3; верхний/средний узел оперируют его top-k. Глубина top-k и over-fetch — продолжение serving-логики каскада.
- Опирается на Модуль 13 (инфраструктура и распределённое обслуживание) — дерево фан-аута, шардирование по документам, тайм-ауты, hedged requests, борьба с хвостовой задержкой берутся оттуда.
- Использует Модуль 5 (понимание запроса) — предсказание интента и класса сущности — вход федерации вертикалей.
- Использует Модули 9 и 11 (LTR и поведенческие сигналы) — обучаемое слияние и whole-page-блендинг как задача ранжирования; клики по блокам вертикалей — сильнейший сигнал интента.
- Питает Модуль 15 (группировка, схлопывание, разнообразие) — финальная сборка применяет лимиты разнообразия и группировку; whole-page-избыточность блоков — прямой мост к разнообразию выдачи. Межисточниковая дедупликация здесь — частный случай схлопывания.
- Питает Модуль 16 (постранжирование и антиспам) — антиспам-фильтры применяются на финальной сборке до обрезки страницы; предельная глубина и ложные триггеры вертикалей — поверхности для злоупотреблений, которые ловит антиспам.
- Связан с Модулями 17 и 18 (свежесть, гео/персонализация) — свежестный и геоинтент триггерят новостные и локальные вертикали соответственно.
- Питает Модуль 19 (измерение и эксперименты) — недетерминизм из-за тайм-аутов и whole-page-метрики полезности измеряются и оптимизируются там.
- Классические работы по слиянию результатов и data fusion в IR (CombSUM/CombMNZ, ранговые методы, Reciprocal Rank Fusion).
- Литература по калибровке и нормализации скоров при объединении разнородных систем поиска.
- Обзоры по федеративному и распределённому поиску (federated/distributed IR): выбор источников (source selection), слияние результатов, оценка покрытия.
- Работы по агрегированному поиску (aggregated search) и вертикальному отбору (vertical selection / presentation): предсказание интента, блендинг, выбор позиции блока.
- Инженерные обзоры архитектур обслуживания запросов: scatter-gather, деревья фан-аута, борьба с хвостовой задержкой, плавная деградация (см. также Модуль 13).
- Материалы по whole-page-оптимизации и оценке полезности страницы выдачи (связь с Модулями 15 и 19).