CoolFace
Apppublic

Hemil087/content_moderation_env

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
App README

Content Moderation Environment

An OpenEnv environment where an LLM agent reviews social media posts, retrieves precedent cases, and makes moderation decisions under platform policy constraints.

HF Space: https://hemil087-content-moderation-env.hf.space


Overview

Content moderation is one of the hardest real-world challenges facing social media platforms. Moderators must balance free expression against harmful content, handle ambiguous edge cases, and maintain consistency across millions of decisions. This environment simulates that task for LLM agents.

The agent receives a reported post with rich metadata (author history, engagement metrics, reporter credibility, automated classifier scores), the platform's community policy, and access to a database of 20 precedent cases. It must decide whether to remove, allow, label, escalate, or (for appeals) overturn/uphold prior decisions.


Tasks

TaskDifficultyEpisodesDescription
easyLow5Clear policy violations (hate speech, dehumanization) or clearly allowed content (news, political opinion). Minimal ambiguity.
mediumMedium5Ambiguous content requiring careful precedent analysis — coded language, edgy satire, graphic news with temporal context.
hardHigh7Appeal cases with conflicting precedents, coded dog-whistle language, compelling but wrong arguments, legal threats. Requires nuanced policy interpretation.

Episodes are selected via episode_index in reset options (default: 0). Each episode has deterministic grading — no randomness in scoring.


Action Space

ActionDescriptionWhen to Use
retrieve_precedentsSearch the precedent database for similar past casesBefore making any final decision
remove_contentRemove the post for policy violationDehumanizing content targeting protected groups
allow_contentApprove the post as policy-compliantPolitical criticism, news reporting, historical context
add_warning_labelKeep post but attach a warning labelGraphic content, ambiguous satire, potentially misleading
escalateSend to senior human moderatorUser threatens legal action or to leave platform
overturn_removalReverse a prior removal (appeals only)Original removal was unjustified (e.g., news/satire removed)
uphold_removalConfirm a prior removal (appeals only)Original removal was correct
python
from models import ContentModerationAction

# Retrieve precedents
action = ContentModerationAction(
    action_type="retrieve_precedents",
    query="hate speech dehumanizing ethnicity"
)

# Make a final decision with reason (citing policy clause improves score)
action = ContentModerationAction(
    action_type="remove_content",
    reason="Dehumanizing language targeting protected ethnic group"
)

Observation Space

After each step, the agent receives:

FieldTypeDescription
post_contentstrThe post or comment being reviewed
post_metadatadictRich metadata: report count, author history, engagement, auto-flag score, reporter credibility, content type, temporal context
policy_summarystrPlatform community policy (always visible)
precedentslistRetrieved precedent cases (populated after retrieve_precedents)
similar_post_countintNumber of similar posts on platform
confidence_guidancestrGuidance on certainty required
actions_takenlistHistory of actions taken this episode
step_countintCurrent step number
messagestrFeedback from environment (including precedent conflict warnings)
doneboolWhether the episode has ended
rewardfloatReward for the last action

Post Metadata Fields

Each post includes realistic Trust & Safety queue metadata:

FieldDescription
author_account_age_daysAccount age (15 = brand new, 3650 = 10yr veteran)
author_prior_violationsPrevious policy violations
author_verifiedVerified account status
author_follower_countAudience reach
content_typetextpost, comment, sharedpost, grouppost, appeal, directmessage_report
has_mediaWhether post includes image/video
engagement_countTotal likes + shares
reporter_accuracy_rateReporter's historical accuracy (trusted flagger vs mass-reporter)
report_categoryWhat the reporter flagged it as
auto_flag_scoreAutomated classifier confidence (0.0–1.0)
current_events_contextActive platform protocols (elections, disasters) — present on some episodes
original_moderator_reasoningHow original decision was made — present on appeal episodes

Reward Structure

EventReward
Retrieved relevant precedent+0.15
Retrieved irrelevant precedent-0.10
Correct final decision+0.50
Wrong final decision-0.30
Near-miss decision (adjacent action)-0.15
Cited correct policy clause in reason+0.10
No reason provided with final action-0.05
Relevant precedent bonus (grader)+0.15
Same action called twice (loop)-0.20
Hit step limit without deciding-0.30

Final episode score is clamped to 0.0–1.0.

Maximum achievable score: 0.90 (retrieve relevant + correct decision + relevant precedent bonus + policy clause citation)

Adjacent action pairs that receive reduced penalty (-0.15 instead of -0.30):

  • remove_contentadd_warning_label
  • allow_contentadd_warning_label
  • overturn_removalallow_content
  • uphold_removalremove_content

Quick Start

Install

bash
pip install openenv-core
pip install git+https://huggingface.co/spaces/Hemil087/content_moderation_env

Use (Sync)

python
from client import ContentModerationEnv
from models import ContentModerationAction

with ContentModerationEnv(base_url="https://hemil087-content-moderation-env.hf.space").sync() as env:
    result = env.reset(options={"task_id": "easy"})
    print(result.observation.post_content)

    # Retrieve precedents
    action = ContentModerationAction(
        action_type="retrieve_precedents",
        query="hate speech dehumanizing"
    )
    result = env.step(action)
    print(result.observation.precedents)

    # Make final decision
    action = ContentModerationAction(
        action_type="remove_content",
        reason="Dehumanizing language targeting protected group"
    )
    result = env.step(action)
    print(f"Score: {result.reward}, Done: {result.done}")

Use (Async)

python
import asyncio
from client import ContentModerationEnv
from models import ContentModerationAction

async def main():
    async with ContentModerationEnv(base_url="https://hemil087-content-moderation-env.hf.space") as env:
        result = await env.reset(options={"task_id": "medium"})
        print(result.observation.post_content)

asyncio.run(main())

Running Inference

The baseline agent uses an LLM via the OpenAI-compatible API:

bash
export HF_TOKEN=your_hf_token_here
export API_BASE_URL=https://router.huggingface.co/v1
export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
python inference.py

Baseline Scores

TaskScore
Easy0.90
Medium0.90
Hard0.90
Average0.90

Model: llama-3.3-70b-versatile via Groq


Local Development

bash
# Clone and install
git clone https://github.com/Hemil087/content-moderation-env.git
cd content-moderation-env
pip install -e .

# Start server
uvicorn server.app:app --reload

# Test endpoints
curl http://localhost:8000/health
curl -X POST http://localhost:8000/reset -H "Content-Type: application/json" \
  -d '{"options": {"task_id": "easy", "episode_index": 0}}'

Project Structure

content-moderation-env/          ← repo root = environment root
├── openenv.yaml                 # Environment manifest
├── pyproject.toml               # Package config
├── uv.lock
├── models.py                    # Action + Observation definitions
├── client.py                    # WebSocket client
├── __init__.py
├── README.md
├── inference.py                 # LLM agent script
└── server/
    ├── app.py                   # FastAPI application
    ├── content_moderation_env_environment.py  # Core environment logic
    ├── __init__.py
    ├── Dockerfile
    └── requirements.txt

Environment Design

Precedent Database

20 fixed precedent cases covering hate speech, political criticism, satire, news reporting, graphic content, and more. Precedents are retrieved via keyword search — deterministic, no external API calls.

Grading

All grading is deterministic (pure Python, no LLM calls). The grader checks:

  1. 1.Was the final action correct?
  2. 2.Did the agent retrieve relevant precedents?
  3. 3.Did the agent cite the correct policy clause in their reason?
  4. 4.Did the agent provide a reason at all?

Precedent Conflict Detection

When retrieved precedents have conflicting decisions (e.g., one says "allowed", another says "removed"), the environment flags this in the message, forcing the agent to reason about which precedent applies.


License

BSD 3-Clause License