Я гоняю один и тот же пул из 60 промптов через шесть ИИ-движков раз в месяц — это часть мониторинга, который я веду для одной ниши. В какой-то момент решил не просто читать ответы, а посчитать, откуда движки берут источники. Ожидал разную выдачу по одной теме — обычное дело для поисковиков. Получил другое: два практически несвязанных множества доменов. Из 174 уникальных источников за последний срез общих оказалось 7.
Расскажу, как я это посчитал, почему дело не в шуме прогона, а в архитектуре, и почему первый вывод оказался не там, где я его искал.
Что за данные и почему их вообще можно сравнивать
Логи мониторинга — это JSON-файлы, по одному на пару (движок, промпт, прогон). У каждого есть поле sources_cited — список объектов с domain и url, если движок в принципе показывает источники. Три временных среза с разницей примерно в месяц: майский, июньский, июльский — один и тот же набор из 60 промптов, одна ниша (контент-производство), русскоязычные запросы.
Первое, что выяснилось при попытке собрать пулы источников по всем пяти движкам, — три из пяти не дают материала для анализа:
-
Perplexity (
sonar-pro) в ответе оставляет только числовые сноски вида[6][8], сами URL приходят отдельным полем API, которое мой прокси не пробрасывает в лог. В майском срезе изредка проскакивали (19 доменов), но это флаки, не система — для честного сравнения массив недостаточен. -
Gemini (
gemini-2.5-flash) и Claude (claude-sonnet-4.5) в этой связке работают без веб-ретрива — генеративка отвечает по весам модели, источников не цитирует по дизайну. Ноль здесь — не баг парсера, а ожидаемое поведение.
Остаются два движка, которые реально отдают машиночитаемый список источников: ChatGPT-search (gpt-4o-search-preview, ссылки идут inline в тексте вида domain) и Яндекс-нейропоиск (search-api-gen, отдаёт чистый список).
Дальше речь только о них. Не потому что остальные три неинтересны, а потому что у них физически нет данных для этого конкретного сравнения. Нельзя сказать, что источники не пересекаются у пяти движков, когда трое из пяти источники вообще не показывают. Честно сравнимы ровно два. Прежде чем считать пересечение, приходится проверить куда более скучную вещь — а движок вообще отдаёт то, что можно сравнивать?
Код: как я считал пересечение
Для каждого среза каждого движка собрать множество доменов, а потом посчитать коэффициент Жаккара между всеми парами множеств. Jaccard = |A∩B| / |A∪B|: единица — множества совпадают полностью, ноль — не пересекаются вообще.
"""Считает, насколько пересекаются пулы источников, которые разные ИИ-движкицитируют по одному и тому же набору запросов.Вход: логи мониторинга — по одному JSON на (движок, промпт, прогон).В каждом JSON есть поле sources_cited: [{"url": ..., "domain": ...}, ...].Выход: матрица коэффициентов Жаккара между пулами + доля уникальных доменов."""import globimport jsonimport os# (метка, путь к папке с логами движка за один срез времени)POINTS = [ ("ChatGPT-май", "logs/baseline-iter1/chatgpt"), ("ChatGPT-июн", "logs/checkpoint-0611/chatgpt"), ("ChatGPT-июл", "logs/checkpoint-0711/chatgpt"), ("Yandex-май", "logs/baseline-iter1/yandex_neuro"), ("Yandex-июн", "logs/checkpoint-0611/yandex_neuro"), ("Yandex-июл", "logs/checkpoint-0711/yandex_neuro"),]def domain_pool(folder): """Множество доменов-источников, процитированных движком за срез.""" pool = set() for path in glob.glob(os.path.join(folder, "*.json")): with open(path, encoding="utf-8") as f: rec = json.load(f) for src in rec.get("sources_cited") or []: dom = src.get("domain") if dom: pool.add(dom) return pooldef jaccard(a, b): union = a | b return len(a & b) / len(union) if union else 0.0pools = [(label, domain_pool(folder)) for label, folder in POINTS]# --- матрица Жаккара ---print("пул источников по срезам:")for label, pool in pools: print(f" {label:12s} {len(pool):4d} доменов")print("\nматрица Жаккара:")print(" " * 13 + "".join(f"{lbl[:11]:>12}" for lbl, _ in pools))for l1, p1 in pools: cells = "".join(f"{jaccard(p1, p2):12.2f}" for _, p2 in pools) print(f"{l1:>12} {cells}")# --- доля источников, уникальных для одного движка (последний срез) ---cg = dict(pools)["ChatGPT-июл"]yx = dict(pools)["Yandex-июл"]shared = cg & yxunion = cg | yxprint(f"\nобщих доменов у ChatGPT и Yandex (июль): {len(shared)} из {len(union)}")print(f"уникальны только ChatGPT: {len(cg - yx)} ({(len(cg - yx) / len(cg)):.0%} его пула)")print(f"уникальны только Yandex: {len(yx - cg)} ({(len(yx - cg) / len(yx)):.0%} его пула)")print(f"встречаются у обоих: {len(shared)} ({(len(shared) / len(union)):.0%} объединения)")
Явзял не пару «ChatGPT сейчас vs Яндекс сейчас», а шесть точек — три среза каждого движка. Это нужно, чтобы отличить структурное непересечение от банального шума. Если ChatGPT-май и ChatGPT-июль пересекаются между собой не сильно лучше, чем ChatGPT и Яндекс в один момент времени, то весь разговор про «разную природу индексов» можно закрывать — окажется, что источники просто дрейфуют случайно у всех.
Что получилось: пересечение не шум, оно структурное
Размеры пулов: ChatGPT май/июнь/июль — 47/78/79 доменов, Яндекс — 92/91/102.
Внутри блока ChatGPT (три среза друг с другом) — 0.17–0.25. Внутри блока Yandex — 0.35–0.46, заметно стабильнее во времени, чем ChatGPT. А между блоками, в любой паре срезов — 0.04–0.07. Разница в 4–9 раз между «движок сам с собой два месяца спустя» и «два движка в один момент» — это и есть ответ на вопрос из предыдущего раздела: непересечение не шум, потому что шум размазал бы значения примерно поровну. Клетки группируются по движку, а не по времени и не по теме запроса.
Меня удивила асимметрия внутри блоков. Ожидал, что оба движка будут одинаково текучими во времени. ChatGPT ощутимо более волатилен — за месяц пул обновляется почти наполовину. У Яндекса core источников устойчивее. Кажется, ChatGPT-search в этой связке чаще подмешивает свежепроиндексированные страницы (в том числе из недавно появившихся нишевых сайтов), а топ органики Яндекса по устоявшимся коммерческим запросам меняется медленнее — крупные площадки годами удерживают позиции.
Уникальность по последнему срезу: 7 доменов из 174
На июльском срезе объединение пулов — 174 домена. Пересечение — 7. Уникальны только для ChatGPT — 72 (91% его пула). Уникальны только для Yandex — 95 (93% его пула). Иначе говоря, 96% всех доменов, которые встретились хоть у одного движка, живут ровно в одном из двух.

У ChatGPT в его 91% уникального пула — плотный хвост нишевых продуктовых и инструментальных сайтов: opusdigital.ru, aizavod.io, contentmatrix.ru, aicontentforge.ru, pixorion.ai, luminai.ru, sellscard.ru, textmark.io и подобные. Заметная доля доменов на .io, .ai, .guru — типичные для стартап-инструментов и небольших продуктовых сайтов зоны, международные по формату, даже когда контент на русском.
У Яндекса в его 93% — крупные российские медиа и площадки экосистемы: vc.ru, dzen.ru, sostav.ru, dtf.ru, companies.rbc.ru, seonews.ru, tenchat.ru, etxt.ru, raketadigital.com. По сути это узнаваемый топ органической выдачи Яндекса по коммерческим и около-коммерческим запросам этой ниши.
В 7 общих источниках — крупные тематические агрегаторы и каталоги ниши, плюс habr.com. Да, эта площадка попала в пул источников у обоих движков одновременно — что логично: авторитетный технический ресурс индексируется и краулится широко, попадает в оба индекса.
Почему так: разные индексы, не разные мнения
ChatGPT-search в этой связке ходит в собственный веб-индекс — по механике похоже на связку с Bing-подобным индексом плюс собственный краулинг OpenAI. Индекс заточен под покрытие широкого веба, включая небольшие и свежие сайты. Отсюда обилие .io/.ai/.guru-доменов и продуктовых страниц, которые никогда не попадут в топ-10 обычной поисковой выдачи по коммерческому запросу, но вполне могут быть проиндексированы и процитированы как источник конкретного факта.
С Яндексом иначе. Нейропоиск строит ответ поверх топа собственной органической выдачи: сначала работает классический поисковый ранжировщик Яндекса — тот же самый, что формирует SERP для обычного пользователя, — а генеративная надстройка суммирует и цитирует то, что уже прошло отбор в топ. Отсюда и объяснение, почему в пуле Яндекса крупные, давно проиндексированные, коммерчески сильные медиа — это буквально топ-10 обычного поиска по теме, только пересказанный моделью.
Это не значит, что Яндекс и ChatGPT по-разному оценили одни и те же 174 страницы. Это два движка, у каждого из которых своя предварительная выборка кандидатов, и выборки почти не пересекаются на входе до ранжирования и генерации. И асимметрия волатильности из прошлого раздела ложится сюда же: если ChatGPT-search сегодня краулит свежую страницу, а завтра — уже нет, пул естественно скачет сильнее. Если Яндекс завязан на топ органики, который меняется медленно (SEO-позиции крупных сайтов инерционны), пул стабильнее во времени.
Что из этого следует
Термин «видимость в ИИ-поиске» без уточнения, в какой именно ЛЛМ — вообще не метрика. Попадание в источники ChatGPT-search и попадание в источники Яндекс-нейропоиска — две разные, почти не связанные задачи с пересечением на уровне 4–7%. Публикация на площадке, которая хорошо ранжируется в органике Яндекса, покрывает пул одного движка и почти не задевает пул другого. И наоборот: присутствие на нишевом продуктовом ресурсе, который любит краулить ChatGPT, ничего не гарантирует по части Яндекс-нейропоиска.
Если задача — присутствовать в источниках обоих движков одновременно, придётся закрывать оба типа площадок отдельно. Рассчитывать, что успех в одном автоматически конвертируется в успех в другом, не выходит: они физически не читают одно и то же.
Ограничения
-
Сравнимы 2 движка из 5, не пять. У Perplexity URL прячутся за числовыми сносками — прокси не пробросил это поле, поэтому у него нет данных, а не ноль пересечения; неправильно было бы делать вывод, что Perplexity «не цитирует источники» — это методологическая дыра сбора, а не наблюдение о движке. У Gemini и Claude в этой связке нет веб-ретрива вовсе, это архитектурная особенность их подключения, а не общий факт про модели.
-
Выборка — 60 промптов одной ниши (контент-производство), русскоязычные запросы. На другой тематике или в другом языке цифры сдвинутся, возможно ощутимо — я не проверял на второй нише.
-
Гранулярность — домен, не URL. Один домен может отдавать десятки разных страниц под разные промпты; это огрубляет картину в обе стороны.
-
Коэффициент Жаккара чувствителен к разнице размеров множеств — пулы 79 и 102 не равны, что немного занижает коэффициент математически. На порядок величины это не влияет, но на третьем знаке после запятой стоит делать скидку.
-
Три среза с шагом в месяц — маловато, чтобы строить долгосрочный тренд волатильности; для этого нужно больше точек во времени.
Дальше интересно повторить то же самое на другой нише и посмотреть, держится ли структура блоков — или это специфика именно этого набора запросов.
ссылка на оригинал статьи https://habr.com/ru/articles/1058190/