Документация, которую удобно читать агентам: llms.txt и Markdown-копии страниц
Документацию всё чаще читает не человек, а агент. Разработчик пишет в Claude Code или Cursor «опубликуй мой localhost:3000 через tuna под паролем», и агент идёт за ответом на tuna.am. Там его ждёт HTML-страница: меню, скрипты, стили, иконки, а где-то посередине — нужная команда. Мы сделали так, чтобы агенту не приходилось продираться через вёрстку. У каждой страницы документации есть Markdown-копия, а оглавление всей документации лежит в одном файле.
Замените последний слеш в адресе на .md — и увидите страницу такой, какой её видит агент:
https://tuna.am/docs/tunnels/http/ → https://tuna.am/docs/tunnels/http.md
- Оглавление документации — tuna.am/llms.txt: больше 250 страниц одним списком, со ссылками и описаниями. Вся документация одним файлом — llms-full.txt.
- У любой страницы документации и блога есть Markdown-копия: последний слеш в адресе меняется на
.md. Английская версия — с префиксом/en/. - Агент, который просит Markdown заголовком
Accept: text/markdown, получает его по обычному адресу страницы. - Хотите, чтобы агент работал с Tuna по документации, — дайте ему ссылку на
llms.txt.
Если ваш агент работает с Tuna и что-то в документации понимает не так, напишите нам. Дальше — зачем всё это агентам, как устроено у нас и как повторить то же на своём Docusaurus.
Зачем агенту Markdown
Страница про HTTP-туннель в HTML весит 101 КБ, её Markdown-копия — 31 КБ. Разница в три с лишним раза — это меню, скрипты, классы и служебные атрибуты. Агенту они не нужны, но токены на них тратятся, а контекст забивается.
Markdown для языковой модели — родной формат: заголовки, списки, таблицы и блоки кода с указанием языка она понимает без подсказок. Агенты и сами предпочитают его: в исследовании Vercel 65% веб-запросов агентов просили Markdown.
Что такое llms.txt и кто его на самом деле читает
llms.txt предложил Джереми Говард в сентябре 2024 года. Это Markdown-файл в корне сайта: заголовок с названием, короткое описание в цитате и разделы со списками ссылок. Раздел Optional по соглашению хранит второстепенное — его агент может пропустить, если контекст на исходе. Там же описано соглашение о Markdown-копиях: у страницы есть версия с .md в адресе, а HTML ссылается на неё через <link rel="alternate" type="text/markdown">.
Фрагмент нашего файла:
# Tuna Документация
> Документация сервиса туннелей tuna.am
## Blog
- [📒 Туннель для 1С — база в интернете одной командой](https://tuna.am/blog/1c-tunnel.md): Новая команда tuna 1c публикует базу 1С:Предприятие…
Поисковые и обучающие краулеры почти не заходят на подобные файлы — по данным Ahrefs в пересказе PPC Land, в мае 2026 года 97% файлов llms.txt на 137 тысячах доменов не получили ни одного запроса от ИИ-систем. В логах dejan.ai за 20 дней краулеры Google, Anthropic и OpenAI запросили robots.txt 1150 раз, а llms.txt — четыре.
Но это нормально: автор предложения с самого начала рассчитывал, что файл пригодится в момент ответа на вопрос, а не для обучения моделей. llms.txt — не способ попасть в поисковую выдачу, а точка входа для агента, которого направили на вашу документацию.
Как это устроено у нас
Документация Tuna собирается на Docusaurus. Markdown-копии делает плагин @signalwire/docusaurus-plugin-llms-txt. После сборки он берёт готовый HTML каждой страницы, превращает его в Markdown и кладёт рядом, а заодно собирает llms.txt и llms-full.txt для обеих локалей.
Почему из HTML, а не из исходников. Наша документация плотно написана на MDX — больше сотни .mdx-файлов с import и без. Конвертер по исходникам выбросил бы компоненты и наши страницы стали бы менее информативными для агентов.
Почему альфа-версия плагина. Стабильная 1.2.2 с нашей настройкой trailingSlash: true ломала все внутренние ссылки: из /docs/foo/ получалось /docs/foo/.md. В 2.0.0-alpha.7 это исправлено, и появились хуки для своих rehype-плагинов, без которых остальное не сделать.
После сборки скрипт добавляет в <head> каждой страницы ссылку на её копию — агент найдёт её, даже если не знает про соглашение с .md. А nginx отдаёт копии с правильным типом и, если агент просит Markdown заголовком Accept, подставляет копию по обычному адресу страницы.
Грабли
Включить плагин легко. Первый Markdown после конвертера выглядел прилично — пока мы не начали читать его глазами.
Якоря в заголовках. Docusaurus добавляет к каждому заголовку ссылку-якорь #. В Markdown она переезжала как есть, вырезаем.
Блоки кода без языка. Docusaurus хранит язык блока на <pre>, а конвертер ищет его на <code>. Больше 700 блоков кода теряли пометку shell, yaml или ini — переносим класс с <pre> на <code>.
Вкладки, которые склеивались. Метки вкладок уходили отдельным списком, а содержимое всех вкладок шло подряд. На блоках с командами для AMD64 и ARM64 агент выбирал бы наугад. Теперь метка стоит внутри своей вкладки:
**Windows**
### Установка с помощью winget
Лишнее экранирование. Генератор Markdown экранировал каждый _ и &, и переменная TUNA_TOKEN превращалась в TUNA\_TOKEN. Хуже всего с адресами: ссылку с \& в UTM-метках агент берёт буквально и ломает. Снимаем экранирование там, где символ по CommonMark не может стать разметкой: _ внутри слова и &, за которым не идёт ссылка на символ.
Комментарии React. При серверной отрисовке React вставляет <!-- --> между соседними текстовыми узлами, и конвертер переносил их как есть: user<!-- -->:pass<!-- -->. Вырезаем.
Плашки, которые теряли смысл. Плашки «Примечание», «Подсказка» и «Осторожно» в HTML — это блоки с иконкой и заголовком. В Markdown от них оставалась одинокая строка «примечание» над обычными абзацами, и агент не отличал предупреждение от текста вокруг. Теперь плашка — это цитата с заголовком:
> **Примечание.** Все флаги с подсказками — в справке:
nginx не знал про .md. Без MIME-типа файл приходил как application/octet-stream, и браузер скачивал страницу вместо того, чтобы показать. Добавили тип.
Попробуйте сами
curl https://tuna.am/llms.txt
curl https://tuna.am/docs/tunnels/http.md
curl -H "Accept: text/markdown" https://tuna.am/docs/tunnels/http/
Или дайте задачу агенту:
Прочитай https://tuna.am/llms.txt, найди, как опубликовать локальный
сервер через tuna, и открой localhost:3000 под паролем.
Как повторить на своём Docusaurus
1. Подключите плагин. Если в конфиге trailingSlash: true, берите ветку 2.x:
plugins: [
[
'@signalwire/docusaurus-plugin-llms-txt',
{
markdown: {
enableFiles: true,
// Абсолютные ссылки агент откроет без базового адреса.
relativePaths: false,
includeDocs: true,
includeBlog: true,
includePages: true,
excludeRoutes: ['**/search', '**/tags/**', '**/page/**'],
beforeDefaultRehypePlugins: [rehypeCleanup],
},
llmsTxt: {
enableLlmsFullTxt: true,
siteTitle: 'Документация проекта',
siteDescription: 'Коротко о том, что это за проект',
},
},
],
],
2. Прочитайте свой Markdown глазами. Якоря, язык блоков кода, вкладки, плашки, экранирование — у каждой документации свой набор мусора. Чинить его удобнее всего своим rehype-плагином в beforeDefaultRehypePlugins, пока страница ещё HTML.
3. Научите веб-сервер отдавать Markdown. В mime.types добавьте строку text/markdown md;. В конфиг nginx — кодировку и выбор копии по Accept:
charset utf-8;
charset_types text/markdown text/plain;
map $http_accept $markdown_requested {
default 0;
"~*text/markdown" 1;
}
map "$markdown_requested$uri" $markdown_twin {
default /.no-markdown-twin;
"~^1(?<page>/.+)/$" $page.md;
}
map $markdown_requested $markdown_vary {
default "";
1 Accept;
}
map $markdown_requested $markdown_cache_control {
default "";
1 "private, no-store";
}
server {
# ...
location ~ /$ {
try_files $markdown_twin ${uri}index.html =404;
add_header Vary $markdown_vary;
add_header Cache-Control $markdown_cache_control;
}
}
Если перед сайтом стоит CDN, проверьте, что он не кэширует ответы с no-store, или настройте для запросов с Accept: text/markdown отдельное правило.
4. Добавьте ссылку на копию в <head>. У нас это делает короткий скрипт после сборки: для каждого index.html, рядом с которым есть .md, он вставляет <link rel="alternate" type="text/markdown" href="...">.
Оставьте отзыв
Если вам нравится пользоваться Tuna, или наоборот вы недовольны чем либо, то пожалуйста оставьте отзыв.
Новости
Анонсы новых функций и статей публикуем в Telegram-канале. Подписывайтесь, чтобы ничего не пропустить.
Помощь
Мы ценим наших пользователей и детально изучаем все обращения, если у вас возникли проблемы с tuna – обязательно свяжитесь с нами одним из способов: