Your first skill was trained on one task domain (Lesson 5). But your agent does many things — code review, test writing, refactoring, documentation, debugging. This lesson covers: training across multiple task types, deciding whether to compose separate skills or merge into one, and what to do when a skill plateaus.
Two architectures for multi-domain skills:
| Approach | When to use | Tradeoff |
|---|---|---|
| Single unified skill | Task types share common patterns (e.g., all involve reading code + producing text) | Simpler to deploy; risk of conflicting rules between domains |
| Separate skills, composed | Task types are genuinely different (e.g., code review vs. deployment scripts) | Cleaner separation; more files to manage; agent picks which to apply |
SkillOpt's task_type field in your data items lets the optimizer learn
type-specific rules within a single skill. Start with one skill containing multiple
task types. If the gate starts rejecting edits that help one type but hurt another,
that's your signal to split.
In your dataloader (Lesson 5), each item has a task_type field. Add items
from a second domain:
# data/my_env_split/train/tasks.json
[
{"id": "review_001", "task_type": "code_review", "input": "...", "ground_truth": "..."},
{"id": "review_002", "task_type": "code_review", "input": "...", "ground_truth": "..."},
{"id": "test_001", "task_type": "test_generation", "input": "...", "ground_truth": "..."},
{"id": "test_002", "task_type": "test_generation", "input": "...", "ground_truth": "..."}
]
Your adapter's get_task_types() returns all types present — the optimizer
sees them all and can learn type-specific rules.
python scripts/train.py --config configs/my_env/default.yaml
Watch the per-type scores in the training log. The optimizer may learn rules that help one type and are neutral for others (good), or rules that help one and hurt another (the gate will reject these).
If you see many rejections after adding a second task type, check whether:
_score() function handles each type appropriately.When a skill saturates (Lesson 9), you have four levers:
| Lever | How | When |
|---|---|---|
| Add harder tasks | Include items the agent currently fails at | The skill is good at easy cases, bad at hard ones |
| Expand task types | Add a new domain to the env (this lesson) | You want the skill to cover more of your workflow |
| Increase edit budget | optimizer.learning_rate: 8 (default 4) |
The optimizer needs larger changes to express new rules |
| Reset and retrain | Start from a fresh seed with updated data | The accumulated skill has cruft from obsolete patterns |
A higher edit budget allows bigger changes per proposal — more expressive, but also more risk of harmful edits slipping through. The gate still protects you, but a single rejected proposal wastes more optimizer compute. Increase by 1–2 at a time.
If you do split into multiple skills, deploy them as layers:
# For pi:
~/.pi/skills/
├── 00-base-coding.md # Universal coding patterns
├── 01-code-review.md # Review-specific rules
└── 02-test-generation.md # Test-writing rules
# For Kiro:
.kiro/steering/
├── skillopt-base.md
├── skillopt-review.md
└── skillopt-tests.md
Number-prefix ordering ensures the base skill loads first. Type-specific skills add rules that only apply in their domain. The agent sees all of them — the rules are additive.
After this course, you have a system that:
Your agent gets better the more you use it. No weight training. No inference overhead. Just validated text edits that encode what works.
SkillOpt paper — §4.2 "Multi-benchmark training" — Microsoft Research. How the optimizer handles multiple task types within one training run, and the per-cell results across 6 benchmarks. ~10 minutes for the multi-env sections.