Skip to content
Back to skills

Light If Preview Checking

ASecurity

Multi-stage training approach using entropy-preserving SFT and token-wise entropy-adaptive RL to improve instruction adherence. Combines data curation with reward-guided reasoning, outperforming larger models on IFEval.

  • 6 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 9, 2026
code-qualitypythongogitperformance

Works with

  • cli

Security analysis

A100/100

Scanned September 9, 2026

npx -y skills add ADu2021/skillXiv --skill light-if-preview-checking --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Light If Preview Checking?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Light If Preview Checking
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/adu2021-light-if-preview-checking/badge)](https://www.skillsdirectory.com/skills/adu2021-light-if-preview-checking)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: light-if-preview-checking
title: Light-IF - Preview and Self-Checking for Instruction Following
version: 0.0.2
engine: skillxiv-v0.0.2-claude-opus-4.6
license: MIT
url: https://arxiv.org/abs/2508.03178
keywords: [instruction-following, self-checking, reasoning, data-curation]
description: "Multi-stage training approach using entropy-preserving SFT and token-wise entropy-adaptive RL to improve instruction adherence. Combines data curation with reward-guided reasoning, outperforming larger models on IFEval."
---

# Light-IF: Preview and Self-Checking for Instruction Following

## Core Concept

Light-IF addresses poor instruction adherence in language models by identifying the root cause: lazy reasoning during the thinking stage. The framework uses a multi-stage approach combining carefully curated data with entropy-adaptive reinforcement learning. By teaching models to preview instructions and self-check their work, the approach achieves strong instruction following without massive scale.

## Architecture Overview

- **Data Curation**: Complex instructions filtered into hard/easy/pass categories with rejection sampling
- **Entropy-Preserving SFT**: Supervised fine-tuning that maintains reasoning diversity
- **Token-wise Entropy-Adaptive RL (TEA-RL)**: Reinforcement learning guided by token-level entropy signals
- **Self-Checking Mechanism**: Models verify their own instruction adherence before finalizing outputs
- **Preview Stage**: Explicit instruction parsing and planning before response generation

## Implementation Steps

### Step 1: Curate Complex Instruction Dataset

Build high-quality dataset by filtering instructions by complexity and verifying solutions.

```python
from typing import List, Dict, Tuple
import random

class InstructionCurator:
    """
    Curate complex instruction dataset with difficulty categorization.
    """

    def __init__(self, classifier_model):
        self.classifier = classifier_model
        self.curated_dataset = []

    def filter_instructions(self, raw_instructions, solutions):
        """
        Filter instructions into difficulty categories.

        Args:
            raw_instructions: List of instruction strings
            solutions: List of (instruction_idx, solution) pairs

        Returns:
            Categorized dataset with hard/easy/pass labels
        """
        categorized = {"hard": [], "easy": [], "pass": []}

        for instruction, solution in zip(raw_instructions, solutions):
            # Analyze instruction complexity
            complexity_score = self._estimate_complexity(instruction)

            # Verify solution correctness via rejection sampling
            solution_quality = self._verify_solution(instruction, solution)

            if solution_quality < 0.5:
                # Solution doesn't properly follow instruction
                category = "pass"
            elif complexity_score > 0.7:
                # Complex instruction with good solution
                category = "hard"
            else:
                # Simple or medium instruction
                category = "easy"

            categorized[category].append({
                "instruction": instruction,
                "solution": solution,
                "complexity": complexity_score,
                "quality": solution_quality
            })

        return categorized

    def _estimate_complexity(self, instruction: str) -> float:
        """
        Estimate instruction complexity on 0-1 scale.

        Args:
            instruction: Instruction text

        Returns:
            Complexity score
        """
        # Multiple complexity heuristics
        complexity_signals = []

        # Signal 1: Instruction length
        length_score = min(len(instruction.split()), 100) / 100.0
        complexity_signals.append(length_score * 0.2)

        # Signal 2: Constraint count
        constraints = instruction.count("must") + instruction.count("cannot") + instruction.count("should")
        constraint_score = min(constraints, 5) / 5.0
        complexity_signals.append(constraint_score * 0.3)

        # Signal 3: Nested structure depth
        nesting = instruction.count("[") + instruction.count("(")
        nesting_score = min(nesting, 4) / 4.0
        complexity_signals.append(nesting_score * 0.3)

        # Signal 4: Semantic complexity (via model)
        model_complexity = self.classifier.predict_complexity(instruction)
        complexity_signals.append(model_complexity * 0.2)

        return sum(complexity_signals)

    def _verify_solution(self, instruction: str, solution: str) -> float:
        """
        Verify solution adherence to instruction.

        Args:
            instruction: Original instruction
            solution: Proposed solution

        Returns:
            Quality score 0-1 indicating instruction adherence
        """
        # Verify key requirements are met
        adherence_checks = []

        # Parse instruction constraints
        constraints = self._parse_constraints(instruction)

        for constraint in constraints:
            satisfied = self._check_constraint(solution, constraint)
            adherence_checks.append(satisfied)

        if not adherence_checks:
            return 1.0  # No constraints found, assume good

        quality = sum(adherence_checks) / len(adherence_checks)

        return quality

    def _parse_constraints(self, instruction: str) -> List[str]:
        """Extract constraints from instruction."""
        import re
        # Simple extraction; improve with NLP in practice
        must_pattern = r"(?:must|should|required to) ([^.!?]+)"
        constraints = re.findall(must_pattern, instruction)
        return constraints

    def _check_constraint(self, solution: str, constraint: str) -> bool:
        """Check if solution satisfies constraint."""
        # Implement constraint checking logic
        return constraint.lower() in solution.lower()

    def apply_rejection_sampling(self, dataset: Dict, target_quality: float = 0.8):
        """
        Apply rejection sampling to improve dataset quality.

        Args:
            dataset: Curated dataset
            target_quality: Target quality threshold

        Returns:
            Filtered dataset meeting quality threshold
        """
        filtered = {"hard": [], "easy": [], "pass": []}

        for category, examples in dataset.items():
            for example in examples:
                if example["quality"] >= target_quality:
                    filtered[category].append(example)

        return filtered
```

### Step 2: Implement Entropy-Preserving Supervised Fine-Tuning

Fine-tune with SFT while maintaining diversity in reasoning patterns.

```python
class EntropyPreservingSFT:
    """
    SFT that preserves reasoning diversity through entropy constraints.
    """

    def __init__(self, model, entropy_weight=0.1):
        self.model = model
        self.entropy_weight = entropy_weight

    def compute_entropy_loss(self, model_outputs: torch.Tensor) -> torch.Tensor:
        """
        Compute entropy regularization to prevent mode collapse.

        Args:
            model_outputs: Model logits [batch, seq_len, vocab_size]

        Returns:
            Entropy regularization loss
        """
        # Compute probability distribution
        probs = torch.softmax(model_outputs, dim=-1)

        # Compute entropy
        entropy = -torch.sum(probs * torch.log(probs + 1e-10), dim=-1)

        # Target: maintain minimum entropy to preserve diversity
        min_target_entropy = 2.0  # Roughly 2 nats for diverse outputs

        entropy_loss = torch.clamp(min_target_entropy - entropy.mean(), min=0)

        return entropy_loss

    def sft_step(self, batch_instructions: List[str], batch_solutions: List[str]):
        """
        Perform SFT step with entropy preservation.

        Args:
            batch_instructions: Batch of instructions
            batch_solutions: Batch of correct solutions

        Returns:
            Loss metrics
        """
        # Prepare input-output pairs
        inputs = [f"Instruction: {inst}\nSolution:" for inst in batch_instructions]

        # Forward pass
        outputs = self.model(inputs)
        logits = outputs.logits

        # Compute standard SFT loss
        sft_loss = self.model.compute_language_modeling_loss(outputs, batch_solutions)

        # Compute entropy loss
        entropy_loss = self.compute_entropy_loss(logits)

        # Combined loss
        total_loss = sft_loss + self.entropy_weight * entropy_loss

        # Backward pass
        total_loss.backward()
        self.model.optimizer.step()
        self.model.optimizer.zero_grad()

        return {
            "sft_loss": sft_loss.item(),
            "entropy_loss": entropy_loss.item(),
            "total_loss": total_loss.item()
        }

    def train_epoch(self, dataset: List[Dict], batch_size: int = 32):
        """
        Train for one epoch.

        Args:
            dataset: Training dataset with instruction-solution pairs
            batch_size: Batch size for training

        Returns:
            Average loss for epoch
        """
        total_loss = 0
        num_batches = 0

        for batch_start in range(0, len(dataset), batch_size):
            batch = dataset[batch_start:batch_start + batch_size]

            instructions = [ex["instruction"] for ex in batch]
            solutions = [ex["solution"] for ex in batch]

            metrics = self.sft_step(instructions, solutions)
            total_loss += metrics["total_loss"]
            num_batches += 1

        return total_loss / num_batches
```

### Step 3: Implement Token-wise Entropy-Adaptive RL

Create RL system that adapts learning by token-level entropy signals.

```python
class TokenwiseEntropyAdaptiveRL:
    """
    RL with token-wise entropy-adaptive reward scaling.
    """

    def __init__(self, model, instruction_reward_fn):
        self.model = model
        self.reward_fn = instruction_reward_fn

    def compute_token_entropy(self, logits: torch.Tensor) -> torch.Tensor:
        """
        Compute entropy at each token position.

        Args:
            logits: Model logits [batch, seq_len, vocab_size]

        Returns:
            Token-level entropy [batch, seq_len]
        """
        probs = torch.softmax(logits, dim=-1)
        entropy = -torch.sum(probs * torch.log(probs + 1e-10), dim=-1)
        return entropy

    def compute_adaptive_rewards(
        self,
        instruction: str,
        solution: str,
        logits: torch.Tensor,
        tokens: List[int]
    ) -> torch.Tensor:
        """
        Compute token-wise rewards adapted by entropy.

        Args:
            instruction: Input instruction
            solution: Generated solution
            logits: Model logits during generation
            tokens: Generated token IDs

        Returns:
            Token-wise rewards [seq_len]
        """
        # Compute instruction adherence reward
        adherence_reward = self.reward_fn.compute_adherence(instruction, solution)

        # Compute token entropy
        token_entropy = self.compute_token_entropy(logits)

        # Adaptive scaling: higher entropy tokens learn more
        entropy_scale = 1.0 + (token_entropy - token_entropy.mean()) / (token_entropy.std() + 1e-6)

        # Scale rewards by entropy
        adaptive_rewards = adherence_reward * entropy_scale.squeeze()

        return adaptive_rewards

    def rl_training_step(
        self,
        instructions: List[str],
        policy_samples: List[Dict],  # Contains generated solutions and logits
        learning_rate: float = 1e-5
    ):
        """
        Perform RL training step with token-wise entropy adaptation.

        Args:
            instructions: Batch of instructions
            policy_samples: Samples from model with associated logits
            learning_rate: Learning rate for optimizer

        Returns:
            Training metrics
        """
        total_policy_loss = 0
        num_samples = 0

        for instruction, sample in zip(instructions, policy_samples):
            solution = sample["solution"]
            logits = sample["logits"]
            log_probs = sample["log_probs"]

            # Compute adaptive rewards
            token_rewards = self.compute_adaptive_rewards(
                instruction,
                solution,
                logits,
                sample["tokens"]
            )

            # REINFORCE loss with adaptive rewards
            # Loss = -sum(log_probs * rewards)
            policy_loss = -(log_probs * token_rewards).sum()

            total_policy_loss += policy_loss
            num_samples += 1

        # Average and optimize
        avg_loss = total_policy_loss / max(num_samples, 1)

        avg_loss.backward()
        torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=1.0)

        self.model.optimizer.step()
        self.model.optimizer.zero_grad()

        return {
            "policy_loss": avg_loss.item(),
            "avg_adherence": token_rewards.mean().item()
        }
```

### Step 4: Implement Preview and Self-Checking

Add explicit preview and verification stages to instruction following.

```python
def generate_with_preview_and_checking(
    model,
    instruction: str,
    max_attempts: int = 3
) -> Tuple[str, bool]:
    """
    Generate response with preview and self-checking stages.

    Args:
        model: Language model
        instruction: User instruction
        max_attempts: Maximum refinement attempts

    Returns:
        (generated_response, meets_instruction_check)
    """
    # Stage 1: Preview - parse and plan
    preview_prompt = f"""
    Instruction: {instruction}

    Before responding, preview what this instruction requires:
    1. Main requirement:
    2. Key constraints:
    3. Output format:

    Preview:
    """

    preview = model.generate(preview_prompt, max_length=200)

    # Stage 2: Generate initial response
    generation_prompt = f"""
    Instruction: {instruction}

    Keep in mind the requirements:
    {preview}

    Response:
    """

    response = model.generate(generation_prompt, max_length=500)

    # Stage 3: Self-check - verify adherence
    check_prompt = f"""
    Instruction: {instruction}
    Response: {response}

    Does this response properly follow the instruction? Check:
    1. Does it meet the main requirement?
    2. Are all constraints satisfied?
    3. Is the output format correct?

    If any answer is NO, the response fails the check.
    Final verdict: PASS or FAIL
    """

    check_result = model.generate(check_prompt, max_length=100)

    passes_check = "PASS" in check_result.upper()

    # Stage 4: Refinement if needed
    attempts = 1
    while not passes_check and attempts < max_attempts:
        refinement_prompt = f"""
        Original instruction: {instruction}
        Previous response: {response}
        Check feedback: {check_result}

        Please refine the response to address the issues identified.
        Refined response:
        """

        response = model.generate(refinement_prompt, max_length=500)

        # Re-check
        check_result = model.generate(check_prompt.replace(response, response), max_length=100)

        passes_check = "PASS" in check_result.upper()

        attempts += 1

    return response, passes_check
```

## Practical Guidance

### When to Use Light-IF

- **Instruction following benchmarks**: IFEval, MTEval, complex prompt scenarios
- **Few-shot instruction learning**: Limited data for specific instruction patterns
- **Quality-focused applications**: Where correct instruction adherence is critical
- **Model scaling constraints**: Achieving strong performance without massive models

### When NOT to Use Light-IF

- **Simple instruction tasks**: Standard prompting may suffice
- **Real-time generation**: Multi-stage preview and checking adds latency
- **Minimal data availability**: Curation requires reasonable dataset size
- **Open-ended generation**: Self-checking works best with verifiable instruction requirements

### Hyperparameter Recommendations

- **Entropy weight in SFT**: 0.05-0.15 (balance diversity vs. instruction adherence)
- **Token entropy threshold**: 2.0 nats for diversity maintenance
- **RL learning rate**: 1e-5 to 5e-5 (conservative for stability)
- **Self-check attempts**: 2-3 (diminishing returns beyond)
- **Data quality threshold (rejection sampling)**: 0.75-0.85

### Key Insights

The critical insight is that instruction-following failures stem from lazy reasoning, not model incompetence. By making models explicitly preview instructions and self-check outputs, the approach forces genuine reasoning about requirements. Token-wise entropy adaptation prevents collapse to single reasoning patterns while focusing learning on uncertain tokens.

## Reference

**Light-IF: Preview and Self-Checking for Instruction Following** (arXiv:2508.03178)

Introduces entropy-preserving SFT and token-wise entropy-adaptive RL for instruction following. Outperforms larger models through systematic data curation, diverse reasoning, and self-checking mechanisms.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.