Сокращаем расходы на AI-агентов на 98%: Cloudflare внедряет новый стандарт ошибок

AI-агенты больше не эксперименты. Они стали производственной инфраструктурой, выполняющей миллиарды HTTP-запросов в день, перемещаясь по сети, обращаясь к API и управляя сложными рабочими процессами.

Но когда эти агенты сталкиваются с ошибкой, они всё ещё получают те же HTML-страницы с ошибками, которые мы создавали для браузеров: сотни строк разметки, CSS и текста, предназначенные для человеческого восприятия. Эти страницы дают агентам намёки, а не инструкции, и тратят впустую время и токены. Этот разрыв — возможность давать агентам инструкции, а не создавать препятствия.

Начиная с сегодняшнего дня, Cloudflare возвращает AI-агентам структурированные полезные данные об ошибках в формате Markdown и JSON, соответствующие RFC 9457, заменяя тяжёлые HTML-страницы машиночитаемыми инструкциями.

Это означает, что когда агент отправляет заголовки Accept: text/markdown, Accept: application/json или Accept: application/problem+json и сталкивается с ошибкой Cloudflare, мы возвращаем один семантический контракт в структурированном формате вместо HTML. И он поставляется вместе с практическими рекомендациями. (Это развивает наше недавнее обновление Markdown for Agents.)

Таким образом, вместо того чтобы просто услышать «Вы заблокированы», агент прочитает: «Вы превысили лимит запросов — подождите 30 секунд и повторите попытку с экспоненциальной задержкой». Вместо просто «Доступ запрещён» агенту будет дана инструкция: «Эта блокировка намеренная: не повторяйте запрос, обратитесь к владельцу сайта».

Эти ответы не только понятнее — они значительно эффективнее. Структурированные ответы об ошибках сокращают размер полезной нагрузки и использование токенов более чем на 98% по сравнению с HTML, если измерять на примере живого ответа с ошибкой 1015 («превышен лимит запросов»). Для агентов, которые сталкиваются с несколькими ошибками в рабочем процессе, экономия быстро накапливается.

Это работает по всей сети Cloudflare автоматически. Владельцам сайтов не нужно ничего настраивать. Браузеры по-прежнему получают тот же HTML-интерфейс, что и раньше.

Это не просто страницы ошибок. Это инструкции для агентной сети.

Что агенты видят сегодня

Когда агент получает ошибку, сгенерированную Cloudflare, это обычно означает, что Cloudflare применяет политику клиента или возвращает ответ платформы от его имени — а не то, что Cloudflare не работает. Эти ответы срабатывают, когда запрос не может быть обработан как есть, например, из-за неверного хоста или проблем с маршрутизацией DNS, контроля доступа, определённого клиентом (WAF, гео-, ASN- или правила для ботов), или ограничений, применяемых на границе сети, таких как лимит запросов. Короче говоря, Cloudflare действует как уровень маршрутизации и безопасности клиента, и ответ объясняет, почему запрос был заблокирован или не может быть выполнен.

Сегодня эти ответы отображаются в виде HTML, созданного для людей:

<!DOCTYPE html>
<html>
<head>
<title>Access denied | example.com used Cloudflare to restrict access</title>
<style>/* 200 lines of CSS */</style>
</head>
<body>
  <div class="cf-wrapper">
    <h1 data-translate="block_headline">Sorry, you have been blocked</h1>
    <!-- ... hundreds more lines ... -->
  </div>
</body>
</html>

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

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

Правила пользовательских ошибок (Custom Error Rules) могут настраивать многие ошибки Cloudflare, включая некоторые случаи с кодом 1xxx. Но они зависят от конфигурации конкретного сайта, поэтому не могут служить универсальным контрактом для агентов по всей сети. Cloudflare находится на пути запроса. Это означает, что мы можем определить ответ машины по умолчанию: повторить попытку или остановиться, подождать и отступить, эскалировать или перенаправить. Страницы ошибок перестают быть украшением и становятся инструкциями к выполнению.

Что мы сделали

Теперь Cloudflare возвращает структурированные ответы, соответствующие RFC 9457, для всех путей ошибок класса 1xxx — это коды ошибок платформы Cloudflare для сбоев на стороне границы сети, таких как проблемы с разрешением DNS, отказы в доступе и ограничения запросов. Оба формата активны: Accept: text/markdown возвращает Markdown, Accept: application/json возвращает JSON, а Accept: application/problem+json возвращает JSON с типом содержимого application/problem+json.

На сегодня это покрывает все ошибки класса 1xxx. В дальнейшем тот же контракт будет распространён на сгенерированные Cloudflare ошибки 4xx и 5xx.

Ответы в Markdown состоят из двух частей:

  • YAML-фронтмэттер (frontmatter) для машиночитаемых полей

  • текстовые разделы для явных инструкций (Что произошло и Что вам следует сделать)

Ответы в JSON содержат те же поля в виде плоского объекта.

YAML-фронтмэттер — это критически важный слой для автоматизации. Он позволяет агенту извлекать стабильные ключи, не парся HTML и не угадывая намерения по тексту. Поля, такие как error_code, error_name и error_category, позволяют агенту классифицировать сбой. retryable и retry_after управляют логикой повторных попыток с задержкой. owner_action_required указывает агенту, нужно ли продолжать попытки или эскалировать проблему. ray_id, timestamp и zone делают логи и передачу в службу поддержки детерминированными.

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

Эта стабильность — не изобретение Cloudflare. RFC 9457 — Детали проблем для HTTP API определяет стандартную структуру JSON для сообщения об ошибках по HTTP, чтобы клиенты могли анализировать ответы об ошибках, не зная заранее конкретный API. Наши ответы в JSON следуют этой структуре, что означает, что любой HTTP-клиент, понимающий «Детали проблем», может разобрать основные члены без Cloudflare-специфичного кода:

Член RFC 9457

Что содержит

type

URI, указывающий на документацию Cloudflare для конкретного кода ошибки

status

Код состояния HTTP (совпадает с фактическим статусом ответа)

title

Краткое, понятное человеку описание проблемы

detail

Понятное человеку объяснение, специфичное для данного случая

instance

Ray ID, идентифицирующий этот конкретный случай ошибки

Операционные поля — error_code, error_category, retryable, retry_after, owner_action_required и другие — являются расширяющими членами RFC 9457. Клиенты, которые их не распознают, просто игнорируют их.

Это работает по всей сети и является дополнительной функцией. Владельцам сайтов не нужно ничего настраивать. Браузеры продолжают получать HTML, если клиенты явно не запрашивают Markdown или JSON.

Как выглядит ответ

Вот как выглядит ошибка превышения лимита запросов (1015) в JSON:

{
  "type": "https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-1xxx-errors/error-1015/",
  "title": "Error 1015: You are being rate limited",
  "status": 429,
  "detail": "You are being rate-limited by the website owner's configuration.",
  "instance": "9d99a4434fz2d168",
  "error_code": 1015,
  "error_name": "rate_limited",
  "error_category": "rate_limit",
  "ray_id": "9d99a4434fz2d168",
  "timestamp": "2026-03-09T11:11:55Z",
  "zone": "<YOUR_DOMAIN>",
  "cloudflare_error": true,
  "retryable": true,
  "retry_after": 30,
  "owner_action_required": false,
  "what_you_should_do": "**Wait and retry.** This block is transient. Wait at least 30 seconds, then retry with exponential backoff.nnRecommended approach:n1. Wait 30 seconds before your next requestn2. If rate-limited again, double the wait time (60s, 120s, etc.)n3. If rate-limiting persists after 5 retries, stop and reassess your request pattern",
  "footer": "This error was generated by Cloudflare on behalf of the website owner."
}

Та же ошибка в Markdown, оптимизированная для рабочих процессов, ориентированных на модели:

---
error_code: 1015
error_name: rate_limited
error_category: rate_limit
status: 429
ray_id: 9d99a39dc992d168
timestamp: 2026-03-09T11:11:28Z
zone: <YOUR_DOMAIN>
cloudflare_error: true
retryable: true
retry_after: 30
owner_action_required: false
---

# Ошибка 1015: Ограничение частоты запросов

## Что произошло

Вас ограничила по частоте запросов конфигурация владельца веб-сайта.

## Что делать

**Подождите и повторите попытку.** Это временная блокировка. Подождите как минимум 30 секунд, затем повторите попытку с экспоненциальной задержкой.

Рекомендуемый подход:
1. Подождите 30 секунд перед следующим запросом
2. Если ограничение снова сработает, увеличьте время ожидания в два раза (60с, 120с и т.д.)
3. Если ограничение сохраняется после 5 попыток, остановитесь и пересмотрите модель ваших запросов

---
Эта ошибка сгенерирована Cloudflare от имени владельца веб-сайта.

Оба формата дают агенту всё необходимое для принятия решения и действия: классифицировать ошибку, выбрать стратегию повтора и определить, требуется ли эскалация. Так выглядит контракт по умолчанию для машин — не конфигурация для каждого сайта, а единое поведение для всей сети. Различие явно прослеживается между семействами ошибок: временная ошибка вроде 1015 говорит «подожди и повтори», в то время как преднамеренные блокировки вроде 1020 или географические ограничения вроде 1009 указывают агенту не повторять попытки, а вместо этого эскалировать проблему.

Один контракт, два формата

Основная ценность не в выборе формата. Она в семантической стабильности.

Агентам нужны детерминированные ответы на операционные вопросы: повторять ли запрос, как долго ждать и требуется ли эскалация. Cloudflare раскрывает один политический контракт через два формата передачи данных. Независимо от того, потребляет клиент Markdown или JSON, операционное значение идентично: тот же идентификатор ошибки, те же сигналы о повторе/задержке, те же инструкции по эскалации.

Клиенты, которые отправляют Accept: application/problem+json, получают ответ application/problem+json; charset=utf-8 — полезно для HTTP-библиотек, которые диспетчеризируют по типу медиа. Клиенты, отправляющие Accept: application/json, получают application/json; charset=utf-8 — то же тело, безопасный вариант по умолчанию для существующих потребителей.

Сокращение размера и эффективность токенов

Этот контракт также значительно меньше того, что он заменяет. HTML-страницы ошибок Cloudflare ориентированы на браузеры и тяжеловесны, в то время как структурированные ответы по дизайну компактны.

Сравнительные измерения для 1015:

Полезная нагрузка

Байты

Токены (cl100k_base)

Размер vs HTML

Токены vs HTML

HTML-ответ

46,645

14,252

Markdown-ответ

798

221

В 58.5 раз меньше

В 64.5 раз меньше

JSON-ответ

970

256

В 48.1 раз меньше

В 55.7 раз меньше

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

Десять категорий, понятные действия

Каждая ошибка 1xxx сопоставлена с error_category. Это превращает обработку ошибок в логику маршрутизации вместо хрупкого парсинга каждой страницы.

Категория

Что это значит

Что должен сделать агент

access_denied

Преднамеренная блокировка: IP, ASN, гео, правило брандмауэра

Не повторять. Связаться с владельцем сайта, если это неожиданно.

rate_limit

Превышена частота запросов

Сделать задержку. Повторить через retry_after секунд.

dns

Сбой разрешения DNS у источника

Не повторять. Сообщить владельцу сайта.

config

Ошибка конфигурации: CNAME, туннель, маршрутизация хоста

Не повторять (обычно). Сообщить владельцу сайта.

tls

Несоответствие версии TLS или шифра

Исправить настройки TLS-клиента. Не повторять в текущем виде.

legal

Блокировка по DMCA или регуляторным требованиям

Не повторять. Это юридическое ограничение.

worker

Ошибка среды выполнения Cloudflare Workers

Не повторять. Владелец сайта должен исправить скрипт.

rewrite

Некорректный результат перезаписи URL

Не повторять. Владелец сайта должен исправить правило.

snippet

Ошибка Cloudflare Snippets

Не повторять. Владелец сайта должен исправить конфигурацию Snippets.

unsupported

Неподдерживаемый метод или устаревшая функция

Изменить запрос. Не повторять в текущем виде.

Два поля делают это полезным для агентов в операционном плане:

  • retryable отвечает на вопрос, может ли повторная попытка увенчаться успехом

  • owner_action_required отвечает на вопрос, требует ли проблема эскалации

Вы можете заменить хрупкие эвристики вроде «if status == 429 then maybe retry» явной логикой управления. Распарсите фронтмэттер один раз, затем ветвитесь по стабильным полям. Простой шаблон:

  • если retryable равен true, подождите retry_after и повторите попытку

  • если owner_action_required равен true, остановитесь и эскалируйте

  • в противном случае, завершайтесь быстро, не нагружая сайт

Вот минимальный пример на Python с использованием этого шаблона:

import time
import yaml


def parse_frontmatter(markdown_text: str) -> dict:
    # Ожидает: ---n<yaml>n---n<body>
    if not markdown_text.startswith("---n"):
        return {}
    _, yaml_block, _ = markdown_text.split("---n", 2)
    return yaml.safe_load(yaml_block) or {}


def handle_cloudflare_error(markdown_text: str) -> str:
    meta = parse_frontmatter(markdown_text)

    if not meta.get("cloudflare_error"):
        return "not_cloudflare_error"

    if meta.get("retryable"):
        wait_seconds = int(meta.get("retry_after", 30))
        time.sleep(wait_seconds)
        return f"retry_after_{wait_seconds}s"

    if meta.get("owner_action_required"):
        return f"escalate_owner_error_{meta.get('error_code')}"

    return "do_not_retry"

В этом ключевой сдвиг: агенты больше не выводят намерения из HTML-текста. Они выполняют явную политику, основанную на структурированных полях.

Как использовать

Отправляйте Accept: text/markdown, Accept: application/json или Accept: application/problem+json.

Для быстрого тестирования вы можете обратиться к любому домену с прокси Cloudflare напрямую по пути /cdn-cgi/error/1015 (или замените 1015 на другой код 1xxx).

curl -s --compressed -H "Accept: text/markdown" -A "TestAgent/1.0" -H "Accept-Encoding: gzip, deflate" "<YOUR_DOMAIN>/cdn-cgi/error/1015"

Пример с другим кодом ошибки:

curl -s --compressed -H "Accept: text/markdown" -A "TestAgent/1.0" -H "Accept-Encoding: gzip, deflate" "<YOUR_DOMAIN>/cdn-cgi/error/1020"

Пример с JSON:

curl -s --compressed -H "Accept: application/json" -A "TestAgent/1.0" -H "Accept-Encoding: gzip, deflate" "<YOUR_DOMAIN>/cdn-cgi/error/1015" | jq .

Пример с деталями проблемы (RFC 9457 Problem Details):

curl -s --compressed -H "Accept: application/problem+json" -A "TestAgent/1.0" -H "Accept-Encoding: gzip, deflate" "<YOUR_DOMAIN>/cdn-cgi/error/1015" | jq .

Поведение детерминировано — выигрывает первый явно указанный структурированный тип:

Заголовок Accept

Ответ

application/json

JSON

application/json; charset=utf-8

JSON

application/problem+json

JSON (тип контента application/problem+json)

application/json, text/markdown;q=0.9

JSON

application/json, text/markdown

JSON (равный q, побеждает первый в списке)

text/markdown

Markdown

text/markdown, application/json

Markdown (равный q, побеждает первый в списке)

text/markdown, */*

Markdown

text/*

Markdown

*/*

HTML (по умолчанию)

Запросы только с подстановочным знаком (*/*) не сигнализируют о предпочтении структурированного формата; клиенты должны явно запрашивать Markdown или JSON.

Если запрос успешен, вы получаете обычный контент источника. Заголовок влияет только на ответы об ошибках, сгенерированные Cloudflare.

Практические примеры использования

Существует ряд ситуаций, когда структурированные ответы об ошибках помогают немедленно:

  1. Агент заблокирован правилом WAF (1020). Агент анализирует error_code, записывает ray_id и прекращает повторные попытки. Он может эскалировать проблему с полезным контекстом вместо циклических повторов.

  2. Инструмент MCP (Model Context Protocol), наткнувшийся на географическое ограничение (1009). Инструмент получает чёткую, машиночитаемую причину, возвращает её оркестратору, и рабочий процесс может выбрать альтернативный путь или уведомить пользователя.

  3. Краулер с ограничением по частоте запросов (1015). Агент читает retryable: true и retry_after, применяет задержку и повторяет попытку предсказуемо, вместо того чтобы непрерывно атаковать конечную точку.

  4. Разработчик отлаживает с помощью curl. Разработчик может точно воспроизвести то, что видит агент, включая преамбулу и рекомендации, без обратной разработки HTML.

  5. Библиотеки HTTP-клиентов, которые понимают RFC 9457. Любой клиент, который обрабатывает application/problem+json или анализирует объекты Problem Details, может обрабатывать ошибки Cloudflare без кода, специфичного для Cloudflare.

В каждом случае результат одинаков: меньше догадок, меньше бесполезных повторных попыток, ниже стоимость модели и быстрее восстановление.

Попробуйте сейчас

Отправьте структурированный заголовок Accept и протестируйте на любом домене, проксируемом через Cloudflare:

curl -s --compressed -H "Accept: text/markdown" -A "TestAgent/1.0" -H "Accept-Encoding: gzip, deflate" "<ВАШ_ДОМЕН>/cdn-cgi/error/1015"
curl -s --compressed -H "Accept: application/json" -A "TestAgent/1.0" -H "Accept-Encoding: gzip, deflate" "<ВАШ_ДОМЕН>/cdn-cgi/error/1015" | jq .
curl -s --compressed -H "Accept: application/problem+json" -A "TestAgent/1.0" -H "Accept-Encoding: gzip, deflate" "<ВАШ_ДОМЕН>/cdn-cgi/error/1015" | jq .

Страницы ошибок — это первый разговор между Cloudflare и агентом. Этот запуск делает этот разговор структурированным, соответствующим стандартам и дешёвым в обработке.

Чтобы это работало во всей сети, среды выполнения агентов по умолчанию должны использовать явные структурированные заголовки Accept, а не голый */*. Используйте Accept: text/markdown, */* для рабочих процессов, ориентированных на модели, и Accept: application/json, */* для типизированного потока управления. Если вы поддерживаете фреймворк агента, SDK или стек автоматизации браузера, установите это значение по умолчанию и рассматривайте голый */* как устаревший запасной вариант.

И это только первый слой. Мы создаём остальную часть стека для агентов поверх него: AI Gateway для маршрутизации, управления и наблюдаемости; Workers AI для вывода; а также примитивы идентификации, безопасности и доступа, которые понадобятся агентам для безопасной работы в масштабах Интернета.