From 3a97f06be131a5ddbe37a3472c93c2aa795fd0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=93=E9=9B=B7?= Date: Fri, 1 Aug 2025 11:49:30 +0800 Subject: [PATCH] add figure to explain AReaLite components --- README.md | 10 +++++----- arealite/README.md | 16 +++++++++++++--- assets/arealite_layers.png | Bin 0 -> 17515 bytes 3 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 assets/arealite_layers.png diff --git a/README.md b/README.md index 3c73302..cb7bbb7 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ like how you enjoy real-world milk tea (cheers). code. This allows users to build their own **agentic** and **RLVR** training workflows with minimal effort. - 🔥 **Asynchronous RL**: With algorithm-system co-design, AReaL supports fully - asynchronous RL for **the fastest training speed**! Experimental support for multi-turn - agentic RL is also provided. + asynchronous RL for **the fastest training speed**! Experimental support for + multi-turn agentic RL is also provided. - 🛠️ **Open & Reproducible**: We continuously release _all code, datasets, and training recipes_ for RL training of LLMs. - 🚀 **Scalability**: AReaL can seamlessly adapt to different computational resource @@ -69,7 +69,7 @@ New highlights in AReaLite: - Instead of the *system-centric* architecture in old AReaL, AReaLite follows an **AI-centric** API design that aims to provide the following key features: - - **Light-weight** & **easy to write** algorithm and training workflow customization. + - **Light-weight** & **easy-to-write** algorithm and training workflow customization. - **Easy to scale up** without knowing system and infrastructure details. - **Adaptable and plugable:** Smooth to integrate with other modern AI applications. @@ -106,8 +106,8 @@ Our training scripts will automatically download the dataset (openai/gsm8k) and python3 -m arealite.launcher.local examples/arealite/gsm8k_grpo.py --config examples/arealite/configs/gsm8k_grpo.yaml ``` -On a Ray cluster with 2 nodes & 8 GPUs each node, runs (Remember to change paths in the YAML -file to your own shared storage): +On a Ray cluster with 2 nodes & 8 GPUs each node, runs (Remember to change paths in the +YAML file to your own shared storage): ``` python3 -m arealite.launcher.ray examples/arealite/gsm8k_grpo.py --config examples/arealite/configs/gsm8k_grpo.yaml \ diff --git a/arealite/README.md b/arealite/README.md index d7b7696..028d651 100644 --- a/arealite/README.md +++ b/arealite/README.md @@ -12,9 +12,9 @@ understand, and develop with effectively. The primary issue stems from its *system-centric* rather than *AI-centric* architecture and API design. An *AI-centric* design aims to provide three key features: -- **Light-weight & focused customization:** Users can implement their algorithms and - training workflows with minimal and concentrated code, often in just a few files or - even a single file. +- **Light-weight & easy-to-write customization:** Users can implement their algorithms + and training workflows with minimal and concentrated code, often in just a few files + or even a single file. - **Easy to scale up:** Experiments can be scaled up seamlessly without requiring knowledge of underlying system or infrastructure details. - **Adaptable and plugable:** Users is free to integrate the system with code or APIs @@ -80,6 +80,16 @@ arealite/ ### Component Overview +The AReaLite codebase is structured into four distinct layers: the API layer, backend +layer, customization layer, and entry point layer. As illustrated in the figure below, +workflow and algorithm customization logic resides in separate layers above the backend. +We prioritize keeping the entry point and customization layers clean and intuitive, +isolating them from the complex backend implementation. With AReaLite, users can define +their custom training workflows and algorithms entirely within a single entry point +file. + +![arealite-layers](../assets/arealite_layers.png) + #### 1. API Layer (`api/`) The API layer establishes clean contracts between components through abstract interfaces diff --git a/assets/arealite_layers.png b/assets/arealite_layers.png new file mode 100644 index 0000000000000000000000000000000000000000..f57fdab3b56cdfcc22999ef9f964bb11f888090c GIT binary patch literal 17515 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