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.
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_cadence ↔ save_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.