> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apiyi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Почему при отказе Claude возвращается пустое содержимое?

> Когда Claude сталкивается с политикой безопасности провайдера, он не возвращает ошибку: возвращаются HTTP 200, пустое содержимое и stop_reason: refusal с категорией отказа. На этой странице показан вывод для каждого формата API, правила тарификации и способы обнаружения такого ответа.

## Краткий ответ

Когда запрос активирует политику безопасности провайдера, Claude **не возвращает ошибку**. API по-прежнему возвращает HTTP 200, но:

* `content` — это **пустой массив** `[]`, а `output_tokens` имеет значение `0`;
* `stop_reason` имеет значение `refusal`;
* `stop_details` указывает **категорию** отказа (например, `cyber`) и содержит краткое пояснение на английском языке.

Это поведение модели, а не сбой API. Код, который напрямую считывает `message.content[0]`, вызовет `IndexError`. В формате, совместимом с OpenAI, вы получаете пустую строку, а `finish_reason` имеет значение `refusal`.

Отказ обычно возвращается в течение 1–2 секунд. Тарифицируется ли он, зависит от категории: **отказ до генерации какого-либо вывода в категории `cyber` (и некоторых других) не тарифицируется** — см. «Правила тарификации» ниже.

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

Ниже приведен один и тот же запрос, вызывающий отказ по кибербезопасности, выполненный четырьмя способами (замеры от 29.09.2026, идентификаторы скрыты):

<Tabs>
  <Tab title="Нативный · Без потоковой передачи">
    `POST /v1/messages`, `stream: false`:

    ```json theme={null}
    {
      "id": "msg_xxxxxxxx",
      "type": "message",
      "role": "assistant",
      "model": "claude-sonnet-5",
      "content": [],
      "stop_reason": "refusal",
      "stop_sequence": null,
      "stop_details": {
        "type": "refusal",
        "category": "cyber",
        "explanation": "This request triggered cyber-related safeguards. To learn about the Cyber Verification Program and apply for access, visit our help center: ..."
      },
      "usage": {
        "input_tokens": 2863,
        "output_tokens": 0,
        "cache_creation_input_tokens": 0,
        "cache_read_input_tokens": 0
      }
    }
    ```
  </Tab>

  <Tab title="Нативный · Потоковая передача">
    `POST /v1/messages`, `stream: true`. **События `content_block_*` отсутствуют вовсе** — сразу за `message_start` следует `message_delta`, содержащий отказ:

    ```text theme={null}
    event: message_start
    data: {"type":"message_start","message":{"id":"msg_xxxxxxxx","content":[],"stop_reason":null,"stop_details":null,"usage":{"input_tokens":2863,"output_tokens":0}, ...}}

    event: message_delta
    data: {"type":"message_delta","delta":{"stop_reason":"refusal","stop_sequence":null,"stop_details":{"type":"refusal","category":"cyber","explanation":"This request triggered cyber-related safeguards. ..."}},"usage":{"input_tokens":2863,"output_tokens":0}}

    event: message_stop
    data: {"type":"message_stop"}
    ```
  </Tab>

  <Tab title="OpenAI-совместимый · Без потоковой передачи">
    `POST /v1/chat/completions`. `content` — пустая строка, `finish_reason` имеет значение `refusal`, и **категория отказа отсутствует**:

    ```json theme={null}
    {
      "id": "msg_xxxxxxxx",
      "object": "chat.completion",
      "model": "claude-sonnet-5",
      "choices": [
        {
          "index": 0,
          "message": { "role": "assistant", "content": "" },
          "finish_reason": "refusal"
        }
      ],
      "usage": { "prompt_tokens": 2863, "total_tokens": 2863 }
    }
    ```
  </Tab>

  <Tab title="OpenAI-совместимый · Потоковая передача">
    `POST /v1/chat/completions`, `stream: true`. Только пустые `delta`, и в одном чанке для `finish_reason` задано `refusal`:

    ```text theme={null}
    data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]}

    data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"refusal"}]}

    data: [DONE]
    ```
  </Tab>
</Tabs>

| Поле | При отказе |
| - | - |
| HTTP-статус | `200`, не ошибка |
| `content` | Пустой массив `[]` в нативном формате; пустая строка `""` в OpenAI-совместимом формате |
| `stop_reason` / `finish_reason` | `refusal` |
| `stop_details` | Только в нативном формате: `type`, `category` (категория отказа), `explanation` (текст на английском) |
| `usage` | Входные tokens подсчитываются как обычно, `output_tokens` равен 0 (подсчет не то же самое, что тарификация — см. ниже) |
| Задержка | Обычно 1–2 секунды, намного быстрее обычного ответа |

<Note>
  Отказ также может произойти **в процессе потоковой передачи**: сначала передается часть текста, а затем ответ завершается с `stop_reason: "refusal"`. Этот частичный вывод является неполным и должен быть отброшен.
</Note>

## Категории отказа

В настоящее время `stop_details.category` имеет пять значений:

| Категория | Значение |
| - | - |
| `cyber` | Может способствовать причинению вреда в киберпространстве, например разработке вредоносного ПО или эксплойтов; может также срабатывать при легитимной деятельности в области безопасности |
| `bio` | Может способствовать причинению биологического вреда; может также срабатывать при полезных исследованиях в области наук о жизни |
| `frontier_llm` | Может содействовать разработке конкурирующих моделей ИИ (ограничено коммерческими условиями поставщика) |
| `reasoning_extraction` | Требует от модели воспроизвести в ответе ее внутреннее рассуждение |
| `general_harms` | Другие области политики использования, не входящие в четыре категории выше |

Если отказ не сопоставляется с именованной категорией, и `category`, и `explanation` имеют значение `null` — это нормальное значение. Текст `explanation` может измениться в любой момент: отображайте его, **не используйте сопоставление строк**.

## Распространенные триггеры

`category: "cyber"` — это то, с чем разработчики сталкиваются чаще всего. В Claude действуют защитные механизмы в реальном времени для запросов, связанных с кибербезопасностью, и любая из следующих задач может привести к их срабатыванию:

* Просьба к модели найти ошибки в коде, определить, «содержит ли фрагмент кода уязвимость», или указать тип уязвимости (CWE)
* Написание или дополнение кода эксплойтов либо шагов для тестирования на проникновение
* Анализ или переписывание вредоносного кода

<Warning>
  **Пакетная оценка и дистилляция датасетов подвержены этому сильнее всего.** При поочередном прогоне целого датасета уязвимостей через модель значительная часть примеров часто отклоняется. Скрипт, предполагающий, что `content[0]` существует всегда, аварийно завершится на отклоненном элементе, создавая впечатление, будто «API работает лишь время от времени».
</Warning>

## Правила тарификации

Согласно правилам провайдера (по состоянию на сентябрь 2026 года; провайдер может скорректировать их по мере оценки доли ложных срабатываний):

| Момент отказа и его категория | Тарификация |
| - | - |
| До начала вывода, категория `cyber`, `general_harms` или `null` | **Не тарифицируется** |
| До начала вывода, категория `bio`, `frontier_llm` или `reasoning_extraction` | Входные tokens тарифицируются |
| В процессе потоковой передачи (любая категория) | Входные tokens плюс уже переданные в потоке выходные данные тарифицируются |

Тарифицируется запрос или нет, отклоненный запрос все равно учитывается в ваших лимитах запросов. `usage` по-прежнему показывает количество tokens — это подсчет количества, а не обязательно списание средств.

## Как это обнаружить и обработать

<Steps>
  <Step title="Проверяйте stop_reason перед чтением содержимого">
    В нативном формате проверяйте `stop_reason == "refusal"`; в OpenAI-совместимом формате проверяйте `finish_reason == "refusal"`. Читайте `content` только после того, как исключите отказ.
  </Step>

  <Step title="Фиксируйте отказы как тип результата">
    Отказ — это успешный вызов, а не сетевая ошибка. При оценке работы фиксируйте его отдельно как «refused» вместе с `stop_details.category`, вместо того чтобы считать его сбоем, требующим повторной попытки.
  </Step>

  <Step title="Не повторяйте запрос с тем же содержимым">
    Повторная отправка того же содержимого обычно приводит к такому же отказу, по-прежнему расходует лимиты запросов, а для некоторых категорий тарифицируется каждый раз.
  </Step>

  <Step title="Сбрасывайте контекст в многоэтапных диалогах">
    После того как на реплику был получен отказ, удалите или перепишите её либо очистите историю перед продолжением. Без сброса последующие запросы продолжат отклоняться.
  </Step>

  <Step title="Анализируйте, какое содержимое отклоняется">
    Группируйте отказы по `category`, чтобы увидеть, какие задачи их вызывают, а затем решите, стоит ли отправлять это содержимое другой модели.
  </Step>
</Steps>

<Tabs>
  <Tab title="Anthropic SDK">
    ```python theme={null}
    import os
    import anthropic

    client = anthropic.Anthropic(
        api_key=os.environ["APIYI_API_KEY"],
        base_url="https://api.apiyi.com",
    )

    def ask(prompt, model="claude-sonnet-5"):
        message = client.messages.create(
            model=model,
            max_tokens=4096,
            messages=[{"role": "user", "content": prompt}],
        )
        if message.stop_reason == "refusal":
            details = getattr(message, "stop_details", None)
            category = getattr(details, "category", None) if details else None
            return {"refused": True, "category": category, "request_id": message.id}

        text = "".join(b.text for b in message.content if b.type == "text")
        return {"refused": False, "text": text}
    ```
  </Tab>

  <Tab title="OpenAI SDK">
    ```python theme={null}
    import os
    from openai import OpenAI

    client = OpenAI(
        api_key=os.environ["APIYI_API_KEY"],
        base_url="https://api.apiyi.com/v1",
    )

    def ask(prompt, model="claude-sonnet-5"):
        resp = client.chat.completions.create(
            model=model,
            max_tokens=4096,
            messages=[{"role": "user", "content": prompt}],
        )
        choice = resp.choices[0]
        if choice.finish_reason == "refusal" or not choice.message.content:
            return {"refused": True, "request_id": resp.id}
        return {"refused": False, "text": choice.message.content}
    ```
  </Tab>
</Tabs>

<Tip>
  Если вам нужна **категория** отказа, вызывайте нативный формат `/v1/messages`. OpenAI-совместимый формат сохраняет только `finish_reason: "refusal"` и не содержит `stop_details`.
</Tip>

## Что насчет легитимных исследований безопасности?

Упомянутая в тексте отказа программа **Cyber Verification Program** — это бесплатная программа подачи заявок от провайдера для легитимной работы в сфере безопасности: после подтверждения личности ограничения для задач «высокого риска двойного назначения», таких как эксплуатация уязвимостей или разработка инструментов для атак, могут быть смягчены. «Запрещенные сценарии использования», такие как разработка программ-вымогателей или массовая эксфильтрация данных, блокируются во всех случаях.

Заявка на участие в программе **подается администратором организации в прямом аккаунте у провайдера**. Что касается сторонних платформ, провайдер отмечает, что «не все платформы участвуют», и APIYI в настоящее время не предоставляет доступ к этой программе.

Поэтому при вызовах через APIYI для отклоненных примеров:

* честно фиксируйте их как «отклоненные» в результатах вашей оценки, сгруппировав по категориям;
* либо обрабатывайте этот контент с помощью другой модели.

APIYI не изменяет и не может изменять политику безопасности провайдера.

## Чем это отличается от отказа OpenAI

| | Claude | OpenAI (серия GPT) |
| - | - | - |
| HTTP-статус | 200 | 200 |
| Тело ответа | Пустое (`content: []`) | Однострочный отказ, например `I can't help with that…` |
| Причина завершения | `refusal` | `stop`, так же, как и при обычном ответе |
| Категория отказа | Да, `stop_details.category` | Нет |
| Тарификация | Отказы до вывода в `cyber` и некоторых других категориях не тарифицируются | Тарифицируется в обычном порядке за текст отказа |
| Определение | Достаточно проверить `stop_reason` | Только по самому тексту |

Чтобы узнать, как выглядит отказ OpenAI, см. раздел [Как выглядит отказ модели OpenAI?](/ru/faq/openai-content-safety-refusal).

## Часто задаваемые вопросы

<AccordionGroup>
  <Accordion title="Тарифицируется ли отказ?">
    Это зависит от категории и момента возникновения. Отказы до начала вывода в `cyber`, `general_harms` или `null` не тарифицируются; `bio`, `frontier_llm` и `reasoning_extraction` тарифицируют входные данные; отказ в процессе потоковой передачи тарифицирует входные данные и уже переданную часть вывода. См. раздел «Правила тарификации» выше.
  </Accordion>

  <Accordion title="Можно ли отключить отказы?">
    Нет. Решение об отказе принимает модель провайдера в рамках своей политики безопасности; APIYI не может отключить их или изменить степень их строгости. Зафиксируйте отклоненный контент как результат с отказом или обработайте его с помощью другой модели.
  </Accordion>

  <Accordion title="Почему для одних примеров из одного и того же набора данных возвращается отказ, а для других — нет?">
    Защитный механизм оценивает каждый запрос по его собственному содержимому. И сам фрагмент кода, и формулировка prompt влияют на результат, поэтому отказ обычно возвращается только для части набора данных. В наших тестах повторная отправка того же отклоненного примера давала стабильный результат.
  </Accordion>

  <Accordion title="Почему output_tokens равен 0 в usage при отказе?">
    Модель остановилась до генерации какого-либо контента, поэтому вывод равен 0 и учитываются только входные данные. По этому же признаку можно отличить отказ от отсечения по `max_tokens`: во втором случае возвращается `stop_reason: "max_tokens"`, а количество выходных tokens равно установленному вами лимиту.
  </Accordion>
</AccordionGroup>

## Связанные документы

<CardGroup cols={2}>
  <Card title="Обработка ответов Claude" icon="braces" href="/ru/api-capabilities/claude-response-handling">
    Структура ответов с потоковой передачей и без нее, значения stop\_reason
  </Card>

  <Card title="Как выглядит отказ модели OpenAI?" icon="message-square-x" href="/ru/faq/openai-content-safety-refusal">
    Как выглядит отказ GPT и как его обнаружить
  </Card>

  <Card title="Как обеспечивается безопасность контента и соответствие требованиям?" icon="shield-check" href="/ru/faq/content-safety">
    Политика платформы в отношении безопасности контента и соответствия требованиям
  </Card>
</CardGroup>
