# Magistral Reasoning Rl

> Build reasoning capabilities through pure RL without distilled traces, achieving 50% AIME accuracy improvement via scalable asynchronous training with novel reward shaping for multilingual consistency.

- Skill: `adu2021/magistral-reasoning-rl` (Agent Skill)
- Install (CLI): `npx skillmds@latest add adu2021/magistral-reasoning-rl`
- Raw SKILL.md: https://api.skillmd.com/api/skills/adu2021/magistral-reasoning-rl/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: adu2021 (https://skillmd.com/u/adu2021)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/adu2021/magistral-reasoning-rl

---


# Magistral: Scaling Reasoning with Reinforcement Learning

## Core Concept

Magistral demonstrates that pure reinforcement learning (without cold-start distilled reasoning traces) achieves strong reasoning through carefully designed rewards and scalable infrastructure. The system improves AIME-24 pass@1 accuracy by nearly 50% using a modified GRPO algorithm, asynchronous weight updates, and multi-stage curriculum learning. Novel insights: RL improves smaller models beyond distillation baselines and unexpectedly enhances multimodal capabilities despite text-only training.

## Architecture Overview

- **Modified GRPO Algorithm**: Five adaptations including eliminated KL divergence penalty, loss normalization across generation groups, advantage normalization, relaxed clipping (Clip-Higher), and zero-variance filtering
- **Four-Component Reward Architecture**: Formatting (think tags), correctness (verified answers/test passing), length penalty (soft constraints), language consistency (multilingual response matching)
- **Asynchronous Infrastructure**: Continuous weight updates without interrupting generators; NCCL broadcast under 5 seconds; GPU-to-GPU communication minimized
- **Multi-Stage Curriculum**: Difficulty progression across stages; batch size reduction (8k→4k→2k); completion length increases (16k→24k→32k tokens)
- **Data Curation Pipeline**: Mathematical problems filtered from 699k to 38k; code problems validated at 35k with comprehensive test suites

## Implementation

### Step 1: Modified GRPO Optimizer Setup

```python
import torch
import torch.nn as nn
from torch.optim import Optimizer

class ModifiedGRPO(Optimizer):
    """
    Group Relative Policy Optimization with Magistral adaptations:
    - Eliminated KL divergence penalty
    - Loss normalization across generation groups
    - Advantage normalization at minibatch level
    - Relaxed upper clipping (Clip-Higher strategy)
    - Zero-variance group filtering
    """

    def __init__(self, params, lr=1e-5, epsilon=1e-6,
                 clip_ratio=2.0, clip_lower=0.5):
        defaults = dict(lr=lr, epsilon=epsilon,
                       clip_ratio=clip_ratio, clip_lower=clip_lower)
        super().__init__(params, defaults)

    def step(self, closure=None):
        loss = None
        if closure is not None:
            loss = closure()

        for group in self.param_groups:
            for p in group['params']:
                if p.grad is None:
                    continue

                grad = p.grad.data

                # Clip-Higher strategy: relaxed upper bound
                # clip_ratio acts as soft upper bound, not hard limit
                if group['clip_ratio'] is not None:
                    # Allow exploration beyond standard PPO bounds
                    grad_magnitude = torch.norm(grad)
                    if grad_magnitude > group['clip_ratio']:
                        grad = grad / (grad_magnitude / group['clip_ratio'])

                p.data.add_(grad, alpha=-group['lr'])

        return loss
```

### Step 2: Reward Shaping Architecture

```python
class RewardShaper:
    """
    Four-component reward function for reasoning tasks.
    Combines formatting, correctness, length penalty, and language consistency.
    """

    def __init__(self, language_code='en', length_target=2000):
        self.language_code = language_code
        self.length_target = length_target
        self.code_executor = CodeExecutor()
        self.answer_verifier = AnswerVerifier()

    def compute_reward(self, generated_text, reference_answer=None,
                      user_language='en', is_code=False):
        """
        Compute total reward from four components.
        Returns scalar reward in [0, 1] range.
        """
        rewards = {}

        # Component 1: Formatting reward
        has_think_tags = '<think>' in generated_text and '</think>' in generated_text
        rewards['formatting'] = 0.25 if has_think_tags else 0.0

        # Component 2: Correctness reward
        if is_code:
            passed_tests = self.code_executor.run_tests(generated_text)
            rewards['correctness'] = min(passed_tests / 10, 1.0) * 0.50  # Up to 50%
        else:
            is_correct = self.answer_verifier.verify(generated_text, reference_answer)
            rewards['correctness'] = 0.50 if is_correct else 0.0

        # Component 3: Length penalty (soft constraint)
        response_length = len(generated_text.split())
        length_penalty = 1.0 - abs(response_length - self.length_target) / (2 * self.length_target)
        length_penalty = max(0, length_penalty)
        rewards['length'] = length_penalty * 0.15  # Up to 15%

        # Component 4: Language consistency
        response_language = self._detect_language(generated_text)
        language_bonus = 0.10 if response_language == user_language else 0.0
        rewards['language'] = language_bonus

        total_reward = sum(rewards.values())

        return {
            'total': min(total_reward, 1.0),
            'breakdown': rewards
        }

    def _detect_language(self, text):
        """Detect primary language of generated text."""
        # Simplified: check for non-ASCII patterns
        if any(ord(c) > 127 for c in text):
            return 'non-en'
        return 'en'
```

### Step 3: Asynchronous Training Infrastructure

```python
class AsynchronousTrainer:
    """
    Manages continuous weight updates without interrupting generators.
    Key innovation: GPU-to-GPU NCCL broadcast completes in <5 seconds.
    """

    def __init__(self, model, reward_shaper, num_generators=8):
        self.model = model
        self.reward_shaper = reward_shaper
        self.num_generators = num_generators
        self.model_queue = []
        self.latest_weights = None

    def async_generation_loop(self, generator_id, input_batch):
        """
        Generator continuously produces samples without blocking on updates.
        Receives periodic weight synchronization via broadcast.
        """
        local_model = copy.deepcopy(self.model)

        while True:
            # Generate samples with current local weights
            with torch.no_grad():
                outputs = local_model.generate(
                    input_batch,
                    max_length=2048,
                    temperature=1.0,
                    do_sample=True,
                    num_return_sequences=4
                )

            # Non-blocking weight sync: receive latest model via NCCL
            if self.latest_weights is not None:
                # GPU-to-GPU broadcast: <5 seconds
                local_model.load_state_dict(self.latest_weights, non_blocking=True)

            yield outputs

    def update_weights(self, loss_tensor):
        """
        Optimizer step updates weights and broadcasts to all generators.
        Does not block generation pipeline.
        """
        self.optimizer.zero_grad()
        loss_tensor.backward()

        # Apply gradient clipping
        torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=1.0)
        self.optimizer.step()

        # Broadcast updated weights to all generators asynchronously
        self.latest_weights = dict(self.model.named_parameters())
```

### Step 4: Multi-Stage Curriculum Learning

```python
class CurriculumScheduler:
    """
    Manages difficulty progression and training configuration across stages.
    Increases challenge: batch size reduction, completion length increase.
    """

    def __init__(self, num_stages=3):
        self.num_stages = num_stages
        self.current_stage = 0

        self.stage_config = [
            {'batch_size': 8192, 'max_length': 16000, 'difficulty': 'easy'},
            {'batch_size': 4096, 'max_length': 24000, 'difficulty': 'medium'},
            {'batch_size': 2048, 'max_length': 32000, 'difficulty': 'hard'}
        ]

    def get_current_config(self):
        return self.stage_config[min(self.current_stage, len(self.stage_config) - 1)]

    def progress_stage(self, val_accuracy):
        """Advance to next stage based on validation performance."""
        if val_accuracy > 0.7 and self.current_stage < self.num_stages - 1:
            self.current_stage += 1
            print(f"Curriculum progression: stage {self.current_stage}")
```

## Practical Guidance

**Data Preparation**:
- Mathematical problems: Collect diverse sources (competition, textbooks); validate with multiple verifiers (SymPy, WolframAlpha)
- Code problems: Use comprehensive test suites (50+ test cases minimum); separate train/test data carefully
- Filtering strategy: Remove ambiguous or duplicate problems; keep diverse difficulty spectrum

**Reward Design Principles**:
- Formatting reward (25%): Enforce think tags for chain-of-thought; strong signal during early training
- Correctness reward (50%): Most important component; use automated verification (answer matching or test execution)
- Length penalty (15%): Soft constraint; encourage natural length without hard cutoffs
- Language consistency (10%): Multilingual support; bonus when response matches user's language

**Scaling Considerations**:
- Batch size correlation: Larger batches enable stable advantage normalization; 8k-16k optimal
- Asynchronous updates: Maximum 10-second intervals between weight syncs; generators may use slightly stale weights
- GPU utilization: 8-16 generator GPUs + 2-4 optimizer GPUs; communication bandwidth saturates around 16 generators

**When to Use Magistral Approach**:
- Building reasoning from scratch without distillation data
- Multilingual reasoning models (language consistency reward handles this)
- Long-context reasoning (curriculum handles up to 32k tokens)
- Smaller models (<25B) where RL outperforms distillation

## Reference

- Group Relative Policy Optimization (GRPO): advantage normalization improves stability in RL for generation
- NCCL all-reduce: collective communication primitive; broadcast is unidirectional variant
- Curriculum learning: gradual difficulty progression prevents early convergence to local optima

