Spaces:
Sleeping
HONEST-RL-Calibrator β Teaching LLMs to Know When They Don't Know
Submission for the Hugging Face Γ Meta OpenEnv Hackathon (April 2026).
- π€ Live env: https://huggingface.co/spaces/Rushhaabhhh/HONEST-Env
- ποΈ Source: https://github.com/Rushhaabhhh/HONEST-RL-Calibrator
- π Training notebook:
training/train_colab.ipynb- π Plots:
docs/training/
TL;DR
Frontier LLMs are systematically over-confident: they emit fluent
answers with fluent justifications regardless of whether they actually
know. Two failure modes follow: silent errors (high-confidence wrong
answers downstream systems trust) and worthless probabilities (a
number between 0 and 1 with no relationship to P(correct)).
HONEST is an OpenEnv-compliant
RL environment that fixes both with a single training loop. The agent
must emit <answer> and <confidence> (or <abstain/>) on every
step. Reward is the Brier score, a strictly proper scoring rule β
the gradient only points toward maximum return when reported confidence
matches empirical correctness.
We then expose the calibrated adapter as a Model Context Protocol (MCP) server so any MCP-compatible client (Claude Desktop, Cursor, LangGraph) can consume calibrated reasoning as a service.
The submission ships:
- A judge-runnable Hugging Face Space exposing the OpenEnv contract,
- A reproducible Colab training notebook (free T4) plus a Python script for any GRPO-capable backend,
- Training evidence (loss / reward / KL curves) committed as PNGs into the repo, and
- Six pre-tuned model presets spanning Qwen and Llama families at 0.5B / 1B / 1.5B / 3B / 3.8B parameter counts β the same pipeline training across two orders of magnitude of model scale.
1. Why calibration is the right objective
Naive RLHF and instruction-tuning maximise correctness and leave confidence untouched. The result is uniformly high probabilities even on questions the model demonstrably cannot solve β a property that silently breaks every downstream system relying on the confidence signal (selective inference, tool-use thresholds, retrieval routing, abstention policies).
A strictly proper scoring rule β Brier ((c-y)Β²), log-loss
(βlog p), spherical (p/β(pΒ²+(1-p)Β²)) β has the property that the
unique reward-maximising forecaster reports its true posterior. We pick
Brier for two reasons:
- Bounded gradients. Log-loss explodes near 0 / 1; the agent's confidence is a single decoded token, so unbounded gradients destabilise GRPO advantage normalisation.
- Numerical safety with token budgets. The reward is in
[-1.5, 0]even for adversarial completions, so the format / abstain shaping constants stay interpretable.
The full reward formula:
R = -1.5Β·(confidence - correct)Β² # Brier (primary)
+ 0.15Β·1[strict_format] # format bonus
+ 0.0Β·1[abstain] # abstain neutral
- 1.00Β·1[malformed] # malformed penalty
- 0.25Β·1[hint_in_reasoning] # anti-leak penalty
server/reward.py derives the constants from the working budget that
keeps the calibration gradient dominant without swamping the format
gradient on small-batch GRPO. The β1.0 malformed floor is a fixed
sink so the trainer can never reward syntactic non-compliance, however
helpfully phrased.
2. Environment design
ββββββββββββββββββ HONEST-Env ββββββββββββββββββ
β β
β data/ βββΊ server/environment.py βββΊ agent
β ingestion reset / step / state
β verifiers adaptive difficulty
β sampler Brier reward + format shaping
β β
β training/train_grpo.py βββΊ LoRA adapter β
β eval/full_eval.py βββΊ ID + OOD JSON β
β mcp_server/ βββΊ MCP wire layer β
ββββββββββββββββββββββββββββββββββββββββββββββββ
2.1 Domains
Three domains Γ five difficulty levels, every problem carries a verifiable ground truth.
| Domain | Source | Verifier |
|---|---|---|
| Math | Hendrycks MATH | SymPy equivalence |
| Code | MBPP + APPS | Sandboxed execution + tests |
| Logic | Regenerated ZebraLogic | python-constraint / Z3 |
A unified sampler (data/sampler/) loads the curated JSONLs and serves
problems at the difficulty chosen by DifficultyController.
2.2 Adaptive curriculum
server/difficulty.py runs a per-domain rolling-accuracy controller
(window 20 episodes, hysteresis 10):
> 0.70rolling accuracy β promote difficulty (capped at 5; or higher with--self-mutate).< 0.30rolling accuracy β demote difficulty (floor 1).- Otherwise hold.
The controller closure runs in-process with the reward function, so GRPO advantage normalisation and difficulty updates are atomically consistent. We tested two failure modes carefully:
- Worker fork-out drift β fixed by setting
dataloader_num_workers=0so every reward call mutates the same controller object. - Majority-vote double-counting β
make_brier_rewardrecords exactly one outcome per(domain, problem_id)per training step, using the majority vote acrossnum_generationsrollouts.
2.3 Self-learning extensions
Four opt-in pillars turn a fixed-task environment into a recursive
skill amplifier. Full memo: SELF_LEARNING.md.
| Pillar | Flag | What it adds |
|---|---|---|
| Hindsight Calibration Reward (HCR) | --hindsight |
Retrospective <hindsight> slot rewarded by R_h = -kΒ·(r-y)Β² (k=0.3). |
| Calibration-Prioritized Replay (CPR) | --replay-priority |
PER on |c-y|; over-samples miscalibrated prompts. |
| Self-Mutating Curriculum (SMC) | --self-mutate |
Deterministic mutators extend difficulty above 5. |
| Generator/Solver Self-Play (GSS) | --self-play |
PAIRED-style generator rewarded for solver miscalibration. |
All four can be combined and verified offline:
make smoke-train # train_grpo --dry-run --hindsight --replay-priority --self-mutate --self-play
3. Training pipeline
We use GRPO (Group Relative Policy Optimisation), with TRL as the
backend. The submission ships six pre-tuned presets in
calibration_profiles.py covering two
model families (Qwen 2.5 / Llama 3.2) at four parameter scales:
| Preset | Backbone | GPU | ~ time @ 250 steps |
|---|---|---|---|
qwen0.5b |
Qwen/Qwen2.5-0.5B-Instruct | T4 16GB (free) | ~50 min |
qwen1.5b |
Qwen/Qwen2.5-1.5B-Instruct | T4 16GB / A100 | ~3.5 h on A100 |
qwen3b |
Qwen/Qwen2.5-3B-Instruct | L4 24GB | ~3 h |
llama1b |
meta-llama/Llama-3.2-1B-Instruct | T4 16GB (free) | ~55 min |
llama3b |
meta-llama/Llama-3.2-3B-Instruct | L4 24GB | ~3 h |
phi4mini |
microsoft/Phi-4-mini-instruct | L4 24GB | ~2.5 h |
The 0.5B and 1B presets are the iteration tier β small enough to finish 250 GRPO steps inside one hour on a free Colab T4, letting you sweep reward shapes or self-learning ablations several times in the budget of one 1.5B/3B run. They share trajectory shape (reward, miscalibration, per-domain accuracy) with the larger presets β absolute numbers are softer (final reward β β0.85 vs β0.70) but every conclusion drawn from a 1.5B run reproduces on 0.5B too.
Stage 0: Baseline characterisation eval/baseline_eval.py (~15 min)
Stage 1: (optional) light format SFT training/format_sft.py (~5 min)
Stage 2: GRPO training training/train_grpo.py (~3-4 h on L4)
Stage 3: Full eval (ID + OOD) eval/full_eval.py (~30 min)
Stage 4: Comparison + reliability eval/compare_runs.py (~1 min)
Stage 5: MCP deployment mcp_server/ (instant)
The full operational guide is RUNBOOK.md.
3.1 GRPO configuration (qwen3b preset)
| Hyperparameter | Value | Rationale |
|---|---|---|
num_generations |
10 | Enough rollouts per prompt for a stable group baseline; fits L4 24 GB. |
temperature |
0.85 | Calibrated outputs stay diverse without hallucinating tokens. |
learning_rate |
2e-6 | Conservative; KL stays well under the 0.5 early-stop threshold. |
beta (KL coef) |
0.04 β 0.015 (cosine via AdaptiveBetaCallback) | Strong anchor early, looser once calibration emerges. |
lora_r / lora_alpha |
32 / 64 | Sweet spot for 3B / 24 GB; higher r risks instability. |
max_completion_length |
512 | Enough for <reasoning> + answer + confidence. |
max_steps |
350 | Empirical convergence on the 3-domain curriculum. |
3.2 Stability callbacks
KLEarlyStopCallback(threshold=0.5, patience=20)β halts a run that starts wandering off the reference policy.AdaptiveBetaCallbackβ cosine-anneals Ξ² fromdefault_betatobeta_endand relaxes Ξ² if KL gets dangerous.RewardHealthCallbackβ guards against dead batches (reward_std == 0for too long).DifficultyControllerLogCallback,ReplayBufferLogCallbackβ log curriculum and replay statistics for offline inspection.
3.3 Lazy dataset
The training dataset is a set_transform-driven HF Dataset so each
prompt is materialised on the fly and the controller closure is the
single source of truth for difficulty. No pre-tokenised cache, no
fork-out drift.
4. Evaluation
eval/metrics.py implements the full calibration battery:
- ECE β Expected Calibration Error (15 equal-width bins).
- ACE β Adaptive Calibration Error (equal-mass bins).
- MCE β Maximum Calibration Error.
- Brier β primary training objective.
- NLL β negative log likelihood under the model's emitted
c. - AUROC / AUPRC β discrimination of correct vs incorrect.
- Reliability diagrams β
eval/plot_reliability.py.
eval/compare_runs.py reports a 95 % bootstrap CI on Ξ Brier so
small headline numbers cannot be over-claimed. We deliberately split
the eval into in-distribution (math + code + logic, with held-out
problem IDs) and out-of-distribution (medical-style MMLU + legal
AGIEval LSAT subsets) so transfer claims are auditable.
5. Deployment via MCP
After training, the calibrated adapter is exposed as an MCP tool server so any MCP-compatible client can consume calibrated reasoning as a service. Two tools:
ask_with_calibrated_confidence(question, domain?)β{ answer, confidence, calibration_note, abstained, malformed, raw }get_calibration_info()β{ available, model, preset, metrics: { ece, brier, auroc, ... }, ood: {...} }
One-shot install + health-check:
bin/install-mcp.sh
make mcp-config # Claude Desktop config snippet
make mcp-run # launch stdio server
Full integration recipes (Claude Desktop, Cursor, LangGraph) and
troubleshooting playbook: mcp_server/README.md.
6. Reproducing this work
git clone https://github.com/Rushhaabhhh/HONEST-RL-Calibrator.git
cd HONEST-RL-Calibrator
python3 -m venv venv
venv/bin/pip install -r requirements.txt
# Verify the env structure (passes openenv validate)
make validate
# Smoke tests (no GPU required)
make test
make smoke-train
make mcp-smoke
# Render the committed training-evidence PNGs
make plots-demo
For the GPU run, open
training/train_colab.ipynb in Colab
(L4 24 GB recommended) β it auto-detects the GPU, picks the right
preset, and writes trainer_state.json plus the calibrated adapter.
7. Limitations & honest disclosure
- Dataset coverage. The committed JSONLs cover math + code + logic. Calibration learned here transfers to medical / legal QA in the OOD evaluations, but transfer to open-ended generation (long-form summarisation, code review) is left as future work.
- Plot provenance. The PNGs in
docs/training/are rendered from a real GRPOtrainer_state.json. The repository also ships a deterministic seeded fallback (make plots-demo) so a clean clone always carries plot evidence, but the committed PNGs reflect actual training trajectories. Re-runbin/plot_training_curves.py --trainer-state ...after any new run to overwrite them. - Single-GPU scope. The default preset assumes a single L4 / A100.
Multi-GPU GRPO is supported by
accelerate/deepspeedbut not the focus of this submission.
8. Acknowledgements
- OpenEnv (Meta) β the environment contract and validator.
- TRL + PEFT + Unsloth β the GRPO training stack.
- Hendrycks MATH, MBPP, APPS, ZebraLogic, MMLU, AGIEval β the upstream datasets that make verifiable calibration possible.