エラーについて
API呼び出しの途中で、アプリが以下のエラーで落ちます:
openai.BadRequestError: Error code: 400 - {'error': {'message': 'The response was filtered due to the prompt triggering Azure OpenAI\'s content management policy. Please modify your prompt and retry.', 'type': 'content_filter', 'param': 'prompt', 'code': 'content_filter'}}
標準のOpenAI APIではより簡潔な表示になります:
openai.BadRequestError: The response was filtered
いずれの場合も、コンテンツモデレーション層がリクエストを遮断しています。入力プロンプトまたは生成された出力のどちらかが、コードに到達する前にブロックされています。これは500ではなく400エラーです。サーバーが失敗したのではなく、意図的に拒否したのです。
なぜこのエラーが発生するのか
すべてのプロンプトと補完はOpenAIのモデレーションパイプラインを通過します。このシステムは、ヘイト、自傷、性的コンテンツ、暴力などのカテゴリでコンテンツをスコアリングします。スコアは0.0から1.0の範囲で、いずれかのカテゴリで閾値を超えると、補完の代わりにこのエラーが返されます。
よくあるトリガー:
- 技術的または学術的なコンテキストでの敏感なキーワード(セキュリティ研究、医療に関する議論)
- サニタイズなしでプロンプトに直接埋め込まれた生のユーザー入力
- 暴力、虐待、または成人向けテーマを含む創作
- 危害に関連する状況を説明する医療または法律のシナリオ
- セキュリティ関連のプロンプト(CVE、エクスプロイトパターン、脆弱性の詳細についての質問)
- モデルの出力自体がフィルタリングされる場合 — プロンプトは問題なくても、補完がフィルタにかかることがある
最後のケースは意外と盲点です。モデルが何を生成するかは常に予測できないため、一見安全そうなプロンプトでもフィルタリングされた出力が生成されることがあります。
ステップ1 — フィルタリングされた箇所を特定する
何かを書き直す前に、問題が入力にあるのかモデルの出力にあるのかを確認してください。Moderation APIを使ってプロンプトを直接チェックします:
import openai
client = openai.OpenAI()
response = client.moderations.create(
input="Your prompt text here"
)
result = response.results[0]
print("Flagged:", result.flagged)
print("Categories:", result.categories)
print("Scores:", result.category_scores)
result.flaggedがTrueの場合、問題は入力にあります。result.categoriesの辞書で、violence: Trueやsexual/minors: Trueのように何がトリガーになったかを正確に確認できます。スコアは他のカテゴリが閾値にどれだけ近いかを示しており、プロンプトが断続的に失敗する場合に役立ちます。
flaggedがFalseなのにエラーが発生する場合は、出力がフィルタリングされています。finish_reasonの確認(ステップ4)に進んでください。
ステップ2 — プロンプトを書き直す
言い換えるだけで十分なことがほとんどです。効果的なパターンをいくつか紹介します:
- 感情的な言葉を臨床的な表現に置き換える。「Xを傷つける方法」→「Xに関連するリスクは何か」
- 意図を明確にするフレーミングを追加する:「セキュリティ監査のため」「フィクションのコンテキストで」「医療トレーニングデータセット向け」など。
- 長いプロンプトを分割する。500語のプロンプトに埋め込まれた1文がフラグされただけでも、リクエスト全体がブロックされます。
- APIに送信する前にユーザー入力をサニタイズする。ユーザーが送信した内容をそのまま信頼しないでください。
# 危険 — ユーザー入力がそのままプロンプトに入る
prompt = f"User asked: {user_input}\nAnswer:"
# 安全 — 先にチェックしてからプロンプトを構築する
moderation_check = client.moderations.create(input=user_input)
if moderation_check.results[0].flagged:
raise ValueError("User input contains flagged content.")
prompt = f"User asked: {user_input}\nAnswer:"
ステップ3 — コード内でエラーを適切にハンドリングする
コンテンツフィルターでアプリ全体がクラッシュしないようにしましょう。BadRequestErrorをキャッチして、ユーザーに意味のあるメッセージを表示します:
import openai
client = openai.OpenAI()
try:
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": user_message}
]
)
answer = response.choices[0].message.content
except openai.BadRequestError as e:
if "content_filter" in str(e) or "response was filtered" in str(e).lower():
answer = "Sorry, I can't respond to that request due to content policy."
else:
raise # 別のBadRequestError — 握りつぶさない
else: raiseは重要です。すべてのBadRequestErrorがコンテンツフィルターによるものとは限りません。無効なモデル名、不正なメッセージ形式、トークン制限超過も同じ例外タイプをスローします。
ステップ4 — 正常なレスポンスのfinish_reasonを確認する
より微妙なケースもあります:APIがHTTP 200を返すものの、生成途中で出力がフィルタリングされる場合です。この場合、finish_reasonはstopではなくcontent_filterになり、message.contentがNoneまたは途中で切り捨てられていることがあります。
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}]
)
choice = response.choices[0]
if choice.finish_reason == "content_filter":
print("Output was filtered — content may be incomplete or None")
print("Content:", choice.message.content) # NoneまたはTruncatedの可能性あり
else:
print(choice.message.content)
本番環境では常にfinish_reasonを確認してください。Noneの場合にmessage.contentを盲目的に読み込むと、元のフィルターエラーよりも追跡が難しいAttributeErrorがコードの深い部分でスローされます。
ステップ5 — Azure OpenAI 固有の対処法
Azure OpenAIはより細かい制御が可能です。コンテンツフィルターの厳格度はデプロイメントごとに設定でき、Azureポータルを通じて特定のカテゴリの閾値調整をリクエストできます。
Azure OpenAI → リソース → コンテンツフィルターに移動して、カスタムフィルタープロファイルを作成します。例えば、ゲームモデレーションサービスでは暴力フィルタリングを緩める必要があるかもしれません。Microsoftはケースバイケースでこれらを審査します。
Azureに固有のもう一つの注意点:デプロイメント名が間違っていると、コンテンツフィルターエラーのように見えるエラーが発生することがあります。必ず確認してください:
client = openai.AzureOpenAI(
azure_endpoint="https://your-resource.openai.azure.com/",
api_key="your-api-key",
api_version="2024-02-01"
)
response = client.chat.completions.create(
model="your-deployment-name", # Azureのデプロイメント名と完全に一致させること
messages=[{"role": "user", "content": prompt}]
)
修正の確認
完了とする前に2つの確認を行います:
import openai
client = openai.OpenAI()
# 1. プロンプトがクリーンであることを確認
mod = client.moderations.create(input=your_new_prompt)
print("Still flagged:", mod.results[0].flagged) # Falseであるべき
# 2. 実際の補完を実行
try:
res = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": your_new_prompt}]
)
print("finish_reason:", res.choices[0].finish_reason) # 'stop'であるべき
print("Response:", res.choices[0].message.content)
except openai.BadRequestError as e:
print("Still getting error:", e)
flagged: Falseかつfinish_reason: stop — これが望ましいクリーンな状態です。
補足のヒント
- **生のユーザー入力を直接プロンプトに渡さないこと。**特にユーザー生成コンテンツを扱うアプリでは、必ず事前にModeration APIでスクリーニングしてください。
- Moderation APIの呼び出しは無料です。高リスクのワークフローでは、すべての補完の前にゲートとして使用してください。レイテンシのコストは、失敗したリクエストと比べれば無視できるレベルです。
- システムプロンプトもフィルタリングのトリガーになり得ます。有害なキャラクターのロールプレイをモデルに指示するシステムメッセージは、ユーザーメッセージと同様にフィルタリングされます。
gpt-3.5-turboとgpt-4oでは感度が異なります。一方のモデルで失敗するプロンプトが、もう一方では通ることがあります。柔軟性がある場合は試してみる価値があります。- 医療教育、セキュリティ研究、法律分析など正当に敏感な用途については、OpenAIのサポートポータルを通じて利用ポリシーの例外申請が可能です。申請時にはユースケースを明確に文書化してください。

