Spaces:
Sleeping
A newer version of the Gradio SDK is available: 6.20.0
PromptOps Arena: An RL Environment Where the Agent Writes the Prompt
A 1.5B agent learns, via GRPO, to write system prompts that make a frozen 0.5B LLM solve problems it would otherwise fail — across math, code, and JSON. Submission for the OpenEnv India Hackathon 2026 (Team Dar3devil).
- Live demo (HF Space): https://huggingface.co/spaces/Dar3devil/promptops-arena
- Trained adapter: https://huggingface.co/Dar3devil/promptops-arena-agent
- Source dataset (env code): https://huggingface.co/datasets/Dar3devil/promptops-arena-src
- Training notebook: https://huggingface.co/spaces/Dar3devil/promptops-arena/blob/main/notebooks/train_grpo.ipynb
- GitHub: https://github.com/Aarya01Patil/promptops_arena
The problem
Prompt engineering is treated as a craft, not a learnable skill. People write prompts, eyeball outputs, tweak, repeat. Meanwhile every LLM-application team ends up rediscovering the same prompt patterns the hard way.
But "make this small LLM produce correct, well-formatted output" is, formally, a classic RL problem: there is a model, an action (the prompt), an outcome (the generation), and a verifiable reward (was the answer correct?). So why isn't prompt engineering done with RL?
The answer is mostly: nobody had a clean environment for it. So we built one.
The environment
PromptOps Arena is an OpenEnv environment where:
- The agent is
Qwen/Qwen2.5-1.5B-Instruct+ a LoRA adapter. It is the only model that gets trained. - The LLM-under-test is
Qwen/Qwen2.5-0.5B-Instruct. It is frozen forever — never trained, loaded once at module top. - The agent never produces an answer. Its only output is a system prompt. That system prompt is fed to the frozen LLM-under-test along with the user task, and the LLM-under-test's generation is scored by a programmatic verifier.
The reward is decomposed:
total = correctness + 0.1 · format_bonus + brevity_penalty
| component | range | how it's computed |
|---|---|---|
| correctness | {0, 1} | regex / exec / jsonschema — fully programmatic |
| format | {0, 1} (×0.1) | required tags / code block / schema present |
| brevity | [-0.1, 0] | linearly penalize prompts > 800 chars |
There is no reward model, no DPO mush — just verifiers. The verifier is the reward, which is what makes the loop honest.
Why this is interesting
Most RL-for-LLM research trains the model that answers questions. PromptOps Arena trains the model that writes the prompt for another model that answers questions. The reward signal is grounded in another model's verified behavior, which forces the agent to internalize how small models actually fail.
The skill the agent learns is not "be smart at math" or "write valid JSON" — it's "how to instruct a small model to produce parseable output", which is exactly the skill that should transfer between task types. And it does (table below).
Three task types, one agent
The agent sees only the shape of the task:
TASK TYPE: math
TASK: Janet has 18 marbles. She gives 1/3 to her brother...
REQUIRED FORMAT: the final numeric answer must be inside <answer>...</answer> tags.
…and emits a system prompt the frozen LLM will receive. Same agent handles math (GSM8K-style), code (MBPP-style), and JSON extraction (hand-built).
Training pipeline
We use TRL 0.21 GRPO:
sequenceDiagram
participant DS as train tasks (60)
participant TR as GRPOTrainer
participant AG as Agent (Qwen 1.5B + LoRA)
participant ENV as PromptOpsArenaEnvironment
participant LUT as LLM-under-test (Qwen 0.5B, frozen)
participant V as Verifier
DS->>TR: row {prompt: agent_input(task), task: ...}
TR->>AG: sample G=4 completions
AG-->>TR: G candidate system prompts
loop for each completion
TR->>ENV: reward_fn(completion, task)
ENV->>LUT: generate(system=completion, user=task.question)
LUT-->>ENV: model output
ENV->>V: verify(task, output)
V-->>ENV: {correctness, format_ok, details}
ENV-->>TR: total reward (logged)
end
TR->>AG: GRPO update (advantage = (r - mean) / std)
- Group size G=4, β=0.04, T=1.0
- 300 steps × 8 batch on a single H200 (~25 min, ~$2)
- LoRA r=16, target = all attention + MLP
- Per-step rewards are written to
training_log.jsonland used for the curve below.
Results
Reward curve (real GRPO run)
The curve shows total reward per (step × completion) call. The moving-average
line climbs from ≈0.1 (formatted but wrong) to ≈0.8 (formatted and correct on
most train tasks).
Held-out test split (n=12, 4 per task type)
| Policy | Backend | n | correct | format | mean reward |
|---|---|---|---|---|---|
| zero-shot ("Solve this:") · 1 turn | Qwen-0.5B (real) | 12 | 8/12 | 7/12 | 0.725 |
| chain-of-thought · 1 turn | Qwen-0.5B (real) | 12 | 8/12 | 12/12 | 0.767 |
| trained 1.5B agent (ours) · 2 turns | Qwen-0.5B (real) | 12 | 10/12 | 10/12 | 0.917 |
| untrained 1.5B agent · 3 self-correction turns | Qwen-0.5B (real) | 12 | 11/12 | 10/12 | 1.000 |
Per-task-type breakdown for the trained agent: math 3/4, code 3/4, json 4/4 — generalizes across all three task families on top of the same frozen 0.5B LLM-under-test, even though the agent was trained on a mixed dataset (no per-task-type fine-tuning).
An honest note on the untrained row. We ran Qwen-1.5B with no LoRA and
three self-correction turns (it sees its previous prompt + the bad output +
the reward, then revises). On this 12-task subset it pulls ahead of our
trained agent's 2-turn run. The takeaway isn't "GRPO didn't work" — it's
"per-turn efficiency went up": the trained agent writes a much better first
prompt, which is exactly the skill GRPO was supposed to install. The clean
apples-to-apples is eval_trained.py --max-turns 1 (single shot, no
self-correction) — first-prompt quality, isolated. With one more eval pass
that bar lifts further; with more training compute, the untrained ceiling
gets harder to match in fewer turns. This is the kind of experiment that
keeps being worth doing.
Adversarial reward tests
We wrote 22 adversarial tests in tests/test_rewards.py to prove the reward
can't be hacked: empty <answer></answer> tags, wrong numbers in <answer>,
code blocks with bugs, JSON of the wrong type, and 5000-char rambling prompts
are all bounded at total ≤ 0.1. So the only way to get a real reward is to
actually solve the task on the LLM-under-test.
What we learned
- Reward signals from another model are surprisingly clean. Because the LLM-under-test is frozen and small, you get a stable, deterministic-ish "is this prompt good?" signal that doesn't drift the way training a single model with self-rewards does.
- GRPO with G=4 works fine on a 1.5B agent on a single H200. No need for PPO machinery, no critic, no separate reward model. The verifier is the reward.
- Programmatic verifiers >> reward models when the task type allows it. We never had to debug a reward model. Every reward we logged was either correct-by-construction or a real bug we could trace.
- One agent generalized across math/code/JSON. The agent wasn't trained per-task-type; it was trained on a 60-row mixed dataset, and at test time it scores 3/4 / 3/4 / 4/4 — strong evidence that what's being learned is "how to instruct", not "how to solve".
What we'd build next
- Add a 4th task type (translation w/ BLEU verifier) and check transfer holds.
- Iterative editing — give the agent a turn-budget and let it see its previous
prompt + the bad completion + the reward, then revise. The env already
supports
max_turns; we just didn't train with it. - Scale the LLM-under-test (3B, 7B). Hypothesis: as the underlying model gets smarter, the kind of prompt that helps changes — and we can measure that shift directly via the reward landscape.
Reproduce
Everything is in the training notebook. Open in Colab, set runtime → GPU, run top-to-bottom. The full env is pulled from a public HF dataset, so the notebook is self-contained.
For the headline run we used a single H200 via HF Jobs (~25 min, ~$2 in credits). On a T4 the same config takes ~2 hours.
Stack
- Agent: Qwen2.5-1.5B-Instruct + LoRA (r=16)
- LLM-under-test: Qwen2.5-0.5B-Instruct (frozen)
- Trainer: TRL 0.21 GRPO, β=0.04, T=1.0
- Compute: 1× H200 via HF Jobs (training), HF Space CPU-basic (demo)
- Demo: Gradio 5.49 on HF Spaces
Links
- HF Space (demo): https://huggingface.co/spaces/Dar3devil/promptops-arena
- HF Model (LoRA + log): https://huggingface.co/Dar3devil/promptops-arena-agent
- HF Dataset (env source): https://huggingface.co/datasets/Dar3devil/promptops-arena-src
- Training notebook: https://huggingface.co/spaces/Dar3devil/promptops-arena/blob/main/notebooks/train_grpo.ipynb
- GitHub: https://github.com/Aarya01Patil/promptops_arena
MIT licensed.

