๊ธฐ๋Šฅ ์ƒ์„ธ ๋ณด๊ณ 

๋ณ€์ด ์ƒ์„ฑ ์—”์ง„ + Scaffold ๊ฐ€๋“œ โ€” ๊ธฐ๋Šฅ ๋ถ„์„ ๋ณด๊ณ ์„œ

๊ฒ€์ฆ ์›์น™: ๋ณธ ๋ณด๊ณ ์„œ์˜ ๋ชจ๋“  ๋™์ž‘ยท์ •๋Ÿ‰ ์ฃผ์žฅ์€ file_path:line ์ธ์šฉ์œผ๋กœ ๊ฒ€์ฆํ•œ๋‹ค. ์ฝ”๋“œ์—์„œ ํ™•์ธ๋˜์ง€ ์•Š์€ ๋‚ด์šฉ์€ "๋ฏธ๊ฒ€์ฆ"์œผ๋กœ ๋ช…์‹œํ•œ๋‹ค. ์ˆ˜์น˜๋Š” ์ฝ”๋“œ์— ๋ช…์‹œ๋œ ๊ฐ’๋งŒ ์ธ์šฉํ•œ๋‹ค.

๋ถ„์„ ๋ฃจํŠธ: AgenticAI4SCIENCE_pyrosetta_track/repos/ai4sci-kaeri ๋Œ€์ƒ ํŒŒ์ผ: pyrosetta_flow/runner.py, pyrosetta_flow/schema.py, pyrosetta_flow/adapter.py(๋ณ€์ด ์ƒ์„ฑ๊ธฐ), AG_src/llm/prompts.py


โ‘  ๋™์ž‘ ์›๋ฆฌ

(1) ๋ณ€์ด ์œ„์น˜ ์„ ํƒ โ€” _mutable_design_positions

๋ณ€์ด ๊ฐ€๋Šฅ ์œ„์น˜๋Š” config.design_positions ์—์„œ ์•ฝ๋ฆฌ๋‹จ(FWKT, 7-10) ๊ณผ Cys ์œ„์น˜(์ดํ™ฉํ™”) ๋ฅผ ์ œ์™ธํ•˜์—ฌ ์‚ฐ์ถœํ•œ๋‹ค.

  • design_positions ๊ธฐ๋ณธ๊ฐ’: [1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12] โ€” pyrosetta_flow/schema.py:14-18 (์ฃผ์„: ์‹ค์ œ ๋ณ€์ด ๊ฐ€๋Šฅ = [1,2,4,5,6,11,12] โ€” Cys3/14ยทFWKT ์ œ์™ธ, schema.py:15-16)
  • ์•ฝ๋ฆฌ๋‹จ ์œ„์น˜ ์ƒ์ˆ˜: PHARMACOPHORE_POSITIONS_1IDX = (7, 8, 9, 10) โ€” runner.py:102
  • Cys ์œ„์น˜ ์ถ”์ถœ: _disulfide_cys_positions ๊ฐ€ ์ฐธ์กฐ ์„œ์—ด์—์„œ C ์ธ ์œ„์น˜(1-indexed)๋ฅผ ๋ชจ๋‘ ๋ฐ˜ํ™˜ โ€” runner.py:114-116
  • ์ตœ์ข… ํ•„ํ„ฐ: mutable_positions = [pos for pos in config.design_positions if pos not in PHARMACOPHORE_POSITIONS_1IDX and pos not in cys] โ€” runner.py:135-138
  • ์•ˆ์ „ ํด๋ฐฑ: ํ•„ํ„ฐ ๊ฒฐ๊ณผ๊ฐ€ ๋น„๋ฉด config.design_positions ์ „์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ (mutable_positions or list(config.design_positions)) โ€” runner.py:139 โ†’ ๋ฏธ๊ฒ€์ฆ ์šฐ๋ ค: ์ด ํด๋ฐฑ์ด ๋ฐœ๋™ํ•˜๋ฉด FWKT/Cys ๊ฐ€ ๋ณ€์ด ํ›„๋ณด์— ๋‹ค์‹œ ํฌํ•จ๋  ์ˆ˜ ์žˆ์œผ๋‚˜, ํ›„์ˆ ํ•˜๋Š” _preserves_scaffold ๊ฒŒ์ดํŠธ๊ฐ€ ํ›„๋‹จ์—์„œ ๊ทธ๋Ÿฐ ์ œ์•ˆ์„ ๊ฑฐ๋ถ€ํ•˜๋ฏ€๋กœ ์‹ค์ œ ํ†ต๊ณผ๋Š” ์ฐจ๋‹จ๋จ(์•„๋ž˜ (3) ์ฐธ์กฐ).

์ด ํ•จ์ˆ˜์˜ ์‚ฐ์ถœ๋ฌผ์€ ๋ณ€์ด ๋ฃจํ”„ ์ง„์ž… ์ง์ „ ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋˜์–ด mutation_positions ๋กœ ์‚ฌ์šฉ๋œ๋‹ค โ€” runner.py:596.

(2) ์•ฝ๋ฆฌ๋‹จ(FWKT 7-10) ยท Cys3/Cys14 ๋ณด์กด ๋ฉ”์ปค๋‹ˆ์ฆ˜

์„ธ ๋‹จ๊ณ„๋กœ ์ž‘๋™ํ•œ๋‹ค.

  1. ์œ„์น˜ ์„ ํƒ ๋‹จ๊ณ„ ์ œ์™ธ (์‚ฌ์ „ ์ฐจ๋‹จ): ์œ„ (1)์—์„œ FWKTยทCys ๋ฅผ ๋ณ€์ด ํ›„๋ณด์—์„œ ์ œ๊ฑฐ โ€” runner.py:132-139.
  2. ์ƒ์„ฑ๊ธฐ ์ž์ฒด ์ œ์•ฝ: ์‹ค์ œ ๋ณ€์ด ์•„๋ฏธ๋…ธ์‚ฐ ํ’€์—์„œ Cys ๋ฅผ ๋ฐฐ์ œ. - AA_NO_CYS = list("ADEFGHIKLMNPQRSTVWY") (19์ข…, Cys ์—†์Œ) โ€” adapter.py:10 - generate_random_mutant ๊ฐ€ ์ด ํ’€์—์„œ๋งŒ ์น˜ํ™˜ โ€” adapter.py:53-54 - generate_guided_mutant ๋„ ์ถ”์ฒœ ํ’€์—์„œ C ๋ฅผ ๋ช…์‹œ ๋ฐฐ์ œ(aa != "C") โ€” adapter.py:91, ํด๋ฐฑ ํ’€๋„ AA_NO_CYS โ€” adapter.py:96 โ†’ ์ฆ‰ ๋ณ€์ด๋กœ ์ธํ•ด ์ƒˆ๋กœ์šด Cys ๊ฐ€ ๋„์ž…๋˜์ง€ ์•Š๋Š”๋‹ค. (๋‹จ, ๊ธฐ์กด Cys ๊ฐ€ ๋‹ค๋ฅธ AA ๋กœ ๋ฐ”๋€Œ๋Š” ๊ฒƒ์€ ์ƒ์„ฑ๊ธฐ๋งŒ์œผ๋กœ๋Š” ๋ชป ๋ง‰์Œ โ€” ๊ทธ๋ž˜์„œ (3) ๊ฒŒ์ดํŠธ๊ฐ€ ํ•„์š”.)
  3. ์‚ฌํ›„ ๊ฒŒ์ดํŠธ (๊ฑฐ๋ถ€): ์ƒ์„ฑ๋œ ํ›„๋ณด๊ฐ€ ์•ฝ๋ฆฌ๋‹จยท์ดํ™ฉํ™”๋ฅผ ๋ณด์กดํ•˜๋Š”์ง€ ๊ฒ€์ฆ โ€” ์•„๋ž˜ (3).

์•ฝ๋ฆฌ๋‹จ ๋ณด์กด ํŒ์ •: - _pharmacophore_slice(seq) = seq[6:10] (7-10 ์œ„์น˜ ์Šฌ๋ผ์ด์Šค) โ€” runner.py:106-107 - _preserves_pharmacophore ๋Š” ํ›„๋ณด์™€ ์ฐธ์กฐ์˜ 7-10 ์Šฌ๋ผ์ด์Šค๊ฐ€ ์™„์ „ ๋™์ผํ•  ๋•Œ๋งŒ True โ€” runner.py:110-111

์ดํ™ฉํ™” ๋ณด์กด ํŒ์ •: - _preserves_disulfide: ์ฐธ์กฐ ์„œ์—ด์˜ ๋ชจ๋“  Cys ์œ„์น˜๊ฐ€ ํ›„๋ณด์—์„œ๋„ C ์—ฌ์•ผ True โ€” runner.py:119-123 - ๊ฒฐํ•ฉ ๊ฒŒ์ดํŠธ _preserves_scaffold = ์•ฝ๋ฆฌ๋‹จ ๋ณด์กด AND ์ดํ™ฉํ™” ๋ณด์กด โ€” runner.py:126-129

(3) ๊ฑฐ๋ถ€ ๋กœ์ง (๋ณ€์ด ์ƒ์„ฑ ๋ฃจํ”„)

ํ›„๋ณด 1๊ฐœ ์ƒ์„ฑ์— ๋Œ€ํ•ด ์•ฝ๋ฆฌ๋‹จ ์žฌ์‹œ๋„ ์™ธ์ธต + dedup ๋‚ด์ธต์˜ ์ด์ค‘ ๋ฃจํ”„ ๊ตฌ์กฐ โ€” runner.py:597-687.

  • ์•ฝ๋ฆฌ๋‹จ ์žฌ์‹œ๋„ ํ•œ๋„: PHARMACOPHORE_RETRY_LIMIT = 3 โ†’ ์™ธ์ธต์€ range(PHARMACOPHORE_RETRY_LIMIT + 1) = 4ํšŒ โ€” runner.py:103, runner.py:601
  • dedup ๋‚ด์ธต: range(config.max_dedup_trials) (๊ธฐ๋ณธ max_dedup_trials = 50) โ€” runner.py:603, schema.py:40
  • ๋ณ€์ด ์ˆ˜ escalation: ๋‚ด์ธต trial ์ด ์ง„ํ–‰๋ ์ˆ˜๋ก ๊ฐ•์ œ ๋ณ€์ด ์ˆ˜๋ฅผ 1โ†’2โ†’max_random_mutationsโ†’max_random_mutations+1 ๋กœ ์ƒํ–ฅ (max_random_mutations ๊ธฐ๋ณธ 3) โ€” runner.py:605-611, schema.py:41
  • ๊ฑฐ๋ถ€ ๋ถ„๊ธฐ:
  • ์›๋ณธ๊ณผ ๋™์ผํ•˜๊ฑฐ๋‚˜ ์ด๋ฏธ ๋ณธ ์„œ์—ด โ†’ continue (์žฌ์‹œ๋„) โ€” runner.py:640-641
  • scaffold ๋ฏธ๋ณด์กด โ†’ break (ํ•ด๋‹น dedup ๋‚ด์ธต ์ฆ‰์‹œ ํƒˆ์ถœ) โ€” runner.py:642-643
  • ํ†ต๊ณผ ์‹œ mutant ํ™•์ • + seen_sequences ๋“ฑ๋ก โ€” runner.py:644-646
  • ํด๋ฐฑ ๊ฒฝ๋กœ: ๋‚ด์ธต์—์„œ mutant ๋ฏธํ™•์ • ์‹œ, ๋งˆ์ง€๋ง‰ ์ œ์•ˆ์ด scaffold ์œ„๋ฐ˜์ด๋ฉด ์™ธ์ธต continue; ์•„๋‹ˆ๋ฉด ๋ณ€์ด ์ˆ˜๋ฅผ max(2, len//2) ๋กœ ๊ฐ•์ œํ•œ ์ถ”๊ฐ€ random ํ›„๋ณด๋ฅผ ๋งŒ๋“ค์–ด scaffoldยทdedupยทnon-native ํ†ต๊ณผ ์‹œ ์ฑ„ํƒ โ€” runner.py:649-672
  • ์ตœ์ข… ์‹คํŒจ ์ฒ˜๋ฆฌ: 4ํšŒ ์™ธ์ธต์„ ๋ชจ๋‘ ์†Œ์ง„ํ•ด๋„ mutant ๊ฐ€ ์—†์œผ๋ฉด fail_reason = "FWKT pharmacophore gate failed after 3 retries" ๋กœ ํ‘œ์‹œํ•˜๊ณ  ๋งˆ์ง€๋ง‰ ์ œ์•ˆ์„ mutant ๋กœ ๋‘  โ€” runner.py:674-679. ์ด ํ›„๋ณด๋Š” ๋„ํ‚น ๋‹จ๊ณ„์—์„œ ddg=999.0 ๋“ฑ์œผ๋กœ ์ฆ‰์‹œ ์‹คํŒจ ์ฒ˜๋ฆฌ โ€” runner.py:721-732.

seen_sequences ์ดˆ๊ธฐ๊ฐ’์€ {config.original_sequence} ๋กœ, ๋„ค์ดํ‹ฐ๋ธŒ ์„œ์—ด์ด ํ›„๋ณด๋กœ ์ ˆ๋Œ€ ์ œ์ถœ๋˜์ง€ ์•Š๋„๋ก ํ•œ๋‹ค โ€” runner.py:458.

(4) LLM ํ”„๋กฌํ”„ํŠธ ์ธก ๋ณด์กด ๋ช…์„ธ โ€” AG_src/llm/prompts.py

LLM ์ง์ ‘ ๋ณ€์ด ์ƒ์„ฑ ๊ฒฝ๋กœ(build_variant_generation_prompt)๋Š” ๋™์ผ ์ œ์•ฝ์„ ์ž์—ฐ์–ด/few-shot ์œผ๋กœ ๊ฐ•์ œํ•œ๋‹ค.

  • ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ VARIANT_DESIGN_SYSTEM_PROMPT: "Preserve C3 and C14 (disulfide) โ€” positions 3 and 14 FIXED", "Preserve FWKT pharmacophore โ€” positions 7,8,9,10 FIXED" โ€” prompts.py:445-458
  • ๊ฑฐ๋ถ€ ์กฐ๊ฑด ๋ช…์‹œ: "Any mutation modifies positions 3, 7, 8, 9, 10, or 14" โ†’ output rejected โ€” prompts.py:456
  • build_variant_generation_prompt ๋ณธ๋ฌธ๋„ ๋™์ผ RULE 1ยท2 ๋ฐ˜๋ณต + few-shot 2๊ฐœ โ€” prompts.py:493-505
  • few-shot ์˜ˆ์‹œ(_SST14_FEW_SHOT_EXAMPLES): AGCKNFFWKTFTSC โ†’ PGCKHFFWKTFISC(pos 1/5/12 ๋ณ€์ด), AECKNLFWKTYTSC(pos 2/6/11 ๋ณ€์ด) โ€” prompts.py:417-442. ๋‘ ์˜ˆ์‹œ ๋ชจ๋‘ 3ยท7ยท8ยท9ยท10ยท14 ๋ฅผ ๋ณด์กดํ•œ๋‹ค(์œก์•ˆ ๊ฒ€์ฆ).
  • Planner ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ๋„ "PRESERVE FWKT pharmacophore (pos 7-10) and Cys3/Cys14 disulfide โ€” never mutate these" โ€” prompts.py:69-71

๋ถˆ์ผ์น˜ (๋ฏธ๊ฒ€์ฆ/์ฃผ์˜): build_variant_generation_prompt ์˜ docstringยท์˜ˆ์‹œ๋Š” ๊ฐ€๋ณ€ ์œ„์น˜๋ฅผ [1,2,4,5,6,11,12,13] (pos 13 ํฌํ•จ)์œผ๋กœ ๊ธฐ์ˆ  โ€” prompts.py:18,255,416,479. ๋ฐ˜๋ฉด ์‹ค์ œ ์—”์ง„ design_positions ๊ธฐ๋ณธ๊ฐ’์€ 13์„ ํฌํ•จํ•˜์ง€ ์•Š์Œ([...,11,12], schema.py:17). ๋˜ํ•œ format_planner_prompt ์˜ fallback ํ‘œ์‹œ ๋ฌธ์ž์—ด์€ [1,2,4,5,6,7,8,9,10,11,12,14](14 ํฌํ•จ) โ€” prompts.py:255. ์ด LLM-๊ฒฝ๋กœ ํ”„๋กฌํ”„ํŠธ์™€ ์‹ค์ œ ์‹คํ–‰ ๊ฒฝ๋กœ(generate_random_mutant/generate_guided_mutant, runner.py:614,627)์˜ ์œ„์น˜ ์ง‘ํ•ฉ์ด ์„œ๋กœ ๋‹ค๋ฅด๋‹ค. ์‹คํ–‰ ๊ฒฝ๋กœ์˜ scaffold ๊ฒŒ์ดํŠธ๊ฐ€ ์ตœ์ข… ์•ˆ์ „๋ง์ด๋ฏ€๋กœ ๊ฒฐ๊ณผ ์ •ํ•ฉ์„ฑ์€ ์œ ์ง€๋˜๋‚˜, ํ”„๋กฌํ”„ํŠธ์˜ ์œ„์น˜ ๋ช…์„ธ๋Š” ์ฝ”๋“œ ๊ธฐ๋ณธ๊ฐ’๊ณผ ๋™๊ธฐํ™”๋˜์–ด ์žˆ์ง€ ์•Š์Œ(๋ฏธํ•ด์†Œ ๋ฌธ์„œ ๋ถ€์ฑ„).


โ‘ก ์˜ํ–ฅ / ํšจ๊ณผ โ€” ์ดํ™ฉํ™” ํŒŒ๊ดด ๋ฐฉ์ง€๊ฐ€ ์—†์œผ๋ฉด?

_preserves_disulfide docstring ์ด ๋ถ€์žฌ ์‹œ ๋ฒ„๊ทธ๋ฅผ ์ง์ ‘ ๋ช…์‹œํ•œ๋‹ค:

"์ด์ „์—” ๊ฐ€๋“œ ๋ถ€์žฌ๋กœ C14โ†’H ๋ณ€์ด๊ฐ€ ํ†ต๊ณผํ•ด SS bond ๊ฐ€ ๊นจ์กŒ๋‹ค." โ€” runner.py:120-121

๊ฐ€๋“œ ๋ถ€์žฌ ์‹œ ๊ฒฐํ•จ (C14H ์‚ฌ๋ก€)

  • SST-14 ๋Š” Cys3-Cys14 ์ดํ™ฉํ™”๊ฒฐํ•ฉ์œผ๋กœ ๊ณ ๋ฆฌํ˜•(cyclic) ๊ตฌ์กฐ๋ฅผ ํ˜•์„ฑํ•œ๋‹ค(ํ”„๋กœ์ ํŠธ ์ปจํ…์ŠคํŠธ CLAUDE.md, prompts.py:456).
  • ๊ฐ€๋“œ๊ฐ€ ์—†์œผ๋ฉด ๋ณ€์ด ์ƒ์„ฑ๊ธฐ๊ฐ€ pos 14 ์˜ C ๋ฅผ ๋‹ค๋ฅธ ์•„๋ฏธ๋…ธ์‚ฐ(์˜ˆ: His)์œผ๋กœ ์น˜ํ™˜ํ•  ์ˆ˜ ์žˆ๊ณ , ๊ทธ ํ›„๋ณด๊ฐ€ ๊ฑฐ๋ถ€ ์—†์ด ๋„ํ‚นยท์ฑ„์ ๊นŒ์ง€ ์ง„์ž…ํ•œ๋‹ค.
  • ๊ฒฐ๊ณผ: ์ดํ™ฉํ™”๊ฒฐํ•ฉ์ด ๋Š๊ธด ์„ ํ˜• ํŽฉํƒ€์ด๋“œ๊ฐ€ "SST-14 analog" ์œผ๋กœ ํ‰๊ฐ€๋จ โ†’ ๊ตฌ์กฐยท๊ฒฐํ•ฉ ์ž์œ ์—๋„ˆ์ง€(ddG) ๊ฒฐ๊ณผ๊ฐ€ ์‹ค์ œ cyclic scaffold ์™€ ๋ฌด๊ด€ํ•œ ๊ฐ’์ด ๋จ โ†’ ํ›„๋ณด ๋žญํ‚นยท์„ ํƒ์„ฑ ํŒ์ • ์˜ค์—ผ.
  • ์ด๋Š” ๋‹จ์ˆœ ์ ์ˆ˜ ์˜ค๋ฅ˜๊ฐ€ ์•„๋‹ˆ๋ผ scaffold ์ •์ฒด์„ฑ ๋ถ•๊ดด(์„ค๊ณ„ ๋Œ€์ƒ์ด ์•„๋‹Œ ๋ถ„์ž๋ฅผ ํ‰๊ฐ€)์— ํ•ด๋‹น.

ํ˜„์žฌ ๊ฐ€๋“œ์˜ ํšจ๊ณผ

  • generate_random_mutant/generate_guided_mutant ๊ฐ€ Cys ๋„์ž…์„ ๋ง‰๊ณ (AA_NO_CYS, adapter.py:10,53,91,96), _mutable_design_positions ๊ฐ€ Cys ์œ„์น˜๋ฅผ ๋ณ€์ด ํ›„๋ณด์—์„œ ์ œ๊ฑฐํ•˜๋ฉฐ(runner.py:135-138), _preserves_scaffold ๊ฐ€ ์ž”์กดํ•˜๋Š” ์œ„๋ฐ˜ ํ›„๋ณด๋ฅผ ๋„ํ‚น ์ง์ „ ๊ฑฐ๋ถ€(runner.py:642-643)ํ•œ๋‹ค.
  • ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋„ํ‚น์— ๋“ค์–ด๊ฐ€๋Š” ๋ชจ๋“  ํ›„๋ณด๋Š” FWKT ์•ฝ๋ฆฌ๋‹จ + Cys3/Cys14 ์ดํ™ฉํ™” ๊ฐ€ ๋ณด์กด๋œ ํ•ฉ๋ฒ• scaffold ์ž„์ด ๋ณด์žฅ๋จ(ํ†ต๊ณผ ํ›„๋ณด ํ•œ์ •).
  • ํ†ตํ•ฉ ์Šค๋ชจํฌ ํ…Œ์ŠคํŠธ๊ฐ€ ์ด ๋ถˆ๋ณ€์‹์„ ๊ฐ„์ ‘ ๊ฒ€์ฆ: assert result["disulfide_intact"] is True, "Cys3-Cys14 ์ดํ™ฉํ™”๊ฒฐํ•ฉ ์œ ์‹ค" โ€” pyrosetta_flow/tests/test_integration_smoke.py:160.

โ‘ข ๊ด€๋ จ Action Item

retro-Q2 (_workspace/release/retro-2026-Q2.md:158-169)์˜ 8๊ฐœ Action Item ์€ ๋ชจ๋‘ CI ๋“ฑ๋กยทharness ๋ฉ”ํƒ€ยท๋ฌธํ—Œ LITERATURE_VALUES ๋“ฑ ์ธํ”„๋ผ/๋ฉ”ํƒ€ ์ž‘์—…์ด๋ฉฐ, scaffold ๊ฐ€๋“œ ๊ธฐ๋Šฅ ์ž์ฒด๋ฅผ ์ง์ ‘ ๋‹ค๋ฃจ๋Š” ํ•ญ๋ชฉ์€ ์—†์Œ.

๊ฐ€์žฅ ๊ทผ์ ‘ํ•œ ๊ฐ„์ ‘ ์—ฐ๊ฒฐ: - Q-2026-Q3-1 "CI์— pytest + routing test ๋“ฑ๋ก" (retro-2026-Q2.md:162): ๋ณธ ๊ฐ€๋“œ(_preserves_disulfide/_preserves_scaffold/_mutable_design_positions)์— ๋Œ€ํ•œ ์ง์ ‘ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ์ €์žฅ์†Œ์— ๋ถ€์žฌํ•จ์„ ํ™•์ธ(๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ pyrosetta_flow/tests, AG_src/tests, tests ์— ์ด ํ•จ์ˆ˜๋ช… ์ง์ ‘ ํ˜ธ์ถœ ํ…Œ์ŠคํŠธ ์—†์Œ โ€” ๊ฐ„์ ‘ ์Šค๋ชจํฌ๋งŒ ์กด์žฌ, test_integration_smoke.py:160). ๋”ฐ๋ผ์„œ ์ด ๊ฐ€๋“œ์˜ ํšŒ๊ท€ ๋ณดํ˜ธ๋Š” Q3-1 ์˜ ์‚ฌ์ •๊ถŒ์— ๋“ค์–ด๊ฐ€๋‚˜, ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •๋˜์ง„ ์•Š์Œ.

์—ฐ๋Œ€ ๊ด€๊ณ„: ๋ณธ ๊ฐ€๋“œ๋Š” 2026-06-10 ์— ๋„์ž…(runner.py:120,133, schema.py:15 ์ฃผ์„ ๋‚ ์งœ)๋˜์–ด, retro-Q2(2026-Q2 ๋ถ„๊ธฐ, CLAUDE.md Stage ์ด๋ ฅ์ƒ 2026-05-11 ์ž‘์„ฑ)๋ณด๋‹ค ์ดํ›„ ์ž‘์—…์ด๋‹ค. ๋”ฐ๋ผ์„œ retro-Q2 Action Item ๊ณผ๋Š” ์ง์ ‘ ๋ฌด๊ด€ํ•˜๋‹ค.

๊ฒฐ๋ก : ์ง์ ‘ ์—ฐ๊ฒฐ๋˜๋Š” Action Item ์—†์Œ. ์‚ฌ์œ  โ€” (a) ๊ฐ€๋“œ ๋„์ž…(2026-06-10)์ด retro-Q2(2026-05-11) ์ดํ›„, (b) retro-Q2 ์˜ 8๊ฑด์€ ๋ฉ”ํƒ€/CI/๋ฌธํ—Œ ์ž‘์—…์œผ๋กœ scaffold ๋ณด์กด ๋กœ์ง๊ณผ ๋„๋ฉ”์ธ์ด ๋‹ค๋ฆ„. ๊ธฐ๋Šฅ ๋™๊ธฐ๋Š” Action Item ์ด ์•„๋‹ˆ๋ผ ๋ฉ”๋ชจ๋ฆฌ์˜ ์„ ํƒ์„ฑ GOAL(sstr2-selectivity-goal, 2026-06-10)์— ์žˆ์Œ โ€” ๋ฌดํ•œ ๋ฐœ๊ตด ์—”์ง„์—์„œ ๋‹ค์ˆ˜ ๋ณ€์ด๋ฅผ ์ž๋™ ์ƒ์„ฑํ•  ๋•Œ scaffold ๋ฌด๊ฒฐ์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•จ.


โ‘ฃ ์™„์„ฑ๋„ % + ๊ทผ๊ฑฐ

์™„์„ฑ๋„: ์•ฝ 85%

๊ทผ๊ฑฐ: - (+) ํ•ต์‹ฌ ๋ณด์กด ๋กœ์ง 3์ค‘ ๋ฐฉ์–ด(์œ„์น˜ ์ œ์™ธ + ์ƒ์„ฑ๊ธฐ Cys ๋ฐฐ์ œ + ์‚ฌํ›„ ๊ฒŒ์ดํŠธ) ๋ชจ๋‘ ๊ตฌํ˜„ยท์—ฐ๊ฒฐ๋จ โ€” runner.py:135-138,642-643, adapter.py:10,91. - (+) ๊ฑฐ๋ถ€ ์‹œ ์žฌ์‹œ๋„ยทescalationยทํด๋ฐฑยท์‹คํŒจํ‘œ์‹œ๊นŒ์ง€ ์™„๋น„ โ€” runner.py:601-679. - (+) LLM ๊ฒฝ๋กœ ํ”„๋กฌํ”„ํŠธ์—๋„ ๋™์ผ ์ œ์•ฝ ๋ช…์„ธ + few-shot โ€” prompts.py:445-458,493-505. - (+) ๋„ํ‚น ๋‹จ๊ณ„๊ฐ€ fail_reason ํ›„๋ณด๋ฅผ ์ฆ‰์‹œ 999 ์ฒ˜๋ฆฌํ•˜์—ฌ ์˜ค์—ผ ์ฐจ๋‹จ โ€” runner.py:721-732. - (โˆ’) ์ง์ ‘ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ถ€์žฌ: _preserves_disulfide/_preserves_scaffold/_mutable_design_positions ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ๋ฏธ๋ฐœ๊ฒฌ(๊ฐ„์ ‘ ์Šค๋ชจํฌ 1๊ฑด๋งŒ, test_integration_smoke.py:160). C14H ํšŒ๊ท€๋ฅผ ๋ช…์‹œ ๋ฐฉ์–ดํ•˜๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์—†์Œ โ†’ ์•ฝ -10%. - (โˆ’) ํ”„๋กฌํ”„ํŠธโ†”์ฝ”๋“œ ์œ„์น˜ ๋ช…์„ธ ๋ถˆ์ผ์น˜: LLM ๊ฒฝ๋กœ ๊ฐ€๋ณ€ ์œ„์น˜([1,2,4,5,6,11,12,13]/14 ํฌํ•จ)์™€ ์‹คํ–‰ ๊ฒฝ๋กœ design_positions([...,11,12])๊ฐ€ ๋‹ค๋ฆ„ โ€” prompts.py:255,479 vs schema.py:17 โ†’ ์•ฝ -5%. - (โˆ’) _mutable_design_positions ํด๋ฐฑ(runner.py:139)์ด FWKT/Cys ๋ฅผ ์žฌํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ๋กœ ์กด์žฌ(๊ฒŒ์ดํŠธ๊ฐ€ ํ›„๋‹จ์—์„œ ์ฐจ๋‹จํ•˜๋‚˜ ๋ฐฉ์–ด์  ์„ค๊ณ„ ๊ด€์ ์—์„œ ๋ฏธ์„ธ ๊ฒฐํ•จ) โ€” ๋ฏธ๊ฒ€์ฆ ์˜ํ–ฅ, ๊ฐ์  ์†Œ.


โ‘ค ํ•™์ˆ  ๊ฐ€์น˜ (์ƒ / ์ค‘ / ํ•˜)

ํ‰๊ฐ€: ์ƒ(้ซ˜)

๊ทผ๊ฑฐ: - ํŽฉํƒ€์ด๋“œ/๋‹จ๋ฐฑ์งˆ ์„ค๊ณ„์—์„œ scaffold ์ œ์•ฝ(disulfide-constrained, pharmacophore-fixed mutagenesis) ์€ ํ‘œ์ค€์ ์ด๊ณ  ํ•™์ˆ ์ ์œผ๋กœ ์ค‘์š”ํ•œ ์„ค๊ณ„ ์›๋ฆฌ๋‹ค. ๋ฌด์ž‘์œ„/์œ ๋„ ๋ณ€์ด๊ฐ€ ์•ฝ๋ฌผํ™œ์„ฑ๋‹จ(FWKT)๊ณผ ๊ตฌ์กฐ ๊ฒฐ์ • ์š”์†Œ(์ดํ™ฉํ™”๊ฒฐํ•ฉ)๋ฅผ ๋ณด์กดํ•˜๋„๋ก ๊ฐ•์ œํ•˜๋Š” ๊ฒƒ์€, ์„ค๊ณ„ ๊ณต๊ฐ„์„ "์ƒ๋ฌผํ•™์ ์œผ๋กœ ์œ ํšจํ•œ analog" ๋กœ ํ•œ์ •ํ•˜๋Š” ํ•ต์‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด๋‹ค. - SST-14 ์˜ FWKT ๋Š” SSTR ๊ฒฐํ•ฉ ์•ฝ๋ฆฌ๋‹จ์œผ๋กœ ์•Œ๋ ค์ ธ ์žˆ๊ณ (prompts.py:449,497 ๋ช…์„ธ), Cys3-Cys14 ์ดํ™ฉํ™”๋Š” ํ™œ์„ฑ ํ˜•ํƒœ(ฮฒ-turn) ์œ ์ง€์— ํ•„์ˆ˜ โ€” ์ด๋ฅผ ์ฝ”๋“œ ๋ ˆ๋ฒจ์—์„œ ๋ถˆ๋ณ€์‹์œผ๋กœ ๋ฐ•์ œํ•œ ๊ฒƒ์€ "์œ ํšจ analog ๋งŒ ํ‰๊ฐ€" ๋ผ๋Š” ๋ฐฉ๋ฒ•๋ก ์  ์ •๋‹น์„ฑ์„ ๋ถ€์—ฌํ•œ๋‹ค. - ๋ณธ ๊ฐ€๋“œ๊ฐ€ ๋ง‰๋Š” ๊ฒฐํ•จ(scaffold ์ •์ฒด์„ฑ ๋ถ•๊ดด, โ‘ก์˜ C14H ์‚ฌ๋ก€)์€ in-silico ์Šคํฌ๋ฆฌ๋‹ ๊ฒฐ๊ณผ์˜ ํƒ€๋‹น์„ฑ(validity) ์— ์ง๊ฒฐ๋˜๋ฏ€๋กœ, ๋‹จ์ˆœ ์—”์ง€๋‹ˆ์–ด๋ง์ด ์•„๋‹Œ ๊ณผํ•™์  ๋ฐฉ๋ฒ•๋ก  ๊ฐ€๋“œ์˜ ์„ฑ๊ฒฉ์„ ๊ฐ€์ง„๋‹ค. - ๊ฐ์  ์š”์†Œ: ๋ณด์กด ํŒ์ •์ด "์œ„์น˜ ๋™์ผ์„ฑ"์ด๋ผ๋Š” ๋‹จ์ˆœ ๋น„๊ต(runner.py:111,123)๋กœ, ๋ณด์กด์  ์น˜ํ™˜(conservative substitution)ยท์ดํ™ฉํ™” ์œ„์ƒ(topology) ๊ฒ€์ฆ ๊ฐ™์€ ์ •๊ตํ•œ ๊ตฌ์กฐ ํ™”ํ•™ ๊ฒ€์ฆ์€ ์•„๋‹˜. ์ด๋Š” ์ ์ • ๋‹จ์ˆœํ™”์ด๋‚˜ "๊ณ ๊ธ‰ ํ•™์ˆ  ๊ธฐ์—ฌ"๋ผ๊ธฐ๋ณด๋‹ค "ํ•„์ˆ˜ ๊ฑด์ „์„ฑ ๊ฐ€๋“œ" ์ˆ˜์ค€. ๊ทธ๋Ÿผ์—๋„ ์Šคํฌ๋ฆฌ๋‹ ๊ฒฐ๊ณผ ์‹ ๋ขฐ์„ฑ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์ด ์ปค ์ข…ํ•ฉ ์ƒ.


โ‘ฅ ์‚ฌ์šฉ๋ฒ• (config / ํ•จ์ˆ˜)

๊ด€๋ จ config (FlowConfig, schema.py)

  • original_sequence (๊ธฐ๋ณธ "AGCKNFFWKTFTSC", schema.py:13) โ€” Cys ์œ„์น˜ยท์•ฝ๋ฆฌ๋‹จ ์Šฌ๋ผ์ด์Šค์˜ ๊ธฐ์ค€ ์„œ์—ด.
  • design_positions (๊ธฐ๋ณธ [1,2,4,5,6,7,8,9,10,11,12], schema.py:14-18) โ€” ๋ณ€์ด ํ›„๋ณด ํ’€์˜ ์›์ฒœ. ์—ฌ๊ธฐ์„œ FWKTยทCys ๊ฐ€ ์ž๋™ ์ œ์™ธ๋จ.
  • max_dedup_trials (๊ธฐ๋ณธ 50, schema.py:40) โ€” scaffold/dedup ์žฌ์‹œ๋„ ๋‚ด์ธต ํšŸ์ˆ˜.
  • max_random_mutations (๊ธฐ๋ณธ 3, schema.py:41) โ€” ๋™์‹œ ๋ณ€์ด ์ˆ˜ ์ƒํ•œ(escalation ์‹œ +1).
  • n_candidates (๊ธฐ๋ณธ 8, schema.py:19) โ€” iteration ๋‹น ์ƒ์„ฑ ํ›„๋ณด ์ˆ˜.

ํ˜ธ์ถœ ํ๋ฆ„ (์ž๋™, runner.py)

  1. run_pyrosetta_agentic_mutdock_flow(config) ์ง„์ž… โ€” runner.py:294.
  2. ๋ณ€์ด ๋‹จ๊ณ„์—์„œ mutation_positions = _mutable_design_positions(config) โ€” runner.py:596.
  3. ํ›„๋ณด๋ณ„ ๋ฃจํ”„์—์„œ generate_guided_mutant(๊ฐ€์ด๋“œ ์žˆ์„ ๋•Œ) ๋˜๋Š” generate_random_mutant ํ˜ธ์ถœ โ€” runner.py:613-638.
  4. ์ œ์•ˆ๋งˆ๋‹ค _preserves_scaffold(proposal, config.original_sequence) ๊ฒŒ์ดํŠธ โ€” runner.py:642. (์ง์ ‘ ํ˜ธ์ถœํ˜• ๋ณด์กฐ: _preserves_pharmacophore, _preserves_disulfide, _disulfide_cys_positions โ€” runner.py:110,119,114)

LLM ์ง์ ‘ ๋ณ€์ด ์ƒ์„ฑ (์„ ํƒ์  ๊ฒฝ๋กœ)

  • build_variant_generation_prompt(reference_sequence, mutable_positions, n_mutations, variant_id) โ€” prompts.py:461-532.
  • ์˜ต์…˜ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ: VARIANT_DESIGN_SYSTEM_PROMPT โ€” prompts.py:445.
  • ์‚ฌ์šฉ ์˜ˆ: provider.generate_json(prompt, system_prompt=VARIANT_DESIGN_SYSTEM_PROMPT) โ€” prompts.py:14-21,485 (docstring ์˜ˆ์‹œ). ๋‹จ, โ‘ฃ์—์„œ ์ง€์ ํ•œ ์œ„์น˜ ๋ช…์„ธ ๋ถˆ์ผ์น˜ ์œ ์˜.

โ‘ฆ (Action ๋ฌด๊ด€ ์‹œ) ์™œ ํ•„์š”ํ•œ๊ฐ€

๋ณธ ๊ธฐ๋Šฅ์€ retro-Q2 Action Item ๊ณผ ์ง์ ‘ ๋ฌด๊ด€(โ‘ข)ํ•˜๋ฏ€๋กœ ํ•„์š”์„ฑ์„ ๋ช…์‹œํ•œ๋‹ค.

  • ์œ ํšจ analog ๋ณด์žฅ: ๋ฌดํ•œ ๋ฐœ๊ตด ์—”์ง„(๋ฉ”๋ชจ๋ฆฌ sstr2-selectivity-goal, continuous.py)์€ ๋งค iteration ๋‹ค์ˆ˜ ๋ณ€์ด๋ฅผ ์ž๋™ ์ƒ์„ฑํ•œ๋‹ค. ๊ฐ€๋“œ๊ฐ€ ์—†์œผ๋ฉด ์ž๋™ ์ƒ์„ฑ ๊ณผ์ •์—์„œ FWKT/์ดํ™ฉํ™”๋ฅผ ๊นจ๋Š” ํ›„๋ณด๊ฐ€ ์„ž์—ฌ, ๊ฒฐํ•ฉํ™œ์„ฑ์„ ์žƒ์€ ๋ถ„์ž๊ฐ€ "SST-14 analog ํ›„๋ณด" ๋กœ ๋„ํ‚นยท๋žญํ‚น์— ์ง„์ž…ํ•œ๋‹ค.
  • C14H ํšŒ๊ท€์˜ ์˜๊ตฌ ์ฐจ๋‹จ: ์‹ค์ œ๋กœ ๊ฐ€๋“œ ๋ถ€์žฌ ์‹œ C14โ†’H ๋ณ€์ด๊ฐ€ ํ†ต๊ณผํ•ด SS bond ๊ฐ€ ๊นจ์ง„ ์ „๋ก€๊ฐ€ ์žˆ๋‹ค(runner.py:120-121). ๋ณธ ๊ฐ€๋“œ๋Š” ๊ทธ ํšŒ๊ท€๋ฅผ ์ฝ”๋“œ ๋ถˆ๋ณ€์‹์œผ๋กœ ๋ด‰์ธํ•œ๋‹ค.
  • ์„ ํƒ์„ฑ ์บ ํŽ˜์ธ ์‹ ๋ขฐ์„ฑ: ์บ ํŽ˜์ธ ๋ชฉํ‘œ๋Š” SSTR2 ์„ ํƒ์„ฑ(ฮ”margin>0, prompts.py:86-95)์ด๋‹ค. scaffold ๊ฐ€ ๊นจ์ง„ ํ›„๋ณด์˜ ddG/์„ ํƒ์„ฑ ์ˆ˜์น˜๋Š” ๋ฌด์˜๋ฏธํ•˜๋ฏ€๋กœ, ๊ฐ€๋“œ๋Š” ์„ ํƒ์„ฑ ํŒ์ •์˜ ์ž…๋ ฅ ๊ฑด์ „์„ฑ์„ ์ง€ํ‚ค๋Š” ์ „์ œ ์กฐ๊ฑด์ด๋‹ค.

๊ฒ€์ฆ ์ธ์šฉ ๋ชฉ๋ก (file:line)

pyrosetta_flow/runner.py - :102 โ€” PHARMACOPHORE_POSITIONS_1IDX = (7, 8, 9, 10) - :103 โ€” PHARMACOPHORE_RETRY_LIMIT = 3 - :106-107 โ€” _pharmacophore_slice (7-10 ์Šฌ๋ผ์ด์Šค) - :110-111 โ€” _preserves_pharmacophore (์Šฌ๋ผ์ด์Šค ๋™์ผ์„ฑ) - :114-116 โ€” _disulfide_cys_positions (์ฐธ์กฐ Cys ์œ„์น˜ 1-indexed) - :119-123 โ€” _preserves_disulfide + docstring "C14โ†’H ๋ณ€์ด๊ฐ€ ํ†ต๊ณผํ•ด SS bond ๊ฐ€ ๊นจ์กŒ๋‹ค" - :126-129 โ€” _preserves_scaffold = ์•ฝ๋ฆฌ๋‹จ AND ์ดํ™ฉํ™” - :132-139 โ€” _mutable_design_positions (FWKTยทCys ์ œ์™ธ + ํด๋ฐฑ) - :458 โ€” seen_sequences = {config.original_sequence} (๋„ค์ดํ‹ฐ๋ธŒ ์ œ์™ธ) - :596 โ€” mutation_positions = _mutable_design_positions(config) - :597-687 โ€” ํ›„๋ณด ์ƒ์„ฑ ์ด์ค‘ ๋ฃจํ”„ - :601 โ€” ์•ฝ๋ฆฌ๋‹จ ์žฌ์‹œ๋„ ์™ธ์ธต range(PHARMACOPHORE_RETRY_LIMIT + 1) - :603 โ€” dedup ๋‚ด์ธต range(max_trials) - :605-611 โ€” ๋ณ€์ด ์ˆ˜ escalation - :613-638 โ€” guided/random ๋ณ€์ด ํ˜ธ์ถœ - :640-643 โ€” ๊ฑฐ๋ถ€ ๋ถ„๊ธฐ(์›๋ณธ/์ค‘๋ณต continue, scaffold ์œ„๋ฐ˜ break) - :644-646 โ€” mutant ํ™•์ • + seen ๋“ฑ๋ก - :649-672 โ€” ํด๋ฐฑ ๊ฒฝ๋กœ - :674-679 โ€” ์ตœ์ข… ์‹คํŒจ fail_reason = "FWKT pharmacophore gate failed after 3 retries" - :721-732 โ€” fail_reason ํ›„๋ณด ddg=999 ์ฒ˜๋ฆฌ

pyrosetta_flow/schema.py - :13 โ€” original_sequence = "AGCKNFFWKTFTSC" - :14-18 โ€” design_positions ๊ธฐ๋ณธ๊ฐ’ + Cys14 ์ œ๊ฑฐ ์ฃผ์„ - :19 โ€” n_candidates = 8 - :40 โ€” max_dedup_trials = 50 - :41 โ€” max_random_mutations = 3

pyrosetta_flow/adapter.py - :10 โ€” AA_NO_CYS = list("ADEFGHIKLMNPQRSTVWY") - :36-55 โ€” generate_random_mutant (AA_NO_CYS ์น˜ํ™˜) - :58-99 โ€” generate_guided_mutant - :91 โ€” ์ถ”์ฒœ ํ’€์—์„œ aa != "C" ๋ฐฐ์ œ - :96 โ€” ํด๋ฐฑ ํ’€ AA_NO_CYS

AG_src/llm/prompts.py - :18,255,416,479 โ€” LLM ๊ฒฝ๋กœ ๊ฐ€๋ณ€ ์œ„์น˜ ๋ช…์„ธ([1,2,4,5,6,11,12,13] ๋“ฑ) โ€” ์ฝ”๋“œ ๊ธฐ๋ณธ๊ฐ’๊ณผ ๋ถˆ์ผ์น˜ - :69-71 โ€” Planner: "PRESERVE FWKT (7-10) and Cys3/Cys14 โ€” never mutate these" - :86-95 โ€” Critic: ์„ ํƒ์„ฑ ฮ”margin ๋ชฉํ‘œ - :417-442 โ€” _SST14_FEW_SHOT_EXAMPLES (3/7/8/9/10/14 ๋ณด์กด ์˜ˆ์‹œ) - :445-458 โ€” VARIANT_DESIGN_SYSTEM_PROMPT (RULE 1ยท2, ๊ฑฐ๋ถ€ ์กฐ๊ฑด) - :461-532 โ€” build_variant_generation_prompt - :493-505 โ€” ํ”„๋กฌํ”„ํŠธ ๋ณธ๋ฌธ RULE ๋ฐ˜๋ณต

ํ…Œ์ŠคํŠธ / Action Item - pyrosetta_flow/tests/test_integration_smoke.py:160 โ€” disulfide_intact is True ๊ฐ„์ ‘ ๊ฒ€์ฆ - _workspace/release/retro-2026-Q2.md:158-169 โ€” Phase E Action Item 8๊ฑด(์ง์ ‘ ๋ฌด๊ด€) - _workspace/release/retro-2026-Q2.md:162 โ€” Q-2026-Q3-1 "CI์— pytest ๋“ฑ๋ก"(๊ฐ„์ ‘ ์—ฐ๊ฒฐ)

๋ฏธ๊ฒ€์ฆ ํ•ญ๋ชฉ - _preserves_disulfide/_preserves_scaffold/_mutable_design_positions ์ง์ ‘ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ถ€์žฌ (์ €์žฅ์†Œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๋ฏธ๋ฐœ๊ฒฌ โ€” ์ง์ ‘ ํ˜ธ์ถœ ํ…Œ์ŠคํŠธ ์—†์Œ, ๊ฐ„์ ‘ ์Šค๋ชจํฌ๋งŒ ์กด์žฌ) - _mutable_design_positions ํด๋ฐฑ(runner.py:139) ๋ฐœ๋™ ์‹œ FWKT/Cys ์žฌํฌํ•จ ๊ฐ€๋Šฅ์„ฑ์˜ ์‹ค์ œ ์˜ํ–ฅ (๊ฒŒ์ดํŠธ ํ›„๋‹จ ์ฐจ๋‹จ์œผ๋กœ ๊ฒฐ๊ณผ ์˜ํ–ฅ์€ ์ฐจ๋‹จ ์ถ”์ •)