Skip to content

Loading search…

Models, experiments and deploys

The files under configs/ besides the agent.

Model roles: configs/models/<name>.toml; --model NAME picks one.

File What it is
deepseek-flash DeepSeek V4.1 Flash writes programs, answers questions about images and points; Molmo2 for a pointer role
grok-low grok-4.7 (low) writes programs, answers questions about images and points; Molmo2 for a pointer role
grok-medium grok-4.7 (medium) writes programs, answers questions about images and points; Molmo2 for a pointer role
opus-5.5 Claude Opus 5.5 writes programs, answers questions about images and points; Molmo2 for a pointer role
qwen3-vl-32b Qwen3-VL-32B (self-hosted, 4 bit) writes programs, answers questions and points; Molmo2 for a pointer role
qwen3.5-397b Qwen3.5-397B-A17B (open weights) writes programs and answers questions about images; Molmo2 points
configs/models/deepseek-flash.toml
# DeepSeek V4.1 Flash writes the programs: the development model. It runs at medium
# reasoning effort for a few cents per RoboDojo episode (R4 on the 84 official pack-0
# cases: 6.4 calls and $0.02 per episode), and OpenRouter serves it from our region.
# The paper's main runs name their model in the experiment (opus-5.5).
description = "DeepSeek V4.1 Flash writes programs, answers questions about images and points; Molmo2 for a pointer role"
include = ["grok-medium.toml"]
[main]
model = "deepseek/deepseek-v4.1-flash"
# The same model answers questions about images inside programs (see grok-medium.toml).
[vlm]
model = "deepseek/deepseek-v4.1-flash"
reasoning_effort = "medium"
configs/models/grok-low.toml
# grok-4.7 at low reasoning effort, for development. At medium effort xAI streams no
# reasoning, and OpenRouter cuts a request after about 5 minutes with nothing received:
# long replies are lost. Low effort answers in 13-60 s (about $0.11 per RoboDojo episode).
description = "grok-4.7 (low) writes programs, answers questions about images and points; Molmo2 for a pointer role"
include = ["grok-medium.toml"]
[main]
reasoning_effort = "low"
configs/models/grok-medium.toml
# Hosted models through OpenRouter (the key from OPENROUTER_API_KEY; never write it
# here). OpenAI, Anthropic and Google models refuse requests from our region (HTTP
# 403), so development runs use x-ai/grok-4.7. Zhang et al. (2026b) run their models at
# medium reasoning effort.
description = "grok-4.7 (medium) writes programs, answers questions about images and points; Molmo2 for a pointer role"
[main]
backend = "openrouter"
model = "x-ai/grok-4.7"
reasoning_effort = "medium"
# On OpenRouter the limit covers reasoning tokens too.
max_output_tokens = 16000
# Streamed: keep-alive chunks arrive while the model thinks, so a dead connection
# shows up within the read timeout (the link to OpenRouter drops now and then).
stream = true
timeout_seconds = 120
# Completions have no side effects; retry dropped connections and rate limits for up to
# 30 minutes (the simulator waits while the model is called).
retries = 3
retry_seconds = 1800
# Questions about images inside programs (query_vlm, holding() without a measured
# gripper, pointing when there is no pointer) go to the main model itself: one model
# sees the task and the images, as in Z2_v01, instead of a second, weaker model
# answering for it (1.1.0 asked qwen3.8-flash, whose answers about what an object is
# changed from one question to the next).
[vlm]
backend = "openrouter"
model = "x-ai/grok-4.7"
reasoning_effort = "low"
stream = true
timeout_seconds = 120
# The link drops now and then; give up after about 5 minutes so the program sees an
# error instead of the kernel being killed.
retries = 3
retry_seconds = 300
# Open-vocabulary pointing (point_to) with Molmo2 (Clark et al., 2026), self-hosted, for
# agents that take the pointer role; the default agent's vlm role points instead.
[pointer]
service = "molmo2"
[runtime]
# Images of the two most recent turns, as in Zhang et al. (2026b); text history is kept.
keep_images = 2
configs/models/opus-5.5.toml
# The paper's main model (Sec. 5): Claude Opus 5.5 through OpenRouter. It refuses
# requests from our region (HTTP 403): run it from a machine in a supported region.
description = "Claude Opus 5.5 writes programs, answers questions about images and points; Molmo2 for a pointer role"
include = ["grok-medium.toml"]
[main]
model = "anthropic/claude-opus-5.5"
# The same model answers questions about images inside programs (see grok-medium.toml).
[vlm]
model = "anthropic/claude-opus-5.5"
reasoning_effort = "low"
configs/models/qwen3-vl-32b.toml
# Self-hosted development model, no API spend: Qwen3-VL-32B-Instruct (4 bit, the
# qwen3-vl-32b service) writes the programs, answers questions and points; Molmo2 for a pointer role.
description = "Qwen3-VL-32B (self-hosted, 4 bit) writes programs, answers questions and points; Molmo2 for a pointer role"
[main]
service = "qwen3-vl-32b"
max_output_tokens = 2048
# Sampling breaks the exact-repeat loops a small model falls into at temperature 0.
temperature = 0.6
timeout_seconds = 600
[vlm]
service = "qwen3-vl-32b"
timeout_seconds = 300
[pointer]
service = "molmo2"
[runtime]
keep_images = 1
# The 32B model in 4 bit on one 24 GB GPU runs out of memory beyond about 7k prompt
# tokens; with images downscaled by the server three turns fit.
max_history_turns = 3
# The task descriptions do not fit its context either.
task_descriptions = false
configs/models/qwen3.5-397b.toml
# Qwen3.5-397B-A17B, open weights, writes programs and answers questions about images:
# the open-weight model of the paper's comparison of language models.
description = "Qwen3.5-397B-A17B (open weights) writes programs and answers questions about images; Molmo2 points"
include = ["grok-medium.toml"]
[main]
model = "qwen/qwen3.5-397b-a17b"
[vlm]
model = "qwen/qwen3.5-397b-a17b"
reasoning_effort = "medium"

The agent with a model on a bench: configs/experiments/<name>.toml.

File What it is
catch-ab reactive layer A/B: a scripted catch on the conveyor, official layouts 0-19 of pack 0
dev the agent, one task, development layouts
eval the agent on official evaluation layouts
paper-stable the paper’s comparison subset: 8 tasks x official layouts 0-3 of pack 0, the submitted configuration
paper-subset the paper’s development subset: 8 tasks x 2 development layouts, the submitted configuration
policy-alone-dev a learned policy alone through run_policy (the agent’s, Simate, unless set), every task, two development layouts
robotwin-dev the agent on RoboTwin, development seeds
robotwin-eval the agent on RoboTwin 2.0, 50 tasks x 5 randomized evaluation episodes
rsi-stable self-improvement: the agent’s own-tool tasks, frozen evaluation on layouts 0-19 of pack 1
subset8 the agent on the 8-task development subset
configs/experiments/catch-ab.toml
# The reactive layer A/B (paper Tab. reactive (b)): one scripted program, no model calls,
# catches an object moving past on the conveyor of match_and_pick_from_conveyor, on
# official layouts 0-19 of pack 0. Both arms wait until an object comes within the left
# arm's straight-down reach, then differ only in how they close on it:
# intercept: the reactive layer follows the object every control tick (skills/reflexes)
# harness run configs/experiments/catch-ab.toml --agent configs/ablations/reflexes.toml \
# --script configs/scripts/catch_intercept.json --output runs/catch/intercept
# reach: the same approach (8 cm above, then down, close, lift) to where it was seen
# harness run configs/experiments/catch-ab.toml --agent configs/ablations/reflexes.toml \
# --script configs/scripts/catch_reach.json --output runs/catch/reach
# A catch is the jaws still holding one second after the lift (stdout "CATCH True").
description = "reactive layer A/B: a scripted catch on the conveyor, official layouts 0-19 of pack 0"
include = ["eval.toml"]
tasks = ["match_and_pick_from_conveyor"]
seeds = "0-19"
configs/experiments/dev.toml
# Development: the agent with its default model on one task and development layouts
# (never evaluation layouts). `harness run configs/experiments/dev.toml
# --deploy <machine>` starts the simulator workers itself; `harness program` uses the
# worker at `endpoint`.
description = "the agent, one task, development layouts"
bench = "robodojo"
tasks = ["general_pickup"]
seeds = [0]
layouts = "procedural"
endpoint = "http://127.0.0.1:8771"
[budget]
max_wall_seconds = 5400
# Cost guard for development checks; the paper runs set no cost cap.
max_cost_usd = 4.0
configs/experiments/eval.toml
# The agent on RoboDojo's official evaluation layouts (pack 0), all tasks, two layouts
# each (raise seeds to "0-49" for the full protocol). Evaluation resolves strictly: a
# skill or service the agent names that cannot run on this machine stops the run.
description = "the agent on official evaluation layouts"
bench = "robodojo"
tasks = "all"
seeds = "0-1"
layouts = "eval"
layout_pack_seed = 0
[budget]
max_wall_seconds = 5400
configs/experiments/paper-stable.toml
# The paper's comparison subset (Sec. Experiments: any model, any policy, the ladder): 8
# tasks on official evaluation layouts 0-3 of pack 0, with the agent as it was submitted
# to the leaderboard (no pointer, no world model). The tasks were chosen from the full
# local evaluation of agent 1.6.3 (pack 0, 25-50 layouts each) so that a comparison can
# show a difference: a base success rate between 15 and 75 %, the same outcome when a
# layout is run again, and both kinds of task: three the agent does with its own tools
# (general_pickup, solve_equation, arrange_largest_number) and five it hands to the
# learned policy. One run per configuration:
# harness run configs/experiments/paper-stable.toml --output runs/paper-stable/base
# ... --model grok-low | --model qwen3.5-397b (language models)
# ... --set 'agent.services.policies=["g05"]' (learned policies)
# ... --agent configs/ablations/r1.toml | r2.toml (the ladder)
description = "the paper's comparison subset: 8 tasks x official layouts 0-3 of pack 0, the submitted configuration"
include = ["eval.toml"]
tasks = ["general_pickup", "solve_equation", "arrange_largest_number", "match_and_pick_from_conveyor",
"pour_balls_into_vase", "pour_liquid_into_cup", "sort_nesting_dolls_by_size", "organize_table"]
seeds = "0-3"
[overrides.agent]
roles = ["main", "vlm", "verifier"]
services = { world_models = [] }
configs/experiments/paper-subset.toml
# The paper's development subset (Sec. Experiments: any model, the ladder, the reactive
# layer): the 8 tasks of subset8.toml on two development layouts each, with the agent as
# it was submitted to the leaderboard (no pointer, no world model). One run per
# configuration:
# harness run configs/experiments/paper-subset.toml --output runs/paper/base
# ... --model grok-low | --model qwen3.5-397b (language models)
# ... --set 'agent.services.policies=["g05"]' (learned policies)
# ... --agent configs/ablations/r1.toml | r2.toml (the ladder)
description = "the paper's development subset: 8 tasks x 2 development layouts, the submitted configuration"
include = ["subset8.toml"]
seeds = "0-1"
[overrides.agent]
roles = ["main", "vlm", "verifier"]
services = { world_models = [] }
configs/experiments/policy-alone-dev.toml
# A learned policy alone on development layouts: one scripted program hands it the
# whole task from the start pose and lets it run to the end of the episode (run_policy
# as RoboDojo's evaluation client runs it; no model calls). scripts/policy_card.py sorts
# its per-task scores into the kinds its description in services/<name>.toml names:
# harness run configs/experiments/policy-alone-dev.toml --script configs/scripts/policy_alone.json --output runs/simate-alone-dev
# harness run configs/experiments/policy-alone-dev.toml --set 'agent.services.policies=["g05"]' --script configs/scripts/policy_alone.json --output runs/g05-alone-dev
# python scripts/policy_card.py runs/simate-alone-dev
description = "a learned policy alone through run_policy (the agent's, Simate, unless set), every task, two development layouts"
bench = "robodojo"
tasks = "all"
seeds = "0-1"
layouts = "procedural"
[budget]
max_wall_seconds = 5400
[overrides.agent]
services = { world_models = [] }
# The script hands the policy the task itself: no start step.
runtime = { strategy_first = false }
configs/experiments/robotwin-dev.toml
# RoboTwin 2.0 development check on stack_blocks_two: the agent with the RoboTwin prompt
# pack (AgileX ALOHA). No learned skill for this embodiment (no policy or world model
# trained on RoboTwin is served), and RoboTwin has no task descriptions.
description = "the agent on RoboTwin, development seeds"
bench = "robotwin"
tasks = ["stack_blocks_two"]
seeds = [0]
layouts = "procedural"
endpoint = "http://127.0.0.1:8772"
[budget]
max_wall_seconds = 5400
max_cost_usd = 4.0
[overrides.agent]
prompt = "../../prompts/robotwin"
# The tools by object id (skills/eef) are tuned to RoboDojo's ARX X5 (table height,
# parking spots, jaw width); RoboTwin's ALOHA uses the manipulation skill's.
skills = { layers = { learned = false }, enable = ["manipulation"], disable = ["eef"] }
services = { policies = [], world_models = [] }
# RoboTwin has no XPolicyLab embodiment profile here: the worker shows programs its own
# observations (the default agent's contract is RoboDojo's leaderboard protocol).
runtime = { task_descriptions = false, contract = "native" }
configs/experiments/robotwin-eval.toml
# RoboTwin 2.0 evaluation as Harness VLA (Zhang et al. 2026a, Tab. 6) reports it: the 50
# tasks, five randomized episodes each (demo_randomized, the official evaluation seeds of
# group 0). The agent as in robotwin-dev.toml: the RoboTwin prompt pack, the manipulation
# skill, no learned skill (none is served for this embodiment), the robot's own
# observations.
description = "the agent on RoboTwin 2.0, 50 tasks x 5 randomized evaluation episodes"
include = ["robotwin-dev.toml"]
tasks = "all"
seeds = "0-4"
layouts = "eval"
layout_pack_seed = 0
configs/experiments/rsi-stable.toml
# Learning from past episodes (paper Sec. 3.3, Tab. RSI) on the three tasks the agent does
# with its own tools, where an entry of its library can change what its programs do
# (general_pickup 68, solve_equation 18, arrange_largest_number 22 in the full local
# evaluation of agent 1.6.3). The library grows on official layouts of pack 0 (explore
# 0-3, validate 4-9) and is evaluated frozen on layouts 0-19 of pack 1, which it never saw
# (pack 0 has only 25 layouts of arrange_largest_number). The agent as submitted.
# python scripts/self_improve.py --experiment configs/experiments/rsi-stable.toml --set layout_pack_seed=0 \
# --tasks general_pickup --explore-seeds 0-3 --validate-seeds 4-9 --min-gain 0.3 \
# --library runs/rsi/library-general_pickup --output runs/rsi/learn-general_pickup
# harness run configs/experiments/rsi-stable.toml --output runs/rsi/eval-none
# harness run configs/experiments/rsi-stable.toml --set 'agent.runtime.library.path="runs/rsi/library"' \
# --output runs/rsi/eval-library
description = "self-improvement: the agent's own-tool tasks, frozen evaluation on layouts 0-19 of pack 1"
include = ["eval.toml"]
tasks = ["general_pickup", "solve_equation", "arrange_largest_number"]
seeds = "0-19"
layout_pack_seed = 1
[overrides.agent]
roles = ["main", "vlm", "verifier"]
services = { world_models = [] }
configs/experiments/subset8.toml
# The 8-task development subset (both conveyor tasks included) that ablations, model
# comparisons and self-improvement share (App. A); 10 episodes each on development
# layouts. An ablation runs it with --agent configs/ablations/<name>.toml.
description = "the agent on the 8-task development subset"
include = ["dev.toml"]
tasks = ["general_pickup", "stack_blocks", "stack_bowls", "classify_objects", "deposit_coin", "insert_tubes",
"match_and_pick_from_conveyor", "pick_from_conveyor_by_image"]
seeds = "0-9"
[budget]
max_total_cost_usd = 20.0

Where services run on one machine: configs/deploy/<machine>.toml.

File What it is
example-1gpu A machine with one 24 GB GPU. The simulator (6-9 GB) leaves room for one model at a time: SAM 3 (7.2 GB) and Simate (14 GB) are on demand in one swap group, so the broker loads the one a skill calls and swaps when the other is needed. The simulator waits while a model loads, so this costs wall time, not episode steps, and the identity is that of a machine where both stay resident. One thing differs: Simate keeps a memory of past frames, and swapped out it starts that memory afresh, so a policy run after a SAM 3 call in the same episode does not see the frames before it (docs/src/content/docs/guides/small-machine.md). Copy to configs/deploy/<your machine>.toml and set the paths.
example-2gpu A machine with two 24 GB GPUs: the agent’s models stay resident on GPU 1 (SAM 3 7.2 GB and Simate 14 GB; started once with harness up), the simulator workers run on GPU 0. Copy to configs/deploy/<your machine>.toml (git-ignored) and set the paths.
example-4gpu A machine with four 24 GB GPUs: the agent’s models resident on GPU 0 (SAM 3 and Simate), Molmo2 and OpenWAM on demand in one swap group on GPU 1 for experiments that turn them on (a pointer role, --set 'agent.services.world_models=["openwam"]'), and the simulator workers on the other two, two per GPU. Copy to configs/deploy/<your machine>.toml (git-ignored) and set the paths and GPUs.
example-api No GPU for models: only the simulator runs here, every model is hosted (the main and vlm roles). Without a segmenter, segmentation and the tools built on it (locate, pick, release, …) drop out of runs on this machine, and without a policy server the learned skills: a smaller configuration with another identity.
configs/deploy/example-1gpu.toml
# A machine with one 24 GB GPU. The simulator (6-9 GB) leaves room for one model at a
# time: SAM 3 (7.2 GB) and Simate (14 GB) are on demand in one swap group, so the broker
# loads the one a skill calls and swaps when the other is needed. The simulator waits
# while a model loads, so this costs wall time, not episode steps, and the identity is
# that of a machine where both stay resident. One thing differs: Simate keeps a memory of
# past frames, and swapped out it starts that memory afresh, so a policy run after a SAM 3
# call in the same episode does not see the frames before it (docs/src/content/docs/guides/small-machine.md).
# Copy to configs/deploy/<your machine>.toml and set the paths.
weights = "~/harness-weights"
state = "~/.cache/harness-policy/services"
gpus = [0]
gpu_memory_gb = 24
[sim]
gpus = [0]
workers_per_gpu = 1
[services.sam3]
gpu = 0
mode = "on_demand"
swap = "models"
[services.simate]
gpu = 0
mode = "on_demand"
swap = "models"
configs/deploy/example-2gpu.toml
# A machine with two 24 GB GPUs: the agent's models stay resident on GPU 1 (SAM 3 7.2 GB
# and Simate 14 GB; started once with `harness up`), the simulator workers run on GPU 0.
# Copy to configs/deploy/<your machine>.toml (git-ignored) and set the paths.
weights = "~/harness-weights"
state = "~/.cache/harness-policy/services"
gpus = [0, 1]
gpu_memory_gb = 24
[sim]
gpus = [0]
workers_per_gpu = 2
[services.sam3]
gpu = 1
[services.simate]
gpu = 1
configs/deploy/example-4gpu.toml
# A machine with four 24 GB GPUs: the agent's models resident on GPU 0 (SAM 3 and
# Simate), Molmo2 and OpenWAM on demand in one swap group on GPU 1 for experiments that
# turn them on (a pointer role, `--set 'agent.services.world_models=["openwam"]'`), and
# the simulator workers on the other two, two per GPU.
# Copy to configs/deploy/<your machine>.toml (git-ignored) and set the paths and GPUs.
weights = "~/harness-weights"
state = "~/.cache/harness-policy/services"
gpus = [0, 1, 2, 3]
gpu_memory_gb = 24
# Simulator workers of `harness run`: two RoboDojo workers per GPU (6-9 GB each).
[sim]
gpus = [2, 3]
workers_per_gpu = 2
base_port = 8771
[services.sam3]
gpu = 0
[services.simate]
gpu = 0
[services.molmo2]
gpu = 1
mode = "on_demand"
swap = "experiments"
[services.openwam]
gpu = 1
mode = "on_demand"
swap = "experiments"
configs/deploy/example-api.toml
# No GPU for models: only the simulator runs here, every model is hosted (the main and
# vlm roles). Without a segmenter, segmentation and the tools built on it (locate, pick,
# release, ...) drop out of runs on this machine, and without a policy server the
# learned skills: a smaller configuration with another identity.
weights = "~/harness-weights"
gpus = [0]
[sim]
gpus = [0]
workers_per_gpu = 1

Options for harness serve --options (a worker started by hand).

File What it is
robodojo_runtime Keyword arguments for benches.robodojo.runtime:create_environment. Pass with: scripts/harness serve –factory benches.robodojo.runtime:create_environment \ –options configs/workers/robodojo_runtime.toml One worker serves one task. Copy this file under configs/local/ for other tasks.
robodojo_runtime_dev Development worker: replays procedurally generated layouts that are disjoint from the official eval packs (see benches/robodojo/devlayouts.py). scripts/harness serve –factory benches.robodojo.runtime:create_environment \ –options configs/workers/robodojo_runtime_dev.toml reset(task, seed) replays <dev_layout_dir>/<task>_<seed>.json and generates it on first use; pre-generate with scripts/robodojo_make_dev_layouts.py. dev_layout_dir defaults to <RoboDojo>/harness_dev_layouts/arx_x5; set it to keep layouts elsewhere.
robotwin_runtime RoboTwin 2.0 evaluation worker: the official clean-to-randomized protocol (demo_randomized scenes, unseen instructions, expert-checked seeds from 100000 * (1 + seed_group)). See configs/workers/robotwin_runtime_dev.toml.
robotwin_runtime_dev RoboTwin 2.0 development worker (benches/robotwin/runtime.py). Development seeds start at 10**7, disjoint from every evaluation seed; a seed counts only if RoboTwin’s expert solves it, and the valid ones are cached under seed_cache_dir (default <RoboTwin>/harness_seeds). PYTHONPATH=third_party/curobo-0.7.8/src scripts/harness serve \ –factory benches.robotwin.runtime:create_environment \ –options configs/workers/robotwin_runtime_dev.toml –port 8772
configs/workers/robodojo_runtime.toml
# Keyword arguments for benches.robodojo.runtime:create_environment.
# Pass with: scripts/harness serve --factory benches.robodojo.runtime:create_environment \
# --options configs/workers/robodojo_runtime.toml
# One worker serves one task. Copy this file under configs/local/ for other tasks.
task = "stack_bowls"
layout_pack_seed = 0
# Per-tick three-camera clips cost seconds per step; enable only for debugging.
record_video = false
[shield]
# Commands that are bugs are rejected (see benches/robodojo/runtime.py).
max_joint_jump_rad = 0.3
max_cartesian_jump_m = 0.25
workspace = { x = [-1.1, 1.1], y = [-1.25, 0.35], z = [0.70, 1.565] }
configs/workers/robodojo_runtime_dev.toml
# Development worker: replays procedurally generated layouts that are disjoint
# from the official eval packs (see benches/robodojo/devlayouts.py).
# scripts/harness serve --factory benches.robodojo.runtime:create_environment \
# --options configs/workers/robodojo_runtime_dev.toml
# reset(task, seed) replays <dev_layout_dir>/<task>_<seed>.json and generates it
# on first use; pre-generate with scripts/robodojo_make_dev_layouts.py. dev_layout_dir
# defaults to <RoboDojo>/harness_dev_layouts/arx_x5; set it to keep layouts elsewhere.
task = "stack_bowls"
layout_source = "procedural"
record_video = false
[shield]
# Commands that are bugs are rejected (see benches/robodojo/runtime.py).
max_joint_jump_rad = 0.3
max_cartesian_jump_m = 0.25
workspace = { x = [-1.1, 1.1], y = [-1.25, 0.35], z = [0.70, 1.565] }
configs/workers/robotwin_runtime.toml
# RoboTwin 2.0 evaluation worker: the official clean-to-randomized protocol
# (demo_randomized scenes, unseen instructions, expert-checked seeds from
# 100000 * (1 + seed_group)). See configs/workers/robotwin_runtime_dev.toml.
task = "stack_blocks_two"
task_config = "demo_randomized"
split = "eval"
seed_group = 0
configs/workers/robotwin_runtime_dev.toml
# RoboTwin 2.0 development worker (benches/robotwin/runtime.py). Development
# seeds start at 10**7, disjoint from every evaluation seed; a seed counts only if
# RoboTwin's expert solves it, and the valid ones are cached under seed_cache_dir
# (default <RoboTwin>/harness_seeds).
# PYTHONPATH=third_party/curobo-0.7.8/src scripts/harness serve \
# --factory benches.robotwin.runtime:create_environment \
# --options configs/workers/robotwin_runtime_dev.toml --port 8772
task = "stack_blocks_two"
task_config = "demo_randomized"
split = "dev"
seed_group = 0