---
title: Обработка отказов при потоковой передаче
url: https://platform.claude.com/docs/ru/test-and-evaluate/strengthen-guardrails/handle-streaming-refusals
description: Обнаруживайте и обрабатывайте причины остановки типа refusal в потоковых ответах, а также повторяйте отклонённые запросы на резервной модели.
---

Начиная с моделей Claude 4, потоковые ответы от API Claude возвращают **`stop_reason`: `"refusal"`**, когда потоковые классификаторы вмешиваются для обработки потенциальных нарушений политики. Эта функция безопасности помогает поддерживать соответствие контента требованиям во время «streaming» (потоковой передачи) в реальном времени.

<Tip>
  На этой странице описано, как отказы проявляются в потоковых ответах. Все значения `stop_reason` и способы их обработки см. в разделе [Причины остановки и резервные варианты](https://platform.claude.com/docs/ru/build-with-claude/handling-stop-reasons). Чтобы повторить отклонённые запросы на другой модели Claude, см. раздел [Отказы и резервные варианты](https://platform.claude.com/docs/ru/build-with-claude/refusals-and-fallback).
</Tip>

## Формат ответа API

Когда потоковые классификаторы обнаруживают контент, нарушающий политики Anthropic, API возвращает следующий ответ:

```json
{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Hello.."
    }
  ],
  "stop_reason": "refusal",
  "stop_details": {
    "type": "refusal",
    "category": "cyber",
    "explanation": "This request was declined because it could enable cyber harm."
  }
}
```

В потоке событий `stop_details` приходит в событии `message_delta` вместе с `stop_reason`.

<Note>
  Ответ `refusal` от потоковых классификаторов включает объект `stop_details` с полем `category` и понятным человеку полем `explanation`, которое вы можете показать пользователю. Полную структуру ответа и доступные категории см. в разделе [Отказы и резервные варианты](https://platform.claude.com/docs/ru/build-with-claude/refusals-and-fallback#refusal-response).

  При отказе объект `stop_details` присутствует всегда, но его поля `category` и `explanation` могут иметь значение `null`, например когда отказ не соответствует ни одной именованной категории. Выполняйте ветвление по `stop_reason` или `stop_details.type`, а не предполагайте, что `category` и `explanation` заполнены, и предоставляйте собственные сообщения для пользователя, когда они равны `null`.
</Note>

## Сброс контекста после отказа

Когда вы получаете **`stop_reason`: `refusal`**, вы должны сбросить контекст разговора, прежде чем продолжить. Вы можете удалить или переформулировать реплику, вызвавшую отказ, либо полностью очистить историю разговора. Попытка продолжить без сброса приведёт к дальнейшим отказам.

<Note>
  Метрики использования по-прежнему предоставляются в ответе, даже если ответ отклонён. Тарифицируется ли отклонённый запрос, зависит от того, когда поступил отказ, и от его категории; см. [Как тарифицируются отказы](https://platform.claude.com/docs/ru/build-with-claude/refusals-and-fallback#how-refusals-are-billed).
</Note>

<Tip>
  Сброс контекста — не единственный способ восстановления. Вы также можете повторить отклонённый запрос на другой модели Claude; на странице [Отказы и резервные варианты](https://platform.claude.com/docs/ru/build-with-claude/refusals-and-fallback) показано, как настроить это с помощью резервного варианта на стороне сервера, промежуточного ПО SDK или ручного повтора.
</Tip>

## Руководство по реализации

Вот как обнаруживать и обрабатывать отказы при потоковой передаче в вашем приложении:

<CodeGroup>
  ```bash cURL
  response=$(curl -N https://api.anthropic.com/v1/messages \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -d '{
      "model": "claude-opus-5-5",
      "messages": [{"role": "user", "content": "Hello"}],
      "max_tokens": 1024,
      "stream": true
    }')

  if echo "$response" | jq -R -e 'select(startswith("data: "))
      | sub("^data: "; "") | fromjson
      | select(.delta.stop_reason == "refusal")' >/dev/null; then
    echo "Response refused - resetting conversation context"
    # Здесь сбросьте состояние диалога
  fi
  ```

  ```bash CLI
  response=$(ant messages create --stream --format jsonl \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: Hello}')

  if echo "$response" | jq -e 'select(.delta.stop_reason == "refusal")' >/dev/null; then
    echo "Response refused - resetting conversation context"
    # Здесь сбросьте состояние диалога
  fi
  ```

  ```python Python
  client = anthropic.Anthropic()
  messages = []


  def reset_conversation():
      """Reset conversation context after refusal"""
      global messages
      messages = []
      print("Conversation reset due to refusal")


  try:
      with client.messages.stream(
          max_tokens=1024,
          messages=messages + [{"role": "user", "content": "Hello"}],
          model="claude-opus-5-5",
      ) as stream:
          for event in stream:
              # Проверка на отказ в message delta
              if event.type == "message_delta":
                  if event.delta.stop_reason == "refusal":
                      reset_conversation()
                      break
  except Exception as e:
      print(f"Error: {e}")
  ```

  ```typescript TypeScript
  const client = new Anthropic();
  let messages: Anthropic.MessageParam[] = [];

  function resetConversation() {
    // Сбросить контекст диалога после отказа
    messages = [];
    console.log("Conversation reset due to refusal");
  }

  try {
    const stream = await client.messages.stream({
      messages: [...messages, { role: "user", content: "Hello" }],
      model: "claude-opus-5-5",
      max_tokens: 1024
    });

    for await (const event of stream) {
      // Проверить наличие отказа в дельте сообщения
      if (event.type === "message_delta" && event.delta.stop_reason === "refusal") {
        resetConversation();
        break;
      }
    }
  } catch (error) {
    console.error("Error:", error);
  }
  ```

  ```csharp C#
  List<Message> messages = new();
  AnthropicClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Hello" }]
  };

  try
  {
      await foreach (var streamEvent in client.Messages.CreateStreaming(parameters))
      {
          if (
              streamEvent.TryPickDelta(out var deltaEvent)
              && deltaEvent.Delta.StopReason == StopReason.Refusal
          )
          {
              ResetConversation();
              break;
          }
      }
  }
  catch (Exception e)
  {
      Console.WriteLine($"Error: {e.Message}");
  }

  void ResetConversation()
  {
      messages.Clear();
      Console.WriteLine("Conversation reset due to refusal");
  }
  ```

  ```go Go
  var messages []anthropic.MessageParam

  func resetConversation() {
  	messages = []anthropic.MessageParam{}
  	fmt.Println("Conversation reset due to refusal")
  }
  // ...
  	client := anthropic.NewClient()

  	stream := client.Messages.NewStreaming(context.TODO(), anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeOpus5_5,
  		MaxTokens: 1024,
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock("Hello")),
  		},
  	})

  streamLoop:
  	for stream.Next() {
  		event := stream.Current()
  		switch eventVariant := event.AsAny().(type) {
  		case anthropic.MessageDeltaEvent:
  			if eventVariant.Delta.StopReason == anthropic.StopReasonRefusal {
  				resetConversation()
  				break streamLoop
  			}
  		}
  	}

  	if err := stream.Err(); err != nil {
  		log.Fatal(err)
  	}
  ```

  ```java Java
  import com.anthropic.core.http.StreamResponse;
  import com.anthropic.models.messages.RawMessageStreamEvent;
  import com.anthropic.models.messages.StopReason;
  // ...

  List<MessageParam> messages = new ArrayList<>();

  void main() {
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024L)
          .addUserMessage("Hello")
          .build();

      try (StreamResponse<RawMessageStreamEvent> stream = client.messages().createStreaming(params)) {
          stream.stream().forEach(event -> {
              event.messageDelta().ifPresent(deltaEvent -> {
                  deltaEvent.delta().stopReason().ifPresent(stopReason -> {
                      if (stopReason.equals(StopReason.REFUSAL)) {
                          resetConversation();
                      }
                  });
              });
          });
      } catch (Exception e) {
          System.err.println("Error: " + e.getMessage());
      }
  }

  void resetConversation() {
      messages.clear();
      IO.println("Conversation reset due to refusal");
  }
  ```

  ```php PHP
  $client = new Client();
  $messages = [];

  function resetConversation(&$messages) {
      $messages = [];
      echo "Conversation reset due to refusal\n";
  }

  try {
      $stream = $client->messages->createStream(
          maxTokens: 1024,
          messages: [
              ['role' => 'user', 'content' => 'Hello']
          ],
          model: 'claude-opus-5-5',
      );

      foreach ($stream as $event) {
          if ($event->type === 'message_delta' && $event->delta->stopReason === 'refusal') {
              resetConversation($messages);
              break;
          }
      }
  } catch (\Exception $e) {
      echo "Error: " . $e->getMessage() . "\n";
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new
  messages = []

  def reset_conversation(messages)
    messages.clear
    puts "Conversation reset due to refusal"
  end

  begin
    stream = client.messages.stream(
      model: :"claude-opus-5-5",
      max_tokens: 1024,
      messages: [{ role: "user", content: "Hello" }]
    )

    stream.each do |event|
      if event.type == :message_delta && event.delta.stop_reason == :refusal
        reset_conversation(messages)
        break
      end
    end
  rescue => e
    puts "Error: #{e.message}"
  end
  ```
</CodeGroup>

## Текущие типы отказов

В настоящее время API обрабатывает отказы тремя различными способами:

| Тип отказа                                   | Формат ответа                | Когда возникает                                              |
| -------------------------------------------- | ---------------------------- | ------------------------------------------------------------ |
| Отказы потоковых классификаторов             | **`stop_reason`: `refusal`** | Во время потоковой передачи, когда контент нарушает политики |
| Проверка входных данных API и авторских прав | Коды ошибок 400              | Когда входные данные не проходят проверки                    |
| Отказы, сгенерированные моделью              | Стандартные текстовые ответы | Когда модель сама отказывается                               |

## Лучшие практики

* **Отслеживайте отказы:** включите проверки **`stop_reason`: `refusal`** в вашу обработку ошибок
* **Сбрасывайте автоматически:** реализуйте автоматический сброс контекста при обнаружении отказов
* **Переключайтесь на другую модель:** настройте [резервный вариант на стороне сервера или промежуточное ПО SDK](https://platform.claude.com/docs/ru/build-with-claude/refusals-and-fallback), чтобы отклонённые запросы повторялись на другой модели Claude вместо показа отказа пользователю. У Claude Haiku 5.5 нет резервного варианта на стороне сервера, поэтому настройте повтор для неё в своём клиенте
* **Используйте резервный кредит при ручных повторах:** если вы реализуете повтор самостоятельно, передавайте токен [резервного кредита](https://platform.claude.com/docs/ru/build-with-claude/fallback-credit) из отказа, чтобы при повторе не оплачивать стоимость кэша подсказок дважды. Отказы Claude Haiku 5.5 не содержат резервного кредита
* **Предоставляйте собственные сообщения:** создавайте понятные пользователю сообщения для улучшения UX при возникновении отказов
* **Отслеживайте закономерности отказов:** следите за частотой отказов, чтобы выявлять потенциальные проблемы с вашими подсказками

## Примечания по миграции

Если вы реализовали обработку отказов, когда эта функция только появилась, или добавляете её в существующую интеграцию, проверьте следующее:

* **Отказы — это ответы, а не ошибки.** Отказ приходит как успешный ответ HTTP 200 с `stop_reason`: `"refusal"`, поэтому мониторинг, построенный только на частоте ошибок, его не выявит. Отслеживайте отказы как отдельный сигнал.
* **Отказы содержат структурированные сведения.** На каждой модели отказ также включает объект `stop_details`, который указывает категорию политики, ставшую причиной отклонения. Полную структуру ответа см. в разделе [Отказы и резервные варианты](https://platform.claude.com/docs/ru/build-with-claude/refusals-and-fallback#refusal-response).
* **Повторяйте на другой модели.** Повторная отправка отклонённого запроса той же модели обычно приводит к новому отказу. Вместо того чтобы только сбрасывать контекст, повторите запрос на резервной модели с помощью [резервного варианта на стороне сервера, промежуточного ПО SDK или ручного повтора](https://platform.claude.com/docs/ru/build-with-claude/refusals-and-fallback) и используйте [резервный кредит](https://platform.claude.com/docs/ru/build-with-claude/fallback-credit), если реализуете повтор самостоятельно. У Claude Haiku 5.5 нет резервного варианта на стороне сервера, а её отказы не содержат резервного кредита, поэтому настройте повтор для неё в своём клиенте.
* **Проверяйте результаты пакетов на наличие отказов.** Отклонённый запрос в [пакете сообщений (Message Batch)](https://platform.claude.com/docs/ru/build-with-claude/batch-processing) возвращается как успешный результат с `stop_reason`: `"refusal"`, а не как результат с ошибкой.
* **Централизуйте обработку на основе `stop_reason`.** API продолжает консолидировать обработку отказов вокруг `stop_reason`: `"refusal"`, поэтому выполняйте ветвление по причине остановки, а не по поведению, специфичному для модели.

## Следующие шаги

<CardGroup cols={2}>
  <Card title="Отказы и резервные варианты" icon="arrows-clockwise" href="https://platform.claude.com/docs/ru/build-with-claude/refusals-and-fallback">
    Повторяйте отклонённые запросы на другой модели Claude — на стороне сервера или в вашем клиенте.
  </Card>

  <Card title="Причины остановки и резервные варианты" icon="code" href="https://platform.claude.com/docs/ru/build-with-claude/handling-stop-reasons">
    Все значения `stop_reason` и способы их обработки.
  </Card>

  <Card title="Потоковая передача сообщений" icon="lightning" href="https://platform.claude.com/docs/ru/build-with-claude/streaming">
    Передавайте ответы потоком и считывайте `stop_reason` из событий `message_delta` по мере их поступления.
  </Card>

  <Card title="Многоязычная поддержка" icon="text-aa" href="https://platform.claude.com/docs/ru/build-with-claude/multilingual-support">
    Обслуживайте пользователей на разных языках благодаря межъязыковым возможностям Claude.
  </Card>
</CardGroup>
