You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(v1): the exchange — chat()/turn(), segments, resume, one user mechanism
The turn half of the agent-programs work, restacked onto the synthesis run
half (make_agent + Agents, flat traces with EpisodeInfo stamps, verdict-file
judging, per-agent retries, the session seal).
- Agent.chat(task) -> ChatSession: the caller IS the run's user; one turn()
per harness segment (the program runs to its exit, the caller answers, the
next segment resumes the exchange with the answer). mask_prompt hides a
scenario prompt from the wire while the task still scores the real row.
_EpisodeAgent.chat stamps agent name/trainable + the shared EpisodeInfo at
mint, so env-driven sessions land in the episode exactly like plain runs.
An exchange is caller-driven, so per-agent retries never apply to chat().
- Harness.resume: relaunch on the accreted conversation by default
(SUPPORTS_MESSAGE_PROMPT); codex overrides it with a native continuation.
- The user loop moves OUT of the model boundary: vf.User/Task.user (session
injection, dialect extend, serve_user, UserError, the -U scaffold, the
placement matrix) is deleted — chat sessions in the env's rollout() are the
one exchange mechanism. The interception drive loop flattens to one turn
per request, keeping per-call records and the concluded-trace seal.
- textarena becomes a recipe env stepping the real engine host-side;
alphabet-sort/color-codeword script their users through SingleAgentEnv
sessions; openenv drives its client from rollout() (no more served user);
user-sim bundled env (two-session relay, the tau-style substrate, null
harness for the modeled user, untrainable via brief()) and kuhn_poker_v1
(self-play reference) added.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-`-T`, `--add-tool` — also scaffold a `vf.Toolset` tool server at `servers/tool.py`
30
30
- Use this to create custom tools which are installed into supported harnesses via MCP.
31
-
-`-U`, `--add-user` — also scaffold a `vf.User` simulator at `servers/user.py`
32
-
- Use this to simulate a user interacting with the model. Not all harnesses support user simulation.
33
31
-`-H`, `--add-harness` — also scaffold a custom `vf.Harness` at `harness.py`, selectable via `--env.agent.harness.id <name>`
34
32
- Prefer a built-in harness unless the model needs to run inside a custom program.
35
33
36
-
Most tasksets do not need specific tools, user simulations or custom harnesses.
34
+
Most tasksets do not need specific tools or custom harnesses. (To simulate a user interacting with the model, open a chat session from an env's `rollout()` and script the user's turns — see the [Agent docs](https://github.com/PrimeIntellect-ai/verifiers/blob/main/docs/v1/agent.md).)
37
35
38
36
> For a production-scale catalog of tasksets, see the companion [`research-environments`](https://github.com/PrimeIntellect-ai/research-environments) repository.
39
37
@@ -336,12 +334,41 @@ checked in as `configs/agentic_judge.toml` (`uv run eval @ configs/agentic_judge
336
334
exporting an `Environment` subclass via `__all__`, or a Hub `org/name[@version]` —
337
335
and its `EnvConfig` surface typed on the CLI (`--env.<agent>.*`, `-h` renders
338
336
them). Empty (the default) keeps the taskset's own story: the env its package
339
-
ships (a *recipe* env like `code_golf_v1`, where the interaction is intrinsic to
340
-
the data), else `SingleAgentEnv`. An explicit id wins over a bundled recipe env.
337
+
ships (a *recipe* env like `code_golf_v1` or `kuhn_poker_v1`, where the
338
+
interaction is intrinsic to the data), else `SingleAgentEnv`. An explicit id wins
339
+
over a bundled recipe env.
341
340
342
341
Bundled envs (`verifiers/v1/envs/`):
343
342
344
343
| id | agents | what it does |
345
344
| --- | --- | --- |
346
345
|`best-of-n`|`agent`|`--env.n` independent attempts per rollout; its metrics mark the argmax-reward sibling (`best`) and whether any reached `--env.threshold` (`pass_at_n`) — rejection sampling and pass@k. A single-agent env keeps the single-agent name, so `--env.agent.*` flags compose unchanged. |
347
346
|`agentic-judge`|`solver`, `judge`| agent-as-judge: the solver plays the task; a code-executing judge agent verifies the finished attempt with real execution, always in its own sandbox, never on the host. The judge's task mirrors the solver task's world (same image, a fresh box in its original state) with the graded transcript uploaded (`/tmp/transcript.md`/`.json`). The verdict channel is a file: the judge writes `{"score": 0-10, "reasoning": ...}` to `/tmp/verdict.json` in its box, scraped onto its trace while the box is alive and validated STRICTLY onto the solver's trace as the `judge` reward — a missing, malformed, or off-scale verdict fails the rollout instead of clamping. The judge must land in a container: pin `--env.judge.harness.runtime.type docker\|prime`, or construction refuses. A judgement that needs no execution belongs on the plugged tier (`env.taskset.task.judges`), not on an agent. |
347
+
|`user-sim`|`assistant`, `user`| a modeled user (an untrainable `user` agent — `brief()` — on the `null` harness by default) opens and drives the conversation from the task's prompt-as-scenario (`--env.persona`); the assistant plays the same task through a masked chat session (`mask_prompt`) — the prompt is hidden from its harness while the task's own rewards and judges still score the real row. The substrate for tau-bench-style evals. |
348
+
349
+
### User simulation: the user is just another agent
350
+
351
+
There is exactly one exchange mechanism: the chat session (`agents.<name>.chat(task)`)
352
+
— whoever calls `turn()` is the run's user, and each turn runs one harness segment
353
+
(the program runs until it yields, the caller answers its final message, the next
354
+
segment resumes the exchange with the answer). A prompt-less task is opened by the
355
+
first `turn(message)`; a prompted task speaks first (take its opening reply with a
356
+
bare `turn()`). Who computes the turns is the env's control flow, not framework
357
+
machinery:
358
+
359
+
```python
360
+
classSortEnv(vf.SingleAgentEnv):
361
+
asyncdefrollout(self, task, agents):
362
+
# a pre-scripted episode: the task is prompt-less, so the first turn opens
363
+
asyncwith agents.agent.chat(task) as session:
364
+
for prompt in task.data.info["user_turns"]:
365
+
if (await session.turn(prompt)).stopped:
366
+
break# a limit or @stop ended the run
367
+
```
368
+
369
+
A *scripted* user is a plain loop like this (a game engine stepping in-process works
370
+
the same way — see the bundled `textarena` taskset). A *modeled* user is another
371
+
agent: open both sessions and relay their `turn()`s into each other — see the bundled
372
+
`user-sim` env, `environments/kuhn_poker_v1` (the same relay as self-play: both seats
373
+
chat sessions of the run's own model), and [chat() in the Agent docs](https://github.com/PrimeIntellect-ai/verifiers/blob/main/docs/v1/agent.md). The
374
+
user runs in the eval process, so there is nothing to declare, place, or serve.
Copy file name to clipboardExpand all lines: docs/v1/environments.md
+31-2Lines changed: 31 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -85,12 +85,41 @@ checked in as `configs/agentic_judge.toml` (`uv run eval @ configs/agentic_judge
85
85
exporting an `Environment` subclass via `__all__`, or a Hub `org/name[@version]` —
86
86
and its `EnvConfig` surface typed on the CLI (`--env.<agent>.*`, `-h` renders
87
87
them). Empty (the default) keeps the taskset's own story: the env its package
88
-
ships (a *recipe* env like `code_golf_v1`, where the interaction is intrinsic to
89
-
the data), else `SingleAgentEnv`. An explicit id wins over a bundled recipe env.
88
+
ships (a *recipe* env like `code_golf_v1` or `kuhn_poker_v1`, where the
89
+
interaction is intrinsic to the data), else `SingleAgentEnv`. An explicit id wins
90
+
over a bundled recipe env.
90
91
91
92
Bundled envs (`verifiers/v1/envs/`):
92
93
93
94
| id | agents | what it does |
94
95
| --- | --- | --- |
95
96
|`best-of-n`|`agent`|`--env.n` independent attempts per rollout; its metrics mark the argmax-reward sibling (`best`) and whether any reached `--env.threshold` (`pass_at_n`) — rejection sampling and pass@k. A single-agent env keeps the single-agent name, so `--env.agent.*` flags compose unchanged. |
96
97
|`agentic-judge`|`solver`, `judge`| agent-as-judge: the solver plays the task; a code-executing judge agent verifies the finished attempt with real execution, always in its own sandbox, never on the host. The judge's task mirrors the solver task's world (same image, a fresh box in its original state) with the graded transcript uploaded (`/tmp/transcript.md`/`.json`). The verdict channel is a file: the judge writes `{"score": 0-10, "reasoning": ...}` to `/tmp/verdict.json` in its box, scraped onto its trace while the box is alive and validated STRICTLY onto the solver's trace as the `judge` reward — a missing, malformed, or off-scale verdict fails the rollout instead of clamping. The judge must land in a container: pin `--env.judge.harness.runtime.type docker\|prime`, or construction refuses. A judgement that needs no execution belongs on the plugged tier (`env.taskset.task.judges`), not on an agent. |
98
+
|`user-sim`|`assistant`, `user`| a modeled user (an untrainable `user` agent — `brief()` — on the `null` harness by default) opens and drives the conversation from the task's prompt-as-scenario (`--env.persona`); the assistant plays the same task through a masked chat session (`mask_prompt`) — the prompt is hidden from its harness while the task's own rewards and judges still score the real row. The substrate for tau-bench-style evals. |
99
+
100
+
## User simulation: the user is just another agent
101
+
102
+
There is exactly one exchange mechanism: the chat session (`agents.<name>.chat(task)`)
103
+
— whoever calls `turn()` is the run's user, and each turn runs one harness segment
104
+
(the program runs until it yields, the caller answers its final message, the next
105
+
segment resumes the exchange with the answer). A prompt-less task is opened by the
106
+
first `turn(message)`; a prompted task speaks first (take its opening reply with a
107
+
bare `turn()`). Who computes the turns is the env's control flow, not framework
108
+
machinery:
109
+
110
+
```python
111
+
classSortEnv(vf.SingleAgentEnv):
112
+
asyncdefrollout(self, task, agents):
113
+
# a pre-scripted episode: the task is prompt-less, so the first turn opens
114
+
asyncwith agents.agent.chat(task) as session:
115
+
for prompt in task.data.info["user_turns"]:
116
+
if (await session.turn(prompt)).stopped:
117
+
break# a limit or @stop ended the run
118
+
```
119
+
120
+
A *scripted* user is a plain loop like this (a game engine stepping in-process works
121
+
the same way — see the bundled `textarena` taskset). A *modeled* user is another
122
+
agent: open both sessions and relay their `turn()`s into each other — see the bundled
123
+
`user-sim` env, `environments/kuhn_poker_v1` (the same relay as self-play: both seats
124
+
chat sessions of the run's own model), and [chat() in the Agent docs](agent.md). The
125
+
user runs in the eval process, so there is nothing to declare, place, or serve.
-`-T`, `--add-tool` — also scaffold a `vf.Toolset` tool server at `servers/tool.py`
23
23
- Use this to create custom tools which are installed into supported harnesses via MCP.
24
-
-`-U`, `--add-user` — also scaffold a `vf.User` simulator at `servers/user.py`
25
-
- Use this to simulate a user interacting with the model. Not all harnesses support user simulation.
26
24
-`-H`, `--add-harness` — also scaffold a custom `vf.Harness` at `harness.py`, selectable via `--env.agent.harness.id <name>`
27
25
- Prefer a built-in harness unless the model needs to run inside a custom program.
28
26
29
-
Most tasksets do not need specific tools, user simulations or custom harnesses.
27
+
Most tasksets do not need specific tools or custom harnesses. (To simulate a user interacting with the model, open a chat session from an env's `rollout()` and script the user's turns — see the [Agent docs](agent.md).)
30
28
31
29
> For a production-scale catalog of tasksets, see the companion [`research-environments`](https://github.com/PrimeIntellect-ai/research-environments) repository.
0 commit comments