English ← MyDocs

流式拒绝


从 Claude 4 模型开始,当流式分类器介入处理潜在的策略违规时,Claude API 的流式响应会返回 stop_reason: "refusal"。这项新的安全功能有助于在实时流式传输期间保持内容合规。

Tip

要了解更多关于 Claude Sonnet 4.5 的 API 安全过滤器触发的拒绝,请参阅了解 Sonnet 4.5 的 API 安全过滤器

API 响应格式

当流式分类器检测到违反 Anthropic 政策的内容时,API 返回以下响应:

{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Hello.."
    }
  ],
  "stop_reason": "refusal"
}
Warning

不包含额外的拒绝消息。您必须处理响应并提供适当的消息给用户。

拒绝后重置上下文

当收到 stop_reason: refusal 时,您必须在继续之前重置对话上下文。您可以删除或重新措辞触发拒绝的那一轮对话,或者完全清除对话历史。尝试在不重置的情况下继续将导致持续拒绝。

Note

即使响应被拒绝,响应中仍会提供使用指标用于计费目的。

您将被计费到拒绝发生前的输出 token。

Tip

如果您在使用 Claude Sonnet 4.5 或 Opus 4.1 时频繁遇到 refusal 停止原因,可以尝试将 API 调用更新为使用 Haiku 4.5(claude-haiku-4-5-20251001),它有不同的使用限制。了解更多关于了解 Sonnet 4.5 的 API 安全过滤器

实现指南

以下是如何在应用程序中检测和处理流式拒绝:

# 流式请求并检查拒绝
response=$(curl -N https://api.anthropic.com/v1/messages \
  --header "anthropic-version: 2023-06-01" \
  --header "content-type: application/json" \
  --header "x-api-key: $ANTHROPIC_API_KEY" \
  --data '{
    "model": "claude-opus-4-7",
    "messages": [{"role": "user", "content": "Hello"}],
    "max_tokens": 1024,
    "stream": true
  }')

# 检查流中的拒绝
if echo "$response" | grep -q '"stop_reason":"refusal"'; then
  echo "响应被拒绝 - 正在重置对话上下文"
  # 在此重置对话状态
fi
import anthropic

client = anthropic.Anthropic()
messages = []


def reset_conversation():
    """拒绝后重置对话上下文"""
    global messages
    messages = []
    print("因拒绝而重置对话")


try:
    with client.messages.stream(
        max_tokens=1024,
        messages=messages + [{"role": "user", "content": "Hello"}],
        model="claude-opus-4-7",
    ) as stream:
        for event in stream:
            # 检查消息增量中的拒绝
            if event.type == "message_delta":
                if event.delta.stop_reason == "refusal":
                    reset_conversation()
                    break
except Exception as e:
    print(f"错误:{e}")
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();
let messages: any[] = [];

function resetConversation() {
  // 拒绝后重置对话上下文
  messages = [];
  console.log("因拒绝而重置对话");
}

try {
  const stream = await client.messages.stream({
    messages: [...messages, { role: "user", content: "Hello" }],
    model: "claude-opus-4-7",
    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);
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Anthropic;
using Anthropic.Models.Messages;

class Program
{
    private static List<Message> messages = new();

    static async Task Main(string[] args)
    {
        AnthropicClient client = new();

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

        try
        {
            await foreach (var msg in client.Messages.CreateStreaming(parameters))
            {
                if (msg.Type == "message_delta" && msg.Delta?.StopReason == "refusal")
                {
                    ResetConversation();
                    break;
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine({{CONTENT}}quot;错误:{e.Message}");
        }
    }

    private static void ResetConversation()
    {
        messages.Clear();
        Console.WriteLine("因拒绝而重置对话");
    }
}
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/anthropics/anthropic-sdk-go"
)

var messages []anthropic.MessageParam

func resetConversation() {
	messages = []anthropic.MessageParam{}
	fmt.Println("因拒绝而重置对话")
}

func main() {
	client := anthropic.NewClient()

	stream := client.Messages.NewStreaming(context.TODO(), anthropic.MessageNewParams{
		Model:     anthropic.ModelClaudeOpus4_7,
		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 == "refusal" {
				resetConversation()
				break streamLoop
			}
		}
	}

	if err := stream.Err(); err != nil {
		log.Fatal(err)
	}
}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.MessageParam;
import com.anthropic.models.messages.Model;
import com.anthropic.core.http.StreamResponse;
import com.anthropic.models.messages.RawMessageStreamEvent;
import com.anthropic.models.messages.StopReason;
import java.util.ArrayList;
import java.util.List;

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

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

    MessageCreateParams params = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_4_7)
        .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("错误:" + e.getMessage());
    }
}

void resetConversation() {
    messages.clear();
    IO.println("因拒绝而重置对话");
}
<?php

use Anthropic\Client;

$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));
$messages = [];

function resetConversation(&$messages) {
    $messages = [];
    echo "因拒绝而重置对话\n";
}

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

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

client = Anthropic::Client.new
messages = []

def reset_conversation(messages)
  messages.clear
  puts "因拒绝而重置对话"
end

begin
  stream = client.messages.stream(
    model: :"claude-opus-4-7",
    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 "错误:#{e.message}"
end

当前拒绝类型

API 目前以三种不同方式处理拒绝:

拒绝类型响应格式发生时机
流式分类器拒绝stop_reason: refusal内容违反策略时的流式传输期间
API 输入和版权验证400 错误代码输入未通过验证检查时
模型生成的拒绝标准文本响应模型自身决定拒绝时
Note

未来的 API 版本将扩展 stop_reason: refusal 模式,以统一所有类型的拒绝处理。

最佳实践

  • 监控拒绝:在错误处理中包含 stop_reason: refusal 检查
  • 自动重置:检测到拒绝时实现自动上下文重置
  • 提供自定义消息:创建用户友好的消息以在拒绝发生时提供更好的用户体验
  • 跟踪拒绝模式:监控拒绝频率以识别提示的潜在问题

迁移说明

  • 未来的模型将把此模式扩展到其他拒绝类型
  • 规划您的错误处理以适应未来拒绝响应的统一