TRL documentation

Интеграция OpenReward для обучения LLM с использованием сред

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v1.18.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Интеграция OpenReward для обучения LLM с использованием сред

OpenReward — это открытая экосистема для сред RL, построенная на Open Reward Standard (ORS) — публичном, независимом от языка протоколе HTTP/SSE, определяющем, как среда предоставляет свои задачи, инструменты, сессии и вознаграждения. Поскольку ORS представляет собой просто протокол, одна и та же среда может работать на платформе OpenReward, быть развернута самостоятельно на любом контейнерном сервисе или локально на localhost для разработки. Каталог готовых к использованию сред доступен на openreward.ai.

В этом руководстве описано, как интегрировать OpenReward с TRL. Подробнее о самом стандарте см. в документации ORS.

Интеграция находится в trl.experimental.openreward и доступна через дополнительный пакет trl[openreward] (импортируется лениво — пользователи, не использующие её, не несут никаких затрат).

Когда использовать среды OpenReward

GRPOTrainer поддерживает обучение на основе сред через слот environment_factory — см. OpenEnv для общего контракта. Используйте OpenReward, если хотите обучать модель в среде, говорящей на ORS: это может быть среда из каталога OpenReward (например, Eigent/SETA, kanishk/EndlessTerminals, nebius/SWE-rebench-V2), среда, размещённая на вашей собственной инфраструктуре, или локальный сервер, который вы разрабатываете.

Установка

pip install trl[openreward]

Эта команда устанавливает Python SDK openreward. Сама интеграция импортирует openreward лениво, поэтому пользователи, которые не используют trl.experimental.openreward, не затрагиваются.

Быстрый старт

Класс OpenRewardSpec подключает одну среду ORS к трём слотам тренера TRL — train_dataset, environment_factory, reward_funcs — за счёт свойств, которые соответствуют этим именам аргументов в соотношении 1:1:

from trl import GRPOConfig, GRPOTrainer
from trl.experimental.openreward import OpenRewardSpec

spec = OpenRewardSpec("Eigent/SETA", num_tasks=64)

trainer = GRPOTrainer(
    model="Qwen/Qwen3-4B",
    args=GRPOConfig(
        num_generations=2,
        max_steps=5,
        max_tool_calling_iterations=20,
        log_completions=True,
    ),
    train_dataset=spec.train_dataset,
    environment_factory=spec.environment_factory,
    reward_funcs=spec.reward_funcs,
)
trainer.train()

Под капотом OpenRewardSpec выполняет три действия, отложенные до первого обращения:

  1. spec.train_dataset: формирует datasets.Dataset на основе списка задач окружения (один HTTP-запрос через SDK). Содержит как минимум prompt, task_index, а также столбцы метаданных для каждой задачи.
  2. spec.environment_factory: возвращает вызываемый объект без аргументов, который при каждом вызове создаёт новый адаптер для конкретного rollout. Адаптер предоставляет один Python-метод на каждый инструмент ORS, с типизированной сигнатурой и docstring, автоматически генерируемыми из JSON Schema окружения. Коллектор инструментов TRL обнаруживает их с помощью inspect.getmembers.
  3. spec.reward_funcs: функция вознаграждения, основанная только на результате (последнее ненулевое вознаграждение в траектории), подходящая для сред с разреженным вознаграждением, таких как SETA.

Использование среды хаб

Передайте имя каталога openreward.ai в качестве цели. SDK считывает OPENREWARD_API_KEY из переменных окружения для аутентификации.

spec = OpenRewardSpec("Eigent/SETA", num_tasks=64)

Использование собственного окружения

Передайте URL напрямую. API-ключ не требуется, если ваш сервер не требует его для доступа.

spec = OpenRewardSpec("https://huggingface.co/proxy/my-org-my-env.hf.space", env_name="my_env")

SDK openreward по умолчанию ожидает платформу с двухсубдоменным расположением (api.<host> для вызовов без состояния и sessions.<host> для вызовов сессий на основе SSE). Для одиночных self-hosted серверов (один URL для всех запросов) задайте переменные окружения, перечисленные ниже, до создания OpenRewardSpec:

import os

URL = "https://huggingface.co/proxy/my-org-my-env.hf.space"
os.environ["OPENREWARD_API_URL"]     = URL
os.environ["OPENREWARD_SESSION_URL"] = URL

spec = OpenRewardSpec(URL, env_name="my_env")

Запуск минимального окружения локально

Самый быстрый способ протестировать интеграцию end-to-end без внешних зависимостей — это небольшой ORS-сервер, определённый с помощью каркаса Environment + Server из SDK openreward. Пример ниже представляет собой полную echo-среду — модель побеждает, вызывая echo(text=...) с целевой строкой задачи.

# server.py
from pydantic import BaseModel
from openreward.environments import Environment, JSONObject, Server, TextBlock, ToolOutput, tool


class EchoTaskSpec(BaseModel):
    target: str

class EchoParams(BaseModel):
    text: str


class EchoEnvironment(Environment):
    def __init__(self, task_spec: JSONObject = {}, secrets: dict[str, str] = {}):
        super().__init__(task_spec)
        self.config = EchoTaskSpec.model_validate(task_spec)

    @classmethod
    def list_splits(cls) -> list[str]:
        return ["train"]

    @classmethod
    def list_tasks(cls, split: str) -> list[JSONObject]:
        return [{"target": "hello"}, {"target": "world"}]

    def get_prompt(self) -> list[TextBlock]:
        return [TextBlock(type="text", text=f"Echo '{self.config.target}' to win.")]

    @tool
    async def echo(self, params: EchoParams) -> ToolOutput:
        """Submit a string. Reward 1.0 + finished if it matches the target.

        Args:
            text: The string to echo back.
        """
        correct = params.text == self.config.target
        return ToolOutput(
            blocks=[TextBlock(type="text", text="match" if correct else "no match")],
            reward=1.0 if correct else 0.0,
            finished=correct,
        )


if __name__ == "__main__":
    Server([EchoEnvironment]).run(host="0.0.0.0", port=8000)

Запустите:

pip install openreward fastapi uvicorn pydantic
python server.py     # listens on :8000

Затем укажите OpenRewardSpec на него (с переопределениями URL, описанными выше):

import os
URL = "http://127.0.0.1:8000"
os.environ["OPENREWARD_API_URL"]     = URL
os.environ["OPENREWARD_SESSION_URL"] = URL

from trl.experimental.openreward import OpenRewardSpec
spec = OpenRewardSpec(URL, env_name="echoenvironment")
print(spec.train_dataset)        # 2 rows, task_index + target columns

Этот же шаблон фикстур используется в собственных тестах TRL — см. trl-internal-testing/openreward-echo-env для развернутого Space.

Выбор задач

OpenRewardSpec принимает либо количество, либо явный список индексов:

spec = OpenRewardSpec("Eigent/SETA", num_tasks=10)                      # first 10 tasks
spec = OpenRewardSpec("Eigent/SETA", indices=[0, 5, 13, 27])            # specific indices
spec = OpenRewardSpec("Eigent/SETA", indices=list(range(50, 100)))      # range

num_tasks и indices взаимоисключающие и оба извлекают только необходимые задачи (без полного сканирования списка задач).

Как работает привязка инструментов

При создании spec вызывает эндпоинт /tools окружения, чтобы получить список спецификаций инструментов (каждая из которых содержит имя, описание и JSON Schema для аргументов). Для каждого инструмента генерируется метод Python на адаптере для конкретного rollout с типизированной сигнатурой и docstring, выведенным из схемы. Методы устанавливаются на класс адаптера, где их находит сборщик инструментов TRL, поэтому transformers.utils.get_json_schema и inspect.getmembers(type(env), isfunction) TRL формируют правильную схему инструментов для модели без необходимости писать обёрточный код для каждого окружения.

Если описание инструмента содержит символы, небезопасные для вставки в исходный код Python, биндер переключается на санитизированную форму, чтобы связывание никогда не завершалось ошибкой в реальных окружениях.

Функции вознаграждения

spec.reward_funcs по умолчанию возвращает только итоговую награду — для каждого rollout он возвращает последнюю ненулевую награду, полученную в ходе траектории. Это правильный вариант по умолчанию для сред с разреженной наградой (например, SETA, где ненулевую награду возвращает только submit_solution).

Если ни один инструмент не возвращает ненулевое вознаграждение, функция вознаграждения по умолчанию возвращает None, а не 0.0. Это касается прогонов, в которых отсутствуют вызовы инструментов, присутствуют только вызовы инструментов без оценки или только неудачные вызовы инструментов. Наблюдаемое значение 0.0 или отрицательное вознаграждение по-прежнему является допустимой оценкой. Поздний вызов инструмента без оценки или неудачный вызов не стирает ранее присвоенную оценку.

GRPO исключает из базовой линии вознаграждения те развёртки, для которых все функции вознаграждения возвращают None, и присваивает им нулевое преимущество. Это не удаляет их токены из нормализации потерь и не отключает другие слагаемые, такие как регуляризация KL. Если другая функция вознаграждения оценивает развёртку, она всё ещё может получить ненулевое преимущество.

Отсутствие оценок не позволяет определить причину завершения эпизода. Если отказ от выполнения или достижение лимита вызовов инструментов следует считать неудачей задачи, возвращайте явную награду за неудачу из вашей среды или пользовательской функции вознаграждения. Сбои инфраструктуры или оценщика могут оставаться без оценки; при этом отслеживайте покрытие оценками наряду со средней наградой.

Если вам нужен собственный reward, напишите обычную reward-функцию TRL и передайте её напрямую:

def my_reward(environments, **kwargs) -> list[float | None]:
    rewards = spec.reward_funcs(environments=environments)
    return [2.0 * reward if reward is not None else None for reward in rewards]

trainer = GRPOTrainer(
    ...,
    reward_funcs=my_reward,
)

Адаптер для каждого rollout предоставляет TRL текущее состояние — env.reward, env.rewards, env.metadata, env.finished, env.last_output — для произвольного пост-хок формирования вознаграждения.

OpenRewardSpec

класс trl.experimental.openreward.OpenRewardSpec

< >

( target: strnum_tasks: int | None = Nonesplit: str = 'train'indices: list[int] | None = Noneapi_key: str | None = Nonesecrets: dict[str, str] | None = Noneenv_name: str | None = Noneinclude_metadata: bool = Truediscover_task_tools: bool = Truetask_tools_discovery_index: int | None = None )

Параметры

  • target (str) — Имя из каталога openreward.ai (“Eigent/SETA”) или URL, указывающий на любой сервер ORS (”https://huggingface.co/proxy/you-seta.hf.space”, “http://localhost:8080”). Определяется автоматически по наличию :// в строке.
  • num_tasks (int, optional) — Ограничение на количество задач, загружаемых в датасет. None использует все задачи, доступные в среде.
  • split (str, optional, по умолчанию “train”) — Список задач какого сплита использовать.
  • indices (list[int], optional) — Конкретные индексы задач для обучения. Взаимоисключается с num_tasks. Полезно для отладки или подмножеств по программе обучения.
  • api_key (str, необязательный) — переопределение OPENREWARD_API_KEY. Используется только в том случае, если target является именем каталога.
  • secrets (dict[str, str], необязательный) — Секреты для каждой сессии, передаваемые в env.session(secrets=).
  • env_name (str, optional) — Переопределение имени окружения для поиска на сервере. Редко требуется.
  • include_metadata (bool, необязательный, по умолчанию True) — Включает метаданные по каждой задаче (difficulty, category, tags, …) в строки набора данных, чтобы функции вознаграждения могли читать их через аргумент inputs TRL.
  • discover_task_tools (bool, optional, по умолчанию True) — Если True, открывается сессия ORS с коротким временем жизни и используется session.list_tools(), чтобы инструменты, специфичные для задачи (GET …/task_tools в ORS — например, @tool(shared=False) и list_task_tools()), были привязаны для GRPO. Если зондирование не удалось, используется только environment.list_tools(). Установите False, чтобы пропустить дополнительную сессию (только общие инструменты / особенности офлайн-режима).
  • task_tools_discovery_index (int, optional) — Индекс задачи, используемый только для сессии обнаружения, если задан; перекрывает многоиндексное зондирование, описанное ниже. Если опущен, а indices= задан, обнаружение открывает одну сессию зондирования для каждого уникального элемента в indices (в отсортированном порядке) и объединяет спецификации инструментов по имени, чтобы привязать специфичные для задачи инструменты из всех перечисленных задач. Если опущен и используется num_tasks / режим полного списка, зондируется только задача 0. Игнорируется, когда discover_task_tools=False.

Единый объект спецификации, который подключает среду ORS к тренеру TRL.

Ограничения

  • Интеграция находится в trl.experimental — API могут измениться. Установите TRL_EXPERIMENTAL_SILENCE=1, чтобы отключить предупреждение в логах CI.
  • В настоящее время поддерживается только один OpenRewardSpec, охватывающий одну среду; обучение в нескольких средах (по образцу паттерна «мета-среда» в OpenEnv) пока не поддерживается.
  • Долгие развёртывания (>15 мин на эпизод) требуют keepalive-пинга — пока не реализовано.

Справочник

Update on GitHub