TRL documentation

تكامل OpenReward لتدريب نماذج اللغة الكبيرة مع بيئات

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

and get access to the augmented documentation experience

to get started

تكامل OpenReward لتدريب نماذج اللغة الكبيرة مع بيئات

OpenReward هو نظام بيئي مفتوح لبيئات التعلم المعزز (RL) مبني على المعيار المفتوح للمكافآت (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]

يقوم هذا الأمر بتثبيت حزمة 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: يُرجع دالة قابلة للاستدعاء بدون حُجج، تُنتج مُكيّفًا جديدًا لكل تمريرة عند كل استدعاء. يعرض المُكيّف طريقة بايثون واحدة لكل أداة ORS، مع توقيع مُنمّط وتعليق توثيقي مُولَّد تلقائيًا من مخطط JSON الخاص بالبيئة. يجمع جامع الأدوات في TRL هذه الطرق عبر inspect.getmembers.
  3. spec.reward_funcs: دالة مكافأة تعتمد على النتيجة فقط (المكافأة غير الفارغة الأخيرة في المسار)، ومناسبة لبيئات المكافآت المتناثرة مثل SETA.

استخدام بيئة مركز

مرّر اسم كتالوج من openreward.ai كهدف. تقرأ مجموعة أدوات المطوّر OPENREWARD_API_KEY من البيئة للمصادقة.

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

استخدام بيئة مستضافة ذاتياً

مرّر عنوان URL مباشرةً. لا حاجة إلى مفتاح API إذا لم يفرض خادمك ذلك.

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

يتوقع SDK الخاص بـ openreward افتراضياً تخطيط منصة مكوّناً من نطاق فرعيين (api.<host> للاستدعاءات غير الحالة وsessions.<host> لاستدعاءات الجلسات القائمة على SSE). بالنسبة لخوادم المضيف الواحد ذاتية الاستضافة (رابط واحد يقدّم كل شيء)، اضبط متغيرات البيئة التالية قبل إنشاء OpenRewardSpec:

import os

URL = "https://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")

تشغيل بيئة دنيا محلياً

أسرع طريقة لتجربة التكامل من البداية إلى النهاية دون اعتماد على مكتبات خارجية هي خادم 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

وهذا هو نمط البيانات التجريبية (fixture) المستخدم أيضًا في اختبارات 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 متنافيان، وكلاهما يسترجع فقط المهام التي يحتاجها (دون مسح كامل لقائمة المهام).

كيف يعمل ربط الأدوات

عند الإنشاء، يستدعي المواصفة نقطة النهاية /tools في البيئة لجلب قائمة مواصفات الأدوات (حيث تحتوي كل أداة على اسم ووصف ومخطط JSON Schema للحوامل). ولكل أداة، يولّد طريقة Python على مُكيّف كل تمريرة (per-rollout adapter) بتوقيع مُنمّط (typed signature) وسلسلة توثيق (docstring) مشتقة من المخطط. وتُضبط هذه الطرق على فئة المُكيّف، حيث يجدها جامع أدوات TRL، وبالتالي ينتج كل من transformers.utils.get_json_schema وinspect.getmembers(type(env), isfunction) الخاص بـ TRL مخطط الأداة الصحيح للنموذج دون الحاجة إلى أي كود غلاف (wrapper) لكل بيئة.

إذا احتوى وصف الأداة على أحرف غير آمنة للإدراج في مصدر Python، فإن المُربط (binder) يلجأ إلى شكل معقّم حتى لا يفشل الربط في البيئات الفعلية.

دوال المكافأة

spec.reward_funcs افتراضيًا مكافأة تعتمد على النتيجة فقط — لكل مسار يُرجع آخر مكافأة غير فارغة رُصدت خلال المسار. هذا هو الافتراضي الصحيح للبيئات ذات المكافآت المتناثرة (مثل SETA، حيث يُرجع submit_solution فقط مكافأة غير فارغة).

إذا لم تُرجِع أي أداة مكافأة غير فارغة، فإن دالة المكافأة الافتراضية تُرجع None، وليس 0.0. ويشمل ذلك عمليات التمرين التي لا تتضمن أي استدعاءات للأدوات، أو استدعاءات غير مُقيَّمة فقط، أو استدعاءات فاشلة فقط. تبقى المكافأة المرصودة 0.0 أو السالبة نتيجة صالحة. ولا يُمحو استدعاء أداة لاحق غير مُقيَّم أو فاشل نتيجة سابقة.

يستثني GRPO عمليات التوليد التي تُرجع جميع دوال المكافأة فيها قيمة None من خط الأساس الخاص بالمكافأة، ويمنحها ميزة تساوي صفراً. ولا يؤدي هذا إلى إزالة رموزها من تطبيع الدالة الخاسرة أو تعطيل مصطلحات أخرى مثل تنظيم KL. وإذا قيّمت دالة مكافأة أخرى عملية التوليد، فقد لا تزال تحصل على ميزة غير صفرية.

عدم وجود درجات لا يحدد سبب انتهاء الحلقة. إذا كان يجب اعتبار الاستسلام أو بلوغ حد استدعاء الأداة فشلاً في المهمة، فعُدّ مكافأة فشل صريحة من بيئتك أو من دالة المكافأة المخصصة. أما أعطال البنية التحتية أو المُقيِّم فيمكن أن تبقى غير قابلة للتقييم؛ راقب تغطية التقييم إلى جانب متوسط المكافأة.

إذا كنت ترغب في مكافأة مخصصة، فاكتب دالة مكافأة 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,
)

يكشف مُكيّف كل تمريرة عن الحالة التشغيلية التي يحتاجها TRL — env.reward، env.rewards، env.metadata، env.finished، env.last_output — لتشكيل المكافآت بعد التنفيذ بشكل تعسفي.

OpenRewardSpec

class 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://you-seta.hf.space”، “http://localhost:8080”). يتم اكتشافه تلقائيًا بناءً على وجود :// في النص.
  • num_tasks (int، اختياري) — الحد الأقصى لعدد المهام التي تُستخرج إلى مجموعة البيانات. استخدام None يعني تضمين جميع المهام التي يوفرها البيئة.
  • split (str، اختياري، الافتراضي “train”) — قائمة المهام الخاصة بأي تقسيم سيتم السحب منها.
  • indices (list[int]، اختياري) — فهارس المهام المحددة للتدريب عليها. يتعارض مع num_tasks. مفيد للتصحيح أو لمجموعات المنهج الفرعية.
  • api_key (str، اختياري) — تجاوز OPENREWARD_API_KEY. يُستخدم فقط عندما يكون target اسم كتالوج.
  • secrets (dict[str, str]، اختياري) — أسرار لكل جلسة تُمرَّر إلى env.session(secrets=).
  • env_name (str، اختياري) — تجاوز لاسم البيئة للبحث عنه على الخادم. نادرًا ما يكون مطلوبًا.
  • include_metadata (bool، اختياري، القيمة الافتراضية True) — دمج بيانات الوصف الخاصة بكل مهمة (difficulty، category، tags، …) في صفوف مجموعة البيانات بحيث يمكن لدوال المكافأة قراءتها عبر معامل inputs في TRL.
  • discover_task_tools (bool، اختياري، القيمة الافتراضية 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، اختياري) — مؤشر المهمة المستخدم فقط لجلسة الاكتشاف عند تعيينه؛ يتجاوز الاستكشاف متعدد المؤشرات أدناه. عند إهماله وتعيين indices=، تفتح عملية الاكتشاف جلسة استكشاف واحدة لكل عنصر مختلف في indices (مرتبة) وتدمج مواصفات الأدوات بالاسم بحيث تُربط الأدوات الخاصة بالمهام من كل مهمة مدرجة. عند إهماله واستخدام num_tasks / وضع القائمة الكاملة، تستكشف المهمة 0 فقط. يُتجاهل عند discover_task_tools=False.

كائن مواصفات واحد يربط بيئة ORS بمدرّب TRL.

القيود

  • التكامل موجود في trl.experimental — قد تتغير واجهات برمجة التطبيقات. اضبط TRL_EXPERIMENTAL_SILENCE=1 لكتم التحذير في سجلات CI.
  • يُعرِّف حاليًا كائن OpenRewardSpec واحدًا يغطي بيئة واحدة؛ ولا يُدعم التدريب متعدد البيئات (على غرار نمط "البيئة الفوقية" في OpenEnv) بعد.
  • تتطلب عمليات النشر طويلة الأمد (>15 دقيقة لكل حلقة) نبضة إبقاء نشط — لم يتم ربطها بعد.

مرجع

Update on GitHub