This repository contains the official implementation for TRACE — Turning Recurrent Agent failures into Capability-targeted training Environments. TRACE is an end-to-end system for environment-specific agent self-improvement.
Large Language Models deployed as agents in complex environments often fail because they lack specific, underlying capabilities. Existing approaches typically rely on generic synthetic data or direct RL on the target environment, which forces the model to learn the missing capabilities implicitly and inefficiently. TRACE solves this by:
- Automatically identifying the exact capabilities the agent lacks,
- Synthesizing targeted training environments that isolate each missing capability,
- Training a separate LoRA adapter per capability via GRPO, and
- Routing at inference time to the right adapter for the task at hand.
The pipeline has four automated steps. Each step is driven by an LLM coding agent
(Claude Code, Codex, etc.) following a markdown prompt under prompts/.
| # | Step | Prompt | Output |
|---|---|---|---|
| 1 | Capability Selection | prompts/<benchmark>/capability_selection.md |
selected_capabilities.json — ranked list of missing capabilities |
| 2 | Environment Generation | prompts/<benchmark>/environment_generation.md |
one synthetic training env per capability |
| 3 | GRPO Training | train/train_grpo.py |
one LoRA adapter per capability |
| 4 | Select & Adapt | moe_gate/ |
gate that routes inference to the right adapter |
TRACE has been validated on three benchmarks across two model scales:
| Benchmark | Base model | Baseline | + TRACE | Δ | Notes |
|---|---|---|---|---|---|
| τ²-Bench (Airline + Retail) | Qwen3-8B | 32.9% | 47.0% | +14.1 pp | from paper |
| ToolSandBox | Qwen3-8B | 0.411 | 0.552 | +0.141 | mean similarity score |
| SWE-bench Verified | Qwen3.6-27B | 58.2% | 62.2% | +4.0 pp | best iter checkpoint; see swebench/qwen3.6_self_trace/ |
The Qwen3.6 SWE-bench reference run lives in swebench/qwen3.6_self_trace/,
including the phase scripts, generated-environment wrapper, and reproduction notes.
It is the recommended starting point for adapting TRACE to a new benchmark.
TRACE/
├── prompts/ Prompts for the LLM-driven pipeline stages.
│ ├── general/ Env-agnostic templates (use as starting point
│ │ when adapting TRACE to a new benchmark).
│ ├── tau-bench/ Filled-in version for τ²-Bench.
│ └── swebench/ Filled-in version for SWE-bench Verified.
│
├── pipeline/ Pipeline-stage scripts (benchmark-agnostic).
│ ├── aggregate_capabilities.py Aggregation step for capability selection.
│ ├── calibrate_environment.py Rollout statistics and pass@k threshold check.
│ └── _summarize.py Trajectory compaction for capability selection.
│
├── swebench/ SWE-bench reference runs (one folder per run).
│ └── qwen3.6_self_trace/ End-to-end Qwen3.6-27B run (phase1-phase6).
│
├── train/ GRPO training + gate training.
│ ├── train_grpo.py Train one capability-specific LoRA.
│ ├── train_router_sft.py SFT warm-start of the MoE gate.
│ ├── warmstart_gater.py Gate-only warm-start with frozen base + LoRAs.
│ ├── grpo_gater.py GRPO fine-tune of the MoE gate.
│ ├── collect_rollouts.py Rollout collection against vLLM.
│ └── ... model.py, ppo.py, inference.py, config.py
│
├── moe_gate/ Inference-time routing across capability LoRAs.
│ ├── README.md How the gate works (per-layer / global modes).
│ ├── gater.py LayerCapabilityGater / TrajectoryGlobalRouter.
│ ├── multi_lora.py MultiLoRAGatedLinear (weighted-mix forward).
│ ├── model_builder.py build_capability_model(...).
│ ├── hf_wrapper.py / freeze_utils.py / smoke_test.py
│
├── configs/ YAML configs for render_pipeline.py.
│ ├── capability_selection.yaml
│ ├── environment_generation.yaml
│ └── moe_gate.yaml
│
├── render_pipeline.py Renders prompt templates with config values.
├── game_registry.py Game/environment registry.
└── requirements.txt
- Python 3.11+
- CUDA-capable GPUs:
- At least 1 GPU for the vLLM inference server during capability selection.
- 4+ GPUs recommended for GRPO training (TP=4 keeps Qwen3.6-27B comfortably in memory at 128k context).
- conda or equivalent.
conda create -n trace python=3.11 -y
conda activate trace
pip install -r requirements.txtTRACE is driven by handing per-stage markdown prompts to an LLM coding agent (Claude Code, Codex, etc.). For each stage you have two choices:
- Use a filled-in prompt from
prompts/<benchmark>/directly — fastest if your benchmark already has a folder. - Render the template in
prompts/general/from a YAML config — best when adapting to a new benchmark.
Both tau-bench and swebench ship with already-rendered, reviewed prompts:
# Capability selection (Step 1)
cat prompts/swebench/capability_selection.md # read it first
# then hand it to Claude Code / Codex / etc.
# Environment generation (Step 2)
cat prompts/swebench/environment_generation.mdEdit one of configs/*.yaml with your model + eval results, then render:
python render_pipeline.py configs/capability_selection.yaml --stage capability
python render_pipeline.py configs/environment_generation.yaml --stage environmentThis writes rendered prompts into prompts/rendered/<run_name>_<stage>.md. Hand
those to your coding agent.
Run the discovery + labeling + aggregation flow described in
prompts/<benchmark>/capability_selection.md. The agent will:
- Read your eval results (pass/fail trajectories).
- Phase 1: Propose 5-10 candidate capabilities.
- Phase 2: Spawn 10 parallel labeling subagents, each independently labeling every
(trajectory, capability) pair as
NA,PRESENT, orLACKING. - Phase 3: Run
pipeline/aggregate_capabilities.pyto compute coverageCov(c)and contrastive gapΔ(c)per run, apply the dual-threshold filter (Cov ≥ ρ,Δ ≥ δ) and cross-run consistency (K of Nruns).
Output: pipeline/selected_capabilities.json — each entry has a name,
description, mean_cov, mean_delta, example failed cases, and status: "PENDING".
Run prompts/<benchmark>/environment_generation.md once per capability. Each
invocation picks the top PENDING capability (highest mean Δ) and:
- Generates a synthetic environment file (a game class for tool-use benchmarks; a pytest-graded scenario JSON set for SWE-bench).
- Self-tests the environment (asserts the target test fails pre-fix and passes after applying the oracle).
- Launches a vLLM server and collects validation rollouts at temperature 1.0.
- Calibrates difficulty by mutation: if the base model's success rate is too high, the agent applies surface-disguising mutations (rename identifiers, inline helpers, add decoy edit sites) and re-tests, up to 5 rounds.
- Marks the capability as
DONEand stops. The user re-invokes for the next one.
Output (tool-use benchmarks): capability_<name>_game.py at the project root.
Output (SWE-bench): pipeline/swebench/<capability>/scenarios_parsed.json
(10 calibrated scenarios per capability).
Train one LoRA adapter per capability.
3a. Launch the vLLM inference server:
conda activate trace
export CUDA_VISIBLE_DEVICES=0,1,2,3 # GPUs for inference
export VLLM_ALLOW_RUNTIME_LORA_UPDATING=True
vllm serve <YOUR_MODEL> \
--host 0.0.0.0 --port 8000 \
--dtype bfloat16 \
--max-model-len 65536 \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.85 \
--enable-prefix-caching \
--enable-chunked-prefill \
--enable-lora \
--max-loras 2 \
--max-lora-rank 32 \
--enable-auto-tool-choice \
--tool-call-parser hermes # use 'qwen3_xml' for Qwen3.63b. Run GRPO training on the remaining GPUs:
conda activate trace
export CUDA_VISIBLE_DEVICES=4,5,6,7
export VLLM_BASE_URLS=http://localhost:8000
export VLLM_MODEL=<YOUR_MODEL>
export VLLM_ALLOW_RUNTIME_LORA_UPDATING=True
torchrun --nproc_per_node=<N_GPUS> --master-port=29501 -m train \
--game capability_<NAME> \
--model <YOUR_MODEL>Repeat for each capability. The output is one LoRA adapter per capability
(pipeline/adapters/<capability>.safetensors).
Once you have N capability-specific LoRAs, train the routing gate that picks the
right one at inference time. See moe_gate/README.md for the architecture and
configs/moe_gate.yaml for hyperparameters.
# SFT warm-start the gate on labeled (prompt, capability) pairs
python -m train.train_router_sft \
--config configs/moe_gate.yaml
# Optional: GRPO fine-tune the gate end-to-end against the synthetic envs
python -m train.grpo_gater \
--config configs/moe_gate.yamlAt inference, build_capability_model(...) from moe_gate/ loads the base
model + all capability LoRAs + the trained gate. The gate routes each forward
through the right adapter (or a soft mixture) per layer.
- Start from
prompts/general/. Copy both files toprompts/<your-benchmark>/and replace the env-agnostic descriptions with benchmark-specific ones (trajectory format, what "pass" means, scenario shape). Theprompts/swebench/folder is a worked example of doing this for a single-turn code-edit benchmark;prompts/tau-bench/does the same for multi-turn tool-use. - Compact your trajectories.
pipeline/_summarize.pyprovides a generic compaction; extend it for your benchmark's trajectory schema if needed. - Pick thresholds. Defaults (
ρ = 0.10,δ = 0.20,K = 8 of 10) come from the paper and work well in practice. Tune only if your benchmark has unusual structure (very few failures, very long trajectories, etc.). - Run. The rest of the pipeline (capability selection → environment generation → GRPO → routing) is benchmark-agnostic.
If you find this work helpful in your research, please consider citing our paper:
@misc{kang2026tracecapabilitytargetedagentictraining,
title={TRACE: Capability-Targeted Agentic Training},
author={Hangoo Kang and Tarun Suresh and Jon Saad-Falcon and Azalia Mirhoseini},
year={2026},
eprint={2604.05336},
archivePrefix={arXiv},
primaryClass={cs.AI},
url={https://arxiv.org/abs/2604.05336},
}