Третья часть истории о генераторе llms.txt для 1С-Битрикс. Первая — о замысле модуля, вторая — о подключении к живому каталогу с 25 доменами. Эта — о том, как мы упёрлись в пределы синхронного PHP, как наш модуль положил продакшен-сайт собственного кейса и что мы исправили в версии 1.1.0: фоновая генерация без таймаутов, живой прогресс-бар, автодобавление разделов и честный разбор инцидента.
Что сломалось: 504 на большом каталоге
Сценарий, знакомый каждому, кто гонял длинные операции из админки Битрикса: на тестовом каталоге из пяти разделов генерация занимала секунды. На живом каталоге — сотни разделов, тысячи товаров — нажимаешь «Сгенерировать», страница висит минуту и отдаёт 504 Gateway Timeout.
Корень честно инженерный, без мистики:
- Генерация шла одним синхронным POST-запросом из формы настроек.
- nginx между браузером и PHP ждёт ответа бэкенда ограниченное время (
proxy_read_timeout). PHP честно работал — но дольше, чем nginx готов ждать. - Виновник долгой работы — опция проверки ссылок: LinkValidator делал HTTP GET к каждой странице каталога с таймаутом 3 секунды. На каталоге в тысячи страниц это часы.
Вывод, который определил всю архитектуру 1.1.0: «просто AJAX с полосой» это не лечит. Полоса рисуется в браузере, а длинный HTTP-запрос всё равно падает по таймауту. Убирать надо сам длинный запрос.
Решение: фоновый CLI-процесс вместо длинного запроса
В 1.1.0 генерация вынесена в фоновый CLI-процесс. CLI работает без nginx-таймаута, поэтому 504 невозможен в принципе:
[Админка] нажали «Сгенерировать»
→ AJAX start_gen: exec("php cli_gen.php docRoot siteId &") → ответ сразу
→ фон: Agent::generateForSite() пишет прогресс в файл
[Браузер] каждые 1 с AJAX gen_status → читает файл → двигает полосу %
→ running=false → «Готово» или ошибка
Новые детали реализации:
cli_gen.php— минимальный CLI-бутстрап Битрикса в корне модуля: принимает корень сайта и ID сайта, поднимает ядро, запускает генерацию. В корень сайта не копируется — AJAX вызывает его по абсолютному пути.lib/Progress.php— файловый счётчик в/upload/eu.llmtxtautomata/.progress_<LID>.json:{total, done, pct, stage, running, error, ts}. Есть защита от «зависшего» счётчика: если файл старше часа, запуск считается мёртвым.- Защита от параллельных запусков: повторный клик по «Сгенерировать» блокируется по флагу
runningв прогресс-файле. - Путь к php-cli определяется автоматически (
command -v php→/usr/bin/php→PHP_BINARY). - Fallback без
exec(): если хостинг запретилexecвdisable_functions, модуль переключается на прежний синхронный режим — с явным предупреждением, что на большом каталоге возможен таймаут. Для таких сайтов штатная рекомендация — авто-агент по cron: он и раньше работал в CLI, где таймаутов нет.
Прогресс-бар с честными процентами
Полоса в админке показывает не «искусственную» анимацию, а реальное состояние генератора:
- тик прогресса — по каждому разделу маппинга, реально добавившему контент;
totalсчитается какразделы × проходы × 2, где проходы — регионы Аспро плюс основной домен, а двойка — кураторскийllms.txtи расширенныйllms-full.txt;- рядом с процентами выводится текущая стадия — название раздела, который собирается прямо сейчас.
На каталоге из 14 разделов с 24 региональными поддоменами это сотни тиков: полоса двигается непрерывно, и видно, что процесс живой, а не завис.
«Добавить все разделы ИБ» одной кнопкой
Раньше маппинг «раздел → секция llms.txt» набивали руками: строку за строкой. В 1.1.0 в настройках появился блок «Добавить все разделы ИБ»:
- вводите ID инфоблока → AJAX подтягивает список активных разделов с реальными названиями и сортировкой;
- галочка «включая вложенные» — берём либо только корневые (
DEPTH_LEVEL=1), либо всю глубину; - каждая строка предзаполнена: ID, заголовок H2, сортировка, лимиты; галочки «активна», «с элементами», «с описанием» уже стоят;
- если разделов больше 200 — модуль попросит подтверждение.
Практическая рекомендация из тестов: либо 5 верхних разделов с вложенными, либо только конечные подразделы без родителей — родитель с включёнными вложенными соберёт смесь «остатков». Порядок секций задаёт поле «Сорт.».
Инцидент, о котором надо рассказать: self-DDoS
Самый поучительный эпизод разработки. На первом тесте фоновой генерации продакшен-сайт кейса упал: 502, сайт недоступен. Причина — собственная опция проверки ссылок в связке с фоновым режимом:
- LinkValidator делал HTTP GET с таймаутом 3 секунды к каждой странице своего же сайта.
- Расширенный режим
llms-full.txtудваивал объём генерации. - Раньше спасал таймаут nginx — запрос умирал, не успев навредить. Фоновый CLI без таймаута пробегал весь каталог до конца: сервер получал сотни самообращений и ложился.
Классический self-DDoS: защиту от внешней атаки сайт выдержал бы, а от собственного модуля — нет. Сайт подняли (остановка фонового процесса, рестарт php-fpm, снятие галочек), после чего зафиксировали архитектурные хотфиксы в 1.1.0:
llms-full.txtполностью пропускает HTTP-валидацию — контент для него проверяется по базе и тексту, без обхода страниц;check_linksвалидирует только кураторскийllms.txt, а не оба файла;- таймаут LinkValidator снижен с 3 до 2 секунд, дубликаты URL отбрасываются до валидации;
- опция проверки ссылок выключена по умолчанию и снабжена красным предупреждением в интерфейсе.
Урок сформулируем так: фоновый режим снимает таймауты не только с полезной работы, но и с вредной. Убирая ограничитель, проверь, что именно ты разгоняешь.
Чистота вывода
Параллельно закрыли накопившиеся шероховатости текста в обоих файлах:
- пустые секции не пишутся — заголовок добавляется «оптимистично» и откатывается, если раздел не дал контента;
- HTML-сущности декодируются (
html_entity_decode+ замена неразрывных пробелов) — в файлах больше нет ; - Aspro-макросы срезаются: служебные подстановки вида
#REGION_TAG_…#и#CITY#не попадают в текст; - обрезка анонсов — по границе слова с многоточием, а не посередине слова на 150-м символе;
- список поддоменов заполняется сам: модуль читает инфоблок регионов Аспро и подставляет живой список — на кейсе это основной домен плюс 24 поддомена без ручного ввода.
Тест на живом каталоге
Финальный прогон — на том же магазине с региональной сетью (инфоблок 11, 24 поддомена):
- генерация без 504, полоса с процентами работает;
- оба файла собираются:
llms.txtиllms-full.txtс описаниями до ~1000 символов; - пустых секций,
и макросов в выводе нет, обрезка аккуратная; php -lпо всем изменённым файлам — чисто (проверяли в docker-контейнере php:8.1-cli);- пакет
eu.llmtxtautomata-1.1.0.zipсобран и готов к повторной подаче на модерацию Маркетплейса.
Кому это нужно
Модуль решает одну задачу — делает сайт на 1С-Битрикс читаемым для нейросетей: ChatGPT, Perplexity, Яндекс Нейро. Версия 1.1.0 добавляет то, чего не хватало взрослым каталогам:
- большим магазинам — генерация любых объёмов без таймаутов и с наблюдаемым прогрессом;
- сетям региональных сайтов на Аспро — автособор поддоменов и раздельные файлы по регионам;
- компаниям без программиста в штате — настройка мышкой: все разделы добавляются кнопкой, cron-агент обновляет файлы сам.
Цена модуля — 4 500 ₽ единоразово через Маркетплейс 1С-Битрикс, установка под ключ — 5 000 ₽. Пока идёт модерация, напишите нам — поставим и настроим вручную. О том, как llms.txt встраивается в общую стратегию продвижения — в большом руководстве SEO для Bitrix, а готовность сайта к AI-поиску можно бесплатно проверить экспресс-аудитом GEO-индекса.
Достоверность: статья написана разработчиком модуля — eu.llmtxtautomata наш коммерческий продукт, это наш интерес. Факты инцидента и хотфиксов — из реального лога разработки и тестов на живом проекте.
Частые вопросы
Почему при генерации llms.txt возникала ошибка 504? Генерация шла одним синхронным POST-запросом из админки. На каталоге в сотни и тысячи разделов запрос длится дольше, чем nginx ждёт ответа от PHP (proxy_read_timeout), — и nginx возвращает 504 Gateway Timeout. В версии 1.1.0 генерация запускается фоновым CLI-процессом без nginx-таймаута, поэтому 504 невозможен в принципе.
Что нового в модуле eu.llmtxtautomata 1.1.0? Фоновая генерация через CLI с живым прогресс-баром в процентах, кнопка «Добавить все разделы ИБ» с автоподстановкой названий, лечение инцидента self-DDoS при проверке ссылок, чистый вывод без пустых секций и HTML-сущностей, автозаполнение списка поддоменов Aspro из инфоблока регионов.
Что такое self-DDoS при проверке ссылок? Опция проверки ссылок делала HTTP GET к каждой странице собственного сайта с таймаутом 3 секунды. На большом каталоге сервер получал сотни запросов сам к себе и ложился. В 1.1.0 llms-full.txt вообще не проходит HTTP-валидацию, проверка осталась только для кураторского llms.txt, таймаут снижен до 2 секунд, а опция по умолчанию выключена.
Модуль работает, если на хостинге запрещён exec()? Да. Если exec в disable_functions, модуль автоматически переключается на синхронный режим с предупреждением, что на очень большом каталоге возможен таймаут. Для таких сайтов рекомендуем генерацию авто-агентом по cron — это штатный механизм Битрикса для длинных задач.
Сколько стоит модуль и когда он появится в Маркетплейсе? Цена — 4 500 ₽ единоразово через Маркетплейс 1С-Битрикс, установка под ключ — 5 000 ₽. Версия 1.1.0 собрана и готовится к повторной подаче на модерацию; следите за обновлениями в блоге.
Об авторе. Евгений Урядов, ИП, занимается интеграциями на 1С-Битрикс с 2012 года. Автор модулей eu.weightsync (синхронизация веса и остатков между сайтом и CRM Битрикс24) и eu.llmtxtautomata (генератор llms.txt). Специализация — связка «Битрикс + современные фронтенды» и оптимизация сайтов под нейросетевые поисковики. Нашли неточность? Напишите на hello@automata.sale — поправим и укажем дату правки.
📞 +7 (906) 311-77-69 · ✉ hello@automata.sale · 💬 Telegram: @automatasale · 🌐 automata.sale
ИП Урядов Евгений Евгеньевич · ИНН 645112058391 · ОГРНИП 312645301900058 Работаем по России, Беларуси и Казахстану