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
@@ -413,12 +411,41 @@ checked in as `configs/agentic_judge.toml` (`uv run eval @ configs/agentic_judge
413
411
exporting an `Environment` subclass via `__all__`, or a Hub `org/name[@version]` —
414
412
and its `EnvConfig` surface typed on the CLI (`--env.<agent>.*`, `-h` renders
415
413
them). Empty (the default) keeps the taskset's own story: the env its package
416
-
ships (a *recipe* env like `code_golf_v1`, where the interaction is intrinsic to
417
-
the data), else `SingleAgentEnv`. An explicit id wins over a bundled recipe env.
414
+
ships (a *recipe* env like `code_golf_v1` or `kuhn_poker_v1`, where the
415
+
interaction is intrinsic to the data), else `SingleAgentEnv`. An explicit id wins
416
+
over a bundled recipe env.
418
417
419
418
Bundled envs (`verifiers/v1/envs/`):
420
419
421
420
| id | agents | what it does |
422
421
| --- | --- | --- |
423
422
|`best-of-n`|`agent`|`--env.n` independent attempts per rollout; `score()` marks 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. |
424
423
|`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. |
424
+
|`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. |
425
+
426
+
### User simulation: the user is just another agent
427
+
428
+
There is exactly one exchange mechanism: the chat session (`agents.<name>.chat(task)`)
429
+
— whoever calls `turn()` is the run's user, and each turn runs one harness segment
430
+
(the program runs until it yields, the caller answers its final message, the next
431
+
segment resumes the exchange with the answer). A prompt-less task is opened by the
432
+
first `turn(message)`; a prompted task speaks first (take its opening reply with a
433
+
bare `turn()`). Who computes the turns is the env's control flow, not framework
434
+
machinery:
435
+
436
+
```python
437
+
classSortEnv(vf.SingleAgentEnv):
438
+
asyncdefrollout(self, task, agents):
439
+
# a pre-scripted episode: the task is prompt-less, so the first turn opens
440
+
asyncwith agents.agent.chat(task) as session:
441
+
for prompt in task.data.info["user_turns"]:
442
+
if (await session.turn(prompt)).stopped:
443
+
break# a limit or @stop ended the run
444
+
```
445
+
446
+
A *scripted* user is a plain loop like this (a game engine stepping in-process works
447
+
the same way — see the bundled `textarena` taskset). A *modeled* user is another
448
+
agent: open both sessions and relay their `turn()`s into each other — see the bundled
449
+
`user-sim` env, `environments/kuhn_poker_v1` (the same relay as self-play: both seats
450
+
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
451
+
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
@@ -163,12 +163,41 @@ checked in as `configs/agentic_judge.toml` (`uv run eval @ configs/agentic_judge
163
163
exporting an `Environment` subclass via `__all__`, or a Hub `org/name[@version]` —
164
164
and its `EnvConfig` surface typed on the CLI (`--env.<agent>.*`, `-h` renders
165
165
them). Empty (the default) keeps the taskset's own story: the env its package
166
-
ships (a *recipe* env like `code_golf_v1`, where the interaction is intrinsic to
167
-
the data), else `SingleAgentEnv`. An explicit id wins over a bundled recipe env.
166
+
ships (a *recipe* env like `code_golf_v1` or `kuhn_poker_v1`, where the
167
+
interaction is intrinsic to the data), else `SingleAgentEnv`. An explicit id wins
168
+
over a bundled recipe env.
168
169
169
170
Bundled envs (`verifiers/v1/envs/`):
170
171
171
172
| id | agents | what it does |
172
173
| --- | --- | --- |
173
174
|`best-of-n`|`agent`|`--env.n` independent attempts per rollout; `score()` marks 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. |
174
175
|`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. |
176
+
|`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. |
177
+
178
+
## User simulation: the user is just another agent
179
+
180
+
There is exactly one exchange mechanism: the chat session (`agents.<name>.chat(task)`)
181
+
— whoever calls `turn()` is the run's user, and each turn runs one harness segment
182
+
(the program runs until it yields, the caller answers its final message, the next
183
+
segment resumes the exchange with the answer). A prompt-less task is opened by the
184
+
first `turn(message)`; a prompted task speaks first (take its opening reply with a
185
+
bare `turn()`). Who computes the turns is the env's control flow, not framework
186
+
machinery:
187
+
188
+
```python
189
+
classSortEnv(vf.SingleAgentEnv):
190
+
asyncdefrollout(self, task, agents):
191
+
# a pre-scripted episode: the task is prompt-less, so the first turn opens
192
+
asyncwith agents.agent.chat(task) as session:
193
+
for prompt in task.data.info["user_turns"]:
194
+
if (await session.turn(prompt)).stopped:
195
+
break# a limit or @stop ended the run
196
+
```
197
+
198
+
A *scripted* user is a plain loop like this (a game engine stepping in-process works
199
+
the same way — see the bundled `textarena` taskset). A *modeled* user is another
200
+
agent: open both sessions and relay their `turn()`s into each other — see the bundled
201
+
`user-sim` env, `environments/kuhn_poker_v1` (the same relay as self-play: both seats
202
+
chat sessions of the run's own model), and [chat() in the Agent docs](agent.md). The
203
+
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