---
title: コンテンツモデレーション
url: https://platform.claude.com/docs/ja/about-claude/use-case-guides/content-moderation
description: コンテンツモデレーションは、デジタルアプリケーションにおいて安全で、敬意があり、生産的な環境を維持するための重要な側面です。このガイドでは、Claudeを使用してデジタルアプリケーション内のコンテンツをモデレートする方法について説明します。
---

> Claudeを使用したコンテンツモデレーションの実装例については、[コンテンツモデレーションクックブック](https://platform.claude.com/cookbook/misc-building-moderation-filter)をご覧ください。

<Tip>
  このガイドは、アプリケーション内のユーザー生成コンテンツのモデレーションに焦点を当てています。Claudeとのやり取りのモデレーションに関するガイダンスをお探しの場合は、

  [ジェイルブレイクとプロンプトインジェクションの軽減](https://platform.claude.com/docs/ja/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks)

  を参照してください。
</Tip>

## Claudeで構築する前に

### コンテンツモデレーションにClaudeを使用するかどうかを決定する

コンテンツモデレーションに従来のMLやルールベースのアプローチではなく、ClaudeのようなLLMを使用すべきであることを示す主な指標を以下に示します。

<AccordionGroup>
  <Accordion title="コスト効率が高く迅速な実装を求めている">
    従来のML手法には、多大なエンジニアリングリソース、MLの専門知識、インフラストラクチャコストが必要です。人間によるモデレーションシステムにはさらに高いコストがかかります。Claudeを使用すれば、はるかに短い時間と低いコストで、高度なモデレーションシステムを稼働させることができます。
  </Accordion>

  <Accordion title="意味の理解と迅速な判断の両方を求めている">
    bag-of-wordsモデルや単純なパターンマッチングなどの従来のMLアプローチは、コンテンツのトーン、意図、文脈を理解するのに苦労することがよくあります。人間によるモデレーションシステムは意味の理解に優れていますが、コンテンツのレビューに時間がかかります。Claudeは、意味の理解とモデレーション判断を迅速に下す能力を組み合わせることで、両方のニーズに対応します。
  </Accordion>

  <Accordion title="一貫したポリシー判断が必要である">
    Claudeは高度な推論能力を活用することで、複雑なモデレーションガイドラインを一様に解釈して適用できます。この一貫性により、すべてのコンテンツの公平な扱いが確保され、ユーザーの信頼を損なう可能性のある一貫性のない、または偏ったモデレーション判断のリスクが軽減されます。
  </Accordion>

  <Accordion title="モデレーションポリシーが時間とともに変更または進化する可能性が高い">
    従来のMLアプローチは一度確立されると、変更するのは手間がかかりデータ集約的な作業になります。一方、製品や顧客のニーズが進化するにつれて、Claudeはトレーニングデータの大規模な再ラベル付けを行うことなく、モデレーションポリシーの変更や追加に容易に適応できます。
  </Accordion>

  <Accordion title="モデレーション判断に解釈可能な推論が必要である">
    モデレーション判断の背後にある明確な説明をユーザーや規制当局に提供したい場合、Claudeは詳細で一貫性のある根拠を生成できます。この透明性は、コンテンツモデレーションの実践において信頼を構築し、説明責任を確保するために重要です。
  </Accordion>

  <Accordion title="個別のモデルを維持することなく多言語サポートが必要である">
    従来のMLアプローチでは通常、サポートする言語ごとに個別のモデルや大規模な翻訳プロセスが必要です。人間によるモデレーションでは、サポートする各言語に堪能な人員を雇用する必要があります。Claudeの多言語機能により、個別のモデルや大規模な翻訳プロセスを必要とせずにさまざまな言語のチケットを分類でき、グローバルな顧客基盤に対するモデレーションを効率化できます。
  </Accordion>

  <Accordion title="マルチモーダルサポートが必要である">
    Claudeのマルチモーダル機能により、テキストと画像の両方にわたってコンテンツを分析および解釈できます。これにより、異なるメディアタイプを一緒に評価する必要がある環境において、包括的なコンテンツモデレーションのための汎用性の高いツールとなります。
  </Accordion>
</AccordionGroup>

<Note>
  すべてのClaudeモデルは、組み込みの安全動作を備えるようにトレーニングされています。そのため、使用するプロンプトに関係なく、Claudeが（

  [利用規約](https://www.anthropic.com/legal/aup)

  に沿って）特に危険とみなされるコンテンツをモデレートする場合があります。たとえば、ユーザーが露骨な性的コンテンツを投稿できるようにしたいアダルトサイトでは、プロンプトで露骨な性的コンテンツをモデレートしないように指定しても、Claudeが依然として露骨なコンテンツをモデレーションが必要なものとしてフラグ付けすることがあります。モデレーションソリューションを構築する前に、AUPを確認することを検討してください。
</Note>

### モデレートするコンテンツの例を生成する

コンテンツモデレーションソリューションを開発する前に、まずフラグ付けすべきコンテンツとフラグ付けすべきでないコンテンツの例を作成します。コンテンツモデレーションシステムが効果的に処理するのが難しい可能性のあるエッジケースや困難なシナリオを必ず含めてください。その後、例を見直して、明確に定義されたモデレーションカテゴリのリストを作成します。 たとえば、ソーシャルメディアプラットフォームが生成する例には、次のようなものが含まれる場合があります。

<CodeGroup exclude="shell">
  ```python Python
  client = anthropic.Anthropic()

  allowed_user_comments = [
      "This movie was great, I really enjoyed it. The main actor really killed it!",
      "I hate Mondays.",
      "It is a great time to invest in gold!",
  ]

  disallowed_user_comments = [
      "Delete this post now or you better hide. I am coming after you and your family.",
      "Stay away from the 5G cellphones!! They are using 5G to control you.",
      "Congratulations! You have won a $1,000 gift card. Click here to claim your prize!",
  ]

  # コンテンツモデレーションをテストするためのサンプルユーザーコメント
  user_comments = allowed_user_comments + disallowed_user_comments

  # コンテンツモデレーションで安全でないと見なされるカテゴリ
  unsafe_categories = [
      "Child Exploitation",
      "Conspiracy Theories",
      "Hate",
      "Indiscriminate Weapons",
      "Intellectual Property",
      "Non-Violent Crimes",
      "Privacy",
      "Self-Harm",
      "Sex Crimes",
      "Sexual Content",
      "Specialized Advice",
      "Violent Crimes",
  ]
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const allowedUserComments = [
    "This movie was great, I really enjoyed it. The main actor really killed it!",
    "I hate Mondays.",
    "It is a great time to invest in gold!"
  ];

  const disallowedUserComments = [
    "Delete this post now or you better hide. I am coming after you and your family.",
    "Stay away from the 5G cellphones!! They are using 5G to control you.",
    "Congratulations! You have won a $1,000 gift card. Click here to claim your prize!"
  ];

  // コンテンツモデレーションをテストするためのサンプルユーザーコメント
  const userComments = [...allowedUserComments, ...disallowedUserComments];

  // コンテンツモデレーションで安全でないと見なされるカテゴリ
  const unsafeCategories = [
    "Child Exploitation",
    "Conspiracy Theories",
    "Hate",
    "Indiscriminate Weapons",
    "Intellectual Property",
    "Non-Violent Crimes",
    "Privacy",
    "Self-Harm",
    "Sex Crimes",
    "Sexual Content",
    "Specialized Advice",
    "Violent Crimes"
  ];
  ```

  ```csharp C#
  var client = new AnthropicClient();

  string[] allowedUserComments =
  [
      "This movie was great, I really enjoyed it. The main actor really killed it!",
      "I hate Mondays.",
      "It is a great time to invest in gold!",
  ];

  string[] disallowedUserComments =
  [
      "Delete this post now or you better hide. I am coming after you and your family.",
      "Stay away from the 5G cellphones!! They are using 5G to control you.",
      "Congratulations! You have won a $1,000 gift card. Click here to claim your prize!",
  ];

  // コンテンツモデレーションをテストするためのサンプルユーザーコメント
  string[] userComments = [.. allowedUserComments, .. disallowedUserComments];

  // コンテンツモデレーションで安全でないと見なされるカテゴリ
  string[] unsafeCategories =
  [
      "Child Exploitation",
      "Conspiracy Theories",
      "Hate",
      "Indiscriminate Weapons",
      "Intellectual Property",
      "Non-Violent Crimes",
      "Privacy",
      "Self-Harm",
      "Sex Crimes",
      "Sexual Content",
      "Specialized Advice",
      "Violent Crimes",
  ];
  ```

  ```go Go
  var client = anthropic.NewClient()

  var allowedUserComments = []string{
  	"This movie was great, I really enjoyed it. The main actor really killed it!",
  	"I hate Mondays.",
  	"It is a great time to invest in gold!",
  }

  var disallowedUserComments = []string{
  	"Delete this post now or you better hide. I am coming after you and your family.",
  	"Stay away from the 5G cellphones!! They are using 5G to control you.",
  	"Congratulations! You have won a $1,000 gift card. Click here to claim your prize!",
  }

  // コンテンツモデレーションをテストするためのサンプルユーザーコメント
  var userComments = slices.Concat(allowedUserComments, disallowedUserComments)

  // コンテンツモデレーションで安全でないと見なされるカテゴリ
  var unsafeCategories = []string{
  	"Child Exploitation",
  	"Conspiracy Theories",
  	"Hate",
  	"Indiscriminate Weapons",
  	"Intellectual Property",
  	"Non-Violent Crimes",
  	"Privacy",
  	"Self-Harm",
  	"Sex Crimes",
  	"Sexual Content",
  	"Specialized Advice",
  	"Violent Crimes",
  }
  ```

  ```java Java
  final AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  final List<String> allowedUserComments = List.of(
          "This movie was great, I really enjoyed it. The main actor really killed it!",
          "I hate Mondays.",
          "It is a great time to invest in gold!");

  final List<String> disallowedUserComments = List.of(
          "Delete this post now or you better hide. I am coming after you and your family.",
          "Stay away from the 5G cellphones!! They are using 5G to control you.",
          "Congratulations! You have won a $1,000 gift card. Click here to claim your prize!");

  // コンテンツモデレーションをテストするためのサンプルユーザーコメント
  final List<String> userComments =
          Stream.concat(allowedUserComments.stream(), disallowedUserComments.stream()).toList();

  // コンテンツモデレーションで安全でないと見なされるカテゴリ
  final List<String> unsafeCategories = List.of(
          "Child Exploitation",
          "Conspiracy Theories",
          "Hate",
          "Indiscriminate Weapons",
          "Intellectual Property",
          "Non-Violent Crimes",
          "Privacy",
          "Self-Harm",
          "Sex Crimes",
          "Sexual Content",
          "Specialized Advice",
          "Violent Crimes");
  ```

  ```php PHP
  $client = new Client();

  $allowedUserComments = [
      'This movie was great, I really enjoyed it. The main actor really killed it!',
      'I hate Mondays.',
      'It is a great time to invest in gold!',
  ];

  $disallowedUserComments = [
      'Delete this post now or you better hide. I am coming after you and your family.',
      'Stay away from the 5G cellphones!! They are using 5G to control you.',
      'Congratulations! You have won a $1,000 gift card. Click here to claim your prize!',
  ];

  // コンテンツモデレーションをテストするためのサンプルユーザーコメント
  $userComments = [...$allowedUserComments, ...$disallowedUserComments];

  // コンテンツモデレーションで安全でないと見なされるカテゴリ
  $unsafeCategories = [
      'Child Exploitation',
      'Conspiracy Theories',
      'Hate',
      'Indiscriminate Weapons',
      'Intellectual Property',
      'Non-Violent Crimes',
      'Privacy',
      'Self-Harm',
      'Sex Crimes',
      'Sexual Content',
      'Specialized Advice',
      'Violent Crimes',
  ];
  ```

  ```ruby Ruby
  CLIENT = Anthropic::Client.new

  ALLOWED_USER_COMMENTS = [
    "This movie was great, I really enjoyed it. The main actor really killed it!",
    "I hate Mondays.",
    "It is a great time to invest in gold!"
  ]

  DISALLOWED_USER_COMMENTS = [
    "Delete this post now or you better hide. I am coming after you and your family.",
    "Stay away from the 5G cellphones!! They are using 5G to control you.",
    "Congratulations! You have won a $1,000 gift card. Click here to claim your prize!"
  ]

  # コンテンツモデレーションをテストするためのサンプルユーザーコメント
  USER_COMMENTS = ALLOWED_USER_COMMENTS + DISALLOWED_USER_COMMENTS

  # コンテンツモデレーションで安全でないと見なされるカテゴリ
  UNSAFE_CATEGORIES = [
    "Child Exploitation",
    "Conspiracy Theories",
    "Hate",
    "Indiscriminate Weapons",
    "Intellectual Property",
    "Non-Violent Crimes",
    "Privacy",
    "Self-Harm",
    "Sex Crimes",
    "Sexual Content",
    "Specialized Advice",
    "Violent Crimes"
  ]
  ```
</CodeGroup>

これらの例を効果的にモデレートするには、言語のニュアンスを理解する必要があります。`This movie was great, I really enjoyed it. The main actor really killed it!`というコメントでは、コンテンツモデレーションシステムは「killed it」が比喩であり、実際の暴力を示すものではないことを認識する必要があります。逆に、暴力への明示的な言及がないにもかかわらず、`Delete this post now or you better hide. I am coming after you and your family.`というコメントは、コンテンツモデレーションシステムによってフラグ付けされるべきです。

安全でないカテゴリは、特定のニーズに合わせてカスタマイズできます。たとえば、未成年者がウェブサイト上でコンテンツを作成するのを防ぎたい場合は、カテゴリに「Underage Posting」を追加できます。

***

## Claudeを使用してコンテンツをモデレートする方法

### 適切なClaudeモデルを選択する

モデルを選択する際は、データのサイズを考慮することが重要です。コストが懸念される場合は、Claude Haiku 5.5のような小型モデルが費用対効果の高さから優れた選択肢です。以下は、月に10億件の投稿を受け取るソーシャルメディアプラットフォームのテキストをモデレートするためのコストの見積もりです。

* **コンテンツサイズ**

  * 月あたりの投稿数：10億
  * 投稿あたりの文字数：100
  * 合計文字数：1,000億

* **推定トークン数**

  * 入力トークン：286億（3.5文字あたり1トークンと仮定）
  * フラグ付けされるメッセージの割合：3%
  * フラグ付けされたメッセージあたりの出力トークン：50
  * 合計出力トークン：15億

* **Claude Haiku 5.5の推定コスト**

  * 入力トークンコスト：28,600 MTok \* $0.10/MTok = $2,860 USD
  * 出力トークンコスト：1,500 MTok \* $0.50/MTok = $750 USD
  * 月額コスト：$2,860 + $750 = $3,610 USD

* **Claude Opus 5の推定コスト**

  * 入力トークンコスト：28,600 MTok \* $5.00/MTok = $143,000 USD
  * 出力トークンコスト：1,500 MTok \* $25.00/MTok = $37,500 USD
  * 月額コスト：$143,000 + $37,500 = $180,500 USD

* **Claude Opus 4.8の推定コスト**

  * 入力トークンコスト：28,600 MTok \* $5.00/MTok = $143,000 USD
  * 出力トークンコスト：1,500 MTok \* $25.00/MTok = $37,500 USD
  * 月額コスト：$143,000 + $37,500 = $180,500 USD

<Tip>
  実際のコストはこれらの見積もりと異なる場合があります。これらの見積もりは、

  [バッチ処理](https://platform.claude.com/docs/ja/about-claude/use-case-guides/content-moderation#consider-batch-processing)

  のセクションで取り上げているプロンプトに基づいています。レスポンスから

  `explanation`

  フィールドを削除することで、出力トークンをさらに削減できます。
</Tip>

### 強力なプロンプトを構築する

Claudeをコンテンツモデレーションに使用するには、Claudeがアプリケーションのモデレーション要件を理解している必要があります。まず、モデレーションのニーズを定義できるプロンプトを作成することから始めます。

<CodeGroup exclude="shell">
  ```python Python
  def moderate_message(message, unsafe_categories):
      # 安全でないカテゴリを、各カテゴリが改行で区切られた文字列に変換
      unsafe_category_str = "\n".join(unsafe_categories)

      # メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
      assessment_prompt = f"""
      Determine whether the following message warrants moderation,
      based on the unsafe categories outlined below.

      Message:
      <message>{message}</message>

      Unsafe Categories:
      <categories>
      {unsafe_category_str}
      </categories>

      Respond with ONLY a JSON object, using the format below:
      {{
      "violation": <Boolean field denoting whether the message should be moderated>,
      "categories": [Comma-separated list of violated categories],
      "explanation": [Optional. Only include if there is a violation.]
      }}
  Do not include markdown formatting or code fences in your response."""

      # コンテンツモデレーションのためにClaudeにリクエストを送信
      response = client.messages.create(
          model="claude-haiku-4-5-20251001",  # Using the Haiku model for lower costs
          max_tokens=200,
          messages=[{"role": "user", "content": assessment_prompt}],
      )

      # ClaudeからのJSONレスポンスを解析
      text_block = next(block for block in response.content if block.type == "text")
      assessment = json.loads(text_block.text)

      # 評価結果から違反ステータスを抽出
      contains_violation = assessment["violation"]

      # 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用
      violated_categories = assessment.get("categories", []) if contains_violation else []
      explanation = assessment.get("explanation") if contains_violation else None

      return contains_violation, violated_categories, explanation


  # 各コメントを処理して結果を出力
  for comment in user_comments:
      print(f"\nComment: {comment}")
      violation, violated_categories, explanation = moderate_message(
          comment, unsafe_categories
      )

      if violation:
          print(f"Violated Categories: {', '.join(violated_categories)}")
          print(f"Explanation: {explanation}")
      else:
          print("No issues detected.")
  ```

  ```typescript TypeScript
  // Claudeが返すJSON評価の形式
  interface ModerationAssessment {
    violation: boolean;
    categories?: string[];
    explanation?: string;
  }

  async function moderateMessage(
    message: string,
    unsafeCategories: string[]
  ): Promise<{ violation: boolean; violatedCategories: string[]; explanation?: string }> {
    // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
    const unsafeCategoryStr = unsafeCategories.join("\n");

    // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
    const assessmentPrompt = `
      Determine whether the following message warrants moderation,
      based on the unsafe categories outlined below.

      Message:
      <message>${message}</message>

      Unsafe Categories:
      <categories>
      ${unsafeCategoryStr}
      </categories>

      Respond with ONLY a JSON object, using the format below:
      {
      "violation": <Boolean field denoting whether the message should be moderated>,
      "categories": [Comma-separated list of violated categories],
      "explanation": [Optional. Only include if there is a violation.]
      }
  Do not include markdown formatting or code fences in your response.`;

    // コンテンツモデレーションのためにClaudeへリクエストを送信
    const response = await client.messages.create({
      model: "claude-haiku-4-5-20251001", // Using the Haiku model for lower costs
      max_tokens: 200,
      messages: [{ role: "user", content: assessmentPrompt }]
    });

    // ClaudeからのJSONレスポンスを解析
    const textBlock = response.content.find((block) => block.type === "text");
    if (!textBlock) {
      throw new Error("Expected a text block in the response");
    }
    const assessment: ModerationAssessment = JSON.parse(textBlock.text);

    // 評価から違反ステータスを抽出
    const containsViolation = assessment.violation;

    // 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用
    const violatedCategories = containsViolation ? (assessment.categories ?? []) : [];
    const explanation = containsViolation ? assessment.explanation : undefined;

    return { violation: containsViolation, violatedCategories, explanation };
  }

  // 各コメントを処理して結果を出力
  for (const comment of userComments) {
    console.log(`\nComment: ${comment}`);
    const { violation, violatedCategories, explanation } = await moderateMessage(
      comment,
      unsafeCategories
    );

    if (violation) {
      console.log(`Violated Categories: ${violatedCategories.join(", ")}`);
      console.log(`Explanation: ${explanation}`);
    } else {
      console.log("No issues detected.");
    }
  }
  ```

  ```csharp C#
  async Task<(bool ContainsViolation, List<string> ViolatedCategories, string? Explanation)> ModerateMessage(
      string message,
      IReadOnlyList<string> categories
  )
  {
      // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
      var unsafeCategoryText = string.Join("\n", categories);

      // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
      var assessmentPrompt = $$"""

      Determine whether the following message warrants moderation,
      based on the unsafe categories outlined below.

      Message:
      <message>{{message}}</message>

      Unsafe Categories:
      <categories>
      {{unsafeCategoryText}}
      </categories>

      Respond with ONLY a JSON object, using the format below:
      {
      "violation": <Boolean field denoting whether the message should be moderated>,
      "categories": [Comma-separated list of violated categories],
      "explanation": [Optional. Only include if there is a violation.]
      }
  Do not include markdown formatting or code fences in your response.
  """;

      // コンテンツモデレーションのためにClaudeへリクエストを送信
      var response = await client.Messages.Create(
          new()
          {
              Model = Model.ClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
              MaxTokens = 200,
              Messages = [new() { Role = Role.User, Content = assessmentPrompt }],
          }
      );

      // 最初のコンテンツブロックをテキストブロックに絞り込み、ClaudeのJSONレスポンスを解析
      if (!response.Content[0].TryPickText(out var textBlock))
      {
          throw new InvalidOperationException("Expected a text response from Claude.");
      }
      var assessment = JsonNode.Parse(textBlock.Text)!;

      // 評価結果から違反ステータスを抽出
      var containsViolation = assessment["violation"]!.GetValue<bool>();

      // 違反がある場合はカテゴリと説明を取得し、そうでなければ空のデフォルト値を使用
      List<string> violatedCategories = containsViolation
          ? assessment["categories"]?.AsArray().Select(category => category!.GetValue<string>()).ToList() ?? []
          : [];
      var explanation = containsViolation ? assessment["explanation"]?.GetValue<string>() : null;

      return (containsViolation, violatedCategories, explanation);
  }

  // 各コメントを処理して結果を出力
  foreach (var comment in userComments)
  {
      Console.WriteLine($"\nComment: {comment}");
      var (violation, violatedCategories, explanation) = await ModerateMessage(comment, unsafeCategories);

      if (violation)
      {
          Console.WriteLine($"Violated Categories: {string.Join(", ", violatedCategories)}");
          Console.WriteLine($"Explanation: {explanation}");
      }
      else
      {
          Console.WriteLine("No issues detected.");
      }
  }
  ```

  ```go Go
  func moderateMessage(message string, unsafeCategories []string) (bool, []string, string) {
  	// 安全でないカテゴリを、各カテゴリが改行で区切られた文字列に変換
  	unsafeCategoryStr := strings.Join(unsafeCategories, "\n")

  	// メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
  	assessmentPrompt := fmt.Sprintf(`
      Determine whether the following message warrants moderation,
      based on the unsafe categories outlined below.

      Message:
      <message>%s</message>

      Unsafe Categories:
      <categories>
      %s
      </categories>

      Respond with ONLY a JSON object, using the format below:
      {
      "violation": <Boolean field denoting whether the message should be moderated>,
      "categories": [Comma-separated list of violated categories],
      "explanation": [Optional. Only include if there is a violation.]
      }
  Do not include markdown formatting or code fences in your response.`, message, unsafeCategoryStr)

  	// コンテンツモデレーションのためにClaudeへリクエストを送信
  	response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
  		MaxTokens: 200,
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock(assessmentPrompt)),
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	// テキストを読み取る前に、最初のコンテンツブロックをテキストブロックに絞り込む
  	textBlock, ok := response.Content[0].AsAny().(anthropic.TextBlock)
  	if !ok {
  		log.Fatalf("expected a text block, got %q", response.Content[0].Type)
  	}

  	// ClaudeからのJSONレスポンスを解析
  	var assessment struct {
  		Violation   bool     `json:"violation"`
  		Categories  []string `json:"categories"`
  		Explanation string   `json:"explanation"`
  	}
  	if err := json.Unmarshal([]byte(textBlock.Text), &assessment); err != nil {
  		log.Fatal(err)
  	}

  	// 違反がある場合はカテゴリと説明を返し、そうでない場合は空のデフォルト値を使用
  	if !assessment.Violation {
  		return false, nil, ""
  	}
  	return true, assessment.Categories, assessment.Explanation
  }

  // moderateAllCommentsは各コメントを処理し、結果を出力します。
  func moderateAllComments() {
  	for _, comment := range userComments {
  		fmt.Printf("\nComment: %s\n", comment)
  		violation, violatedCategories, explanation := moderateMessage(comment, unsafeCategories)

  		if violation {
  			fmt.Printf("Violated Categories: %s\n", strings.Join(violatedCategories, ", "))
  			fmt.Printf("Explanation: %s\n", explanation)
  		} else {
  			fmt.Println("No issues detected.")
  		}
  	}
  }
  ```

  ```java Java
  record ModerationResult(boolean violation, List<String> violatedCategories, String explanation) {}

  ModerationResult moderateMessage(String message, List<String> unsafeCategories)
          throws JsonProcessingException {
      // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
      String unsafeCategoryStr = String.join("\n", unsafeCategories);

      // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
      String assessmentPrompt = """

              Determine whether the following message warrants moderation,
              based on the unsafe categories outlined below.

              Message:
              <message>%s</message>

              Unsafe Categories:
              <categories>
              %s
              </categories>

              Respond with ONLY a JSON object, using the format below:
              {
              "violation": <Boolean field denoting whether the message should be moderated>,
              "categories": [Comma-separated list of violated categories],
              "explanation": [Optional. Only include if there is a violation.]
              }
          Do not include markdown formatting or code fences in your response."""
              .formatted(message, unsafeCategoryStr);

      // コンテンツモデレーションのためにClaudeにリクエストを送信
      Message response = client.messages().create(MessageCreateParams.builder()
              .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
              .maxTokens(200)
              .addUserMessage(assessmentPrompt)
              .build());

      // ClaudeからのJSONレスポンスを解析
      String assessmentJson = response.content().stream()
              .flatMap(contentBlock -> contentBlock.text().stream())
              .findFirst()
              .orElseThrow()
              .text();
      ObjectMapper mapper = new ObjectMapper();
      JsonNode assessment = mapper.readTree(assessmentJson);

      // 評価結果から違反ステータスを抽出
      boolean containsViolation = assessment.required("violation").asBoolean();

      // 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用
      List<String> violatedCategories = containsViolation && assessment.has("categories")
              ? mapper.convertValue(assessment.get("categories"), new TypeReference<List<String>>() {})
              : List.of();
      String explanation = containsViolation && assessment.hasNonNull("explanation")
              ? assessment.get("explanation").asText()
              : null;

      return new ModerationResult(containsViolation, violatedCategories, explanation);
  }

  // 各コメントを処理して結果を出力
  void printModerationResults() throws JsonProcessingException {
      for (String comment : userComments) {
          IO.println("\nComment: " + comment);
          ModerationResult result = moderateMessage(comment, unsafeCategories);

          if (result.violation()) {
              IO.println("Violated Categories: " + String.join(", ", result.violatedCategories()));
              IO.println("Explanation: " + result.explanation());
          } else {
              IO.println("No issues detected.");
          }
      }
  }
  ```

  ```php PHP
  $moderateMessage = function (string $message, array $unsafeCategories) use ($client): array {
      // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換します
      $unsafeCategoryStr = implode("\n", $unsafeCategories);

      // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築します
      $assessmentPrompt = <<<PROMPT

          Determine whether the following message warrants moderation,
          based on the unsafe categories outlined below.

          Message:
          <message>{$message}</message>

          Unsafe Categories:
          <categories>
          {$unsafeCategoryStr}
          </categories>

          Respond with ONLY a JSON object, using the format below:
          {
          "violation": <Boolean field denoting whether the message should be moderated>,
          "categories": [Comma-separated list of violated categories],
          "explanation": [Optional. Only include if there is a violation.]
          }
      Do not include markdown formatting or code fences in your response.
      PROMPT;

      // コンテンツモデレーションのためにClaudeへリクエストを送信します
      $response = $client->messages->create(
          model: 'claude-haiku-4-5-20251001', // Using the Haiku model for lower costs
          maxTokens: 200,
          messages: [['role' => 'user', 'content' => $assessmentPrompt]],
      );

      // ClaudeからのJSONレスポンスを解析します。SDKは各コンテンツブロックを
      // 具象クラスにデコードするため、テキストを読み取る前にTextBlockを見つけます。
      $textBlock = array_find($response->content, fn ($block) => $block instanceof \Anthropic\Messages\TextBlock)
          ?? throw new \RuntimeException('Expected a text block in the response.');
      $assessment = json_decode($textBlock->text, associative: true, flags: JSON_THROW_ON_ERROR);

      // 評価結果から違反ステータスを抽出します
      $containsViolation = $assessment['violation'];

      // 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用します
      $violatedCategories = $containsViolation ? ($assessment['categories'] ?? []) : [];
      $explanation = $containsViolation ? ($assessment['explanation'] ?? null) : null;

      return [$containsViolation, $violatedCategories, $explanation];
  };

  // 各コメントを処理して結果を出力します
  foreach ($userComments as $comment) {
      echo "\nComment: {$comment}\n";
      [$violation, $violatedCategories, $explanation] = $moderateMessage($comment, $unsafeCategories);

      if ($violation) {
          echo 'Violated Categories: ' . implode(', ', $violatedCategories) . "\n";
          echo "Explanation: {$explanation}\n";
      } else {
          echo "No issues detected.\n";
      }
  }
  ```

  ```ruby Ruby
  def moderate_message(message, unsafe_categories)
    # 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
    unsafe_category_str = unsafe_categories.join("\n")

    # メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
    assessment_prompt = <<~PROMPT.chomp

          Determine whether the following message warrants moderation,
          based on the unsafe categories outlined below.

          Message:
          <message>#{message}</message>

          Unsafe Categories:
          <categories>
          #{unsafe_category_str}
          </categories>

          Respond with ONLY a JSON object, using the format below:
          {
          "violation": <Boolean field denoting whether the message should be moderated>,
          "categories": [Comma-separated list of violated categories],
          "explanation": [Optional. Only include if there is a violation.]
          }
      Do not include markdown formatting or code fences in your response.
    PROMPT

    # コンテンツモデレーションのためにClaudeにリクエストを送信
    response = CLIENT.messages.create(
      model: "claude-haiku-4-5-20251001", # Using the Haiku model for lower costs
      max_tokens: 200,
      messages: [{role: :user, content: assessment_prompt}]
    )

    # ClaudeからのJSONレスポンスを解析
    text_block = response.content.find { it.type == :text }
    assessment = JSON.parse(text_block.text)

    # 評価結果から違反ステータスを抽出
    contains_violation = assessment["violation"]

    # 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用
    violated_categories = contains_violation ? assessment.fetch("categories", []) : []
    explanation = contains_violation ? assessment["explanation"] : nil

    [contains_violation, violated_categories, explanation]
  end


  # 各コメントを処理して結果を出力
  USER_COMMENTS.each do |comment|
    puts "\nComment: #{comment}"
    violation, violated_categories, explanation = moderate_message(comment, UNSAFE_CATEGORIES)

    if violation
      puts "Violated Categories: #{violated_categories.join(", ")}"
      puts "Explanation: #{explanation}"
    else
      puts "No issues detected."
    end
  end
  ```
</CodeGroup>

この例では、`moderate_message`関数に、安全でないコンテンツカテゴリと評価対象のメッセージを含む評価プロンプトが含まれています。このプロンプトは、先に定義した安全でないカテゴリに基づいて、メッセージをモデレートすべきかどうかを評価するようClaudeに求めます。

次に、モデルの評価が解析され、違反があるかどうかが判断されます。違反がある場合、Claudeは違反したカテゴリのリストと、そのメッセージが安全でない理由の説明も返します。

### プロンプトを評価する

コンテンツモデレーションは分類問題です。したがって、[分類クックブック](https://platform.claude.com/cookbook/capabilities-classification-guide)で概説されているのと同じ手法を使用して、コンテンツモデレーションシステムの精度を判断できます。

もう1つの考慮事項として、コンテンツモデレーションを二値分類問題として扱う代わりに、さまざまなリスクレベルを表す複数のカテゴリを作成することもできます。複数のリスクレベルを作成することで、モデレーションの厳しさを調整できます。たとえば、高リスクとみなされるユーザークエリは自動的にブロックし、中リスクのクエリが多いユーザーは人間によるレビューのためにフラグ付けする、といったことが考えられます。

<CodeGroup exclude="shell">
  ```python Python
  def assess_risk_level(message, unsafe_categories):
      # 安全でないカテゴリを、各カテゴリが改行で区切られた文字列に変換
      unsafe_category_str = "\n".join(unsafe_categories)

      # メッセージ、安全でないカテゴリ、リスクレベル定義を含む、Claude用のプロンプトを構築
      assessment_prompt = f"""
      Assess the risk level of the following message,
      based on the unsafe categories listed below.

  Message:
  <message>{message}</message>

  Unsafe Categories:
  <categories>
  {unsafe_category_str}
  </categories>

  Assign a risk level based on your confidence that the user's message should be moderated
  based on the defined unsafe categories:

  0 - No risk
  1 - Low risk
  2 - Medium risk
  3 - High risk

  Respond with ONLY a JSON object, using the format below:
  {{
    "risk_level": <Numerical field denoting the risk level>,
    "categories": [Comma-separated list of violated categories],
    "explanation": <Optional. Only include if risk level is greater than 0>
  }}
  Do not include markdown formatting or code fences in your response."""

      # リスク評価のためにClaudeにリクエストを送信
      response = client.messages.create(
          model="claude-haiku-4-5-20251001",  # Using the Haiku model for lower costs
          max_tokens=200,
          messages=[{"role": "user", "content": assessment_prompt}],
      )

      # ClaudeからのJSONレスポンスを解析
      text_block = next(block for block in response.content if block.type == "text")
      assessment = json.loads(text_block.text)

      # 評価結果からリスクレベル、違反カテゴリ、説明を抽出
      risk_level = assessment["risk_level"]
      violated_categories = assessment["categories"]
      explanation = assessment.get("explanation")

      return risk_level, violated_categories, explanation


  # 各コメントを処理して結果を出力
  for comment in user_comments:
      print(f"\nComment: {comment}")
      risk_level, violated_categories, explanation = assess_risk_level(
          comment, unsafe_categories
      )

      print(f"Risk Level: {risk_level}")
      if violated_categories:
          print(f"Violated Categories: {', '.join(violated_categories)}")
      if explanation:
          print(f"Explanation: {explanation}")
  ```

  ```typescript TypeScript
  // Claudeが返すJSONリスク評価の形式
  interface RiskAssessment {
    risk_level: number;
    categories: string[];
    explanation?: string;
  }

  async function assessRiskLevel(
    message: string,
    unsafeCategories: string[]
  ): Promise<{ riskLevel: number; violatedCategories: string[]; explanation?: string }> {
    // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
    const unsafeCategoryStr = unsafeCategories.join("\n");

    // メッセージ、安全でないカテゴリ、リスクレベル定義を含む、Claude用のプロンプトを構築
    const assessmentPrompt = `
      Assess the risk level of the following message,
      based on the unsafe categories listed below.

  Message:
  <message>${message}</message>

  Unsafe Categories:
  <categories>
  ${unsafeCategoryStr}
  </categories>

  Assign a risk level based on your confidence that the user's message should be moderated
  based on the defined unsafe categories:

  0 - No risk
  1 - Low risk
  2 - Medium risk
  3 - High risk

  Respond with ONLY a JSON object, using the format below:
  {
    "risk_level": <Numerical field denoting the risk level>,
    "categories": [Comma-separated list of violated categories],
    "explanation": <Optional. Only include if risk level is greater than 0>
  }
  Do not include markdown formatting or code fences in your response.`;

    // リスク評価のためにClaudeへリクエストを送信
    const response = await client.messages.create({
      model: "claude-haiku-4-5-20251001", // Using the Haiku model for lower costs
      max_tokens: 200,
      messages: [{ role: "user", content: assessmentPrompt }]
    });

    // ClaudeからのJSONレスポンスを解析
    const textBlock = response.content.find((block) => block.type === "text");
    if (!textBlock) {
      throw new Error("Expected a text block in the response");
    }
    const assessment: RiskAssessment = JSON.parse(textBlock.text);

    // 評価からリスクレベル、違反カテゴリ、説明を抽出
    const { risk_level: riskLevel, categories: violatedCategories, explanation } = assessment;

    return { riskLevel, violatedCategories, explanation };
  }

  // 各コメントを処理して結果を出力
  for (const comment of userComments) {
    console.log(`\nComment: ${comment}`);
    const { riskLevel, violatedCategories, explanation } = await assessRiskLevel(
      comment,
      unsafeCategories
    );

    console.log(`Risk Level: ${riskLevel}`);
    if (violatedCategories.length > 0) {
      console.log(`Violated Categories: ${violatedCategories.join(", ")}`);
    }
    if (explanation) {
      console.log(`Explanation: ${explanation}`);
    }
  }
  ```

  ```csharp C#
  async Task<(int RiskLevel, List<string> ViolatedCategories, string? Explanation)> AssessRiskLevel(
      string message,
      IReadOnlyList<string> categories
  )
  {
      // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
      var unsafeCategoryText = string.Join("\n", categories);

      // メッセージ、安全でないカテゴリ、リスクレベル定義を含む、Claude用のプロンプトを構築
      var assessmentPrompt = $$"""

      Assess the risk level of the following message,
      based on the unsafe categories listed below.

  Message:
  <message>{{message}}</message>

  Unsafe Categories:
  <categories>
  {{unsafeCategoryText}}
  </categories>

  Assign a risk level based on your confidence that the user's message should be moderated
  based on the defined unsafe categories:

  0 - No risk
  1 - Low risk
  2 - Medium risk
  3 - High risk

  Respond with ONLY a JSON object, using the format below:
  {
    "risk_level": <Numerical field denoting the risk level>,
    "categories": [Comma-separated list of violated categories],
    "explanation": <Optional. Only include if risk level is greater than 0>
  }
  Do not include markdown formatting or code fences in your response.
  """;

      // リスク評価のためにClaudeへリクエストを送信
      var response = await client.Messages.Create(
          new()
          {
              Model = Model.ClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
              MaxTokens = 200,
              Messages = [new() { Role = Role.User, Content = assessmentPrompt }],
          }
      );

      // 最初のコンテンツブロックをテキストブロックに絞り込み、ClaudeのJSONレスポンスを解析
      if (!response.Content[0].TryPickText(out var textBlock))
      {
          throw new InvalidOperationException("Expected a text response from Claude.");
      }
      var assessment = JsonNode.Parse(textBlock.Text)!;

      // 評価結果からリスクレベル、違反カテゴリ、説明を抽出
      var riskLevel = assessment["risk_level"]!.GetValue<int>();
      var violatedCategories = assessment["categories"]!
          .AsArray()
          .Select(category => category!.GetValue<string>())
          .ToList();
      var explanation = assessment["explanation"]?.GetValue<string>();

      return (riskLevel, violatedCategories, explanation);
  }

  // 各コメントを処理して結果を出力
  foreach (var comment in userComments)
  {
      Console.WriteLine($"\nComment: {comment}");
      var (riskLevel, violatedCategories, explanation) = await AssessRiskLevel(comment, unsafeCategories);

      Console.WriteLine($"Risk Level: {riskLevel}");
      if (violatedCategories.Count > 0)
      {
          Console.WriteLine($"Violated Categories: {string.Join(", ", violatedCategories)}");
      }
      if (!string.IsNullOrEmpty(explanation))
      {
          Console.WriteLine($"Explanation: {explanation}");
      }
  }
  ```

  ```go Go
  func assessRiskLevel(message string, unsafeCategories []string) (int, []string, string) {
  	// 安全でないカテゴリを、各カテゴリが改行で区切られた文字列に変換
  	unsafeCategoryStr := strings.Join(unsafeCategories, "\n")

  	// メッセージ、安全でないカテゴリ、リスクレベル定義を含む、Claude用のプロンプトを構築
  	assessmentPrompt := fmt.Sprintf(`
      Assess the risk level of the following message,
      based on the unsafe categories listed below.

  Message:
  <message>%s</message>

  Unsafe Categories:
  <categories>
  %s
  </categories>

  Assign a risk level based on your confidence that the user's message should be moderated
  based on the defined unsafe categories:

  0 - No risk
  1 - Low risk
  2 - Medium risk
  3 - High risk

  Respond with ONLY a JSON object, using the format below:
  {
    "risk_level": <Numerical field denoting the risk level>,
    "categories": [Comma-separated list of violated categories],
    "explanation": <Optional. Only include if risk level is greater than 0>
  }
  Do not include markdown formatting or code fences in your response.`, message, unsafeCategoryStr)

  	// リスク評価のためにClaudeへリクエストを送信
  	response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
  		MaxTokens: 200,
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock(assessmentPrompt)),
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	// テキストを読み取る前に、最初のコンテンツブロックをテキストブロックに絞り込む
  	textBlock, ok := response.Content[0].AsAny().(anthropic.TextBlock)
  	if !ok {
  		log.Fatalf("expected a text block, got %q", response.Content[0].Type)
  	}

  	// ClaudeからのJSONレスポンスを解析
  	var assessment struct {
  		RiskLevel   int      `json:"risk_level"`
  		Categories  []string `json:"categories"`
  		Explanation string   `json:"explanation"`
  	}
  	if err := json.Unmarshal([]byte(textBlock.Text), &assessment); err != nil {
  		log.Fatal(err)
  	}

  	// 評価からリスクレベル、違反したカテゴリ、説明を返す
  	return assessment.RiskLevel, assessment.Categories, assessment.Explanation
  }

  // assessAllRiskLevelsは各コメントを処理し、結果を出力します。
  func assessAllRiskLevels() {
  	for _, comment := range userComments {
  		fmt.Printf("\nComment: %s\n", comment)
  		riskLevel, violatedCategories, explanation := assessRiskLevel(comment, unsafeCategories)

  		fmt.Printf("Risk Level: %d\n", riskLevel)
  		if len(violatedCategories) > 0 {
  			fmt.Printf("Violated Categories: %s\n", strings.Join(violatedCategories, ", "))
  		}
  		if explanation != "" {
  			fmt.Printf("Explanation: %s\n", explanation)
  		}
  	}
  }
  ```

  ```java Java
  record RiskAssessment(int riskLevel, List<String> violatedCategories, String explanation) {}

  RiskAssessment assessRiskLevel(String message, List<String> unsafeCategories)
          throws JsonProcessingException {
      // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
      String unsafeCategoryStr = String.join("\n", unsafeCategories);

      // メッセージ、安全でないカテゴリ、リスクレベル定義を含む、Claude用のプロンプトを構築
      String assessmentPrompt = """

              Assess the risk level of the following message,
              based on the unsafe categories listed below.

          Message:
          <message>%s</message>

          Unsafe Categories:
          <categories>
          %s
          </categories>

          Assign a risk level based on your confidence that the user's message should be moderated
          based on the defined unsafe categories:

          0 - No risk
          1 - Low risk
          2 - Medium risk
          3 - High risk

          Respond with ONLY a JSON object, using the format below:
          {
            "risk_level": <Numerical field denoting the risk level>,
            "categories": [Comma-separated list of violated categories],
            "explanation": <Optional. Only include if risk level is greater than 0>
          }
          Do not include markdown formatting or code fences in your response."""
              .formatted(message, unsafeCategoryStr);

      // リスク評価のためにClaudeにリクエストを送信
      Message response = client.messages().create(MessageCreateParams.builder()
              .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
              .maxTokens(200)
              .addUserMessage(assessmentPrompt)
              .build());

      // ClaudeからのJSONレスポンスを解析
      String assessmentJson = response.content().stream()
              .flatMap(contentBlock -> contentBlock.text().stream())
              .findFirst()
              .orElseThrow()
              .text();
      ObjectMapper mapper = new ObjectMapper();
      JsonNode assessment = mapper.readTree(assessmentJson);

      // 評価結果からリスクレベル、違反カテゴリ、説明を抽出
      int riskLevel = assessment.required("risk_level").asInt();
      JsonNode categoriesNode = assessment.required("categories");
      List<String> violatedCategories = categoriesNode.isNull()
              ? List.of()
              : mapper.convertValue(categoriesNode, new TypeReference<List<String>>() {});
      String explanation = assessment.hasNonNull("explanation")
              ? assessment.get("explanation").asText()
              : null;

      return new RiskAssessment(riskLevel, violatedCategories, explanation);
  }

  // 各コメントを処理して結果を出力
  void printRiskLevels() throws JsonProcessingException {
      for (String comment : userComments) {
          IO.println("\nComment: " + comment);
          RiskAssessment assessment = assessRiskLevel(comment, unsafeCategories);

          IO.println("Risk Level: " + assessment.riskLevel());
          if (!assessment.violatedCategories().isEmpty()) {
              IO.println("Violated Categories: " + String.join(", ", assessment.violatedCategories()));
          }
          if (assessment.explanation() != null && !assessment.explanation().isEmpty()) {
              IO.println("Explanation: " + assessment.explanation());
          }
      }
  }
  ```

  ```php PHP
  $assessRiskLevel = function (string $message, array $unsafeCategories) use ($client): array {
      // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換します
      $unsafeCategoryStr = implode("\n", $unsafeCategories);

      // メッセージ、安全でないカテゴリ、リスクレベル定義を含む、Claude用のプロンプトを構築します
      $assessmentPrompt = <<<PROMPT

          Assess the risk level of the following message,
          based on the unsafe categories listed below.

      Message:
      <message>{$message}</message>

      Unsafe Categories:
      <categories>
      {$unsafeCategoryStr}
      </categories>

      Assign a risk level based on your confidence that the user's message should be moderated
      based on the defined unsafe categories:

      0 - No risk
      1 - Low risk
      2 - Medium risk
      3 - High risk

      Respond with ONLY a JSON object, using the format below:
      {
        "risk_level": <Numerical field denoting the risk level>,
        "categories": [Comma-separated list of violated categories],
        "explanation": <Optional. Only include if risk level is greater than 0>
      }
      Do not include markdown formatting or code fences in your response.
      PROMPT;

      // リスク評価のためにClaudeへリクエストを送信します
      $response = $client->messages->create(
          model: 'claude-haiku-4-5-20251001', // Using the Haiku model for lower costs
          maxTokens: 200,
          messages: [['role' => 'user', 'content' => $assessmentPrompt]],
      );

      // ClaudeからのJSONレスポンスを解析します。SDKは各コンテンツブロックを
      // 具象クラスにデコードするため、テキストを読み取る前にTextBlockを見つけます。
      $textBlock = array_find($response->content, fn ($block) => $block instanceof \Anthropic\Messages\TextBlock)
          ?? throw new \RuntimeException('Expected a text block in the response.');
      $assessment = json_decode($textBlock->text, associative: true, flags: JSON_THROW_ON_ERROR);

      // 評価結果からリスクレベル、違反カテゴリ、説明を抽出します
      $riskLevel = $assessment['risk_level'];
      $violatedCategories = $assessment['categories'];
      $explanation = $assessment['explanation'] ?? null;

      return [$riskLevel, $violatedCategories, $explanation];
  };

  // 各コメントを処理して結果を出力します
  foreach ($userComments as $comment) {
      echo "\nComment: {$comment}\n";
      [$riskLevel, $violatedCategories, $explanation] = $assessRiskLevel($comment, $unsafeCategories);

      echo "Risk Level: {$riskLevel}\n";
      if ($violatedCategories) {
          echo 'Violated Categories: ' . implode(', ', $violatedCategories) . "\n";
      }
      if ($explanation) {
          echo "Explanation: {$explanation}\n";
      }
  }
  ```

  ```ruby Ruby
  def assess_risk_level(message, unsafe_categories)
    # 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
    unsafe_category_str = unsafe_categories.join("\n")

    # メッセージ、安全でないカテゴリ、リスクレベル定義を含む、Claude用のプロンプトを構築
    assessment_prompt = <<~PROMPT.chomp

          Assess the risk level of the following message,
          based on the unsafe categories listed below.

      Message:
      <message>#{message}</message>

      Unsafe Categories:
      <categories>
      #{unsafe_category_str}
      </categories>

      Assign a risk level based on your confidence that the user's message should be moderated
      based on the defined unsafe categories:

      0 - No risk
      1 - Low risk
      2 - Medium risk
      3 - High risk

      Respond with ONLY a JSON object, using the format below:
      {
        "risk_level": <Numerical field denoting the risk level>,
        "categories": [Comma-separated list of violated categories],
        "explanation": <Optional. Only include if risk level is greater than 0>
      }
      Do not include markdown formatting or code fences in your response.
    PROMPT

    # リスク評価のためにClaudeにリクエストを送信
    response = CLIENT.messages.create(
      model: "claude-haiku-4-5-20251001", # Using the Haiku model for lower costs
      max_tokens: 200,
      messages: [{role: :user, content: assessment_prompt}]
    )

    # ClaudeからのJSONレスポンスを解析
    text_block = response.content.find { it.type == :text }
    assessment = JSON.parse(text_block.text)

    # 評価結果からリスクレベル、違反カテゴリ、説明を抽出
    risk_level = assessment["risk_level"]
    violated_categories = assessment["categories"]
    explanation = assessment["explanation"]

    [risk_level, violated_categories, explanation]
  end


  # 各コメントを処理して結果を出力
  USER_COMMENTS.each do |comment|
    puts "\nComment: #{comment}"
    risk_level, violated_categories, explanation = assess_risk_level(comment, UNSAFE_CATEGORIES)

    puts "Risk Level: #{risk_level}"
    puts "Violated Categories: #{violated_categories.join(", ")}" if violated_categories&.any?
    puts "Explanation: #{explanation}" if explanation
  end
  ```
</CodeGroup>

このコードは、Claudeを使用してメッセージのリスクレベルを評価する`assess_risk_level`関数を実装しています。この関数は、メッセージと安全でないカテゴリを入力として受け取ります。

関数内では、評価対象のメッセージ、安全でないカテゴリ、およびリスクレベルを評価するための具体的な指示を含むプロンプトがClaude向けに生成されます。このプロンプトは、リスクレベル、違反したカテゴリ、およびオプションの説明を含むJSONオブジェクトで応答するようClaudeに指示します。

このアプローチにより、リスクレベルを割り当てることで柔軟なコンテンツモデレーションが可能になります。より大きなシステムにシームレスに統合して、評価されたリスクレベルに基づいてコンテンツフィルタリングを自動化したり、人間によるレビューのためにコメントにフラグを付けたりできます。たとえば、このコードを実行すると、`Delete this post now or you better hide. I am coming after you and your family.`というコメントは、危険な脅迫であるため高リスクと識別されます。逆に、`Stay away from the 5G cellphones!! They are using 5G to control you.`というコメントは中リスクに分類されます。

### プロンプトをデプロイする

ソリューションの品質に自信が持てたら、本番環境にデプロイします。本番環境でコンテンツモデレーションを使用する際に従うべきベストプラクティスを以下に示します。

1. **ユーザーに明確なフィードバックを提供する：** コンテンツモデレーションによってユーザー入力がブロックされたり、レスポンスにフラグが付けられたりした場合は、メッセージにフラグが付けられた理由と、適切に言い換える方法をユーザーが理解できるよう、有益で建設的なフィードバックを提供します。前述のコード例では、これはClaudeのレスポンスの`explanation`フィールドを通じて行われます。

2. **モデレートされたコンテンツを分析する：** モデレーションシステムによってフラグ付けされているコンテンツの種類を追跡し、傾向や改善の余地がある領域を特定します。

3. **継続的に評価し改善する：** 適合率や再現率の追跡などの指標を使用して、コンテンツモデレーションシステムのパフォーマンスを定期的に評価します。このデータを使用して、モデレーションプロンプト、キーワード、評価基準を反復的に改良します。

***

## パフォーマンスを向上させる

複雑なシナリオでは、標準的な[プロンプトエンジニアリング手法](https://platform.claude.com/docs/ja/build-with-claude/prompt-engineering/overview)に加えて、パフォーマンスを向上させるための追加の戦略を検討することが役立つ場合があります。以下に高度な戦略をいくつか示します。

### トピックを定義し、例を提供する

プロンプトに安全でないカテゴリを列挙することに加えて、各カテゴリに関連する定義やフレーズを提供することで、さらなる改善が可能です。

<CodeGroup exclude="shell">
  ```python Python
  # コンテンツモデレーションで安全でないと見なされるカテゴリとその定義
  unsafe_category_definitions = {
      "Child Exploitation": "Content that depicts child nudity or that enables, encourages, excuses, or depicts the sexual abuse of children.",
      "Conspiracy Theories": """Content that promotes or endorses unfounded, false, or misleading theories about events, situations, or individuals,
          which can incite harm or undermine public trust in institutions or facts.""",
      "Hate": """Content that is hateful toward people on the basis of their protected characteristics
          (race, color, ethnicity, national origin, disability, religious affiliation, caste, sexual orientation, sex, gender identity and serious disease),
          or content that perpetuates negative stereotypes.""",
      "Indiscriminate Weapons": "Content that enables the creation of weapons of mass destruction.",
      "Intellectual Property": "Content that violates the intellectual property rights of third parties.",
      "Non-Violent Crimes": "Content that enables, encourages, or excuses the commission of non-violent crimes.",
      "Privacy": "Content that contains sensitive, personal information about private individuals.",
      "Self-Harm": "Content that enables, encourages, or excuses acts of intentional self-harm.",
      "Sex Crimes": "Content that enables, encourages, or excuses the commission of sex crimes.",
      "Sexual Content": "Content that contains erotic descriptions or explicit depictions of sex acts.",
      "Specialized Advice": """Content that contains financial, medical, or legal advice.
          Financial advice includes guidance on investments, stocks, bonds, or any financial planning.""",
      "Violent Crimes": "Content that enables, encourages, or excuses the commission of violent crimes.",
  }


  def moderate_message_with_definitions(message, unsafe_category_definitions):
      # 安全でないカテゴリの文字列をフォーマット(各カテゴリとその定義を改行で区切る)
      unsafe_category_str = "\n".join(
          f"{category}: {definition}"
          for category, definition in unsafe_category_definitions.items()
      )

      # メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
      assessment_prompt = f"""Determine whether the following message warrants moderation, based on the unsafe categories outlined below.

  Message:
  <message>{message}</message>

  Unsafe Categories and Their Definitions:
  <categories>
  {unsafe_category_str}
  </categories>

  It's important that you remember all unsafe categories and their definitions.

  Respond with ONLY a JSON object, using the format below:
  {{
    "violation": <Boolean field denoting whether the message should be moderated>,
    "categories": [Comma-separated list of violated categories],
    "explanation": [Optional. Only include if there is a violation.]
  }}
  Do not include markdown formatting or code fences in your response."""

      # コンテンツモデレーションのためにClaudeにリクエストを送信
      response = client.messages.create(
          model="claude-haiku-4-5-20251001",  # Using the Haiku model for lower costs
          max_tokens=200,
          messages=[{"role": "user", "content": assessment_prompt}],
      )

      # ClaudeからのJSONレスポンスを解析
      text_block = next(block for block in response.content if block.type == "text")
      assessment = json.loads(text_block.text)

      # 評価結果から違反ステータスを抽出
      contains_violation = assessment["violation"]

      # 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用
      violated_categories = assessment.get("categories", []) if contains_violation else []
      explanation = assessment.get("explanation") if contains_violation else None

      return contains_violation, violated_categories, explanation


  # 各コメントを処理して結果を出力
  for comment in user_comments:
      print(f"\nComment: {comment}")
      violation, violated_categories, explanation = moderate_message_with_definitions(
          comment, unsafe_category_definitions
      )

      if violation:
          print(f"Violated Categories: {', '.join(violated_categories)}")
          print(f"Explanation: {explanation}")
      else:
          print("No issues detected.")
  ```

  ```typescript TypeScript
  // Claudeが返すJSON評価の形式
  interface DefinitionBasedAssessment {
    violation: boolean;
    categories?: string[];
    explanation?: string;
  }

  // コンテンツモデレーションで安全でないと見なされるカテゴリとその定義
  // （オブジェクトのキーは挿入順を保持するため、カテゴリはこの順序で表示されます）
  const unsafeCategoryDefinitions: Record<string, string> = {
    "Child Exploitation":
      "Content that depicts child nudity or that enables, encourages, excuses, or depicts the sexual abuse of children.",
    "Conspiracy Theories": `Content that promotes or endorses unfounded, false, or misleading theories about events, situations, or individuals,
          which can incite harm or undermine public trust in institutions or facts.`,
    Hate: `Content that is hateful toward people on the basis of their protected characteristics
          (race, color, ethnicity, national origin, disability, religious affiliation, caste, sexual orientation, sex, gender identity and serious disease),
          or content that perpetuates negative stereotypes.`,
    "Indiscriminate Weapons":
      "Content that enables the creation of weapons of mass destruction.",
    "Intellectual Property":
      "Content that violates the intellectual property rights of third parties.",
    "Non-Violent Crimes":
      "Content that enables, encourages, or excuses the commission of non-violent crimes.",
    Privacy: "Content that contains sensitive, personal information about private individuals.",
    "Self-Harm": "Content that enables, encourages, or excuses acts of intentional self-harm.",
    "Sex Crimes": "Content that enables, encourages, or excuses the commission of sex crimes.",
    "Sexual Content":
      "Content that contains erotic descriptions or explicit depictions of sex acts.",
    "Specialized Advice": `Content that contains financial, medical, or legal advice.
          Financial advice includes guidance on investments, stocks, bonds, or any financial planning.`,
    "Violent Crimes":
      "Content that enables, encourages, or excuses the commission of violent crimes."
  };

  async function moderateMessageWithDefinitions(
    message: string,
    unsafeCategoryDefinitions: Record<string, string>
  ): Promise<{ violation: boolean; violatedCategories: string[]; explanation?: string }> {
    // 安全でないカテゴリの文字列をフォーマットし、各カテゴリとその定義を改行で区切る
    const unsafeCategoryStr = Object.entries(unsafeCategoryDefinitions)
      .map(([category, definition]) => `${category}: ${definition}`)
      .join("\n");

    // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
    const assessmentPrompt = `Determine whether the following message warrants moderation, based on the unsafe categories outlined below.

  Message:
  <message>${message}</message>

  Unsafe Categories and Their Definitions:
  <categories>
  ${unsafeCategoryStr}
  </categories>

  It's important that you remember all unsafe categories and their definitions.

  Respond with ONLY a JSON object, using the format below:
  {
    "violation": <Boolean field denoting whether the message should be moderated>,
    "categories": [Comma-separated list of violated categories],
    "explanation": [Optional. Only include if there is a violation.]
  }
  Do not include markdown formatting or code fences in your response.`;

    // コンテンツモデレーションのためにClaudeへリクエストを送信
    const response = await client.messages.create({
      model: "claude-haiku-4-5-20251001", // Using the Haiku model for lower costs
      max_tokens: 200,
      messages: [{ role: "user", content: assessmentPrompt }]
    });

    // ClaudeからのJSONレスポンスを解析
    const textBlock = response.content.find((block) => block.type === "text");
    if (!textBlock) {
      throw new Error("Expected a text block in the response");
    }
    const assessment: DefinitionBasedAssessment = JSON.parse(textBlock.text);

    // 評価から違反ステータスを抽出
    const containsViolation = assessment.violation;

    // 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用
    const violatedCategories = containsViolation ? (assessment.categories ?? []) : [];
    const explanation = containsViolation ? assessment.explanation : undefined;

    return { violation: containsViolation, violatedCategories, explanation };
  }

  // 各コメントを処理して結果を出力
  for (const comment of userComments) {
    console.log(`\nComment: ${comment}`);
    const { violation, violatedCategories, explanation } = await moderateMessageWithDefinitions(
      comment,
      unsafeCategoryDefinitions
    );

    if (violation) {
      console.log(`Violated Categories: ${violatedCategories.join(", ")}`);
      console.log(`Explanation: ${explanation}`);
    } else {
      console.log("No issues detected.");
    }
  }
  ```

  ```csharp C#
  // コンテンツモデレーションで安全でないと見なされるカテゴリとその定義。
  // エントリは挿入順を保持するため、レンダリングされるプロンプトには
  // カテゴリがこの順序どおりに列挙されます。
  (string Category, string Definition)[] unsafeCategoryDefinitions =
  [
      (
          "Child Exploitation",
          "Content that depicts child nudity or that enables, encourages, excuses, or depicts the sexual abuse of children."
      ),
      (
          "Conspiracy Theories",
          """
          Content that promotes or endorses unfounded, false, or misleading theories about events, situations, or individuals,
                  which can incite harm or undermine public trust in institutions or facts.
          """
      ),
      (
          "Hate",
          """
          Content that is hateful toward people on the basis of their protected characteristics
                  (race, color, ethnicity, national origin, disability, religious affiliation, caste, sexual orientation, sex, gender identity and serious disease),
                  or content that perpetuates negative stereotypes.
          """
      ),
      ("Indiscriminate Weapons", "Content that enables the creation of weapons of mass destruction."),
      ("Intellectual Property", "Content that violates the intellectual property rights of third parties."),
      ("Non-Violent Crimes", "Content that enables, encourages, or excuses the commission of non-violent crimes."),
      ("Privacy", "Content that contains sensitive, personal information about private individuals."),
      ("Self-Harm", "Content that enables, encourages, or excuses acts of intentional self-harm."),
      ("Sex Crimes", "Content that enables, encourages, or excuses the commission of sex crimes."),
      ("Sexual Content", "Content that contains erotic descriptions or explicit depictions of sex acts."),
      (
          "Specialized Advice",
          """
          Content that contains financial, medical, or legal advice.
                  Financial advice includes guidance on investments, stocks, bonds, or any financial planning.
          """
      ),
      ("Violent Crimes", "Content that enables, encourages, or excuses the commission of violent crimes."),
  ];


  async Task<(bool ContainsViolation, List<string> ViolatedCategories, string? Explanation)> ModerateMessageWithDefinitions(
      string message,
      IReadOnlyList<(string Category, string Definition)> categoryDefinitions
  )
  {
      // 安全でないカテゴリの文字列をフォーマット（各カテゴリとその定義を改行で区切る）
      var unsafeCategoryText = string.Join(
          "\n",
          categoryDefinitions.Select(entry => $"{entry.Category}: {entry.Definition}")
      );

      // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
      var assessmentPrompt = $$"""
  Determine whether the following message warrants moderation, based on the unsafe categories outlined below.

  Message:
  <message>{{message}}</message>

  Unsafe Categories and Their Definitions:
  <categories>
  {{unsafeCategoryText}}
  </categories>

  It's important that you remember all unsafe categories and their definitions.

  Respond with ONLY a JSON object, using the format below:
  {
    "violation": <Boolean field denoting whether the message should be moderated>,
    "categories": [Comma-separated list of violated categories],
    "explanation": [Optional. Only include if there is a violation.]
  }
  Do not include markdown formatting or code fences in your response.
  """;

      // コンテンツモデレーションのためにClaudeへリクエストを送信
      var response = await client.Messages.Create(
          new()
          {
              Model = Model.ClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
              MaxTokens = 200,
              Messages = [new() { Role = Role.User, Content = assessmentPrompt }],
          }
      );

      // 最初のコンテンツブロックをテキストブロックに絞り込み、ClaudeのJSONレスポンスを解析
      if (!response.Content[0].TryPickText(out var textBlock))
      {
          throw new InvalidOperationException("Expected a text response from Claude.");
      }
      var assessment = JsonNode.Parse(textBlock.Text)!;

      // 評価結果から違反ステータスを抽出
      var containsViolation = assessment["violation"]!.GetValue<bool>();

      // 違反がある場合はカテゴリと説明を取得し、そうでなければ空のデフォルト値を使用
      List<string> violatedCategories = containsViolation
          ? assessment["categories"]?.AsArray().Select(category => category!.GetValue<string>()).ToList() ?? []
          : [];
      var explanation = containsViolation ? assessment["explanation"]?.GetValue<string>() : null;

      return (containsViolation, violatedCategories, explanation);
  }

  // 各コメントを処理して結果を出力
  foreach (var comment in userComments)
  {
      Console.WriteLine($"\nComment: {comment}");
      var (violation, violatedCategories, explanation) = await ModerateMessageWithDefinitions(
          comment,
          unsafeCategoryDefinitions
      );

      if (violation)
      {
          Console.WriteLine($"Violated Categories: {string.Join(", ", violatedCategories)}");
          Console.WriteLine($"Explanation: {explanation}");
      }
      else
      {
          Console.WriteLine("No issues detected.");
      }
  }
  ```

  ```go Go
  // コンテンツモデレーションで安全でないと見なされるカテゴリとその定義。
  // カテゴリ/定義のペアのスライス（マップではなく）を使うことで、レンダリング
  // 順序が安定します。Goのマップはランダムな順序で反復されます。
  type categoryDefinition struct {
  	category   string
  	definition string
  }

  var unsafeCategoryDefinitions = []categoryDefinition{
  	{"Child Exploitation", "Content that depicts child nudity or that enables, encourages, excuses, or depicts the sexual abuse of children."},
  	{"Conspiracy Theories", `Content that promotes or endorses unfounded, false, or misleading theories about events, situations, or individuals,
          which can incite harm or undermine public trust in institutions or facts.`},
  	{"Hate", `Content that is hateful toward people on the basis of their protected characteristics
          (race, color, ethnicity, national origin, disability, religious affiliation, caste, sexual orientation, sex, gender identity and serious disease),
          or content that perpetuates negative stereotypes.`},
  	{"Indiscriminate Weapons", "Content that enables the creation of weapons of mass destruction."},
  	{"Intellectual Property", "Content that violates the intellectual property rights of third parties."},
  	{"Non-Violent Crimes", "Content that enables, encourages, or excuses the commission of non-violent crimes."},
  	{"Privacy", "Content that contains sensitive, personal information about private individuals."},
  	{"Self-Harm", "Content that enables, encourages, or excuses acts of intentional self-harm."},
  	{"Sex Crimes", "Content that enables, encourages, or excuses the commission of sex crimes."},
  	{"Sexual Content", "Content that contains erotic descriptions or explicit depictions of sex acts."},
  	{"Specialized Advice", `Content that contains financial, medical, or legal advice.
          Financial advice includes guidance on investments, stocks, bonds, or any financial planning.`},
  	{"Violent Crimes", "Content that enables, encourages, or excuses the commission of violent crimes."},
  }

  func moderateMessageWithDefinitions(message string, unsafeCategoryDefinitions []categoryDefinition) (bool, []string, string) {
  	// 安全でないカテゴリの文字列をフォーマット（各カテゴリとその定義を改行で区切る）
  	categoryLines := make([]string, len(unsafeCategoryDefinitions))
  	for i, entry := range unsafeCategoryDefinitions {
  		categoryLines[i] = fmt.Sprintf("%s: %s", entry.category, entry.definition)
  	}
  	unsafeCategoryStr := strings.Join(categoryLines, "\n")

  	// メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
  	assessmentPrompt := fmt.Sprintf(`Determine whether the following message warrants moderation, based on the unsafe categories outlined below.

  Message:
  <message>%s</message>

  Unsafe Categories and Their Definitions:
  <categories>
  %s
  </categories>

  It's important that you remember all unsafe categories and their definitions.

  Respond with ONLY a JSON object, using the format below:
  {
    "violation": <Boolean field denoting whether the message should be moderated>,
    "categories": [Comma-separated list of violated categories],
    "explanation": [Optional. Only include if there is a violation.]
  }
  Do not include markdown formatting or code fences in your response.`, message, unsafeCategoryStr)

  	// コンテンツモデレーションのためにClaudeへリクエストを送信
  	response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
  		MaxTokens: 200,
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock(assessmentPrompt)),
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	// テキストを読み取る前に、最初のコンテンツブロックをテキストブロックに絞り込む
  	textBlock, ok := response.Content[0].AsAny().(anthropic.TextBlock)
  	if !ok {
  		log.Fatalf("expected a text block, got %q", response.Content[0].Type)
  	}

  	// ClaudeからのJSONレスポンスを解析
  	var assessment struct {
  		Violation   bool     `json:"violation"`
  		Categories  []string `json:"categories"`
  		Explanation string   `json:"explanation"`
  	}
  	if err := json.Unmarshal([]byte(textBlock.Text), &assessment); err != nil {
  		log.Fatal(err)
  	}

  	// 違反がある場合はカテゴリと説明を返し、そうでない場合は空のデフォルト値を使用
  	if !assessment.Violation {
  		return false, nil, ""
  	}
  	return true, assessment.Categories, assessment.Explanation
  }

  // moderateAllCommentsWithDefinitionsは各コメントを処理し、結果を出力します。
  func moderateAllCommentsWithDefinitions() {
  	for _, comment := range userComments {
  		fmt.Printf("\nComment: %s\n", comment)
  		violation, violatedCategories, explanation := moderateMessageWithDefinitions(comment, unsafeCategoryDefinitions)

  		if violation {
  			fmt.Printf("Violated Categories: %s\n", strings.Join(violatedCategories, ", "))
  			fmt.Printf("Explanation: %s\n", explanation)
  		} else {
  			fmt.Println("No issues detected.")
  		}
  	}
  }
  ```

  ```java Java
  // コンテンツモデレーションで安全でないと見なされるカテゴリとその定義
  record CategoryDefinition(String category, String definition) {}

  final List<CategoryDefinition> unsafeCategoryDefinitions = List.of(
          new CategoryDefinition(
                  "Child Exploitation",
                  "Content that depicts child nudity or that enables, encourages, excuses, or depicts the sexual abuse of children."),
          new CategoryDefinition(
                  "Conspiracy Theories",
                  """
                  Content that promotes or endorses unfounded, false, or misleading theories about events, situations, or individuals,
                          which can incite harm or undermine public trust in institutions or facts."""),
          new CategoryDefinition(
                  "Hate",
                  """
                  Content that is hateful toward people on the basis of their protected characteristics
                          (race, color, ethnicity, national origin, disability, religious affiliation, caste, sexual orientation, sex, gender identity and serious disease),
                          or content that perpetuates negative stereotypes."""),
          new CategoryDefinition(
                  "Indiscriminate Weapons",
                  "Content that enables the creation of weapons of mass destruction."),
          new CategoryDefinition(
                  "Intellectual Property",
                  "Content that violates the intellectual property rights of third parties."),
          new CategoryDefinition(
                  "Non-Violent Crimes",
                  "Content that enables, encourages, or excuses the commission of non-violent crimes."),
          new CategoryDefinition(
                  "Privacy",
                  "Content that contains sensitive, personal information about private individuals."),
          new CategoryDefinition(
                  "Self-Harm",
                  "Content that enables, encourages, or excuses acts of intentional self-harm."),
          new CategoryDefinition(
                  "Sex Crimes",
                  "Content that enables, encourages, or excuses the commission of sex crimes."),
          new CategoryDefinition(
                  "Sexual Content",
                  "Content that contains erotic descriptions or explicit depictions of sex acts."),
          new CategoryDefinition(
                  "Specialized Advice",
                  """
                  Content that contains financial, medical, or legal advice.
                          Financial advice includes guidance on investments, stocks, bonds, or any financial planning."""),
          new CategoryDefinition(
                  "Violent Crimes",
                  "Content that enables, encourages, or excuses the commission of violent crimes."));

  record ModerationDecision(boolean violation, List<String> violatedCategories, String explanation) {}

  ModerationDecision moderateMessageWithDefinitions(
          String message, List<CategoryDefinition> unsafeCategoryDefinitions)
          throws JsonProcessingException {
      // 安全でないカテゴリの文字列をフォーマット(各カテゴリとその定義を改行で区切る)
      String unsafeCategoryStr = unsafeCategoryDefinitions.stream()
              .map(categoryDefinition ->
                      categoryDefinition.category() + ": " + categoryDefinition.definition())
              .collect(Collectors.joining("\n"));

      // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
      String assessmentPrompt = """
          Determine whether the following message warrants moderation, based on the unsafe categories outlined below.

          Message:
          <message>%s</message>

          Unsafe Categories and Their Definitions:
          <categories>
          %s
          </categories>

          It's important that you remember all unsafe categories and their definitions.

          Respond with ONLY a JSON object, using the format below:
          {
            "violation": <Boolean field denoting whether the message should be moderated>,
            "categories": [Comma-separated list of violated categories],
            "explanation": [Optional. Only include if there is a violation.]
          }
          Do not include markdown formatting or code fences in your response."""
              .formatted(message, unsafeCategoryStr);

      // コンテンツモデレーションのためにClaudeにリクエストを送信
      Message response = client.messages().create(MessageCreateParams.builder()
              .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
              .maxTokens(200)
              .addUserMessage(assessmentPrompt)
              .build());

      // ClaudeからのJSONレスポンスを解析
      String assessmentJson = response.content().stream()
              .flatMap(contentBlock -> contentBlock.text().stream())
              .findFirst()
              .orElseThrow()
              .text();
      ObjectMapper mapper = new ObjectMapper();
      JsonNode assessment = mapper.readTree(assessmentJson);

      // 評価結果から違反ステータスを抽出
      boolean containsViolation = assessment.required("violation").asBoolean();

      // 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用
      List<String> violatedCategories = containsViolation && assessment.has("categories")
              ? mapper.convertValue(assessment.get("categories"), new TypeReference<List<String>>() {})
              : List.of();
      String explanation = containsViolation && assessment.hasNonNull("explanation")
              ? assessment.get("explanation").asText()
              : null;

      return new ModerationDecision(containsViolation, violatedCategories, explanation);
  }

  // 各コメントを処理して結果を出力
  void printModerationResultsWithDefinitions() throws JsonProcessingException {
      for (String comment : userComments) {
          IO.println("\nComment: " + comment);
          ModerationDecision result = moderateMessageWithDefinitions(comment, unsafeCategoryDefinitions);

          if (result.violation()) {
              IO.println("Violated Categories: " + String.join(", ", result.violatedCategories()));
              IO.println("Explanation: " + result.explanation());
          } else {
              IO.println("No issues detected.");
          }
      }
  }
  ```

  ```php PHP
  // コンテンツモデレーションで安全でないと見なされるカテゴリとその定義
  $unsafeCategoryDefinitions = [
      'Child Exploitation' => 'Content that depicts child nudity or that enables, encourages, excuses, or depicts the sexual abuse of children.',
      'Conspiracy Theories' => 'Content that promotes or endorses unfounded, false, or misleading theories about events, situations, or individuals,
          which can incite harm or undermine public trust in institutions or facts.',
      'Hate' => 'Content that is hateful toward people on the basis of their protected characteristics
          (race, color, ethnicity, national origin, disability, religious affiliation, caste, sexual orientation, sex, gender identity and serious disease),
          or content that perpetuates negative stereotypes.',
      'Indiscriminate Weapons' => 'Content that enables the creation of weapons of mass destruction.',
      'Intellectual Property' => 'Content that violates the intellectual property rights of third parties.',
      'Non-Violent Crimes' => 'Content that enables, encourages, or excuses the commission of non-violent crimes.',
      'Privacy' => 'Content that contains sensitive, personal information about private individuals.',
      'Self-Harm' => 'Content that enables, encourages, or excuses acts of intentional self-harm.',
      'Sex Crimes' => 'Content that enables, encourages, or excuses the commission of sex crimes.',
      'Sexual Content' => 'Content that contains erotic descriptions or explicit depictions of sex acts.',
      'Specialized Advice' => 'Content that contains financial, medical, or legal advice.
          Financial advice includes guidance on investments, stocks, bonds, or any financial planning.',
      'Violent Crimes' => 'Content that enables, encourages, or excuses the commission of violent crimes.',
  ];

  $moderateMessageWithDefinitions = function (string $message, array $unsafeCategoryDefinitions) use ($client): array {
      // 安全でないカテゴリの文字列を、各カテゴリとその定義を改行で区切ってフォーマットします
      $categoryLines = [];
      foreach ($unsafeCategoryDefinitions as $category => $definition) {
          $categoryLines[] = "{$category}: {$definition}";
      }
      $unsafeCategoryStr = implode("\n", $categoryLines);

      // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築します
      $assessmentPrompt = <<<PROMPT
      Determine whether the following message warrants moderation, based on the unsafe categories outlined below.

      Message:
      <message>{$message}</message>

      Unsafe Categories and Their Definitions:
      <categories>
      {$unsafeCategoryStr}
      </categories>

      It's important that you remember all unsafe categories and their definitions.

      Respond with ONLY a JSON object, using the format below:
      {
        "violation": <Boolean field denoting whether the message should be moderated>,
        "categories": [Comma-separated list of violated categories],
        "explanation": [Optional. Only include if there is a violation.]
      }
      Do not include markdown formatting or code fences in your response.
      PROMPT;

      // コンテンツモデレーションのためにClaudeへリクエストを送信します
      $response = $client->messages->create(
          model: 'claude-haiku-4-5-20251001', // Using the Haiku model for lower costs
          maxTokens: 200,
          messages: [['role' => 'user', 'content' => $assessmentPrompt]],
      );

      // ClaudeからのJSONレスポンスを解析します。SDKは各コンテンツブロックを
      // 具象クラスにデコードするため、テキストを読み取る前にTextBlockを見つけます。
      $textBlock = array_find($response->content, fn ($block) => $block instanceof \Anthropic\Messages\TextBlock)
          ?? throw new \RuntimeException('Expected a text block in the response.');
      $assessment = json_decode($textBlock->text, associative: true, flags: JSON_THROW_ON_ERROR);

      // 評価結果から違反ステータスを抽出します
      $containsViolation = $assessment['violation'];

      // 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用します
      $violatedCategories = $containsViolation ? ($assessment['categories'] ?? []) : [];
      $explanation = $containsViolation ? ($assessment['explanation'] ?? null) : null;

      return [$containsViolation, $violatedCategories, $explanation];
  };

  // 各コメントを処理して結果を出力します
  foreach ($userComments as $comment) {
      echo "\nComment: {$comment}\n";
      [$violation, $violatedCategories, $explanation] = $moderateMessageWithDefinitions($comment, $unsafeCategoryDefinitions);

      if ($violation) {
          echo 'Violated Categories: ' . implode(', ', $violatedCategories) . "\n";
          echo "Explanation: {$explanation}\n";
      } else {
          echo "No issues detected.\n";
      }
  }
  ```

  ```ruby Ruby
  # コンテンツモデレーションで安全でないと見なされるカテゴリとその定義
  UNSAFE_CATEGORY_DEFINITIONS = {
    "Child Exploitation" => "Content that depicts child nudity or that enables, encourages, excuses, or depicts the sexual abuse of children.",
    "Conspiracy Theories" => "Content that promotes or endorses unfounded, false, or misleading theories about events, situations, or individuals,
          which can incite harm or undermine public trust in institutions or facts.",
    "Hate" => "Content that is hateful toward people on the basis of their protected characteristics
          (race, color, ethnicity, national origin, disability, religious affiliation, caste, sexual orientation, sex, gender identity and serious disease),
          or content that perpetuates negative stereotypes.",
    "Indiscriminate Weapons" => "Content that enables the creation of weapons of mass destruction.",
    "Intellectual Property" => "Content that violates the intellectual property rights of third parties.",
    "Non-Violent Crimes" => "Content that enables, encourages, or excuses the commission of non-violent crimes.",
    "Privacy" => "Content that contains sensitive, personal information about private individuals.",
    "Self-Harm" => "Content that enables, encourages, or excuses acts of intentional self-harm.",
    "Sex Crimes" => "Content that enables, encourages, or excuses the commission of sex crimes.",
    "Sexual Content" => "Content that contains erotic descriptions or explicit depictions of sex acts.",
    "Specialized Advice" => "Content that contains financial, medical, or legal advice.
          Financial advice includes guidance on investments, stocks, bonds, or any financial planning.",
    "Violent Crimes" => "Content that enables, encourages, or excuses the commission of violent crimes."
  }


  def moderate_message_with_definitions(message, unsafe_category_definitions)
    # 安全でないカテゴリの文字列をフォーマット（各カテゴリとその定義を改行で区切る）
    unsafe_category_str = unsafe_category_definitions
      .map { |category, definition| "#{category}: #{definition}" }
      .join("\n")

    # メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
    assessment_prompt = <<~PROMPT.chomp
      Determine whether the following message warrants moderation, based on the unsafe categories outlined below.

      Message:
      <message>#{message}</message>

      Unsafe Categories and Their Definitions:
      <categories>
      #{unsafe_category_str}
      </categories>

      It's important that you remember all unsafe categories and their definitions.

      Respond with ONLY a JSON object, using the format below:
      {
        "violation": <Boolean field denoting whether the message should be moderated>,
        "categories": [Comma-separated list of violated categories],
        "explanation": [Optional. Only include if there is a violation.]
      }
      Do not include markdown formatting or code fences in your response.
    PROMPT

    # コンテンツモデレーションのためにClaudeにリクエストを送信
    response = CLIENT.messages.create(
      model: "claude-haiku-4-5-20251001", # Using the Haiku model for lower costs
      max_tokens: 200,
      messages: [{role: :user, content: assessment_prompt}]
    )

    # ClaudeからのJSONレスポンスを解析
    text_block = response.content.find { it.type == :text }
    assessment = JSON.parse(text_block.text)

    # 評価結果から違反ステータスを抽出
    contains_violation = assessment["violation"]

    # 違反がある場合はカテゴリと説明を取得し、そうでない場合は空のデフォルト値を使用
    violated_categories = contains_violation ? assessment.fetch("categories", []) : []
    explanation = contains_violation ? assessment["explanation"] : nil

    [contains_violation, violated_categories, explanation]
  end


  # 各コメントを処理して結果を出力
  USER_COMMENTS.each do |comment|
    puts "\nComment: #{comment}"
    violation, violated_categories, explanation = moderate_message_with_definitions(comment, UNSAFE_CATEGORY_DEFINITIONS)

    if violation
      puts "Violated Categories: #{violated_categories.join(", ")}"
      puts "Explanation: #{explanation}"
    else
      puts "No issues detected."
    end
  end
  ```
</CodeGroup>

`moderate_message_with_definitions`関数は、各安全でないカテゴリを詳細な定義と組み合わせられるようにすることで、先の`moderate_message`関数を拡張しています。これはコード内で、元の関数の`unsafe_categories`コレクションを`unsafe_category_definitions`マッピングに置き換えることで実現されています。このマッピングは、各安全でないカテゴリを対応する定義と組み合わせます。カテゴリ名とその定義の両方がプロンプトに含まれます。

特に、`Specialized Advice`カテゴリの定義では、禁止すべき金融アドバイスの種類が指定されるようになりました。その結果、以前は`moderate_message`の評価を通過していた`It is a great time to invest in gold!`というコメントが、違反をトリガーするようになります。

### バッチ処理を検討する

リアルタイムのモデレーションが必要ない状況でコストを削減するには、メッセージをバッチでモデレートすることを検討してください。プロンプトのコンテキスト内に複数のメッセージを含め、どのメッセージをモデレートすべきかをClaudeに評価させます。

<CodeGroup exclude="shell">
  ```python Python
  def batch_moderate_messages(messages, unsafe_categories):
      # 安全でないカテゴリを、各カテゴリが改行で区切られた文字列に変換
      unsafe_category_str = "\n".join(unsafe_categories)

      # メッセージの文字列をフォーマット(各メッセージをXML風のタグで囲み、IDを付与)
      messages_str = "\n".join(
          [f"<message id={idx}>{msg}</message>" for idx, msg in enumerate(messages)]
      )

      # メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
      assessment_prompt = f"""Determine the messages to moderate, based on the unsafe categories outlined below.

  Messages:
  <messages>
  {messages_str}
  </messages>

  Unsafe Categories:
  <categories>
  {unsafe_category_str}
  </categories>

  Respond with ONLY a JSON object, using the format below:
  {{
    "violations": [
      {{
        "id": <message id>,
        "categories": [list of violated categories],
        "explanation": <Explanation of why there's a violation>
      }}
    ]
  }}

  Important Notes:
  - Remember to analyze every message for a violation.
  - Select any number of violations that reasonably apply.
  - Do not include markdown formatting or code fences in your response."""

      # コンテンツモデレーションのためにClaudeにリクエストを送信
      response = client.messages.create(
          model="claude-haiku-4-5-20251001",  # Using the Haiku model for lower costs
          max_tokens=2048,  # Increased max token count to handle batches
          messages=[{"role": "user", "content": assessment_prompt}],
      )

      # ClaudeからのJSONレスポンスを解析
      text_block = next(block for block in response.content if block.type == "text")
      assessment = json.loads(text_block.text)
      return assessment


  # コメントのバッチを処理してレスポンスを取得
  response_obj = batch_moderate_messages(user_comments, unsafe_categories)

  # 検出された各違反の結果を出力
  for violation in response_obj["violations"]:
      print(f"""Comment: {user_comments[violation["id"]]}
  Violated Categories: {", ".join(violation["categories"])}
  Explanation: {violation["explanation"]}
  """)
  ```

  ```typescript TypeScript
  // Claudeが返すJSONバッチ評価の形式
  interface BatchAssessment {
    violations: {
      id: number;
      categories: string[];
      explanation: string;
    }[];
  }

  async function batchModerateMessages(
    messages: string[],
    unsafeCategories: string[]
  ): Promise<BatchAssessment> {
    // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
    const unsafeCategoryStr = unsafeCategories.join("\n");

    // メッセージ文字列をフォーマットし、各メッセージをXML風のタグで囲んでIDを付与
    const messagesStr = messages
      .map((msg, idx) => `<message id=${idx}>${msg}</message>`)
      .join("\n");

    // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
    const assessmentPrompt = `Determine the messages to moderate, based on the unsafe categories outlined below.

  Messages:
  <messages>
  ${messagesStr}
  </messages>

  Unsafe Categories:
  <categories>
  ${unsafeCategoryStr}
  </categories>

  Respond with ONLY a JSON object, using the format below:
  {
    "violations": [
      {
        "id": <message id>,
        "categories": [list of violated categories],
        "explanation": <Explanation of why there's a violation>
      }
    ]
  }

  Important Notes:
  - Remember to analyze every message for a violation.
  - Select any number of violations that reasonably apply.
  - Do not include markdown formatting or code fences in your response.`;

    // コンテンツモデレーションのためにClaudeへリクエストを送信
    const response = await client.messages.create({
      model: "claude-haiku-4-5-20251001", // Using the Haiku model for lower costs
      max_tokens: 2048, // Increased max token count to handle batches
      messages: [{ role: "user", content: assessmentPrompt }]
    });

    // ClaudeからのJSONレスポンスを解析
    const textBlock = response.content.find((block) => block.type === "text");
    if (!textBlock) {
      throw new Error("Expected a text block in the response");
    }
    const assessment: BatchAssessment = JSON.parse(textBlock.text);
    return assessment;
  }

  // コメントのバッチを処理してレスポンスを取得
  const batchAssessment = await batchModerateMessages(userComments, unsafeCategories);

  // 検出された各違反の結果を出力
  for (const violation of batchAssessment.violations) {
    console.log(`Comment: ${userComments[violation.id]}
  Violated Categories: ${violation.categories.join(", ")}
  Explanation: ${violation.explanation}
  `);
  }
  ```

  ```csharp C#
  async Task<JsonNode> BatchModerateMessages(IReadOnlyList<string> messages, IReadOnlyList<string> categories)
  {
      // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
      var unsafeCategoryText = string.Join("\n", categories);

      // メッセージ文字列をフォーマット（各メッセージをXML風タグで囲み、IDを付与）
      var messagesText = string.Join(
          "\n",
          messages.Select((message, index) => $"<message id={index}>{message}</message>")
      );

      // メッセージ群と安全でないカテゴリを含む、Claude用のプロンプトを構築
      var assessmentPrompt = $$"""
  Determine the messages to moderate, based on the unsafe categories outlined below.

  Messages:
  <messages>
  {{messagesText}}
  </messages>

  Unsafe Categories:
  <categories>
  {{unsafeCategoryText}}
  </categories>

  Respond with ONLY a JSON object, using the format below:
  {
    "violations": [
      {
        "id": <message id>,
        "categories": [list of violated categories],
        "explanation": <Explanation of why there's a violation>
      }
    ]
  }

  Important Notes:
  - Remember to analyze every message for a violation.
  - Select any number of violations that reasonably apply.
  - Do not include markdown formatting or code fences in your response.
  """;

      // コンテンツモデレーションのためにClaudeへリクエストを送信
      var response = await client.Messages.Create(
          new()
          {
              Model = Model.ClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
              MaxTokens = 2048, // Increased max token count to handle batches
              Messages = [new() { Role = Role.User, Content = assessmentPrompt }],
          }
      );

      // 最初のコンテンツブロックをテキストブロックに絞り込み、ClaudeのJSONレスポンスを解析
      if (!response.Content[0].TryPickText(out var textBlock))
      {
          throw new InvalidOperationException("Expected a text response from Claude.");
      }
      return JsonNode.Parse(textBlock.Text)!;
  }

  // コメントのバッチを処理してレスポンスを取得
  var moderationResults = await BatchModerateMessages(userComments, unsafeCategories);

  // 検出された各違反の結果を出力
  foreach (var violation in moderationResults["violations"]!.AsArray())
  {
      var flaggedComment = userComments[violation!["id"]!.GetValue<int>()];
      var violatedCategories = string.Join(
          ", ",
          violation["categories"]!.AsArray().Select(category => category!.GetValue<string>())
      );
      var explanation = violation["explanation"]!.GetValue<string>();

      Console.WriteLine($"""
          Comment: {flaggedComment}
          Violated Categories: {violatedCategories}
          Explanation: {explanation}

          """);
  }
  ```

  ```go Go
  // batchViolationはClaudeの"violations"配列の1エントリです：違反した
  // メッセージのインデックス、違反したカテゴリ、およびその理由。
  type batchViolation struct {
  	ID          int      `json:"id"`
  	Categories  []string `json:"categories"`
  	Explanation string   `json:"explanation"`
  }

  func batchModerateMessages(messages []string, unsafeCategories []string) []batchViolation {
  	// 安全でないカテゴリを、各カテゴリが改行で区切られた文字列に変換
  	unsafeCategoryStr := strings.Join(unsafeCategories, "\n")

  	// メッセージの文字列をフォーマット（各メッセージをXML風のタグで囲み、IDを付与）
  	messageLines := make([]string, len(messages))
  	for i, message := range messages {
  		messageLines[i] = fmt.Sprintf("<message id=%d>%s</message>", i, message)
  	}
  	messagesStr := strings.Join(messageLines, "\n")

  	// メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
  	assessmentPrompt := fmt.Sprintf(`Determine the messages to moderate, based on the unsafe categories outlined below.

  Messages:
  <messages>
  %s
  </messages>

  Unsafe Categories:
  <categories>
  %s
  </categories>

  Respond with ONLY a JSON object, using the format below:
  {
    "violations": [
      {
        "id": <message id>,
        "categories": [list of violated categories],
        "explanation": <Explanation of why there's a violation>
      }
    ]
  }

  Important Notes:
  - Remember to analyze every message for a violation.
  - Select any number of violations that reasonably apply.
  - Do not include markdown formatting or code fences in your response.`, messagesStr, unsafeCategoryStr)

  	// コンテンツモデレーションのためにClaudeへリクエストを送信
  	response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeHaiku4_5_20251001, // Using the Haiku model for lower costs
  		MaxTokens: 2048,                                   // Increased max token count to handle batches
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock(assessmentPrompt)),
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	// テキストを読み取る前に、最初のコンテンツブロックをテキストブロックに絞り込む
  	textBlock, ok := response.Content[0].AsAny().(anthropic.TextBlock)
  	if !ok {
  		log.Fatalf("expected a text block, got %q", response.Content[0].Type)
  	}

  	// ClaudeからのJSONレスポンスを解析
  	var assessment struct {
  		Violations []batchViolation `json:"violations"`
  	}
  	if err := json.Unmarshal([]byte(textBlock.Text), &assessment); err != nil {
  		log.Fatal(err)
  	}
  	return assessment.Violations
  }

  // moderateAllCommentsAsBatchはコメントのバッチ全体を単一のリクエストで
  // モデレートし、検出された各違反の結果を出力します。
  func moderateAllCommentsAsBatch() {
  	// コメントのバッチを処理してレスポンスを取得
  	violations := batchModerateMessages(userComments, unsafeCategories)

  	// 検出された各違反の結果を出力
  	for _, violation := range violations {
  		fmt.Printf(`Comment: %s
  Violated Categories: %s
  Explanation: %s

  `, userComments[violation.ID], strings.Join(violation.Categories, ", "), violation.Explanation)
  	}
  }
  ```

  ```java Java
  JsonNode batchModerateMessages(List<String> messages, List<String> unsafeCategories)
          throws JsonProcessingException {
      // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
      String unsafeCategoryStr = String.join("\n", unsafeCategories);

      // メッセージ文字列をフォーマット(各メッセージをXML風タグで囲み、IDを付与)
      String messagesStr = IntStream.range(0, messages.size())
              .mapToObj(idx -> "<message id=%d>%s</message>".formatted(idx, messages.get(idx)))
              .collect(Collectors.joining("\n"));

      // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
      String assessmentPrompt = """
          Determine the messages to moderate, based on the unsafe categories outlined below.

          Messages:
          <messages>
          %s
          </messages>

          Unsafe Categories:
          <categories>
          %s
          </categories>

          Respond with ONLY a JSON object, using the format below:
          {
            "violations": [
              {
                "id": <message id>,
                "categories": [list of violated categories],
                "explanation": <Explanation of why there's a violation>
              }
            ]
          }

          Important Notes:
          - Remember to analyze every message for a violation.
          - Select any number of violations that reasonably apply.
          - Do not include markdown formatting or code fences in your response."""
              .formatted(messagesStr, unsafeCategoryStr);

      // コンテンツモデレーションのためにClaudeにリクエストを送信
      Message response = client.messages().create(MessageCreateParams.builder()
              .model(Model.CLAUDE_HAIKU_4_5_20251001) // Using the Haiku model for lower costs
              .maxTokens(2048) // Increased max token count to handle batches
              .addUserMessage(assessmentPrompt)
              .build());

      // ClaudeからのJSONレスポンスを解析
      String assessmentJson = response.content().stream()
              .flatMap(contentBlock -> contentBlock.text().stream())
              .findFirst()
              .orElseThrow()
              .text();
      return new ObjectMapper().readTree(assessmentJson);
  }

  // コメントのバッチを処理し、検出された各違反の結果を出力
  void printBatchViolations() throws JsonProcessingException {
      JsonNode response = batchModerateMessages(userComments, unsafeCategories);

      ObjectMapper mapper = new ObjectMapper();
      for (JsonNode violation : response.required("violations")) {
          List<String> violatedCategories =
                  mapper.convertValue(violation.required("categories"), new TypeReference<List<String>>() {});
          IO.println("""
                  Comment: %s
                  Violated Categories: %s
                  Explanation: %s
                  """.formatted(
                          userComments.get(violation.required("id").asInt()),
                          String.join(", ", violatedCategories),
                          violation.required("explanation").asText()));
      }
  }
  ```

  ```php PHP
  $batchModerateMessages = function (array $messages, array $unsafeCategories) use ($client): array {
      // 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換します
      $unsafeCategoryStr = implode("\n", $unsafeCategories);

      // メッセージの文字列を、各メッセージをXML風のタグで囲みIDを付与してフォーマットします
      $messageLines = [];
      foreach ($messages as $idx => $msg) {
          $messageLines[] = "<message id={$idx}>{$msg}</message>";
      }
      $messagesStr = implode("\n", $messageLines);

      // メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築します
      $assessmentPrompt = <<<PROMPT
      Determine the messages to moderate, based on the unsafe categories outlined below.

      Messages:
      <messages>
      {$messagesStr}
      </messages>

      Unsafe Categories:
      <categories>
      {$unsafeCategoryStr}
      </categories>

      Respond with ONLY a JSON object, using the format below:
      {
        "violations": [
          {
            "id": <message id>,
            "categories": [list of violated categories],
            "explanation": <Explanation of why there's a violation>
          }
        ]
      }

      Important Notes:
      - Remember to analyze every message for a violation.
      - Select any number of violations that reasonably apply.
      - Do not include markdown formatting or code fences in your response.
      PROMPT;

      // コンテンツモデレーションのためにClaudeへリクエストを送信します
      $response = $client->messages->create(
          model: 'claude-haiku-4-5-20251001', // Using the Haiku model for lower costs
          maxTokens: 2048, // Increased max token count to handle batches
          messages: [['role' => 'user', 'content' => $assessmentPrompt]],
      );

      // ClaudeからのJSONレスポンスを解析します。SDKは各コンテンツブロックを
      // 具象クラスにデコードするため、テキストを読み取る前にTextBlockを見つけます。
      $textBlock = array_find($response->content, fn ($block) => $block instanceof \Anthropic\Messages\TextBlock)
          ?? throw new \RuntimeException('Expected a text block in the response.');

      return json_decode($textBlock->text, associative: true, flags: JSON_THROW_ON_ERROR);
  };

  // コメントのバッチを処理してレスポンスを取得します
  $responseObj = $batchModerateMessages($userComments, $unsafeCategories);

  // 検出された各違反の結果を出力します
  foreach ($responseObj['violations'] as $violation) {
      echo "Comment: {$userComments[$violation['id']]}\n";
      echo 'Violated Categories: ' . implode(', ', $violation['categories']) . "\n";
      echo "Explanation: {$violation['explanation']}\n\n";
  }
  ```

  ```ruby Ruby
  def batch_moderate_messages(messages, unsafe_categories)
    # 安全でないカテゴリを、各カテゴリを改行で区切った文字列に変換
    unsafe_category_str = unsafe_categories.join("\n")

    # メッセージの文字列をフォーマット（各メッセージをXML風のタグで囲み、IDを付与）
    messages_str = messages
      .map.with_index { |message, index| "<message id=#{index}>#{message}</message>" }
      .join("\n")

    # メッセージと安全でないカテゴリを含む、Claude用のプロンプトを構築
    assessment_prompt = <<~PROMPT.chomp
      Determine the messages to moderate, based on the unsafe categories outlined below.

      Messages:
      <messages>
      #{messages_str}
      </messages>

      Unsafe Categories:
      <categories>
      #{unsafe_category_str}
      </categories>

      Respond with ONLY a JSON object, using the format below:
      {
        "violations": [
          {
            "id": <message id>,
            "categories": [list of violated categories],
            "explanation": <Explanation of why there's a violation>
          }
        ]
      }

      Important Notes:
      - Remember to analyze every message for a violation.
      - Select any number of violations that reasonably apply.
      - Do not include markdown formatting or code fences in your response.
    PROMPT

    # コンテンツモデレーションのためにClaudeにリクエストを送信
    response = CLIENT.messages.create(
      model: "claude-haiku-4-5-20251001", # Using the Haiku model for lower costs
      max_tokens: 2048, # Increased max token count to handle batches
      messages: [{role: :user, content: assessment_prompt}]
    )

    # ClaudeからのJSONレスポンスを解析
    text_block = response.content.find { it.type == :text }
    JSON.parse(text_block.text)
  end


  # コメントのバッチを処理してレスポンスを取得
  response_obj = batch_moderate_messages(USER_COMMENTS, UNSAFE_CATEGORIES)

  # 検出された各違反の結果を出力
  response_obj["violations"].each do |violation|
    puts <<~RESULT
      Comment: #{USER_COMMENTS[violation["id"]]}
      Violated Categories: #{violation["categories"].join(", ")}
      Explanation: #{violation["explanation"]}

    RESULT
  end
  ```
</CodeGroup>

この例では、`batch_moderate_messages`関数が、1回のClaude API呼び出しでメッセージのバッチ全体のモデレーションを処理します。 関数内では、評価対象のメッセージのリストと安全でないコンテンツカテゴリを含むプロンプトが作成されます。このプロンプトは、違反を含むすべてのメッセージを列挙したJSONオブジェクトを返すようClaudeに指示します。レスポンス内の各メッセージは、バッチ内でのメッセージの位置に対応する`id`によって識別されます。 特定のニーズに最適なバッチサイズを見つけるには、ある程度の実験が必要になる場合があることに留意してください。バッチサイズを大きくするとコストを下げられますが、品質がわずかに低下する可能性もあります。さらに、より長いレスポンスに対応するために、Claude API呼び出しの`max_tokens`パラメータを増やす必要がある場合があります。選択したモデルが出力できる最大トークン数の詳細については、[モデル比較表](https://platform.claude.com/docs/ja/models/overview#latest-models-comparison)を参照してください。

<CardGroup cols={2}>
  <Card title="コンテンツモデレーションクックブック" icon="link" href="https://platform.claude.com/cookbook/misc-building-moderation-filter">
    Claudeをコンテンツモデレーションに使用する方法について、完全に実装されたコードベースの例をご覧ください。
  </Card>

  <Card title="ジェイルブレイクの軽減" icon="link" href="https://platform.claude.com/docs/ja/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks">
    Claudeとのやり取りをモデレートするためのガードレール手法を探索してください。
  </Card>
</CardGroup>
