DeepRetro 完整教學
本文目的:把 DeepRetro 的「為什麼要用 LLM 做逆合成」、「整個 pipeline 怎麼運作」、「怎麼跑起來然後接進你的 lead optimization 工作流」一次講清楚。重點放在架構理解、實務操作、幻覺防護機制、以及對你場景的適用性評估。
1. 專案定位與動機
1.1 一句話總結
DeepRetro 是一個 開源的混合式逆合成規劃工具 (hybrid retrosynthesis planning tool),先讓 AiZynthFinder(template-based MCTS engine)嘗試,失敗時再讓 LLM(Claude / DeepSeek-R1)做 single-step disconnection,然後遞歸處理每個前驅物,直到所有分子都能從商業可購的 building block 合成。
1.2 為什麼需要 LLM 做逆合成
傳統逆合成工具(MCTS-based 如 AiZynthFinder, ASKCOS, Synthia)的局限:
| 傳統方法的問題 | DeepRetro 的解法 |
|---|---|
| Template 覆蓋率有限:只能建議 training set 見過的反應類型 | LLM 見過整個化學文獻,能提出 novel disconnection |
| 失敗就失敗:找不到路徑就回傳空結果 | AZ 失敗 → LLM 接手,不放棄任何分子 |
| 無法解釋推理過程 | LLM 回覆含 Chain-of-Thought,可以看到每步的推理依據 |
| 固定搜尋策略 | 可以透過不同 prompt 版本(V1-V4)調整搜尋偏好 |
| 對新骨架無能為力 | LLM 的 generalization 能力可以處理 training set 之外的結構 |
1.3 與其他工具的定位差異
| 工具 | 方法 | LLM 角色 | 開源 |
|---|---|---|---|
| AiZynthFinder | Template-based MCTS | 無 | 是 |
| ASKCOS | Template + NN | 無 | 是 |
| Synthia (Merck) | Rules + MCTS | 無 | 否 |
| ChemCrow | LLM agent + tool use | 主導但無 recursive | 是 |
| DeepRetro | AZ 先行 + LLM fallback + recursive refinement | 後援 + recursive | 是 |
DeepRetro 的獨特之處:不是讓 LLM 完全主導(那樣幻覺太多),而是讓 LLM 只在 template engine 失敗時介入,且每個建議都要過 heuristic + ML 雙重幻覺檢測。
1.4 對你的場景意味著什麼
你做 lead optimization,產出新的 analog(類似物)後需要:
- 確認 analog 可合成 — DeepRetro 可以快速給出合成路線
- 合成步驟不要太多 — AiZynthFinder 的 tree search 天生偏好短路線;max_depth 可調
- Building block 要可購買 — AZ 用 ZINC stock 做 in-stock 判斷
2. 安裝指南
2.1 兩種部署方式
| 模式 | 場景 | 複雜度 |
|---|---|---|
| Docker(推薦) | 快速上手、不碰本機環境 | 低 |
| 本機開發 | 需要改程式碼、debug、整合到管線 | 中 |
2.2 Docker 部署(5 分鐘)
1# 1. Clone
2git clone https://github.com/deepforestsci/DeepRetro.git
3cd DeepRetro
4
5# 2. 設定環境變數
6cp env.example .env
7# 編輯 .env,填入你的 API key:
8# API_KEY=your-backend-api-key # 用來驗證 API 呼叫
9# ANTHROPIC_API_KEY=sk-ant-xxx # Claude API key
10# FIREWORKS_API_KEY=xxx # 如果要用 DeepSeek-R1
必填:
API_KEY(自訂,用於 X-API-KEY header)+ 至少一個 LLM API key。
1# 3. Build & Start(首次約 10-15 分鐘,會下載 USPTO 模型 ~1.5GB)
2docker-compose up -d --build
3
4# 4. 驗證
5curl -H "X-API-KEY: your-backend-api-key" http://localhost:5000/api/health
6# 預期回覆:{"status": "healthy"}
2.3 本機開發安裝
1# 需要 Python 3.9 + Conda
2conda env create -f environment.yml
3conda activate deepretro
4
5# 下載 USPTO 模型
6mkdir -p aizynthfinder/models
7python -m aizynthfinder.tools.download_public_data aizynthfinder/models/
8
9# 設定 .env(同上)
10cp env.example .env
11# 編輯 .env
12
13# 啟動後端
14python src/api.py
15# 另一個 terminal 啟動前端
16cd viewer && python -m http.server 8000
2.4 環境變數完整清單
| 變數 | 必要性 | 說明 |
|---|---|---|
API_KEY | 必填 | 後端 API 認證 key |
ANTHROPIC_API_KEY | 必填(用 Claude 時) | Anthropic API key |
FIREWORKS_API_KEY | 選填 | DeepSeek-R1 via Fireworks |
OPENAI_API_KEY | 選填 | OpenAI 模型 |
ENABLE_LOGGING | 選填 | True / False,控制 structlog |
LANGFUSE_SECRET_KEY | 選填 | Langfuse LLM observability |
AZ_MODELS_PATH | 選填 | AiZynthFinder 模型路徑 |
3. 核心架構深入解析
3.1 Pipeline 全景
1Target SMILES
2 │
3 ▼
4┌─────────────────────────┐
5│ rec_run_prithvi() │ ◄── 遞歸入口
6│ ┌─────────────────────┐│
7│ │ 1. is_basic_molecule?││ → 碳原子 < 5 → 直接標記 in_stock
8│ │ 2. cycle detection ││ → canonical SMILES 去重
9│ │ 3. depth >= 50? ││ → 防止無窮遞歸
10│ │ 4. run_az() ││ → AiZynthFinder tree search
11│ │ ├─ solved → 回傳 ││
12│ │ └─ not solved ────┼┼─┐
13│ └─────────────────────┘│ │
14│ │ │
15│ ┌─────────────────────┐│ │
16│ │ 5. llm_pipeline() ││◄─┘
17│ │ ├─ call_LLM() ││ → LiteLLM 呼叫(temperature=0.0 起)
18│ │ ├─ split_cot_json ││ → 解析 <cot>/<json> 結構
19│ │ ├─ validate_json ││ → 提取 data/explanation/confidence
20│ │ ├─ validity_check ││ → RDKit SMILES 驗證
21│ │ ├─ stability_check││ → 化學穩定性(選填)
22│ │ └─ hallucination ││ → 幻覺評分(選填)
23│ └─────────────────────┘│
24│ │
25│ ┌─────────────────────┐│
26│ │ 6. 遞歸 ││ → 對每個 validated precursor
27│ │ rec_run_prithvi() ││ 重新走步驟 1-6
28│ └─────────────────────┘│
29└─────────────────────────┘
30 │
31 ▼
32┌─────────────────────────┐
33│ add_metadata() │ → reagent_agent / conditions_agent / literature_agent
34└─────────────────────────┘
35 │
36 ▼
37 JSON result(tree structure)
3.2 遞歸邏輯(rec_run_prithvi())的三個關鍵設計
Cycle detection:用 visited set + canonical SMILES 避免 A → B → A 的無窮迴圈。RDKit 先做 canonicalization 確保同一分子的不同 SMILES 寫法不會繞過 detection。
Depth limiting:max_depth=50,避免過深的路線。實務上,你可能想調到 5-10(大多數可行的合成路線在 5 步以內)。
First-success-break:LLM 可能回傳多條路徑(3-5 條),但遞歸只要找到一條全部 solved 的就 break。這是效率考量。
3.3 LLM 呼叫策略
llm_pipeline() 有一個內建的 retry 機制:
1run = 0.0
2max_run = 1.5 if (stability or hallucination) else 0.6
3while output_pathways == [] and run < max_run:
4 call_LLM(molecule, temperature=run)
5 run += 0.1
- 從 temperature=0.0 開始(最確定的回覆)
- 如果失敗,逐步提高 temperature 到 0.6 或 1.5
- 每次呼叫都做 split → validate → check 的完整流程
- DeepSeek 第一次失敗後 fallback 到 Claude Opus
3.4 AiZynthFinder 整合細節
- Stock:使用 ZINC database 做 in-stock 判斷
- Expansion policy:USPTO(免費)或 Pistachio(商業授權)
- Filter policy:USPTO filter
- 基本分子 bypass:
BASIC_MOLECULESlist 含 80+ 常見試劑/溶劑(乙醇、醋酸、Grignard 等),這些直接標記in_stock=True不進 tree search
4. Prompt 工程:四個版本的演進
DeepRetro 的 prompt 設計是理解這個專案的核心。四個版本反映了 prompt engineering 在化學領域的最佳實踐演進。
4.1 V1(基礎版 SYS_PROMPT + USER_PROMPT)
- System prompt 設定 “expert organic chemist specializing in retrosynthesis”
- 要求 CoT reasoning 在
<thinking>tags 裡 - 輸出格式:
<cot>+<json>分區 - JSON 結構:
data(SMILES list of lists) +explanation+confidence_scores
4.2 V4(進階版 SYS_PROMPT_V4)
比 V1 多了:
- Initial validation:要求 LLM 先驗證 SMILES 有效性
- 四階段結構化分析:
structural_decomposition→ 骨架、官能基、立體中心disconnection_analysis→ 策略性斷鍵、transforms、stereochemical strategypractical_evaluation→ 商業可得性、反應條件、製程考量proposal_refinement→ 排序、文獻驗證、failure mode
- 每階段都有 “wait” 反思步驟:強制 LLM 回顧自己的分析
- Edge case handling:複雜分子、簡單分子、特殊結構的處理指南
- Quality checks:SMILES 有效性、解釋完整性、分數校準
- Confidence score 公式:feasibility 33% + implementation 33% + strategic value 34%
4.3 DeepSeek 專用 prompt
- 使用
<think>而非<cot>tags(符合 DeepSeek-R1 原生格式) - max_completion_tokens 加倍到 16384
- V4 版本不用 system prompt(DeepSeek 風格),全部塞進 user prompt
4.4 7-Member Ring Addon
偵測到 7 元環時自動注入範例:
1Examples of some 7-membered rings retrosynthesis:
21. ClC(C(OC)=C1)=CC2=C1[C@@H]3... when broken down gives ...
這解決了 LLM 對非典型環系統容易幻覺的問題。
5. 幻覺防護機制(Hallucination Guard)
這是 DeepRetro 最有價值的技術貢獻。LLM 做逆合成最大的問題就是「看起來合理但化學上不可能」的幻覺。
5.1 Heuristic Checker(hallucination_checker.py)
核心函數 calculate_hallucination_score(reactant_smiles, product_smiles) 做 5 項檢查:
| 檢查項目 | 扣分規則 | 原理 |
|---|---|---|
| Atom count mismatch | 每個 mismatch 的原子 -5 分(上限 100) | 反應不能無中生有原子 |
| Ring size change | 每個環變化 -25 分(上限 50) | 環系統不應隨意增減 |
| Substituent position swap | 每次 swap -60 分(上限 100) | LLM 常把 ortho/meta/para 搞混 |
| Aromaticity shift | -40 分 | 芳香性不應大幅改變 |
| Unnecessary bond formation | 每條多餘鍵 -5 分(上限 30) | 反應物不該比產物多鍵 |
Score 100 → 0 分制:
= 80:low severity → 保留
- 40-79:medium severity → 保留
- 20-39:high severity → 過濾掉
- < 20:critical severity → 過濾掉
5.2 ML Classifier(hallucination_classifier.py)
- 預訓練模型(
model_out/model.joblib,scikit-learn) - 使用 domain features 做二元分類
- 與 heuristic checker 互補
5.3 實務意義
對你做 lead optimization analog 的逆合成:
- 不需要手動 review 每條 LLM 建議的路線
- 明顯不合理的(substituent 換位、原子數不對)自動過濾
- 保留 severity low/medium 的路線給化學家做最終判斷
6. Web GUI 互動操作
6.1 啟動
1# Docker 用戶:已自動啟動
2# 本機開發:
3cd viewer && python -m http.server 8000
4# 瀏覽器開 http://localhost:8000
6.2 主要功能
Smart Retrosynthesis:
- 在 SMILES 欄位輸入目標分子
- 選擇 LLM 模型(Claude 4 Sonnet / Claude 3.7 / DeepSeek-R1)
- 勾選 Advanced Prompt / Stability Check / Hallucination Check
- 點 “Analyze”
- 結果以 D3.js tree 呈現,每個節點是一個分子
Partial Rerun(核心功能):
- 對 tree 中任一步驟不滿意 → 點擊該步驟 → 選擇 “Rerun from here”
- 系統會保留左側(upstream)的路徑,只重新規劃右側(downstream)
- 使用者可以指定不同的 LLM 或設定
File Management:
- 上傳 / 下載 JSON 格式的路徑結果
- 可以儲存 → 修改 → 重新載入 → partial rerun
6.3 Advanced Settings
1{
2 "llm_models": {
3 "claude4": {
4 "internal_name": "claude-4-sonnet-20250514",
5 "display_name": "Claude 4 Sonnet",
6 "supports_advanced_prompt": true,
7 "supports_stability_check": true,
8 "supports_hallucination_check": true
9 }
10 },
11 "az_models": {
12 "USPTO": { "display_name": "USPTO (Free)", "requires_permissions": false }
13 }
14}
7. API 使用指南
7.1 基本呼叫
1# 完整逆合成
2curl -X POST http://localhost:5000/api/retrosynthesis \
3 -H "Content-Type: application/json" \
4 -H "X-API-KEY: your-api-key" \
5 -d '{
6 "smiles": "CC(C)(C)OC(=O)N[C@@H](CC1=CC=CC=C1)C(=O)O",
7 "model_type": "claude4",
8 "advanced_prompt": true,
9 "model_version": "USPTO",
10 "stability_flag": true,
11 "hallucination_check": true
12 }'
7.2 回傳結構
1{
2 "steps": [
3 {
4 "step": "1",
5 "reactants": ["SMILES1", "SMILES2"],
6 "products": [{"smiles": "target_SMILES"}],
7 "reagents": ["SMILES_reagent"],
8 "conditions": {"temperature": "25C", "solvent": "THF"},
9 "reactionmetrics": [{"closestliterature": "..."}]
10 }
11 ],
12 "dependencies": {"1": ["2", "3"]}
13}
7.3 Partial Rerun
1curl -X POST http://localhost:5000/api/partial_rerun \
2 -H "Content-Type: application/json" \
3 -H "X-API-KEY: your-api-key" \
4 -d '{
5 "smiles": "original_target",
6 "steps": 3,
7 "model_type": "claude4"
8 }'
系統會:
- 從
partial.json讀取上次結果 - 找到 step 3 及其所有 downstream steps
- 移除這些 steps
- 對 step 3 的 product 重新做 retrosynthesis
- 重新編號 + 接回原本的 upstream steps
8. 與你的 Lead Optimization 管線整合
8.1 典型工作流
1你的 Lead Optimization Pipeline
2 │
3 ▼
4 新 analog SMILES 列表
5 │
6 ▼
7 ┌────────────────────────┐
8 │ DeepRetro batch script │
9 │ for smiles in list: │
10 │ POST /retrosynthesis │
11 │ 結果存 JSON │
12 └────────────────────────┘
13 │
14 ▼
15 篩選條件:
16 ├── 合成步驟 <= 5
17 ├── 所有 leaf 分子在 stock
18 └── hallucination score >= 80
19 │
20 ▼
21 化學家 review(GUI partial rerun)
22 │
23 ▼
24 選定合成路線 → 實驗室
8.2 Python 整合範例
1import requests, json
2
3API_URL = "http://localhost:5000/api/retrosynthesis"
4API_KEY = "your-api-key"
5
6def analyze_analog(smiles: str) -> dict:
7 """對單一 analog 做逆合成分析"""
8 resp = requests.post(API_URL, json={
9 "smiles": smiles,
10 "model_type": "claude4",
11 "advanced_prompt": True,
12 "model_version": "USPTO",
13 "stability_flag": True,
14 "hallucination_check": True,
15 }, headers={"X-API-KEY": API_KEY})
16 return resp.json()
17
18def count_steps(result: dict) -> int:
19 """計算合成步驟數"""
20 return len(result.get("steps", []))
21
22def all_in_stock(result: dict) -> bool:
23 """檢查所有末端分子是否在 stock"""
24 # 找出 dependency tree 中的 leaf nodes
25 deps = result.get("dependencies", {})
26 all_steps = {s["step"] for s in result.get("steps", [])}
27 downstream = set()
28 for v_list in deps.values():
29 downstream.update(v_list)
30 leaf_steps = all_steps - set(deps.keys())
31 # leaf steps 的 reactants 就是 building blocks
32 return True # 需要對接 stock API 做實際確認
33
34# 批次分析
35analogs = ["CCO", "CC(=O)Oc1ccccc1C(=O)O", "..."]
36for smiles in analogs:
37 result = analyze_analog(smiles)
38 steps = count_steps(result)
39 print(f"{smiles}: {steps} steps")
8.3 Building Block 可購性的限制與解法
DeepRetro 使用 ZINC stock 判斷 in-stock,但 ZINC 是一個學術資料庫,不等於「你的供應商明天就能出貨」。
現實中的補充方案:
| 供應商資料庫 | API | 特色 |
|---|---|---|
| eMolecules | REST API | 聚合多家供應商,含價格 |
| MolPort | REST API | 大量 in-stock building blocks |
| Enamine REAL | 下載 / API | 2.5B+ 分子可合成 |
| Sigma-Aldrich | 手動查詢 | 最常見試劑 |
你可以在 DeepRetro 的 is_basic_molecule() 和 stock 判斷後面加一層:
1# 在 format_output() 後加
2for step in result["steps"]:
3 for reactant in step["reactants"]:
4 # 對接你的供應商 API
5 availability = check_supplier(reactant)
6 step["supplier_availability"] = availability
9. Protecting Group 處理
9.1 機制
DeepRetro 有一個精巧的 protecting group detection + masking 機制:
1PG_MAP = {
2 "OMe": ("OC", "$"), # Methoxy → $
3 "OBn": ("COCc1ccccc1", "%"), # Benzyl ether → %
4 "OEt": ("COC", "&"), # Ethoxy → &
5}
啟用 use_protecting_group_feature=True 後:
- 偵測 SMILES 中的 OMe/OBn/OEt patterns
- 替換為 $/%/& 符號
- 在 prompt 中注入
PROTECTING_GROUP_CONTEXT - LLM 會在建議中考慮去保護步驟
9.2 對 lead optimization 的意義
你的 analog 如果有保護基(做 selective modification 時常見),這個功能會讓 LLM:
- 建議正確的去保護條件(HCl/MeOH for OMe, H2/Pd-C for OBn)
- 考慮正交去保護策略(orthogonal deprotection)
- 不會把保護基當作要合成的 target
10. 進階配置與調校
10.1 控制合成步驟數
修改 rec_prithvi.py 中的 max_depth:
1# 預設 50 步——太多了,實務上改成 5-10
2res, solved = rec_run_prithvi(
3 molecule=smiles,
4 max_depth=8, # 限制 8 步以內
5 ...
6)
10.2 模型選擇策略
| 場景 | 推薦模型 | 理由 |
|---|---|---|
| 最高品質 | Claude 4 Sonnet + Advanced Prompt | 最好的 CoT 結構化分析 |
| 預算有限 | DeepSeek-R1 (Fireworks) | 成本較低但品質堪用 |
| 快速初篩 | Claude 4 Sonnet(無 Advanced Prompt) | 較短 prompt = 較低延遲 |
| 困難分子 | Claude 4 + Advanced Prompt + Stability + Hallucination | 全部開啟,retry 次數多 |
10.3 Langfuse 觀測
設定 LANGFUSE_* 環境變數後,所有 LLM 呼叫自動追蹤:
- Token 用量
- 延遲
- CoT 內容
- 成功/失敗率
對管線化使用非常有用——可以看哪些分子類型容易導致 LLM 失敗。
10.4 Caching 機制
@cache_results decorator 做了兩件事:
- 相同 SMILES + 相同模型 → 不重複呼叫 LLM
- Cache 存在
cache_api/目錄
注意:如果你改了 prompt 或模型設定,記得用 API 清除 cache:
1curl -X POST http://localhost:5000/api/clear_molecule_cache \ 2 -H "X-API-KEY: your-key" \ 3 -d '{"molecule": "SMILES_STRING"}'
11. 限制、風險與展望
11.1 已知限制
| 限制 | 影響 | Workaround |
|---|---|---|
| ZINC stock 不等於現實庫存 | leaf 分子可能買不到 | 對接 eMolecules / MolPort API |
| USPTO 模型覆蓋率 | 只含 ~50K 反應,對新反應類型無能為力 | 使用 Pistachio(需商業授權) |
| LLM 成本 | Claude Opus 每次呼叫約 $0.05-0.15 | 用 cache、降低 max_depth、改用 DeepSeek |
| 不支援多步 one-pot | 每步都是獨立反應 | 後處理合併 |
| Python 3.9 綁定 | Conda environment.yml 指定 3.9 | 容器化避開 |
| 無 batch API | 每次只分析一個分子 | 自己包一個 for loop |
| Hallucination checker 有盲點 | Heuristic 規則無法涵蓋所有化學知識 | ML classifier 補充 |
11.2 資安考量
ast.literal_eval()解析 LLM 回覆 — 比eval()安全但仍有邊界 casepicklecache — 本地使用無風險,但不要用網路接收的 cache 檔- CORS 全開 — production 部署必須限縮
- API Key 認證 — 只是 simple token,production 建議加 JWT / OAuth
11.3 未來發展方向(從 commit history 推測)
- Hallucination ML classifier 持續強化(近期 5+ commits)
- Pipeline checks 模組化(
deepretro/algorithms/pipeline_checks.py) - deepretro/ package 重構:更 Pythonic 的 API、完整的 type hints
- 可能增加 reaction classification 功能(
reaction_prediction/)
11.4 與 DeepRetro 論文的對應
論文標題:DeepRetro: Retrosynthetic Pathway Discovery using Iterative LLM Reasoning(arXiv:2507.07060)
引文 BibTeX:
1@misc{sathyanarayana2025deepretro,
2 title={DeepRetro: Retrosynthetic Pathway Discovery using Iterative LLM Reasoning},
3 author={Shreyas V. Sathyanarayana and Rahil Shah and Sharanabasava D. Hiremath
4 and Rishikesh Panda and Rahul Jana and Riya Singh
5 and Rida Irfan and Ashwin Murali and Bharath Ramsundar},
6 year={2025},
7 eprint={2507.07060},
8 archivePrefix={arXiv},
9 primaryClass={q-bio.QM}
10}
Comments