The Jerusalem PostStudent opens fire outside school in Turkey, eight pupils wounded, NTV reportsDaily MaverickStudent opens fire outside school in Turkey, eight pupils wounded, NTV reportsוואלהבמהלך חג הסוכות תוגבל כניסה למטיילים במשטחי אימונים בדרוםBollywood HungamaEXCLUSIVE: Ajay Devgn-Rohit Jugraj’s horror thriller titled SuryasparshInquirer EntertainmentJopay Paguia ‘respects’ Rochelle Pangilinan, but stands firm on her work ethicInquirerAgusan solon pushes national soil strategy through SUCsХабрКак в игровых студиях принимаются технические решения, когда на кону денюжкиCollider‘Marvel’s Wolverine’ Officially Changes Controversial Gameplay Feature After Fan BacklashSouth China Morning PostCan China’s grain reserves protect food security against El Nino?The South AfricanAircraft crash reported near Morningstar Airfield on the N7RTL BoulevardRekenkamer: Van Weel zette Kamer op verkeerde been over 10.000 onbehandelde aangiftenGhaflaBoundaries And Public Office: Omanga’s School Attire Trend Sparks Debate Online
The Daily Newsstand · Free, Always
Tuesday, September 22, 2026

Почему ИИ для программирования выбирает одни и те же библиотеки: я просканировал 30 сайтов документации

Translate

Я собираю фидбэк по проекту, который советую людям как альтернативу тяжёлому фреймворку. Вставляю его в диалог с ассистентом кода и прошу подсказать библиотеку под задачу, которую он решает лучше всего. Ассистент называет три конкурента покрупнее и ни разу — мой проект. Дело не в качестве кода: я решил проверить гипотезу техническими средствами.

Гипотез было три. Может, дело в файле robots.txt, который блокирует ботов ИИ. Может, у проекта просто нет llms.txt — модного файла, который сейчас советуют выкладывать. Может, документация физически нечитаема для краулера. Я написал скрипт, прогнал его на 30 сайтах документации популярных Python- и JS-библиотек и получил цифры, которые сходятся не с той гипотезой, что я держал в голове изначально.

Коротко результат: robots.txt не блокирует ботов ИИ ни на одном из 30 доменов — ни по адресу документации, ни в корне домена; llms.txt есть у 11 проектов, и независимые логи показывают, что его почти никто не запрашивает; а пустая для краулера страница нашлась у трёх проектов, но после проверки внутренних страниц из них остался один.

Зачем проверять документацию, а не сами ответы ассистентов

По Stack Overflow Developer Survey 2025 (49 000+ респондентов, 177 стран) 84% разработчиков используют или планируют использовать ИИ-инструменты, 51% профессиональных разработчиков — ежедневно. JetBrains State of Developer Ecosystem 2025 (24 534 респондента) даёт похожую цифру: 85% регулярно применяют ИИ-инструменты в разработке, 62% полагаются на хотя бы один выделенный ИИ-ассистент для кода. По данным GitHub Octoverse 2025, 80% новых разработчиков на платформе начинают пользоваться Copilot в первую же неделю.

То есть решение, какую библиотеку взять, массово принимает не разработчик, листающий сравнения на форумах, а нейросеть для программирования, отвечающая по накопленным паттернам обучения. Если эти паттерны систематически смещены в пользу уже раскрученных проектов, у новых и нишевых инструментов почти нет шанса пробиться — не потому что они хуже, а потому что модель их не видит или не помнит.

При этом доверие к качеству советов падает: тот же опрос Stack Overflow фиксирует снижение доверия к точности ИИ с 40% в 2024 году до 29% в 2025-м, а топ-жалобой (66% респондентов) стало «почти правильно, но не совсем». Разработчики продолжают пользоваться инструментом чаще, но верят ему меньше — комбинация, в которой источник рекомендации имеет значение.

Я решил проверить один конкретный технический слой этой проблемы — доступность документации для краулеров, которые собирают данные для ИИ-ассистентов и поисковых нейроответов. Не потому что это единственная причина смещения, а потому что это единственный слой, который можно измерить без доступа к весам модели: HTTP-запросом.

Что я замерял и почему не прогонял сами модели

Три HTTP-проверки на 30 доменах документации, без единого обращения к языковой модели. Прогон моделей и разбор доступности сайта я развёл намеренно. Это разные величины. Ответ модели зависит от десятков факторов помимо доступности страницы. А доступность страницы — конкретный технический факт, который не зависит от того, какую модель спросили. Смешай их в одном эксперименте — и потом не скажешь, что именно ты измерил.

Список — 30 проектов документации: FastAPI, Pydantic, LangChain, Polars, Ruff, Django, Flask, NumPy, pandas, Requests, httpx, SQLAlchemy, Celery, Pytest, Scikit-learn, PyTorch, Hugging Face Transformers, Streamlit, Typer, Rich, Vite, Svelte, Next.js, Tailwind CSS, Prisma, Supabase, Astro, Bun, Deno, Zod. Здесь намеренно смешаны старые широко используемые Python-библиотеки и более новые JS/TS-инструменты: хотелось увидеть, различаются ли паттерны между поколениями проектов.

Для каждого домена скрипт проверяет три вещи. Отдаёт ли сайт /llms.txt и /llms-full.txt — и какого они размера. Блокирует ли robots.txt пятерых распространённых ботов ИИ: GPTBot (OpenAI), ClaudeBot (Anthropic), PerplexityBot, OAI-SearchBot и YandexBot. И сколько текста реально лежит в HTML главной страницы документации, если не исполнять JavaScript.

Код скрипта: разбор robots.txt и проверка llms.txt

Скрипт целиком — четыре блока ниже подряд, больше в нём ничего нет, зависимостей вне стандартной библиотеки тоже нет. Первый блок — заголовок запроса, список проверяемых ботов и загрузка страницы.

#!/usr/bin/env python3
import json, re, sys, urllib.request, urllib.error

UA = "Mozilla/5.0 (compatible; llms-txt-probe/1.0)"
BOTS = ["GPTBot", "ClaudeBot", "PerplexityBot", "OAI-SearchBot", "YandexBot"]

def fetch(url, timeout=12):
    req = urllib.request.Request(url, headers={"User-Agent": UA})
    try:
        with urllib.request.urlopen(req, timeout=timeout) as r:
            return r.status, r.read().decode("utf-8", "replace")
    except urllib.error.HTTPError as e:
        return e.code, ""
    except Exception as e:
        return None, str(e)

Ошибку сети fetch не роняет, а возвращает None вместо кода ответа — это пригодится дальше, чтобы отличить недоступный файл от файла, который открылся и всё разрешил.

Разбор robots.txt — не библиотечный парсер, а короткая ручная реализация: ищем группу правил для конкретного бота, если её нет — берём общую группу User-agent: *, и смотрим, есть ли в ней Disallow: /.

def robots_allows(robots_txt, bot):
    """Грубый разбор: ищем группу User-agent для бота и смотрим Disallow: /"""
    if not robots_txt:
        return None
    groups, current = {}, []
    for line in robots_txt.splitlines():
        line = line.split("#")[0].strip()
        if not line:
            continue
        k, _, v = line.partition(":")
        k, v = k.strip().lower(), v.strip()
        if k == "user-agent":
            current = [v]
            groups.setdefault(v.lower(), [])
        elif k in ("disallow", "allow") and current:
            groups.setdefault(current[0].lower(), []).append((k, v))
    rules = groups.get(bot.lower())
    if rules is None:
        rules = groups.get("*")
        if rules is None:
            return True
    return not any(k == "disallow" and v == "/" for k, v in rules)

None в возврате — отдельный случай, отличный и от разрешения, и от запрета: файл robots.txt по этому адресу вообще не открылся, вернул 404 или редирект не туда. Я держу это как отдельное значение специально, чтобы не спутать отсутствие файла с файлом, который явно разрешает всё.

Сама проверка страницы собрана в одну функцию: три запроса плюс грубая очистка HTML от тегов и скриптов, чтобы посчитать, сколько текста остаётся, если не выполнять JavaScript, — именно так документацию видит краулер, который не запускает браузер.

def probe(name, base):
    out = {"проект": name, "домен": base}
    st, body = fetch(base.rstrip("/") + "/llms.txt")
    out["llms_txt"] = bool(st == 200 and body and not body.lstrip().startswith("<"))
    out["llms_txt_байт"] = len(body) if out["llms_txt"] else 0
    st_f, body_f = fetch(base.rstrip("/") + "/llms-full.txt")
    out["llms_full_txt"] = bool(st_f == 200 and body_f and not body_f.lstrip().startswith("<"))
    st_r, robots = fetch(base.rstrip("/") + "/robots.txt")
    robots = robots if st_r == 200 else ""
    out["robots"] = {b: robots_allows(robots, b) for b in BOTS}
    st_m, html = fetch(base)
    text = re.sub(r"<script.*?</script>|<style.*?</style>", " ", html or "", flags=re.S | re.I)
    text = re.sub(r"<[^>]+>", " ", text)
    out["текста_в_html"] = len(re.sub(r"\s+", " ", text).strip())
    out["статус_главной"] = st_m
    return out

Проверка not body.lstrip().startswith("<") в третьей строке нужна из-за граблей: часть доменов на несуществующий путь /llms.txt отвечает не 404, а статусом 200 и обычной HTML-заглушкой о том, что страница не найдена, — без этой строки скрипт засчитал бы такой домен как имеющий файл.

Последний блок — точка входа. На вход JSON-список пар из имени проекта и адреса, на выход JSON с результатами.

if __name__ == "__main__":
    targets = json.load(open(sys.argv[1]))
    res = [probe(n, u) for n, u in targets]
    json.dump(res, open(sys.argv[2], "w"), ensure_ascii=False, indent=1)
    print(f"проверено: {len(res)}")

Четыре блока подряд, сохранённые в probe.py, — это рабочий скрипт. Файл targets.json выглядит так: [["FastAPI", "https://fastapi.tiangolo.com"], ["Pydantic", "https://docs.pydantic.dev"]] и дальше по списку. Запуск — python3 probe.py targets.json out.json.

Схема: скрипт делает четыре HTTP-запроса на домен документации — llms.txt, llms-full.txt, robots.txt и главную страницу без JS — и превращает ответы в четыре числа

Один прогон скрипта — четыре запроса на домен, без единого обращения к модели.

Что показал скан 30 сайтов документации

Из 30 проверенных доменов llms.txt отдают 11 (37%), llms-full.txt — 9 (30%). Запрета для ботов ИИ нет нигде: ни одному из пяти на 30 доменах не досталось Disallow: / — ни адресным правилом, ни через общую группу User-agent: *.

С robots.txt пришлось делать второй заход, и первый был кривым. Сначала я проверял файл по тому же адресу, где лежит документация: numpy.org/doc/stable/robots.txt. Так он нашёлся у 14 доменов из 30, а у остальных 16 не нашёлся — и эти 16 были дырой в выводе, потому что отсутствие файла на под-пути не говорит о домене ничего. Поэтому я прогнал robots.txt отдельно по корню каждого домена. В корне файл отдают 23 домена из 30, и ни в одном из них нет запрета для GPTBot, ClaudeBot, PerplexityBot, OAI-SearchBot и YandexBot. Оставшиеся семь — LangChain, Ruff, httpx, SQLAlchemy, Scikit-learn, Vite и Tailwind CSS — не отдали файл и в корне, а значит, запрещать им нечего. Оговорка про под-путь остаётся одной фразой: у проектов вроде NumPy или pandas правила и должны лежать в корне домена, отсутствие своего robots.txt на /doc/stable/ — устройство протокола, а вовсе не упущение команды.

Два домена ответили по-разному в двух прогонах: LangChain и SQLAlchemy в первом заходе отдали robots.txt, во втором по тому же адресу не ответили. Похоже на защиту от частых запросов со стороны CDN. На вывод это не влияет: в том прогоне, где файл открывался, запретов для ботов ИИ в нём не было.

Ещё одна поправка того же рода, и она про мой собственный счёт файлов. Одиннадцать из тридцати — это счёт по корню документации. У Svelte по корню llms.txt нет, а на внутренней странице он отдаётся, 813 байт. То есть моя же развилка между корнем и под-путём сработала дважды: сначала на robots.txt, потом на llms.txt. Точный счёт — одиннадцать проектов по корню и как минимум двенадцатый, если считать под-пути, а полного перебора всех адресов я не делал.

Медиана объёма текста в HTML главной страницы документации — 4115,5 знака: значений тридцать, чётное число, поэтому медиана попадает между двумя соседними замерами. Разброс вокруг неё большой: у Astro текста в HTML — 0 знаков, у PyTorch — 50, у Svelte — 1118. У этих трёх корневая страница документации почти пуста до исполнения JavaScript. Что лежит за ней — статика или такой же клиентский рендер, — по корню сказать нельзя, и я это проверил отдельно; результат разошёлся с тем, что я написал в первой версии разбора.

Проект

llms.txt

llms-full.txt

robots.txt в корне домена

Текст в HTML главной, знаков

FastAPI

доступ открыт

19 828

Pydantic

10 748 байт

есть

доступ открыт

1 900

LangChain

24 170 байт

не отдан

11 043

Polars

доступ открыт

4 964

Ruff

1 773 байт

не отдан

2 872

Django

доступ открыт

10 407

Flask

доступ открыт

6 992

NumPy

доступ открыт

1 974

pandas

доступ открыт

1 770

Requests

доступ открыт

4 327

httpx

не отдан

3 801

SQLAlchemy

не отдан

4 162

Celery

доступ открыт

2 003

Pytest

доступ открыт

5 287

Scikit-learn

не отдан

4 069

PyTorch

387 839 байт

доступ открыт

50

Hugging Face Transformers

67 193 байт

есть

доступ открыт

5 416

Streamlit

67 081 байт

есть

доступ открыт

3 304

Typer

доступ открыт

11 556

Rich

доступ открыт

2 443

Vite

3 507 байт

есть

не отдан

3 870

Svelte

доступ открыт

1 118

Next.js

48 351 байт

есть

доступ открыт

8 939

Tailwind CSS

не отдан

5 504

Prisma

7 736 байт

есть

доступ открыт

10 172

Supabase

доступ открыт

3 522

Astro

доступ открыт

0

Bun

есть

доступ открыт

6 096

Deno

5 053 байт

есть

доступ открыт

6 957

Zod

22 104 байт

есть

доступ открыт

3 273

Колонка robots.txt в таблице — та самая, на которой держится вывод о нулевой блокировке, и в ней стоят результаты по корню домена. «Разрешает всех» — файл открылся и ни одному из пяти ботов ничего не запрещает. «Не отдан» — файла нет и в корне, запрещать нечем.

Размер самого llms.txt тоже разнится сильно: от 1,7 КБ у Ruff до 387 КБ у PyTorch — при том что у PyTorch на главной странице документации без JavaScript всего 50 знаков текста. Получается ситуация, в которой у одного и того же проекта огромный структурированный файл специально для машин соседствует с почти пустой страницей для всех остальных способов чтения.

Таблица позволяет проверить ходовую догадку: мол, llms.txt заводят молодые проекты, а старые библиотеки о нём не слышали. Моя выборка эту догадку не подтверждает. Среди одиннадцати старых широко используемых проектов — Django, Flask, NumPy, pandas, Requests, SQLAlchemy, Celery, Pytest, Scikit-learn, PyTorch, Hugging Face Transformers — файл есть у двух: PyTorch и Hugging Face Transformers, причём у PyTorch он самый большой в выборке. Среди девятнадцати более новых — у девяти, то есть у десяти новых инструментов, включая Astro, Supabase, Tailwind CSS, Svelte, Bun и FastAPI, файла нет.

Перевес в долях есть, 2 из 11 против 9 из 19. Дело в том. чтоllms.txt чаще приезжает в комплекте с современным генератором документации — Mintlify, Nextra, VitePress кладут его из коробки, — а новые проекты чаще берут такой генератор.

График: объём текста в HTML без JavaScript у корня документации и у внутренней страницы — пустой корень у Astro и Svelte наполняется внутри, у PyTorch пусто и там и там

Три проекта из тридцати отдают краулеру почти пустой корень документации — но пустой корень ещё не значит пустую документацию.

Модель советует раскрученное, а не подходящее задаче

Twist и соавторы опубликовали первое исследование предпочтений языковых моделей по языкам и библиотекам (arXiv:2503.17181, ACL Findings 2026, восемь моделей включая GPT-4o, Claude 3.5 и Llama 3.2): NumPy используется моделями избыточно — до 48% случаев его выбор расходится с эталонным решением задачи.

Ещё показательнее выбор языка: модели берут Python в 90–97% бенчмарк-задач и в 58% задач инициализации высокопроизводительных проектов, где Python объективно не оптимален — Rust в тех же тестах не выбран ни разу. Декларируемые предпочтения модели расходятся с реальным кодом в 83% случаев: спросить у модели рекомендацию и посмотреть на написанный ею код — два разных эксперимента с разным результатом. Авторы формулируют вывод жёстко: модели приоритизируют узнаваемость и популярность, а не пригодность инструмента к конкретной задаче.

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

Когда нужного пакета не существует, модель придумывает его имя

Если модель не помнит подходящий пакет, она иногда не признаётся в этом, а называет несуществующий. Это явление называют slopsquatting — по аналогии со squatting, захватом ещё не занятого имени, только здесь захватывают не домен, а вымышленное имя пакета, которое кто-то потом может зарегистрировать со злым умыслом.

Spracklen и соавторы проверили это на 2,23 млн сгенерированных фрагментов кода на 16 моделях для Python и JavaScript (arXiv:2406.10279, USENIX Security 2025): 19,7% рекомендованных пакетов не существуют — 205 474 уникальных выдуманных имени. У коммерческих моделей средний уровень галлюцинаций — 5,2%, у открытых — 21,7%, более чем в четыре раза выше.

Классификация выдуманных имён из того же корпуса показывает, что это не случайный шум: 38% — конфляции двух реальных пакетов (например, jscodeshift и react-codemod сливаются в несуществующий react-codeshift), 13% — опечатки-подобные варианты, 51% — полностью выдуманные названия. Отдельно интересно, что 8,7% галлюцинированных Python-пакетов на деле оказались существующими npm-пакетами — модель путает экосистемы; а совпадений с реально удалёнными из PyPI пакетами 2020–2022 годов — лишь 0,17%, то есть версия «модель просто вспоминает забытое» почти не подтверждается.

Хуже то, что галлюцинации повторяемы. При десятикратном повторе одного и того же промпта 43% выдуманных имён повторялись во всех десяти запусках, 58% — более одного раза. Это делает slopsquatting не теоретическим риском, а предсказуемой и потому выгодной атакой: злоумышленник может спросить модель много раз, найти устойчиво галлюцинируемое имя пакета и зарегистрировать его заранее.

Поэтому важно держать имя пакета и точное написание API однозначными и часто повторёнными в тексте.

Помогает ли llms.txt: 97% файлов не получили ни одного запроса за месяц

Здесь моя гипотеза не подтвердилась данными, и об этом стоит сказать прямо, а не подогнать вывод под ожидания рынка. Файл llms.txt предложил Джереми Ховард (Answer.AI, ранее fast.ai) 3 сентября 2024 года как markdown-файл в корне сайта с кратким описанием проекта и ссылками на важные страницы. 10 августа 2026 года вышла версия 2 спецификации — первая ревизия с момента запуска, добавляющая формальные способы находить markdown-версии страниц через link-заголовки.

Масштабное измерение Ahrefs по 137 210 доменам (данные за май 2026) даёт цифры, которые расходятся с ожиданиями продавцов генераторов llms.txt: 28% доменов публикуют файл, и 97% этих файлов не получили за месяц ни одного запроса.

Знаменатель тут легко перепутать, и путают часто. Оставшиеся 3% — это доля файлов, до которых хоть раз кто-то дошёл, а не доля запросов. Уже внутри запросов к этим трём процентам 96% пришли от ботов, и лишь 19,5% из них — от именованных ИИ-инструментов, где лидируют GPTBot и Claude-Code. Ещё 12% — краулеры сервисов, которые измеряют видимость сайта в ответах нейросетей: их называют GEO-инструментами, от generative engine optimization, по аналогии с SEO. То есть часть и без того редких обращений — это рынок, ходящий проверять сам себя, а не языковая модель, читающая документацию.

Позиция Google звучит ещё прямолинейнее. Джон Мюллер в апреле 2025 года сравнил файл с keywords meta tag: «Насколько мне известно, ни один ИИ-сервис не заявлял об использовании LLMs.TXT, и по логам сервера видно, что они его даже не запрашивают». Позже он подтвердил, что наличие файла на доменах Google не означает одобрения стандарта.

Ходит ещё одно измерение в ту же сторону: шесть месяцев логов, 57 отслеживаемых ИИ-ботов и ни одного запроса к llms.txt — приписывают разработчикам плагина SEO Framework. Первоисточника я не нашёл, цифры гуляют пересказом в стороннем посте, поэтому держу их как слух того же направления, а не как второй независимый замер рядом с Ahrefs.

Я смотрел, отдаёт ли сервер файл, а не кто файл запрашивает. Но картина складывается та же: 11 проектов из 30 потратили ресурс на подготовку файла (для PyTorch это 387 КБ структурированного текста), при том что независимо собранные логи трафика показывают почти нулевой спрос на него со стороны ИИ-ботов.

Краулеры ИИ не выполняют JavaScript — и не видят часть документации

Это самый прямой ответ на исходный вопрос, почему документация не читается моделью: краулер физически не может прочитать то, что рендерится только в браузере. Совместное исследование Vercel и MERJ по логам сети Vercel (17 декабря 2024 года; 569 млн запросов GPTBot и 370 млн запросов ClaudeBot за месяц, вместе около 20% от 4,5 млрд запросов Googlebot за тот же период) даёт однозначный вывод: ни один из основных ИИ-краулеров не рендерит JavaScript — ни GPTBot, ни OAI-SearchBot, ни ClaudeBot, ни Meta-ExternalAgent, ни Bytespider. Файлы .js эти боты иногда скачивают (ChatGPT — в 11,5% запросов, Claude — в 23,84%), но не выполняют. Исключения — Gemini, который использует инфраструктуру Googlebot, и AppleBot с браузерным рендерингом.

Возраст этих данных — слабое место всей конструкции, и я его не прячу: отчёту 21 месяц. За это время спецификация llms.txt успела получить вторую версию, краулеры обновлялись не раз. Публичного замера такого же масштаба посвежее я не нашёл, а мой скан здесь не помощник: он показывает, что отдаёт сервер, а не что исполняет бот. Так что «краулеры не рендерят JavaScript» — лучшее доступное знание конца 2024 года, а не проверенный вчера факт.

Тот же отчёт фиксирует ещё одну странность: даже когда пользователь прямо просит у Claude или ChatGPT «свежие» данные из документации Next.js, в серверных логах nextjs.org зачастую нет немедленного запроса — похоже, модель отвечает по кэшу или обучающим данным, хотя утверждает, что взяла свежие. Это стоит держать в голове отдельно от вопроса про JavaScript: даже полностью статичная и открытая страница не гарантирует, что модель действительно к ней сходила именно сейчас.

Мои три аномалии в таблице — Astro (0 знаков в HTML), PyTorch (50) и Svelte (1118) — выглядят ровно тем случаем, который описывает Vercel: страница валидна, отдаёт 200-й статус, но текст появляется только после исполнения JavaScript в браузере. Для человека это незаметно, браузер отрисует всё за долю секунды. Для краулера, который не рендерит JS, страница пуста независимо от того, есть ли на домене llms.txt и что написано в robots.txt. Правда, корневая страница документации — плохой представитель всей документации, и вторая проба это показала.

Пустой корень — это ещё не пустая документация

Измерять по одной странице на проект было ошибкой метода, и она вылезла сразу, как только я взял вторую страницу. Тем же скриптом я прогнал по одной внутренней странице у трёх аномальных проектов и у трёх контрольных, где на корне текста было много.

Страница

Текст в HTML без JS, знаков

Astro, /en/guides/deploy/

9 485

Svelte, /docs/svelte/what-are-runes

2 670

PyTorch, /docs/stable/generated/torch.nn.Linear.html

73

FastAPI, /tutorial/first-steps/ (контроль)

19 122

Django, /en/5.2/intro/tutorial01/ (контроль)

13 557

Vite, /guide/ (контроль)

9 852

У Astro и Svelte пустым оказался только корень документации — по сути лендинг-оболочка, за которой лежат обычные статические страницы: 9485 и 2670 знаков текста без единой строчки исполненного JavaScript. Это уровень контрольных проектов, и вывод о них я снимаю: их документация краулеру видна.

Аномалия остаётся у PyTorch, и там она хуже, чем выглядела. На корне документации 50 знаков, на внутренней странице справочника по отдельному слою нейросети — 73 знака. Краулеру, который не исполняет JavaScript, страница с сигнатурой класса, параметрами и примером кода отдаёт семь десятков знаков служебного текста. Рядом, на том же домене, лежит llms.txt на 387 КБ — самый большой файл в выборке, который, судя по чужим логам, почти никто не запрашивает.

Обобщение «у трёх проектов из тридцати документация нечитаема» было неверным. Правильное — у одного из тридцати, и увидеть это можно было только второй пробой. Сколько ещё таких PyTorch прячется среди остальных 27, я не знаю: по корневой странице этого не видно, я измерял вход, а не документацию.

Как проверить свой проект одной командой

Проверка, ради которой всё затевалось, умещается в одну строку и делает то же, что скрипт выше: забирает страницу, выкидывает скрипты, стили и теги, считает оставшийся текст. Адрес в примере — та самая страница PyTorch, чтобы результат было с чем сверить; подставьте свой.

curl -sL -A "GPTBot" https://docs.pytorch.org/docs/stable/generated/torch.nn.Linear.html | python3 -c "import re,sys; h=sys.stdin.read(); t=re.sub(r'<script.*?</script>|<style.*?</style>',' ',h,flags=re.S|re.I); t=re.sub(r'<[^>]+>',' ',t); print(len(re.sub(r'\s+',' ',t).strip()))"

Что считать нормой. Медиана по моей выборке — 4115,5 знака, у контрольных внутренних страниц 9–19 тысяч. Несколько тысяч знаков на содержательной странице документации — нормально. Несколько сотен — повод открыть исходник ответа и посмотреть, что туда попало: часто это меню и подвал, а текст статьи дорисовывается отдельно. Несколько десятков, как у PyTorch, — текст целиком рисует браузер, и краулер его не увидит. Гнать команду надо минимум по двум адресам: корню документации и живой внутренней странице. У Astro и Svelte разница между ними оказалась решающей.

Вторая команда — про блокировку, и её достаточно выполнить раз в полгода:

curl -s https://svelte.dev/robots.txt | grep -iE -A3 "gptbot|claudebot|perplexitybot|oai-searchbot|yandexbot|user-agent: \*"

В примере снова живой адрес: так видно, как выглядит нормальный ответ. Норма — отсутствие Disallow: / в выведенных группах. Брать файл надо из корня домена: на под-пути документации его обычно нет вовсе, и пустой ответ оттуда не значит ничего — это ровно те грабли, на которые я наступил в первом прогоне.

Почему статьи на Хабре про llms.txt друг другу противоречат

На Хабре можно найти материалы с прямо противоположными выводами про один и тот же файл. Один разбирает llms.txt скептически и называет его файлом, который никому не нужен. Но делает это без данных о том, почему конкретные проекты всё равно не попадают в советы ИИ: ни смещения по популярности, ни slopsquatting, ни рендеринга JavaScript. Другой утверждает, что «ChatGPT, Perplexity и Claude уже его читают», и обещает эффект на цитируемость в течение одной-четырёх недель после публикации файла.

Второе прямо расходится с внешними данными выше. У Ahrefs 97% файлов не получают ни одного запроса за месяц. Мюллер говорит, что ни один ИИ-сервис не заявлял об использовании файла как входного сигнала.

Разница в том, что авторы генераторов llms.txt и агентства, которые их продают, заинтересованы в том, чтобы файл работал, и легко принимают корреляцию за причину. Проект, который вообще что-то знает о видимости для ИИ, обычно и так делает многое правильно: открытая документация, семантическая разметка, современный стек. Рост цитируемости приписывают последнему добавленному файлу.

Проверить обещание про эффект за неделю напрямую логами трафика я не могу — доступа к серверным логам чужих доменов у меня нет. Но есть чужое независимое измерение с внятной методологией: 137 тысяч доменов, реальные логи запросов. Оно этому обещанию противоречит.

Из трёх проверенных способов работает один, и то у одного проекта из тридцати

Robots.txt не понадобился никому из 30 проектов: в корне файл отдают 23 домена, и ни один не блокирует ботов ИИ. Чинить здесь нечего — проверить у себя одной командой и больше не тратить на это внимание. У своего проекта я это сделал: файл открыт, GPTBot и ClaudeBot ничего не запрещают.

Llms.txt статистически не окупается: его завели 11 проектов из 30, а логи 137 тысяч доменов показывают, что 97% таких файлов за месяц не получили ни одного запроса. Я его, скорее всего, добавлю — подготовка стоит немного времени и хотя бы не вредит. Но не как рецепт видимости и не за счёт часов, которые лучше потратить на текст, читаемый без JavaScript.

Третий способ — отдача текста без JavaScript — единственный, где выборка показала реальную разницу между проектами, но после второй пробы он сильно съёжился. Из трёх подозреваемых двое оказались чистыми: у Astro и Svelte пуст только корень документации. Остался один проект из тридцати, PyTorch, у которого и вход, и внутренняя страница отдают краулеру несколько десятков знаков. Один случай из тридцати — не эпидемия, а редкая и дорогая поломка: пустая страница отсекает саму возможность быть прочитанной, и никакой llms.txt этого не компенсирует.

Ботов я не блокирую, текст без JavaScript отдаю, llms.txt погоды не делает — значит, ни одна из трёх технических причин ко мне не относится, а ассистент всё равно советует не меня. Остаются две, которые лежат внутри модели: перевес в сторону узнаваемого и привычка выдумывать имена вместо признания незнания. Сайтом они не лечатся, и разбирать их придётся кому-то с доступом к весам, а не мне с curl. Что делаю дальше я — гоняю ту же команду по внутренним страницам своей документации раз в релиз, чтобы не превратиться в PyTorch незаметно для себя.

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.