Campaign Manager
SkillDev toolsA skill for dev tools by lamm-mit.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Campaign Manager skill
What this skill tells your AI
The instructions your AI receives, as published by lamm-mit/scienceclaw in skills/campaign-manager/SKILL.md and read by ahel’s review.
Goal-oriented protein design campaign planning. Converts abstract objectives into concrete computational pipelines with cost/time estimates, health monitoring, and adaptive decision-making.
Campaign Types
| Goal | Pipeline | Expected Timeline |
|---|---|---|
| Proof-of-concept binder | BoltzGen → Chai → ipSAE | 1–2 weeks compute |
| High-throughput screen | RFdiffusion → MPNN → ESMFold → AF2 | 2–4 weeks compute |
| High-quality therapeutic | BindCraft → ipSAE → affinity maturation | 4–8 weeks compute |
| Enzyme design | RFdiffusion + LigandMPNN → Boltz | 3–6 weeks compute |
| Quick exploration | BoltzGen 50 designs → Chai | 3–5 days compute |
Campaign Planning
from dataclasses import dataclass, field
from typing import List, Optional
import json
@dataclass
class DesignCampaign:
name: str
target_pdb: str
goal: str # "binder", "enzyme", "scaffold"
hotspot_residues: List[str]
binder_length_range: tuple = (60, 100)
# Pipeline config
n_backbone_designs: int = 200
n_seqs_per_backbone: int = 8
structure_prediction_tool: str = "colabfold" # or "chai", "boltz"
sequence_design_tool: str = "proteinmpnn" # or "ligandmpnn", "solublempnn"
# Quality thresholds
iptm_threshold: float = 0.6
plddt_threshold: float = 75.0
ipsae_top_n: int = 20
# Budget
gpu_hours_budget: Optional[float] = None
designs_to_order: int = 10
def estimate_pipeline(self) -> dict:
"""Estimate compute requirements and expected yields."""
total_seqs = self.n_backbone_designs * self.n_seqs_per_backbone
# Empirical pass rates
esm_pass_rate = 0.65
af2_pass_rate = 0.25
ipsae_final = self.ipsae_top_n
esm_survivors = int(total_seqs * esm_pass_rate)
af2_survivors = int(esm_survivors * af2_pass_rate)
final_candidates = min(af2_survivors, ipsae_final)
# GPU time estimates (A100)
rfdiffusion_hours = self.n_backbone_designs * 0.1 / 60
mpnn_hours = 0.1 # fast
esm_hours = esm_survivors * 0.01 / 60
af2_hours = esm_survivors * 2.5 / 60 # ~2.5 min per pair
return {
"total_sequences_designed": total_seqs,
"esm_survivors": esm_survivors,
"af2_survivors": af2_survivors,
"expected_final_candidates": final_candidates,
"gpu_hours": {
"rfdiffusion": round(rfdiffusion_hours, 1),
"mpnn": round(mpnn_hours, 1),
"esmfold": round(esm_hours, 1),
"af2_validation": round(af2_hours, 1),
"total": round(rfdiffusion_hours + esm_hours + af2_hours, 1),
},
"wall_time_days": round(
(rfdiffusion_hours + esm_hours + af2_hours) / 24, 1
),
"meets_budget": (
self.gpu_hours_budget is None or
(rfdiffusion_hours + af2_hours) <= self.gpu_hours_budget
)
}
Campaign Health Assessment
def assess_campaign_health(campaign_dir: str, stage: str) -> dict:
"""
Check campaign progress and flag issues.
stage: "post_rfdiffusion" | "post_mpnn" | "post_esm" | "post_af2"
"""
import os, glob, json
import numpy as np
health = {"stage": stage, "issues": [], "recommendations": []}
if stage == "post_af2":
json_files = glob.glob(f"{campaign_dir}/af2/**/*.json", recursive=True)
iptm_vals = []
plddt_vals = []
for f in json_files:
with open(f) as fp:
r = json.load(fp)
iptm_vals.append(r.get("iptm", 0))
plddt_vals.append(np.array(r.get("plddt", [0])).mean())
if not iptm_vals:
health["issues"].append("No AF2 results found")
return health
pass_rate = sum(1 for x in iptm_vals if x > 0.6) / len(iptm_vals)
health["metrics"] = {
"n_predictions": len(iptm_vals),
"mean_iptm": np.mean(iptm_vals),
"pass_rate_iptm06": pass_rate,
"mean_plddt": np.mean(plddt_vals),
}
if pass_rate < 0.05:
health["issues"].append("Very low pass rate (<5%) — redesign needed")
health["recommendations"].extend([
"Check hotspot accessibility in target structure",
"Try BoltzGen instead of RFdiffusion",
"Reduce binder length constraints",
"Verify target PDB quality",
])
elif pass_rate < 0.15:
health["issues"].append("Low pass rate (<15%) — consider adjustments")
health["recommendations"].append("Try partial diffusion for diversity")
return health
Adaptive Pipeline Decisions
def decide_next_step(health: dict) -> str:
"""
Given campaign health assessment, recommend next action.
"""
issues = health.get("issues", [])
metrics = health.get("metrics", {})
pass_rate = metrics.get("pass_rate_iptm06", 0)
if pass_rate == 0:
return "ABORT: Zero passing designs. Review target preparation and hotspot selection."
elif pass_rate < 0.05:
return (
"REDESIGN: Switch to BindCraft for built-in AF2 validation during design, "
"or verify hotspot residues are solvent-exposed."
)
elif pass_rate < 0.10:
return (
"EXPAND: Generate 2x more backbones with partial diffusion from best current designs. "
"Also try SolubleMPNN for better expression."
)
elif metrics.get("mean_iptm", 0) > 0.75:
return (
"ADVANCE: Strong campaign. Run ipSAE ranking on top designs, "
"run biophysical QC, and prepare ordering list."
)
else:
return (
"CONTINUE: Moderate progress. Apply ipSAE ranking, "
"consider affinity maturation on top 5 designs via partial diffusion."
)
Cost Estimation
def estimate_costs(campaign: DesignCampaign,
gpu_cost_per_hour: float = 2.50, # USD, A100 on-demand
gene_synthesis_per_seq: float = 150.0,
expression_per_sample: float = 80.0) -> dict:
estimates = campaign.estimate_pipeline()
gpu_hours = estimates["gpu_hours"]["total"]
return {
"compute_cost_usd": round(gpu_hours * gpu_cost_per_hour, 0),
"gene_synthesis_usd": round(campaign.designs_to_order * gene_synthesis_per_seq, 0),
"expression_screening_usd": round(campaign.designs_to_order * expression_per_sample, 0),
"total_estimated_usd": round(
gpu_hours * gpu_cost_per_hour +
campaign.designs_to_order * (gene_synthesis_per_seq + expression_per_sample),
0
),
}
Affinity Maturation (Second Round)
After identifying binders, improve affinity via computational iteration:
# Partial diffusion on best designs
python3 RFdiffusion/scripts/run_inference.py \
inference.input_pdb=best_binder.pdb \
'contigmap.contigs=[A1-999/0 B1-80]' \
diffuser.partial_T=20 \
inference.num_designs=50 \
inference.output_prefix=matured/binder
# Then: ProteinMPNN → AF2 → ipSAE (same pipeline)
Signals
- GitHub stars
- 242
- Forks
- 42
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
campaign-manager-lamm-mit- Source
- github.com/lamm-mit/scienceclaw