A 1.5 B-parameter LLM that designs auction mechanisms robust to a
population of strategic adversaries (colluders, shaders, dropouts,
exploiters). Trained with GRPO (TRL) on the
kabilesh-c/Daedalus-Env OpenEnv environment using
Unsloth + 4-bit + LoRA.
Given a partial observation of an auction market — recent (welfare,
fairness, participation) outcomes, round number, episode length — the
model emits a structured JSON mechanism:
The mechanism is then used — the env runs 5 market rounds against an
adaptive adversarial population, and scores the result on the composite
reward R = welfare_ratio · fairness_score · participation_rate · stability_score.
This is the inverse of the usual RL setup: the model is the
referee, not the player.
LoRA, r = 16, applied to all attention + MLP linear layers
Quantisation (training)
4-bit NF4 via bitsandbytes + Unsloth
Storage format
merged 16-bit bf16 (no separate adapter file required at inference)
Context length
32,768 tokens (Qwen2.5 default)
Output schema
Pydantic DaedalusAction — see live Space for full type definition
License
Apache-2.0 (inherited from Qwen2.5-1.5B-Instruct)
3. Training details
Pipeline
Qwen2.5-1.5B-Instruct (4-bit, Unsloth)
|
| LoRA r=16 on attn + MLP
v
+--- SFT on synthetic (prompt, valid_mechanism) pairs
| (teaches JSON shape only; ~300 steps)
v
GRPO (TRL) on DAEDALUS env, 50 steps
reward = format_reward + welfare + fairness + composite
|
| merge_and_unload + push_to_hub_merged
v
kabilesh-c/daedalus-designer-v2 (this checkpoint)
Hyperparameters
RL algorithm
GRPO (Group-Relative PPO, no value head)
Generations per state
4
Steps
50
Batch size
4 × 2 grad-accum × 1 GPU = 8 effective
Learning rate
5e-6 (LoRA)
Reward stack
format (JSON parseability) → welfare → fairness → composite
Hardware
1 × NVIDIA L4 (24 GB) on Hugging Face Jobs
Wall-clock
~7 minutes per +50-step iteration
Cost
~$0.10 per iteration
Training signals (from training_history.json)
Metric
Range
Mean
What it tells you
grad_norm
0.46 – 0.84
0.71
healthy gradient flow, no vanish/explode
reward_std (within-group)
0.53 – 0.86
0.69
GRPO advantage signal stays informative
entropy (policy)
1.65 – 2.50 nats
1.95
narrowed by ~5.5 nats from uniform; still exploring
completions/mean_length
140 tokens (pinned)
140
schema-locked output
Plots and per-step CSV are served from the live Space at /plots/.
4. Evaluation
30-episode baseline boxplot on the env's true composite reward (no
training-time shaping):
Policy
Mean R
IQR
Uniform-random mechanisms
+0.326
≈ 0.27
Informed-greedy (hand-engineered "robust")
+0.434
≈ 0.21
The +0.108 gap (≈ +33 % relative) is the structural signal the model is
trained to find. The trained model's online behaviour can be inspected
directly in the live Space — every /api/design call hits this
checkpoint.
A typical stage-3 (mixed shaders + colluders) output:
Picks second-price (truthful in static, robust to non-VCG-aware bidders).
Hides bid distribution but reveals clearing price — gives honest bidders calibration signal while starving cartels of enforcement signal.
Penalty ranking: collusion (1.9) > shill (1.2) > withdrawal (0.6) matches the relative cost of each pathology in this population.
5. Inference
5.1 Hosted (no setup) — call the live Space
python
1import requests
23BASE ="https://kabilesh-c-daedalus-env.hf.space"45# Reset and read the initial observation6r = requests.post(BASE +"/reset",7 json={"session_id":"demo","n_agents":8,"episode_length":10})8obs = r.json()["observation"]910# Ask the trained designer for a mechanism11r = requests.post(BASE +"/api/design", json={12"round_number": obs["round_number"],13"episode_length": obs["episode_length"],14"market_outcomes": obs.get("market_outcomes",[]),15})16print(r.json()["mechanism"])1718# Apply it and observe the reward19r = requests.post(BASE +"/step",20 json={"session_id":"demo","action": r.json()["mechanism"]})21print("R =", r.json()["reward"])
5.2 Local — transformers + huggingface_hub
Important: this repo was originally pushed via Unsloth's
save_pretrained_merged(), which leaves a leftover adapter_config.json
whose base_model_name_or_path points at ./sft-merged (a path that
only exists on the training filesystem). Plain from_pretrained(repo_id)
will follow that pointer and crash with
HFValidationError: Repo id must use alphanumeric chars. Use
snapshot_download with ignore_patterns to skip the adapter file:
python
1from huggingface_hub import snapshot_download
2from transformers import AutoModelForCausalLM, AutoTokenizer
3import torch
45local_dir = snapshot_download(6 repo_id="kabilesh-c/daedalus-designer-v2",7 ignore_patterns=["adapter_config.json","adapter_model.safetensors"],8)910tok = AutoTokenizer.from_pretrained(local_dir)11model = AutoModelForCausalLM.from_pretrained(12 local_dir,13 torch_dtype=torch.bfloat16,# fp16 if no bf1614 device_map="auto",15)1617system =(18"You are an auction mechanism designer. Given a market observation, "19"output ONLY a JSON object matching the DaedalusAction schema."20)21user =(22"round_number: 3 / 10\n"23"recent_outcomes: [{welfare:0.7, fairness:0.4, participation:1.0}]\n"24"Respond with the JSON only."25)26prompt = tok.apply_chat_template(27[{"role":"system","content": system},28{"role":"user","content": user}],29 tokenize=False, add_generation_prompt=True,30)31ids = tok(prompt, return_tensors="pt").to(model.device)32out = model.generate(**ids, max_new_tokens=180, do_sample=False, temperature=0.0)33print(tok.decode(out[0, ids.input_ids.shape[1]:], skip_special_tokens=True))
5.3 Local — full env rollouts via inference.py
The repo at kabilesh-c/Daedalus-Env ships an
inference.py that reproduces the §4 baseline numbers:
bash
1# one-shot mechanism for a fresh env2python inference.py
34# 30 episodes trained-vs-random (writes inference_results.json)5python inference.py --n-episodes 30 --baseline
67# point at a different checkpoint8python inference.py --repo-id kabilesh-c/daedalus-designer-v2
6. Intended use & limitations
Intended use: demonstration / research on LLM-driven mechanism
design. The model is trained to output well-formed DaedalusAction
JSON for the DAEDALUS env; it has no production guarantees.
Limitations:
Trained for only 50 GRPO steps — the +50 step lift over a randomly-
initialised LoRA is real but small in absolute terms (composite-reward
mean from −0.594 → −0.579).
The "format reward" dominates early steps; malformed JSON is heavily
penalised, so the model is schema-locked but not strategically
perfect. On stage-4 (full-adversarial) populations it occasionally
emits coalition_policy: "allow", which craters the run.
Reward is engineered for the DAEDALUS env's specific multiplicative
composite. The model is not guaranteed to produce sensible
mechanisms outside this rubric.
1@misc{kabilesh2026daedalus,
2 title = {DAEDALUS: Training an LLM to Design Auction Markets via Adversarial RL},
3 author = {Laksh Krish Kabilesh},
4 year = {2026},
5 url = {https://huggingface.co/spaces/kabilesh-c/Daedalus-Env},
6}