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, rebased onto the restructured run
half (abstract Environment + SingleAgentEnv, one EnvConfig per env, agentic
judging, 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.
_RoleAgent.chat stamps roles/trainable at mint, so env-driven sessions land
role-stamped exactly like plain runs.
- 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) 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
@@ -400,7 +398,7 @@ uv run eval my-task-v1 --env.id agentic-judge --env.judge.harness.runtime.type d
400
398
exporting an `Environment` subclass via `__all__`, or a Hub `org/name[@version]` —
401
399
and its `EnvConfig` surface typed on the CLI (`--env.<role>.*`, `-h` renders them).
402
400
Empty (the default) keeps the taskset's own story: the env its package ships (a
403
-
*recipe* env like `code_golf_v1`, where the interaction is
401
+
*recipe* env like `code_golf_v1` or `kuhn_poker_v1`, where the interaction is
404
402
intrinsic to the data), else `SingleAgentEnv`. An explicit id wins over a
|`best-of-n`|`solver`|`--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. |
412
410
|`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 verdict spec is a **judge plugin** (`--env.spec.id score\|rubric\|reference`, the same registry and format as `env.taskset.task.judges`) — write your grading criteria once; the parsed verdict + per-criterion metrics land on the solver's trace exactly as the plugged tier records them. The judge's verdict 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 judge seat defaults to the taskset's default harness and 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, not on an agent. |
411
+
|`user-sim`|`assistant`, `user`| a modeled user (an untrainable `user` seat, `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. |
412
+
413
+
### User simulation: the user is just another agent
414
+
415
+
There is exactly one exchange mechanism: the chat session (`agents[...].chat(task)`)
416
+
— whoever calls `turn()` is the run's user, and each turn runs one harness segment
417
+
(the program runs until it yields, the caller answers its final message, the next
418
+
segment resumes the exchange with the answer). A prompt-less task is opened by the
419
+
first `turn(message)`; a prompted task speaks first (take its opening reply with a
420
+
bare `turn()`). Who computes the turns is the env's control flow, not framework
421
+
machinery:
422
+
423
+
```python
424
+
classSortEnv(vf.SingleAgentEnv):
425
+
asyncdefrollout(self, task, agents):
426
+
# a pre-scripted episode: the task is prompt-less, so the first turn opens
427
+
asyncwith agents["agent"].chat(task) as session:
428
+
for prompt in task.data.info["user_turns"]:
429
+
if (await session.turn(prompt)).stopped:
430
+
break# a limit or @stop ended the run
431
+
return {"agent": session.trace}
432
+
```
433
+
434
+
A *scripted* user is a plain loop like this (a game engine stepping in-process works
435
+
the same way — see the bundled `textarena` taskset). A *modeled* user is another agent
436
+
role: open both sessions and relay their `turn()`s into each other — see the bundled
437
+
`user-sim` env, `environments/kuhn_poker_v1` (the same relay as self-play: both seats
438
+
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
439
+
user runs in the eval process, so there is nothing to declare, place, or serve.
|`best-of-n`|`solver`|`--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. |
162
162
|`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 verdict spec is a **judge plugin** (`--env.spec.id score\|rubric\|reference`, the same registry and format as `env.taskset.task.judges`) — write your grading criteria once; the parsed verdict + per-criterion metrics land on the solver's trace exactly as the plugged tier records them. The judge's verdict 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 judge seat defaults to the taskset's default harness and 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, not on an agent. |
163
+
|`user-sim`|`assistant`, `user`| a modeled user (an untrainable `user` seat, `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. |
164
+
165
+
## User simulation: the user is just another agent
166
+
167
+
There is exactly one exchange mechanism: the chat session (`agents[...].chat(task)`)
168
+
— whoever calls `turn()` is the run's user, and each turn runs one harness segment
169
+
(the program runs until it yields, the caller answers its final message, the next
170
+
segment resumes the exchange with the answer). A prompt-less task is opened by the
171
+
first `turn(message)`; a prompted task speaks first (take its opening reply with a
172
+
bare `turn()`). Who computes the turns is the env's control flow, not framework
173
+
machinery:
174
+
175
+
```python
176
+
classSortEnv(vf.SingleAgentEnv):
177
+
asyncdefrollout(self, task, agents):
178
+
# a pre-scripted episode: the task is prompt-less, so the first turn opens
179
+
asyncwith agents["agent"].chat(task) as session:
180
+
for prompt in task.data.info["user_turns"]:
181
+
if (await session.turn(prompt)).stopped:
182
+
break# a limit or @stop ended the run
183
+
return {"agent": session.trace}
184
+
```
185
+
186
+
A *scripted* user is a plain loop like this (a game engine stepping in-process works
187
+
the same way — see the bundled `textarena` taskset). A *modeled* user is another agent
188
+
role: open both sessions and relay their `turn()`s into each other — see the bundled
189
+
`user-sim` env, `environments/kuhn_poker_v1` (the same relay as self-play: both seats
190
+
chat sessions of the run's own model), and [chat() in the Agent docs](agent.md). The
191
+
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