[Блог](https://tuna.am/blog.md)|[Как устроено](https://tuna.am/blog/tags/engineering)

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

28 октября · 7 мин

Документацию всё чаще читает не человек, а агент. Разработчик пишет в Claude Code или Cursor «опубликуй мой localhost:3000 через tuna под паролем», и агент идёт за ответом на tuna.am. Там его ждёт HTML-страница: меню, скрипты, стили, иконки, а где-то посередине — нужная команда. Мы сделали так, чтобы агенту не приходилось продираться через вёрстку. У каждой страницы документации есть Markdown-копия, а оглавление всей документации лежит в одном файле.

Замените последний слеш в адресе на `.md` — и увидите страницу такой, какой её видит агент:

```text
https://tuna.am/docs/tunnels/http/  →  https://tuna.am/docs/tunnels/http.md

```

> **Коротко**
>
> * Оглавление документации — [tuna.am/llms.txt](https://tuna.am/llms.txt): больше 250 страниц одним списком, со ссылками и описаниями. Вся документация одним файлом — [llms-full.txt](https://tuna.am/llms-full.txt).
> * У любой страницы документации и блога есть Markdown-копия: последний слеш в адресе меняется на `.md`. Английская версия — с префиксом `/en/`.
> * Агент, который просит Markdown заголовком `Accept: text/markdown`, получает его по обычному адресу страницы.
> * Хотите, чтобы агент работал с Tuna по документации, — дайте ему ссылку на `llms.txt`.

Если ваш агент работает с Tuna и что-то в документации понимает не так, [напишите нам](https://tuna.am/docs/support.md). Дальше — зачем всё это агентам, как устроено у нас и как повторить то же на своём Docusaurus.

## Зачем агенту Markdown

Страница про [HTTP-туннель](https://tuna.am/docs/tunnels/http.md) в HTML весит 101 КБ, её Markdown-копия — 31 КБ. Разница в три с лишним раза — это меню, скрипты, классы и служебные атрибуты. Агенту они не нужны, но токены на них тратятся, а контекст забивается.

Markdown для языковой модели — родной формат: заголовки, списки, таблицы и блоки кода с указанием языка она понимает без подсказок. Агенты и сами предпочитают его: в [исследовании Vercel](https://vercel.com/kb/guide/make-your-documentation-readable-by-ai-agents) 65% веб-запросов агентов просили Markdown.

## Что такое llms.txt и кто его на самом деле читает

[llms.txt](https://llmstxt.org/) предложил Джереми Говард в сентябре 2024 года. Это Markdown-файл в корне сайта: заголовок с названием, короткое описание в цитате и разделы со списками ссылок. Раздел `Optional` по соглашению хранит второстепенное — его агент может пропустить, если контекст на исходе. Там же описано соглашение о Markdown-копиях: у страницы есть версия с `.md` в адресе, а HTML ссылается на неё через `<link rel="alternate" type="text/markdown">`.

Фрагмент нашего файла:

```markdown
# Tuna Документация

> Документация сервиса туннелей tuna.am

## Blog

- [📒 Туннель для 1С — база в интернете одной командой](https://tuna.am/blog/1c-tunnel.md): Новая команда tuna 1c публикует базу 1С:Предприятие…

```

Поисковые и обучающие краулеры почти не заходят на подобные файлы — по данным Ahrefs в [пересказе PPC Land](https://ppc.land/llms-txt-adoption-rises-8-8x-but-97-of-files-get-zero-ai-requests/), в мае 2026 года 97% файлов llms.txt на 137 тысячах доменов не получили ни одного запроса от ИИ-систем. В [логах dejan.ai](https://dejan.ai/blog/crawler-census-robots-llms-okf/) за 20 дней краулеры Google, Anthropic и OpenAI запросили robots.txt 1150 раз, а llms.txt — четыре.

Но это нормально: автор предложения с самого начала рассчитывал, что файл пригодится в момент ответа на вопрос, а не для обучения моделей. llms.txt — не способ попасть в поисковую выдачу, а точка входа для агента, которого направили на вашу документацию.

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

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

Документация Tuna собирается на Docusaurus. Markdown-копии делает плагин [@signalwire/docusaurus-plugin-llms-txt](https://github.com/signalwire/docusaurus-plugins/tree/main/packages/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 агент выбирал бы наугад. Теперь метка стоит внутри своей вкладки:

```markdown
**Windows**

### Установка с помощью winget

```

**Лишнее экранирование.** Генератор Markdown экранировал каждый `_` и `&`, и переменная `TUNA_TOKEN` превращалась в `TUNA\_TOKEN`. Хуже всего с адресами: ссылку с `\&` в UTM-метках агент берёт буквально и ломает. Снимаем экранирование там, где символ по CommonMark не может стать разметкой: `_` внутри слова и `&`, за которым не идёт ссылка на символ.

**Комментарии React.** При серверной отрисовке React вставляет `<!-- -->` между соседними текстовыми узлами, и конвертер переносил их как есть: `user<!-- -->:pass<!-- -->`. Вырезаем.

**Плашки, которые теряли смысл.** Плашки «Примечание», «Подсказка» и «Осторожно» в HTML — это блоки с иконкой и заголовком. В Markdown от них оставалась одинокая строка «примечание» над обычными абзацами, и агент не отличал предупреждение от текста вокруг. Теперь плашка — это цитата с заголовком:

```markdown
> **Примечание.** Все флаги с подсказками — в справке:

```

**nginx не знал про `.md`.** Без MIME-типа файл приходил как `application/octet-stream`, и браузер скачивал страницу вместо того, чтобы показать. Добавили тип.

## Попробуйте сами

```shell
curl https://tuna.am/llms.txt

```

```shell
curl https://tuna.am/docs/tunnels/http.md

```

```shell
curl -H "Accept: text/markdown" https://tuna.am/docs/tunnels/http/

```

Или дайте задачу агенту:

```text
Прочитай https://tuna.am/llms.txt, найди, как опубликовать локальный
сервер через tuna, и открой localhost:3000 под паролем.

```

## Как повторить на своём Docusaurus

**1. Подключите плагин.** Если в конфиге `trailingSlash: true`, берите ветку 2.x:

docusaurus.config.ts

```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

```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, или наоборот вы недовольны чем либо, то пожалуйста [оставьте отзыв](https://my.tuna.am/?show_feedback=1&lang=ru).

![](/blog/img/2024-08-22/feedback.gif)

## Новости

Анонсы новых функций и статей публикуем в [Telegram-канале](https://t.me/tuna_am). Подписывайтесь, чтобы ничего не пропустить.

## Помощь

Мы ценим наших пользователей и детально изучаем все обращения, если у вас возникли проблемы с tuna – обязательно свяжитесь с нами одним из способов:

* [Электронная почта](mailto:info@tuna.am)
* [Telegram сообщество](https://telegram.me/+Kgbtt0_gJwI5ZmEy)

Категории: [Как устроено](https://tuna.am/blog/tags/engineering)

[старее →🗞️ Что нового в Tuna — тёмная тема, адреса для файрвола и переезд Linux-пакетов](https://tuna.am/blog/whats-new-october-2026.md)
