Перейти к основному содержимому

Документация, которую удобно читать агентам: llms.txt и Markdown-копии страниц

· 7 мин

Документацию всё чаще читает не человек, а агент. Разработчик пишет в 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 — не способ попасть в поисковую выдачу, а точка входа для агента, которого направили на вашу документацию.

Как это устроено у нас​

Markdown-копии собираются из готового HTML, а не из исходников. Так в них попадает всё, что видит человек, включая вставленные компоненты.

Документация 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:

docusaurus.config.ts
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:

nginx
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 – обязательно свяжитесь с нами одним из способов: