用途
generate-nemo-gym-env 用于构建某个环境(env)的 NeMo Gym 变体。NeMo Gym 是 NVIDIA 的 RL gym 层,专为 LLM 智能体设计,基于 Ray 做编排,并在 episode 结束后进行评分(post-episode grading)。当有人要求搭建一个 NeMo Gym Resources Server、把现有环境移植到 NeMo Gym,或需要 NeMo Gym 版本的环境时,使用该技能。
当用户已经拥有一个共享领域模块(<domain>.py)并希望得到 NeMo Gym 变体时,应封装它,而不要复制逻辑。
概念
NeMo Gym 是 NVIDIA 面向 LLM 智能体的 RL gym 层。它构建在 Ray 之上,提供一个基于 FastAPI 的 SimpleResourcesServer,该 Server 为每个工具暴露一个 POST /<tool> 端点,并附带标准的 /seed_session(基于 cookie 的会话引导)和 /verify(episode 结束后评分器)。目标文档位于 docs.nvidia.com/nemo/gym/latest。
Python 包名为 nemo_gym,通过以下命令安装:
pip install git+https://github.com/NVIDIA-NeMo/Gym
注意:NeMo Gym 需要 Python 3.12+。
原型(Archetypes)
- 纯 Python 游戏:单个工具端点;/verify 对 ground_truth 做子串匹配。
- 有状态沙箱:每个会话在 self.sessions 中维护沙箱;首次工具调用时惰性初始化。
- 视觉 / 计算机使用:每个动作一个端点;/verify 奖励调用过 terminate(success) 的轨迹。
推荐文件布局
实际路径由用户选择,典型结构如下:
<env_dir>/nemo_gym/
├── pyproject.toml # nemo_gym (git+) + e2b-* + fastapi + uvicorn + requests
├── __init__.py
├── Dockerfile # 面向 Ray 的多阶段构建
├── configs/<env>.yaml # NeMo Gym 配置(entrypoint、domain、description)
├── server.py # SimpleResourcesServer 子类,含工具端点
├── rollout.py # 原始 requests + cookie 会话
└── README.md
实现顺序
1. Server 类 — server.py
from nemo_gym.base_resources_server import (
BaseResourcesServerConfig,
BaseSeedSessionRequest, BaseSeedSessionResponse,
BaseVerifyRequest, BaseVerifyResponse,
SimpleResourcesServer,
)
from nemo_gym.server_utils import SESSION_ID_KEY
from fastapi import FastAPI, Request
from pydantic import BaseModel, Field
from typing import Any, Dict
class MyConfig(BaseResourcesServerConfig):
pass
class GuessReq(BaseModel):
word: str
class ToolResponse(BaseModel):
output: str
class MyVerifyRequest(BaseVerifyRequest):
ground_truth: list = []
class MyResourcesServer(SimpleResourcesServer):
config: MyConfig
sessions: Dict[str, Dict[str, Any]] = Field(default_factory=dict)
def setup_webserver(self) -> FastAPI:
app = super().setup_webserver()
app.post("/guess")(self.guess)
return app
async def seed_session(self, body: BaseSeedSessionRequest) -> BaseSeedSessionResponse:
return BaseSeedSessionResponse()
def _sess(self, request: Request) -> Dict[str, Any]:
sid = request.session[SESSION_ID_KEY]
if sid not in self.sessions:
self.sessions[sid] = {"game": WordleGame(), "step": 0}
return self.sessions[sid]
async def guess(self, body: GuessReq, request: Request) -> ToolResponse:
sess = self._sess(request)
feedback = sess["game"].guess(body.word)
sess["step"] += 1
return ToolResponse(output=feedback)
async def verify(self, body: MyVerifyRequest) -> BaseVerifyResponse:
# Compute reward from the response trajectory + ground truth
expected = ""
if body.ground_truth and isinstance(body.ground_truth, list):
expected = body.ground_truth[0].get("expected_output", "")
reward = 0.0
for item in body.response.output:
if hasattr(item, "type") and item.type == "function_call_output":
if expected and expected in getattr(item, "output", ""):
reward = 1.0; break
return BaseVerifyResponse(**body.model_dump(), reward=reward)
if __name__ == "__main__":
MyResourcesServer.run_webserver()
关键约定:
- 每个工具一个端点,在 setup_webserver() 中注册。请求体上的 Pydantic 模型即为 JSON 形状。
- 会话存放在 self.sessions 中,以 request.session[SESSION_ID_KEY] 为键,首次调用时惰性初始化。NeMo Gym 会在 POST /seed_session 时设置会话 cookie。
- verify() 是评分器。读取 body.ground_truth(由训练器传入)和 body.response.output(轨迹)。返回 BaseVerifyResponse(**body.model_dump(), reward=...)。
2. NeMo Gym 配置 — configs/<name>.yaml
my_env_resources_server:
resources_servers:
my_env:
entrypoint: server.py
domain: agent
description: "What this env does"
这是通过 ng_run "+config_paths=[configs/my_env.yaml]" 启动时,NeMo Gym CLI 查找的文件。
3. Rollout — rollout.py
NeMo Gym 没有 Python 客户端 SDK。rollout 通过 requests 直接发起原始 HTTP 请求,并使用 Session 保持 cookie:
import requests
session = requests.Session()
session.post(f"{ENV_URL}/seed_session", json={}).raise_for_status()
r = session.post(f"{ENV_URL}/guess", json={"word": "crane"})
result = r.json()["output"]
面向 LLM 的工具定义在 rollout.py 中硬编码(没有 introspect 端点)。需要与 server.py 中的请求 schema 完全镜像一致。
4. Dockerfile
多阶段构建。NeMo Gym 会拉入 Ray 以及相当重的依赖栈,Docker 镜像约 1.5GB。容器默认暴露端口 11000。部署到 HF Spaces 时,需覆盖为端口 7860(Spaces 限制单端口)。
验证关卡
- 导入:uv run python -c "import os; os.environ.setdefault('E2B_API_KEY','x'); from server import MyResourcesServer" 成功。
- 本地服务器:尝试 uv run python server.py。注意:NeMo Gym 的 run_webserver() 会初始化 Ray 集群,在共享 SLURM / HF 集群节点上会失败(gcs_server 无法绑定)。在这些机器上,只能通过 Docker / HF Space 部署。
- 端点冒烟测试:运行时,curl http://localhost:11000/seed_session -X POST 返回 200 并设置会话 cookie。
- Rollout:MAX_TURNS=3 uv run python rollout.py 对已部署的 Space 端到端运行。
常见坑
- No module named 'anyio':nemo_gym 并非每次安装都会固定其完整传递依赖集。需在依赖中显式添加 anyio>=4.0、attrs>=23.0、fastapi>=0.115、uvicorn、requests。
- Address already in use 或 gcs_server 崩溃:Ray 初始化失败,几乎总是共享集群问题。记录并告知用户通过 Space 部署。
- rollout 未设置 cookie:确保使用 requests.Session(),而不是裸的 requests.post()。会话 cookie 是 SID 句柄。
- /verify 意外返回 reward 0:ground_truth 被包在列表中。检查 body.ground_truth[0].get("expected_output"),而不是 body.ground_truth.get(...)。
- 硬编码工具 schema 漂移:当更改 server 端点的 Pydantic body 时,需手动更新 rollout.py 中匹配的工具定义。没有 list_tools()。
参考
- references/architecture.md — Ray 编排、含 responses_create_params 的数据集格式、部署说明
官方文档
- NVIDIA-NeMo/Gym — 源码仓库
- NeMo Gym latest docs
- Creating a Resource Server tutorial
- Creating a Training Environment tutorial
限制
除上述内容外,关于该技能的收费、价格、平台支持范围、效果表现等事实暂未提供。