инструменты для сайта

инструменты / ai-data.json / как устроен файл

Как устроен файл ai-data.json

ai-data.json - JSON-файл в корне сайта: справка о компании для нейросети, а не для поисковика. У формата нет ни одного официального стандарта и ни одной публичной спецификации - имя файла и состав полей задал GEO-аудит студии «Мегагрупп», это соглашение одной студии, принятое де-факто. Рядом по другим адресам живут другие имена того же по смыслу файла: ai.json, /.well-known/ai-agent.json, agents.json. Общего между ними нет, кроме идеи. Здесь мы описываем ровно то, что встречается на живых сайтах под именем ai-data.json, - и ровно то, что проверяем сами.

Раз спеки нет, эталон - это наш собственный код: что кладёт в файл генератор (yadro/ai_data.php) и что проверяет проверялка (yadro/pravila_ai_data.php). На этой странице - оба, полями, примером и правилами с весами. Судить чужой файл по правилам, которые нигде не названы, нельзя - поэтому они названы здесь.

Десять полей верхнего уровня

Порядок в таблице - тот же, в котором поля кладёт функция ai_data_sobrat(): это не список по алфавиту, а порядок, в котором их встретит и человек, открывший файл текстом.

Десять полей файла, по порядку сборки
ПолеТипЧто в нём лежит
nameстрока Имя компании: из разметки Organization, LocalBusiness, Store или HomeAndConstructionBusiness (берётся первый подходящий тип), а если разметки на главной нет - из заголовка страницы (title).
descriptionстрока Описание компании из мета-тега description главной страницы. Не из разметки - её описание может рассказывать не то же самое, что заявлено посетителю.
urlстрока Адрес сайта, каким он получился после редиректов, а не тот, что ввели в форму генератора.
contactобъект Три поля: phone (список телефонов из разметки и ссылок tel:, без повторов одного номера в разной записи), email (из разметки или первой ссылки mailto:) и address (склеен из частей почтового адреса разметки либо взят строкой как есть).
logoстрока или null Ссылка на логотип из разметки, приведённая к полному адресу: относительный путь вроде /logo.png машине читать не от чего. Не нашли или значение адресом не является - null, а не пустая строка.
socialсписок строк Ссылки на соцсети - только на восемь известных хостов (vk.com, t.me, ok.ru, youtube.com, rutube.ru, dzen.ru, wa.me, telegram.me), из разметки и со страницы, тоже полными адресами. Ссылка на любой другой хост в файл не попадает, откуда бы она ни пришла.
openingHoursобъект или пустой список Часы работы одной строкой в поле office, собранные из openingHours либо, если его нет, из openingHoursSpecification. Не нашли - пустой список [], а не пустой объект: без данных это обычный пустой PHP-массив, а json_encode() отдаёт его списком.
areaServedобъект или пустой список Город и, если он отличается от города, регион обслуживания - из разметки адреса. Не нашли ничего - тот же пустой список [], а не пустой объект, и по той же причине.
advantagesсписок Всегда пустой список. Генератор не заполняет его никогда - см. раздел ниже.
servicesсписок объектов До двадцати услуг. У каждой - name (H1 страницы, а если он пуст - title), description (мета-тег страницы услуги), url, price (из разметки Offer, Product или Service либо из текста вида «от 5 900 ₽», иначе null) и faq (пары вопрос-ответ из разметки FAQPage).

Обязательный минимум

Прежде чем считать пользу, проверялка решает: формат вообще соблюдён? Достаточно не любого непустого JSON-объекта, а трёх условий разом. У поля name обязан быть виден хотя бы один значащий знак - и у url тоже; число или список в этих полях в счёт не идёт, такое значение проверялка считает отсутствующим, как будто поля не было вовсе. И хотя бы одно из двух полей, contact или services, обязано быть непустым массивом - строка вроде "contact": "звоните нам" массивом не является, и это не минимум, а его имитация.

Не хватает хотя бы одного из трёх условий - формат не проходит, и находка так и называется: «Нет обязательного минимума». Без имени и способа связаться со справкой нечего делать - на вопрос «кто это и как до них добраться» она не отвечает, а ради этого её и читают.

Как мы узнаём, что файл вообще наш

Это отдельная проверка, и ради неё страница вообще была написана. У формата нет спецификации, значит нет и способа отличить настоящий ai-data.json от чужого JSON-файла, кроме как по набору полей. А поля вроде name и description есть почти у любого манифеста в интернете - у .webmanifest, у минимального package.json, у манифеста браузерного расширения. Раньше проверялка признавала своим файлом любые два поля из восьми - и все три перечисленных манифеста проходили как «наш», только неполный: человек получал «не заполнены услуги» про файл, который вообще не пытался быть ai-data.json.

Теперь правило в три шага. Сначала грубый отсев: список верхнего уровня (файл начинается с [, а не с {) - это не наш формат вовсе, а любой из явно чужих ключей - dependencies, devDependencies, scripts, require, autoload, items, offers, products, @context, @type, @graph - сразу останавливает разбор, ещё до подсчёта совпадений: это подписи package.json, composer.json, товарных выгрузок и куска собственной разметки JSON-LD, сохранённого в файл, - каких у ai-data.json не бывает. Дальше проверялка ищет совпадения среди восьми полей - name, description, url, contact, services, advantages, areaServed, openingHours - и смотрит только на то, есть ли ключ, а не на то, что в нём лежит: пустой contact всё ещё считается совпадением. Совпадений нужно не меньше двух. А дальше - главное условие: хотя бы одно из совпавших полей обязано быть из пятёрки отличительных - contact, services, advantages, areaServed или openingHours. Их не бывает у чужих манифестов, а name и description есть у всех подряд, поэтому пары из этих двух общих полей мало.

Файл, который не набрал двух совпадений или набрал их только среди общих полей, получает не обвинение в неполноте, а честный ответ: «Это не похоже на ai-data.json». Мы не судим его как испорченный наш файл - мы прямо говорим, что не узнали в нём этот формат. Это наше отличие от типичного GEO-аудита: обвинять в недоделке файл, который и не пытался быть тем, что мы ищем, нечестно.

Пример

Сокращённый файл по образцу живого сайта - компания и адрес вымышленные, устройство настоящее: две услуги вместо двадцати, но с теми же полями и в том же порядке.

{
  "name": "ООО «Пример»",
  "description": "Производим и устанавливаем пластиковые окна и балконные рамы в Екатеринбурге с 2008 года.",
  "url": "https://primer.ru/",
  "contact": {
    "phone": ["+7 (343) 200-00-00"],
    "email": "info@primer.ru",
    "address": "620000, Екатеринбург, ул. Заводская, 1"
  },
  "logo": "https://primer.ru/logo.png",
  "social": ["https://vk.com/primer"],
  "openingHours": {
    "office": "Mo-Fr 09:00-18:00"
  },
  "areaServed": {
    "city": "Екатеринбург"
  },
  "advantages": [],
  "services": [
    {
      "name": "Монтаж пластиковых окон",
      "description": "Устанавливаем окна по ГОСТу с гарантией три года и выездом на замер в день обращения.",
      "url": "https://primer.ru/uslugi/montazh/",
      "price": "от 5 900 ₽",
      "faq": [
        {
          "question": "Сколько ждать окно после заказа?",
          "answer": "От семи рабочих дней с момента замера."
        }
      ]
    },
    {
      "name": "Остекление балконов",
      "description": "Остекляем балконы и лоджии под ключ, с отделкой или без неё.",
      "url": "https://primer.ru/uslugi/osteklenie-balkonov/",
      "price": null,
      "faq": []
    }
  ]
}

Второй услуге такой файл ничего не выдумывает: цены на странице не было, поэтому price - null, а не «уточняйте» или прочерк. Пустое поле в этом формате - честный ответ «мы не нашли», а не место для красивой фразы.

Семь правил проверки и их веса

Правила из следующего списка считаются только поверх файла, который уже прошёл обязательный минимум и опознан как ai-data.json. Шкала - десять баллов, вес каждого правила ниже - ровно тот, что в коде (yadro/pravila_ai_data.php). Первые три весят по два балла: это поля, ради которых нейросеть вообще открывает такую справку - цена, готовый ответ и способ связаться. Остальные четыре весят по одному: это не про содержание файла, а про его аккуратность.

  1. Цены, 2 балла. Цена - единственное число во всём файле, которое нейросеть может процитировать дословно и не соврать. Правило не требует цену у каждой услуги: балл берётся, если она указана хотя бы у трети списка - точнее, число услуг с ценой, умноженное на три, обязано быть не меньше общего числа услуг. Без неё на вопрос «сколько стоит» нейросеть либо промолчит, либо возьмёт чужую цифру - а это хуже, чем ничего не сказать.
  2. Вопросы и ответы, 2 балла. Готовая пара вопрос-ответ, собранная из разметки FAQPage на странице услуги, - текст, который нейросеть вставляет в ответ почти как есть, а не пересказывает своими словами. Правило бинарное: два балла, если в файле есть хотя бы одна пара, и ноль, если нет ни одной, - сколько бы услуг ни было в списке.
  3. Контакты, 2 балла. Единственное правило с настоящей частичной оценкой: заполнены два поля из трёх (телефон, почта, адрес) - один балл, заполнены все три - два, меньше двух - ноль. Вес такой же высокий, как у цен и вопросов: без способа связаться справка не выполняет свою главную работу, сколько бы услуг в ней ни было перечислено.
  4. Живые адреса, 1 балл. Проверяем, отвечают ли адреса услуг живым кодом, - но только если карта кодов ответа вообще передана: без неё правило засчитывает балл по умолчанию, а не штрафует за то, что не проверялось. Вес ниже первых трёх, потому что это не про содержание файла, а про его аккуратность. Услуг в файле нет вовсе - балл не даётся, см. оговорку под списком.
  5. Без повторов адресов, 1 балл. Если два разных пункта списка услуг ведут на один и тот же адрес, для нейросети это одна и та же страница под двумя именами - она их всё равно объединит. Правило ловит небрежность сборки файла, а не нехватку контента, вес поэтому такой же скромный, как у живых адресов. Услуг в файле нет вовсе - балл не даётся, см. оговорку под списком.
  6. Услуги не статьи, 1 балл. Отдельно смотрим, не затесалась ли в список услуг статья или новость: по имени (начинается со слова «статья», «новость» или «блог») либо по сегменту адреса (stati, statya, blog, news, novosti). Ошибка обидная - нейросеть предложит человеку почитать вместо того, чтобы продать услугу, - но она про один пункт списка, а не про весь файл, поэтому балл тоже один. Услуг в файле нет вовсе - балл не даётся, см. оговорку под списком.
  7. Описания заполнены, 1 балл. Последнее правило ловит не отсутствие текста, а заглушки: описание компании и описание каждой услуги короче 40 знаков мы не признаём настоящим текстом - «Продажа окон» это ещё не описание. Порог низкий нарочно, чтобы не спорить о качестве текста и ловить только его полное отсутствие, поэтому и вес минимальный.

Файл, в котором услуг нет вовсе

Три правила из семи - живые адреса, повторы адресов и «услуги не статьи» - судят не файл целиком, а список услуг. Когда список пуст ("services": [], ключа нет вовсе или в списке нет ни одного объекта), каждое из них даёт ноль баллов и находку «Услуг в файле нет» - спокойную, не красную: файл ничего не нарушил, просто судить было не по чему.

Раньше эти три правила давали в такой пустоте полный балл: нарушать нечего, значит правило соблюдено. Файл без единой услуги набирал так 6 из 10 и получал три похвалы за то, чего в нём нет, - вплоть до «У каждой услуги свой адрес» про файл, где услуг ноль. Хвалить за отсутствующее для нас ровно то же самое, что обвинять за несуществующее, и запрещено по той же причине. Теперь файл с полными контактами и описанием, но без услуг, честно берёт 3 балла из 10.

Чего мы не требуем и почему

Дата обновления файла - в шкале ноль баллов, и поля для неё в самом формате тоже нет. У единственного живого образца, ради которого этот инструмент и делался, такого поля не оказалось вовсе - а требовать его значило бы навязывать чужому файлу свою же выдумку и потом судить его за то, что мы сами придумали.

По той же причине шкала пользы не смотрит на всё содержимое файла разом: она читает только contact, description и содержимое services (цену, вопросы, адрес, описание). Логотип, ссылки на соцсети, часы работы и область обслуживания в десять баллов не входят - это честные поля файла, но у нас нет способа доказательно сказать, что их отсутствие ухудшает ответ нейросети.

Поле advantages (достоинства) генератор не заполняет никогда и по той же причине не начисляет за него баллов проверялка: доказанные фактом достоинства вроде «работаем с 2008 года» разметкой не подтверждаются, а придумывать их за компанию нельзя. Пустой список здесь - осознанный отказ, а не дыра в сборке.

Собрать такой файл по своему сайту можно генератором ai-data.json, а файл, который уже лежит на сайте, - нашей же проверкой. Соберём и внедрим файл за вас - это услуга студии.