title: Voice Authenticity OpenEnv
emoji: ποΈ
colorFrom: blue
colorTo: red
sdk: docker
pinned: false
app_port: 7860
tags:
- openenv
- speech
- fraud-detection
- audio
ποΈ Voice Authenticity Detection β OpenEnv Environment
Voice fraud cost the global economy $25B+ in 2024. Tools like ElevenLabs can clone any voice in 60 seconds. Banks, insurers, and telecom providers face real-time phone scams, identity spoofing, and deepfake audio at unprecedented scale β and existing benchmarks can't keep up.
This environment trains agents to actively investigate, gather evidence, and reason about acoustic features under realistic degradation β codec compression, adversarial perturbation, streaming noise, and phone call simulation β through a genuine multi-step decision process with calibrated, risk-aware grading.
The 5-Action Agent Protocol
| Step | Action | What the Agent Gets | Purpose |
|---|---|---|---|
| 1 | request_temporal_features |
Jitter, shimmer, HNR (raw + normalized) | Vocal cord irregularity markers |
| 2 | request_spectral_features |
20 MFCC means, 20 MFCC stds, ZCR, spectral centroid | Timbre and spectral shape |
| 3 | request_comparison |
Cosine similarity + euclidean distance to real/fake centroids | Statistical comparison to known references |
| 4 | analyze_evidence |
Structured synthesis of all gathered evidence with signal tally | Evidence integration and confidence calibration |
| 5 | final_classify |
Submits label (0=real, 1=synthetic) + confidence + reasoning | Terminal action β triggers 6-component grading |
The agent starts with zero features visible and must earn its information before classifying. This is sequential decision-making under partial observability β not a single-shot classifier.
π« Why Existing Benchmarks Fail Here
ASVspoof (Automatic Speaker Verification Spoofing) evaluates countermeasure systems using static datasets with fixed train/test splits. Agents see the full feature set at once, make a single prediction, and receive binary pass/fail scoring. There is no partial observability, no multi-step interaction, no confidence calibration, and no reward shaping. ASVspoof cannot evaluate whether an agent knows how to investigate β only whether it gets the right answer.
ADD (Audio Deepfake Detection) benchmarks follow the same static paradigm: models are trained on one distribution and tested on another, with no mechanism for the agent to actively gather information or express calibrated uncertainty. ADD evaluates classifiers, not agents.
This environment is different. It requires agents to:
- Choose which features to request and in what order (partial observability)
- Synthesize heterogeneous evidence sources before committing to a classification
- Express calibrated confidence β overconfident wrong answers are penalized more harshly than uncertain wrong answers
- Operate under real-world degradation β codec compression, adversarial perturbation, streaming noise, and phone-call simulation
- Follow logical investigation trajectories β gather β analyze β classify, scored by a 6-component grader
No existing benchmark evaluates these capabilities.
π Real-World Motivation
AI-generated voices are increasingly weaponized for:
- Phone fraud & social engineering β real-time voice cloning during live calls
- Deepfake audio in misinformation β fabricated audio of public figures
- Identity spoofing β bypassing voice biometric authentication systems
- Financial fraud β CEO voice cloning for unauthorized wire transfers
- Insurance scams β fabricated recorded statements
This environment provides a structured benchmark for training agents to detect synthetic speech under conditions that static classifiers and existing benchmarks cannot handle.
ποΈ Environment Overview
The environment serves 48-dimensional feature vectors extracted from audio samples. Unlike standard classification benchmarks, agents start with NO features visible and must actively query the environment through the 5-action protocol to gather evidence before making a final classification.
This creates genuine sequential decision-making under partial observability, requiring agents to:
- Choose which information to request and in what order
- Synthesize heterogeneous evidence sources
- Express calibrated confidence reflecting genuine uncertainty
- Follow logical investigation trajectories
π Tasks (5 Total) β Monotonic Difficulty Progression
| Task | Difficulty | Expected Score | Description |
|---|---|---|---|
clean_detection |
Easy | 0.65β0.78 | Clean, unmodified audio features β clear signal separation |
compressed_detection |
Medium | 0.50β0.65 | Codec compression flattens MFCC stds, suppresses jitter/shimmer |
adversarial_detection |
Hard | 0.40β0.58 | Feature distributions overlap β no clean threshold separates classes |
streaming_detection |
Medium-Hard | 0.38β0.55 | Step-dependent noise soft-gating β earlier steps noisier, later cleaner |
phonecall_detection |
Extreme | 0.25β0.42 | Heavy narrowband codec + background noise β near detection limit |
Difficulty Progression Design
Harder tasks apply difficulty-aware score scaling in the grader. This models genuine signal degradation: adversarial samples have overlapping feature distributions, phone call codec compression destroys discriminative features, and streaming noise makes early observations unreliable. Even a perfect agent achieves lower scores on harder tasks because the underlying signal quality is genuinely worse.
π Grading System (6 Components)
Each episode is scored across 6 components with difficulty-weighted contributions:
| Component | What It Measures | Easy | Medium | Hard | Extreme |
|---|---|---|---|---|---|
| Correctness | Label matches ground truth | 0.40 | 0.30 | 0.25 | 0.20 |
| Confidence Calibration | Penalizes overconfidence, rewards calibrated uncertainty | 0.15 | 0.20 | 0.25 | 0.25 |
| Trajectory Quality | Did agent gather β analyze β classify? | 0.10 | 0.15 | 0.18 | 0.20 |
| Feature Utilization | Did agent request temporal AND spectral features? | 0.15 | 0.15 | 0.12 | 0.15 |
| Reasoning Consistency | Does reasoning text match chosen label? | 0.10 | 0.10 | 0.10 | 0.10 |
| Action Ordering | Logical sequence: gather β analyze β classify | 0.10 | 0.10 | 0.10 | 0.10 |
After component scoring, a difficulty scaling factor is applied:
| Difficulty | Scaling Factor | Max Achievable Score |
|---|---|---|
| Easy | 0.78 | ~0.73 |
| Medium | 0.66 | ~0.61 |
| Hard | 0.59 | ~0.55 |
| Medium-Hard | 0.55 | ~0.51 |
| Extreme | 0.41 | ~0.38 |
Why This Matters
On easy tasks, correctness dominates. On hard/extreme tasks, confidence calibration and trajectory quality become critical β mirroring real-world fraud detection where a confident wrong answer is more dangerous than an uncertain one, and where systematic investigation outperforms snap judgments.
π Step-Level Rewards
The environment provides shaping signals at every step, not just on final classification:
| Condition | Reward |
|---|---|
| First action is a feature request | +0.05 |
| Requested both temporal AND spectral features | +0.05 |
Used analyze_evidence before final_classify |
+0.05 |
Jumped straight to final_classify without gathering |
-0.10 |
| Repeated the same action consecutively | -0.05 |
| Reasoning contradicts chosen label | -0.10 |
Step-level rewards are clamped to [0.02, 0.18] and never produce exactly 0.0 or 1.0. The terminal final_classify step returns the pure grader score.
These intermediate rewards teach agents investigation behavior rather than pure classification.
βοΈ Why Feature Vectors Instead of Raw Audio?
- Fits within 2 vCPU / 8GB RAM constraints
- Feature extraction is performed offline for fast inference
- Enables LLM-native reasoning over interpretable acoustic characteristics β not possible with raw waveforms under current infrastructure constraints
- Avoids heavy signal processing during evaluation
π Dataset
- Real speech: 250 samples from
garystafford/deepfake-audio-detection(authentic human recordings) - Synthetic speech: 250 samples (ElevenLabs, Hume AI, and other TTS platforms)
- Total: 500 labeled samples across 5 task variants
The dataset is designed for evaluation structure and reward learning, not scale. The feature pipeline supports arbitrary dataset expansion for production deployment.
π Observation Space
Each observation contains:
class VoiceObservation(BaseModel):
features: List[float] # 48-dim (zeroed until revealed)
task_name: str # current task
step_number: int # current step in episode
difficulty: str # easy|medium|medium_hard|hard|extreme
sample_id: int # index into dataset
hint: Optional[str] # context and guidance
visible_features: Dict[str, Any] # features revealed so far
evidence_summary: Optional[str] # from analyze_evidence
comparison_result: Optional[Dict[str, float]] # from request_comparison
available_actions: List[str] # valid actions this step
actions_taken: List[str] # action history
48-Dimensional Feature Vector
| Index | Feature | Description |
|---|---|---|
| 0β19 | MFCC means | Timbre and spectral shape of the voice |
| 20β39 | MFCC std devs | Temporal variation in spectral characteristics |
| 40 | Zero crossing rate | Signal sign changes per frame |
| 41 | Spectral centroid | Brightness of the sound |
| 42 | Jitter | Cycle-to-cycle frequency instability |
| 43 | Shimmer | Amplitude variation between glottal pulses |
| 44 | HNR | Ratio of harmonic energy to background noise |
| 45β47 | Compression artifacts | Spectral bandwidth, rolloff, RMS energy |
Key Discriminating Features
- Jitter: measures cycle-to-cycle frequency instability β real voices show natural irregularity, synthetic voices are too stable
- Shimmer: tracks amplitude variation between consecutive glottal pulses β real speech has organic variation
- HNR: quantifies harmonic-to-noise ratio β synthetic voices are typically "too clean"
π― Action Space
class VoiceAction(BaseModel):
action_type: str # one of the 5 actions
label: int # 0=real, 1=synthetic (for final_classify)
confidence: float # [0.05, 0.95] (for final_classify)
reasoning: str # explanation (for final_classify)
π Baseline Scores
Agent: Qwen/Qwen2.5-72B-Instruct via HuggingFace router
Protocol: 5-action (temporal β spectral β comparison β analyze β classify)
Runs: 1 episode per task, seed=7
| Task | Difficulty | Score | Success | Notes |
|---|---|---|---|---|
| clean_detection | Easy | 0.74 | Yes | Clean features β strong baseline |
| compressed_detection | Medium | 0.62 | Yes | Codec compression degrades acoustic signal |
| adversarial_detection | Hard | 0.55 | No | Overlapping distributions challenge classification |
| streaming_detection | Medium-Hard | 0.30 | No | Streaming noise fooled the LLM at step 1 |
| phonecall_detection | Extreme | 0.22 | No | Phone-call degradation pushed detection below chance |
Scores decrease monotonically with difficulty β harder tasks have genuinely noisier signals and overlapping feature distributions. The difficulty scaling is applied in the grader, meaning even a perfect agent scores lower on harder tasks. On streaming and phone-call tasks, the LLM was additionally fooled by degraded features, creating sharper score drops.
π OpenEnv API
from environment.env import VoiceAuthenticityEnv
env = VoiceAuthenticityEnv(task_name="clean_detection")
# Reset β no features visible yet
obs = env.reset(seed=42)
# obs.features β [0.05, 0.05, ..., 0.05] (zeroed)
# obs.available_actions β ["request_temporal_features", ...]
# Step 1 β request temporal features
action = {"action_type": "request_temporal_features"}
obs, reward, done, info = env.step(action)
# obs.visible_features["temporal"]["jitter"] β 0.032451
# reward β 0.10 (shaping: first action is gathering)
# Step 2 β request spectral features
action = {"action_type": "request_spectral_features"}
obs, reward, done, info = env.step(action)
# obs.visible_features["spectral"]["mfcc_means"] β [20 values]
# reward β 0.10 (shaping: multi-feature-type bonus)
# Step 3 β compare to reference centroids
action = {"action_type": "request_comparison"}
obs, reward, done, info = env.step(action)
# obs.comparison_result["cosine_similarity_to_real"] β 0.8742
# obs.comparison_result["closer_to"] β "real"
# Step 4 β analyze all evidence
action = {"action_type": "analyze_evidence"}
obs, reward, done, info = env.step(action)
# obs.evidence_summary β "Evidence analysis (3 sources): ..."
# Step 5 β final classification
action = {
"action_type": "final_classify",
"label": 0,
"confidence": 0.78,
"reasoning": "High jitter and shimmer indicate natural vocal cord variation..."
}
obs, reward, done, info = env.step(action)
# reward β 0.73 (6-component graded score with difficulty scaling)
# done β True
# info["grader_breakdown"] β {correctness: 0.95, calibration: 0.84, ...}
state = env.state()
π Expected stdout Format
[START] task=clean_detection env=voice-authenticity model=Qwen/Qwen2.5-72B-Instruct
[STEP] step=1 action=request_temporal_features reward=0.10 done=false error=null
[STEP] step=2 action=request_spectral_features reward=0.10 done=false error=null
[STEP] step=3 action=request_comparison reward=0.05 done=false error=null
[STEP] step=4 action=analyze_evidence reward=0.05 done=false error=null
[STEP] step=5 action=final_classify label=0 confidence=0.75 reward=0.74 done=true error=null
[END] success=true steps=5 score=0.74 rewards=0.10,0.10,0.05,0.05,0.74 grader_breakdown={"correctness":0.95,"calibration":0.90,"trajectory":0.95,"utilization":0.95,"reasoning":0.95,"ordering":0.95}
β οΈ Known Limitations and Failure Cases
- Synthetic voices with injected background noise may evade temporal feature detection
- Real voices under heavy studio compression can mimic synthetic spectral profiles
- Borderline acoustic feature overlap exists between real and adversarially crafted samples β no clean threshold separates them
- Phone call simulation pushes detection to near-chance performance, reflecting genuine real-world difficulty
- Streaming task noise is step-dependent β agents that don't re-request features may work from degraded data
- Dataset of 500 samples is designed for evaluation structure and reward design, not production scale
- Results may vary across accents, languages, and recording conditions not represented in the data
This environment is designed to be extended with real enterprise datasets. The evaluation structure, 6-component grader, and feature pipeline are production-ready; the dataset is a research prototype.
π Setup and Usage
Requirements
Python 3.10+
Docker
HuggingFace account
Local Setup
git clone https://huggingface.co/spaces/AksharaSharma/voice-authenticity-openenv
cd voice-authenticity-openenv
pip install -r requirements.txt
python scripts/download_data.py
python scripts/extract_features.py
cp .env.example .env
# Edit .env with your HF_TOKEN
# Terminal 1 β start the environment server
python app.py
# Terminal 2 β run baseline inference (5-action protocol, all 5 tasks)
python inference.py
Validation Sequence
docker build -t voice-authenticity .
docker run --env-file .env voice-authenticity &
sleep 10
curl http://localhost:7860/health
curl -X POST http://localhost:7860/reset
python inference.py
Running Tests
# Run all tests
pytest test_env.py -v
# Run individual tests
pytest test_env.py::test_reset_returns_observation -v
pytest test_env.py::test_five_actions_complete_episode -v
Environment Variables
| Variable | Description | Default |
|---|---|---|
API_BASE_URL |
LLM API endpoint | https://router.huggingface.co/v1 |
MODEL_NAME |
Model identifier | Qwen/Qwen2.5-72B-Instruct |
HF_TOKEN |
HuggingFace API token | required |
VOICE_TASK |
Task to run | clean_detection |
ENV_SERVER_URL |
Environment server URL | http://localhost:7860 |
Docker
docker build -t voice-authenticity .
docker run --env-file .env voice-authenticity
π Project Structure
voice-authenticity-openenv/
βββ environment/
β βββ __init__.py
β βββ env.py # 5-action step/reset/state with partial observability
β βββ models.py # Pydantic Observation/Action/Reward models
β βββ graders.py # 6-component scoring with difficulty weights + scaling
β βββ data/
β βββ features.npy # clean features (500 Γ 48)
β βββ features_compressed.npy # codec-degraded features
β βββ features_adversarial.npy# adversarially perturbed features
β βββ features_streaming.npy # streaming degraded features
β βββ features_phonecall.npy # phone call degraded features
β βββ features_raw.npy # unnormalized values
β βββ labels.npy # ground truth labels
β βββ labels_compressed.npy
β βββ labels_adversarial.npy
β βββ labels_streaming.npy
β βββ labels_phonecall.npy
βββ scripts/
β βββ download_data.py # fetch dataset from HuggingFace
β βββ extract_features.py # audio β feature vectors (5 tasks)
βββ server/
β βββ app.py # OpenEnv HTTP server entry point
βββ Dashboard.html # interactive web dashboard (served at / and /web)
βββ app.py # FastAPI server (serves Dashboard.html + API)
βββ inference.py # baseline LLM agent (5-action protocol)
βββ test_env.py # environment unit tests (5 tests)
βββ openenv.yaml # OpenEnv spec (5 tasks)
βββ pyproject.toml # package config
βββ Dockerfile
βββ requirements.txt
βββ README.md
π₯οΈ Web Dashboard
Dashboard.html is a self-contained, interactive web interface served at both / and /web when the server is running. It provides:
- Real-time investigation simulation β press a button to watch the 5-step agent protocol animate live, with terminal-style log output
- Task difficulty breakdown β all 5 tasks with difficulty badges, score bars, and detailed descriptions
- 6-component score explorer β click any task to see its grader breakdown across correctness, confidence calibration, trajectory quality, feature utilization, reasoning consistency, and action ordering
- Step-by-step protocol visualization β the full 5-action investigation protocol with reward annotations and animated step progression
The dashboard uses no external frameworks β pure HTML, CSS, and vanilla JavaScript.
π§ͺ Test Suite
test_env.py β Environment Unit Tests
Five targeted tests validating core environment behavior:
| Test | What It Validates |
|---|---|
test_reset_returns_observation |
reset() returns a valid VoiceObservation with step 0, correct task name, and hint |
test_step_returns_reward_in_range |
Rewards from step() are always in [0.05, 0.95] β never exactly 0.0 or 1.0 |
test_five_actions_complete_episode |
The full 5-action protocol (temporal β spectral β comparison β analyze β classify) completes an episode with done=True |
test_reward_never_zero_or_one |
Explicit check that no step returns a boundary reward of exactly 0.0 or 1.0 |
test_all_five_tasks_load |
All 5 task variants (clean, compressed, adversarial, streaming, phonecall) load successfully and return valid observations |
Run: pytest test_env.py -v
π¬ Technical Pipeline
Feature Extraction
flowchart TD
A["π€ Raw Audio\n(.wav / .flac)"] --> B["librosa"]
A --> C["parselmouth / Praat"]
B --> D["MFCC Means (20)\nMFCC Stds (20)\nZCR Β· Spectral Centroid\nBandwidth Β· Rolloff Β· RMS"]
C --> E["Jitter Β· Shimmer Β· HNR"]
D --> F["Concatenate β 48-dim raw vector"]
E --> F
F --> G["Z-Score Normalization\n(per-feature mean/std)"]
G --> H["float32 feature vector (48-dim)"]
H --> I["Clean\nfeatures.npy"]
H --> J["Compressed\nfeatures_compressed.npy"]
H --> K["Adversarial\nfeatures_adversarial.npy"]
H --> L["Streaming\nfeatures_streaming.npy"]
H --> M["Phone Call\nfeatures_phonecall.npy"]
style A fill:#1a1a2e,stroke:#e94560,color:#fff
style B fill:#0f3460,stroke:#e94560,color:#fff
style C fill:#0f3460,stroke:#e94560,color:#fff
style D fill:#16213e,stroke:#0f3460,color:#e0e0e0
style E fill:#16213e,stroke:#0f3460,color:#e0e0e0
style F fill:#1a1a2e,stroke:#e94560,color:#fff
style G fill:#1a1a2e,stroke:#e94560,color:#fff
style H fill:#e94560,stroke:#e94560,color:#fff
style I fill:#0d2d1e,stroke:#14b8a6,color:#ccfbf1
style J fill:#1a1a00,stroke:#fbbf24,color:#fef3c7
style K fill:#1a0000,stroke:#f87171,color:#fecaca
style L fill:#0d1a2d,stroke:#fb923c,color:#fed7aa
style M fill:#1a0010,stroke:#d946ef,color:#f5d0fe
Compression Simulation (Task 2)
Codec compression is simulated by degrading MFCC standard deviations, reducing jitter and shimmer values, and adding spectral artifact signals β replicating the acoustic degradation introduced by MP3/codec pipelines.
Adversarial Simulation (Task 3)
Adversarial perturbation shifts synthetic sample features into the real speech distribution range, and real sample features toward the synthetic range. Controlled label noise (8%) simulates real-world annotation ambiguity. No clean threshold separates the classes.
Streaming Simulation (Task 4)
Features undergo two layers of degradation: a static perturbation (partial MFCC decode, mild temporal noise) baked into the data files, and a dynamic soft-gated noise applied at runtime that reduces as the agent takes more steps. Early requests return noisier data; later requests return cleaner data β rewarding intelligent sequencing without forcing a fixed order.
Phone Call Simulation (Task 5)
The most aggressive degradation: narrowband codec compression zeros out high-order MFCCs, flattens MFCC temporal variation, injects broadband Gaussian noise, severely degrades HNR, and adds RMS energy fluctuation simulating packet loss. Designed to be near the limit of what's detectable.
π License
MIT