---
title: ストリーミング拒否の処理
url: https://platform.claude.com/docs/ja/test-and-evaluate/strengthen-guardrails/handle-streaming-refusals
description: ストリーミングレスポンスにおける拒否の停止理由を検出して処理し、拒否されたリクエストをフォールバックモデルで再試行します。
---

Claude 4モデル以降、ClaudeのAPIからの「streaming」（ストリーミング）レスポンスは、ストリーミング分類器が潜在的なポリシー違反を処理するために介入した場合に **`stop_reason`: `"refusal"`** を返します。この安全機能は、リアルタイムストリーミング中のコンテンツコンプライアンスの維持に役立ちます。

<Tip>
  このページでは、ストリーミングレスポンスにおいて拒否がどのように現れるかを説明します。すべての `stop_reason` の値とその処理方法については、[停止理由とフォールバック](https://platform.claude.com/docs/ja/build-with-claude/handling-stop-reasons)を参照してください。拒否されたリクエストを別のClaudeモデルで再試行するには、[拒否とフォールバック](https://platform.claude.com/docs/ja/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` は `stop_reason` とともに `message_delta` イベントで届きます。

<Note>
  ストリーミング分類器からの `refusal` レスポンスには、`category` と、ユーザーに表示できる人間が読める形式の `explanation` を持つ `stop_details` オブジェクトが含まれます。完全なレスポンスの形式と利用可能なカテゴリについては、[拒否とフォールバック](https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback#refusal-response)を参照してください。

  拒否の際には `stop_details` オブジェクトは常に存在しますが、その `category` および `explanation` フィールドは `null` になる場合があります。たとえば、拒否が名前付きカテゴリのいずれにも対応しない場合などです。`category` と `explanation` に値が入っていると仮定するのではなく、`stop_reason` または `stop_details.type` で分岐し、それらが `null` の場合は独自のユーザー向けメッセージを提供してください。
</Note>

## 拒否後のコンテキストのリセット

**`stop_reason`: `refusal`** を受け取った場合、続行する前に会話コンテキストをリセットする必要があります。拒否を引き起こしたターンを削除または言い換えるか、会話履歴を完全にクリアすることができます。リセットせずに続行しようとすると、拒否が継続して発生します。

<Note>
  レスポンスが拒否された場合でも、使用量メトリクスはレスポンスに含まれます。拒否されたリクエストが課金されるかどうかは、拒否が届くタイミングとそのカテゴリによって異なります。[拒否の課金方法](https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback#how-refusals-are-billed)を参照してください。
</Note>

<Tip>
  コンテキストのリセットは、回復するための唯一の方法ではありません。拒否されたリクエストを別のClaudeモデルで再試行することもできます。[拒否とフォールバック](https://platform.claude.com/docs/ja/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 で拒否（refusal）が返されていないか確認します
              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は現在、拒否を3つの異なる方法で処理しています。

| 拒否タイプ           | レスポンス形式                      | 発生するタイミング                  |
| --------------- | ---------------------------- | -------------------------- |
| ストリーミング分類器による拒否 | **`stop_reason`: `refusal`** | ストリーミング中にコンテンツがポリシーに違反した場合 |
| API入力および著作権の検証  | 400エラーコード                    | 入力が検証チェックに失敗した場合           |
| モデル生成の拒否        | 標準のテキストレスポンス                 | モデル自体が拒否した場合               |

## ベストプラクティス

* **拒否を監視する：** エラー処理に **`stop_reason`: `refusal`** のチェックを含めます
* **自動的にリセットする：** 拒否が検出されたときに自動的にコンテキストをリセットする処理を実装します
* **別のモデルにフォールバックする：** [サーバーサイドフォールバックまたはSDKミドルウェア](https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback)を設定し、拒否をユーザーに表示する代わりに、拒否されたリクエストが別のClaudeモデルで再試行されるようにします。Claude Haiku 5.5にはサーバーサイドフォールバックがないため、その再試行はクライアント側で設定してください
* **手動での再試行時にフォールバッククレジットを利用する：** 再試行を自分で構築する場合は、拒否の[フォールバッククレジット](https://platform.claude.com/docs/ja/build-with-claude/fallback-credit)トークンを渡して、再試行でプロンプトキャッシングのコストを二重に支払わないようにします。Claude Haiku 5.5の拒否にはフォールバッククレジットが付与されません
* **カスタムメッセージを提供する：** 拒否が発生した際のUX向上のために、ユーザーフレンドリーなメッセージを作成します
* **拒否パターンを追跡する：** 拒否の頻度を監視して、プロンプトの潜在的な問題を特定します

## 移行に関する注意事項

この機能が最初にリリースされたときに拒否処理を構築した場合、または既存の統合に追加する場合は、次の点を確認してください。

* **拒否はエラーではなくレスポンスです。** 拒否は `stop_reason`: `"refusal"` を持つ成功したHTTP 200レスポンスとして届くため、エラー率のみに基づく監視では検出されません。拒否は独自のシグナルとして追跡してください。
* **拒否には構造化された詳細が含まれます。** すべてのモデルにおいて、拒否には、拒否の背景にあるポリシーカテゴリを識別する `stop_details` オブジェクトも含まれます。完全なレスポンスの形式については、[拒否とフォールバック](https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback#refusal-response)を参照してください。
* **別のモデルで再試行してください。** 拒否されたリクエストを同じモデルに再送信すると、通常は再び拒否されます。コンテキストをリセットするだけでなく、[サーバーサイドフォールバック、SDKミドルウェア、または手動での再試行](https://platform.claude.com/docs/ja/build-with-claude/refusals-and-fallback)を使ってフォールバックモデルで再試行し、再試行を自分で構築する場合は[フォールバッククレジット](https://platform.claude.com/docs/ja/build-with-claude/fallback-credit)を利用してください。Claude Haiku 5.5にはサーバーサイドフォールバックがなく、その拒否にはフォールバッククレジットが付与されないため、その再試行はクライアント側で設定してください。
* **バッチ結果の拒否を確認してください。** [Message Batch](https://platform.claude.com/docs/ja/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/ja/build-with-claude/refusals-and-fallback">
    拒否されたリクエストを、サーバーサイドまたはクライアントで別のClaudeモデルで再試行します。
  </Card>

  <Card title="停止理由とフォールバック" icon="code" href="https://platform.claude.com/docs/ja/build-with-claude/handling-stop-reasons">
    すべての `stop_reason` の値とその処理方法。
  </Card>

  <Card title="メッセージのストリーミング" icon="lightning" href="https://platform.claude.com/docs/ja/build-with-claude/streaming">
    レスポンスをストリーミングし、届いた `message_delta` イベントから `stop_reason` を読み取ります。
  </Card>

  <Card title="多言語サポート" icon="text-aa" href="https://platform.claude.com/docs/ja/build-with-claude/multilingual-support">
    Claudeの言語横断的な機能で、さまざまな言語のユーザーに対応します。
  </Card>
</CardGroup>
