SuperAPI AI 工具集

generate-nemo-gym-env

智能体与模型

查看项目

更新于 10-15 11:04

技能介绍

用途

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

限制

除上述内容外,关于该技能的收费、价格、平台支持范围、效果表现等事实暂未提供。