BY Campaign Optimizer (Active Learning)
Multi-round design campaigns generate scored designs at every iteration. This skill turns that scoring history into actionable parameter changes for the next round — training a lightweight Random Forest on the designs you already have, ranking which features actually discriminate good from bad, and proposing thresholds and design counts that target the most promising regions of feature space.
The optimizer is the iteration loop: it runs after the diagnosis step
(by-failure-diagnosis) has identified which features matter, and decides what
parameters to push or relax for the next round. Diagnosis answers "what went
wrong?"; this skill answers "what should we try next?".
When to Use This Skill
✅ Use this skill when:
- A design round has completed and scores are written to
*_scores.jsonfiles - The campaign has ≥10 scored designs total across all rounds
- You are deciding how to configure the next round of a multi-round campaign
by-failure-diagnosishas identified discriminating features and you need concrete threshold values- You want a feature-importance ranking to decide what to optimize for
- The user asks "what should I change for the next round?" or "tune my campaign"
❌ Do NOT use this skill when:
- The campaign has fewer than 10 scored designs → use rule-based defaults from
by-campaign-managerinstead - This is the first round (no prior scores exist) → use
by-researchandby-design-workflowto set initial parameters - Designs are failing for non-statistical reasons (e.g., compute errors, bad
target structure) → run
by-failure-diagnosisfirst to confirm the signal is real - The campaign is a single-shot screen with no planned iterations
- You only need a "why did designs fail?" report → use
by-failure-diagnosis(the diagnostic counterpart to this optimizer)
Quick Start
from proteus_cli.campaign.active_learning import (
has_enough_data,
suggest_from_campaign,
)
campaign_dir = "campaigns/tnfa/campaign_20260520_001"
if has_enough_data(campaign_dir):
result = suggest_from_campaign(campaign_dir)
print(f"Source: {result.source}") # "active_learning"
print(f"Confidence: {result.confidence}") # "high" if >30 designs
print(f"Top feature: {result.feature_importances[0]}")
print(f"Recommendations: {result.recommended_parameters}")
Or via the CLI scripts shipped with this skill:
# Step 1: train RF, write optimizer_output.json
python scripts/optimize_from_csv.py \
--scores campaign/round_1/scores.csv \
--output campaign/round_1/optimizer_output.json
# Step 2: write next-round YAML config
python scripts/propose_next_round.py \
--optimizer-output campaign/round_1/optimizer_output.json \
--previous-config campaign/round_1/config.yaml \
--output campaign/round_2/config.yaml
Installation
| Package | Version | License | Commercial Use | Install |
|---|---|---|---|---|
| scikit-learn | ≥1.3 | BSD-3-Clause | ✅ Permitted | pip install scikit-learn |
| pandas | ≥2.0 | BSD-3-Clause | ✅ Permitted | pip install pandas |
| numpy | ≥1.24 | BSD-3-Clause | ✅ Permitted | pip install numpy |
| PyYAML | ≥6.0 | MIT | ✅ Permitted | pip install pyyaml |
Or install the ML extras bundle:
pip install by-agent[ml]
License Compliance: All packages permit commercial use in AI applications.
Inputs
Required:
- Scored designs: either
- A campaign directory containing
*_scores.jsonfiles (Python API), or - A flat CSV with one row per design (CLI scripts)
- A campaign directory containing
- Each scored design must contain numeric values for some subset of:
ipsae(oripsae_min),iptm,plddt,rmsd,liabilities,cdr3_length,net_charge,hydrophobic_fraction - A
statuscolumn (PASS/FAIL) is optional but improves recommendations
Optional:
min_designs(default 10) — the active-learning threshold- A previous-round YAML config to use as a template for the next round
- A target feature to optimize (defaults to
ipsae)
See references/active-learning.md for sample-size guidance and references/random-forest-approach.md for feature-engineering details.
Outputs
Primary results:
optimizer_output.json— feature importances, recommended thresholds, exploration regions, confidence level, and a human-readable explanationOptimizationResultdataclass (Python API) with the same content
Next-round config:
config_round_N+1.yaml— design parameters with adjusted thresholds, updatednum_designs, and feature targets carried over from the optimizer
Diagnostic fields in optimizer_output.json:
source—"active_learning"if RF trained,"rule_based"if fallbackconfidence—"low"/"medium"/"high"based on training-set sizefeature_importances— sorted list of(feature, importance)tuplesrecommended_parameters— concrete values for the next roundexploration_regions— feature-space regions to sample more denselywarnings— any files skipped or features dropped
Clarification Questions
⚠️ CRITICAL: ASK THIS FIRST. Always confirm the user has at least 10 scored designs before running the optimizer. Below the threshold, the RF will not train and you should route to rule-based defaults instead.
Scored data available? (ASK THIS FIRST) How many scored designs exist in the campaign? Where are the
*_scores.jsonfiles (or the scores CSV)? If fewer than 10, stop and use rule-based defaults.Target metric to optimize. ipSAE is the default. Should we optimize for composite score, ipTM, or a custom weighted target? Different targets produce different recommendations.
Pass/fail labels. Does the data include a
statuscolumn withPASS/FAILlabels, or only continuous scores? With labels, the optimizer can suggest thresholds that match the empirical pass boundary.Has diagnosis been run? If
by-failure-diagnosisflagged a feature as a strong discriminator, prioritize that feature when interpreting RF importances.Compute and design-count budget for next round. How many designs is the user willing to run? The optimizer can suggest
increase_num_designsif the search space is under-sampled.Diversity vs exploitation. Should the next round exploit the top-quartile region (narrow thresholds) or explore widely (lower thresholds, higher diversity weight)? See references/active-learning.md#acquisition-functions.
Round number. Which round number is being planned? The optimizer keeps a history file so later rounds can compare improvement trajectories.
Standard Workflow
🚨 MANDATORY: USE THE SHIPPED SCRIPTS — DO NOT REIMPLEMENT THE RF INLINE. 🚨
Confirm data sufficiency.
from proteus_cli.campaign.active_learning import has_enough_data assert has_enough_data(campaign_dir, min_designs=10)✅ VERIFICATION:
Trueprinted. IfFalse, stop and use rule-based defaults.(Optional) Run diagnosis first. If the previous-round pass rate is below 20%, invoke
by-failure-diagnosisto identify discriminating features. Use those features as priors when interpreting the optimizer's importance ranking.Train RF and write optimizer output.
python scripts/optimize_from_csv.py \ --scores campaign/round_N/scores.csv \ --target ipsae \ --output campaign/round_N/optimizer_output.json✅ VERIFICATION:
✓ Optimizer completed: N designs, top feature: <feat>Inspect feature importances. Read
optimizer_output.json. The top feature drives the most variance in ipSAE; the recommended thresholds are derived from the top-quartile mean.Propose next-round config.
python scripts/propose_next_round.py \ --optimizer-output campaign/round_N/optimizer_output.json \ --previous-config campaign/round_N/config.yaml \ --output campaign/round_N+1/config.yaml✅ VERIFICATION:
✓ Next-round config written: <path>Review the diff. Compare the new config to the previous one. Surface every parameter change to the user before launching the next round.
Launch next round. Hand off to
by-design-workflowwith the new config. The optimizer does not submit jobs.
❌ DON'T:
- ❌ Skip step 1 — running RF on <10 designs returns garbage importances
- ❌ Discard the optimizer's
warningslist — it flags skipped files and dropped features - ❌ Apply recommendations blindly when
confidenceis"low"— review with the user first
When Scripts Fail
Follow the standard Script Failure Hierarchy:
- Fix and retry (90%) — Install scikit-learn / pyyaml; re-run.
- Modify the script (5%) — Adjust
--targetor feature list if your campaign uses non-standard metric names. - Use as reference (4%) — Read
optimize_from_csv.pyand adapt the training loop if you need a different model class. - Write from scratch (1%) — Only if the campaign requires a fundamentally different optimizer (e.g., Bayesian optimization with a Gaussian process).
For active-learning theory, see references/active-learning.md. For RF hyperparameter rationale, see references/random-forest-approach.md.
Decision Points
| Decision | Options | Guidance |
|---|---|---|
| Acquisition function | Greedy exploit / uncertainty / expected improvement | Greedy when ≥30 designs; uncertainty when 10–30 |
| Threshold adjustment | Tighten / hold / loosen | Tighten if pass rate ≥40%; loosen if <20% |
| Design count | Hold / increase / decrease | Increase if recommended_parameters.increase_num_designs is True |
| Confidence floor | Accept low / medium / high only | Require ≥medium before propagating to config |
See references/active-learning.md#acquisition-functions for the full decision tree.
Common Issues
| Issue | Cause | Solution | Details |
|---|---|---|---|
source: rule_based returned unexpectedly |
<10 scored designs OR scikit-learn missing | Check has_enough_data; pip install scikit-learn |
active-learning.md |
| All importances near 0 | All designs have similar scores (low variance) | Run more diverse designs first; lower thresholds | random-forest-approach.md |
| Recommended threshold = 0 | Feature missing from most scores | Backfill scores with by-screening |
active-learning.md |
| RF predicts same ipSAE for every input | max_depth=5 too shallow for noisy data |
Edit optimize_from_csv.py to raise max_depth to 10 |
random-forest-approach.md |
cdr3_length dominates importance |
Class imbalance in CDR3 lengths | Stratify training or drop the feature | random-forest-approach.md |
optimizer_output.json missing exploration_regions |
All top-quartile designs cluster tightly | Increase round size; reduce greedy exploitation | active-learning.md |
files_skipped > 0 in result |
Malformed JSON in *_scores.json |
Open the offending files; fix or remove | — |
Suggested min_ipsae lower than previous round |
Top-quartile mean drifted down | Investigate degradation before applying; rerun diagnosis | active-learning.md |
| YAML output has wrong key names | Previous config used non-standard keys | Edit propose_next_round.py key map |
— |
| Importances differ wildly between rounds | RF has high variance with small N | Average importances across last 3 rounds before acting | random-forest-approach.md |
confidence: low despite >30 designs |
High files_skipped count |
Inspect warnings; fix data quality |
— |
Best Practices
- 🚨 CRITICAL: Always check
has_enough_databefore training — RF on <10 points is meaningless. - ✅ REQUIRED: Run
by-failure-diagnosisfirst if previous-round pass rate is below 20%. - ✅ Treat importance < 0.10 as noise; do not propagate those features to the next-round config.
- ✅ Use the top-quartile mean (not the maximum) when deriving thresholds — robust to outliers.
- ✅ Carry a constant 10% safety margin on thresholds (e.g.,
min_ipsae = top_quartile_mean * 0.9). - ✅ Review the diff between rounds — never auto-apply without user sign-off.
- ✨ Optional: Average feature importances across the last 3 rounds for stability.
- ❌ Never use the optimizer as a replacement for diagnosis — they answer different questions.
- ❌ Never train on data from different targets — restrict to one campaign per fit.
- ❌ Never push
min_ipsaeabove 0.75 without manual verification — the RF will happily recommend impossible thresholds.
Suggested Next Steps
After running the optimizer:
by-design-workflow— Launch the next round with the proposed YAML config. This is the normal forward path once the user approves the recommendations.by-campaign-manager— Record the optimizer recommendations and round number in the campaign state file so later sessions can reconstruct the trajectory.by-failure-diagnosis— Re-run after the next round completes to check whether the predicted discriminators held up empirically. Closed-loop improvement requires this pairing.by-knowledge— Persist any surprising findings (e.g.,cdr3_lengthdominating importance for a specific target family) so future campaigns start with stronger priors.
These chain because each skill operates on a different artifact: the optimizer produces parameters, the workflow consumes them, the campaign manager tracks history, and diagnosis closes the loop on whether the recommendations worked.
Related Skills
Upstream (run first):
by-failure-diagnosis— Identifies which features discriminate PASS/FAIL. Use its top features as priors when reading optimizer importances.by-screening— Produces the*_scores.jsonfiles this skill consumes.
Downstream (run after):
by-design-workflow— Consumes the next-round YAML and launches the round.by-campaign-manager— Persists round history and reconstructs trajectories.
Complementary:
by-knowledge— Long-term memory across campaigns; learns which features matter for which target families.by-hypothesis-debate— When the optimizer is uncertain (low confidence), use hypothesis-debate to choose between competing parameter strategies before committing GPU compute.
References
Detailed documentation:
- references/active-learning.md — How active learning applies to protein design, acquisition functions, sample-size minimums, exploration vs exploitation trade-offs.
- references/random-forest-approach.md — Random Forest hyperparameters, feature engineering, importance interpretation, fallback behaviour for sparse data.
Scripts:
scripts/optimize_from_csv.py— CLI: reads scored designs CSV, trains the Random Forest, writesoptimizer_output.jsonwith feature importances and recommendations.scripts/propose_next_round.py— CLI: reads optimizer output and a previous config, writes the next-round YAML config with adjusted thresholds and design parameters.
Key references:
- EVOLVEpro (Science, 2024) — few-shot active learning for protein engineering using protein language model embeddings. This skill uses hand-crafted structural and developability features instead of PLM embeddings.
- Settles, B. (2009). Active Learning Literature Survey. University of Wisconsin–Madison Computer Sciences Technical Report 1648.
- Breiman, L. (2001). Random Forests. Machine Learning 45, 5–32.
License: All dependencies (scikit-learn, pandas, numpy, PyYAML) permit commercial use.