ARCHITECTURE
An LLM agent learns to be a race engineer — reading hidden state, calling the pit, managing tyres across a 12-lap sprint with six independently-scored reward dimensions.
FastAPI server wrapping a deterministic F1 physics model. Each step = one strategic decision. 25+ racetrack CSVs, 6 scenario families, 5 rule-based opponents, real weather events.
POST /reset · POST /step · GET /stateQwen3-4B warm-started with SFT on 4,900 expert turns (enriched obs, thinking-off render), then GRPO-refined for 200 steps with a fully-deterministic reward function. No LLM judge — every reward is a Python check.
train.py --base-checkpoint sft/merged --backend trlSix scenario families × 5 seeds. Real LLM forward pass through the env (not the scripted-fallback bug we caught) — the trained model averages 0.62, +0.20 over untrained Qwen3-4B. On weather it (0.97) actually edges the rule-based expert (0.95).
evaluate.py --model grpo_v2/merged --n-seeds 5ENVIRONMENT
Each circuit has a natural scenario archetype. The agent must generalise across very different strategic challenges — not just memorise one sequence.
TRY IT LIVE
Select a scenario, seed, and policy, then hit Run Race. The environment runs a full episode server-side and streams the lap-by-lap result back — no command line needed. The trained policy is the SFT+GRPO Qwen3-4B model (grpo_v2).
# pip install openenv from f1_strategist import F1Action, F1Env with F1Env.from_env("Deltasthic/f1-strategist") as env: obs = await env.reset(task="weather_roulette") while not obs.done: action = my_agent(obs) obs = await env.step(F1Action(message=action)) print(obs.score)
# Investigation INSPECT_TYRE_DEGRADATION CHECK_OPPONENT_STRATEGY <num> REQUEST_FORECAST ASSESS_UNDERCUT_WINDOW INSPECT_FUEL_MARGIN # Strategy PIT_NOW <soft|medium|hard|inter|wet> SET_MODE <push|conserve|race|defend> RADIO_DRIVER <"message"> DEFEND_POSITION · HOLD_GAP · DONE
# 1. Reset curl -X POST .../reset \ -d '{"task":"weather_roulette","seed":7}' # 2. Step curl -X POST .../step \ -d '{"action":"PIT_NOW inter"}' # 3. Run full episode (returns trajectory) curl -X POST .../simulate \ -d '{"task":"weather_roulette","seed":7}'
TRAINING EVIDENCE
Every plot comes from the actual grpo_v2 checkpoint run. No cherry-picking — 5 seeds per task family, averaged. The training reward curve is generated from grpo_v2/checkpoint-200/trainer_state.json — every point is a real logging step.
| Scenario | Random | Untrained | Trained ↑ | Expert |
|---|---|---|---|---|
| Dry sprint | — | — | — | — |
| Weather roulette | — | — | — | — |
| Late safety car | — | — | — | — |
| Championship | — | — | — | — |
| VSC window | — | — | — | — |
| Tyre cliff | — | — | — | — |
trainer_state.json · peak 0.93
MODEL'S JOURNEY
We didn't get to the final number on the first try. Five distinct bugs hid the model's true performance — including one that proved the originally-reported 0.79 was a hand-coded scripted policy, not the LLM. The chart below is every iteration we ran. Honest numbers. Real model.
weighted_final across 4–6 scenarios at each stage. Star is our shipping checkpoint.
Reported "0.79 trained" was actually a scripted-policy fallback. Verified bit-exact by running the rule policy with no model loaded — std=0.000 across 5 seeds was the smoking gun.
Qwen3 has reasoning mode on by default. With max_new_tokens=64 the model never finished thinking → unparseable rambles → STAY_OUT default every step.
Trained with thinking-on, eval'd with thinking-off. The chat-template prefix the model never saw at training tanked the score by another 0.10.
format_obs stripped the scenario briefing/hint. The model couldn't tell late_safety_car from dry_strategy_sprint at lap 0 → applied the wrong playbook everywhere.
Cold GRPO from base Qwen3 hit reward-variance collapse by step 200. Fix: SFT warm-start first, then GRPO. Each layer added what the other couldn't — the breakthrough.
Closing the 0.30 gap to expert isn't a single fix — it's a tier list of changes. Ranked by expected ROI per hour of work, the things we'd start with:
Full tier list with rationale lives in blog.md under "What's next — concrete starting points". This is the document we'd hand to the person picking this up next.
MATERIALS
All judge-required materials in one place. The Colab notebook is self-contained and re-runnable. The blog post is the full writeup on HF. The video walks through the live environment.
Full writeup: environment design, GRPO training setup, results analysis, and key learnings. Pushed as blog.md to the HF Space repo.
Shows the environment in action, the trained vs untrained comparison on Weather Roulette, and the key pit-call decision at Lap 7. Link in README.
See README for link →Full environment smoke test, SFT warm-start, GRPO training with TRL + Unsloth, evaluation, and result plots. Verified runnable on T4/A100.
Open in Colab →TEAM
Meta PyTorch OpenEnv Hackathon · Grand Finale · Bangalore · April 2026