8000 Run every method on Pi (evolver.base: pi) by iamziyuzhao · Pull Request #92 · Infini-AI-Lab/hermit · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
b9686a7
mwm and RRSI: refuse a learner backend other than Codex or Claude Cod…
iamziyuzhao Nov 9, 2026
b35d2ea
mwm and RRSI backend check: test it from the config and across the al…
iamziyuzhao Nov 10, 2026
9c64073
Merge main into fix/mwm-rrsi-backend-check
iamziyuzhao Nov 10, 2026
1bea09e
Run every method on Pi: evolver.base: pi for all Evolvers 8000
iamziyuzhao Nov 10, 2026
abb0350
Backends can give Pi a longer reply: max_output_tokens
iamziyuzhao Nov 10, 2026
03ed729
Merge main (OpenCode, the AHE texts' stream names) into the Pi branch
iamziyuzhao Nov 10, 2026
47791e9
Pi on every method: fixes from the post-PR audit of 03ed729
iamziyuzhao Nov 10, 2026
1de2b35
Merge main (deploy retires skills, agent skills) into the Pi branch
iamziyuzhao Nov 10, 2026
45acd88
Pi: a bash command the model gives no time limit stops after command_…
iamziyuzhao Nov 10, 2026
8b67768
Test mwm on Pi with a model port a non-root sandbox can forward
iamziyuzhao Nov 10, 2026
9115611
Pi: a session's effort is what the model is sent; Pi's settings reach…
iamziyuzhao Nov 10, 2026
ac43960
Merge main (the sandbox runs a granted venv's python by its own name)…
iamziyuzhao Nov 10, 2026
9b8131b
Test that a Responses API backend's extra_body stays out of Pi's samp…
iamziyuzhao Nov 10, 2026
25833aa
Pi: the last request's status decides an outage, in each of Pi's erro…
iamziyuzhao Nov 10, 2026
e5490a4
Pi: an answer the provider declined is a refusal; the error tests rep…
iamziyuzhao Nov 11, 2026
677b36c
EvoSafe on Pi: a failed session is no answer, whatever its text
iamziyuzhao Nov 11, 2026
5d2adb4
Merge main (#90 DeepSeek Harness) into PR 92
haizhongzheng Nov 11, 2026
e3df125
Review fixes: Harness² usage and Pi trajectories, claude-code, model …
haizhongzheng Nov 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions docs/framework/agent-layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,10 @@ What the Base Agent is used for. Two kinds:
runs' trajectories and writes a new adapter, calling a Base Agent: `direct` for methods that decide every step in
their own code (GEPA, SkillOpt, ReHarness, Continual Harness), `codex`, `claude`, `pi`, `opencode` or `dsh` for
methods whose learner is an agent (Meta-Harness Adapter, the `codex`, `claude`, `pi`, `opencode` and `dsh` Evolvers).
The run's model reaches an Evolver as `ctx.agent`, a `direct` Base Agent metered against the budget;
`ask(ctx.agent, messages, max_tokens=...)` returns the reply as the `llm` tool does.
With `evolver.base: pi`, a method that decides every step in its own code calls Pi as its model
([every method on Pi](../methods/agent.md#every-method-on-pi)). The run's model reaches an Evolver as
`ctx.agent`, a `direct` Base Agent metered against the budget; `ask(ctx.agent, messages, max_tokens=...)` returns
the reply as the `llm` tool does.

What is specific to a task belongs to Layer 3: its environment, rules, turns, scoring, workspace and tools. A
benchmark that defines its own steps keeps its loop in Layer 3 unchanged and makes every model call through Layer
Expand All @@ -118,11 +120,11 @@ relayed to the URL it would have reached with its body, key and reply unchanged
stream). CL-Bench's runner and ServeLearnBench's agent reach their model this way, through the launcher's meter as
before. The same endpoint over a CLI Base Agent answers each request with one session of it (the system messages
as its instructions, the rest as its prompt, its reply as the reply): `llm: {cli: claude, model: ..., login: claude,
...}` (or `cli: codex`, `login: chatgpt`) makes the run's model one only the CLI reaches, such as a model on a
...}` (or `cli: codex`, `login: chatgpt`, or `cli: pi` with a `<backend>/<model>` reference) makes the run's model one only the CLI reaches, such as a model on a
subscription, for the Evolvers that call `ctx.agent` and the `llm` tool, and `agent.model: {cli: ...}` the same for an
acting pipeline, which sees a local URL. Such a session works in an empty directory of its own, in a bubblewrap
sandbox of its own (`isolation: sandbox`, the default) or a container (`isolation: container`), and is given no tools
(`text_only`): a Claude Code session can only reply, a Codex session can still run commands inside its sandbox. A chat
(`text_only`): a Claude Code or Pi session can only reply, a Codex session can still run commands inside its sandbox. A chat
call sees no files, so a role that is to read files runs a session itself (`make(...).run(prompt, workspace)`). What a
session cannot do is refused, not dropped ([the model contract](../guides/claude-subscription.md#the-model-contract)).
Each call's event stream is kept under the run's `llm_sessions/`.
Expand Down Expand Up @@ -232,16 +234,16 @@ accepted; a key given in two layers is refused. On the command line a layer's se
|---|---|---|
| OfficeQA in Codex, every method | Acting Agent with OfficeQA's task functions (`utils/task.py`) | `codex` |
| OfficeQA in direct chat | Acting Agent running SkillOpt's chat loop | `direct`, per turn |
| GEPA, SkillOpt, ReHarness, Continual Harness, OPRO | Evolver Agent | `direct` (`ctx.agent`); OPRO in OfficeQA: `codex` through `llm: {cli: codex}` |
| Meta-Harness Adapter, `codex` and `claude` Evolvers | Evolver Agent | `codex` or `claude` |
| GEPA, SkillOpt, ReHarness, Continual Harness, OPRO | Evolver Agent | `direct` (`ctx.agent`); OPRO in OfficeQA: `codex` through `llm: {cli: codex}`; with `evolver.base: pi`, `pi` through `llm: {cli: pi}` |
| Meta-Harness Adapter, ASPIRE, `codex` and `claude` Evolvers | Evolver Agent | `codex`, `claude`, `pi` or `opencode` |
| `pi` Evolver | Evolver Agent | `pi` |
| `opencode` Evolver | Evolver Agent | `opencode` |
| `dsh` Evolver | Evolver Agent | `dsh` |
| RRSI Adapter | Evolver Agent: upstream's rules and roles, the proposer's prompt | `codex` or `claude` for the proposer, and the same, one call per upstream `generate`, for the analyst, digesters and critic |
| AHE Adapter | Evolver Agent: the Evolve Agent and the Agent Debugger | `codex` or `claude`, one session per step and per task |
| RRSI Adapter | Evolver Agent: upstream's rules and roles, the proposer's prompt | `codex`, `claude` or `pi` for the proposer, and the same, one call per upstream `generate`, for the analyst, digesters and critic |
| AHE Adapter | Evolver Agent: the Evolve Agent and the Agent Debugger | `codex`, `claude` or `pi`, one session per step and per task |
| CL-Bench, ServeLearnBench | Acting Agent running the benchmark's loop | `direct`, per call, through the local endpoint |
| MWM | Evolver Agent | `codex` or `claude`, in a view of the run's sandbox with MWM's native mounts (`/w`, `/evidence`, ...) |
| Harness² | Evolver Agent: upstream's proposer | `claude`, one call per upstream session (or `codex`) |
| MWM | Evolver Agent | `codex`, `claude` or `pi`, in a view of the run's sandbox with MWM's native mounts (`/w`, `/evidence`, ...) |
| Harness² | Evolver Agent: upstream's proposer | `claude`, one call per upstream session (or `codex` or `pi`) |

## Outside the layers

Expand Down
3 changes: 2 additions & 1 deletion docs/framework/custom-evolver.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ evolver:
```

Use `needs` to declare required resources: `validation_data`, `environment`, or `send_material_out`. The Trainer
checks them against the run settings before starting. Set `supports_resume = True` if the method understands the
checks them against the run settings before starting. `llm` says that the method calls the run's model (`ctx.agent`,
the `llm` tool) rather than a CLI of its own, so that `evolver.base: pi` makes that model Pi. Set `supports_resume = True` if the method understands the
previous session handle; this is independent of storing files in `state/`. A param that names a model other than
the run's `llm` (an embedding model, for one) is listed in the class's `model_params`, for example
`model_params = ("embedding",)`: the loader binds a `<backend>/<model id>` reference there against the run's backends,
Expand Down
4 changes: 2 additions & 2 deletions docs/framework/evolver.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ For selection guidance, setup, and the complete parameter defaults, use [Choose
| `ahe_adapter` | [AHE Adapter](../methods/ahe_adapter.md), with `template: ahe@1.0`: one AHE iteration per step. It classifies each task by its k rollouts, scores the previous step's change manifest and rolls HARMFUL changes back, then runs an Agent Debugger session per task and an Evolve Agent session that edits the adapter; finalize hands back the best-ever iteration. | The Codex CLI, k rows per task sharing `task_field`, an acting agent that runs the adapter's hooks (Codex, Claude Code) |
| `mwm` | MetaWorld Model, with `template: mwm`: one native MWM learner session per block (the step), under the MWM contract, with the task's evidence store mounted at `/evidence`, in its own network namespace. See `docs/methods/mwm.md`. | The Codex CLI, a task that keeps an MWM evidence store (`evolver_access.environment`), `task-info/mwm/`, `data.shuffle: false` |
| `reharness` | [ReHarness](../methods/reharness.md), with `template: reharness`: one instance of the stream per step. It revises each reflection the acting agent retrieved, from that reflection's own source trajectory with the new trajectory as feedback, then writes the instance's reflection with the revised ones as demonstrations, through the run's `llm` tool with the paper's prompts; no validation data. | `llm`, `data.step_size: 1` and `data.shuffle: false`, an embedding endpoint (`evolver.params.embedding`, and `REHARNESS_EMBED_URL` for the acting side's reader; BM25 without) |
| `meta_harness_adapter` | [Meta-Harness Adapter](../methods/meta_harness_adapter.md), with `template: meta-harness-adapter`: one candidate per step. Each step's rollouts evaluate the candidate handed back last; when none is waiting, a Codex or Claude Code proposer reads every earlier candidate's files, scores and trajectories and writes k new adapters, which pass `check_spec` before they are queued; no validation data. | The proposer CLI, `data.step_size` covering the training split (the search set) |
| `rrsi_adapter` | [RRSI Adapter](../methods/rrsi_adapter.md), with `template: rrsi-adapter`: one candidate per step. Each step's rollouts evaluate the candidate handed back last; when a round's candidates are all measured, upstream's selection decides it, upstream's analyst reads the incumbent's evaluation, and m Codex or Claude Code proposer sessions edit the incumbent under RRSI's edit budget, each diff screened by upstream's critic and a smoke before it is queued; no validation data. | The proposer CLI and the Codex CLI (the analyst, digesters and critic), `data.step_size` covering the training split with k rows per task, `evolver.params.domain`, `python -m pip install -e . --group rrsi` |
| `meta_harness_adapter` | [Meta-Harness Adapter](../methods/meta_harness_adapter.md), with `template: meta-harness-adapter`: one candidate per step. Each step's rollouts evaluate the candidate handed back last; when none is waiting, a Codex, Claude Code, Pi or OpenCode proposer reads every earlier candidate's files, scores and trajectories and writes k new adapters, which pass `check_spec` before they are queued; no validation data. | The proposer CLI, `data.step_size` covering the training split (the search set) |
| `rrsi_adapter` | [RRSI Adapter](../methods/rrsi_adapter.md), with `template: rrsi-adapter`: one candidate per step. Each step's rollouts evaluate the candidate handed back last; when a round's candidates are all measured, upstream's selection decides it, upstream's analyst reads the incumbent's evaluation, and m Codex, Claude Code or Pi proposer sessions edit the incumbent under RRSI's edit budget, each diff screened by upstream's critic and a smoke before it is queued; no validation data. | The proposer CLI (Codex, Claude Code or Pi), which also runs the analyst, digesters and critic, `data.step_size` covering the training split with k rows per task, `evolver.params.domain`, `python -m pip install -e . --group rrsi` |

Change a parameter with `evolver.params` in the run config; each Evolver's `evolver.yaml` under
`hermit/agent/evolver/methods/` lists them with their defaults. For `gepa`, `data.step_size` is the reflection
Expand Down
8 changes: 4 additions & 4 deletions docs/get-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@ python -m pip install "hermit-harness[mini-swe-agent]"
| Prompt-only learner | The Codex CLI, Linux with bubblewrap, and a learner backend that serves the Responses API |
| GEPA | `python -m pip install -e . --group gepa`, learner endpoint, validation split |
| SkillOpt | `python -m pip install -e . --group skillopt`, learner endpoint, validation split |
| Harness² | Python 3.12 or 3.13, `python -m pip install -e . --group harness2`, Claude Code, and the execution environment described in [Harness²](../methods/harness2.md) |
| RRSI Adapter | `python -m pip install -e . --group rrsi`, a CLI for the proposer and its analyst, digesters and critic (Codex or Claude Code), a training split evaluated k times per task |
| Harness² | Python 3.12 or 3.13, `python -m pip install -e . --group harness2`, Claude Code (or Codex or Pi), and the execution environment described in [Harness²](../methods/harness2.md) |
| RRSI Adapter | `python -m pip install -e . --group rrsi`, a CLI for the proposer and its analyst, digesters and critic (Codex, Claude Code or Pi), a training split evaluated k times per task |
| Codex / Claude learner | Selected authenticated CLI and supported program-execution environment |
| MWM | Linux learner environment, Codex or Claude Code, evidence store, task deployment contract, model access |
| MWM | Linux learner environment, Codex, Claude Code or Pi, evidence store, task deployment contract, model access |
| Acting and learning on a Claude subscription | Claude Code 2.1.248 or later, `claude setup-token`, Linux with bubblewrap for its sandboxes; see [Claude subscription](../guides/claude-subscription.md) |
| Pi acting agent or learner | The `pi` release binary on `PATH` (GitHub release `pi-linux-x64.tar.gz`, checked against its `SHA256SUMS`) and a model reference |
| Pi acting agent or learner | The `pi` release binary on `PATH` (GitHub release `pi-linux-x64.tar.gz`, checked against its `SHA256SUMS`) and a model reference. For a Pi acting agent put the release's directory on `PATH`, or link `pi` from outside `/usr`: its task sandbox shows a program's own directory and follows no link from `/usr` |
| OpenCode acting agent or learner | The `opencode` 2.2 release binary on `PATH` (GitHub release `opencode-linux-x64.tar.gz` of `anomalyco/opencode`, checked against the release's sha256 digest) and a model reference |
| DeepSeek Harness acting agent or learner | The `dsh` command on `PATH` (Node.js 24.9 or later, `npm install -g @deepseek-ai/dsh@0.2.2-rc.1`) and a model reference |
| Benchmark recipe | Its dataset, scorer, model settings, and environment; see [catalog](../recipes/index.md) |
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/claude-subscription.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ A session is not a model API. This is what a chat request through the endpoint g
| `reasoning_effort` | The session's effort; on Claude Code low, medium, high, xhigh or max, else 400 |
| `model` | Must be the endpoint's; another is 400, never a quiet substitute |
| `max_tokens`, `max_completion_tokens` | Accepted and not applied, and the reply's `unapplied` says so: methods send them as a cap, and the CLI's own cap would cut the reply and continue it by itself |
| `temperature`, `top_p`, `stop`, `seed`, `logprobs`, penalties, anything else | 400: the CLI has no setting. `llm.unapplied: [temperature]` accepts a named field going unapplied |
| `temperature`, `top_p`, `stop`, `seed`, `logprobs`, penalties, anything else | 400: the CLI has no setting. `llm.unapplied: [temperature]` accepts a named field going unapplied. Pi applies `temperature` and `top_p` where its backend speaks an OpenAI-compatible API |
| `n` above 1; a `response_format` other than text | 400 |
| `stream`, `stream_options` | Kept: the reply arrives whole, as chunks at the end |
| `parallel_tool_calls`; `tools: []`; `tool_choice` `none` or `auto` | Accepted: they ask for nothing a session would do |
Expand Down
7 changes: 4 additions & 3 deletions docs/guides/model-backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ hermit train --config run.yaml model=vllm/Qwen/Qwen3-30B-A3B-Instruct-2507-FP8
| `agent.params.model` (or `agent.model`) with `agent.name: codex` / `claude-code` | the CLI acting agent | Responses API / Messages API |
| `agent.params.model` (or `agent.model`) with `agent.name: mini-swe-agent` | mini-swe-agent, through litellm | chat completions |
| `evolver.params.model` of the `codex`, `claude` and `mwm` Evolvers | the learner's CLI session | Responses API / Messages API |
| `agent.params.model` with `agent.name: pi`, `evolver.params.model` of the `pi` Evolver | Pi | chat completions, else the Messages API, else the Responses API: the first the backend serves |
| `agent.params.model` with `agent.name: pi`, the model of any method on `evolver.base: pi` | Pi | chat completions, else the Messages API, else the Responses API: the first the backend serves |
| `agent.params.model` with `agent.name: opencode`, `evolver.params.model` of the `opencode` Evolver | OpenCode | chat completions |
| `agent.params.model` with `agent.name: dsh`, `evolver.params.model` of the `dsh` Evolver | DeepSeek Harness | chat completions, else the Messages API, else the Responses API: the first the backend serves |
| `model` (top level) | the default for all of the above when they name none | |
Expand Down Expand Up @@ -108,8 +108,9 @@ lab-sglang: # a new one, like the built-in sglang at
| `messages_url` | the root the Anthropic Messages API is under, when it is not `base_url` without its `/v1` |
| `litellm` | the litellm route prefix, for task agents that call through litellm (`fireworks_ai`, `hosted_vllm`) |
| `prices` | per model id, `{input, cached_input, output}` in USD per million tokens; a model without one has an unknown cost (never zero) |
| `extra_body` | fields added to every chat request, such as `reasoning_effort` |
| `context_window` | tokens; Codex is told it as `model_context_window`, Claude Code as `CLAUDE_CODE_MAX_CONTEXT_TOKENS`, DeepSeek Harness as its provider's `contextWindow` |
| `extra_body` | fields added to every chat request, such as `reasoning_effort`; Pi sends them as its model's `samplingParams` with each chat-completions request, under a session's own effort |
| `context_window` | tokens; Codex is told it as `model_context_window`, Claude Code as `CLAUDE_CODE_MAX_CONTEXT_TOKENS`, Pi as `contextWindow`, DeepSeek Harness as its provider's `contextWindow` |
| `max_output_tokens` | tokens one request may answer with; Pi is told it as `maxTokens`, and otherwise uses its default of 16384: a model that reasons long can be cut before it answers, and that session fails |
| `developer_role` | `keep` (default), or `system`: a relay in front of the backend renames the `developer` role Codex uses, for an endpoint that refuses it (the built-in `fireworks` sets it, as the examples always did; Qwen3.6's chat template refuses a `system` message after the first, so a local server keeps the role) |
| `models` | aliases: short names for model ids |
| `from` | start from a copy of another backend (the examples put a metering relay in front of one this way); the copy keeps the source's key variable, and a copy at another `base_url` drops the source's `messages_url` |
Expand Down
24 changes: 24 additions & 0 deletions docs/methods/agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@ is refused.
The `dsh` learner signs in as the `pi` learner does (`evolver.base: dsh`) and copies no file of `~/.dsh` (`home: []`).
On the single-file prompt-only template, the Codex learner is the [prompt-only](prompt-only.md) method.

## Every method on Pi

`evolver.base: pi` with `evolver.model: <backend>/<model>` runs any method's learner on Pi, on that model:

| Method | What runs on Pi |
|---|---|
| `pi`, Meta-Harness Adapter, ASPIRE, MWM, AHE Adapter, Harness², RRSI Adapter, EvoSafe | The method's own sessions, as on Codex or Claude Code |
| GEPA, SkillOpt, ACE, OPRO, ReHarness, Continual Harness | They call the run's model, so that model becomes Pi: each call is one session with no tools (the same as `llm: {cli: pi, model: <backend>/<model>}`) |

Pi signs in only through the model reference and has no web search. Where the backend speaks an OpenAI-compatible API
(chat completions or the Responses API), a request's `temperature` and `top_p` reach the model as Pi's
`samplingParams`; on the Messages API Pi ignores them, so there a request that asks for a `temperature` is refused
unless `evolver.pi.unapplied` names it. The backend's `extra_body` goes with each chat-completions request. A session's
`effort` is what the model is sent, over an `extra_body.reasoning_effort`; on the Messages API Pi gives `xhigh` and `max`
the same thinking budget as `high`, so there they are refused. Pi has no sub-agents: an adapter's sub-agents stay
files its acting agent can read. Pi's `grep` and `find` tools run `rg` and `fd`, which an offline Pi cannot fetch: RRSI's
digesters get them only where the machine has them. Pi caps one reply at 16384 tokens unless the backend says otherwise:
a reply cut there comes back with `finish_reason: length`, or fails the session that needed it whole. Where a long answer
is due (EvoSafe's whole files) and the model reasons at length, give the backend a larger
[`max_output_tokens`](../guides/model-backends.md). Pi's `bash` has no time limit of its own: a command the model
gives none stops after 600 seconds, so one command that never ends costs that much of a step, not the whole call.
`command_timeout_s` in `evolver.pi` (the learner's sessions) or `agent.pi` (the acting agent's) changes it; `0` turns it
off.

## Requirements and setup

Install and authenticate the selected learner CLI in the environment where training runs. Sandboxed program
Expand Down
Loading
Loading
0