Moderation Systems — OpenAI, Perspective, Llama Guard
Moderation is where the safety policies from Lessons 12-16 meet users. OpenAI's `omni-moderation-latest` (2024) is built on GPT-4o and classifies text and images in one call. It scores 42% better on a multilingual test set than the prior-generation moderation endpoint, and the response schema returns 13 category booleans: harassment, harassment/threatening, hate, hate/threatening, illicit, illicit/violent, self-harm, self-harm/intent, self-harm/instructions, sexual, sexual/minors, violence, violence/graphic. It is free for most developers. The usual deployment layers input moderation (before generation), output moderation (after generation), and custom domain rules. Parallel async calls hide latency, and a placeholder response can cover a flag. Llama Guard 3/4 (Lesson 16) covers 14 MLCommons hazards plus Code Interpreter Abuse, in 8 languages (v3) and multi-image (v4). Perspective API (Google Jigsaw) predates the LLM-as-moderator wave and scores mostly one dimension, toxicity, with severe-toxicity, insult, and profanity variants. Researchers still use it as a baseline. Azure Content Moderator was deprecated in February 2024 and retires in February 2027, replaced by Azure AI Content Safety.
Learning Objectives
- Describe the OpenAI Moderation API's category taxonomy and how it differs from Llama Guard 3's MLCommons set.
- Describe the three-layer moderation pattern (input, output, custom) and name one failure mode of each.
- Describe Perspective API's position as a pre-LLM-era baseline and why it remains used in research.
- State the Azure deprecation timeline.
The Problem
Lessons 12-16 cover attacks and the tooling that defends against them. This page covers the deployed moderation systems that turn those defenses into something running where users touch the product. The three-layer pattern is the default configuration in 2026.
The Concept
OpenAI Moderation API
omni-moderation-latest (2024). Built on GPT-4o. Classifies text + images in one call. Free for most developers.
Categories (13 booleans in the response schema):
- harassment, harassment/threatening
- hate, hate/threatening
- self-harm, self-harm/intent, self-harm/instructions
- sexual, sexual/minors
- violence, violence/graphic
- illicit, illicit/violent
Multimodal support applies to violence, self-harm, and sexual but not sexual/minors; the rest are text-only.
The harness in main.py collapses the /threatening, /intent, /instructions, and /graphic sub-categories into their top-level parents to stay small. Production code should use the full 13-category schema.
42% better results on a multilingual test set than the prior-generation moderation endpoint. The API returns per-category scores, and your application sets the thresholds.
Llama Guard 3/4
Covered in Lesson 16. 14 MLCommons hazard categories (organized differently from OpenAI's 13 response-schema booleans). Supports 8 languages (v3). Llama Guard 4 (April 2025) is natively multimodal, 12B.
The OpenAI and Llama Guard taxonomies overlap but diverge. OpenAI has "illicit" as one broad category, while Llama Guard splits it into "violent crimes" and "non-violent crimes". Pick the one whose taxonomy fits your policy.
Perspective API (Google Jigsaw)
Toxicity scoring system predating the LLM-as-moderator wave (pre-2020). Categories: TOXICITY, SEVERE_TOXICITY, INSULT, PROFANITY, THREAT, IDENTITY_ATTACK. Single-dimension primary score (TOXICITY) with sub-dimension variants.
Researchers keep using it as a content-moderation baseline because the API is stable, documented, and has years of calibration data. For LLM-adjacent work, Llama Guard or OpenAI Moderation is usually a better fit.
The three-layer pattern
- Input moderation. Classify the user's prompt before generation. Reject if flagged. Latency: one classifier call.
- Output moderation. Classify the model's output before delivery. Replace with a refusal if flagged. Latency: one classifier call after generation.
- Custom moderation. Domain-specific rules (regex, allowlists, business policy). Runs at either input or output.
The layers run in sequence by design. Input moderation has to finish before generation starts, and output moderation runs after generation ends. Parallelism lives inside a layer: run several classifiers (say OpenAI Moderation, Llama Guard, and Perspective) on the same text at once and you only pay for the slowest one. You can also show a placeholder ("one moment, checking...") while input moderation completes and hold back the first streamed token. What happens on a flag is configurable: refuse, sanitize, or escalate to human review.
Failure modes
- Input only. Misses bad content the model produces from a clean-looking prompt, such as the encoding attacks in Lessons 12-14 that slip past input classifiers.
- Output only. Allows any input to reach the model; increases cost; surfaces internal reasoning to attacker.
- Custom only. Not robust across categories; regexes are brittle.
Layering is the default for that reason: each layer covers a gap in the others.
Azure deprecation
Azure Content Moderator: deprecated February 2024, retired February 2027. Replaced by Azure AI Content Safety, which is LLM-based and integrates with Azure OpenAI. For Azure deployments the migration is a multi-year project running from 2024 to 2027.
Where this fits in Phase 18
Lesson 16 covers moderation tooling from the red-team side. Here it is covered as deployed. Lesson 30 closes with the current dual-use capability evidence.
Use It
main.py builds a three-layer moderation harness: an input moderator (keyword triggers mapped to category scores), a custom moderator (domain rules), and an output moderator (the same classifier run on the model's reply). Feed it prompts and watch which layer catches each one.
Ship It
The output is outputs/skill-moderation-stack.md. Given a deployment, it recommends a moderation stack: which classifier runs at input, which at output, which custom rules apply, and which judge handles edge cases.
I'd run main.py first and read the six prompts before reading the code. Each one gets a different outcome: the weather question passes, the memecoin and headache prompts hit custom rules, the bomb and "you idiot" prompts get flagged at input. Seeing that split makes the layer boundaries concrete.
Look at the order in run(). Custom rules fire after input moderation and before the model is called, so in this harness they act as a second input check, not an output one. The concept section says custom rules can run at either end, but this code only wires up the input side.
The output moderator never fires on any of the default prompts. Anything that would trip it already got refused at input, and the mock model's replies are harmless. In fact no prompt can trigger it as written: model_respond only echoes the first 40 characters of the prompt back, so any trigger word in the reply was already in the prompt and got caught at input. That is a limit of the toy, not evidence the output layer is redundant. To see it work, I'd edit model_respond to return a trigger word for some clean-looking prompt, which is the gap the Failure modes list says input-only setups leave open. Remember too that the classifier here is a keyword lookup returning 0.9 on a match. Real scores are graded, and the threshold you pick matters far more than it does in this demo.
Exercises
-
Run
main.py. Run a benign, borderline, and harmful input through all three layers. Report which layer fires for each. -
Extend the harness with Perspective-API-style toxicity scoring on a specific category. Compare its threshold behaviour to the category score.
-
Read the OpenAI Moderation API docs and the Llama Guard 3 category list. Map each OpenAI category to the closest Llama Guard categories. Identify three categories that do not cleanly map.
-
Design a moderation stack for a code-assistant deployment (e.g., GitHub Copilot). Identify the categories most and least relevant and propose custom rules.
-
Azure Content Moderator retires February 2027. Plan a migration to Azure AI Content Safety. Identify the highest-risk element of the migration.
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| OpenAI Moderation | "omni-moderation-latest" | GPT-4o-based 13-category (text) classifier with partial multimodal support |
| Perspective API | "Google Jigsaw toxicity" | Pre-LLM-era toxicity scoring baseline |
| Llama Guard | "MLCommons 14-category" | Meta's hazard classifier (v3: 8B text, 8 langs; v4: 12B multimodal) |
| Input moderation | "pre-generation filter" | Classifier on user prompt before model call |
| Output moderation | "post-generation filter" | Classifier on model output before delivery |
| Custom moderation | "domain rules" | Deployment-specific rules (regex, allowlist, policy) |
| Layered moderation | "all three layers" | Standard production deployment pattern |
Further Reading
- OpenAI Moderation API docs: omni-moderation endpoint
- Meta PurpleLlama + Llama Guard: Llama Guard repo
- Google Jigsaw Perspective API: toxicity scoring
- Azure AI Content Safety: Azure replacement
Code
- main.py (python, 127 lines)