InfoClassifier / README.md
UTKARSH MISHRA
Initial commit: InfoClassifier project
e5e36db
|
Raw History Blame Contribute Delete
11.3 kB

A newer version of the Gradio SDK is available: 6.31.0

Upgrade
metadata
title: InformationIntegrityEnv
emoji: πŸ”
colorFrom: blue
colorTo: green
sdk: gradio
sdk_version: 6.11.0
app_file: app.py
pinned: false
license: mit
tags:
  - openenv
  - content-moderation
  - rl-environment
  - agent-benchmark

InformationIntegrityEnv

A deterministic, benchmarkable OpenEnv environment for evaluating LLM agents on reasoning-based content moderation.

Agents must verify claims, weigh conflicting evidence, and make explainable moderation decisions (allow / flag / remove). Three difficulty tiers β€” easy, medium, hard β€” provide 15 fully static scenarios that run completely offline.


Architecture

tasks/
  easy/scenarios.json     ← 5 static scenarios (clear-cut misinformation)
  medium/scenarios.json   ← 5 static scenarios (misleading / mixed evidence)
  hard/scenarios.json     ← 5 static scenarios (conflicting evidence, nuanced)
  graders.py              ← EasyGrader / MediumGrader / HardGrader
  registry.py             ← TASKS["easy"] / TASKS["medium"] / TASKS["hard"]
        β”‚
        β–Ό
InformationIntegrityEnv         (environment.py)
  reset(difficulty, scenario_id) β†’ Observation
  step(action)                   β†’ (Observation, Reward, done, info)
  state()                        β†’ full debug state
        β”‚
  β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
  β”‚            β”‚
inference.py   server.py
(benchmark     (FastAPI
 agent loop)    REST API)

No external API calls occur during evaluation. DataCollector is only used to build/refresh datasets.


Quick Start

1. Install

pip install -r requirements.txt

2. Set API keys (for the LLM agent β€” not needed for just the server)

export HF_TOKEN=sk-...          # or OPENAI_API_KEY
export MODEL_NAME=gpt-4o
export API_BASE_URL=https://api.openai.com/v1

3. Run the full benchmark

python inference.py --benchmark --out results.json

This loops over all 15 scenarios (5 per difficulty) and prints:

[START] episode=E01 difficulty=easy claims=1 sources=5
[STEP]  step=1 action=check_source reward=+0.200 total=+0.200 done=False ...
[STEP]  step=2 action=verify_claim  reward=+0.200 total=+0.400 done=False ...
[STEP]  step=3 action=decide        reward=+1.000 total=+1.400 done=True  ...
[END]   episode=E01 steps=3 decision=remove total_reward=+1.4000 grader_score=0.8750

...

difficulty=easy   avg_score=0.7800  avg_reward=1.2000  n=5
difficulty=medium avg_score=0.6300  avg_reward=0.9000  n=5
difficulty=hard   avg_score=0.5200  avg_reward=0.6500  n=5
Difficulty tiers well separated: easy > medium > hard βœ“

4. Single episode (local, no server)

# Random scenario from medium dataset
python inference.py --local --difficulty medium

# Specific scenario by index
python inference.py --local --difficulty hard --scenario-id 2

# Specific scenario by id
python inference.py --local --difficulty easy --scenario-id easy_001

# From a custom JSON file (DataCollector output)
python inference.py --local --scenario my_scenario.json

5. Start the API server

python server.py
# or
uvicorn server:app --reload

Action Space

Action Required fields Description
verify_claim claim_id Investigate a claim; returns true / false / partially_true / unknown
check_source source_id Read a source; returns title, text, stance, reliability
decide decision Final call: allow / flag / remove β€” ends episode

All actions accept an optional reasoning string (logged, not graded).

Minimum evidence required before deciding (or premature penalty applies):

  • β‰₯ 1 claim verified
  • β‰₯ 2 sources checked

Observation Space

{
  "content": "The post text under review",
  "claims": [
    {"id": "claim_001", "text": "...", "verification_status": "unverified", "confidence": 0.0}
  ],
  "available_sources": [
    {"id": "src_001", "title": "...", "text": "...", "url": "...",
     "stance": "contradicting", "reliability": 0.97, "source_type": "official"}
  ],
  "interaction_history": [],
  "status_flags": {
    "decision_made": false,
    "sufficient_evidence": false,
    "premature_decision": false,
    "redundant_actions": 0,
    "max_steps_reached": false
  },
  "step_count": 0,
  "task_difficulty": "medium",
  "episode_id": "uuid"
}

Reward Function

Component Value Trigger
Verify claim +0.20 Per unique claim verified
Check source +0.20 Per unique source checked
Multi-source bonus +0.20 When β‰₯ 3 unique sources checked
Correct decision +1.00 Exact ground truth match
Partial decision +0.50 One tier off (flag↔remove, flag↔allow)
Premature decision βˆ’0.50 Deciding before sufficient evidence
Redundant action βˆ’0.10 Re-checking same claim/source
Inefficiency βˆ’0.05 Per step beyond efficient threshold
Over-aggression βˆ’0.30 remove when ground truth is allow/flag

Total clipped to [-2.0, +2.0]. All values configurable in reward_config.yaml.


Grader Scoring

Each episode is scored [0.0, 1.0] across four dimensions:

Dimension Easy Medium Hard
Decision correctness 50% 35% 30%
Evidence breadth 25% 35% 30%
Reasoning depth 15% 20% 30%
Efficiency 10% 10% 10%

Task Datasets

Easy (5 scenarios)

Clear-cut false claims contradicted by multiple authoritative sources (WHO, CDC, NASA, FDA). Ground truth: 3Γ— remove, 1Γ— flag, 1Γ— allow.

ID Topic Ground Truth
easy_001 Bleach cures COVID remove
easy_002 NASA fabricated climate data remove
easy_003 mRNA vaccines alter DNA remove
easy_004 Sydney is Australia's capital flag
easy_005 Webb telescope early universe allow

Medium (5 scenarios)

Partially true or misleadingly framed content. Mixed evidence requires weighing. Ground truth: 4Γ— flag, 1Γ— allow.

ID Topic Ground Truth
medium_001 Sugar more addictive than cocaine flag
medium_002 Real unemployment is 20%+ flag
medium_003 AI will eliminate 85M jobs flag
medium_004 Pharma knew OxyContin was addictive allow
medium_005 Facebook hid teen mental health data flag

Hard (5 scenarios)

Conflicting credible evidence, genuinely contested claims, contested policy debates. Ground truth: 5Γ— flag.

ID Topic Ground Truth
hard_001 NATO provoked Ukraine invasion flag
hard_002 Lockdowns killed more than COVID flag
hard_003 SEC crackdown is banking conspiracy flag
hard_004 AI poses extinction-level risk flag
hard_005 British colonialism net beneficial flag

API Reference

Method Endpoint Description
GET /health Liveness probe
GET /tasks List all available scenario datasets
POST /reset Start a new episode
POST /step Submit one action
GET /state/{session_id} Full internal state (debug/audit)
GET /scenario/{session_id} Scenario metadata (no ground truth)
DELETE /session/{session_id} End session and free memory
POST /collect Build a live scenario via DataCollector (dataset creation only)

Reset β€” evaluation path (no DataCollector needed)

# Random scenario from easy dataset
curl -X POST http://localhost:8000/reset \
  -H "Content-Type: application/json" \
  -d '{"difficulty": "easy"}'

# Specific scenario by index
curl -X POST http://localhost:8000/reset \
  -d '{"difficulty": "medium", "scenario_id": 2}'

# Specific scenario by id
curl -X POST http://localhost:8000/reset \
  -d '{"difficulty": "hard", "scenario_id": "hard_001"}'

Full episode example

SESSION=$(curl -s -X POST http://localhost:8000/reset \
  -H "Content-Type: application/json" \
  -d '{"difficulty": "easy"}' | jq -r .session_id)

# Check a source
curl -X POST http://localhost:8000/step \
  -d "{\"session_id\": \"$SESSION\", \"action\": {\"type\": \"check_source\", \"source_id\": \"src_001\"}}"

# Verify a claim
curl -X POST http://localhost:8000/step \
  -d "{\"session_id\": \"$SESSION\", \"action\": {\"type\": \"verify_claim\", \"claim_id\": \"claim_001\"}}"

# Decide
curl -X POST http://localhost:8000/step \
  -d "{\"session_id\": \"$SESSION\", \"action\": {\"type\": \"decide\", \"decision\": \"remove\"}}"

Docker

Server (default)

docker build -t info-integrity-env .
docker run --env-file .env -p 8000:8000 info-integrity-env

Benchmark (offline β€” no external services needed)

docker run --env-file .env info-integrity-env \
  python inference.py --benchmark --out /tmp/results.json

Full stack with Redis (for live data collection)

docker compose up --build

Baseline Results

Difficulty Avg Grader Score Avg Reward Decision Accuracy
Easy ~0.78 ~1.20 ~85%
Medium ~0.63 ~0.90 ~70%
Hard ~0.52 ~0.65 ~55%

Results vary by LLM. Scores above from gpt-4o with temperature=0.


Extending the Environment

File What to change
tasks/{difficulty}/scenarios.json Add or edit scenarios (run openenv validate after)
reward_config.yaml Tune reward/penalty magnitudes
tasks/graders.py Register custom graders: GraderFactory.register("hard_v2", MyGrader)
data_collector.py Add new search backends via SearchBackend ABC
domain_reliability.yaml Add trusted domains with custom reliability scores

Project Structure

β”œβ”€β”€ models.py                    Pydantic models (Observation, Action, Reward, Scenario, ...)
β”œβ”€β”€ environment.py               InformationIntegrityEnv (OpenEnv core)
β”œβ”€β”€ reward.py                    Configurable dense reward function
β”œβ”€β”€ explainability.py            Structured reasoning + template-based explanation
β”œβ”€β”€ data_collector.py            Multi-backend data collection (dataset creation only)
β”œβ”€β”€ inference.py                 Benchmark agent + single-episode runner
β”œβ”€β”€ server.py                    FastAPI server (HF Space / Docker)
β”œβ”€β”€ tasks/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ graders.py               Easy / Medium / Hard deterministic graders
β”‚   β”œβ”€β”€ registry.py              TASKS registry β€” lazy-loads scenario datasets
β”‚   β”œβ”€β”€ easy/
β”‚   β”‚   └── scenarios.json       5 easy scenarios
β”‚   β”œβ”€β”€ medium/
β”‚   β”‚   └── scenarios.json       5 medium scenarios
β”‚   └── hard/
β”‚       └── scenarios.json       5 hard scenarios
β”œβ”€β”€ openenv.yaml                 OpenEnv v1.1.0 specification
β”œβ”€β”€ reward_config.yaml           Reward magnitudes (runtime-configurable)
β”œβ”€β”€ domain_reliability.yaml      Domain trust scores for DataCollector
β”œβ”€β”€ Dockerfile                   Production container
β”œβ”€β”€ docker-compose.yml           Local dev stack (server + Redis)
└── requirements.txt