기능 상세 보고
기능 분석 보고서 — PyRosetta 도킹 엔진 (실측 ΔG)
분석 루트: AgenticAI4SCIENCE_pyrosetta_track/repos/ai4sci-kaeri
대상 파일:
- AG_src/scripts/flexpep_dock.py (실측 도킹·채점 핵심)
- pyrosetta_flow/docking_executor.py (subprocess 실행 레이어)
- pyrosetta_flow/scoring_pipeline.py (대안 스코어링 체인)
- pyrosetta_flow/runner.py (도킹 오케스트레이션·호출부)
- pyrosetta_flow/schema.py (FlowConfig 기본값)
작성 원칙: 모든 주장은 file_path:line 인용. 미확인 항목은 "미검증" 명시. 읽기전용 분석.
① 동작 원리
(a) 전체 호출 경로
도킹은 별도 conda 환경(bio-tools)의 standalone 스크립트를 subprocess로 호출하는 구조다.
runner.py:299에서 도킹 스크립트 경로를AG_src/scripts/flexpep_dock.py로 고정.- subprocess 실행은
docking_executor.py:_run_script(docking_executor.py:38-75) 가 담당하며,runner.py:195에서 re-export 하여 하위호환을 유지한다. - conda Python 경로 해석은
_resolve_conda_python(docking_executor.py:18-35):~/miniforge3|miniconda3|anaconda3/envs/<env>/bin/python직접 경로 → 없으면conda run -n <env>→ 그래도 없으면sys.executable폴백 (docking_executor.py:45-51). - 스크립트의 stdout 마지막 줄만 JSON 으로 파싱한다 (
docking_executor.py:65-75). 모든 PyRosetta 로그는 stderr 로 분리 (flexpep_dock.py:18-20,init_pyrosetta의-mute allflexpep_dock.py:38-45).
(b) FlexPepDock refine
- PyRosetta 초기화 옵션:
-mute all -ex1 -ex2aro -ignore_unrecognized_res -flexPepDocking:pep_refine -constraints:cst_fa_weight 1.0(flexpep_dock.py:38-45). - 변이체 입력은 MutateResidue 방식을 선호한다 (
flexpep_dock.py:52-151): 레퍼런스 복합체(AlphaFold3 등)의 백본을 보존하고 side-chain만 target 서열로 치환.runner.py:740-741에서--reference-complex <template> --target-sequence <mutant>인자로 전달. - 펩타이드 체인 자동 선택: 요청 체인이 없거나 길이가 target과 크게 다르면 길이 근접 체인으로 자동 전환 (
flexpep_dock.py:96-114). - 서열 차이 위치만 1→3letter 코드(
_AA1TO3flexpep_dock.py:75-80)로 변이 적용 (flexpep_dock.py:136-150). - 체인 재정렬: FlexPepDockingProtocol은 펩타이드가 마지막 체인일 것을 요구하므로, PDB 텍스트를 dump→체인 블록 재배치→reload 한다 (
reorder_peptide_lastflexpep_dock.py:158-236). PyRosetta 버전 호환성을 위해 객체 조작 대신 텍스트 조작을 택했다고 명시 (flexpep_dock.py:162-165). - 이황화결합 처리: 펩타이드(마지막 체인)에서 Cys 잔기 탐색(
_find_peptide_cys_residuesflexpep_dock.py:243-250). Cys가 정확히 2개면conformation().detect_disulfides()를 우선 사용하고, 실패 시 수동AtomPairConstraint(SG-SG harmonic 2.05 Å, sd 0.3) 폴백 (flexpep_dock.py:313-333,_add_disulfide_constraintflexpep_dock.py:253-272). refine 후 SG-SG 거리(<3.0 Å면 INTACT)를 검사해 결과에 포함 (_check_disulfide_distanceflexpep_dock.py:275-290,flexpep_dock.py:338-341). - 수동 constraint를 PDB 재정렬 직후 적용하면 AtomID 손상으로 segfault가 났던 이력이 주석에 기록됨 (
flexpep_dock.py:310-312). 이 때문에 detect_disulfides 우선. - 실제 refine 실행:
FlexPepDockingProtocol().apply(pose)(flexpep_dock.py:335-336). ab-initio 변형은set_lowres_preoptimize(True)추가 (flexpep_dock.py:369-371).
(c) InterfaceAnalyzer ddG
compute_interface_ddg(flexpep_dock.py:394-406):InterfaceAnalyzerMover(1)(jump_id=1로 수용체 체인 A ↔ 펩타이드 체인 B 분리),set_pack_input(True),set_pack_separated(True),apply후get_interface_dG()반환. 더 음수일수록 강한 결합.- 총 에너지:
compute_total_score=get_fa_scorefxn()(pose)(flexpep_dock.py:409-413). - 결과 JSON:
ddg,total_score,pre_score,score_delta,clash_score,constraint_violations(현재 항상 0,flexpep_dock.py:551), 그리고 Cys 2개일 때disulfide_intact/sg_sg_distance(flexpep_dock.py:545-558).
(d) clash 계산 (peptide-only)
compute_clash_score(flexpep_dock.py:416-446): fa_scorefxn 적용 후, 마지막 체인(펩타이드) 잔기만 순회하며 잔기별fa_rep > 10.0 REU개수를 센다 (flexpep_dock.py:441-446).- 펩타이드 체인 범위는
chain_begin/chain_end(n_chains)로 한정 (flexpep_dock.py:435-439). 단일 체인이면 전체 폴백. - 주석(
flexpep_dock.py:422-426)에 버그 수정 이력 기록: 이전 구현이 전체 pose(수용체+펩타이드 ~486잔기)를 세어 native·변이체 모두 clash≈50 으로 QC 게이트(≤10) 전원 탈락했던 문제를, 펩타이드 잔기만 세도록 수정함.
(e) 대안 스코어링 체인 (보조)
scoring_pipeline.py:_apply_alternative_scoring (scoring_pipeline.py:42-281)은 FlexPepDock ddG에 부가 점수를 적층한다. 각 단계는 독립적으로 graceful skip:
- Step 0: cheap objectives(반감기/ADMET surrogate, 서열 기반) scoring_pipeline.py:79-95.
- Step 0.5: pepADMET GNN 독성 추론(별도 env 배치 subprocess) → admet 페널티. 미설치/실패 시 skip, 즉 가짜 안전 판정을 만들지 않음(fail-closed) scoring_pipeline.py:99-120.
- Step 1: GNINA rescore (binary 없으면 dry-run) scoring_pipeline.py:123-157.
- Step 2: ECR consensus(GNINA+ddG 통합) scoring_pipeline.py:159-189.
- Step 3: Pareto/NSGA-II 순위(pymoo) scoring_pipeline.py:201-243.
- Step 4: BO 추천(부수효과 없음, 로그만) scoring_pipeline.py:249-279.
이 모듈은 ddG 자체를 만들지 않는다 — FlexPepDock 결과(ddG·clash)를 입력으로 받아 순위만 보강한다.
② 영향 (실측 vs mock, fail-closed 999)
실측 ΔG — mock 경로 없음
flexpep_dock.py전체에 mock/fake/placeholder/stub 도킹 경로는 없다(grep으로 미발견). PyRosetta가 import되지 않으면 스크립트 자체가 예외로 실패한다 (init_pyrosettaflexpep_dock.py:35-45, 각 함수의 지연 import).- 따라서 stdout JSON의
ddg는 항상 실제InterfaceAnalyzerMover.get_interface_dG()값이다 (flexpep_dock.py:406, 539). 실데이터 증거:runs/pyrosetta_flow/global_selectivity_leaderboard.json에ddg: -32.1162등 실측값 다수 기록. - "mock"이라는 용어는 보조 GNINA 단계의 dry-run 폴백에만 존재(
scoring_pipeline.py:7,54,123,147-148)하며, 이는 ddG가 아니라 부가 CNN 점수의 부재를 honest하게 표기하는 용도다.
fail-closed 999 규약
도킹 실패 시 가짜 좋은 점수 대신 999(=명백히 탈락)로 처리하는 것이 일관된 규약이다.
- 사전 게이트(예: FWKT pharmacophore) 실패 후보: _dock_one 진입 시 fail_reason이 있으면 도킹을 건너뛰고 ddg=999.0, total_score=999.0, clash_score=999.0 반환 (runner.py:721-732).
- subprocess 예외(타임아웃·크래시·JSON 파싱 실패 등): except 블록에서 동일하게 999로 마킹 (runner.py:757-767). _run_script가 returncode≠0/타임아웃/JSON 파싱 실패를 RuntimeError로 올린다 (docking_executor.py:57-75).
- baseline 도킹 실패: trial_result.get("ddg", 999.0) (runner.py:385).
- validation 트라이얼 실패: return 999.0 (runner.py:1342-1344), 이후 ddg < 900 / ddg <= 0 필터로 catastrophic 실패 제거 (runner.py:1362, 1371, 1379).
- 하류 소비: PASS 판정은 c.ddg <= ddg_threshold and c.ddg < 900 (runner.py:282), Pareto 입력·BO 관측도 ddg < 900 유효성 필터 적용(scoring_pipeline.py:252, runner.py:251-253). 즉 999는 순위/통계에서 자동 배제된다.
영향 요약: 도킹이 죽으면 결과가 사라지는 게 아니라 "확실히 나쁜 후보"로 표식되어 자연 도태된다 → 환각성 좋은 점수가 leaderboard에 침투할 수 없는 구조.
③ 관련 Action Item
- 코드 주석에 직접 기록된 결함/수정 이력(사실상의 Action Item closure):
- clash 전체-pose 집계 버그 수정(peptide-only로 전환) —
flexpep_dock.py:422-426. (사용자 메모리sstr2-clash-gate-issue [FIXED]와 일치.) - 이황화 수동 constraint segfault → detect_disulfides 우선 전환 —
flexpep_dock.py:310-312. - C3(무한 hang 방지) timeout 도입 —
docking_executor.py:55-58. script_timeout 300→600 상향(refine ~4min/후보) —schema.py:35. - C4(malformed stdout 방어) JSON 파싱 try/except —
docking_executor.py:68-75. - P1 분해: god-object runner에서 도킹 subprocess 레이어를
docking_executor.py로 추출 —runner.py:193-195,docking_executor.py:3. - 미확인(미검증): 별도의 구조화된 Action Item 레지스트리(예:
_workspace/release/의 R1~R7, VR-cycle 목록)는 본 분석 grep 범위에서 발견하지 못함 → 본 엔진과의 1:1 매핑은 미검증. (CLAUDE.md Stage 이력에 R1~R7/VR-cycle-10~14 언급은 있으나 본 도킹 파일들과의 직접 연결 코드는 확인 못 함.)
④ 완성도
완성도: 약 90%
근거:
- 실측 ΔG 파이프라인 완비: MutateResidue→체인 재정렬→이황화 처리→FlexPepDock refine→InterfaceAnalyzer ddG→peptide-only clash 전 과정이 구현·연결됨 (flexpep_dock.py:453-558).
- 운영 견고성 확보: timeout(docking_executor.py:55-58), JSON 파싱 방어(docking_executor.py:68-75), fail-closed 999(runner.py:721-767), 병렬 실행(runner.py:769-772), best-of-N baseline(runner.py:371-392), 멀티-트라이얼 검증+CV 조기종료(runner.py:1308-1395), baseline 캐시(runner.py:344-408) 등.
- 실제 산출물 존재: leaderboard에 실측 ddg/margin/hc50 539+ 후보 기록.
감점 요인:
- constraint_violations 가 항상 0 하드코딩 — 이황화/거리 위반을 점수에 반영하지 않음 (flexpep_dock.py:551). disulfide는 INTACT 플래그만 보고하고 게이트화 여부는 본 파일에서 미확인.
- ab-initio 경로는 detect_disulfides 우선 적용이 아니라 수동 constraint만 사용 (flexpep_dock.py:366-367) — refine 경로의 segfault 회피 패턴이 ab-initio엔 미적용(잠재 리스크, 미검증).
- ddG 절대값의 실험 친화도 대비 검증(calibration) 코드는 본 파일군에 없음 → 순위용 지표로만 신뢰 가능, 절대 친화도 주장 불가.
⑤ 학술 가치
중~상 (상에 근접)
근거:
- 상 요소: 표준 FlexPepDock refine + InterfaceAnalyzer ddG는 펩타이드-수용체 결합 평가에서 학계 통용 프로토콜이며, MutateResidue 기반 백본 보존 변이(flexpep_dock.py:52-151)는 변이체 ΔΔG 비교에 방법론적으로 타당. 멀티-트라이얼+CV 조기종료(runner.py:1366-1376)와 top-3 mean 채택(runner.py:1382, 1395)은 PyRosetta 확률적 출력의 재현성 문제를 정직하게 다룬다.
- 중으로 끌어내리는 요소: ddG 절대값 calibration 부재, constraint_violations 미반영, disulfide 게이트 강제 여부 미확인. 단일 receptor 구조(template) 의존 — ensemble/induced-fit 정도는 본 파일에서 미검증.
- 정직성(높은 학술 가치): mock 없음 + fail-closed 999 + GNINA dry-run 명시 표기 → "계산 불가능을 계산 가능한 척하지 않는다" 원칙이 코드에 구현됨. 이는 결과 신뢰성 주장에 유리.
⑥ 사용법
conda bio-tools subprocess 직접 호출
스크립트 헤더(flexpep_dock.py:8-17) 기준:
# 직접 복합체 입력
conda run -n bio-tools python AG_src/scripts/flexpep_dock.py \
--input complex.pdb --output refined.pdb --protocol flexpep_refine
# 레퍼런스+변이 (변이체 권장)
conda run -n bio-tools python AG_src/scripts/flexpep_dock.py \
--input complex.pdb --output refined.pdb --protocol flexpep_refine \
--reference-complex ref.pdb --target-sequence SGCKNFFWKTFTCA --peptide-chain 1
- 인자:
--input(필수),--output(필수),--protocol(flexpep_refine|flexpep_abinitio),--reference-complex,--target-sequence,--peptide-chain(기본 1) —flexpep_dock.py:457-477. - conda env 기본값
bio-tools(schema.py:21)._resolve_conda_python가 miniforge3/miniconda3/anaconda3 envs 경로를 자동 탐색 (docking_executor.py:26-33). - 출력은 stdout 마지막 줄 JSON, 로그는 stderr (
flexpep_dock.py:18-20).
파이프라인을 통한 병렬 호출 (max_workers)
- iteration 내 후보 도킹은
ThreadPoolExecutor(max_workers=...)로 병렬 (runner.py:769-772). max_workers = min(n_jobs, config.max_parallel_workers, os.cpu_count() or 4)(runner.py:699). 기본max_parallel_workers=32(schema.py:31).- validation 트라이얼은 별도 풀
min(remaining_trials, config.validation_max_workers, cpu_count)(runner.py:1347), 기본validation_max_workers=4(schema.py:43). - subprocess 타임아웃
script_timeout=600s(schema.py:35). 각 후보는 독립 subprocess라 GIL 영향 없음(실제 연산은 PyRosetta C++). - baseline은
n_baseline_trials=3best-of-N (schema.py:36,runner.py:371-392),reuse_baseline=True시 epoch 간 캐시 재사용 (runner.py:344-408,schema.py:54).
off-target 선택성 (관련 모듈)
- 선택성 도킹은
AG_src/scripts/offtarget_dock.py가 SSTR1/3/4/5에 동일 FlexPepDock+InterfaceAnalyzer 적용 (offtarget_dock.py:5-11). 비용 큼 →enable_selectivity=False기본 (schema.py:45-51).
⑦ 무관 시 필요 이유
본 엔진은 프로젝트 핵심과 직접 관련되어 "무관"에 해당하지 않는다. 근거: 프로젝트 목표가 SSTR2 표적 방사성의약품 후보의 결합 강도(ΔG) 기반 스크리닝(CLAUDE.md 프로젝트 컨텍스트)이며, 본 엔진이 그 ΔG를 산출하는 유일한 실측 채점원이다. leaderboard의 ddg/margin/delta_margin(runs/pyrosetta_flow/global_selectivity_leaderboard.json)이 모두 이 엔진 출력에 의존한다.
검증 인용 목록
pyrosetta_flow/docking_executor.py:18-35— conda Python 경로 해석pyrosetta_flow/docking_executor.py:38-75— subprocess 실행/timeout/JSON 파싱 방어pyrosetta_flow/scoring_pipeline.py:42-281— 대안 스코어링 체인(graceful skip, ddg<900 필터)pyrosetta_flow/scoring_pipeline.py:99-120— pepADMET fail-closed(가짜 안전판정 금지)AG_src/scripts/flexpep_dock.py:35-45— PyRosetta init 옵션AG_src/scripts/flexpep_dock.py:52-151— MutateResidue 변이체 준비AG_src/scripts/flexpep_dock.py:158-236— 펩타이드 마지막 체인 재정렬AG_src/scripts/flexpep_dock.py:243-341— 이황화 탐지/constraint/거리 검사AG_src/scripts/flexpep_dock.py:394-406— InterfaceAnalyzer ddGAG_src/scripts/flexpep_dock.py:416-446— peptide-only clash 계산AG_src/scripts/flexpep_dock.py:545-558— 결과 JSON 스키마(constraint_violations=0 하드코딩)pyrosetta_flow/runner.py:299— flexpep_dock.py 경로 고정pyrosetta_flow/runner.py:371-408— baseline best-of-N + 캐시pyrosetta_flow/runner.py:716-772— _dock_one + ThreadPool 병렬, fail-closed 999pyrosetta_flow/runner.py:1308-1395— 멀티-트라이얼 검증 + CV 조기종료pyrosetta_flow/schema.py:21,31,35,36,43,54— conda_env/max_workers/timeout/baseline 기본값runs/pyrosetta_flow/global_selectivity_leaderboard.json— 실측 ddg 값 존재 증거
미검증 항목: ① 구조화된 Action Item 레지스트리와 본 엔진의 1:1 매핑 ② disulfide INTACT 플래그의 게이트 강제 여부 ③ ab-initio 경로 segfault 회피 적용 ④ ddG 절대값 실험 calibration.