TRL documentation
Интеграция OpenReward для обучения LLM с использованием сред
Интеграция 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 выполняет три действия, отложенные до первого обращения:
spec.train_dataset: формируетdatasets.Datasetна основе списка задач окружения (один HTTP-запрос через SDK). Содержит как минимумprompt,task_index, а также столбцы метаданных для каждой задачи.spec.environment_factory: возвращает вызываемый объект без аргументов, который при каждом вызове создаёт новый адаптер для конкретного rollout. Адаптер предоставляет один Python-метод на каждый инструмент ORS, с типизированной сигнатурой и docstring, автоматически генерируемыми из JSON Schema окружения. Коллектор инструментов TRL обнаруживает их с помощьюinspect.getmembers.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))) # rangenum_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
< source >( 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, …) в строки набора данных, чтобы функции вознаграждения могли
читать их через аргумент
inputsTRL. - 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-пинга — пока не реализовано.
Справочник
- Открытый стандарт вознаграждений
- Платформа OpenReward
- Python SDK
openreward - Пространство Echo env —
trl-internal-testing/openreward-echo-env