Tutorial: MediGenius — LangGraph 多 Agent 醫療 AI 助手
一份「讀完就能理解架構 + 知道怎麼改 + 清楚資安邊界」的內部技術手冊。 目標讀者:已會 Python + FastAPI,想學 LangGraph multi-agent 編排、RAG pipeline、和 fallback chain 設計模式的工程師。
1. 為什麼要看 MediGenius?
1.1 「多 Agent」vs「純 Prompt」的關鍵差異
大多數 medical AI chatbot 的做法是「塞一大段 system prompt 進 LLM,讓它扮演醫生」——這是 pure-prompt approach(例如 Doctor 類 repo)。這做法簡單但有三個致命缺陷:
| 問題 | Pure-Prompt 做法 | MediGenius 做法 |
|---|---|---|
| 知識邊界 | LLM 只知道訓練資料裡的東西 | RAG 從醫學 PDF 即時檢索,知識可更新 |
| 正確性保障 | 全靠 LLM 自己猜,無法驗證 | 多層 fallback:RAG → LLM → Wikipedia → Tavily |
| 可追蹤性 | 回答沒有 source attribution | 每個回覆附 source 標籤(Medical Literature / Wikipedia / Web) |
| 記憶 | 每次對話獨立,無跨 session 記憶 | SQLite 持久化 + 20-turn sliding window |
| 可擴充性 | 加功能 = 改 prompt | 加功能 = 新增 Agent 節點 + routing 條件 |
1.2 這個 repo 適合學什麼
- LangGraph StateGraph 實戰:8 個節點、6 條 conditional edge 的完整範例
- RAG pipeline 從 PDF 到 ChromaDB:含 chunking 策略(512 chunk / 128 overlap)
- Fallback chain 設計模式:planner → retriever → LLM → Wikipedia → Tavily → executor
- FastAPI + React 全端整合:含 Docker、CI/CD、Render 部署
- SQLite 長期記憶:SQLAlchemy ORM + session management
1.3 適用 / 不適用
| 適用 | 不適用 |
|---|---|
| 學習 multi-agent 架構的教學範例 | 生產環境醫療系統(缺 disclaimer、缺 HIPAA 合規) |
| RAG + fallback chain 設計參考 | 中文醫療場景(keyword 全英文) |
| 全端 AI app 快速起手 | 高併發場景(in-memory state dict 無鎖) |
| Groq / LLaMA 3.3 整合範例 | 需要 streaming response 的場景 |
2. 安裝與環境
2.1 前置條件
- Python 3.10+
- Node.js 18+
- API Keys:
GROQ_API_KEY(從 Groq Console 取得)TAVILY_API_KEY(從 Tavily AI 取得,選填——沒有則 Tavily fallback 不啟動)
2.2 本地安裝
1# 1. Clone
2git clone https://github.com/Md-Emon-Hasan/MediGenius.git
3cd MediGenius
4
5# 2. 建立 .env(在 root 目錄)
6cat > .env << 'EOF'
7GROQ_API_KEY=your_groq_key_here
8TAVILY_API_KEY=your_tavily_key_here
9EOF
10
11# 3. 一鍵啟動(自動建 venv + 裝 backend/frontend deps)
12python run.py
啟動後:
- Backend API:
http://localhost:8000(Swagger docs:/docs) - Frontend UI:
http://localhost:5173
2.3 Docker 部署
1# 需要先在 root 目錄建 .env
2docker-compose up --build
- Backend:port 8000
- Frontend:port 80(Nginx reverse proxy)
2.4 依賴概覽
後端核心(backend/requirements.txt):
| 類別 | 套件 |
|---|---|
| API | FastAPI 0.128 + Uvicorn 0.40 |
| AI/ML | LangChain-core 1.2.6 + LangGraph 1.0.5 + LangChain-Groq 1.1.1 |
| Embedding | sentence-transformers 5.2 + HuggingFace Hub 0.36 |
| Vector DB | ChromaDB 1.4 |
| PyPDF 6.5 | |
| 搜尋 | wikipedia 1.4 + duckduckgo-search 8.1 |
| DB | SQLAlchemy 2.0.45 |
| 推理 | PyTorch 2.9.1(sentence-transformers 需要) |
前端:React 19 + Vite 7 + Tailwind CSS 4 + DaisyUI 5 + react-markdown
3. 架構深度解析
3.1 LangGraph StateGraph 總覽
MediGenius 的核心是一個 LangGraph StateGraph,定義在 backend/app/core/langgraph_workflow.py。整個 workflow 由 8 個 Agent 節點組成,透過 conditional edges 做 routing。
graph TD
START((Start)) --> Memory["MemoryAgent
修剪歷史 ≤ 20 輪"]
Memory --> Planner["PlannerAgent
Keyword Routing"]
Planner -->|"含醫療關鍵字"| Retriever["RetrieverAgent
ChromaDB RAG"]
Planner -->|"不含關鍵字"| LLM["LLMAgent
Direct LLM"]
Retriever -->|"RAG 成功"| Executor["ExecutorAgent
合成最終回覆"]
Retriever -->|"RAG 失敗"| LLM_fallback["LLMAgent
Fallback"]
LLM -->|"LLM 成功"| Executor
LLM -->|"LLM 失敗"| Retriever
LLM_fallback -->|"LLM 成功"| Executor
LLM_fallback -->|"LLM 失敗"| Wiki["WikipediaAgent
Wikipedia Search"]
Wiki -->|"Wiki 成功"| Executor
Wiki -->|"Wiki 失敗"| Tavily["TavilyAgent
Web Search"]
Tavily --> Executor
Executor --> END((End))
style Memory fill:#fdf6b2,stroke:#333
style Planner fill:#c9f,stroke:#333
style Retriever fill:#a0e3a0,stroke:#333
style LLM fill:#9fd4ff,stroke:#333
style LLM_fallback fill:#9fd4ff,stroke:#333
style Wiki fill:#ffe599,stroke:#333
style Tavily fill:#ffbdbd,stroke:#333
style Executor fill:#f9f,stroke:#333
3.2 AgentState — 共享狀態物件
所有節點共享一個 AgentState(TypedDict),定義在 backend/app/core/state.py:
1class AgentState(TypedDict):
2 question: str # 使用者問題
3 documents: List[Document] # RAG 檢索到的文件
4 generation: str # 最終回覆文字
5 source: str # 來源標籤
6 search_query: Optional[str] # 搜尋查詢(預留)
7 conversation_history: List[Dict] # 對話歷史
8 llm_attempted: bool # LLM 是否已嘗試
9 llm_success: bool # LLM 是否成功
10 rag_attempted: bool # RAG 是否已嘗試
11 rag_success: bool # RAG 是否成功
12 wiki_attempted: bool # Wikipedia 是否已嘗試
13 wiki_success: bool # Wikipedia 是否成功
14 tavily_attempted: bool # Tavily 是否已嘗試
15 tavily_success: bool # Tavily 是否成功
16 current_tool: Optional[str] # Planner 決定的工具
17 retry_count: int # 重試計數
設計重點:每個 Agent 只讀寫自己負責的 flag(如 rag_success),routing function 根據 flag 決定下一步。這是 LangGraph 的核心模式——狀態驅動的條件路由。
3.3 各 Agent 節點詳解
Memory Agent(agents/memory.py)
最簡單的節點。做一件事:如果 conversation_history 超過 20 筆,截到最後 20 筆。
1def MemoryAgent(state: AgentState) -> AgentState:
2 history = state.get("conversation_history", [])
3 if len(history) > 20:
4 history = history[-20:]
5 state["conversation_history"] = history
6 return state
為什麼需要:防止下游 Agent 把太長的 history 塞進 LLM prompt 導致 token 爆掉。20 輪 = 40 messages(user + assistant),大約 4,000-8,000 tokens。
Planner Agent(agents/planner.py)
路由決策的核心。用 keyword matching 判斷使用者的問題是否含醫療關鍵字:
1MEDICAL_KEYWORDS = [
2 "fever", "pain", "headache", "nausea", "cancer", "diabetes",
3 "treatment", "therapy", "medication", "heart", "lung", ...
4] # 共 ~130 個
5
6def PlannerAgent(state: AgentState) -> AgentState:
7 question = state["question"].lower()
8 contains_medical = any(kw in question for kw in MEDICAL_KEYWORDS)
9 state["current_tool"] = "retriever" if contains_medical else "llm_agent"
10 return state
設計取捨:
- 優點:零延遲、零 cost(不需要呼叫 LLM 做 intent classification)
- 缺點:(1) 新增醫療領域要手動加 keyword (2) 無法處理隱性醫療問題(如「我最近覺得很累」不含 keyword 但可能是醫療問題) (3) 只支援英文
- 改進方向:可用 LLM 做 zero-shot classification,或用 embedding similarity 做 semantic routing
Retriever Agent(agents/retriever.py)
RAG 的核心。從 ChromaDB 向量資料庫檢索相關文件:
- 取最近 3 筆 user 訊息作為 context enrichment
- 組合成
combined_query = question + context - 呼叫
retriever.invoke(combined_query)(ChromaDB cosine similarity,k=3) - 過濾掉 < 50 字的無效文件
- 設定
rag_successflag
重要細節:Retriever 不做回答生成——它只檢索文件,把結果放進 state["documents"],交給 Executor 合成。
LLM Agent(agents/llm_agent.py)
直接用 Groq LLM 回答,不經過 RAG:
- 組裝最近 5 輪對話為 context
- System prompt:「You are a compassionate and knowledgeable medical AI assistant」
- 呼叫 Groq API(llama-3.3-70b-versatile,temperature=0.3)
- 回覆 > 10 字才算成功
溫度 0.3 的考量:醫療場景需要穩定、可重現的回答,不適合高溫度的創意生成。
Wikipedia Agent / Tavily Agent(fallback chain)
當 RAG 和 LLM 都失敗時的 fallback:
- Wikipedia:
WikipediaAPIWrapper,搜尋question + "medical symptoms treatment",回傳 > 100 字才算成功 - Tavily:web search API,搜尋
question + "medical health treatment symptoms",取 top 3 結果
Fallback 順序:LLM → RAG → LLM → Wikipedia → Tavily → Executor(最終一定會到 Executor)
Executor Agent(agents/executor.py)
匯整所有上游結果,合成最終回覆:
- 如果有
documents(RAG / Wiki / Tavily 產出) → 取前 3 篇、每篇 1,000 字,用 LLM 合成回答 - 如果有
generation(LLM Agent 產出)→ 直接使用 - 都沒有 → 回退到預設回覆「Please consult a healthcare professional」
- 將 user + assistant 訊息 append 到
conversation_history
3.4 Routing Functions — 控制流的精髓
LangGraph 的 conditional edges 透過 routing functions 實作:
1def _route_after_planner(state):
2 return "retriever" if state["current_tool"] == "retriever" else "llm_agent"
3
4def _route_after_llm(state):
5 return "executor" if state.get("llm_success") else "retriever"
6
7def _route_after_rag(state):
8 return "executor" if state.get("rag_success") else "llm_agent"
9
10def _route_after_wiki(state):
11 return "executor" if state.get("wiki_success") else "tavily"
12
13def _route_after_tavily(state):
14 return "executor" # Tavily 是最後防線,無論如何都進 Executor
設計模式:每個 routing function 只看一個 boolean flag,非常簡潔。缺點是沒有 retry_count 的利用——理論上可以在重試 N 次後做不同決策。
3.5 ChatService — 編排層
backend/app/services/chat_service.py 是 workflow 的調用者:
- Startup:
initialize_workflow()編譯 LangGraph StateGraph - Per-request:
- 從
conversation_statesdict 取或建 session state reset_query_state()清除前一次查詢的 flag(保留 conversation_history)workflow_app.ainvoke(state)執行整個 graph- 結果存入 SQLite + 更新 in-memory state
- 從
3.6 資料流全景
1使用者輸入 "What are the symptoms of diabetes?"
2 │
3 ├── POST /api/v1/chat
4 │ └── ChatService.process_message()
5 │ ├── db_service.save_message(user)
6 │ ├── reset_query_state()
7 │ ├── workflow.ainvoke(state)
8 │ │ ├── MemoryAgent: 修剪歷史
9 │ │ ├── PlannerAgent: "diabetes" 命中 → current_tool="retriever"
10 │ │ ├── RetrieverAgent: ChromaDB 找到 3 筆文件 → rag_success=True
11 │ │ └── ExecutorAgent: 用 LLM 合成文件內容為回覆
12 │ └── db_service.save_message(assistant)
13 │
14 └── Response: { response: "...", source: "Medical Literature Database", ... }
4. RAG Pipeline 詳解
4.1 PDF 載入與切片
backend/app/tools/pdf_loader.py:
1# 1. 載入 PDF
2loader = PyPDFLoader(pdf_path) # 逐頁載入
3docs = loader.load()
4
5# 2. 切片
6splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
7 chunk_size=512, # 每個 chunk 512 tokens
8 chunk_overlap=128, # 重疊 128 tokens
9 separators=["\n\n", ". ", "\n", " "], # 優先在段落/句子邊界切割
10)
11splits = splitter.split_documents(docs)
為什麼 512/128:
- 512 tokens 大約 350-400 字,足以包含一個完整的醫學概念
- 128 tokens 重疊確保跨 chunk 的上下文不會斷裂
from_tiktoken_encoder確保 token 計數精確(vs 字元計數)
4.2 Embedding 與向量儲存
backend/app/tools/vector_store.py:
- Embedding model:
sentence-transformers/all-MiniLM-L6-v2(384 維,輕量級) - Vector DB:ChromaDB,cosine similarity
- Persist:存在
backend/storage/vector_store/ - Retrieval:
as_retriever(search_kwargs={"k": 3})——每次取 top 3 最相似文件
啟動流程(在 main.py lifespan 中):
- 檢查
medical_book.pdf是否存在 - 用
process_pdf()切片 get_or_create_vectorstore(documents)— 如果已有 ChromaDB 檔就載入,否則建新的- 只在首次啟動時做 embedding,之後直接載入 persist 目錄
4.3 自訂知識庫
要加入自己的醫學文件:
1# 方法 1:替換預設 PDF
2cp your_medical_book.pdf backend/data/medical_book.pdf
3rm -rf backend/storage/vector_store/* # 清除舊向量
4# 重啟 backend,會自動重建
5
6# 方法 2:修改 config.py 指定多個 PDF(需改 code)
7# PDF_PATH 目前只支援單一檔案
5. 持久化與 Session 管理
5.1 SQLite 資料模型
backend/app/models/message.py:
1class Message(Base):
2 __tablename__ = "messages"
3 id = Column(Integer, primary_key=True)
4 session_id = Column(String(255), index=True) # UUID
5 role = Column(String(50)) # "user" | "assistant"
6 content = Column(Text)
7 source = Column(String(255)) # e.g. "Medical Literature Database"
8 timestamp = Column(DateTime, default=datetime.utcnow)
5.2 雙層記憶架構
MediGenius 有兩層記憶:
| 層 | 媒介 | 生命週期 | 用途 |
|---|---|---|---|
| In-memory state | Python dict(ChatService.conversation_states) | process 存活期間 | 提供 LangGraph workflow 的即時 context |
| Persistent store | SQLite | 永久 | 跨 session 的歷史記錄、session 管理 |
重要限制:in-memory state 在 server 重啟後消失。雖然 SQLite 保存了完整歷史,但 conversation_history(workflow 用的那份)會重置。也就是說——重啟後第一次查詢不會有 conversation context。
5.3 Session API
| Endpoint | Method | 功能 |
|---|---|---|
/api/v1/chat | POST | 處理訊息,觸發 workflow |
/api/v1/clear | POST | 清除 in-memory state |
/api/v1/new-chat | POST | 建立新 session |
/api/v1/history | GET | 取當前 session 歷史 |
/api/v1/sessions | GET | 列出所有 session |
/api/v1/session/{id} | GET | 載入特定 session |
/api/v1/session/{id} | DELETE | 刪除 session |
/api/v1/health | GET | 健康檢查 |
6. 資安掃描
6.1 高風險項目
| 項目 | 嚴重度 | 說明 |
|---|---|---|
| CORS 全開 | HIGH | allow_origins=["*"] + allow_credentials=True — 任何網站都能發 credentialed request |
| Session secret 每次隨機 | MEDIUM | secret_key=secrets.token_hex(32) 在每次啟動時重新生成,重啟即失效所有 session |
| 無 rate limiting | MEDIUM | 沒有任何 API rate limit,可被暴力呼叫消耗 Groq API quota |
| 無輸入驗證 | MEDIUM | ChatRequest.message 直接進 LLM prompt,無 sanitization |
| 無 HTTPS 強制 | MEDIUM | Docker 設定的 frontend 只開 port 80,production 需在 reverse proxy 層加 TLS |
6.2 中風險項目
| 項目 | 嚴重度 | 說明 |
|---|---|---|
| API key 管理 | MEDIUM-LOW | 用 .env + python-dotenv,符合基本實務;.gitignore 已排除 .env |
| SQLite 無加密 | LOW | 對話內容明文存儲,但這是 demo 專案可接受 |
| 無 authentication | MEDIUM | 任何人都能存取所有 API endpoint |
| 醫療 disclaimer 缺失 | MEDIUM | Benchmark 顯示 disclaimer rate = 0%,production 用途必須加上 |
| PDF 上傳缺失 | LOW | 目前只支援預設 PDF,無動態上傳 endpoint(也因此無 file upload 攻擊面) |
6.3 供應鏈依賴
- PyTorch 2.9.1:大型依賴(~2 GB),僅用於 sentence-transformers;如果不需要本地 embedding 可考慮替換
- 依賴版本固定:
requirements.txt用==鎖定版本,良好實務 - 無
requirements.txtlock file:pip freeze 產出的,沒有用 pip-tools / poetry.lock,但對 demo 專案可接受
6.4 Prompt Injection 風險
使用者輸入直接嵌入 LLM prompt(ExecutorAgent / LLMAgent),例如:
1prompt = (
2 "You are an experienced medical doctor...\n"
3 f"Patient's Current Question:\n{question}\n" # 直接嵌入
4 f"Medical Information:\n{content}\n"
5)
攻擊者可透過精心設計的問題嘗試覆寫 system prompt。建議:加入 input sanitization + prompt 分離(system / user message)。
7. 前端架構
7.1 單檔式 React App
整個前端是一個 687 行的 App.jsx,包含 6 個 section:
- Imports:React hooks + ReactMarkdown
- Utility helpers:
formatTimeAgo()+buildDownloadText() - Sidebar:session 列表、new chat、developer info、theme toggle
- ChatArea:welcome screen(含 6 個 quick questions)、messages、typing indicator
- InputArea:textarea + send button(支援 Enter 發送、Shift+Enter 換行)
- App root:所有 state + API logic(16 個 state variables)
7.2 UI 設計特色
- Glassmorphism:大量使用
glass-effectclass(半透明 + 模糊背景) - Dark/Light 切換:用
data-themeattribute + CSS variables - Responsive:768px 斷點,mobile 時 sidebar 變 overlay
- Markdown 渲染:bot 回覆用
react-markdown渲染,支援 code block / list / heading - Quick Questions:6 個預設問題按鈕,降低使用者首次互動門檻
7.3 API 串接
前端透過 /api/v1/* endpoint 與後端溝通。Vite dev server 和 Nginx production 都做 reverse proxy:
1// vite.config.js
2server: { proxy: { '/api': 'http://localhost:8000' } }
3
4// nginx.conf(production)
5location /api/ { proxy_pass http://backend:8000; }
8. 測試與 CI/CD
8.1 後端測試
12 個測試檔,約 1,084 行:
| 測試檔 | 行數 | 測試對象 |
|---|---|---|
test_agents.py | 204 | 所有 8 個 Agent 的單元測試 |
test_workflow.py | 41 | LangGraph workflow 組裝 |
test_workflow_routing.py | 44 | Routing function 邏輯 |
test_api.py | 92 | API endpoint 整合測試 |
test_api_edge_cases.py | 47 | API 邊界條件 |
test_services.py | 154 | ChatService / DatabaseService |
test_database.py | 74 | SQLite CRUD |
test_tools.py | 159 | LLM client / vector store / search tools |
test_coverage_gaps.py | 165 | 補充覆蓋率缺口 |
test_logging.py | 36 | Logging 配置 |
test_app.py | 5 | App import 驗證 |
conftest.py | 63 | Pytest fixtures |
執行方式:
1cd backend
2python -m pytest tests/ # 全部跑
3python -m pytest --cov=app tests/ --cov-report=term-missing # 含覆蓋率
8.2 前端測試
1cd frontend
2npx vitest run # 跑一次
3npm run test # watch mode
8.3 CI/CD Pipeline
.github/workflows/ci-cd.yml 定義了三個 job:
1backend-test → flake8 + isort --check + pytest --cov
2frontend-test → npm install + vitest + npm build
3docker-build → (只在 push to master 時)docker build 兩個 image
9. 部署選項
9.1 本地開發
1python run.py # 自動建 venv、裝 deps、啟動 backend + frontend
9.2 Docker Compose
1docker-compose up --build
注意事項:
docker-compose.yml把 backend storage 掛載為 volume,確保 SQLite + ChromaDB 持久化- Frontend 用 multi-stage build(Node build → Nginx serve)
- 沒有
.env的 Docker-level 設定——需要在docker-compose.yml的environment裡加 API keys
9.3 Render(雲端)
render.yml 定義了 Render 的部署 manifest:
- Backend:Python Web Service
- Frontend:Static Site
- Persistent disk:for SQLite storage
10. 與 Pure-Prompt 做法的對比分析
這一節比較 MediGenius 的 multi-agent 架構與典型 pure-prompt 醫療 chatbot 的差異。
10.1 架構對比
| 面向 | Pure-Prompt(如 Doctor repo) | MediGenius |
|---|---|---|
| 核心 | 一個 LLM call + system prompt | 8 個 Agent 節點 + StateGraph |
| 知識來源 | LLM 訓練資料 | PDF RAG + LLM + Wikipedia + Web search |
| 路由決策 | 無(所有問題走同一條路) | Planner keyword routing + conditional fallback |
| 記憶 | 無或短期(conversation buffer) | SQLite 持久化 + 20-turn sliding window |
| 可追蹤性 | 無 source 標示 | 每個回覆附 source 標籤 |
| 錯誤處理 | LLM 幻覺就是最終答案 | 多層 fallback:RAG → LLM → Wiki → Tavily |
| 擴充性 | 改 prompt | 新增 Agent 節點 + routing edge |
| Code 複雜度 | ~50 行 | ~2,000+ 行(backend) |
| 延遲 | 1 次 LLM call | 2-4 次工具呼叫 + 1 次 LLM call |
| 成本 | 1 次 API call | 2-5 次 API call |
10.2 什麼時候該用 Pure-Prompt
- Quick prototype:驗證概念,不需要正確性保障
- 非醫療場景:幻覺風險可容忍的應用
- Cost-sensitive:每次查詢只花一次 API call
- Low-latency 需求:不能容忍多次工具呼叫的延遲
10.3 什麼時候該用 Multi-Agent
- 需要 source attribution:使用者需要知道答案來自哪裡
- 知識需要更新:PDF / 文件可以隨時替換,不用重新訓練 LLM
- 需要 fallback:單一來源不可靠時有備案
- 需要記憶:跨 session 的長期對話上下文
- 需要審計:每個 Agent 的 flag 都記錄在 state 中,可追蹤決策路徑
10.4 MediGenius 的架構限制
- Planner 是 keyword 比對,不是語義理解:「我最近容易累」不會觸發 RAG
- 單一 PDF 限制:只支援一個
medical_book.pdf,沒有多文件管理 - Executor 是 terminal 節點:ExplanationAgent 被跳過(
executor → END),缺少後處理 - No streaming:整個 workflow 是 sync 的(
ainvoke等全部完成才回覆) - In-memory state dict 無鎖:多個 concurrent request 同一 session 可能 race condition
- LLM fallback 路徑有環:理論上
LLM → Retriever → LLM可能循環(實務上 flag 機制會防止)
11. 改造建議與延伸方向
11.1 短期改進(Low-hanging fruit)
| 項目 | 做法 | 影響 |
|---|---|---|
| 加 rate limiting | FastAPI middleware 用 slowapi | 防止 API abuse |
| 收斂 CORS | 設定 allow_origins=["https://medigenius.onrender.com"] | 堵住 CSRF |
| 加 disclaimer | 在 ExecutorAgent 回覆尾端固定附加免責聲明 | 醫療合規 |
| 多 PDF 支援 | 修改 process_pdf() 接受資料夾、glob 所有 PDF | 擴充知識庫 |
| Streaming response | 用 FastAPI StreamingResponse + LangGraph streaming | 改善 UX |
11.2 中期改進
| 項目 | 做法 |
|---|---|
| Semantic routing | 用 embedding similarity 取代 keyword matching |
| PubMed 整合 | 新增 PubMedAgent,使用 Entrez API 搜尋學術文獻 |
| 多語言 | Planner 加入中文 keyword 或改用 language-agnostic classifier |
| Conversation context 持久化 | 啟動時從 SQLite 恢復 conversation_history |
| 分離 system / user prompt | 用 LangChain ChatPromptTemplate 的 system / human message 分離 |
11.3 長期方向
| 項目 | 做法 |
|---|---|
| 接入 ExplanationAgent | 在 Executor 後加 post-processing(摘要、simplify、highlight) |
| 引入 LLM-based Planner | 用小模型做 intent classification + tool selection |
| Human-in-the-loop | 不確定度高時要求使用者確認 / 重新提問 |
| HIPAA 合規 | 加密 SQLite、audit log、access control |
| 微調 embedding | 用 medical corpus fine-tune sentence-transformers |
附錄 A:快速啟動 Cheatsheet
1# Clone + 設定
2git clone https://github.com/Md-Emon-Hasan/MediGenius.git
3cd MediGenius
4echo "GROQ_API_KEY=gsk_xxx" > .env
5echo "TAVILY_API_KEY=tvly_xxx" >> .env
6
7# 本地啟動
8python run.py
9
10# Docker 啟動
11docker-compose up --build
12
13# 跑後端測試
14cd backend && python -m pytest --cov=app tests/
15
16# 跑前端測試
17cd frontend && npx vitest run
附錄 B:關鍵檔案速查
| 檔案 | 用途 |
|---|---|
backend/app/core/langgraph_workflow.py | LangGraph StateGraph 定義(8 節點 + 6 條 conditional edge) |
backend/app/core/state.py | AgentState TypedDict + 初始化/重置函式 |
backend/app/agents/planner.py | 130 個醫療 keyword + routing 決策 |
backend/app/agents/retriever.py | ChromaDB RAG 檢索 |
backend/app/agents/executor.py | 最終回覆合成 |
backend/app/tools/vector_store.py | ChromaDB 建立/載入/retriever factory |
backend/app/tools/pdf_loader.py | PDF 載入 + 切片 |
backend/app/services/chat_service.py | Workflow 編排 + session state 管理 |
backend/app/services/database_service.py | SQLite CRUD |
backend/app/main.py | FastAPI app + lifespan(startup 流程) |
frontend/src/App.jsx | React 全端 UI(687 行 monolithic) |
docker-compose.yml | Docker 部署設定 |
.github/workflows/ci-cd.yml | CI/CD pipeline |
Comments