-p,--path <dir>— parent directory, default:./environments-T,--add-tool— also scaffold avf.Toolsettool server atservers/tool.py- Use this option to create custom tools which are installed into the harnesses via MCP. However, not all harnesses support external tools.
-U,--add-user— also scaffold avf.Usersimulator atservers/user.py- Use this when you need to simulate a user interacting with the main LLM. However, not all harnesses support user simulation.
-H,--add-harness— also scaffold a customvf.Harnessatharness.py, selectable via--harness.id <name>- In general, you should build your environments so that any of the built-in harnesses work. There are few reasons to build a custom harness.
For a production-scale catalog of benchmark environments, see the companion research-environments repository.
A minimal environment
@vf.metric to record non-scored values and @vf.group_reward for group rewards, which might be useful for training.
Making values configurable
If you want to make certain fields configurable for the user, subclassvf.TasksetConfig:
vf.TasksetConfig are load-time settings, such as splits (e.g., train/test), dataset names, etc.
You can also use vf.TaskConfig to configure task data, such as scoring parameters:
TaskData fields;
values uniform across the taskset live on the config — load-time ones directly on the
TasksetConfig, task-facing ones under task. A task can also be constructed directly —
AdditionTask(data, config=AdditionTaskConfig(...)) — and an omitted config defaults to
the declared type’s defaults, so a standalone task works out of the box. Overriding
from_trace(trace) (not implemented by default) opts a task into being derived from a
finished rollout’s bare Trace — how a multi-agent step spawns a follow-up task. Only the data rides the wire:
trace.task.data is the TaskData (with trace.task.type recording the producing Task
class’s name), and behavior re-attaches by constructing the task class around it.
Adding Tools
Some environments require custom tools, which are bundled as avf.Toolset (similar to how a vf.Taskset bundles vf.Task).
Tools are exposed as MCP servers to the given harness and thus need a harness which exposes MCP support (via SUPPORTS_MCP).
You can create them like this (remember the bootstrapping with uv run init MY_ENV -T):
Using Judges
If your reward is semantic, use an LLM judge.taskset.task.judge.model in your config (it is a string).