Repository navigation
Description
Problem
NeMo Gym is trying to support a familiar RL-style setup in a modern, LLM-agent setting:
Experiment with LLM-based Agents on Tasks sampled from different Environments, collect rollouts, evaluate performance, and optionally train the Agent or Policy Model from those rollouts.
The concepts are not new: an Agent interacts with an Environment, produces a trajectory, and receives reward. In today's LLM setting, the Environment is often interfaced through language, tools, APIs, filesystems, browsers, sandboxes, or MCP servers. The Task may be partially observable, long-context, and agentic. The reward may be terminal-only, as in many RLVR workflows, or it may eventually be emitted per turn, tool call, or environment step.
The confusion is in how we map those concepts onto NeMo Gym's async-first, server-first architecture.
The docs currently use environment in a few overlapping ways. The concepts page says an environment contains dataset, agent harness, verifier, and state. The terminology page then says a Gym environment is "a resources server together with an agent and dataset(s)" and that every environment has exactly one resources server.
That wording is hard to map onto the architecture. It also showed up in SWE-bench work such as #1738, where task setup, sandbox lifecycle, and grading could drift into agent-server code instead of being modeled as Environment or World responsibilities.
The goal of this issue is to clarify the mental model so we can extend the system without mixing up Agent, Environment, Task, Resources Server, Model Server, and Sandbox.
This is an architecture clarification and design direction, not a claim that every contract is final.
Proposed mental model
Agent
An Agent is the decision-making system:
Policy Model + Agent Harness
The Policy Model is the LLM whose weights we may evaluate or train.
The Agent Harness is the orchestration around the model: tool calls, model calls, planning loops, context management, stopping behavior, CLI invocation, memory, critics/value functions, or other adaptive pieces. Today, the most common learning target is the Policy Model weights, but the broader Agent design space can include adaptive memory, critics, value functions, or harness-level learning.
An Agent Server is how Gym deploys this orchestration, usually under responses_api_agents/.
Black-box CLI agents such as Claude Code or Codex still fit this model. We wrap the CLI in an Agent Server, but conceptually it is still an Agent Harness around model-backed behavior.
Environment
An Environment is the world or problem family the Agent interacts with.
It defines the state, the actions/tools available to the Agent, the observations returned to the Agent, the transition semantics of the world, and the reward authority. In a language-agent setting, those pieces may be exposed through text, tool calls, filesystems, shells, browsers, MCP servers, APIs, databases, or other runtime systems.
For SWE-bench, the Environment is the code-repair world. Its state includes the repository filesystem: the repo at the base commit, dependencies/build artifacts in the image, files changed during the attempt, command outputs, logs, and test execution state. The Sandbox hosts that filesystem and process runtime, but the Environment defines what that state means, how it is initialized from a Task, and how changes to it are scored.
A browser is not part of the standard SWE-bench Environment because the SWE-bench world is repo + shell/test execution + patch verification. But in a browser-based debugging Environment, browser DOM/page state, screenshots, clicks, and app runtime state could be part of the Environment state and action/observation interface.
Current RLVR-style Environments often produce sparse terminal reward through verify() after the full episode is collected. That remains valid. The design should also allow richer Environments to emit reward per step, per turn, or per tool call when the interaction is mediated by Gym.
Notice that nothing prevents the Agent to create a Model of the parts of the MDP (Environment) like for example a Reward Model to help with its own learning.
Task
A Task is one problem instance sampled from an Environment.
For SWE-bench, a task is one issue instance: repo, base state, problem statement, instance id, and privileged grading metadata.
This is different from the Environment. SWE-bench is the environment/world family; django__django-13741 is a task inside it. A dataset is a collection of tasks.
Resources Server
A Resources Server is an implementation mechanism for Environment capabilities.
It may expose task seeding, state, tools, MCP endpoints, verify(), optional step(), or a facade over other services. Many Environments today are implemented as one Resources Server, but we should not define Environment as exactly one Resources Server forever.
Model Server
A Model Server is separate from the Agent Server so inference and training can scale independently. This separation matters for NeMo Gym and NeMo-RL: rollout collection, model inference, and training should be able to scale without forcing the Agent Harness and model backend into the same process.
Sandbox
A Sandbox is runtime and isolation.
It can host Environment state, Agent execution, both together, or neither. For SWE-bench, Docker or Apptainer provides the repo filesystem and execution boundary.
The sandbox is not the Environment by itself. It is the runtime substrate where Environment state and/or Agent execution happen.
Benchmark
A Benchmark is a fixed evaluation product built on top of an Environment:
- fixed task split
- fixed metric
- fixed protocol
- baselines or leaderboard claims
For example, SWE-bench is an Environment/world family. SWE-bench Verified is a benchmark configuration built from a fixed task set and scoring protocol.
SWE-bench as the concrete example
Using SWE-bench as the running example:
- Task: one SWE-bench instance, with problem statement, repo/image info, instance id, and grading metadata.
- Environment: the SWE-bench world: repo state, patch-producing interaction, and test-based reward semantics.
- Resources Server(s): implementation of SWE-bench setup and verification, such as
seed_sessionandverify. - Agent:
claude_code_agent,anyswe_agent, OpenClaw, or another harness wrapped as an Agent Server. - Policy Model: the LLM backing the Agent.
- Model Server: optional serving layer for inference, decoupled from the Agent Server.
- Sandbox: Docker, Apptainer, OpenSandbox, or another runtime.
- Benchmark: SWE-bench Verified as a frozen task split and metric.
One naming caveat: SWE-bench's own "evaluation harness" is its grading/test execution machinery. That is not the same thing as a Gym Agent Harness.
Desired direction
We should update docs and architecture language so they say:
- Agent = Policy Model + Agent Harness.
- Agent Server deploys Agent orchestration.
- Model Server serves inference separately for scalability and training integration.
- Environment defines the world, state, actions/tools, observations, transitions, and reward semantics.
- Resources Servers implement Environment capabilities, but are not always the whole Environment concept.
- Task is a first-class problem instance inside an Environment.
- Sandbox is runtime/isolation for the Agent, the Environment, or both.
- Benchmark is a fixed evaluation protocol built on top of an Environment.
This should preserve what works today while leaving room for external Environments, black-box Agent internals, remote sandboxes, per-step rewards, and richer learning setups.
Why this matters
A natural requirement is:
Swap any Agent with any compatible Environment and collect rollouts.
The target composition should look like:
agent server x environment x sandbox backend x task set x model server
not one wrapper per agent/benchmark pair.
For SWE-bench, the long-term direction is that SWE-specific setup and verification live with the SWE-bench Environment, while black-box agents remain normal Agent Servers that consume the Environment contract.
Related
- Decouple SWE environment infrastructure from agent harnesses #1249: parent issue for decoupling SWE environment infrastructure from agent harnesses
- Converge #1677 swe_env decoupling into anyswe + verify on SWE-bench Verified (#1249) #1738: SWE-bench convergence PR that exposed the boundary question
- feat: Add initial prototype for Agent / Environment responsibility split. #1801: initial prototype for the Agent / Environment responsibility split
- Docs:
fern/versions/latest/pages/about/concepts/environments.mdx - Docs:
fern/versions/latest/pages/about/concepts/key-terminology.mdx
Activity
ffrujeri commentedon Jun 27, 2026
This comment is the design note companion to the issue body. It is exploratory: a design direction for clearer boundaries, grounded in the prototype in #1801, not a claim that every part is final.
The useful way to read the prototype is step by step: a Task is sampled, an Environment accepts it and returns episode context, the Agent Harness orchestrates the agent side of the interaction, the Sandbox hosts runtime, and the Environment produces reward.
Bigger picture
At the end of the day, Gym is experimenting with LLM-based Agents on Tasks created in different Environments or Worlds.
Those Tasks may look like partially observable MDPs, contextual MDPs, tool-use problems, code-repair tasks, browser workflows, or long-context agentic tasks. The interface is often language plus tools rather than a small numeric action space, but the high-level loop is still familiar:
The rollout can be used for:
The design should not assume the Policy Model is the only thing that can ever adapt. Future Agents may include adaptive harnesses, memory modules, critics, value functions, reward models, or other pieces. But today, the main learning path is still through the LLM / Policy Model.
NeMo Gym and NeMo-RL are trying to make this work in an async-first, server-first architecture: rollout collection, inference, environment interaction, verification, and training should scale independently.
What we are trying to fix
The current wording makes it too easy to treat an Environment, a Resources Server, an Agent Server, a Sandbox, and a Task as interchangeable pieces.
That confusion showed up in #1738. The PR does useful work by extracting SWE-bench setup, grading, and sandbox-provider logic, but the boundary is still blurry: SWE-specific world setup and verification can live under an agent-server path, and the agent wrapper becomes the place where environment behavior is assembled.
The simpler target is:
Core concepts
Agent
The decision-making system: Policy Model plus Agent Harness. The harness orchestrates the agent side of the interaction: model calls, tool calls, CLI invocation, planning loops, context management, stopping behavior, memory, and similar logic.
In Gym, this orchestration is deployed as an Agent Server under
responses_api_agents/.Black-box CLIs such as Claude Code or Codex still fit this model. Their internals may not be native to Gym, but the wrapper Agent Server lets Gym collect rollouts from them.
Policy Model / Model Server
The Policy Model is the LLM. The Model Server is separate from the Agent Server so inference and training can scale independently. This is important for NeMo-RL integration: the rollout system should not require the model backend and the agent orchestration to live in one process.
Environment
The world/problem family. It defines state, actions or tools, observations, transition semantics, and reward authority.
Current RLVR-style environments often produce sparse terminal reward through
verify()after the full episode is collected. That remains valid. But the design should also allow richer Environments to emit reward per step, per turn, or per tool call when the interaction is mediated by Gym.The invariant is:
Task
One problem instance sampled from an Environment. For SWE-bench, this is one
instance_idwith problem statement, repo/base state, image or world-init data, and privileged grading metadata.Resources Server
An implementation mechanism for Environment capabilities: task seeding, state, tools,
verify(), optionalstep(), MCP endpoints, or a facade over other services.A simple Environment may be implemented as one Resources Server. More complex Environments may be composed from multiple Resources Servers or external systems. The concept should not force exactly one process forever.
Sandbox
Runtime and isolation. It can host the Environment state, the Agent execution, both, or neither. Docker and Apptainer are sandbox backends for SWE-bench; they are not themselves the Environment.
[-]Design: model NeMo Gym environments as an RL system — Environment / Agent-Harness / Sandbox contracts[/-][+]Design: clearly split between NeMo Gym agent and environments responsibilities as RL System — Environment / Agent-Harness / Sandbox contracts[/+][-]Design: clearly split between NeMo Gym agent and environments responsibilities as RL System — Environment / Agent-Harness / Sandbox contracts[/-][+]Design: clearly split between NeMo Gym agent and environments responsibilities as RL System — Environment / Agent(-Harness) / Sandbox contracts[/+][-]Design: clearly split between NeMo Gym agent and environments responsibilities as RL System — Environment / Agent(-Harness) / Sandbox contracts[/-][+]Design: clearly split between NeMo Gym agent and environments responsibilities as an RL Problem — Environment / Agent(-Harness) / Sandbox (run-time environments) contracts[/+][-]Design: clearly split between NeMo Gym agent and environments responsibilities as an RL Problem — Environment / Agent(-Harness) / Sandbox (run-time environments) contracts[/-][+]Clarify Agent, Environment, Task, and Sandbox boundaries[/+]ffrujeri commentedon Jun 30, 2026
Concrete Prototype #1801
PR #1801 makes the discussion concrete with a SWE-bench prototype:
resources_servers/swe_bench/becomes the SWE-bench Environment resources server.resources_servers/swe_bench/task.pyintroducesSweTask, an Environment-internal Task value built from dataset row metadata.resources_servers/swe_bench/session.pyintroducesSessionDescriptor, the Environment response after accepting a task.resources_servers/swe_bench/app.pyexposesseed_sessionandverify.responses_api_agents/claude_code_agent/app.pystays the Claude Code Agent Server, but learns how to consume the descriptor for topologyagent_in_env.nemo_gym/sandbox/providers/docker/adds a Docker sandbox backend used by both the agent working sandbox and the verifier sandbox.The important point is that this does not make Claude Code a SWE-specific environment. It lets the Claude Code Agent Harness interact with a SWE-bench Environment through a small contract.
Prototype flow, step by step
1. The rollout starts from a dataset row
The rollout input is still a JSONL row with
responses_create_paramsandverifier_metadata.For SWE-bench, the row carries public prompt data plus privileged fields such as
instance_id,dataset_name,instance_dict, test metadata, and optionally a container formatter.At this point, the data is just storage format. It becomes a Task when the Environment parses it.
2. The Agent Server asks the Environment to seed the task
claude_code_agent.run()sends the run body to:The Environment parses the row into a
SweTaskusingparse_task_from_request().Conceptually:
SweTaskincludes:instance_id/task_idproblem_statementThis is the Task, not the Environment and not the Agent.
3. The Environment returns episode context
swe_bench.seed_session()selects the SWE harness family, builds aSandboxSpec, and returns aSessionDescriptor.The descriptor is not the Task. It is the Environment saying:
In #1801 it includes:
agent_in_env/verifyThis is where the Environment owns the SWE-bench world setup instead of asking the Agent Server to know SWE-bench details.
4. The episode is the Agent-Environment interaction
The Agent Server does not “run the episode” by itself. The episode is the interaction between the Agent and the Environment.
The Agent Harness orchestrates the agent side of that interaction: model calls, CLI invocation, tool use, stopping behavior, and artifact capture.
For topology
agent_in_env, the prototype interaction is:SWE-bench is terminal-verification style in this prototype: the agent acts in the repo world, then the Environment scores the final patch.
5. The sandbox hosts the runtime
For SWE-bench, the sandbox is the runtime for the repo world and the black-box CLI execution.
In #1801,
claude_code_agentreads the descriptor'ssandbox.spec, starts anAsyncSandboxwith the configured provider, injects model egress environment variables, stages Claude settings, and runsclaude -pinside the task workdir.After Claude Code stops, the Agent Harness harvests the artifact with:
That patch becomes
verifier_metadata.model_patch.6. The Agent returns the artifact for verification
After the interaction,
claude_code_agentsends:with:
NeMoGymResponsetrajectory built from Claude Code stream-jsonmodel_patchThe Agent is not the reward authority. It only returns what happened and the artifact it produced.
7. The Environment verifies in a fresh sandbox
swe_bench.verify()parses the same task again, applies the submitted patch to the task value, and callsverify_task().verify_task()then:This is the hermetic twin: verification does not reuse the agent's working sandbox, so agent-side artifacts do not accidentally affect scoring.
8. The rollout receives reward
The Environment returns a response with:
rewardresolvedpatch_existsmask_sampleerror_kindThat result flows back through the Agent Server to the rollout driver.
Reward timing: terminal today, step-level later
The current SWE-bench prototype uses terminal
verify()because raw Claude Code shell use is not mediated by a Gym step API. Gym sees the final patch and grades it.The design should also support less sparse rewards when the Environment mediates the interaction:
verify().step(action)returns observation, reward, done, and metadata.For black-box agents, per-step reward requires the relevant action boundary to be visible to Gym: MCP tools, a shell broker, a browser broker, or another Environment API. If the agent directly controls a raw shell inside a sandbox, Gym may only observe the final artifact unless we instrument that boundary.
What this clarifies
The prototype separates responsibilities like this:
Why this is better than an agent-specific SWE wrapper
The target composition should be:
not a new wrapper for every agent and benchmark pair.
In this model:
claude_code_agentremains a general Agent Server for Claude Code.swe_bench.verified:marker belongs on the Environment configuration once the protocol is baselined.How to read #1738 from this perspective
#1738 is useful as a stepping stone. It validates several pieces we want:
But it should not become the long-term abstraction if it means SWE-specific Environment behavior is owned by an agent wrapper. The follow-up design should move toward a SWE-bench Environment contract that black-box Agents can consume without becoming SWE-specific Agents themselves.
Open design questions
A few details still need design work:
seed_sessionreturn a formal session descriptor with sandbox, MCP, egress, and placement fields?verify()only?The immediate next step is to align the docs and terminology so people can discuss these questions without first untangling overloaded words.