Diseño autónomo de proteínas basado en modelos fundacionales multimodales — Generación autónoma de 100 nuevas secuencias manteniendo el sitio activo de las serina proteasas
En los episodios 02 (incrustaciones de proteínas) y 11 (predicción de estructura y afinidad), vimos qué tan bien comprenden los modelos fundacionales las proteínas naturales. Sin embargo, la pregunta realmente interesante es: ¿puede este modelo diseñar desde cero nuevas proteínas que no existen en la naturaleza? ESM3 de EvolutionaryScale (2024) es el primer modelo fundacional de proteínas a gran escala capaz de generar condicionalmente cualquier modalidad, al haber aprendido las tres modalidades de secuencia, estructura y función dentro de un único transformador. En este episodio, construimos una herramienta práctica que fija el triplete catalítico (Ser195-His57-Asp102, numeración de la quimotripsina) del sitio activo de las serina proteasas y diseña de forma autónoma el resto de la secuencia. Generamos 100 candidatos reales de serina proteasa verdaderamente novedosos con una similitud inferior al 30% con respecto a las secuencias naturales, completando la validación de la estructura 3D mediante AF2/Boltz.
📚 Recomendación de episodios previos (muy recomendable)
Este episodio representa la cúspide de los contenidos avanzados y técnicos de IA y biología en DryBench. Antes de comenzar, se recomienda encarecidamente revisar primero los siguientes episodios de DryBench:
- DryBench ai-native #3 Transformadores e incrustaciones
- DryBench ai-native #12 Fundamentos de PyTorch
- DryBench ai-native #13 HuggingFace y API comerciales
Si se accede a este episodio sin los conocimientos previos, se iniciará directamente con el código práctico sin reexplicar los principios de los transformadores multimodales, el aprendizaje de modelos de lenguaje enmascarado y la gestión de modelos grandes en PyTorch, lo que dificultará su comprensión.
Lo que ya aprendimos en DryBench
En DryBench ai-native #3 aprendimos que las incrustaciones de los transformadores son escalables e independientes del dominio y la modalidad; en #12, adquirimos los fundamentos para optimizar tensores grandes, la memoria de GPU y la precisión mixta mediante PyTorch; y en #13, comprendimos que HuggingFace proporciona una API estándar para cargar e inferir modelos preentrenados.
ESM3 es un ejemplo paradigmático de cómo estos tres principios convergen en los dominios de las proteínas. Los tokens de secuencia, los tokens discretos de estructura (similares al alfabeto 3Di) y los tokens de dominio funcional se integran en un vocabulario unificado, y el transformador se entrena para predecir tokens en posiciones y modalidades arbitrarias mediante un enfoque de modelo de lenguaje enmascarado. Los resultados son notables: se puede fijar la estructura de un sitio activo específico y generar el resto de la secuencia, o codificar de forma autónoma nuevas secuencias condicionadas por una función deseada (por ejemplo, fluorescencia, catálisis, unión). Este artículo transforma esa capacidad en una herramienta práctica para el diseño de enzimas.
Definición del problema
Escenario práctico: Diseño autónomo de una nueva serina proteasa
Las serina proteasas son enzimas que degradan proteínas; la quimotripsina, la tripsina y la elastasa son ejemplos representativos. El mecanismo catalítico común implica que el hidroxilo de Ser195 actúa como nucleófilo, la His57 funciona como base general para el movimiento de protones y la Asp102 estabiliza a la His57. Este tríada debe mantener una disposición 3D óptima para la actividad.
Nuestro objetivo:
- Similitud de secuencia inferior al 30% con las serina proteasas naturales (verdaderamente novedosas).
- Mantener las coordenadas 3D del tríada catalítico Ser-His-Asp (RMSD ≤ 2 Å predicho por AF2).
- Generación autónoma de 100 candidatos (aproximadamente 30-60 minutos).
- Tasa de éxito ≥ 40% en la autoconsistencia (generación ESM3 → predicción AF2 → replicación del sitio activo).
- Revalidación selectiva de las 5 mejores mediante predicción de actividad experimental (Rosetta · MD).
El espectro del diseño de proteínas de novo
- Rosetta (desde 2003): Diseño de proteínas basado en la física; el estándar clásico.
- RFdiffusion (Baker Lab, 2023): Generación de la estructura principal mediante modelos de difusión. El diseño posterior de la secuencia se realiza con ProteinMPNN [1].
- ProteinMPNN (Baker Lab, 2022): Estándar para el rediseño de secuencias a partir de la estructura principal [2].
- RFdiffusion + ProteinMPNN + AF2: Pipeline estándar moderno (estructura principal → secuencia → validación).
- Chroma (2024, Baker Lab): Modelo atómico completo basado en la difusión [3].
- ESM3 (2024): Primer modelo fundacional multimodal a gran escala. Integración de la generación condicional [4].
- Boltz-2 (episodio 11): Especializado en la predicción; el diseño es limitado.
- Diseño condicionado por ligandos: RFdiffusion-Motif · Chroma-Ligand.
Este episodio combina la generación condicional de ESM3 (episodio principal) con una comparación de conjuntos mediante validación AF2/Boltz y RFdiffusion+ProteinMPNN (expansión).
Indicadores objetivo de este episodio
- Extracción de las coordenadas 3D del triplete catalítico a partir de la PDB de quimotripsina (1AB9, 4CHA, etc.).
- Generación de 100 secuencias con ESM3 condicional (longitud objetivo: 200~250 residuos).
- Filtro de diversidad (identidad por pares ≤ 70%).
- Prueba de similitud con la secuencia natural (BLAST de UniProt Swiss-Prot o incrustaciones de ESM) → mantener solo aquellas con ≤ 30%.
- Predicción de estructura con AF2/Boltz → proporción de reproducción del sitio activo con RMSD ≤ 2 Å ≥ 40%.
- pLDDT medio ≥ 70 (confiabilidad estructural).
Stack de herramientas e infraestructura requerida
| Herramienta | Función | Licencia |
|---|---|---|
ESM3 (esm3-sm-open-v1 1.4B) | Generación condicional de 3 vías | Abierto académico, comercial bajo negociación separada |
| ESM3 large (pesos abiertos, no comercial) | Modelo grande alternativo | Licencia EvolutionaryScale |
| RFdiffusion + ProteinMPNN | Comparación de conjuntos | Abierto académico |
| Boltz-2 (episodio 11) | Validación de predicción estructural de secuencias generadas | MIT |
| AlphaFold2 (o ColabFold) | Validación estructural alternativa | Apache 2.0 |
| Biopython · PyMOL | Manipulación y visualización de secuencias/estructuras | Licencia Biopython · LGPL |
| BLAST · Foldseek | Búsqueda de secuencias y estructuras naturales similares | Abierto académico |
| PyTorch | Backend | BSD |
Requisitos de infraestructura:
- Pesos abiertos pequeños de ESM3 1.4B: GPU con al menos 16-24GB de VRAM. Aproximadamente 4GB de VRAM en fp16.
- ESM3 grande (98B, privado): Solo acceso a API académico.
- RFdiffusion: GPU de centro de datos de 24-48GB de VRAM.
- Validación AF2/Boltz: Ver episodio 11 (GPU de gama alta para consumidores o GPU de centro de datos de 24-48GB de VRAM).
- Generación y validación por lotes grandes: Estación de trabajo de centro de datos con 80GB+ de VRAM (acceso general no disponible, se recomienda nube bajo demanda).
- RAM ≥ 32GB.
Costo estimado para la reproducción por parte del estudiante: Costo API 0 al usar GPU local. Generación de 100 secuencias + validación AF2/Boltz aprox. 4~8 horas (basado en GPU de 24-48GB de VRAM).
Implementación práctica del pipeline
Flujo general:
Paso 1. Definición del sitio activo · Preparación de las condiciones de ESM3
Se extraen las coordenadas 3D de los residuos del tríada catalítica de la quimotripsina (PDB 4CHA, 5CHA, 1AB9, etc.).
from dataclasses import dataclassfrom pathlib import Path
import torchimport numpy as npfrom Bio.PDB import PDBParser
@dataclassclass ActiveSiteConstraint: \"\"\"Definir condiciones del sitio activo.\"\"\" site_name: str # e.g. "serine_protease_triad" residue_positions: list[int] # Posición (0-based) del residuo en el sitio activo en la secuencia generada residue_types: list[str] # Aminoácidos en cada posición (ej. [\"S\", \"H\", \"D\"]) coordinates_backbone: np.ndarray # (N_residuos, 4, 3): Coordenadas N, CA, C, O ideal_distances: dict[tuple[int, int], float] # Distancia ideal entre residuos del sitio activo (Å)
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: \"\"\"Extraer coordenadas del triplete catalítico desde PDB.
Ejemplo de Quimotripsina (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 faltante: {chain_id}:{resid}\") aa = AA_3TO1.get(residue.get_resname().upper(), "X") if aa != expected_aa: raise ValueError(f\"Discrepancia de aminoácido en sitio activo {chain_id}:{resid}: esperado {expected_aa}, obtenido {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)
# Distancia ideal CA-CA entre residuos del sitio activo (basado en datos experimentales de quimotripsina) ca_positions = coordinates_arr[:, 1, :] # Solo CA 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, )
# Ejemplo: tríada catalítica de quimotripsinaCHYMOTRYPSIN_TRIAD_4CHA = [ ("A", 57, "H"), # His57 (histidine base) ("A", 102, "D"), # Asp102 (aspartate stabilizer) ("A", 195, "S"), # Ser195 (serine nucleophile)]Paso 2. Generación condicional de ESM3
El SDK de ESM3 permite especificar máscaras para las pistas de secuencia, estructura y función, y luego realizar una eliminación iterativa de las máscaras.
from esm.models.esm3 import ESM3from esm.sdk.api import ESMProtein, GenerationConfig
class ESM3Designer: """Diseño de proteínas condicional de ESM3."""
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, # longitud similar a la quimotripsina num_samples: int = 100, temperature: float = 0.7, seed: int | None = None, ) -> list[dict]: """Genera nuevas secuencias manteniendo las condiciones del sitio activo.
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): # Proteína inicial: secuencia enmascarada, posiciones de residuos del sitio activo fijas seq_list = ["_"] * target_length
# Posiciones del sitio activo (distribución aleatoria dentro de target_length, ej.: mitad) 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) # Verificar post-hoc si el residuo del sitio activo se mantuvo realmente 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}] Fallo en la generación: {e}")
return generated
def _distribute_triad_positions( self, target_length: int, n_triad: int, spread_ratio: float = 0.4, ) -> list[int]: """Distribución uniforme de residuos del sitio activo a lo largo de target_length.
La quimotripsina real está en las posiciones 57, 102 y 195. En una cadena de 245 residuos, esto corresponde al 21%, 42% y 80%. """ """ chymo_ref = [57, 102, 195] chymo_length = 245 positions = [ int(target_length * (p / chymo_length)) for p in chymo_ref[:n_triad] ] return positionsPaso 3. Filtro de diversidad
Asegurar la diversidad aplicando un filtro de identidad por pares a las secuencias generadas.
def compute_pairwise_identity(seq_a: str, seq_b: str) -> float: """Si las longitudes son iguales, posición por posición; si no, alineamiento.""" 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) # Si las longitudes difieren, alineamiento global 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]: """Filtro voraz: mantiene solo aquellos con baja identidad respecto a las secuencias ya aceptadas.""" 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 keptPaso 4. Verificación de la similitud con secuencias naturales
Utilice BLAST o los modelos de incrustación ESM del módulo 02, junto con FAISS, para verificar la similitud con secuencias naturales.
def blast_against_swissprot( query_seq: str, swissprot_fasta_path: Path, e_threshold: float = 1e-5,) -> list[dict]: """Verificación de similitud de secuencias naturales mediante BLAST.
En la práctica, se utiliza una base de datos local de BLAST (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]: """Comprueba si seq es lo suficientemente novedosa, con identidad máxima respecto a secuencias naturales.
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_idPaso 5. Validación de la estructura con AF2/Boltz
Predicción de la estructura 3D de la secuencia generada mediante AF2 o Boltz-2 (volumen 11) y comparación con las coordenadas del sitio activo original utilizando RMSD.
import subprocessimport yaml
def predict_structure_boltz(sequence: str, output_dir: Path, seq_id: str) -> Path: """Predicción de estructura mediante el pipeline Boltz-2 del capítulo 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"Fallo de Boltz-2 {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: """Cálculo de RMSD tras el alineamiento de Kabsch.""" 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]: """¿Reproduce la estructura predicha el sitio activo?
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 se almacena en el factor B del átomo CA (convención AF2/Boltz) 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_plddtPaso 6. Integración y clasificación de los candidatos
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: """Sitio activo → generación de nueva secuencia → diversidad, novedad y validación → clasificación.""" output_dir.mkdir(parents=True, exist_ok=True)
print("[1/6] Carga de restricciones del sitio activo") constraint = load_catalytic_triad_from_pdb(catalytic_pdb, triad_selection) print(f" triad: {constraint.residue_types}, distances: {constraint.ideal_distances}")
print("[2/6] Generación condicional de ESM3") 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" Generados {len(generated)}, sitio activo conservado {len(preserved)}")
print("[3/6] Filtro de diversidad") 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" Tras diversidad {len(diverse)}")
print("[4/6] Prueba de novedad de secuencia natural") 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] Verificación estructural con Boltz-2 ({len(novel_candidates)} casos)") 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, # calculado más abajo )) except Exception as e: print(f" [{i}] {seq_id} fallido: {e}")
print(f"[6/6] Clasificación compuesta · Guardar CSV") if not candidates_verified: return pd.DataFrame()
df = pd.DataFrame([asdict(c) for c in candidates_verified]) # compuesto: plddt (40%) + RMSD del sitio activo (40%) + novedad (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 compuestos: {df.head(5)['composite_score'].tolist()}") return df
# Ejemplo de ejecución (tríada catalítica de la quimotripsina → 100 nuevas serina proteasas)# 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,# )Paso 7. Comparación de los resultados de RFdiffusion + ProteinMPNN (extensión opcional)
Ejecutar el flujo de trabajo estándar del laboratorio Baker con las mismas condiciones del sitio activo y comparar los resultados.
def run_rfdiffusion_motif( active_site_pdb: Path, triad_residues: list[int], output_dir: Path, num_designs: int = 100,) -> list[Path]: """RFdiffusion motif scaffolding.
Para casos prácticos, consulte el ejemplo de GitHub de RosettaCommons RFdiffusion. """ """ # Concepto stub: llamada a la CLI de RFdiffusion # subprocess.run(["python", "run_inference.py", ...]) return [] # Implementación completa en producción
def run_proteinmpnn( backbone_pdb: Path, output_dir: Path, num_sequences: int = 10,) -> list[str]: """Coloca la secuencia en el backbone con ProteinMPNN.""" # subprocess.run(["python", "protein_mpnn_run.py", ...]) return []Rendimiento, costo y casos de fallo conocidos
Referencias de rendimiento (citas de evaluaciones comparativas públicas)
| Enfoque | Evaluación comparativa (autoconsistencia RMSD ≤ 2 Å de AF2) | Novedad | Fuente |
|---|---|---|---|
| Diseño de enzimas con Rosetta | 20–30 % | Baja | Baker Lab, 2003+ |
| ProteinMPNN solo (rediseño del esqueleto principal existente) | 40–50 % | Baja–Media | Dauparas et al., Science 2022 [2] |
| RFdiffusion + ProteinMPNN + AF2 | 60–70 % | Media–Alta | Watson et al., Nature 2023 [1] |
| Generación condicional de ESM3 | 40–55 % (estimado) | Media–Alta | Hayes et al., bioRxiv 2024 [4] |
| Chroma (todo-átomo de Baker Lab) | 65–75 % | Media–Alta | Ingraham et al., Nature 2023 [3] |
| RFdiffusion + ProteinMPNN + validación con Boltz-2 | ~65–72 % (conjunto de 11 miembros) | Media–Alta | Evaluación comparativa de la comunidad |
Observaciones prácticas: RFdiffusion + ProteinMPNN + AF2 es el estándar de oro para el diseño basado en el esqueleto principal. ESM3 destaca por su flexibilidad condicional (condiciones arbitrarias de función, secuencia y estructura). El enfoque de conjunto ofrece los mejores resultados.
Costo estimado de reproducción para estudiantes
- Costo de la API: 0 (completamente local).
- Generación de 100 secuencias: con ESM3 1.4B en una GPU de 16–24 GB de VRAM, entre 30 minutos y 1 hora.
- Validación de 100 secuencias con Boltz-2: entre 2 y 4 horas según el flujo de trabajo de un solo miembro.
- Descarga e indexación local de BLAST swissprot: aproximadamente 500 MB, entre 1 y 2 horas.
5 casos de fallo conocidos (recopilación de la comunidad y bibliográfica)
-
Alto pLDDT en la secuencia generada, pero fallo en la reproducción del sitio activo Síntoma: El pLDDT predicho por AF2/Boltz es superior a 80, pero la disposición de los residuos del sitio activo está desalineada. Causa: El pLDDT mide la confianza global y no refleja directamente la disposición 3D de residuos específicos. En particular, las regiones de lazo pueden tener un alto pLDDT a pesar de su flexibilidad. Prevención: (a) Verificar individualmente el pLDDT de los residuos del sitio activo (Paso 5 de este capítulo), (b) realizar obligatoriamente la comprobación del RMSD del sitio activo en la estructura predicha, (c) revalidar la estabilidad mediante simulaciones de dinámica molecular (OpenMM · GROMACS), (d) generar repetidamente con múltiples semillas y aplicar consenso. Fuente: Discusión sobre la autoconsistencia de RFdiffusion frente a AF2 [1].
-
Excesiva similitud con la secuencia natural (no es realmente novedosa) Síntoma: Al realizar una búsqueda BLAST en UniProt con la secuencia generada, se obtienen múltiples resultados con una identidad superior al 40%. Causa: El modelo base no logra desviarse de la distribución de secuencias naturales aprendida durante el entrenamiento. Solución: (a) aplicar filtros de diversidad estrictos (sección 3 de este artículo), (b) aplicar filtros de similitud con UniProt posteriormente (sección 4 de este artículo), (c) aumentar la temperatura para incrementar la diversidad del muestreo (0.9+), (d) forzar la generación condicional bajo condiciones de baja similitud (pérdida auxiliar). Referencia: Discusión en varios artículos sobre diseño de novo [3][4].
-
Ausencia de actividad funcional medida (catalítica o fluorescente) Síntoma: La estructura 3D reproduce perfectamente los requisitos solicitados, pero tras la expresión y purificación experimental, la actividad real es nula. Causa: La actividad no depende únicamente de la disposición de los residuos del sitio activo, sino también de la estabilidad global del plegamiento, la dinámica y el posicionamiento sutil de las cadenas laterales. El plegamiento de proteínas y el rendimiento de expresión son problemas independientes. Solución: (a) mejorar la predicción de actividad mediante simulaciones de dinámica molecular (MD) y diseño de enzimas con Rosetta, (b) someter a los mejores candidatos a múltiples experimentos en paralelo, (c) automatizar el cribado de alto rendimiento (display de levaduras o display de fagos), (d) utilizar etiquetas de estabilidad e indicadores fluorescentes junto con los ensayos experimentales. Referencia: Discusión sobre la validación experimental en el artículo de RFdiffusion de Watson et al. [1].
-
Solo se conservan los residuos del sitio activo, pero la orientación es incorrecta Síntoma: La secuencia contiene Ser195, His57 y Asp102, pero en el espacio 3D no se establece la disposición catalítica correcta (por ejemplo, el grupo hidroxilo de la serina no forma un enlace de hidrógeno con la histidina). Causa: ESM3 conserva solo la identidad del residuo, sin optimizar por separado los rotámeros de las cadenas laterales ni su posición relativa en 3D. Solución: (a) inyectar simultáneamente restricciones de cadena principal y cadenas laterales en el rastro estructural de ESM3, (b) optimizar las cadenas laterales posteriormente con diseño de enzimas mediante Rosetta, (c) verificar posteriormente las distancias CA-CA del sitio activo y los ángulos diedros (verificar
ideal_distancesen la sección 1 de este artículo), (d) validar la dinámica mediante MDFF y MDshaping. Referencia: Discusión en el artículo de ESM3 [4]; tutorial de diseño de enzimas con Rosetta. -
Limitaciones de la licencia comercial de ESM3 Síntomas: El modelo grande ESM3 de EvolutionaryScale (98B) requiere un acuerdo por separado para su uso comercial. La versión pequeña (1.4B) es de código abierto, pero sigue dando prioridad al uso académico. Causa: Políticas de EvolutionaryScale y licencia diferente a la del ESM2 existente de Meta. Solución: (a) Para proyectos académicos, utilizar los pesos abiertos de 1.4B; (b) para proyectos comerciales, consultar la licencia o utilizar alternativas (combinación RFdiffusion + ProteinMPNN); (c) al realizar un ajuste fino personalizado, volver a verificar las cláusulas de la licencia. Fuente: Licencia ESM de EvolutionaryScale [10].
Ideas para ampliar
- Diseño de complejos: Diseñar interfaces proteína-proteína en lugar de proteínas individuales (véase la sección 11 con Boltz-2).
- Diseño de anticuerpos: Generar bucles CDR condicionalmente para el diseño de anticuerpos (véase RFantibody e IgLM).
- Enzima de novo con sustrato: Utilizar el sitio de unión a un sustrato específico como condición para el acoplamiento (véase la sección 08).
- Péptido cíclico: Diseñar de forma autónoma péptidos cíclicos estables mediante condiciones de conexión N-C.
- Optimización multiobjetivo: Optimizar simultáneamente la actividad, la estabilidad (estabilidad térmica, resistencia a la agregación) y la solubilidad.
- Diseño de proteínas fluorescentes: Mantener el trípode Ser65-Tyr66-Gly67 del cromóforo de GFP para crear nuevas proteínas fluorescentes.
Próxima sección
- Sección 11
structure-affinity-boltz: Predicción de la unión a ligandos de las proteínas generadas. - Sección 12
single-cell-perturbation: Reacción in silico al expresar la proteína generada en células. - Sección 07
drug-target-gnn: Puntuación GNN del potencial inhibidor de una nueva proteasa. - Sección 14
bio-mcp-agent: Exponer el diseño de proteínas como herramienta MCP → diseño autónomo de experimentos por agentes.
Referencias
- 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 - Dauparas J, Anishchenko I, Bennett N, et al. "Diseño robusto de secuencias de proteínas basado en aprendizaje profundo utilizando ProteinMPNN." Science 2022.
https://www.science.org/doi/10.1126/science.add2187 - Ingraham J, Baranov M, Costello Z, et al. "Iluminando el espacio de proteínas con un modelo generativo programable (Chroma)." Nature 2023.
- Hayes T, Rao R, Akin H, et al. "Simulando 500 millones de años de evolución con un modelo de lenguaje (ESM3)." bioRxiv 2024.
https://www.biorxiv.org/content/10.1101/2024.07.01.600583v1 - EvolutionaryScale ESM en GitHub:
https://github.com/evolutionaryscale/esm - RFdiffusion en GitHub:
https://github.com/RosettaCommons/RFdiffusion - ProteinMPNN en GitHub:
https://github.com/dauparas/ProteinMPNN - AlphaFold2 (DeepMind):
https://github.com/google-deepmind/alphafold - ColabFold:
https://github.com/sokrypton/ColabFold - Licencia de EvolutionaryScale ESM:
https://www.evolutionaryscale.ai/· GitHub LICENSE - Chroma (Baker Lab):
https://github.com/RosettaCommons/chroma - Baker Lab (información general):
https://www.bakerlab.org/ - pyrosetta:
https://www.pyrosetta.org/ - PyMOL (código abierto):
https://github.com/schrodinger/pymol-open-source - Boltz en GitHub (véase la nota 11):
https://github.com/jwohlwend/boltz - Biopython:
https://biopython.org/ - Foldseek (búsqueda de similitud estructural):
https://github.com/steineggerlab/foldseek - UniProt (búsqueda de secuencias naturales):
https://www.uniprot.org/ - RCSB PDB (quimotripsina 4CHA, etc.):
https://www.rcsb.org/ - Tutorial de diseño de enzimas de RosettaCommons:
https://www.rosettacommons.org/docs/latest/application_documentation/design/enzyme-design