Multimodal Foundation for Autonomous Protein Design: Generating 100 Novel Sequences Maintaining Serine Protease Active Site
In Part 02 (Protein Embeddings) and Part 11 (Structure/Affinity Prediction), we saw how well foundation models understand natural proteins. But the truly interesting question is this: can these models code entirely new proteins from scratch, ones that don't exist in nature? EvolutionaryScale's ESM3 (2024) is the first large-scale protein foundation model that learns sequence, structure, and function modalities with a single transformer, enabling conditional generation of arbitrary modalities. This installment builds a practical pipeline for leveraging its capabilities: fixing the catalytic triad (Ser195-His57-Asp102, chymotrypsin numbering) of serine proteases and autonomously designing the remaining sequence in a novel way. We generate 100 truly novel serine protease candidates with less than 30% sequence similarity to natural sequences and complete the process by verifying their 3D structures with AF2/Boltz.
📚 Recommended Prerequisites (Strongly Recommended)
This installment is the pinnacle of the AI x Biology hardcore advanced course. We strongly recommend completing the following DryBench installments before diving in.
- DryBench ai-native #3 Transformers and Embeddings
- DryBench ai-native #12 PyTorch Basics
- DryBench ai-native #13 HuggingFace and Commercial APIs
Without completing the prerequisites, it will be difficult to follow the practical code in this installment, as it proceeds without re-explaining the principles of multi-modal transformers, masked language model training, and large-scale PyTorch model management.
We Learned This in Our DryBench Course
In DryBench ai-native #3, we learned that transformer embeddings are scalable regardless of domain or modality, in #12 we learned the basics of large-scale tensor, GPU memory, and mixed precision optimization with PyTorch, and in #13 we learned that HuggingFace provides a standard loading and inference API for pre-trained models.
ESM3 is a case study where these three principles reach their peak in the protein domain. Sequence tokens, discrete structure tokens (similar to the 3Di alphabet), and functional domain tokens are placed in a single integrated vocabulary, and the transformer is trained to predict tokens at arbitrary locations and in arbitrary modalities in a masked language model style. The results are astounding. It can fix a specific active site structure and generate the remaining sequence, or autonomously code new sequences with desired functions (e.g., fluorescence, catalysis, binding) as conditions. This installment turns that ability into a practical enzyme design pipeline.
Defining the Hardcore Problem
Practical Scenario: Autonomous Design of Novel Serine Protease
Serine proteases are enzymes that break down proteins, with chymotrypsin, trypsin, and elastase being representative examples. Common catalytic mechanism: Ser195's hydroxyl is the nucleophile, His57 is the general base that transfers a proton, and Asp102 stabilizes His57. This triangle must maintain a perfect 3D arrangement for activity.
Our goal:
- Less than 30% sequence similarity to natural serine proteases (truly novel).
- Maintain the 3D coordinates of the Ser-His-Asp catalytic triad (AF2 predicted RMSD ≤ 2 Å).
- Autonomously generate 100 candidates (approximately 30-60 minutes).
- Self-consistency (ESM3 generation → AF2 prediction → active site reproduction) success rate of 40%+.
- Re-validate the top 5 using active site prediction (Rosetta, MD) (optional).
Spectrum of De Novo Protein Design
- Rosetta (2003 onwards): Physics-based protein design, an established standard.
- RFdiffusion (Baker Lab 2023): Generates the backbone using a diffusion model. Subsequent sequence design uses ProteinMPNN [1].
- ProteinMPNN (Baker Lab 2022): The standard for backbone → sequence redesign [2].
- RFdiffusion + ProteinMPNN + AF2: Modern standard pipeline (backbone → sequence → validation).
- Chroma (2024, Baker Lab): Diffusion-based, all-atom [3].
- ESM3 (2024): The first large-scale multi-modal foundation. Integrates conditional generation [4].
- Boltz-2 (Part 11): Specialized for prediction, limited in design.
- Ligand-conditioned design: RFdiffusion-Motif, Chroma-Ligand.
This installment uses ESM3 conditional generation (main installment) + AF2/Boltz validation + RFdiffusion+ProteinMPNN ensemble comparison (extension).
Target Metrics for This Installment
- Extract the 3D coordinates of the catalytic triad from chymotrypsin PDB (1AB9, 4CHA, etc.).
- Generate 100 sequences using ESM3 conditional generation (target_length 200-250 residues).
- Apply a diversity filter (pairwise identity ≤ 70%).
- Check for similarity to natural sequences (UniProt Swiss-Prot BLAST or ESM embeddings) → retain only those with less than 30% similarity.
- Predict structures using AF2/Boltz → achieve a 40%+ reproduction rate of active site RMSD ≤ 2 Å.
- Average pLDDT ≥ 70 (structural confidence).
Tool Stack and Infrastructure Requirements
| Tool | Role | License |
|---|---|---|
ESM3 (esm3-sm-open-v1 1.4B) | 3-track conditional generation | Academic open, commercial separate agreement |
| ESM3 large (open weights, non-commercial) | Alternative large model | EvolutionaryScale license |
| RFdiffusion + ProteinMPNN | Ensemble comparison | Academic open |
| Boltz-2 (Part 11) | Generates and validates the structure of the generated sequence | MIT |
| AlphaFold2 (or ColabFold) | Alternative structure validation | Apache 2.0 |
| Biopython · PyMOL | Sequence/structure manipulation and visualization | Biopython License · LGPL |
| BLAST · Foldseek | Search for similar natural sequences/structures | Academic open |
| PyTorch | Backend | BSD |
Infrastructure Requirements:
- ESM3 1.4B small open weights: Server GPU with 16-24GB VRAM or higher. Approximately 4GB VRAM in fp16.
- ESM3 large (98B, private): Access only through academic API.
- RFdiffusion: Data center GPU with 24-48GB VRAM.
- AF2/Boltz validation: See Part 11 (high-end consumer GPU or data center GPU with 24-48GB VRAM).
- Large-scale batch generation and validation: Data center workstation with 80GB+ VRAM (generally inaccessible, cloud on-demand recommended).
- RAM 32GB or higher.
Estimated reproduction cost for learners: If using local GPU, API cost is 0. Generating 100 sequences + AF2/Boltz validation takes approximately 4-8 hours (based on a 24-48GB VRAM GPU).
Practical Pipeline Implementation
Overall Flow:
Step 1. Active Site Definition · ESM3 Constraint Preparation
Extract the 3D coordinates of the catalytic triad residues from Chymotrypsin (PDB 4CHA, 5CHA, 1AB9, etc.).
from dataclasses import dataclassfrom pathlib import Path
import torchimport numpy as npfrom Bio.PDB import PDBParser
@dataclassclass ActiveSiteConstraint: """Defines active site constraints.""" site_name: str # e.g. "serine_protease_triad" residue_positions: list[int] # Positions of the active site residues in the generated sequence (0-based) residue_types: list[str] # Amino acids at each position (e.g., ["S", "H", "D"]) coordinates_backbone: np.ndarray # (N_residues, 4, 3): N, CA, C, O coordinates ideal_distances: dict[tuple[int, int], float] # Ideal distances between active site residues (Å)
AA_3TO1 = { "ALA": "A", "ARG": "R", "ASN": "N", "ASP": "D", "CYS": "C", "GLN": "Q", "GLU": "E", "GLY": "G", "HIS": "H", "ILE": "I", "LEU": "L", "LYS": "K", "MET": "M", "PHE": "F", "PRO": "P", "SER": "S", "THR": "T", "TRP": "W", "TYR": "Y", "VAL": "V",}
def load_catalytic_triad_from_pdb( pdb_path: Path, triad_selection: list[tuple[str, int, str]], # [(chain, resid, expected_aa)] site_name: str = "serine_protease_triad",) -> ActiveSiteConstraint: """Extracts catalytic triad coordinates from a PDB file.
Example for Chymotrypsin (4CHA): [("A", 195, "S"), ("A", 57, "H"), ("A", 102, "D")] """ parser = PDBParser(QUIET=True) structure = parser.get_structure("target", str(pdb_path))
residue_types = [] coordinates = [] positions = [] for i, (chain_id, resid, expected_aa) in enumerate(triad_selection): try: chain = structure[0][chain_id] residue = chain[resid] except KeyError: raise ValueError(f"PDB missing: {chain_id}:{resid}") aa = AA_3TO1.get(residue.get_resname().upper(), "X") if aa != expected_aa: raise ValueError(f"Active site {chain_id}:{resid} amino acid mismatch: expected {expected_aa}, actual {aa}") residue_types.append(aa) coords = np.array([ residue["N"].get_coord(), residue["CA"].get_coord(), residue["C"].get_coord(), residue["O"].get_coord(), ]) coordinates.append(coords) positions.append(i)
coordinates_arr = np.stack(coordinates, axis=0)
# Ideal distances between active site residues (based on chymotrypsin measurements) ca_positions = coordinates_arr[:, 1, :] # CA only ideal_dist = {} for i in range(len(triad_selection)): for j in range(i + 1, len(triad_selection)): d = float(np.linalg.norm(ca_positions[i] - ca_positions[j])) ideal_dist[(i, j)] = d
return ActiveSiteConstraint( site_name=site_name, residue_positions=positions, residue_types=residue_types, coordinates_backbone=coordinates_arr, ideal_distances=ideal_dist, )
# Example: chymotrypsin catalytic triadCHYMOTRYPSIN_TRIAD_4CHA = [ ("A", 57, "H"), # His57 (histidine base) ("A", 102, "D"), # Asp102 (aspartate stabilizer) ("A", 195, "S"), # Ser195 (serine nucleophile)]Step 2. ESM3 Conditional Generation
ESM3 SDK allows specifying masks for sequence/structure/function tracks and iteratively unmasking.
from esm.models.esm3 import ESM3from esm.sdk.api import ESMProtein, GenerationConfig
class ESM3Designer: """ESM3-based protein design."""
def __init__(self, device: str = "cuda", model_name: str = "esm3-sm-open-v1"): self.device = device self.model = ESM3.from_pretrained(model_name).to(device).eval() self.model_name = model_name
@torch.no_grad() def design_with_active_site( self, constraint: ActiveSiteConstraint, target_length: int = 245, # Similar length to chymotrypsin num_samples: int = 100, temperature: float = 0.7, seed: int | None = None, ) -> list[dict]: """Generates new sequences while maintaining active site constraints.
Returns: [{sequence, seed, active_site_preserved: bool}] """ if seed is not None: torch.manual_seed(seed) np.random.seed(seed)
generated = [] for i in range(num_samples): # Initial protein: sequence masked, active site residue positions fixed seq_list = ["_"] * target_length
# Active site positions (placed arbitrarily in the target length, e.g., in the middle) triad_positions = self._distribute_triad_positions( target_length, len(constraint.residue_positions), ) for pos, aa in zip(triad_positions, constraint.residue_types): seq_list[pos] = aa initial_sequence = "".join(seq_list)
protein = ESMProtein(sequence=initial_sequence)
# ESM3 iterative decoding config = GenerationConfig( track="sequence", num_steps=max(target_length // 4, 20), temperature=temperature, ) try: result = self.model.generate(protein, config) # Post-hoc check to ensure that the active site residues are actually maintained preserved = all( result.sequence[pos] == aa for pos, aa in zip(triad_positions, constraint.residue_types) ) generated.append({ "sequence": result.sequence, "seed": (seed or 0) + i, "triad_positions": triad_positions, "active_site_preserved": preserved, "length": len(result.sequence), }) except Exception as e: print(f"[{i}] Generation failed: {e}")
return generated
def _distribute_triad_positions( self, target_length: int, n_triad: int, spread_ratio: float = 0.4, ) -> list[int]: """Distribute active site residues evenly across the target length.
In the actual chymotrypsin, the positions are 57, 102, and 195 out of 245 residues, which are 21%, 42%, and 80%. In the new design, maintain similar relative positions. """ chymo_ref = [57, 102, 195] chymo_length = 245 positions = [ int(target_length * (p / chymo_length)) for p in chymo_ref[:n_triad] ] return positionsStep 3. Diversity Filter
Ensure diversity by filtering generated sequences using a pairwise identity threshold.
def compute_pairwise_identity(seq_a: str, seq_b: str) -> float: """If the lengths are equal, perform position-wise comparison; otherwise, perform alignment.""" if len(seq_a) == len(seq_b): matches = sum(1 for a, b in zip(seq_a, seq_b) if a == b) return matches / len(seq_a) # If lengths are different, perform global alignment from Bio import pairwise2 aln = pairwise2.align.globalxx(seq_a, seq_b, one_alignment_only=True)[0] return aln.score / max(len(seq_a), len(seq_b))
def diversity_filter( sequences: list[str], max_pairwise_identity: float = 0.7,) -> list[str]: """Greedy filter: keep only sequences with low identity to already selected sequences.""" kept = [] for seq in sequences: keep = True for existing in kept: if compute_pairwise_identity(seq, existing) > max_pairwise_identity: keep = False break if keep: kept.append(seq) return keptStep 4. Natural Sequence Similarity Check
Use BLAST or ESM embeddings + FAISS to check for similarity to natural sequences.
def blast_against_swissprot( query_seq: str, swissprot_fasta_path: Path, e_threshold: float = 1e-5,) -> list[dict]: """Perform similarity search against natural sequences using BLAST.
In practice, use a local BLAST database (makeblastdb + blastp). """ import subprocess import tempfile
with tempfile.NamedTemporaryFile(mode="w", suffix=".fasta", delete=False) as query_f: query_f.write(f">query\n{query_seq}\n") query_path = query_f.name
try: result = subprocess.run( ["blastp", "-query", query_path, "-db", str(swissprot_fasta_path), "-outfmt", "6 qseqid sseqid pident evalue bitscore", "-evalue", str(e_threshold), "-max_target_seqs", "5"], capture_output=True, text=True, check=True, ) hits = [] for line in result.stdout.strip().split("\n"): if not line: continue fields = line.split("\t") hits.append({ "subject": fields[1], "identity": float(fields[2]), "e_value": float(fields[3]), "bit_score": float(fields[4]), }) return hits except (subprocess.CalledProcessError, FileNotFoundError): return []
def natural_similarity_check( seq: str, max_identity: float = 30.0, # %) -> tuple[bool, float]: """Check if the sequence is novel compared to natural sequences (identity ≤ max_identity).
Returns: (is_novel, max_identity_found) """ hits = blast_against_swissprot(seq, Path("/path/to/swissprot.fasta")) if not hits: return True, 0.0 max_id = max(h["identity"] for h in hits) return max_id < max_identity, max_idStep 5. AF2/Boltz Structure Validation
Predict the 3D structure of the generated sequences using AF2 or Boltz-2 (from paper 11) and compare the RMSD to the original required active site coordinates.
import subprocessimport yaml
def predict_structure_boltz(sequence: str, output_dir: Path, seq_id: str) -> Path: """Predict structure using Boltz-2 pipeline from paper 11.""" yaml_content = { "version": 1, "sequences": [{"protein": {"id": "A", "sequence": sequence}}], } output_dir.mkdir(parents=True, exist_ok=True) yaml_path = output_dir / f"{seq_id}.yaml" with open(yaml_path, "w") as f: yaml.safe_dump(yaml_content, f)
try: subprocess.run([ "boltz", "predict", str(yaml_path), "--out_dir", str(output_dir), "--use_msa_server", ], check=True, capture_output=True) except subprocess.CalledProcessError as e: raise RuntimeError(f"Boltz-2 failed for {seq_id}: {e.stderr.decode()[:500]}")
return output_dir / seq_id / f"{seq_id}_model_0.pdb"
def kabsch_rmsd(coords_a: np.ndarray, coords_b: np.ndarray) -> float: """Calculate RMSD after Kabsch alignment.""" a = coords_a - coords_a.mean(axis=0) b = coords_b - coords_b.mean(axis=0) h = a.T @ b u, _, vt = np.linalg.svd(h) d = np.sign(np.linalg.det(vt.T @ u.T)) correction = np.eye(3) correction[2, 2] = d r = vt.T @ correction @ u.T a_aligned = a @ r.T return float(np.sqrt(np.mean(np.sum((a_aligned - b) ** 2, axis=1))))
def check_active_site_recovery( predicted_pdb: Path, triad_positions: list[int], original_constraint: ActiveSiteConstraint, rmsd_threshold: float = 2.0,) -> tuple[bool, float, float]: """Check if the predicted structure recovers the active site.
Returns: (recovered, rmsd, mean_plddt) """ parser = PDBParser(QUIET=True) structure = parser.get_structure("predicted", str(predicted_pdb)) residues_list = list(structure.get_residues())
predicted_coords = [] plddt_values = [] for pos in triad_positions: if pos >= len(residues_list): return False, float("inf"), 0.0 residue = residues_list[pos] try: coords = np.array([ residue["N"].get_coord(), residue["CA"].get_coord(), residue["C"].get_coord(), residue["O"].get_coord(), ]) except KeyError: return False, float("inf"), 0.0 predicted_coords.append(coords) # pLDDT is stored in the B-factor of the CA atom (AF2/Boltz convention) plddt_values.append(float(residue["CA"].get_bfactor()))
predicted_arr = np.stack(predicted_coords, axis=0).reshape(-1, 3) reference_arr = original_constraint.coordinates_backbone.reshape(-1, 3) rmsd = kabsch_rmsd(predicted_arr, reference_arr) mean_plddt = float(np.mean(plddt_values)) return rmsd < rmsd_threshold, rmsd, mean_plddtStep 6. Integration · Candidate Ranking
from dataclasses import asdictimport pandas as pd
@dataclassclass DesignCandidate: sequence: str length: int seed: int active_site_preserved_in_seq: bool triad_positions: list[int] pdb_path: str | None active_site_rmsd: float plddt_active_site: float natural_identity_max: float novelty_score: float composite_score: float
def full_design_pipeline( catalytic_pdb: Path, triad_selection: list[tuple[str, int, str]], target_length: int, output_dir: Path, num_samples: int = 100, device: str = "cuda",) -> pd.DataFrame: """Active site → new sequence generation → diversity · novelty · validation → ranking.""" output_dir.mkdir(parents=True, exist_ok=True)
print("[1/6] Loading active site constraint") constraint = load_catalytic_triad_from_pdb(catalytic_pdb, triad_selection) print(f" triad: {constraint.residue_types}, distances: {constraint.ideal_distances}")
print("[2/6] ESM3 conditional generation") designer = ESM3Designer(device=device) generated = designer.design_with_active_site( constraint, target_length, num_samples, temperature=0.7, seed=42, ) preserved = [g for g in generated if g["active_site_preserved"]] print(f" Generated {len(generated)}, active site preserved {len(preserved)}")
print("[3/6] Diversity filter") preserved_seqs = [g["sequence"] for g in preserved] diverse_seqs = diversity_filter(preserved_seqs, max_pairwise_identity=0.7) diverse = [g for g in preserved if g["sequence"] in diverse_seqs] print(f" After diversity filtering: {len(diverse)}")
print("[4/6] Natural sequence novelty check") novel_candidates = [] for g in diverse: is_novel, max_id = natural_similarity_check(g["sequence"]) g["natural_identity_max"] = max_id g["novelty_score"] = 1.0 - (max_id / 100.0) if is_novel: novel_candidates.append(g) print(f" Novel (identity ≤ 30%): {len(novel_candidates)}")
print(f"[5/6] Boltz-2 structure validation ({len(novel_candidates)} sequences)") candidates_verified = [] for i, g in enumerate(novel_candidates): seq_id = f"design_{g['seed']}" try: pdb = predict_structure_boltz(g["sequence"], output_dir / "structures", seq_id) recovered, rmsd, plddt = check_active_site_recovery( pdb, g["triad_positions"], constraint, ) candidates_verified.append(DesignCandidate( sequence=g["sequence"], length=g["length"], seed=g["seed"], active_site_preserved_in_seq=g["active_site_preserved"], triad_positions=g["triad_positions"], pdb_path=str(pdb), active_site_rmsd=rmsd, plddt_active_site=plddt, natural_identity_max=g["natural_identity_max"], novelty_score=g["novelty_score"], composite_score=0.0, # Calculated below )) except Exception as e: print(f" [{i}] {seq_id} failed: {e}")
print("[6/6] Composite ranking · CSV saving") if not candidates_verified: return pd.DataFrame()
df = pd.DataFrame([asdict(c) for c in candidates_verified]) # Composite: pLDDT (40%) + active site RMSD (40%) + novelty (20%) df["composite_score"] = ( df["plddt_active_site"] / 100 * 0.4 + (1 - df["active_site_rmsd"].clip(0, 5) / 5.0) * 0.4 + df["novelty_score"] * 0.2 ) df = df.sort_values("composite_score", ascending=False) df.to_csv(output_dir / "design_candidates.csv", index=False) print(f" Top 5 composite scores: {df.head(5)['composite_score'].tolist()}") return df
# Example: (chymotrypsin catalytic triad → 100 new serine protease)# result = full_design_pipeline(# catalytic_pdb=Path("./4CHA.pdb"),# triad_selection=CHYMOTRYPSIN_TRIAD_4CHA,# target_length=245,# output_dir=Path("./serine_protease_design"),# num_samples=100,# )Step 7. RFdiffusion + ProteinMPNN Ensemble Comparison (Optional Extension)
Run the same active site constraint with the Baker Lab standard pipeline and compare the results.
def run_rfdiffusion_motif( active_site_pdb: Path, triad_residues: list[int], output_dir: Path, num_designs: int = 100,) -> list[Path]: """RFdiffusion motif scaffolding.
Fix the catalytic triad as a motif and generate the rest of the backbone. In practice, refer to the RosettaCommons RFdiffusion GitHub example. """ # Conceptual stub: call RFdiffusion CLI # subprocess.run(["python", "run_inference.py", ...]) return [] # Complete when implementing
def run_proteinmpnn( backbone_pdb: Path, output_dir: Path, num_sequences: int = 10,) -> list[str]: """Use ProteinMPNN to place sequences onto the backbone.""" # subprocess.run(["python", "protein_mpnn_run.py", ...]) return []
## Performance, Cost, and Known Failure Cases
### Performance Reference (Citing Public Benchmarks)
| Approach | Benchmark (self-consistency AF2 RMSD ≤ 2Å) | Novelty | Source ||------|:-------------------------------------:|:-------:|------|| Rosetta enzyme design | 20~30% | Low | Baker Lab 2003+ || ProteinMPNN alone (re-designing existing backbone) | 40~50% | Low to Medium | Dauparas et al., Science 2022 [2] || RFdiffusion + ProteinMPNN + AF2 | 60~70% | Medium to High | Watson et al., Nature 2023 [1] || ESM3 conditional generation | 40~55% (estimated) | Medium to High | Hayes et al., bioRxiv 2024 [4] || Chroma (Baker Lab all-atom) | 65~75% | Medium to High | Ingraham et al., Nature 2023 [3] || RFdiffusion + ProteinMPNN + Boltz-2 validation | ~65~72% (ensemble of 11) | Medium to High | Community benchmark |
**Practical Observation:** RFdiffusion + ProteinMPNN + AF2 is the gold standard for backbone-first design. ESM3 excels in conditional flexibility (arbitrary conditions for function, sequence, or structure). Ensemble approaches yield the best results.
### Estimated Cost for Learner Reproduction
- API cost: 0 (completely local).- Generating 100 sequences: Based on ESM3 1.4B, it takes 30 minutes to 1 hour on a 16-24GB VRAM GPU.- Validating 100 with Boltz-2: Based on the ensemble of 11 pipeline, it takes 2-4 hours.- Downloading and indexing UniProt/Swissprot locally: Approximately 500MB and 1-2 hours.
### 5 Known Failure Cases (Collected from Community and Papers)
1. **High pLDDT for generated sequences, but failure in active site reconstruction.** Symptoms: AF2/Boltz predicted pLDDT is 80+, but the arrangement of active site residues is incorrect. Cause: pLDDT is a global confidence score and is separate from the specific 3D arrangement of residues. Especially in loop regions, pLDDT can be high even with flexibility. Mitigation: (a) Individually check the pLDDT of active site residues (Step 5 in this chapter), (b) Mandatory check of RMSD of the active site in the predicted structure, (c) Re-validate stability with MD simulations (OpenMM, GROMACS), (d) Repeat generation with multiple seeds and perform consensus. Source: Discussion on RFdiffusion vs. AF2 self-consistency [1].
2. **Too similar to natural sequences (not truly novel).** Symptoms: When the generated sequence is BLASTed against UniProt, it shows multiple hits with identity greater than 40%. Cause: The foundation model fails to move beyond the distribution of natural sequences it was trained on. Mitigation: (a) Apply a strict diversity filter (Step 3 in this chapter), (b) Apply a UniProt similarity filter after generation (Step 4 in this chapter), (c) Increase sampling diversity by increasing the temperature (0.9+), (d) Enforce lower similarity using conditional generation with auxiliary loss. Source: Discussions in several de novo design papers [3][4].
3. **Target function (catalytic activity or fluorescence) is absent in actual measurement.** Symptoms: The 3D structure perfectly reproduces the required conditions. After experimental expression and purification, the actual activity is 0. Cause: Activity requires not only the arrangement of active site residues but also the overall folding stability, dynamics, and subtle side chain arrangements. Protein folding and expression yield are also separate issues. Mitigation: (a) Enhance activity prediction with MD simulations and Rosetta enzyme design, (b) Perform multiple parallel experiments with top candidates, (c) Automate high-throughput screening (yeast or phage display), (d) Use stability tags and fluorescent reporters in experimental measurements. Source: Discussion on experimental validation in Watson et al.'s RFdiffusion paper [1].
4. **Only the active site residues are maintained, but the orientation is incorrect.** Symptoms: Ser195, His57, and Asp102 are present in the sequence, but in 3D space, the catalytic arrangement (Ser hydroxyl forms a hydrogen bond with His) is not present. Cause: ESM3 only maintains the identity of residues and does not optimize the side chain rotamer or 3D relative position. Mitigation: (a) Inject backbone + side chain constraints together into the structure track of ESM3, (b) Post-process with Rosetta enzyme design to optimize side chains, (c) Post-validate CA-CA distance and dihedral angles of the active site (check ideal_distances in Step 1 of this chapter), (d) Validate dynamics with MDFF or MDshaping. Source: Discussion in the ESM3 paper [4]; Rosetta enzyme design tutorial.
5. **ESM3 commercial license limitations.** Symptoms: EvolutionaryScale ESM3 large (98B) model requires separate agreement for commercial use. The small (1.4B) model is open-source but still primarily for academic use. Cause: EvolutionaryScale policy and different license compared to the previous Meta ESM2. Mitigation: (a) For academic projects, use the open-weight 1.4B model, (b) For commercial projects, inquire about the license or use an alternative (RFdiffusion + ProteinMPNN combination), (c) Re-confirm the license terms when performing custom fine-tuning. Source: EvolutionaryScale ESM license [10].
## Extended Ideas
- **Complex design:** Design protein-protein interfaces instead of single proteins (linked to Boltz-2 in Chapter 11).- **Antibody design:** Design antibodies with CDR loop conditional generation (refer to RFantibody and IgLM).- **De novo enzyme with substrate:** Utilize the docking of a specific substrate binding site as a conditional constraint (refer to Chapter 8).- **Cyclic peptide:** Autonomously design stable cyclic peptides with an N-C connection constraint.- **Multi-objective optimization:** Simultaneously optimize for activity, stability (thermal stability, aggregation resistance), and solubility.- **Fluorescent protein design:** Design novel fluorescent proteins while maintaining the GFP chromophore (Ser65-Tyr66-Gly67) triad.
## Next Chapter
- Chapter 11: `structure-affinity-boltz`: Predicting ligand binding of generated proteins.- Chapter 12: `single-cell-perturbation`: In silico simulation of the response when the generated protein is expressed in cells.- Chapter 7: `drug-target-gnn`: GNN scoring of potential inhibitors for a novel protease.- Chapter 14: `bio-mcp-agent`: Exposing protein design as an MCP tool, enabling autonomous experimental design by an agent.
## References
1. Watson JL, Juergens D, Bennett NR, et al. "De novo design of protein structure and function with RFdiffusion." Nature 2023. `https://www.nature.com/articles/s41586-023-06415-8`2. Dauparas J, Anishchenko I, Bennett N, et al. "Robust deep learning-based protein sequence design using ProteinMPNN." Science 2022. `https://www.science.org/doi/10.1126/science.add2187`3. Ingraham J, Baranov M, Costello Z, et al. "Illuminating protein space with a programmable generative model (Chroma)." Nature 2023.4. Hayes T, Rao R, Akin H, et al. "Simulating 500 million years of evolution with a language model (ESM3)." bioRxiv 2024. `https://www.biorxiv.org/content/10.1101/2024.07.01.600583v1`5. EvolutionaryScale ESM GitHub: `https://github.com/evolutionaryscale/esm`6. RFdiffusion GitHub: `https://github.com/RosettaCommons/RFdiffusion`7. ProteinMPNN GitHub: `https://github.com/dauparas/ProteinMPNN`8. AlphaFold2 (DeepMind): `https://github.com/google-deepmind/alphafold`9. ColabFold: `https://github.com/sokrypton/ColabFold`10. EvolutionaryScale ESM License: `https://www.evolutionaryscale.ai/` · GitHub LICENSE11. Chroma (Baker Lab): `https://github.com/RosettaCommons/chroma`12. Baker Lab comprehensive: `https://www.bakerlab.org/`13. pyrosetta: `https://www.pyrosetta.org/`14. PyMOL open-source: `https://github.com/schrodinger/pymol-open-source`15. Boltz GitHub (refer to Chapter 11): `https://github.com/jwohlwend/boltz`16. Biopython: `https://biopython.org/`17. Foldseek (structure similarity search): `https://github.com/steineggerlab/foldseek`18. UniProt (natural sequence search): `https://www.uniprot.org/`19. RCSB PDB (chymotrypsin 4CHA, etc.): `https://www.rcsb.org/`20. RosettaCommons enzyme design tutorial: `https://www.rosettacommons.org/docs/latest/application_documentation/design/enzyme-design`