Для одного из клиентов собрал упоминания бренда в ответах ChatGPT. Как обычно — на 20 вопросов, но теперь тремя разными способами. Бренд без подсказки прозвучал на одном вопросе из 19 в первом съёме и ни разу в двух других. А теперь к деталям.

Что я измерял и на каких вопросах

Раньше я описывал общую схему замера видимости бренда в ответах шести нейросетей. Здесь только ChatGPT. Данные взяты из мониторинга, который я собирал для одного из клиентов: ему помогали растить видимость в ответах ИИ-моделей. ChatGPT я снял трижды за одну неделю, каждый раз новым способом и на одних и тех же 20 вопросах. Вопросы из одной ниши, продвижения бизнеса в нейросетях, от «где заказать» и «сколько стоит» до «как сделать, чтобы нейросети рекомендовали мой бренд». В 19 вопросах имени бренда клиента нет. Двадцатый брендовый и сравнительный: «бренд X или конкурент Y — кто лучше».

Каждый ответ ложится в JSON-лог: текст, признак поиска, источники, отметки об упоминании бренда и шести конкурентов, дефекты записи. Конкурентов я отобрал по ответам Алисы AI и Perplexity. Лог заполняет парсер, мой код, который вытаскивает из текста ответа ссылки и имена. От мониторинга мне нужны были доля ответов, где ИИ-модель называет бренд без подсказки, и список сайтов, на которые она опирается.

Задумано было просто. На деле мониторинг несколько раз врал не потому, что врёт ChatGPT, а потому, что врал мой парсер.

Три способа снять ответы ChatGPT на одних 20 вопросах

Один набор вопросов, три способа съёма — три разных лога
Один набор вопросов, три способа съёма — три разных лога

API — программный доступ к ИИ-моделям OpenAI: запрос отправляет код, ответ приходит в виде данных для программы. Два съёма из трёх шли через шлюз, платного посредника к API OpenAI со своим балансом. Интерфейс, то есть обычное окно чата, я снимал поштучно через сторонний сервис.

первый съём (API)

второй (интерфейс)

третий (API)

Как снят

через шлюз

окно чата, поштучно через сторонний сервис

через шлюз

ИИ-модель

gpt-4o, поиск опцией web_search_options

та, что отвечает в интерфейсе

gpt-5.5, поиск инструментом

Запросов

60 (20 × 3 повтора)

20 (по одному)

60 (20 × 3)

Ответов

60

20

43, ещё 17 запросов вернули ошибку 402

Признак поиска

не записан, поле пустое у всех 60

неизвестен, такого поля нет

поиск отработал в 33 из 43

Ответов с источниками

37 из 60

19 из 20

33 из 43

Медиана длины ответа

1 497 знаков

5 137 знаков

3 935 знаков

Цена ответа

25–36 ₽ через шлюз

10 ₽

25–36 ₽ через шлюз

Ошибка 402 в третьем съёме значит, что у шлюза кончились деньги на балансе. Баланс иссяк посреди прогона, все 17 ошибок пришлись на третий повтор, и он потерян целиком.

Поиск опцией и поиск инструментом — два способа подключить ИИ-модели веб-поиск через API. У gpt-4o поиск включался параметром запроса web_search_options: ИИ-модель сама решает, искать ли, а по ответу не видно, искала ли она. Отсюда пустое поле признака у всех 60 записей первого съёма, хотя источники есть в 37 ответах. У этих источников нет и пометки, пришли они от API или вытащены из текста ответа, так что ответ из поиска по такому логу не отличить от ответа по памяти даже там, где ссылки есть. У gpt-5.5 поиск подключён инструментом: вызов поиска и ссылки-аннотации приходят отдельными полями ответа, и их можно проверить. Как это устроено, описано в документации OpenAI о веб-поиске в API.

ИИ-модели в двух API-съёмах разные, gpt-4o в первом и gpt-5.5 в третьем. Эти два столбца я считаю порознь и динамику между ними не ищу. Медианы 1 497 и 3 935 знаков по двум точкам тоже не объяснить: разницу могла дать смена ИИ-модели, смена механизма поиска или случай на небольшой выборке.

В третьем съёме поиск отработал в 33 ответах из 43. На трёх вопросах ChatGPT не вышел в поиск ни в одном повторе: «как заказать аудит видимости бренда в нейросетях», «как управлять репутацией компании в ответах ИИ» и «можно ли гарантировать попадание в ответы нейросети». Ещё у четырёх вопросов поиск отработал не во всех повторах. Всего в логе 10 записей с дефектом «поиск не отработал».

Как часто ChatGPT называет бренд без подсказки

первый съём (API)

второй (интерфейс)

третий (API)

ответов на 19 небрендовых вопросов

1 из 57

0 из 19

0 из 41

небрендовых вопросов, где бренд прозвучал хотя бы раз

1 из 19

0 из 19

0 из 19

ответов на брендовый вопрос

3 из 3

1 из 1

2 из 2

Единственное небрендовое упоминание — в первом съёме: вопрос про аудит видимости, один ответ из трёх повторов. ChatGPT сослался в нём на статью бренда клиента о ручной проверке видимости. На брендовом вопросе имя звучит во всех ответах, его задаёт сам вопрос. Ответ из интерфейса на него вышел длиной 27 098 знаков, с таблицей сравнения.

Число 57 здесь означает 19 вопросов, каждый из которых снят трижды. По ответам доля выходит 1 из 57, меньше 2%. По вопросам 1 из 19: хотя бы раз бренд прозвучал на одном вопросе из девятнадцати. Данные одни и те же, а цифры отличаются в три раза.

Шесть отслеживаемых конкурентов ChatGPT назвал хоть раз только в 3 ответах из 43 третьего съёма. Других игроков на коммерческих вопросах он называет. Кого именно, по этим логам не сказать: лог отмечает только бренд и шестёрку, отобранную по ответам Алисы AI и Perplexity.

Интерфейс и API ChatGPT на одних вопросах: 17 общих доменов из 49 и 68

Интерфейс и API ссылаются на разные сайты даже на одинаковых вопросах.
Интерфейс и API ссылаются на разные сайты даже на одинаковых вопросах

После исправленного разбора ссылок-иконок второй съём, через интерфейс, дал 49 доменов-источников по всем 20 вопросам, третий, через API, дал 68. Общих доменов у них 17. Из 12 вопросов, где источники нашлись у обоих способов, в 6 не совпал ни один домен.

Одно это число ещё не доказывает, что дело в способе: источники меняются и между повторами одного способа. Поэтому я пересчитал те же логи парами ответов. Метрика — доля общих доменов в паре: общие домены делятся на все домены пары, суммарно по вопросам, где источники есть у обоих ответов. Служебные домены вроде google.com, openai.com и bing.com исключены одинаково во всех трёх парах.

Пара ответов

Вопросов

Общих / всех доменов

Доля общих

Вопросов без общих

третий съём (API): повтор 1 против повтора 2

9

14 / 61

23%

1

первый съём (API): повтор 1 против повтора 2

12

38 / 93

41%

1

второй (интерфейс) против третьего (API), только повтор 1

8

8 / 70

11%

3

Общих доменов у интерфейса и API вдвое меньше, чем у двух повторов самого API: 11% против 23%. Сравнение нечистое: интерфейс и API-съём отличаются сразу тремя вещами, способом съёма, ИИ-моделью и числом ответов на вопрос. Число ответов я выровнял, взяв из API только первый повтор, 20 ответов против 20. Способ и ИИ-модель на этих данных не разделить. В парах по 8–12 вопросов, так что это порядок величины, а не точная оценка.

Внутри одного способа список источников тоже плывёт. Даже у двух повторов первого съёма общих меньше половины доменов, и целиком список от повтора к повтору почти не совпадает. ИИ-модели в первом и третьем съёмах разные, так что 41% и 23% между собой я не сравниваю.

По восьми коммерческим вопросам я сверил оба способа с Алисой AI, ответы которой снимал в день третьего съёма. У Алисы AI на этих вопросах 39 доменов. Интерфейс ChatGPT дал 30, общих с Алисой 7. API дал 40, общих тоже 7. Каким способом ни снимай, с Алисой AI ChatGPT делит меньше четверти своих доменов. На вопросе «кто в России делает GEO-продвижение под ключ» (GEO — продвижение в ответах нейросетей) общих доменов нет совсем.

Вопрос p001, «как сделать, чтобы нейросети рекомендовали мой бренд», я смотрел отдельно: на нём ChatGPT объясняет, как попасть в его же ответы. Интерфейс сослался на bing.com, blogs.bing.com и help.openai.com. API в третьем съёме ссылался на developers.google.com четыре раза, на help.openai.com три раза и на support.google.com дважды. Отвечает он по документации OpenAI, Google и Bing, в том числе по рекомендациям Bing для вебмастеров. У OpenAI есть и страница о роботах, которые ходят по сайтам: GPTBot, OAI-SearchBot и ChatGPT-User.

Что ломает подсчёт источников в логах ChatGPT

Каждая из трёх поломок лечится своей проверкой в коде, а не чёрным списком доменов.
Каждая из трёх поломок лечится своей проверкой в коде, а не чёрным списком доменов.

В ответах из интерфейса источники приходят картинками-иконками: ![](https://www.google.com/s2/favicons?domain=https%3A%2F%2Fsite.ru&sz=128). Это сервис иконок Google: по адресу сайта он отдаёт его значок, фавикон, который браузер показывает на вкладке. Сам адрес спрятан в параметре domain= в закодированном виде, %3A вместо двоеточия и %2F вместо косой черты. Я проверил: на такой запрос сервис перенаправляет на gstatic.com/faviconV2. В 20 ответах второго съёма таких иконок оказалось 82.

Мой парсер брал любой адрес в круглых скобках. Так было:

import re

def source_urls(text: str) -> list[str]:
    # так было: ловит и ссылки, и картинки-иконки
    return re.findall(r"\((https?://[^\s\)]+)\)", text)

Выражение ловит и ссылки, и картинки. Повторы одного адреса внутри ответа парсер отбрасывал, и из 82 иконок уникальных внутри своих ответов осталось 32. Все 32 легли в список источников как google.com: это 32 записи из 120, примерно каждая четвёртая, и иконки входят в эти 120.

Так стало:

import re
from urllib.parse import urlparse, parse_qs, unquote

def source_domains(answer: str) -> set[str]:
    """Домены источников из markdown-ответа, включая ссылки-иконки."""
    domains = set()
    for url in re.findall(r"https?://[^\s)\]\"']+", answer):
        parts = urlparse(url)
        host = (parts.hostname or "").removeprefix("www.")
        if host == "google.com" and parts.path.startswith("/s2/favicons"):
            inner = unquote(parse_qs(parts.query).get("domain", [""])[0])
            host = (urlparse(inner).hostname or inner).removeprefix("www.")
        if host:
            domains.add(host)
    return domains

Функция берёт любую ссылку и отдельно проверяет адреса сервиса иконок. Если хост google.com, а путь начинается с /s2/favicons, настоящий домен достаётся из параметра domain= и раскодируется. Без этой проверки иконки сливаются в один google.com, к ответу отношения не имеющий. Этой функцией и считаются источники интерфейса: отдельного поля с источниками, как у API, у него нет, есть только текст ответа.

Сторонний сервис съёма добавил своё расхождение. В 20 ответах он насчитал 382 ссылки, а в тексте тех же ответов видно 120, и расходятся цифры в 14 ответах из 20. Чей счётчик правее, я разбираться не стал: сервис мог считать ссылки из разметки вне текста или дублировать одну и ту же ссылку. Лог пишет это отдельным дефектом, «сервис насчитал N ссылок, в тексте видно M», и число сервиса на веру не берёт.

В третьем съёме, через API, на том же p001 в источники попали ваш-домен.ru, example.com и schema.org. Это домены из примеров кода и шаблонов внутри самого ответа, поиск такие страницы не открывал. Чёрный список доменов тут не спасает: сегодня example.com, завтра что-то ещё. Поэтому в API-съёме поиск засчитывается по источникам, которые пришли от API, и по его служебным полям: вызовам поиска и ссылкам-аннотациям, которые API прикладывает к ответу отдельно от текста. Медиана таких аннотаций в ответе третьего съёма равна 4.

def search_performed(channel_type, sources, extra):
    if channel_type == "model_bare":          # канал без поиска
        return False
    if extra.get("search_calls") or extra.get("api_annotations"):
        return True
    if any(s.get("origin") == "api" for s in sources):
        return True                            # ссылка пришла от API, а не из текста
    if channel_type == "surface":              # интерфейс отвечает поверх выдачи,
        return None                            # но подтверждения нет — «неизвестно»
    return False

Канал без поиска сразу получает False. Если API сообщил о вызовах поиска, приложил аннотации или хотя бы один источник помечен как пришедший от него, функция возвращает True. Ответ из интерфейса отдельного подтверждения не несёт, и для него функция возвращает None, то есть неизвестно. Три вопроса третьего съёма, где ChatGPT ни разу не вышел в поиск, не проходят ни одну проверку на True и получают False. В лог они уходят с дефектом «поиск не отработал».

Ошибки 402 из третьего съёма в логе записаны как ошибки. Запиши я их как ответы без бренда, ноль по бренду вырос бы на 17 записей, за которыми нет ни одного ответа.

Что мониторинг ChatGPT показывает и чего не показывает

На этих логах мониторинг показывает, как часто ChatGPT называет бренд без подсказки и на какие сайты опирается в ответе. Что увидит конкретный человек в окне чата, он не показывает: общих источников у API с интерфейсом вдвое меньше, чем у API с собственным повтором, а сам интерфейс снят одним ответом на вопрос. Одиночный ответ тоже ничего не доказывает: единственное небрендовое упоминание бренда клиента прозвучало в одном повторе из трёх.

Для кода мониторинга я вынес отсюда такие правила:

  1. Один способ съёма на ряд. Интерфейс и API в одну цифру не смешиваю: подпись «доля упоминаний в ChatGPT» без способа съёма прячет два разных числа. Цена у выбора такая. Интерфейс стоит 10 ₽ за ответ и ближе к окну чата, но признака поиска у него нет. API через шлюз обходится в 25–36 ₽, признак поиска и источники приходят отдельными полями, но по источникам он расходится с окном чата. Какой из двух брать для ряда, три съёма одной недели не решают.

  2. Счёт по вопросам при фиксированном числе повторов. 1 из 57 по ответам и 1 из 19 по вопросам получены из одних данных. Повтор показывает, устойчив ли ответ, и не добавляет вопросу веса, поэтому считаю по вопросам. Доля вопросов, где бренд прозвучал хотя бы раз, растёт с числом повторов, поэтому число повторов на вопрос я фиксирую и пишу рядом с цифрой. Ошибки вроде 402 идут отдельной строкой и в ноль не попадают.

  3. Источники API-съёма берутся из поля API. Регулярное выражение по тексту ответа ловит иконки и примеры кода, поэтому в API-съёме в подсчёт идёт то, что API отдаёт отдельным полем. У интерфейса такого поля нет: его источники считаются по тексту с разбором иконок, как в source_domains, а признак поиска остаётся неизвестным. Число ссылок от стороннего сервиса на веру не беру ни там, ни там.

  4. Серия повторов на каждый вопрос. Два повтора одного API-съёма делят 23% доменов в третьем съёме и 41% в первом, поэтому вывод я строю по нескольким запускам.

Эти правила пригодятся как вопросы к любому отчёту о видимости в ChatGPT: каким способом сняты ответы, считалась доля по вопросам или по ответам, сколько было повторов на вопрос и куда ушли ошибки. Без этих подписей одна и та же выборка даёт и 1 из 57, и 1 из 19. Нормальный отчёт отвечает так: один способ съёма на весь ряд, доля по вопросам, число повторов указано рядом с цифрой, ошибки вынесены отдельной строкой.