--- 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: ```python 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 ```python 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 ```python 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 ```bash 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 ```bash 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 ```bash # 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 ```bash 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 ```mermaid 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