← System Design
AI Systems

Design an AI Content Moderation System

Watch: "Harmful Content Detection / Content Moderation | ML System Design Problem Breakdown" — Hello Interview
In simple words

Airport security for posts: a quick scanner catches the obvious stuff instantly, a detailed scanner checks bags more carefully in the background, and anything uncertain goes to a human officer. The officers' decisions teach the scanners to be better next time.

Problem Statement

Design an AI-assisted content moderation system for a social platform: every post, comment, image and video is checked for harmful content (hate speech, harassment, spam, violence, nudity), clearly bad content is removed quickly, uncertain cases go to human reviewers, and users can appeal. It combines ML models, rules and people, at the scale of millions of items a day.

Functional Requirements

Non-Functional Requirements

Core Entities

API Design

Internal:
  POST /v1/moderate { item_id, type, text?, media_url? }  -> { decision, scores } (sync for text)
  events: ContentCreated -> async pipeline for media
Reviewer tool:
  GET  /v1/review/next?queue=hate_speech  -> task
  POST /v1/review/{task_id} { decision, policy, notes }
Users:
  POST /v1/reports { item_id, reason }    POST /v1/appeals { item_id, message }

High-Level Design (HLD)

Fast path, deep path and human reviewNew postFast pathhashes · rules ·small modelContent eventsDeep models(multimodal)Decision engine(thresholds)Human reviewqueuesLabels →retrainingbeforepublishuncertain
Cheap checks run instantly, heavier models follow asynchronously, and uncertain cases go to people whose decisions train the models.

Low-Level Design (LLD)

Data modelcontent_itemsitem_id PKtype, author_idtext / media refassessmentsitem_id, model_versioncategory scoresdecision, reasonreview_taskstask_id PKitem_id, queuepriority, leasedecisionscored(re-scored)uncertain →review
Every decision stores the model version and scores, so it can be explained, audited and used as training data.

Thresholds, not a single yes/no

for each category c:
  score >= remove_threshold[c]   -> remove automatically   (set for very high precision)
  score >= review_threshold[c]   -> hide or limit reach + send to human review
  else                           -> allow (may be re-scored as signals arrive)

Severity matters: for the gravest categories, review thresholds are low and
reviews are prioritised; for spam, auto-actions are more aggressive.

Signals improve over time

A post's harm often becomes clearer after exposure: user reports, blocks, and engagement patterns are extra features. Items are re-scored when such signals arrive, which is why the pipeline is event-driven rather than one-shot.

Review queue prioritisation

priority = severity(category) x model_score x expected_views(author reach)
queues partitioned by category and language; reviewers pull the next task
each item gets a lease while being reviewed (expired lease -> back to the queue)
a sample of auto-decisions also goes to review to measure precision

Feedback loop

Reviewer decisions and appeal outcomes are labels. Models are retrained regularly and evaluated on a held-out, recent set per category and language before a new version's thresholds are set and it's canaried.

Follow-up Questions

Block before posting, or post and then check?
Both: the cheap fast path runs before publishing (block obvious spam and known-bad media); heavier models run right after, with the item's reach limited until they finish. Blocking everything on deep models would make posting slow.
How do you handle a new type of abuse the models haven't seen?
Rules and hash lists can be updated in minutes while new labelled data is collected; user reports surface it; models are retrained with the new examples.
How do you measure whether moderation is working?
Precision of auto-removals (sampled review), prevalence of harmful content seen by users (sampled audits), time to action, appeal overturn rate, reviewer agreement.

Related