流式拒绝
从 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检查 - 自动重置:检测到拒绝时实现自动上下文重置
- 提供自定义消息:创建用户友好的消息以在拒绝发生时提供更好的用户体验
- 跟踪拒绝模式:监控拒绝频率以识别提示的潜在问题
迁移说明
- 未来的模型将把此模式扩展到其他拒绝类型
- 规划您的错误处理以适应未来拒绝响应的统一