Competitive miner agents for the Radar network. Each agent uses the
code-injection model — submit .py files that run inside the official
agent image.
The default agent. A single autonomous loop where the LLM picks tools (research, sketch, validate, submit) until it ships a model. A strategy is a personality prompt — the validation loop, tools, and fallback chain are shared. One strategy is selected per round based on prior history and the current frontier.
Strategies (selected via strategies.select_strategy(challenge, state)):
| Strategy | When it's picked (default) | What it pushes the LLM to do |
|---|---|---|
reliable_baseline |
No frontier exists yet | Conservative bootstrap |
simple_modeler |
Default | Build a clean working model |
frontier_sniper |
Have prior code in bucket | Surgical micro-improvements |
ensemble_distiller |
Frontier ≥ 3 members | Cherry-pick best operational choice per role |
ablation_scientist |
≥5 entries in bucket | One controlled experiment per round |
training_optimizer |
Plateau in bucket | Freeze architecture, change training dynamics |
bucket_specialist |
Override only | Evolve the bucket's saved best |
pareto_hunter |
Override only | Attack the weakest scored objective |
bucket_specialist and pareto_hunter are opt-in via
state['strategy_override'] written into the scratchpad.
The selector is task-agnostic — it reads only the FLOPs bucket and the challenge's frontier/history. New tasks (vision, NLP, graph) work without strategy changes because each strategy reads task params and constraints from the challenge dict directly.
Generates a unified diff against the best frontier code instead of a fresh
model. Specialised sibling to autonomous for rounds where small surgical
patches dominate.
Each agent is a self-contained directory that can be passed directly to
--agent_dir:
agents/
autonomous/ ← default; pass this dir to --agent_dir
agent.py ← entry point: design_architecture(challenge, client)
tools.py ← tool definitions + handlers + build_tools(challenge)
core/
arch_knowledge.py # operation reference + frontier-gap analysis
fallback_templates.py # task-agnostic guaranteed-valid templates
flops_estimator.py # torch FlopCounterMode wrapper
history.py # scratchpad state, buckets, history entries
input_shape.py # task-agnostic input shape inference
output_shape.py # task-agnostic output shape inference
prompt_builder.py # task params / sizing / frontier formatting
trace.py # per-layer shape + FLOPs trace
validation.py # AST + FLOPs validation
strategies/
__init__.py # registry + select_strategy + build_strategy
reliable_baseline.py
simple_modeler.py
frontier_sniper.py
ensemble_distiller.py
ablation_scientist.py
training_optimizer.py
bucket_specialist.py
pareto_hunter.py
patch_decoder/
agent.py
core/...
tests/
...
python miner/neuron.py --agent_dir agents/autonomous/
# Frontier-patch sibling
python miner/neuron.py --agent_dir agents/patch_decoder/The harness volume-mounts the agent directory to /workspace/agent/ and calls
design_architecture(challenge, client) from agent.py.
Each agent must define in agent.py:
def design_architecture(challenge: dict, client: GatedClient) -> dict:
"""Returns {"code": str, "name": str, "motivation": str,
"prompt_id": str (optional)}"""prompt_id is the new optional field for prompt-evolution
attribution: when the miner runs python miner/neuron.py optimize [--optimizer gepa|random_mutate] --watch the optimizer writes
prompts/active.json with a population of prompt variants, and each
agent in this repo rotates through that population by round_id,
appending the active variant's template to its LLM system prompt
and returning the variant's id. The validator persists that id on
the experiment row so Phase C scores attribute back to the prompt
that produced them — closing the GEPA loop. Agents in this repo do
this transparently; if the optimizer hasn't been run the file is
absent and the agents fall back to their hardcoded prompts.
client—GatedClientis the only way to make HTTP requests:client.get(url) → bytesclient.get_json(url) → dictclient.post(url, data) → bytesclient.post_json(url, payload) → dictclient.put(url, data) → int
load_scratchpad(challenge)/save_scratchpad(challenge, scratch_dir)— injected into module namespace by the harness (don't define them, just call them)- No
main(),if __name__, stdin/stdout, Dockerfile, or requirements.txt - Available libraries: Python 3.11 stdlib + numpy + torch
| Key | Usage |
|---|---|
challenge["db_url"] |
client.get_json(f"{url}/experiments/recent") |
challenge["desearch_url"] |
client.post_json(f"{url}/search", {"query": "...", "count": 10, "date_filter": "PAST_2_YEARS"}) |
challenge["llm_url"] |
client.post_json(f"{url}/v1/chat/completions", {"model": "...", "messages": [...], "temperature": 0.7, "max_tokens": 4096}) |
challenge["llm_url"]/v1/models |
client.get_json(...) to list allowed models |
Understanding the end-to-end flow prevents the most common rejection: a missing
build_model() definition.
The validator calls launch_agent_pod() (in validator/collection.py) to spin
up a sandboxed container with the miner's agent directory mounted at
/workspace/agent/. It then calls run_agent_on_pod(), passing the challenge
as JSON.
The official harness (runner/agent/harness.py) inside the container:
- Imports the miner's
agent.pymodule. - Calls
agent_mod.design_architecture(challenge, client). - The agent function returns a dict:
{"code": str, "name": str, "motivation": str}. - The harness prints the result as JSON to stdout.
run_agent_on_pod() reads the JSON from stdout. If the result contains a
"code" key it wraps it in a Proposal, uploads the code to R2, and returns
the proposal.
Before any training or scoring, the validator runs
pre_validate_code(proposal.code). This step AST-parses the code string and
checks for top-level def build_model(...) and def build_optimizer(model).
If either is missing the proposal is rejected immediately.
build_model's positional argument names come from
challenge["task"]["task_params"].keys() and change between tasks — never
hardcode them.
The agent's tool loop has a strict contract:
- The LLM iteratively designs with
analyze_task→estimate_layer_flops/sketch_architecture→validate_code→submit. validate_coderuns the same AST + FLOPs + output-shape checks the validator does.- If the loop exhausts time or turns without a fully-validated submission,
the agent falls through to a guaranteed-valid template
(
core/fallback_templates.py) sized for the current bucket.
python -m pytest tests/ -v| Parameter | Value |
|---|---|
| Size buckets | 5 (tiny, small, medium-small, medium, large) |
| Sigmoid steepness | 20 (5% improvement → ~0.73 score) |
| Softmax temperature | 0.1 (winner takes almost all) |
| EMA alpha | 0.3 (30% new, 70% history) |
| Pareto dominance bonus | 1.5x multiplier |
| FLOPs target | 60% of bucket max |
| FLOPs tolerance | ±10% of bucket bounds |