AntiAtropos / curriculum.py
div18
feat(curriculum): add progressive training curriculum management
52a986a
Raw
History Blame Contribute Delete
4.36 kB
"""
AntiAtropos Curriculum Training.
Defines progressive difficulty stages that the agent must pass before advancing.
Failed stages are retried with higher temperature for exploration.
Each stage specifies:
- task: Which task to run
- max_steps: Episode length (shorter = easier)
- pass_threshold: Minimum composite score to advance
- temperature: Suggest LLM temperature for this stage
- description: Human-readable label
"""
from dataclasses import dataclass
from typing import List, Optional
@dataclass
class CurriculumStage:
"""A single stage in the training curriculum."""
task: str
max_steps: int
pass_threshold: float
temperature: float = 0.0
description: str = ""
retries: int = 0 # Number of failed attempts so far
max_retries: int = 3 # Max retries before advancing anyway
@property
def retry_temperature(self) -> float:
"""Temperature increases with retries to encourage exploration."""
if self.retries == 0:
return self.temperature
# 0.3, 0.6, 0.9 on retries
return min(1.0, self.temperature + self.retries * 0.3)
@property
def should_skip(self) -> bool:
"""Skip this stage if too many retries."""
return self.retries >= self.max_retries
# Progressive curriculum: start easy, add complexity
CURRICULUM: List[CurriculumStage] = [
CurriculumStage(
task="task-1", max_steps=40, pass_threshold=0.40,
temperature=0.0, description="Short ramp β€” learn basic scaling",
),
CurriculumStage(
task="task-1", max_steps=60, pass_threshold=0.50,
temperature=0.0, description="Standard ramp β€” scale proactively",
),
CurriculumStage(
task="task-1", max_steps=100, pass_threshold=0.55,
temperature=0.0, description="Full ramp β€” cost-aware scaling",
),
CurriculumStage(
task="task-2", max_steps=40, pass_threshold=0.35,
temperature=0.0, description="Short fault β€” learn reroute/scale on failure",
),
CurriculumStage(
task="task-2", max_steps=60, pass_threshold=0.45,
temperature=0.3, description="Standard fault β€” fast recovery",
),
CurriculumStage(
task="task-3", max_steps=40, pass_threshold=0.35,
temperature=0.0, description="Short surge β€” protect VIP during spike",
),
CurriculumStage(
task="task-3", max_steps=60, pass_threshold=0.45,
temperature=0.3, description="Standard surge β€” sustained VIP protection",
),
# Final combined test
CurriculumStage(
task="task-1", max_steps=100, pass_threshold=0.55,
temperature=0.0, description="Final: full ramp at low temp",
),
CurriculumStage(
task="task-2", max_steps=60, pass_threshold=0.50,
temperature=0.0, description="Final: fault recovery at low temp",
),
CurriculumStage(
task="task-3", max_steps=60, pass_threshold=0.50,
temperature=0.0, description="Final: surge protection at low temp",
),
]
class CurriculumTracker:
"""Tracks progress through the curriculum stages."""
def __init__(self, stages: Optional[List[CurriculumStage]] = None):
self._stages = stages or CURRICULUM
self._current_idx: int = 0
@property
def current(self) -> CurriculumStage:
return self._stages[self._current_idx]
@property
def current_index(self) -> int:
return self._current_idx
@property
def total_stages(self) -> int:
return len(self._stages)
@property
def is_complete(self) -> bool:
return self._current_idx >= len(self._stages)
def report_score(self, score: float) -> bool:
"""Report a score for the current stage. Returns True if passed."""
if score >= self.current.pass_threshold:
self._current_idx += 1
return True
else:
self.current.retries += 1
if self.current.should_skip:
self._current_idx += 1
return False
def progress_summary(self) -> str:
stage = self.current
return (
f"Stage {self._current_idx + 1}/{self.total_stages}: "
f"{stage.description} "
f"(task={stage.task}, max_steps={stage.max_steps}, "
f"threshold={stage.pass_threshold}, retries={stage.retries})"
)