Track 3 · With BrewSLM · Lesson 5

Recipes and task handlers: the training config without the code

After this lesson you can read a recipe as the declarative form of your Track 2 TrainingArguments + LoraConfig, and explain how the task-handler dispatcher picks the handler that applies the right tokenization and scoring.

Level: intermediate Read time: ~10 min Prerequisites: Clean and prepare: the manifest is the source of truth

In lesson 2.5 you wrote a LoraConfig and a TrainingArguments in Python. A BrewSLM recipe is exactly that information as declarative config — the same knobs you learned, without the boilerplate.

A recipe is your TrainingArguments, declared

recipe:
  base_model: HuggingFaceTB/SmolLM2-135M-Instruct
  method: lora                 # vs full fine-tune
  lora: { r: 16, alpha: 32, dropout: 0.05, target_modules: [q_proj, v_proj] }
  optim:
    learning_rate: 2.0e-4
    scheduler: cosine
    warmup_ratio: 0.03
    epochs: 3
  batch: { per_device: 8, grad_accum: 2 }   # effective batch 16
  precision: bf16
  checkpoint_cadence: epoch

Every field here is a concept you already own from Track 1 and built in Track 2: rank and alpha, target modules, the cosine schedule with warmup, effective batch size, bf16. The recipe just makes them a named, versioned, reusable object instead of inline arguments — so you can apply the same recipe across projects, or A/B two recipes on the same data.

From Track 2

Recipe ↔ TrainingArguments + LoraConfig. checkpoint_cadencesave_strategy. There's nothing in a recipe you didn't already set by hand — which is the whole point of having done it by hand first.

The task-handler dispatcher

Here's where the platform does more than your script did. The manifest's task_profile routes to a task handler — and the handler owns the task-specific mechanics (how to apply the chat template, build labels and the loss mask, and how to score() the output). The dispatcher routes to one of a general set:

ClassificationHandler        QAHandler
StructuredExtractionHandler  (span_set or field_match)
RAGHandler                   AlignmentHandler
Seq2SeqHandler               VisionLanguageHandler
AudioTranscriptHandler       SafetyHandler

Our sentiment task routes to the ClassificationHandler. Notice the design: handlers are named after task shapes (classification, extraction, alignment), never after one narrow domain. A new use case is "which existing shape is this, and what scoring mode?" — not "write a new handler." That generality is why the same eleven stages serve wildly different projects.

You choose the recipe; the handler does the rest

So the division of labor is: the recipe carries the hyperparameters you tuned by hand, and the handler carries the tokenization, masking, and scoring you wrote by hand — selected automatically from the manifest. You pick a recipe (or accept the autopilot default) and the platform assembles a runnable train plan. Before it spends a GPU-hour on that plan, though, it runs preflight — the next lesson.

Key idea

A recipe is your TrainingArguments + LoraConfig as reusable config; a task handler is your tokenize/mask/score code, picked by task shape. Together they reproduce your by-hand training step — declaratively, and general across task types.

Key terms

recipe
Declarative training config: base model, method, LoRA knobs, optimizer/schedule, batch, precision, checkpoint cadence.
task-handler dispatcher
Routes the manifest's task_profile to the handler that owns that task's tokenization, masking, and scoring.
task handler
A general, task-shape-named component (Classification, QA, StructuredExtraction, RAG, …) implementing score().
checkpoint cadence
How often checkpoints are written during training (the recipe's save_strategy).

Check yourself

Answers are saved to this browser.

Progress is stored locally in your browser.