354

Linker Design 实战:连接子设计的计算方法

连接子设计是 PROTAC、双功能分子与片段连接的关键。这篇给出计算方法、约束条件与评估流程。

连接子(linker)设计出现在多个场景:PROTAC 连接靶蛋白配体与 E3 配体、片段连接(fragment linking)、双功能分子、抗体偶联药物。它的难点在于连接子既要保证两端能同时结合,又要自身尽量不损害性质。

连接子要满足的约束

维度 要求
长度 两端距离必须匹配,太短无法同时结合,太长熵损失大
出口向量 连接点的方向要与两端配体的出口朝向兼容
柔性 需要一定柔性适应构象,但过柔损失结合熵
性质 连接子贡献大量分子量与可旋转键,直接损害通透性
稳定性 避免易代谢或化学不稳定的连接(如酯、二硫)
合成可行性 需要可靠的连接化学

第一步:确定几何要求

from rdkit import Chem
from rdkit.Chem import AllChem
import numpy as np

def measure_exit_vectors(complex_pdb, lig1_resname, lig2_resname,
                         atom1_name, atom2_name):
    """从三元复合物结构中测量两个连接点的距离与方向"""
    from Bio.PDB import PDBParser
    parser = PDBParser(QUIET=True)
    st = parser.get_structure("x", complex_pdb)

    a1 = a2 = None
    for res in st.get_residues():
        if res.get_resname() == lig1_resname and atom1_name in res:
            a1 = res[atom1_name].get_coord()
        if res.get_resname() == lig2_resname and atom2_name in res:
            a2 = res[atom2_name].get_coord()

    if a1 is None or a2 is None:
        return None
    dist = np.linalg.norm(a1 - a2)
    return {"distance": float(dist),
            "vector": (a2 - a1).tolist()}

# 关键:连接子需要跨越的距离决定了它的长度范围
# 经验:每个 sp3 碳约贡献 1.2~1.5 Å 的伸展长度
#      距离 12 Å 大致需要 8~10 个原子的链(伸展构象)
#      但实际不会完全伸展,通常需要更长

第二步:枚举连接子

# 常用连接子片段库(按类型分组)
LINKERS = {
    "alkyl": [
        "CC", "CCC", "CCCC", "CCCCC", "CCCCCC",
    ],
    "peg": [                      # PEG 类:亲水、柔性
        "COC", "COCCOC", "COCCOCCOC", "COCCOCCOCCOC",
    ],
    "rigid_aromatic": [           # 刚性:减少熵损失
        "c1ccccc1", "c1ccc(cc1)c1ccccc1", "c1ccncc1",
    ],
    "rigid_aliphatic": [
        "C1CCC(CC1)",             # 环己烷
        "C1CC(C1)",               # 环丁烷
        "C12CC3CC(CC(C3)C1)C2",   # 金刚烷
    ],
    "piperazine": [               # 常用于改善溶解度
        "N1CCN(CC1)", "N1CCC(CC1)",
    ],
    "triazole": [                 # 点击化学产物,合成方便
        "c1cn(nn1)",
    ],
}

def build_linked_molecule(warhead1, linker, warhead2):
    """用连接点标记组装分子"""
    smi = f"{warhead1}{linker}{warhead2}"
    m = Chem.MolFromSmiles(smi)
    return Chem.MolToSmiles(m) if m else None

candidates = []
for ltype, linkers in LINKERS.items():
    for l in linkers:
        smi = build_linked_molecule(wh1_smiles, l, wh2_smiles)
        if smi:
            candidates.append({"smiles": smi, "linker": l, "type": ltype})

print(f"枚举出 {len(candidates)} 个候选")

第三步:几何可行性筛选

from rdkit.Chem import rdMolTransforms

def check_span(smiles, atom_idx1, atom_idx2, target_dist,
               tolerance=2.0, n_confs=100, seed=0xf00d):
    """检查分子能否达到目标跨距"""
    mol = Chem.AddHs(Chem.MolFromSmiles(smiles))
    p = AllChem.ETKDGv3(); p.randomSeed = seed; p.pruneRmsThresh = 0.5
    cids = AllChem.EmbedMultipleConfs(mol, numConfs=n_confs, params=p)
    if not cids:
        return False, None
    AllChem.MMFFOptimizeMoleculeConfs(mol, maxIters=500)

    dists = []
    for cid in cids:
        conf = mol.GetConformer(cid)
        d = rdMolTransforms.GetBondLength(conf, atom_idx1, atom_idx2) \
            if False else np.linalg.norm(
                np.array(conf.GetAtomPosition(atom_idx1)) -
                np.array(conf.GetAtomPosition(atom_idx2)))
        dists.append(d)

    dists = np.array(dists)
    feasible = np.abs(dists - target_dist) <= tolerance
    return bool(feasible.any()), {
        "min": float(dists.min()), "max": float(dists.max()),
        "n_feasible": int(feasible.sum()), "n_total": len(dists),
        "feasible_frac": float(feasible.mean()),
    }

# 关键指标:feasible_frac
#   高 → 分子容易达到所需构象,熵代价小
#   低 → 需要「碰运气」才能摆对,熵代价大,降解效率可能低

feasible_frac 是很有价值的指标:它反映的是「在所有可能构象中,有多大比例能满足几何要求」。这个比例低,意味着形成三元复合物需要付出很大的构象熵代价。

第四步:性质惩罚评估

from rdkit.Chem import Descriptors, Crippen

def linker_penalty(full_smiles, wh1_smiles, wh2_smiles):
    """评估连接子带来的性质代价"""
    full = Chem.MolFromSmiles(full_smiles)
    w1 = Chem.MolFromSmiles(wh1_smiles)
    w2 = Chem.MolFromSmiles(wh2_smiles)

    return {
        "total_mw": Descriptors.MolWt(full),
        "linker_mw": Descriptors.MolWt(full) - Descriptors.MolWt(w1)
                     - Descriptors.MolWt(w2),
        "total_rotb": Descriptors.NumRotatableBonds(full),
        "linker_rotb": (Descriptors.NumRotatableBonds(full)
                        - Descriptors.NumRotatableBonds(w1)
                        - Descriptors.NumRotatableBonds(w2)),
        "tpsa": Descriptors.TPSA(full),
        "clogp": Crippen.MolLogP(full),
    }

# 经验参考(PROTAC 类分子):
#   分子量常在 700~1100,远超 Ro5
#   可旋转键 10~20
#   TPSA 常 > 150
#   这些都严重损害被动通透性 —— 见「新药模态」模块关于
#   分子伞效应与构象塌缩的讨论

第五步:三元复合物建模

# 对 PROTAC 类分子,最终要看能否形成稳定的三元复合物

# 方案一:蛋白-蛋白对接 + 连接子约束
#   1) 用 PPI 对接程序采样两个蛋白的相对取向
#   2) 筛选出两个配体结合位点距离在连接子可及范围内的构象
#   3) 用连接子约束做精修

# 方案二:AF3 类模型直接预测(见 344)
#   Boltz / Chai-1 可以尝试预测含两个蛋白 + 一个配体的复合物
#   但这类体系超出常规训练分布,结果需谨慎对待

# 方案三:MD 采样验证稳定性
#   对候选三元复合物跑 MD,看是否稳定

# 现实:三元复合物预测仍是开放难题(见 359、370),
#      计算只能缩小范围,最终需要实验验证

常见坑与提示

  • 先从结构中测量所需跨距,连接子长度是硬约束
  • feasible_frac(可行构象比例)比「能否达到」更有信息量;
  • 连接子大幅增加分子量与可旋转键,通透性代价必须一并评估;
  • 三元复合物预测仍不成熟,计算只能缩小范围,需实验验证。

延伸资源