Перейти до основного вмісту
БЛОГ

Аудит готовності до ШІ-агентів: перевірка сайту за 30 хвилин

Аудит готовності до ШІ-агентів за 30 хвилин: шість блоків curl-команд (доступ, контент, виявлення, дані, API, правила) і один скрипт на всі перевірки.

Автор: IMozzОновлено 2026-10-14
Аудит готовності до ШІ-агентів за 30 хвилин — aiSiteReady

Аудит готовності до ШІ-агентів для свого сайту займає близько 30 хвилин перевірок: шість п'ятихвилинних блоків, один термінал і кілька команд curl, які покажуть, чи можуть ШІ-агенти дістатися до сайту, прочитати його, знайти, зрозуміти й викликати. Оцінка результатів і вибір, що виправляти, заберуть ще близько 10 хвилин.

Кожна команда нижче справжня. Я прогнав їх усі на нашому робочому сайті www.ai-siteready.com 2 і 3 жовтня 2026 року, а виводи — це скорочені копії того, що повернулося.

Сканер автоматизує все це, але один раз пройти руками корисно: видно, як кожен сигнал виглядає на рівні HTTP, і автоматичному звіту легше довіряти. Блоки йдуть у тому порядку, в якому бот зустрічає ваш сайт: спершу доступ, бо решта не має значення, якщо файрвол відхиляє бота, а виклик наприкінці, бо більшості сайтів він поки не потрібен. Саму ідею стека пояснює стаття що таке готовність до ШІ-агентів.

Ключові висновки

  • Аудит складається з шести блоків (доступ, читання, виявлення, розуміння, виклик, правила). Разом вони зачіпають усі 21 перевірку сканера aiSiteReady.
  • Починайте з доступу й читання. Бот, що отримав 403 або порожню JavaScript-оболонку, решти не побачить, а аналіз Vercel від грудня 2024 року показав, що краулери OpenAI, Anthropic і Perplexity не рендерять JavaScript (Vercel).
  • У ранніх блоках більшість сайтів проходить, у пізніх — майже ніхто. Cloudflare у квітні 2026 року просканувала 200 000 найпопулярніших доменів: robots.txt є в 78%, близько 4% оголошують уподобання щодо ШІ, а Markdown-узгодження проходять 3,9% (Cloudflare).
  • Не всі промахи рівнозначні. Google пише, що спеціальні файли для ШІ не потрібні, а Google Search ігнорує llms.txt (Google). Пріоритизуйте за впливом і зусиллями.
  • Ручний аудит — це знімок. Автоматизуйте його, коли шаблонів багато або ви часто випускаєте релізи.

Що входить у 30-хвилинний аудит готовності до ШІ-агентів?

Шість блоків, кожен націлений на свій тип збою. aiSiteReady групує перевірки в п'ять категорій: Видимість, Доступність контенту, Правила для ботів, Protocol/API, Комерція. Аудит проходить крізь них у тому порядку, в якому краулер зустрічає сайт, тому одна категорія може розтягнутися на два блоки.

Таймлайн із шести п'ятихвилинних блоків: доступ, читання, виявлення, розуміння, виклик, правила; нижче сітка з 21 перевірки сканера за блоками.

Потрібні curl і perl, вони є в macOS і в більшості дистрибутивів Linux. Підставте в змінну свій сайт і тільки свій: далі команди надсилають user agent краулерів, і це нормально для перевірки власних правил, але не для чужого сервера.

Перевірки сторінок запускайте на головній і на одному URL кожного шаблону, наприклад статті чи картки товару: проблема часто живе лише в одному шаблоні. Другий рядок нижче проходить за редиректами, тож голий домен або адреса з http:// зводиться до того origin, який справді відповідає.

export SITE=https://www.example.com    # ваш сайт, без слеша в кінці
SITE=$(curl -sS -L -o /dev/null -w '%{url_effective}' "$SITE/" | sed -E 's#^(https?://[^/]+).*#\1#')    # apex -> www, http -> https
hdr() { curl -sS -D - -o /dev/null "$@" | tr -d '\r'; }    # GET, друкуємо лише заголовки

Як перевірити, що ШІ-боти можуть дістатися до вашого сайту? (хвилини 0–5)

Запросіть головну сторінку й robots.txt як зазвичай, потім повторіть запит головної з user agent ШІ-краулерів. Кожна відповідь має бути звичайним 200 або відмовою, яку ви обрали свідомо.

curl -sS -o /dev/null -L -w 'status=%{http_code} redirects=%{num_redirects} time=%{time_total}s\n' "$SITE/"
curl -sS -o /dev/null -L -w 'robots.txt: %{http_code} %{content_type}\n' "$SITE/robots.txt"
for bot in GPTBot OAI-SearchBot ClaudeBot PerplexityBot; do
  printf '%-15s ' "$bot"
  curl -sS -o /dev/null -L -A "Mozilla/5.0 (compatible; $bot/1.0)" -w '%{http_code}\n' "$SITE/"
done
status=200 redirects=0 time=0.318752s
robots.txt: 200 text/plain; charset=utf-8
GPTBot          200
OAI-SearchBot   200
ClaudeBot       200
PerplexityBot   200
СигналДобреПогано
Головна сторінка200, не більш як один редирект403, 429, 5xx, ланцюжок редиректів
robots.txt200, text/plain, правила, які ви писали5xx, 429 або тайм-аут; HTML за цим шляхом
User agent ботівтой самий 200, що отримує браузер403, 429, сторінка-перевірка
Правила Disallowнічого зайвогоDisallow: / для ШІ-бота

У рядку про robots.txt прихована асиметрія. За RFC 9309, якщо файл недоступний (4xx), краулер може звертатися до будь-яких ресурсів, але якщо він недосяжний через помилку сервера чи мережі, краулер має вважати, що закрито все (RFC 9309). Google вважає будь-який 4xx, крім 429, відсутнім файлом (Google), тож 429 зараховуйте як провал. Зламаний robots.txt гірший, ніж відсутній. Тепер пошукайте суцільне блокування, яке хтось міг додати випадково:

curl -sS "$SITE/robots.txt" | tr -d '\r' | awk '{l=$0; sub(/#.*/,"",l); gsub(/[ \t]/,"",l); k=tolower(l)} k==""{next} k~/^user-agent:/{if(!g)u=""; g=1; u=u" "substr(l,12); next} {g=0} k=="disallow:/"{print substr(u,2)" -> Disallow: /"}'

На нашому сайті команда нічого не друкує, і це гарний результат. Якщо передати їй файл, що закриває навчальний краулер OpenAI, вона надрукує GPTBot -> Disallow: /. Група, у якій названо кілька ботів, виводиться одним рядком, а Disallow:/ без пробілу теж розпізнається.

Відсутність випадкового блокування ще не означає, що перевірку сканера ai_bot_rules пройдено. Їй потрібні іменовані групи для OAI-SearchBot, Claude-SearchBot і PerplexityBot плюс явний дозвіл або заборона хоча б для одного навчального краулера: GPTBot, ClaudeBot чи CCBot. Одна група User-agent: * дає в ній лише частковий результат. Команда grep -i '^user-agent' на вашому robots.txt покаже, які групи у вас є.

Цикл за user agent перевіряє лише правила, прив'язані до рядка user agent, і надсилає ім'я краулера з вашої власної IP-адреси. Файрволи фільтрують ще й за IP-адресою та за bot score, а OpenAI публікує IP-діапазони кожного краулера (OpenAI). Файрвол із захистом від ботів може заблокувати запит, який видає себе за краулер, але надходить не з його опублікованих IP-діапазонів, і водночас пропустити справжній краулер.

Тож 403 на підроблений user agent — підказка, яку треба звірити з логами файрвола, а не доказ блокування, а 200 потрібен, але недостатній. Anthropic додає, що блокування за IP може завадити її ботам узагалі прочитати robots.txt (Anthropic). Після зміни правил зачекайте: OpenAI повідомляє, що її системам може знадобитися близько 24 годин, щоб оновити результати пошуку (OpenAI).

Відповідні перевірки сканера: robots_txt і ai_bot_rules.

Чи може ШІ-агент прочитати сторінку без JavaScript? (хвилини 5–10)

Завантажте сторінку так, як це робить найпростіший краулер, без JavaScript, і порахуйте слова, що вціліли. Фільтр на perl викидає блоки script, style, template і SVG, а потім прибирає решту тегів.

curl -sS -L "$SITE/" | perl -0777 -pe 's/<(script|style|noscript|template|svg)\b.*?<\/\1>//gsi; s/<[^>]+>/ /gs' | wc -w
curl -sS -L "$SITE/" | grep -o -i -E '<(h1|h2|main|title)[ >]' | tr 'A-Z' 'a-z' | sort | uniq -c
1246
   1 <h1
   5 <h2
   1 <main
   1 <title>

Потрібні сотні слів, один h1, справжні заголовки й ваш власний текст; майже нульовий лічильник і скелет із розмітки — привід насторожитися. Я прогнав той самий фільтр на сторінці, де в body лише порожній div і тег script, тобто на класичній клієнтській оболонці, і він надрукував 0. Саме це отримує GPTBot або ClaudeBot від такої сторінки. За вимірюваннями Vercel від грудня 2024 року, винятком є Gemini та AppleBot: вони рендерять сторінки (Vercel).

Вебзастосунок за логіном покаже лише оболонку входу, і це очікувано. А великий лічильник сам по собі ще нічого не означає: якщо слова — це cookie-банери та навігація, прочитайте сам текст.

Відповідна перевірка сканера: ssr_content.

Як перевірити виявлення: від sitemap до llms.txt? (хвилини 10–15)

Переконайтеся, що бот знаходить ваші сторінки без здогадок: sitemap, на який вказує robots.txt, один canonical на сторінку, зв'язки виявлення в заголовку Link і, за бажанням, llms.txt.

curl -sS "$SITE/robots.txt" | grep -i '^sitemap:'
hdr "$SITE/sitemap.xml" | grep -i -E '^(HTTP|content-type)'
curl -sS "$SITE/sitemap.xml" | grep -o '<loc>' | wc -l
curl -sS "$SITE/" | grep -o '<link[^>]*rel="canonical"[^>]*>'
hdr "$SITE/" | grep -i '^link:'
hdr "$SITE/llms.txt" | grep -i -E '^(HTTP|content-type)'
hdr "$SITE/llms-full.txt" | grep -i -E '^(HTTP|content-type)'
Sitemap: https://www.ai-siteready.com/sitemap.xml
HTTP/2 200
content-type: application/xml; charset=utf-8
57
<link id="i18n-can" rel="canonical" href="https://www.ai-siteready.com">
link: </sitemap.xml>; rel="sitemap", </llms.txt>; rel="describedby", </site.webmanifest>; rel="manifest", </.well-known/api-catalog>; rel="api-catalog"
HTTP/2 200
content-type: text/markdown; charset=utf-8
HTTP/2 200
content-type: text/markdown; charset=utf-8

Порівняйте вивід із тим, як виглядає добрий і поганий результат:

СигналДобреПогано
SitemapXML, ненульова кількість <loc>, указаний у robots.txt404, HTML-сторінка з відповіддю 200, URL зі staging-домену
Canonicalодин абсолютний URL, що збігається зі сторінкоювідсутній, подвоєний або розходиться з sitemap
llms.txt200 з text/markdown або text/plainHTML за цим шляхом, який часто віддає маршрут-заглушка (catch-all)

Заголовок Link — найдешевший сигнал: RFC 8288 визначає його як спосіб серіалізувати посилання в HTTP-заголовки, і кожне посилання мусить мати rel (RFC 8288). Наш заголовок вказує на sitemap, llms.txt і каталог API, і клієнтові не потрібно розбирати HTML.

З llms.txt — без ілюзій. У пропозиції обов'язковий лише H1, за ним ідуть стисле резюме в цитаті та розділи з посиланнями (llmstxt.org); це пропозиція, а не стандарт. Ahrefs з'ясував, що близько 28% зі 137 000 доменів у його даних Web Analytics публікують такий файл, а 97% цих файлів за травень 2026 року не отримали жодного запиту (Ahrefs). Публікувати його дешево, але це погана причина відкладати те, що вище в списку.

Відповідні перевірки сканера: sitemap, canonical_metadata, link_headers, llms_txt, llms_full_txt.

Чи говорять ваші структуровані дані, хто й коли це написав? (хвилини 15–20)

Виведіть типи JSON-LD із сирого HTML і перевірте, що в статті є автор і дати. У ARTICLE підставте будь-яку статтю свого сайту; у нас це була /blog/serve-markdown-to-ai-agents. Структуровані дані, які скрипт підставляє після завантаження, у цей вивід не потраплять, і в цьому весь сенс.

ARTICLE="$SITE/path/to/an/article"    # будь-яка стаття вашого сайту
curl -sS "$SITE/" | grep -o '"@type": *"[^"]*"' | sort | uniq -c
curl -sS "$ARTICLE" | grep -o -E '"(datePublished|dateModified)": *"[^"]*"|"author": *\{[^}]*\}'
  10 "@type":"Answer"
   1 "@type":"FAQPage"
   1 "@type":"ImageObject"
   1 "@type":"Offer"
   1 "@type":"Organization"
  10 "@type":"Question"
   1 "@type":"SoftwareApplication"
   1 "@type":"WebSite"
"datePublished":"2026-06-07"
"dateModified":"2026-06-07"
"author":{"@type":"Person","name":"IMozz","url":"https://github.com/i-mozz"}

На головній мають бути Organization і WebSite, а в кожній статті — BlogPosting з автором Person, datePublished і dateModified. Google називає ці три властивості рекомендованими для Article і жодну не вважає обов'язковою (Google). Шукайте порожній вивід, дати лише у видимому тексті або автора «admin». В інтернет-магазинів тут має з'явитися й Product. Для статей див. чек-лист автора, дати та JSON-LD.

Відповідна перевірка сканера: generic_structured_data.

Чи може агент викликати можливості вашого сайту? (хвилини 20–25)

Запросіть Markdown, а тоді перевірте well-known шляхи, де оголошуються агентські ендпоінти. Контентним сайтам другу половину можна проглянути: вона важлива продуктам з API чи інструментами.

hdr -H 'Accept: text/markdown' "$SITE/" | grep -i -E '^(HTTP|content-type|vary)'
for p in api-catalog agent-skills/index.json ai-catalog.json mcp/server-card.json agent-card.json \
         oauth-authorization-server oauth-protected-resource; do
  printf '%-44s ' "/.well-known/$p"
  curl -sS -o /dev/null -L -w '%{http_code} %{content_type}\n' "$SITE/.well-known/$p"
done
HTTP/2 200
content-type: text/markdown; charset=utf-8
vary: Accept-Encoding
vary: Accept
/.well-known/api-catalog                     200 application/linkset+json; charset=utf-8; profile="https://www.rfc-editor.org/info/rfc9727"
/.well-known/agent-skills/index.json         200 application/json; charset=utf-8
/.well-known/ai-catalog.json                 404 application/json
/.well-known/mcp/server-card.json            200 application/json; charset=utf-8
/.well-known/agent-card.json                 200 application/json; charset=utf-8
/.well-known/oauth-authorization-server      404 application/json
/.well-known/oauth-protected-resource        404 application/json

З Markdown потрібен text/markdown разом із Vary: Accept: так кеш не віддасть Markdown браузеру, а сам Vary: Accept-Encoding не зараховується. Якщо ви не на Cloudflare, див. Markdown без Cloudflare. Читайте результат обходу за кодом статусу, а не за типом вмісту: три наші рядки з 404 усе одно показують application/json, бо тіло помилки — JSON.

ШляхЩо оголошуєСтатус стандарту
api-catalogваші API з посиланням на файл OpenAPIRFC 9727 (RFC 9727)
ai-catalog.jsonШІ-артефакти, зокрема MCP Server Cardчернетка; точка входу для виявлення в чернетці MCP
mcp/server-card.jsonMCP-сервер і його ендпоінтекспериментальний, не офіційне розширення; старий шлях (репозиторій MCP)
agent-skills/index.jsonнавички, які може завантажити агентDraft 0.2.0 (репозиторій специфікації)
oauth-*метадані OAuth-сервера й ресурсуRFC 8414 та RFC 9728 (RFC 8414, RFC 9728)

Тип сайту визначає, що означає 404. Сайт без OAuth-захищеного API має отримати 404 в обох рядках OAuth, і в нас так і є; сайт, де працює OAuth, має отримати 200. Сканер вважає такий 404 незастосовним, якщо не бачить ознак OAuth, але не в профілі API/застосунок: там 404 — провал навіть для сервісу, який працює лише з API-ключами.

Картка MCP переїхала. Поточна чернетка дозволяє розміщувати картку за будь-яким незарезервованим URI, рекомендує <mcp-url>/server-card і будує виявлення через /.well-known/ai-catalog.json; шлях /.well-known/mcp/server-card у ній розглянуто й не рекомендовано (discovery.md). Сканер aiSiteReady досі перевіряє старий шлях, а наш сайт поки віддає картку там само й без AI Catalog. Подробиці — у статті MCP Server Card.

digest в Agent Skills — це SHA-256, тож команда curl -sS "$SITE/.well-known/agent-skills/scan-url/SKILL.md" | shasum -a 256 має надрукувати hex-частину значення sha256: з індексу; на нашому сайті так і відбувається. Підтримка всього цього в агентів нерівномірна, а частина специфікацій — чернетки.

Відповідні перевірки сканера: markdown_negotiation, api_catalog, mcp_server_card, agent_skills, a2a_webmcp_readiness, oauth_authorization_server, oauth_protected_resource.

Які правила використання, ліміти та дані про товари ви публікуєте? (хвилини 25–30)

Останній блок: сказати агентам, що їм можна, як швидко і, якщо ви щось продаєте, що саме.

curl -sS "$SITE/robots.txt" | grep -i -E '^content-(signal|usage)'
hdr "$SITE/" | grep -i '^content-usage:' || echo 'content-usage header: none'
curl -sS -o /dev/null -w 'tdmrep: %{http_code}\n' "$SITE/.well-known/tdmrep.json"
hdr "$SITE/" | grep -i -E '^(ratelimit|ratelimit-policy|retry-after)'
curl -sS -o /dev/null -w 'ucp: %{http_code}\n' "$SITE/.well-known/ucp"
Content-Signal: search=yes, ai-input=yes, ai-train=yes
content-usage header: none
tdmrep: 404
ratelimit-policy: "scan";q=20;w=60
ucp: 404

Уподобання щодо використання. Content Signals, які Cloudflare опублікувала у вересні 2025 року, додають у robots.txt рядок Content-Signal із трьома сигналами — search, ai-input і ai-train, кожен зі значенням yes або no (Cloudflare). У нас дозволено всі три. Власний словник IETF з мітками train-ai, ai-use і search поки лишається Internet-Draft (ревізія 08, вересень 2026) і може змінитися (IETF aipref).

Варіант IETF передається рядком Content-Usage у robots.txt або заголовком відповіді Content-Usage, тому блок перевіряє обидва місця. Наш сайт, як видно з виводу, не публікує ні того, ні іншого, а сканер aiSiteReady поки теж не читає Content-Usage. Будь-яке явне машинозчитуване уподобання краще за мовчання.

Підказки про ліміти. Чернетка IETF для заголовків RateLimit-Policy і RateLimit зараз у ревізії 11 (травень 2026) і не є RFC; її приклад — RateLimit-Policy: "default";q=100;w=10, тобто квота 100 за 10 секунд (IETF). Наш заголовок оголошує політику scan: 20 запитів за 60 секунд, це публічний ліміт на запуск скану. Синтаксис, обробку 429 і готові конфігурації для серверів описано в посібнику з RateLimit-Policy.

Комерція залежить від типу сайту. aiSiteReady — SaaS-інструмент, тож 404 на /.well-known/ucp для нас коректний. На нашій головній є Offer із ціною, але немає Product, а поодинокий офер не вважається даними про товар. Для магазину запустіть команду з @type із блоку 4 на сторінці товару й очікуйте Product плюс Offer із price та priceCurrency.

Перевірка сканера щодо фідів товарів також шукає підказки про фіди в заголовках і метаданих. UCP — протокол, який Shopify і Google анонсували в січні 2026 року, — розміщує профіль продавця за адресою /.well-known/ucp (Shopify, UCP). Кроки описано в статті як опублікувати профіль UCP.

Відповідні перевірки сканера: content_usage_policy, rate_limit_hints, product_structured_data, product_feed_hints, agentic_commerce_signals.

Як оцінити аудит і вирішити, що виправляти першим? (хвилини 30–40)

Оцінюйте кожен рядок так само, як сканер: PASS — 1, PARTIAL — 0,5, FAIL — 0, а рядок, що сайту не стосується, випадає. aiSiteReady до того ж зважує перевірки за важливістю (high ×2, medium ×1, low ×0,5) і згортає категорії за профілем в оцінку Agent Readiness Score від 0 до 100 та рівень від 0 до 5 (методологія).

Перевірка, яку не вдалося завершити, отримує статус unknown — «не вдалося перевірити», а не «провалено», тому перевірте її ще раз: доки ви цього не зробили, вона дає 0, як провал, і лишається в загальному рахунку. Профіль Контентний сайт надає категоріям Protocol/API і Комерція нульову вагу, тож ці рядки можна пропустити.

Заповніть робочу таблицю, по рядку на перевірку. Вага — це важливість перевірки в сканері: H, M або L, а прочерк означає, що окремої перевірки в сканері немає. Ваги взято зі сканера; у моїй пріоритизації нижче llms.txt стоїть нижче навмисно.

ПеревіркаКомандаПройдено, колиВагаВиправлення
1 · доступ ботівцикл curl -Aтой самий 200, що в браузера, для кожного user agent ШІ—налаштування ботів у файрволі чи CDN
1 · правила для ШІ-ботівawk, grep '^user-agent'іменовані групи для пошукових ботів, явне рішення щодо навчання, немає випадкового блокуванняHпосібник із robots.txt
1 · robots.txt%{http_code}, %{content_type}200 text/plain або свідомий 404; ніколи 5xx чи 429Hпосібник із robots.txt
2 · контент у сирому HTMLлічильник слів perlсотні слів, один h1HJavaScript-сайти
3 · sitemapgrep -o '<loc>'XML, ненульова кількість, указаний у robots.txtHпошук сторінок
3 · canonical, заголовок Linkgrep, hdrодин абсолютний canonical; зв'язки виявленняL, Mпошук сторінок
3 · llms.txthdr200, Markdown, H1, резюме, посиланняHllms.txt
4 · JSON-LDgrep '@type'Organization, WebSite; у статей автор і датиMструктуровані дані
5 · Markdownзаголовок Accepttext/markdown і Vary: AcceptHMarkdown
5 · каталог APIобхід well-known200 linkset+json із посиланням на файл OpenAPIMкаталог API
5 · MCP, skills, A2Aобхід well-known200 JSON, якщо ви публікуєте цю поверхнюMвиклик агентами
5 · метадані OAuthобхід well-known200, якщо у вас є OAuth; профіль API/застосунок його очікуєMOAuth
6 · правила використанняgrep content-явний машинозчитуваний сигналMправила використання
6 · підказки про лімітиhdrRateLimit-Policy або Retry-AfterLліміти
6 · лише магазинисторінка товару, ucpProduct і Offer із ціною; профіль віддає 200M, Lкомерція

Потім відсортуйте провали за впливом (скільки в стеку блокує промах) і зусиллями (скільки часу забере виправлення). Розстановка нижче якісна й розрахована на типовий контентний сайт; в API-продукту протокольні пункти піднімуться вище.

Матриця «вплив — зусилля»: блокування ботів і немає sitemap — швидкі перемоги, контент лише після JavaScript — велика ставка, MCP і OAuth — відкласти.

Ідіть по верхньому ряду зліва направо. Правило, що блокує ботів, часто виправляється одним рядком і розблоковує все інше. Сайт із клієнтським рендерингом — це проєкт, але без нього решті стека нічого описувати. Нижній ряд — доопрацювання й задачі, що залежать від продукту. Перезапускайте аудит після кожного виправлення.

Чи можна запустити весь аудит одним скриптом?

Так, для всього, що зводиться до заголовка чи збігу в тілі. Скрипт виконує швидкі перевірки з усіх шести блоків і друкує для кожної PASS, WARN, FAIL або INFO: FAIL — блокувальна проблема, WARN — варто виправити, INFO — необов'язково або залежить від продукту. Пороги за кількістю слів — моє практичне правило, а не пороги сканера.

Спершу скрипт проходить за редиректами від переданого URL, тож голий домен або адреса з http:// перевіряється на тому origin, який відповідає. Ще він дивиться на тип вмісту, а не лише на код статусу: односторінковий застосунок може відповідати 200 з HTML на будь-який шлях.

#!/usr/bin/env bash
# agent-audit.sh - швидка перевірка готовності до ШІ-агентів. Тестуйте лише власні сайти.
# Usage: ./agent-audit.sh https://www.example.com
set -u
SITE="${1:?usage: agent-audit.sh https://www.example.com}"
SITE="${SITE%/}"
# Audit the origin the home page lands on (apex -> www, http -> https).
SITE=$(curl -sS -m 10 -L -o /dev/null -w '%{url_effective}' "$SITE/" 2>/dev/null | sed -E 's#^(https?://[^/]+).*#\1#')
pass=0; warn=0; fail=0
ok()   { printf 'PASS  %s\n' "$1"; pass=$((pass+1)); }
warn() { printf 'WARN  %s\n' "$1"; warn=$((warn+1)); }
bad()  { printf 'FAIL  %s\n' "$1"; fail=$((fail+1)); }
info() { printf 'INFO  %s\n' "$1"; }
code() { curl -sS -L -m 10 -o /dev/null -w '%{http_code}' "$@" 2>/dev/null; }
ctyp() { curl -sS -L -m 10 -o /dev/null -w '%{http_code} %{content_type}' "$@" 2>/dev/null; }
hdrs() { curl -sS -L -m 10 -D - -o /dev/null "$@" 2>/dev/null | tr -d '\r'; }
body() { curl -sS -L -m 10 "$@" 2>/dev/null; }

# 1. REACH
c=$(code "$SITE/")
[ "$c" = 200 ] && ok "home returns 200" || bad "home returned $c"
c=$(ctyp "$SITE/robots.txt")
case "$c" in
  "200 text/plain"*) ok "robots.txt returns 200 text/plain" ;;
  200*) warn "robots.txt returns 200 as ${c#200 } (HTML catch-all?)" ;;
  429*) bad "robots.txt returned 429 (Google treats it like a 5xx)" ;;
  4??*) warn "robots.txt returned ${c%% *} (no rules: crawlers may fetch everything)" ;;
  *)    bad "robots.txt returned ${c%% *} (5xx/unreachable = crawlers assume full disallow)" ;;
esac
for bot in GPTBot OAI-SearchBot ClaudeBot PerplexityBot; do
  c=$(code -A "Mozilla/5.0 (compatible; $bot/1.0)" "$SITE/")
  [ "$c" = 200 ] && ok "UA $bot gets 200" || bad "UA $bot gets $c (user-agent rule? confirm in firewall logs)"
done

# 2. READ
html=$(body "$SITE/")
words=$(printf '%s' "$html" | perl -0777 -pe 's/<(script|style|noscript|template|svg)\b.*?<\/\1>//gsi; s/<[^>]+>/ /gs' | wc -w | tr -d ' ')
if   [ "$words" -ge 200 ]; then ok "$words words in raw HTML"
elif [ "$words" -ge 50 ];  then warn "only $words words in raw HTML"
else bad "$words words in raw HTML (JavaScript shell?)"; fi
printf '%s' "$html" | grep -qi '<h1[ >]' && ok "an h1 is in the raw HTML" || bad "no h1 in the raw HTML"

# 3. DISCOVER
if body "$SITE/robots.txt" | grep -qi '^sitemap:'; then ok "robots.txt lists a Sitemap"; else warn "no Sitemap line in robots.txt"; fi
body "$SITE/sitemap.xml" | grep -qiE '<(urlset|sitemapindex)' && ok "sitemap.xml parses as a sitemap" || warn "no sitemap at /sitemap.xml"
printf '%s' "$html" | grep -qiE '<link[^>]+rel="canonical"' && ok "canonical link found" || warn "no canonical link"
hdrs "$SITE/" | grep -qiE '^link:.*rel="?(sitemap|api-catalog|describedby|alternate)' && ok "Link header advertises discovery relations" || info "no discovery Link header"
case "$(ctyp "$SITE/llms.txt")" in
  "200 text/markdown"*|"200 text/plain"*) ok "llms.txt exists (optional)" ;;
  *) info "no llms.txt (optional)" ;;
esac

# 4. UNDERSTAND
n=$(printf '%s' "$html" | grep -oi 'application/ld+json' | wc -l | tr -d ' ')
[ "$n" -ge 1 ] && ok "$n JSON-LD block(s) in raw HTML" || warn "no JSON-LD in raw HTML"

# 5. CALL
h=$(hdrs -H 'Accept: text/markdown' "$SITE/")
if printf '%s' "$h" | grep -qi '^content-type: text/markdown'; then
  printf '%s' "$h" | grep -qiE '^vary:(.*[ ,])?accept *(,|$)' && ok "Markdown negotiation with Vary: Accept" || warn "Markdown served but no Vary: Accept"
else info "no Markdown negotiation on /"; fi
for p in /.well-known/api-catalog /.well-known/agent-skills/index.json /.well-known/ai-catalog.json /.well-known/mcp/server-card.json /.well-known/agent-card.json; do
  case "$(ctyp "$SITE$p")" in
    200*json*) ok "$p found" ;;
    *) info "$p not found (fine unless you ship it)" ;;
  esac
done

# 6. GOVERN
body "$SITE/robots.txt" | grep -qi '^content-signal:' && ok "Content-Signal in robots.txt" || info "no Content-Signal line"
if body "$SITE/robots.txt" | grep -qi '^content-usage:' || hdrs "$SITE/" | grep -qi '^content-usage:'; then
  ok "Content-Usage (aipref) in robots.txt or a header"
else info "no Content-Usage rule or header"; fi
hdrs "$SITE/" | grep -qiE '^(ratelimit|ratelimit-policy|retry-after):' && ok "rate-limit header on /" || info "no RateLimit-Policy header"

printf '\n%s pass, %s warn, %s fail\n' "$pass" "$warn" "$fail"
[ "$fail" -eq 0 ]

Я запустив його на https://www.ai-siteready.com, на голому https://ai-siteready.com і на http://ai-siteready.com, і всі три прогони збіглися. Кожен надрукував 21 рядок PASS і два рядки INFO (немає AI Catalog і немає Content-Usage) та завершився рядком 21 pass, 0 warn, 0 fail. Це кількість рядків самого скрипта, а не 21 перевірка сканера.

Чистий прогін на нашому сайті сам по собі мало що доводить: я будував сайт під ці перевірки. Показовіший — випадок із помилками. На локальній заглушці, яка віддає лише порожній div і тег script, скрипт закінчується рядком 5 pass, 5 warn, 2 fail і друкує два рядки, що важать найбільше.

FAIL  0 words in raw HTML (JavaScript shell?)
FAIL  no h1 in the raw HTML

За будь-якого FAIL скрипт завершується з ненульовим кодом, тож його можна запускати в CI. Він не оцінить якість контенту, не перевірить JSON-LD за схемою і не відрізнить справжню Markdown-сторінку від підробки. Саме тут потрібен сканер.

Коли автоматизувати аудит готовності до ШІ-агентів?

Переходьте на автоматизацію, коли впираєтеся в будь-яку з цих меж:

  • більше за жменьку шаблонів сторінок;
  • релізи настільки часті, що регресії ймовірні;
  • потрібні докази, які можна передати розробникові;
  • хочеться порівнювати скани в часі.

curl перевіряє один URL за раз, а проблема, що б'є найболючіше, зазвичай ховається в шаблоні, який ви пропустили. Браузерні інструменти закривають інший зріз: у Lighthouse, починаючи з Chrome 150, є експериментальна категорія Agentic Browsing — WebMCP, доступність, стабільність верстки й машинозчитуване резюме в корені домену (Chrome).

Саме це автоматизує aiSiteReady. Він завантажує сирий HTML публічного URL без JavaScript, як прості ШІ-краулери, під власним user agent aiAgentReady-Scanner/0.1. Він запускає до 21 перевірки, які покривають ті самі ділянки, що й ваші шість блоків; обраний профіль (Усі перевірки, Контентний сайт, API/застосунок, Інтернет-магазин) визначає, які з них запускаються і що враховується. Скан лише читає й надсилає кожен запит через fetcher із захистом від SSRF.

Він повертає оцінку Agent Readiness Score від 0 до 100 із доказами за кожною перевіркою. Якщо файрвол відповідає сканеру 403 або 429, більшість перевірок отримують unknown, а не fail, бо це вимірює доступ сканера, а не ваш сайт. До кожної проваленої чи часткової перевірки додаються зрозумілі рекомендації та готова задача для розробника. Перевірки, ваги й рівні описано на сторінці методології.

Запустіть безкоштовне сканування, щоб побачити свою оцінку, свій рівень і те, які виправлення робити першими, англійською, українською чи російською.

Спершу доступ і читання, потім виявлення та дані, а агентські ендпоінти й правила — коли їх потребує продукт. Тридцять хвилин із curl знаходять проблеми, що важливі, скрипт не дає їм повернутися, а сканування покриває сторінки, яких ви не перевіряли.

IMozz має 20 років у розробці ПЗ, а останній рік будує продукти за допомогою LLM. Він розвиває aiSiteReady, сканер лише для читання, що перевіряє, чи може ШІ-агент прочитати сайт. Сканер серверно рендерить власний контент як робочий приклад.

Часті запитання

Скільки триває аудит готовності до ШІ-агентів?
Близько 30 хвилин перевірок на один сайт уручну — шість блоків приблизно по п'ять хвилин: доступ ботів, контент у сирому HTML, файли виявлення, структуровані дані, агентські ендпоінти та правила використання. Ще близько 10 хвилин іде на оцінку результатів. Інструменти — curl і кілька shell-фільтрів. Готовий скрипт відпрацьовує за секунди, а найдовше триває розбір виводу й рішення, що виправляти першим.
Як перевірити, чи може ШІ прочитати мій сайт?
Завантажте сторінку так, як це робить простий краулер, без JavaScript: зробіть curl, виріжте скрипти й теги та порахуйте слова, що лишилися. Якщо заголовок і основний текст на місці, прості краулери сторінку прочитають. Якщо повернулася порожня оболонка, GPTBot і ClaudeBot майже нічого не побачать: за даними Vercel, вони не рендерять JavaScript.
Чи доводить відповідь 200 на curl з user agent GPTBot, що ChatGPT дістанеться сайту?
Ні. Вона доводить лише, що сервер і правила за user agent пропускають такий рядок. Файрволи фільтрують ще й за IP-адресою чи оцінкою бота, а OpenAI публікує IP-діапазони своїх краулерів. Відповідь 403 теж не доводить блокування: файрвол із захистом від ботів може відхилити запит, який видає себе за краулер, але надходить не з його опублікованих IP-діапазонів. Вважайте 403 підказкою й звірте з логами файрвола. Відповідь 200 потрібна, але недостатня.
Що виправляти першим після аудиту?
Спершу проблеми доступу й читання: robots.txt або файрвол, що відхиляють ботів, і контент, який з'являється лише після запуску JavaScript. Далі йдуть прості покращення у виявленні: sitemap, canonical і структуровані дані з автором та датами. llms.txt, картки MCP, agent skills і OAuth discovery відкладайте: це робота, що залежить від продукту.