This commit is contained in:
DatTT127
2026-06-24 10:33:07 +07:00
parent 16a91bd17e
commit f705113711
77 changed files with 8999 additions and 14 deletions

View File

@@ -7,16 +7,17 @@ Orchestrates API routers, role checks, Socratic circuit-breaker state evaluation
Core Backend Team
## Boundary
FastAPI server, API routers, authentication middleware, circuit breaker engine, report generator, RAG coordinator, ledger logger, and connections to Postgres, S3, Redis, Triton, Qdrant, ladybugDB.
FastAPI server, API routers, authentication middleware, circuit breaker engine, report generator, RAG coordinator, RAG-Referee (BERT), ledger logger, and connections to Postgres + pgvector, S3 (MinIO), Redis, Triton, ladybugDB.
## Internal Design
- Built with FastAPI (Python) and Uvicorn for async HTTP server.
- Authentication middleware validates JWT tokens and enforces RBAC (roles: RO_RADIOLOGIST, RO_THERAPIST).
- Socratic circuit-breaker engine monitors interaction telemetry (hover duration, decision time, override magnitude) and triggers safety dialogs.
- Clinical Report Engine uses ReportLab to generate bilingual PDF reports per Circular 46/2018/TT-BYT.
- RAG Coordinator orchestrates Retrieval-Augmented Generation: dense vector lookup in Qdrant, graph traversal in ladybugDB, prompt enrichment, LLM generation on Triton (PhoGPT/MedGemma), and hallucination guarding.
- RAG Coordinator orchestrates Retrieval-Augmented Generation: dense vector lookup in pgvector (PostgreSQL HNSW), graph traversal in ladybugDB, mandatory pre-generation retrieval, prompt enrichment, LLM generation on browser WebLLM (GemmaE2B) or cloud Vertex AI (MedGemma via NFR-16a), and hallucination guarding via BERT RAG-Referee.
- NLP Scrubber (Microsoft Presidio): re-verifies client edge redaction, refines residual PII, and returns error if unresolvable.
- Ledger Logger appends immutable, cryptographically chained audit logs to Postgres via triggers preventing UPDATE/DELETE.
- Connections: Postgres (via SQLAlchemy), S3 (via boto3), Redis (via redis-py), Triton (via gRPC), Qdrant (via gRPC/HTTP), ladybugDB (via in-process C++ bindings).
- Connections: Postgres + pgvector (via SQLAlchemy), S3 (via boto3), Redis (via redis-py), Triton (via gRPC — CV + EmbeddingGemma only), ladybugDB (via in-process C++ bindings).
- Model weights loaded at startup from internal registry; cached in memory.
- API endpoints layered: public clinical (sessions, analysis, reports, feedback) and internal/local safety (explanations, safety, drift, RAG, activations, annotations, ground-truth, escalation, morphology, telemetry).

View File

@@ -11,10 +11,10 @@ Qdrant vector database instances, ladybugDB graph database instances, embedding
## Internal Design
- Hybrid knowledge architecture: vector similarity search + graph traversal
- Qdrant: stores guideline section embeddings (BioClinicalBERT, PubMedBERT) with payload metadata
- pgVec: stores guideline section embeddings (BioClinicalBERT, PubMedBERT) with payload metadata
- ladybugDB: stores ontology concepts (SNOMED-CT, LOINC, RadLex) and relational axioms
- EmbeddingGemma: generates 768-dimension vectors for text chunks
- PhoGPT/MedGPT: LLM for answer generation with constrained decoding
- GemmaE2B/MedGemma: LLM for answer generation with constrained decoding
- Retrieval pipeline: hybrid search (vector + BM25) → graph expansion → reranking
- Grounding module: verifies LLM outputs against source guidelines with citation extraction
- Arbitration engine: resolves conflicting evidence using belief propagation

Submodule workspace/sprint_1_2/Design_Material/LEGACY/VKIST_ML/codebase-vkist-ultrasound-legacy deleted from 8b2758928a

View File

@@ -1,578 +0,0 @@
# KNEE ULTRASOUND ANALYSIS API: TÀI LIỆU KỸ THUẬT & HƯỚNG DẪN SỬ DỤNG
**Phiên bản:** 1.0 | **Ngày:** 03/2026
**Framework:** FastAPI + PyTorch | **Python:** 3.10+
---
## 1. Tổng quan hệ thống
Knee Ultrasound Analysis API được xây dựng bằng FastAPI, chuyên phục vụ tác vụ phân tích ảnh siêu âm đầu gối. Hệ thống tích hợp nhiều mô hình Học máy sâu (Deep Learning) nhằm tự động hóa quy trình phân loại mặt cắt, phát hiện dấu hiệu viêm, phân đoạn cấu trúc giải phẫu và đo lường kích thước tổn thương định lượng.
### 1.1 Các chức năng chính
* **Phân loại góc chụp siêu âm (Angle Classification):** Tự động nhận dạng mặt cắt/góc chụp từ ảnh đầu vào bao gồm các nhãn: `med-lat`, `post-trans`, `sup-trans-flex`, và `sup-up-long`.
* **Phát hiện viêm (Inflammation Detection):** Xác định sự hiện diện của tình trạng viêm khớp gối qua hai góc chụp chính là `sup-up-long``post-trans`.
* **Phân đoạn ảnh ngữ nghĩa (Segmentation):** Tách biệt các cấu trúc giải phẫu đích (dịch khớp, gân, xương, màng hoạt dịch...) thành các phân vùng mặt nạ màu riêng biệt.
* **Đo độ dày tự động (Thickness Measurement):** Tự động tính toán khoảng cách hình học theo đơn vị milimét ($mm$) giữa các phân vùng mô mềm đã được phân đoạn (chỉ áp dụng đối với mặt cắt góc `sup-up-long`).
* **Đánh giá mức độ viêm (Severity Analysis):** Xếp hạng thang điểm mức độ nghiêm trọng của viêm từ cấp độ 0 (Rất nhẹ) đến cấp độ 3 (Nặng) dựa trên tỷ lệ diện tích dịch khớp và sự tăng sinh màng hoạt dịch.
### 1.2 Luồng xử lý dữ liệu tổng thể (Pipeline Processing)
Khi nhận tập tin ảnh từ máy trạm (Client), hệ thống sẽ thực hiện phân nhánh xử lý logic động dựa trên kết quả của khối phân loại góc chụp:
| Góc chụp phát hiện | Quy trình xử lý chi tiết trong Backend pipeline |
| --- | --- |
| **`post-trans`** | Phân loại góc $\rightarrow$ Phát hiện viêm $\rightarrow$ Phân đoạn ảnh POST $\rightarrow$ Trả kết quả JSON & Mask.|
| **`sup-up-long`** | Phân loại góc $\rightarrow$ Phát hiện viêm $\rightarrow$ Phân đoạn ảnh SUP $\rightarrow$ Đo độ dày mô $\rightarrow$ Đánh giá mức độ nặng $\rightarrow$ Trả kết quả.|
| **`med-lat`** or **`sup-trans-flex`** | Chỉ thực hiện phân loại góc $\rightarrow$ Trả kết quả trực tiếp (Bỏ qua nhánh phân đoạn & đo lường).|
```plantuml
@startuml
skinparam PackageStyle rect
skinparam BackgroundColor #FFFFFF
skinparam ArrowColor #2C3E50
title KIẾN TRÚC ĐIỀU HƯỚNG PIPELINE BACKEND (FASTAPI)
start
:Nhận ảnh siêu âm thô (Multipart HTTP Post);
:Chạy mô hình Phân loại Góc chụp (Angle Classification);
if (Góc chụp nhận diện được là gì?) then (post-trans)
:Chạy mô hình Phát hiện Viêm;
:Chạy mô hình Phân đoạn góc POST (Deeplabv3 ResNet101);
:Kết xuất Ảnh Overlay Mask & Metadata;
elseif (sup-up-long) then (sup-up-long)
:Chạy mô hình Phát hiện Viêm;
:Chạy mô hình Phân đoạn góc SUP (Tùy chọn: Deeplabv3 / UNet3+ / vv.);
:Tính toán độ dày tự động (Thickness Measurement);
:Tính toán điểm Severity (Severity Score Analysis);
:Kết xuất Ảnh Overlay Mask & Thống kê định lượng;
else (med-lat / sup-trans-flex)
:Ghi nhận nhãn góc chụp;
note right: Không cấu hình phân đoạn\ncho các mặt cắt này
endif
:Đóng gói Response JSON (Success=True);
:Trả kết quả về cho Client Application;
stop
@enduml
```
---
## 2. Hướng dẫn cài đặt & Triển khai môi trường
### 2.1 Yêu cầu hệ thống tối thiểu
| Thành phần cấu phần | Thông số kỹ thuật yêu cầu tối thiểu |
| --- | --- |
| **Hệ điều hành** | Ubuntu 20.04+ / Windows 10+ / macOS 12+ |
| **Môi trường Python** | Phiên bản 3.10 cố định |
| **Bộ nhớ RAM** | 16 GB trở lên |
| **Bộ xử lý đồ họa (GPU)** | NVIDIA GPU hỗ trợ nền tảng CUDA 12.4 (Khuyến nghị để tối ưu tốc độ) |
| **Dung lượng VRAM** | Tối thiểu 8 GB (Khuyến nghị 16 GB nếu chạy song song đồng thời nhiều mô hình)|
| **Ổ cứng lưu trữ** | Tối thiểu 15 GB dung lượng trống (Dành cho bộ cài đặt và file weights `.pth`) |
| **Bộ công cụ bổ trợ** | CUDA Toolkit 12.4 & cuDNN 9.x tương thích tương ứng|
### 2.2 Khởi tạo môi trường ảo
Tải mã nguồn dự án từ kho lưu trữ Git:
```bash
git clone https://github.com/itvkist/vkist-ultrasound.git
cd vkist-ultrasound
```
Khuyến nghị thiết lập môi trường bằng **Conda** nhằm quản lý và cô lập các gói phụ thuộc:
```bash
conda create -n vkist-ultrasound python=3.10 -y
conda activate vkist-ultrasound
```
*Hoặc khởi tạo nhanh bằng mô-đun thư viện chuẩn `venv` nếu hệ thống chưa cài đặt Anaconda*:
```bash
# Trên nền tảng hệ điều hành Linux / macOS
python3.10 -m venv venv
source venv/bin/activate
# Trên nền tảng hệ điều hành Windows
venv\Scripts\activate
```
### 2.3 Cài đặt các gói thư viện phụ thuộc (Dependencies)
Thực hiện cài đặt các thư viện lõi quy định trong tệp cấu hình:
```bash
pip install -r requirements.txt
```
Trong trường hợp nhân của khung phần mềm PyTorch không nhận diện được phần cứng CUDA, tiến hành ghi đè cài đặt thủ công phiên bản biên dịch GPU:
```bash
pip install torch==2.5.0+cu124 torchvision==0.20.0+cu124 --index-url https://download.pytorch.org/whl/cu124
```
> ⚠️ **LƯU Ý QUAN TRỌNG VỀ PACKAGE NATTEN:**
> Dòng cấu hình cài đặt gói `natten==0.17.3+torch250cu124` mặc định đã bị gắn chú thích (`#` comment out) trong tệp `requirements.txt`. Nếu bạn sử dụng các kiến trúc mạng Transformer nâng cao yêu cầu gói này, bắt buộc cài đặt thủ công qua liên kết phân phối bánh xe (wheels) chính thức:
> `pip install natten==0.17.3+torch250cu124 -f https://shi-labs.com/natten/wheels/`
>
>
### 2.4 Cấu trúc cây thư mục dự án chuẩn
Để đảm bảo hệ thống FastAPI khởi chạy chính xác và tự động nạp các tệp trọng số mô hình, cấu trúc cây thư mục dự án cần được sắp xếp như sau:
```text
project/
├── app.py # Trình khởi chạy máy chủ chính - FastAPI Server
├── requirements.txt # Danh sách các gói thư viện phụ thuộc hệ thống
├── arch/ # Thư mục mã nguồn chứa định nghĩa kiến trúc mạng Custom
│ ├── efficientfeedback.py # Định nghĩa mạng EfficientFeedbackNetwork
│ └── unet3plus_att.py # Định nghĩa mạng phân đoạn UNet3Plus_Attention
├── models/ # Thư mục lưu trữ tệp trọng số nhị phân (.pth)
│ ├── best_convnext_tiny.pth
│ ├── best_densenet.pth
│ ├── best_resnet50.pth
│ ├── best_efficientnet_b2.pth
│ ├── best_swin_v2_s.pth
│ ├── efficientnet_b0_ultrasound_2_class.pth
│ ├── best_model_Deeplav3.pth
│ ├── unet_resnet101.pth
│ ├── efficientfeedback.pth
│ ├── unet3plus_att.pth
│ └── best_model_deeplabv3_resnet101_seed_16.pth
├── templates/ # Các tài nguyên phục vụ giao diện Web tích hợp
│ ├── index.html
│ ├── css/
│ └── js/
├── uploads/ # Thư mục lưu trữ tạm ảnh thô đầu vào (Tự động khởi tạo)
└── results/ # Thư mục lưu trữ ảnh kết quả phân đoạn (Tự động khởi tạo)
```
### 2.5 Khởi động máy chủ dịch vụ
Thực thi lệnh chạy máy chủ tại thư mục gốc:
```bash
python app.py
```
**Mô phỏng nhật ký màn hình console khi máy chủ khởi chạy thành công:**
```độc_thoại
[INFO] Loading deep learning weights safely to memory...
[INFO] Device successfully mapped: cuda (NVIDIA GeForce RTX 4090)
[INFO] FastAPI Application core initialized.
[INFO] Uvicorn server running on http://localhost:8000 (Press CTRL+C to quit)
```
* **Giao diện Web UI kiểm thử trực quan:** `http://localhost:8000`
* **Tài liệu API tương tác tự động (Swagger UI):** `http://localhost:8000/docs`
---
## 3. Tài liệu đặc tả API (API Reference)
### 3.1 Trạng thái hoạt động (Health Check)
* **Endpoint:** `GET /api/health`
* **Chức năng:** Kiểm tra tính sẵn sàng phục vụ của cụm dịch vụ API Backend.
* **Định dạng dữ liệu phản hồi (Response JSON):**
```json
{
"status": "healthy"
}
```
### 3.2 Phân tích ảnh siêu âm chính - `POST /api/analyze`
Mã hóa dữ liệu đầu vào dưới dạng tệp tin `multipart/form-data` để gửi tới mạng mạng nơ-ron xử lý.
#### Các tham số yêu cầu (Request Parameters)
| Tham số cấu hình | Phương thức truyền | Kiểu dữ liệu | Giá trị mặc định | Định nghĩa chức năng chi tiết |
| --- | --- | --- | --- | --- |
| **`image`** | Multipart Form | Binary File | *Bắt buộc* | Tệp tin ảnh siêu âm đầu gối cần xử lý (Hỗ trợ mở rộng định dạng: `.jpg`, `.png`, `.bmp`).|
| **`angle_model`** | Query String | String | `convnext` | Tên định danh mô hình đảm nhận tác vụ phân loại góc chụp.|
| **`inflammation_model`** | Query String | String | `efficientnet_b0` | Mô hình phát hiện tình trạng viêm (Hiện tại cố định cấu hình mạng).|
| **`segment_model_sup`** | Query String | String | `deeplabv3` | Mô hình phân đoạn cấu trúc giải phẫu dành cho mặt cắt góc `sup-up-long`.|
| **`segment_model_post`** | Query String | String | `deeplabv3_resnet101` | Mô hình phân đoạn cấu trúc giải phẫu dành cho mặt cắt góc `post-trans`.|
#### Danh sách định danh mô hình khả dụng trong hệ thống
| Phân nhóm Task | Tên tham số truyền vào | Kiến trúc mạng nơ-ron gốc | Mô tả đặc tính đầu ra |
| --- | --- | --- | --- |
| **Phân loại Góc chụp** | `convnext` | ConvNeXt Tiny | Phân cấp phân loại ra 4 lớp nhãn đầu ra.|
| | `densenet` | DenseNet-121 | Mạng kết nối dày đặc.|
| | `resnet50` | ResNet-50 | Kiến trúc mạng dư thừa tiêu chuẩn.|
| | `efficientnet_b2` | EfficientNet-B2 | Tối ưu hóa đa quy mô tài nguyên mạng.|
| | `swin` | Swin Transformer V2-S | Kiến trúc Attention cửa sổ dịch chuyển.|
| **Phân đoạn góc SUP** | `deeplabv3` | DeepLabV3 ResNet-50 | Trích xuất đặc trưng đa tỷ lệ với 7 lớp đầu ra.|
| | `unet_resnet101` | UNet + ResNet-101 | Kiến trúc Encoder-Decoder kết hợp ResNet.|
| | `efficientfeedback` | EfficientFeedbackNetwork | Thiết kế tùy biến riêng có liên kết phản hồi dữ liệu.|
| | `unet3plus` | UNet3+ with Attention | Cơ chế Attention kết hợp kết nối toàn diện Full-scale.|
| **Phân đoạn góc POST** | `deeplabv3_resnet101` | DeepLabV3 ResNet-101 | Cấu trúc chuyên sâu phân đoạn góc nhìn mặt sau.|
#### Cấu trúc dữ liệu JSON phản hồi (Response Body Schema)
```json
{
"success": true,
"filename": "d3b07384-d113-4ec8-a141-8664b07fb8d3.jpg",
"images": {
"original": "/uploads/d3b07384-d113-4ec8-a141-8664b07fb8d3.jpg",
"segmented": "/results/seg_d3b07384-d113-4ec8-a141-8664b07fb8d3.jpg"
},
"models_used": {
"angle_model": "convnext",
"inflammation_model": "efficientnet_b0",
"segmentation_model": "deeplabv3"
},
"angle": {
"class": "sup-up-long",
"confidence": 98.45
},
"inflammation": {
"detected": true,
"confidence": 94.20
},
"segmentation": {
"angle_type": "sup",
"classes_detected": ["background", "effusion", "fat", "femur", "synovium", "tendon"],
"color_legend": {
"background": [0, 0, 0],
"effusion": [255, 0, 0],
"synovium": [255, 0, 255]
}
},
"measurement": {
"thickness_mm": 6.87,
"thickness_px": 100,
"location_x": 256
},
"severity": {
"level": 3,
"severity": "Nặng",
"combined_score": 18.4,
"effusion": {
"pixels": 25400,
"ratio": 0.096,
"thickness": 6.87
},
"synovium": {
"pixels": 12500,
"ratio": 0.047
}
}
}
```
#### Ví dụ mã triển khai gọi dịch vụ (Client Invocations)
* **Sử dụng lệnh Client cURL CLI:**
```bash
curl -X POST "http://localhost:8000/api/analyze?angle_model=convnext&segment_model_sup=deeplabv3" \
-F "image=@/data/medical/knee_sample.jpg"
```
* **Triển khai ứng dụng gọi qua script Python (Requests):**
```python
import requests
url = "http://localhost:8000/api/analyze"
query_parameters = {
"angle_model": "swin",
"segment_model_sup": "unet3plus"
}
target_image_path = "path/to/clinical_knee.jpg"
with open(target_image_path, "rb") as image_file:
payload = {"image": image_file}
api_response = requests.post(url, params=query_parameters, files=payload)
parsed_result = api_response.json()
print("Phân loại góc:", parsed_result["angle"]["class"])
print("Số liệu đo lường hình học:", parsed_result.get("measurement"))
```
---
## 4. Các thông số cấu hình lõi hệ thống
### 4.1 Hằng số hệ thống trong `app.py`
| Tên định danh hằng số | Giá trị mặc định | Diễn giải chức năng kỹ thuật |
| --- | --- | --- |
| `UPLOAD_FOLDER` | `'uploads'` | Đường dẫn cục bộ lưu trữ file ảnh thô nhận từ máy trạm.|
| `RESULTS_FOLDER` | `'results'` | Đường dẫn lưu ảnh màu sau phân đoạn (Color Mask Overlayed).|
| `TEMPLATES_FOLDER` | `'templates'` | Thư mục chứa mã nguồn giao diện phân tích Web UI.|
| `PIXEL_TO_MM` | $\frac{45.0}{655.0} \approx 0.0687$ | Hệ số chuyển đổi từ độ phân giải pixel sang kích thước thực tế ($mm$). Phụ thuộc cố định vào cấu hình đầu ra của phần cứng máy quét siêu âm.|
| `DEFAULT_MEASURE_IDS` | `[1, 5]` | Danh sách mảng chứa ID nhãn lớp cấu trúc giải phẫu kích hoạt thuật toán đo độ dày: `1 = effusion` (Dịch khớp), `5 = synovium` (Màng hoạt dịch).|
| `device` | `cuda` hoặc `cpu` | Khối phần cứng thực thi tính toán đồ họa (Tự động thiết lập dựa trên tính khả dụng của driver NVIDIA).|
### 4.2 Cấu hình Pipeline tiền xử lý và biến đổi ma trận ảnh (Transforms)
Hệ thống phân tách ảnh đầu vào thành các luồng biến đổi riêng biệt trước khi nạp vào tensor mô hình tùy thuộc vào mục tiêu xử lý chuyên biệt:
| Luồng xử lý Pipeline ảnh | Kích thước chuyển đổi (Resize) | Quy định chuẩn hóa phân phối ma trận (Normalization) |
| --- | --- | --- |
| **Phân loại góc & Phát hiện viêm** | <br>$224 \times 224$ pixel | Áp dụng phân phối phân cấp:<br> $\text{mean} = [0.485, 0.456, 0.406]$, <br> $\text{std} = [0.229, 0.224, 0.225]$ |
| **Phân đoạn cấu trúc (Segmentation)** | <br>$512 \times 512$ pixel | Không áp dụng chuẩn hóa phân phối (Chỉ thực thi hàm chuyển đổi tensor `ToTensor()`) |
---
## 5. Ràng buộc kỹ thuật & Quy tắc thiết kế hệ thống
### 5.1 Quản lý và giải phóng tài nguyên bộ nhớ GPU (VRAM Leak Warning)
Trong phiên bản hiện tại, logic xử lý nội tại của API kích hoạt các hàm `load_angle_model()`, `load_inflammation_model()`, và `load_segmentation_model_*()` trực tiếp bên trong vòng đời của mỗi phiên request nhận về. Hành vi này ép buộc GPU liên tục nạp lại dữ liệu tệp `.pth` vào VRAM cho mỗi giao dịch HTTP, sinh ra độ trễ (Overhead) I/O lớn và tiềm ẩn nguy cơ tràn bộ nhớ hệ thống. Khi triển khai môi trường Production, bắt buộc phải tái cấu trúc chuyển các hàm này thành Singleton dịch vụ (Tải một lần duy nhất lúc khởi động tiến trình Web Server).
### 5.2 Ràng buộc phi tuyến tính của tham số vật lý `PIXEL_TO_MM`
Hằng số quy đổi $\text{PIXEL\_TO\_MM} = \frac{45.0}{655.0}$ là một giá trị được cấu hình cứng (Hardcoded) trong mã nguồn, đặc trưng duy nhất cho một dòng máy siêu âm lâm sàng có tỷ lệ hiển thị $45mm$ tương đương với độ phân giải vùng quét $655\text{ px}$. Khi hệ thống thu thập ảnh siêu âm từ các thiết bị chuẩn đoán hình ảnh khác, hoặc thay đổi độ phân giải ảnh xuất ra, số liệu đo khoảng cách tổn thương sẽ sai lệch nghiêm trọng nếu hằng số này không được hiệu chuẩn lại thông qua ma trận nội quan của máy quét mới.
### 5.3 Quy tắc ánh xạ phân lớp (Class Remapping Matrix) đối với mô hình Custom
Hai mô hình tùy biến sâu phục vụ mặt cắt góc nhìn phía trên bánh chè (`UNet3+``EfficientFeedback Network`) được huấn luyện trên tập dữ liệu đặc thù sở hữu thứ tự cấu trúc mảng nhãn đầu ra lệch pha hoàn toàn so với kiến trúc phân cấp chuẩn của hệ thống. Để thống nhất dữ liệu trả về cho Client, khối Backend API thực hiện cơ chế tự động chuyển đổi chỉ mục mảng (Index Remapping) theo bảng đặc tả logic dưới đây:
| Chỉ mục Mô hình gốc (Output Model Index) | Chỉ mục chuẩn hóa hệ thống (Standard System Index) | Tên nhãn lớp giải phẫu tương ứng (Anatomical Label Class) |
| --- | --- | --- |
| `0` | `0` | **`background`** (Nền ảnh không chứa cấu trúc) |
| `1` | `2` | **`fat`** (Lớp mô mỡ dưới da) |
| `2` | `6` | **`tendon`** (Cấu trúc gân cơ) |
| `3` | `1` | **`effusion`** (Vùng tụ dịch khớp gối ổ viêm) |
| `4` | `4` | **`femur`** (Ranh giới cấu trúc xương đùi) |
| `5` | `5` | **`synovium`** (Màng hoạt dịch bao quanh khớp) |
| `6` | `3` | **`fat-pat`** (Tổ chức mỡ Hoffa) |
### 5.4 Cơ chế tự động dọn dẹp tập tin tồn đọng (Garbage Collection Task)
Các tập tin ảnh thô tải lên thư mục `uploads/` và ảnh xử lý nhị phân kết xuất lưu trong `results/` được ghi dưới dạng định danh chuỗi không trùng lặp UUID và lưu trữ vô thời hạn trên đĩa cứng hệ thống. Hệ thống lõi của ứng dụng không tích hợp cơ chế tự động giải phóng (Auto-deletion) các tệp cũ. Khi chạy vận hành dài hạn trong hệ thống y tế thực tế, bắt buộc phải cấu hình thêm Background Task (sử dụng thư viện Asyncio) hoặc thiết lập dịch vụ Cronjob của hệ điều hành để dọn dẹp định kỳ tránh cạn kiệt dung lượng ổ đĩa lưu trữ.
---
## 6. Giải pháp mở rộng tính năng mã nguồn (Backend Optimization Guide)
### 6.1 Tăng tốc độ phản hồi bằng Cơ chế Caching Mô hình Toàn cục
Thay thế kiến trúc nạp tải mô hình cũ bằng một kho lưu trữ Cache tĩnh trong bộ nhớ RAM, tối ưu hóa thời gian xử lý request từ mức giây xuống mức mili-giây:
```python
# Cấu hình biến lưu trữ toàn cục ở đầu tệp app.py
global_model_cache = {}
def get_cached_angle_model(selected_model_name: str):
cache_lookup_key = f"angle_classification_{selected_model_name}"
if cache_lookup_key not in global_model_cache:
# Thực hiện nạp trọng số mô hình từ đĩa cứng lần đầu tiên
global_model_cache[cache_lookup_key] = load_angle_model(selected_model_name)
return global_model_cache[cache_lookup_key]
# Áp dụng logic tương tự cho get_inflammation_model(), get_seg_model_sup(), get_seg_model_post()
```
### 6.2 Thêm mới một kiến trúc phân loại góc chụp (Ví dụ: Vision Transformer - ViT)
Để tích hợp một mạng nơ-ron mới vào hệ thống xử lý, tuân thủ nghiêm ngặt quy trình 3 bước sau:
* **Bước 1:** Bổ sung khối xử lý điều kiện rẽ nhánh logic vào hàm khởi tạo mô hình `load_angle_model()`:
```python
elif model_name == 'vit':
from torchvision.models import vit_b_16
# Khởi tạo mạng mạng ViT không nạp trọng số mặc định ImageNet
model = vit_b_16(weights=None)
# Tái cấu trúc tầng phân loại tuyến tính cuối cùng tương thích với 4 lớp nhãn đầu gối
model.heads[0] = nn.Linear(model.heads[0].in_features, 4)
# Tải tệp trọng số huấn luyện cục bộ từ thư mục mô hình
checkpoint_tensor = torch.load('models/best_vit_b16.pth', map_location=device, weights_only=False)
model.load_state_dict(checkpoint_tensor)
```
* **Bước 2:** Di chuyển tệp trọng số huấn luyện nhị phân của mạng (`best_vit_b16.pth`) vào chính xác không gian lưu trữ của thư mục `/models/`.
* **Bước 3:** Ứng dụng phía Client có thể kích hoạt mạng mới bằng cách truyền giá trị định danh qua tham số URL: `/api/analyze?angle_model=vit`.
### 6.3 Xử lý luồng dữ liệu phân đoạn song song đồng thời (Batch Processing API)
Tối ưu hóa năng lực phục vụ của Server đối với bài toán nhận diện hàng loạt ảnh cùng lúc từ phòng khám bằng endpoint xử lý không đồng bộ:
```python
from fastapi import errors, status
from fastapi.responses import JSONResponse
from typing import List
@app.post('/api/analyze_batch', status_code=status.HTTP_200_OK)
async def analyze_batch_images(images: List[UploadFile] = File(...)):
import asyncio
# Đóng gói các tác vụ phân tích ảnh đơn lẻ vào một danh sách hàng đợi task không đồng bộ
async_tasks_queue = [analyze_single_image_pipeline(img) for img in images]
# Kích hoạt thực thi đồng thời trên luồng phần cứng thông qua gather cơ chế
compiled_batch_results = await asyncio.gather(*async_tasks_queue)
return JSONResponse({
"results": compiled_batch_results,
"processed_count": len(compiled_batch_results)
})
```
### 6.4 Bản đóng gói container hóa ứng dụng (Production Dockerfile)
Đóng gói toàn bộ ML Stack bao gồm trình điều khiển GPU NVIDIA CUDA để triển khai đồng bộ trên các hạ tầng Cloud hoặc máy chủ On-Premise của bệnh viện:
```dockerfile
# Sử dụng Base Image chứa sẵn môi trường CUDA 12.4 và cuDNN 9 của NVIDIA
FROM nvidia/cuda:12.4.0-cudnn9-runtime-ubuntu22.04
# Cài đặt môi trường Python 3.10 và các gói hệ thống cốt lõi
RUN apt-get update && apt-get install -y \
python3.10 \
python3-pip \
git \
&& rm -rf /var/lib/apt/lists/*
# Thiết lập không gian làm việc nội bộ bên trong container
WORKDIR /app
# Sao chép và cài đặt danh sách các thư viện Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Sao chép toàn bộ mã nguồn ứng dụng vào Container
COPY . .
# Lắng nghe và kích hoạt ứng dụng Web Server FastAPI
CMD ["python", "app.py"]
```
* **Lệnh khởi dựng Image hệ thống:** `docker build -t medical-api-service .`
* **Lệnh kích hoạt Container chia sẻ tài nguyên phần cứng GPU vật lý:**
```bash
docker run --gpus all -p 8000:8000 -v $(pwd)/models:/app/models medical-api-service
```
### 6.5 Bộ chuyển đổi tiếp nhận trực tiếp luồng dữ liệu ảnh y tế chuẩn DICOM
Mở rộng chức năng cho phép hệ thống API đọc trực tiếp tệp tin ảnh gốc dạng `.dcm` trích xuất trực tiếp từ các thiết bị siêu âm chuẩn lâm sàng trong bệnh viện mà không cần qua bước chuyển đổi định dạng thủ công:
```python
import pydicom
from PIL import Image
import io
@app.post('/api/analyze_dicom')
async def analyze_dicom_file(file: UploadFile = File(...)):
# Đọc luồng byte nhị phân trực tiếp từ tệp DICOM tải lên
dicom_dataset = pydicom.dcmread(file.file)
# Trích xuất ma trận pixel thô từ thẻ DICOM Pixel Data
raw_pixel_array = dicom_dataset.pixel_array
# Chuyển đổi ma trận mảng Numpy sang định dạng ảnh PIL tương thích với Pipeline biến đổi
converted_image_pil = Image.fromarray(raw_pixel_array).convert('RGB')
# Chuyển tiếp ảnh đã đổi định dạng vào luồng xử lý tự động nội bộ của API
analysis_output_json = await execute_core_analysis_pipeline(converted_image_pil)
return analysis_output_json
```
---
## Phụ lục: Đặc tả Dữ liệu định lượng lâm sàng
### Phụ lục A: Bảng phân định mã màu mặt nạ phân đoạn ngữ nghĩa (Color Map Legend)
1. Cấu trúc Mặt cắt mặt trên bánh chè - Góc SUP (`sup-up-long`)
Góc SUP tập trung khoanh vùng các lớp mô mềm phía trước đầu gối phục vụ thuật toán tính toán độ dày dịch tụ.
| Từ khóa nhãn (Key) | Cấu trúc giải phẫu đích | Mã màu hiển thị (RGB) | Trực quan hóa màu sắc |
| --- | --- | --- | --- |
| `background` | Nền ảnh siêu âm | `[0, 0, 0]` | ⬛ Đen (Không chứa dữ liệu) |
| `effusion` | Vùng dịch khớp tụ ổ viêm | `[255, 0, 0]` | 🟥 Đỏ |
| `fat` | Tổ chức mô mỡ dưới da | `[255, 255, 0]` | 🟨 Vàng |
| `fat-pat` | Khối mỡ Hoffa | `[0, 255, 255]` | 🟦 Lam sáng |
| `femur` | Cấu trúc bề mặt xương đùi | `[0, 255, 0]` | 🟩 Xanh lá |
| `synovium` | Lớp màng hoạt dịch tăng sinh | `[255, 0, 255]` | 🟪 Tím |
| `tendon` | Vùng bó gân cơ | `[0, 0, 255]` | 🟦 Xanh dương |
> 🔄 **QUY TẮC CHUYỂN ĐỔI CHUYỂN GÓC (SUP $\rightarrow$ POST):**
> Khi hệ thống chuyển đổi trạng thái phân tích sang mặt cắt phía sau khớp gối (Góc `POST`), ma trận thuật toán phân đoạn sẽ tự động tái cấu trúc màu sắc ngữ nghĩa: Vùng tổn thương chứa **`effusion`** (màu đỏ) sẽ chuyển trạng thái biểu diễn thành **`baker's cyst`** (Kén Baker), và tổ chức cấu trúc vùng **`fat-pat`** (màu lam sáng) sẽ hoán đổi ý nghĩa thành vùng **`muscle`** (Cơ bắp vùng khoeo).
>
>
2. Cấu trúc Mặt cắt mặt sau vùng khoeo chân - Góc POST (`post-trans`)
| Từ khóa nhãn (Key) | Cấu trúc giải phẫu đích | Mã màu hiển thị (RGB) | Trực quan hóa màu sắc |
| --- | --- | --- | --- |
| `background` | Nền ảnh siêu âm | `[0, 0, 0]` | ⬛ Đen |
| `baker's cyst` | Tổ chức kén hoạt dịch vùng khoeo (Baker) | `[255, 0, 0]` | 🟥 Đỏ |
| `fat` | Lớp mô mỡ | `[255, 255, 0]` | 🟨 Vàng |
| `muscle` | Các nhóm cơ bắp vùng sau gối | `[0, 255, 255]` | 🟦 Lam sáng |
| `femur` | Cấu trúc xương đùi sau | `[0, 255, 0]` | 🟩 Xanh lá |
| `synovium` | Màng hoạt dịch mặt sau | `[255, 0, 255]` | 🟪 Tím |
| `tendon` | Hệ thống gân cơ mặt sau | `[0, 0, 255]` | 🟦 Xanh dương |
---
### Phụ lục B: Thang điểm đánh giá mức độ nghiêm trọng của ổ viêm (Clinical Severity Score)
Hệ thống chấm điểm toán học tự động căn cứ trên trọng số diện tích và độ dày phân tách để đưa ra kết luận mức độ bệnh lý lâm sàng thông qua phương trình tuyến tính tổng hợp:
$$\text{combined\_score} = \text{effusion\_score} \times 0.6 + \text{synovium\_score} \times 0.4$$
Dựa trên kết quả giá trị của biến số $\text{combined\_score}$, hệ thống tự động phân cấp thành 4 ngưỡng trạng thái lâm sàng tương ứng:
* **Mức 0 - Rất nhẹ ($\text{score} < 3$):** Trạng thái dịch khớp cấu trúc màng hoạt dịch nằm hoàn toàn trong giới hạn sinh bình thường của thể.
* **Mức 1 - Nhẹ ($\text{score}$ từ $3$ đến $7.9$):** Xuất hiện hiện tượng tụ dịch khớp lớp mỏng, màng hoạt dịch dấu hiệu tăng sinh nhẹ cấu trúc màng.
* **Mức 2 - Trung bình ($\text{score}$ từ $8$ đến $15$):** Lượng dịch tụ khớp gối mức độ vừa phải, màng hoạt dịch bắt đầu phì đại tăng sinh nét.
* **Mức 3 - Nặng ($\text{score} > 15$):** Lớp tụ dịch khớp gối dày kích thước lớn, màng hoạt dịch tăng sinh phì đại mạnh, lan rộng diện tích cấu trúc giải phẫu xung quanh.

View File

@@ -1,267 +0,0 @@
Dưới đây là toàn bộ nội dung tài liệu về **Công nghệ siêu âm khớp gối (PILOT)** đã được làm sạch, đồng bộ hóa cấu trúc và làm giàu thông tin (enrichment). Các hình ảnh mất mát và sơ đồ quy trình đã được mã hóa chi tiết bằng ngôn ngữ **PlantUML** cùng với các mô tả dữ liệu cấu trúc trực quan nhằm phục vụ tối ưu cho việc huấn luyện, tích hợp hoặc phát triển hệ thống backend của kỹ sư AI/ML stack.
---
# CÔNG NGHỆ SIÊU ÂM KHỚP GỐI (PILOT)
**Tác giả:** Nguyễn Đăng Hà
**Đơn vị phát triển:** VKIST (Viện Khoa học và Công nghệ Việt Nam - Hàn Quốc)
---
## 1. Giới thiệu chung & Mục tiêu nghiên cứu
### Giới thiệu chung
* Tràn dịch khớp gối là một trong những biểu hiện lâm sàng xuất hiện thường gặp của các bệnh lý liên quan đến cơ - xương - khớp.
* Việc nhận diện chính xác và đánh giá chi tiết mức độ viêm khớp gối có ý nghĩa vô cùng quan trọng đối với quá trình điều trị cũng như phục hồi chức năng của bệnh nhân.
* Hiện nay, phương pháp siêu âm được xem là giải pháp hiệu quả hàng đầu để đánh giá tình trạng này nhờ vào các đặc tính: an toàn, hoàn toàn không xâm lấn và tiết kiệm tối đa chi phí cho người bệnh.
### Mục tiêu nghiên cứu của dự án
1. Xây dựng một quy trình chuẩn hóa trong siêu âm chẩn đoán và thiết lập một cơ sở dữ liệu hình ảnh lớn (Large Dataset) chuyên biệt về tràn dịch khớp gối.
2. Nghiên cứu và phát triển phần mềm ứng dụng Trí tuệ nhân tạo (AI) giúp hỗ trợ các bác sĩ lâm sàng chẩn đoán nhanh chóng, hiệu quả tình trạng tràn dịch khớp gối.
3. Tiến hành thử nghiệm lâm sàng, kiểm thử và đánh giá độ chính xác của thuật toán AI trên các tập dữ liệu thực tế thu thập tại Bệnh viện E.
---
## 2. Quy trình xử lý dữ liệu và Kiến trúc hệ thống (Pipeline AI)
Hệ thống xử lý ảnh siêu âm được thiết kế theo một chuỗi pipeline tuần tự từ ảnh thô (Raw Image) đầu vào cho đến khi kết xuất báo cáo định lượng. Dưới đây là sơ đồ kiến trúc luồng dữ liệu của hệ thống:
```plantuml
@startuml
skinparam handwritten false
skinparam monochrome false
skinparam packageStyle rect
skinparam shadowing true
title SƠ ĐỒ PIPELINE XỬ LÝ ẢNH SIÊU ÂM KHỚP GỐI (AI BACKEND)
start
:Ảnh siêu âm đầu gối thô (Raw Image Input);
note right: Tải lên từ client thông qua Web UI
partition "Khối Tiền Xử Lý (Preprocessing Block)" {
:Tiền xử lý dữ liệu ảnh siêu âm;
note right: Chuẩn hóa kích thước, lọc nhiễu, cân bằng độ tương phản
}
partition "Khối Phân Loại (Classification Block)" {
:Phân loại góc chụp (Scan View Classification);
note right: Nhận diện mặt cắt: L SUPRAPAT LONG, L POST TRANS, vv.
}
partition "Khối Nhận Diện & Phân Đoạn (Detection & Segmentation Block)" {
:Nhận biết tình trạng viêm;
if (Phát hiện thấy có viêm?) then (Có viêm)
:Khoanh vùng viêm (Lesion Segmentation);
note right: Sử dụng các mô hình: DeepLabV3, MedSAM, UltraSAM
else (Không viêm)
:Kết luận: Bình thường;
detach
endif
}
partition "Khối Định Lượng & Phân Tích (Quantitative Analytics Block)" {
:Phân tích mức độ viêm;
note right: Tính toán diện tích vùng viêm,\ntỷ lệ phần trăm pixel viêm, quan hệ không gian với mô gân
}
:Kết xuất kết quả (Output Analytics & Report);
note right: Trả về file JSON kết quả, ảnh phân đoạn \nvà lưu trữ vào cơ sở dữ liệu hệ thống
stop
@enduml
```
---
## 3. Các Mô hình Deep Learning áp dụng trong hệ thống
Mô hình AI được chia tách thành ba nhiệm vụ chính: Phân loại mặt cắt ảnh siêu âm, Phát hiện viêm (biến cố cố định) và Phân đoạn ngữ nghĩa (Semantic Segmentation) các vùng giải phẫu tổn thương.
### 3.1. Mô hình Phân loại Góc chụp (Scan View Classification - `angle_model`)
* **Nhiệm vụ:** Tự động nhận diện và phân loại cấu trúc tư thế đặt đầu dò siêu âm.
* **Mô hình mặc định:** `convnext`
* **Các kiến trúc hỗ trợ:**
* **ConvNeXt Tiny:** Cấu hình gồm 4 lớp đầu ra (Mặc định).
* **DenseNet-121:** Trích xuất đặc trưng sâu nhờ cơ chế kết nối dày đặc.
* **ResNet-50:** Kiến trúc mạng phần dư chuẩn hóa giúp tối ưu hóa quá trình hội tụ.
* **EfficientNet-B2:** Cân bằng tối ưu giữa hiệu năng và tài nguyên tính toán.
* **Swin Transformer V2-S:** Mô hình dựa trên cơ chế Attention dịch chuyển cửa sổ, nâng cao khả năng học đặc trưng toàn cục.
* **Các mặt cắt mục tiêu chính:**
* `L SUPRAPAT LONG` (Mặt cắt dọc trên xương bánh chè khớp gối trái).
* `L POST TRANS` (Mặt cắt ngang phía sau khớp gối trái).
### 3.2. Mô hình Phát hiện Viêm (Inflammation Detection - `inflammation_model`)
* **Nhiệm vụ:** Tự động phát hiện và đánh giá tình trạng viêm (hiện cố định trong hệ thống).
* **Mô hình mặc định:** `efficientnet_b0`
### 3.3. Mô hình Khoanh vùng & Phân đoạn tổn thương (Segmentation Models)
Hệ thống cho phép tùy chọn linh hoạt các kiến trúc mạng tiên tiến nhằm khoanh vùng chính xác các cấu trúc giải phẫu và ổ viêm theo từng nhóm góc chụp:
#### 3.3.1. Nhóm Phân đoạn SUP (Mặt cắt dọc trên xương bánh chè - `segment_model_sup`)
| Tên Mô Hình (Giá trị truyền vào) | Kiến trúc kĩ thuật & Vai trò trong hệ thống |
| --- | --- |
| **deeplabv3** | Kiến trúc DeepLabV3 ResNet-50 — 7 lớp (Mặc định). Phân đoạn phân cấp chuẩn giúp nhận diện ranh giới vùng mô mềm phức tạp. |
| **unet_resnet101** | Kiến trúc UNet kết hợp với encoder ResNet-101 mạnh mẽ, tối ưu việc khôi phục chi tiết không gian ảnh. |
| **efficientfeedback** | Kiến trúc EfficientFeedbackNetwork (tùy chỉnh), tăng cường cơ chế phản hồi đặc trưng để làm mịn biên tổn thương. |
| **unet3plus** | Kiến trúc UNet3+ kết hợp cơ chế Attention (tùy chỉnh), tối ưu khả năng kết nối bỏ qua full-scale cho ảnh siêu âm. |
#### 3.3.2. Nhóm Phân đoạn POST (Mặt cắt ngang phía sau - `segment_model_post`)
| Tên Mô Hình (Giá trị truyền vào) | Kiến trúc kĩ thuật & Vai trò trong hệ thống |
| --- | --- |
| **deeplabv3_resnet101** | Kiến trúc DeepLabV3 ResNet-101 — 7 lớp (Mặc định). Chuyên biệt cho việc phân đoạn góc post-trans, tối ưu hóa độ chính xác biên tổn thương vùng khoeo sau gối. |
*(Lưu ý: Các mô hình MedSAM và UltraSAM không xuất hiện trong danh mục cấu hình kỹ thuật của phiên bản Pilot 2025 này, nên đã được thay thế bằng các mô hình thực tế hệ thống đang hỗ trợ bao gồm UNet và EfficientFeedback).*
### 3.3. Cơ chế phân tích định lượng mức độ viêm (Severity Analytics)
Sau khi mô hình phân đoạn (ví dụ: DeepLabV3) đưa ra mặt nạ phân đoạn (Segmentation Mask) , hệ thống thực hiện thuật toán đếm pixel để đưa ra báo cáo tự động:
* **Phân cấp độ viêm:** Hệ thống tự động phân loại thành 3 mức độ từ nhẹ đến nặng bao gồm: **Viêm mức 1**, **Viêm mức 2**, và **Viêm mức 3** (Nặng).
* **Các chỉ số định lượng đầu ra (Output Metrics):**
* **Tỷ lệ vùng viêm:** $\text{Tỷ lệ \%} = \frac{\text{Tổng số pixel vùng viêm}}{\text{Tổng số pixel của toàn bộ ảnh}} \times 100\%$.
* **Phân tích quan hệ không gian (Spatial/Boundary Analysis):** Xác định vị trí tương quan của ổ viêm với các cấu trúc giải phẫu lân cận như: Vùng mô mỡ (`Fat`), Vùng gân (`Tendon`), Xương đùi (`Femur`).
* *Ví dụ phân tích hệ thống:* "Vùng viêm nằm giữa vùng gân chiếm tỷ lệ $14.4\%$ vùng xung quanh".
---
## 4. Đặc tả chức năng của Phần mềm Web (Web UI/UX Specification)
Phần mềm được xây dựng dưới dạng ứng dụng Web tích hợp (Integrated Ultrasound Analytics Platform) sở hữu các chức năng cụ thể sau:
* **Tải lên ảnh đầu vào:** Giao diện hỗ trợ Kéo & thả ảnh siêu âm trực tiếp hoặc nút chọn file ảnh từ máy tính.
* **Tiền xử lý tự động:** Tự động chuẩn hóa ảnh, khử nhiễu đầu vào.
* **Nhận diện tự động:** Xác định góc chụp/mặt cắt ảnh tự động thông qua khối phân loại.
* **Phân đoạn thông minh:** Tự động định vị, khoanh vùng tổn thương viêm bằng màu trực quan đè lên ảnh gốc (Overlay Mask).
* **Định lượng đa chỉ số:** Trả về độ tin cậy của mô hình ($\%$ Confidence), phân loại mức độ nặng, tính toán diện tích pixel tổn thương.
* **Tương tác lâm sàng:** Cho phép bác sĩ nhập ghi chú, nhận xét và các khuyến nghị điều trị trực tiếp trên giao diện.
* **Lưu trữ & Kết xuất:** Lưu trữ kết quả phân tích vào DB và cho phép xuất báo cáo, tải ảnh phân đoạn về máy.
---
## 5. Kết quả thử nghiệm dữ liệu thực tế tại Bệnh viện E
Hệ thống đã được đánh giá chéo giữa kết luận tự động của mô hình AI và chẩn đoán lâm sàng thực tế của các bác sĩ chuyên khoa tại Bệnh viện E mang lại kết quả có độ tương quan rất cao:
Bảng so sánh dữ liệu thử nghiệm thực tế 1
### Bảng 1: So sánh dữ liệu thử nghiệm thực tế 1
| Tiêu chí | Kết luận tự động từ AI | Kết luận lâm sàng của Bác sĩ |
| :--- | :--- | :--- |
| **Ca bệnh 1** | - Phân loại: **Có viêm**<br>- Độ tin cậy: **94.5%**<br>- Mức độ: **Nặng**<br>- Tỷ lệ diện tích viêm: **6.95%**<br>- Mô tả không gian: Vùng viêm lan rộng, nằm giữa vùng gân (chiếm 37.2% vùng xung quanh). | **Khớp gối phải:** Màng hoạt dịch dày, có dịch kích thước ~6.8mm, xuất hiện gai xương nhỏ khe đùi chày trong ngoài khớp.<br><br>**Khớp gối trái:** Màng hoạt dịch dày, có dịch ~11mm (dịch đồng nhất).<br><br>**KẾT LUẬN:** Viêm màng hoạt dịch, tràn dịch khớp gối hai bên kèm thoái hóa khớp gối hai bên. |
| **Ca bệnh 2** | - Phân loại: **Có viêm**<br>- Độ tin cậy: **95.1%**<br>- Mức độ: **Nặng**<br>- Tỷ lệ diện tích viêm: **7.54%**<br>- Mô tả không gian: Vùng viêm lan rộng, nằm giữa vùng gân (chiếm 12.4% vùng xung quanh). | (Đồng nhất dữ liệu chẩn đoán lâm sàng tương tự ca bệnh 1 cho thấy độ tương quan cao về mặt định lượng của mô hình đối với các ca tràn dịch mức độ nặng). |
---
### Bảng 2: So sánh dữ liệu thử nghiệm thực tế 2
| Tiêu chí | Kết luận tự động từ AI | Kết luận lâm sàng của Bác sĩ |
| :--- | :--- | :--- |
| **Đánh giá Khớp Gối Phải** | - Trạng thái: **Có viêm**<br>- Độ tin cậy: **86.5%**<br>- Mức độ: **Nặng**<br>- Tỷ lệ vùng viêm: **6.77%**<br>- Vùng viêm nằm giữa vùng gân (24.6% vùng bao quanh). | - Màng hoạt dịch dày, có dịch kích thước **10mm** (tính chất dịch đồng nhất).<br>- Xuất hiện gai xương tại vị trí khe đùi chày ngoài khớp.<br>- Bề mặt sụn khớp đều, tuy nhiên cấu trúc âm không đồng nhất.<br>- Không có hình ảnh kén khoeo. |
| **Đánh giá Khớp Gối Trái** | - Trạng thái: **Có viêm**<br>- Độ tin cậy: **91.7%**<br>- Mức độ: **Nặng**<br>- Tỷ lệ vùng viêm: **15.96%**<br>- Vùng viêm nằm giữa vùng gân (30.5% vùng bao quanh). | - Màng hoạt dịch dày, ghi nhận có ít dịch.<br>- Xuất hiện gai xương tại khe đùi chày trong ngoài khớp.<br>- Bề mặt sụn khớp đều, cấu trúc âm không đồng nhất.<br>- Không có hình ảnh kén khoeo. |
| **KẾT LUẬN CHUNG** | *(AI hỗ trợ định lượng diện tích tổn thương chuyên sâu)* | **Hình ảnh:** Viêm màng hoạt dịch khớp gối hai bên, Tràn dịch khớp gối phải kèm theo Thoái hóa khớp gối hai bên. |
## 6. Hướng dẫn cài đặt và Triển khai mã nguồn Backend (ML Stack Deployment)
Dành cho coding agent vận hành hoặc deploy môi trường ứng dụng:
### 6.1. Yêu cầu hệ thống (Prerequisites)
* **Hệ điều hành hỗ trợ:** Ubuntu / Windows.
* **Trình quản lý gói:** Anaconda / Miniconda.
* **Hỗ trợ phần cứng:** CUDA Toolkit được cài đặt sẵn (Bắt buộc nếu cấu hình sử dụng GPU tăng tốc suy luận).
### 6.2. Các bước triển khai chi tiết
**Bước 1: Tải mã nguồn từ kho lưu trữ (Git Clone)**
```bash
# Thực hiện clone thư mục chứa dự án từ Git Server hệ thống
git clone https://vkist-hub.com/itvkist/vkist-ultrasound.git
# Di chuyển vào thư mục và cập nhật nếu đã tồn tại phiên bản trước đó
cd vkist-ultrasound
git pull
```
**Bước 2: Khởi tạo môi trường ảo và Cài đặt các thư viện phụ thuộc**
```bash
# Tạo môi trường ảo conda mới chạy Python phiên bản ổn định 3.10
conda create -n vkist-ultrasound python=3.10 -y
# Kích hoạt môi trường ảo vừa tạo
conda activate vkist-ultrasound
# Tiến hành cài đặt tất cả các dependencies phụ thuộc nằm trong file requirements
pip install -r requirements.txt
```
**Bước 3: Khởi chạy ứng dụng Web Server Backend**
```bash
# Chạy file khởi tạo ứng dụng chính
python app.py
```
>
> **Thông tin triển khai thành công:** Sau khi dòng lệnh thực thi hoàn tất, phần mềm cục bộ sẽ khởi chạy và lắng nghe kết nối tại địa chỉ URL mặc định: `http://localhost:8000`.
>
>

View File

@@ -1,256 +0,0 @@
# VKIST ULTRASOUND - TÀI LIỆU HƯỚNG DẪN & GIỚI THIỆU DỰ ÁN
---
## 1. Giới thiệu chung
VKIST Ultrasound là hệ thống hỗ trợ chẩn đoán viêm khớp gối tiên tiến ứng dụng Trí tuệ nhân tạo (AI). Hệ thống giúp bác sĩ phân tích hình ảnh siêu âm một cách tự động, từ việc nhận diện chính xác góc chụp đến việc đo đạc các chỉ số bệnh lý phức tạp. Qua đó, giải pháp này giúp tối ưu hóa quy trình khám chữa bệnh, giảm thiểu sai sót chủ quan và nâng cao hiệu suất làm việc tại các cơ sở y tế.
---
## 2. Quy trình xử lý toàn diện (AI Pipeline Workflow)
Hệ thống vận hành theo một quy trình khép kín, tự động phân nhánh logic dựa trên tính chất hình ảnh đầu vào:
1. **Tiếp nhận & Tiền xử lý ảnh:** Tải ảnh siêu âm thô lên hệ thống. AI tự động áp dụng thuật toán tăng cường tương phản CLAHE để làm rõ nét các cấu trúc giải phẫu bị mờ hoặc nhiễu.
2. **Phân loại góc chụp (Angle Classification):** Tự động xác định tư thế chụp/mặt cắt nhằm kích hoạt nhánh pipeline phân tích chuyên biệt.
3. **Phát hiện Viêm (Inflammation Detection):** Đánh giá sơ bộ sự hiện diện của dịch khớp hoặc tình trạng tăng sinh màng hoạt dịch.
4. **Phân vùng & Đo đạc (Segmentation & Measurement):** Tách biệt các lớp mô giải phẫu (xương, dịch, màng, gân...) và tự động xác định độ dày tổn thương tại các vùng trọng yếu.
5. **Đánh giá mức độ nặng (Severity Scoring):** Tính toán chỉ số tổng hợp để phân cấp mức độ viêm khớp.
6. **Quản lý hồ sơ & Báo cáo:** Lưu trữ dữ liệu chuẩn hóa vào hệ thống nội bộ và kết xuất phiếu kết quả khám bệnh dạng PDF.
Dưới đây là sơ đồ luồng logic của hệ thống được thiết kế bằng **PlantUML** giúp lập trình viên backend dễ dàng triển khai:
```plantuml
@startuml
skinparam handwritten false
skinparam monochrome false
skinparam packageStyle rect
skinparam shadowing true
title SƠ ĐỒ ĐIỀU HƯỚNG LOGIC PIPELINE - VKIST ULTRASOUND AI
start
:Tiếp nhận ảnh siêu âm từ Web UI;
:Áp dụng thuật toán tiền xử lý CLAHE (Tăng cường độ nét);
:Mô hình Phân loại góc chụp (Angle Model);
note right: Tích hợp ConvNeXt / Swin / DenseNet
if (Góc chụp hợp lệ?) then (Không thuộc góc Sup_up_long / Post_trans)
:Hiển thị nhãn góc chụp (ví dụ: Med-Lat Long);
:Thông báo lỗi: Góc chụp đầu vào không hỗ trợ xử lý tiếp;
stop
else (Hợp lệ)
:Mô hình Phát hiện viêm (EfficientNet-B0);
if (Trạng thái ổ khớp?) then (Không viêm)
:Hiển thị trạng thái KHÔNG VIÊM trên giao diện;
:Cho phép nhập thông tin và lưu trữ cơ bản;
else (Có viêm)
:Hiển thị trạng thái CÓ VIÊM;
if (Góc chụp phát hiện?) then (Post_trans)
:Chạy phân đoạn vùng tổn thương (DeepLabV3-ResNet101);
:Khoanh vùng, gắn nhãn màu Nang Baker (Baker's Cyst);
else (Sup_up_long / Suprapat)
:Chạy phân đoạn vùng tổn thương (DeepLabV3-ResNet50);
partition "Thuật toán Đo đạc thông minh" {
:Smart ROI (Tập trung 1/3 khu vực giữa);
:Continuous Segment Search (Tìm đoạn dịch liên tục dài nhất);
:Xác định độ dày ổ dịch/màng hoạt dịch (mm);
}
:Tính điểm toán học phân cấp Mức độ bệnh (Cấp 0 -> 3);
endif
fi
endif
split
:Nhập thông tin bệnh nhân & Chẩn đoán lâm sàng;
:Lưu trữ cấu trúc thư mục nội bộ (patients/);
split again
:Xuất phiếu kết quả khám bệnh dạng PDF (report.pdf);
end split
stop
@enduml
```
---
## 3. Các Tính năng Chi tiết & Mô hình học máy
### 3.1. Hệ thống Mô hình AI Đa dạng (Multi-Model Architecture)
* **Khối Phân loại Góc chụp:** Tích hợp các kiến trúc mạng mạng nơ-ron tiên tiến (SOTA) bao gồm ConvNeXt-Tiny (đạt độ chính xác tuyệt đối 100% trên tập kiểm thử) , Swin Transformer, và DenseNet. Hệ thống mặc định sử dụng ConvNeXt làm lõi phân loại chính đầu tiên cho mọi ảnh.
* **Khối Phân vùng (Semantic Segmentation):** Hỗ trợ linh hoạt các mô hình DeepLabV3+ (hoặc DeepLabV3-ResNet50 với độ chính xác 91.67%), UNet3+ Attention, và EfficientFeedback. Các cấu trúc này được tối ưu hóa sâu nhằm nhận diện chính xác đường biên ranh giới màng hoạt dịch phức tạp.
### 3.2. Tính năng Đo đạc Thông minh (Smart Measurement)
Nhánh xử lý góc `Sup_up_long` tích hợp hai thuật toán hình học độc quyền nhằm loại bỏ sai số đo đạc:
* **Smart ROI:** Hệ thống tự động khoanh vùng và tập trung phân tích vào khu vực 1/3 chính giữa của vùng nghi ngờ, nơi có giá trị và chỉ số chẩn đoán lâm sàng cao nhất.
* **Continuous Segment Search:** Thuật toán tự động tìm kiếm đoạn mặt nạ (mask) liên tục dài nhất. Cơ chế này đảm bảo kết quả tính toán độ dày của màng và dịch khớp phản ánh đúng thực tế, khách quan và không bị ảnh hưởng bởi nhiễu ảnh cục bộ.
### 3.3. Phân cấp Mức độ Bệnh (Severity Classification)
Hệ thống tính toán chỉ số kết hợp giữa diện tích dịch khớp tụ và mức độ tăng sinh màng hoạt dịch để phân định thành 4 cấp độ bệnh lý rõ ràng:
| Cấp độ viêm | Phân loại lâm sàng | Đặc điểm cấu trúc hình ảnh siêu âm |
| --- | --- | --- |
| **Cấp 0** | Rất nhẹ | Lượng dịch khớp nằm trong giới hạn sinh lý bình thường.|
| **Cấp 1** | Nhẹ | Lớp dịch khớp mỏng, xuất hiện tăng sinh màng hoạt dịch mức độ nhẹ.|
| **Cấp 2** | Trung bình | Lượng dịch khớp và màng hoạt dịch tăng sinh phì đại rõ rệt.|
| **Cấp 3** | Nặng | Ổ dịch khớp dày, màng hoạt dịch phát triển mạnh và xâm lấn sâu.|
---
## 4. Đặc tả Giao diện & Hướng dẫn Vận hành Hệ thống
Giao diện Web UI của ứng dụng được thiết kế phân cấp tối giản theo bố cục 3 luồng dọc: **Bảng trái (Cấu hình Mô hình)** $\rightarrow$ **Bảng giữa (Tải ảnh & Hiển thị)** $\rightarrow$ **Bảng phải (Nhập dữ liệu & Xem kết quả phân tích)**.
### Quy trình vận hành 4 bước dành cho Bác sĩ:
#### Bước 1: Cấu hình Mô hình AI (Left Panel)
* Trước khi tiến hành tải ảnh siêu âm, bác sĩ có thể linh hoạt tùy chọn thuật toán AI mong muốn cho từng block tác vụ tại bảng điều khiển bên trái. Hệ thống đã được cấu hình mặc định sẵn các mô hình tối ưu nhất.
#### Bước 2: Tải ảnh siêu âm lên hệ thống (Middle Panel)
* Bác sĩ thực hiện kéo thả tập tin ảnh trực tiếp vào vùng nhận diện hoặc nhấn nút `"Chọn ảnh"`.
* Ngay sau khi tệp tin được nạp, hệ thống sẽ tự động kích hoạt toàn bộ Pipeline phân tích ngầm theo luồng xử lý tự động.
#### Bước 3: Đọc kết quả phân tích tự động từ AI (Right Panel & Modal Popup)
Hệ thống tự động hiển thị kết quả trực quan dựa trên nhãn mặt cắt nhận diện được:
*
**Trường hợp góc chụp không hỗ trợ (ví dụ: `Med-Lat Long`):** Hệ thống lập tức xuất nhãn cảnh báo màu tím `"GÓC CHỤP: Med-Lat Long"` và dừng tiến trình xử lý tiếp theo.
*
**Trường hợp góc mặt sau `Post_trans`:** Hệ thống kiểm tra tình trạng viêm. Nếu có viêm, ảnh phân đoạn sẽ hiển thị màu overlay trực quan khoanh vùng chính xác ổ tổn thương **Nang Baker** (Baker's cyst) kèm bảng chú thích màu sắc mô giải phẫu tương ứng.
*
**Trường hợp góc mặt trước `Sup_up_long`:** Hệ thống tiến hành phân đoạn, hiển thị đường lưới đo đạc thông minh. Đồng thời tính toán chi tiết độ dày ổ dịch bằng đơn vị milimét ($mm$) và đưa ra chẩn đoán mức độ viêm chính xác.
#### Bước 4: Lưu trữ hồ sơ dữ liệu & Xuất bản phiếu khám lâm sàng
* Bác sĩ thực hiện điền đầy đủ thông tin vào form biểu mẫu bệnh nhân ở bảng bên phải (Trong đó hai trường dữ liệu **Mã bệnh nhân****Họ và tên** là bắt buộc). Ghi nhận thêm nhận xét lâm sàng cá nhân vào ô "Chẩn đoán của bác sĩ".
* Nhấn nút `"Lưu dữ liệu"` để hệ thống đóng gói và lưu trữ nội bộ vào máy chủ.
* Nhấn nút `"Xuất phiếu khám (PDF)"` để tải về máy mẫu phiếu in kết quả y khoa chính thức trả cho bệnh nhân.
---
## 5. Đặc tả Kiến trúc Lưu trữ Dữ liệu Đầu ra
### 5.1. Cấu trúc cây thư mục lưu trữ hồ sơ bệnh nhân (Storage Structure)
Khi bác sĩ nhấn chọn chức năng lưu trữ dữ liệu , hệ thống tự động khởi tạo cấu trúc thư mục phân cấp động theo mốc thời gian thực tại phân vùng `patients/` nhằm tránh trùng lặp thông tin:
```text
patients/
└── <Mã_Bệnh_Nhân>_<Họ_Và_Tên_Không_Dấu>/
└── <NamThangNgay>_<GioPhutGiay>/
├── info.txt # Tệp tin cấu trúc lưu trữ siêu dữ liệu định lượng của ca bệnh
├── original.png # File hình ảnh siêu âm gốc (hoặc ảnh đã tăng cường CLAHE)
├── segmented.png # File ảnh overlay mặt nạ phân đoạn màu từ AI mô hình
└── report.pdf # File phiếu kết quả chẩn đoán y khoa chính thức xuất cho bệnh nhân
```
*Ví dụ thực tế:* `patients\BN0001_Nguyen_Van_A\20260505_170011\`
### 5.2. Định dạng cấu trúc tệp dữ liệu `info.txt`
Tệp tin `info.txt` đóng vai trò lưu trữ toàn bộ thông tin tối giản, giúp hệ thống hoặc các agent xử lý số liệu có thể dễ dàng phân tích (parse) dữ liệu mà không cần đọc file PDF:
```text
--- THÔNG TIN BỆNH NHÂN ---
Mã bệnh nhân: BN0001
Họ tên: Nguyễn Văn A
Giới tính: Nam
Tuổi: 88
Ghi chú lâm sàng: Tràn dịch và có thể viêm
--- KẾT QUẢ PHÂN TÍCH AI ---
Góc chụp: sup-up-long (99.93%)
Viêm nhiễm: Có (94.44%)
Độ dày màng: 6.53 mm (95 px)
Vị trí x: 420
Mức độ: Trung bình
Mô tả: Dịch khớp trung bình (51px), màng hoạt dịch tăng sinh vừa
```
(Ghi chú: Nội dung trên được ánh xạ chính xác từ các chỉ số định lượng thu được qua mô hình phân đoạn hình học của hệ thống ).
### 5.3. Quy chuẩn nội dung phiếu kết quả y khoa `report.pdf`
Phiếu kết quả chẩn đoán hình ảnh dạng PDF được tạo tự động với cấu trúc bố cục chuẩn hóa y tế gồm 4 phần chính:
1.
**Thông tin Cơ sở & Tiêu đề:** Biểu trưng nhận diện VKIST, tên "TRUNG TÂM CHẨN ĐOÁN HÌNH ẢNH VKIST", địa chỉ "Khu Công nghệ cao Hòa Lạc, Thạch Thất, Hà Nội" kèm tiêu đề lớn **"PHIẾU KẾT QUẢ SIÊU ÂM KHỚP GỐI"**.
2. I. Thông tin bệnh nhân: Hiển thị chi tiết Họ tên, Giới tính, Mã BN, Tuổi của người bệnh.
3. II. Hình ảnh siêu âm: Chèn song song hai khung hình trực quan bao gồm `Hình 1: Ảnh gốc / Tăng cường``Hình 2: Ảnh phân đoạn AI` (có kèm sơ đồ lưới đo đạc và chú thích màu).
4. **III. Kết quả phân tích tự động (AI Metric):**
* Góc chụp dự đoán đạt tỷ lệ tương ứng (Ví dụ: `sup-up-long` - Độ tin cậy: `99.93%`).
* Tình trạng viêm ổ khớp (Ví dụ: `Có khả năng viêm` - Độ tin cậy: `94.44%`).
* Chỉ số đo đạc vật lý: Độ dày dịch & màng hoạt dịch tính toán được đạt mức `6.53 mm`.
* Đánh giá mức độ viêm tổng hợp: `Trung bình`.
* Chi tiết mô tả định lượng: Dịch khớp trung bình ($51\text{ px}$), màng hoạt dịch tăng sinh vừa ($95\text{ px}$).
5. IV. Chẩn đoán và kết luận của Bác sĩ: Trích xuất nguyên vẹn nội dung ghi chú lâm sàng do bác sĩ trực tiếp nhập vào hệ thống (Ví dụ: `"Tràn dịch và có thể viêm"`).

View File

@@ -1,11 +0,0 @@
# ML Model Architecture Report
| File | Architectures (code ranges) |
|------|-----------------------------|
| `ML/arch/unet3plus_att.py` | - **UNet3Plus_Attention** (87268)<br> &nbsp;&nbsp;- SelfAttention (725)<br> &nbsp;&nbsp;- AttentionGate (2866)<br> &nbsp;&nbsp;- conv_block (6983) |
| `ML/arch/efficientfeedback.py` | - **EfficientFeedbackNetwork** (171239)<br> &nbsp;&nbsp;- convblock (914)<br> &nbsp;&nbsp;- DecoderBlock (1735)<br> &nbsp;&nbsp;- ASPP_module (3766)<br> &nbsp;&nbsp;- CAM_Module (6886)<br> &nbsp;&nbsp;- S_Module (93131)<br> &nbsp;&nbsp;- FeedbackSpatialAttention (134151)<br> &nbsp;&nbsp;- StageAttentionwCAM (153168) |
| `ML/segment_anything/modeling/image_encoder.py` | - **ImageEncoderViT** (18119)<br> &nbsp;&nbsp;- PatchEmbed (389420)<br> &nbsp;&nbsp;- Block (122187)<br> &nbsp;&nbsp;- Attention (190254) |
| `ML/segment_anything/modeling/prompt_encoder.py` | - **PromptEncoder** (17181)<br> &nbsp;&nbsp;- PositionEmbeddingRandom (183226) |
| `ML/segment_anything/modeling/mask_decoder.py` | - **MaskDecoder** (17190)<br> &nbsp;&nbsp;- MLP (168190) |
| `ML/segment_anything/modeling/transformer.py` | - **TwoWayTransformer** (17108)<br> &nbsp;&nbsp;- TwoWayAttentionBlock (110183)<br> &nbsp;&nbsp;- Attention (186243) |
| `ML/segment_anything/modeling/sam.py` | - **Sam** (19181) *(no internal subarchitectures; uses imported modules)* |

View File

@@ -0,0 +1,570 @@
# Sprint 1-2 Architecture Specification
**Sprint:** Sprint 1 (June 15 June 26) + Sprint 2 (June 29 July 10)
**Theme:** "The Fast PoC Baseline" + "Multi-Modal & NLP Integration"
**Parent Document:** [SOLUTION_ARCHITECTURE_SPEC.md](../SOLUTION_ARCHITECTURE_SPEC.md)
---
## 1. Sprint Scope & Goals
### 1.1 Sprint 1: The Fast PoC Baseline
- Establish interactive end-to-end processing pipeline
- Rapid UI prototype with high-fidelity mockups
- Configure initial FastAPI server pipelines to process raw array matrices
- Output early inference mask previews onto browser-based preview window canvas
- Wire ultrasound image ingest pathways into classification pipeline
### 1.2 Sprint 2: Multi-Modal & NLP Integration
- Embed localized NLP translation modules
- Implement client-side privacy scrubbing masks for Decree 13 compliance
- Configure on-device string parsers to sanitize personal identification tokens
- Deploy multi-modal diagnostic chat system
- Build zero-friction NLP conduits for clinical abbreviations
---
## 2. Architectural Constraints for Sprint 1-2
| Constraint | Implementation |
| --- | --- |
| **Air-Gapped Hospital LAN** | No external cloud processing during normal operation. All inference and storage on-premise via K3s. NFR-16a governs emergency cloud fallback only. |
| **Code & Issue Hosting (NFR-16a exception)** | GitLab CE and Jira run self-hosted on a cloud VM (not SaaS). All code, tickets, and pipeline configs transit over public internet to reach the VM. Compensating controls: no PHI in commits or tickets (pre-push hook + team policy); SSH-only git access with certificate pinning; daily GitLab RDB backup to hospital MinIO (not cloud storage); cloud VM IAM with minimum-role access and 2FA; VM access restricted to hospital whitelisted IPs. This exception is reviewed at PoC sign-off. |
| **Network Bandwidth (10 Mbps max)** | Optimize payload sizes, leverage local caching (IndexedDB, Redis). |
| **DICOM Compliance** | Process DICOM images via pydicom stream over local HTTPS. |
| **Client Memory ≤ 150 MB** | PWA uses React + Zustand + LiteRT (no dedicated GPU required). |
| **Latency ≤ 1.5s for inference** | Heavy ML offloaded to Triton Inference Server with ONNX/TensorRT. |
| **Zero PHI Leakage** | Client-side scrubbing before network transfer; no PHI in Git/Jenkins. |
---
## 3. System Context Diagram (C4) - Sprint 1-2 Scope (FR-25 Synovitis Grading)
```plantuml
@startuml "VKIST_MSK_Sprint1_2_Context_FR25"
!include <C4/C4_Context>
title System Context - Sprint 1-2: FR-25 Synovitis Grading & NLP Integration
' Sprint 1-2 scope: FR-25 only with NLP safety layer
' Active Users: UP5 | Governance: UP1, UP4 | Future: UP6, UP7, UP8
' === ACTORS IN SPRINT SCOPE ===
Person(radiologist, "Diagnostic Radiologist (UP5)", "FR-25: load scan, review AI grade (0-3), confirm/override, finalize & sign, view GradCAM, engage circuit breaker, review RAG evidence")
' === GOVERNANCE / NFR ALIGNMENT ===
Person_Ext(senior_expert, "Healthcare Senior Expert (UP1)", "Governance: clinical protocol validation, MOH guideline approval, model threshold sign-off")
Person_Ext(support_staff, "Support Staff (UP4)", "Registration, case queue management")
' === FUTURE SPRINTS ===
Person_Ext(therapist, "Physical Therapist (UP6)", "Future sprint: prescription parser, 3D mapping, kinetic overlay, patient education")
Person_Ext(ortho_surgeon, "Orthopedic Surgeon (UP7)", "Future sprint: treatment planning, aggregated dashboard, 3D anatomy education")
Person_Ext(patient_caregiver, "Patient & Caregiver (UP8)", "Future sprint: self-monitoring portal, inflammation tracker, medication reminders")
Person(admin, "System Administrator", "K3s deployment, model updates, NGINX failover, Prometheus/Grafana monitoring")
' === SYSTEM ===
System(mpps, "VKIST MSK Processing System\n(FR-25 Sprint 1-2)", "Knee ultrasound AI: angle→inflammation→segmentation\nGrading: synovitis 0-3 with GradCAM\nSafety: Circuit Breaker, BERT drift, RAG-Referee\nNLP: Decree 13 scrubbing, GemmaE2B/MedGemma explanations\nReporting: Circular 46 PDF")
' === EXTERNAL SYSTEMS IN SCOPE ===
System_Ext(pacs, "Hospital PACS / Ultrasound", "DICOM source (C-MOVE + direct capture)")
System_Ext(emr, "Hospital EMR/HIS", "Finalized reports, audit logs (HL7/FHIR)")
System_Ext(triton, "Triton Inference Server", "GPU inference: angle, inflammation, segmentation + embeddings")
System_Ext(knowledge, "Clinical Knowledge Stack", "ladybugDB (ontology), pgvector (MOH guideline vectors in Postgres HNSW), EmbeddingGemma (RAG embedding model), GemmaE2B (browser WebLLM), MedGemma (cloud Vertex AI)")
' === RELATIONSHIPS ===
Rel(radiologist, mpps, "Load scan, review grade, confirm/override, finalize, view explanations, engage safety dialog", "HTTPS")
Rel(admin, mpps, "Deploys, monitors, configures", "HTTPS/SSH")
Rel(senior_expert, mpps, "Validates protocols, approves thresholds", "HTTPS")
Rel(support_staff, mpps, "Registration, case queue", "HTTPS")
Rel(therapist, mpps, "Not in Sprint 1-2 scope", "—")
Rel(ortho_surgeon, mpps, "Not in Sprint 1-2 scope", "—")
Rel(patient_caregiver, mpps, "Not in Sprint 1-2 scope", "—")
Rel(mpps, pacs, "DICOM import", "C-MOVE + upload")
Rel(mpps, emr, "Finalized reports, audit", "HL7/FHIR")
Rel(mpps, triton, "ML inference", "gRPC :8001")
Rel(mpps, knowledge, "RAG + ontology + LLM", "gRPC/HTTP + C++")
@enduml
```
---
## 4. Container Diagram (C4) - Sprint 1-2 Implementation
```plantuml
@startuml "VKIST_MSK_Containers_Sprint1_2"
!include <C4/C4_Container>
title Container Diagram - VKIST MSK Platform (Sprint 1-2)
Person(radiologist, "Radiologist (UP5)", "Performs primary diagnosis, view validation, and severity grading.")
Person(therapist, "Physical Therapist (UP6)", "Observes scans, evaluates kinetic data, and structures physical rehabilitation plans.")
System_Boundary(hospital_lan, "Air-Gapped Hospital LAN (Max 10 Mbps)") {
Container(pwa, "React PWA Frontend", "React, TS, Zustand, LiteRT, MediaPipe, Dexie.js", "Interactive visualization, local WebAssembly inference for view-angle validation, encrypted IndexedDB cache, DICOM upload, edge guardrail (Transformers.js BERT + OpenRedaction + pii-filter) in dedicated WebWorkers")
Container(nginx, "NGINX Reverse Proxy + Keepalived", "NGINX 1.27, Keepalived", "SSL/TLS termination, single VIP, instant failover (<=2s), routes to active FastAPI node")
System_Boundary(backend_servers, "Application Server Cluster") {
Container(fastapi, "FastAPI Application Server", "Python, Uvicorn, SQLAlchemy, ReportLab", "API orchestration, angle classification routing, inflammation detection, segmentation post-processing, measurement, severity analysis, PDF report generation, client-side scrubbing validation")
System_Boundary(ml_inference, "ML Inference Stack (Sprint 1-2)") {
Container(triton, "Triton Inference Server", "NVIDIA Triton, ONNX, OpenVINO, TensorRT", "Executes 3-step ML pipeline: angle classification, inflammation detection, segmentation (UNet3+/DeepLabV3)")
ContainerDb(model_store, "Local Model Store", "ONNX/Torch weights on disk", "Stores model checkpoints mounted read-only into Triton")
}
System_Boundary(observability, "Observability (Sprint 1-2)") {
Container(prometheus, "Prometheus", "Prometheus", "Scrapes FastAPI and Triton metrics")
Container(grafana, "Grafana", "Grafana", "Operational dashboards for inference latency, GPU utilization, cache hit rate")
}
System_Boundary(data_stack, "Data Stack (Sprint 1-2)") {
ContainerDb(postgres, "PostgreSQL + PostGIS", "PostgreSQL", "Stores session metadata, EMR records, spatial markers, audit ledger")
ContainerDb(redis, "Redis Cache", "Redis", "Session state, JWT tokens, rate limiting")
ContainerDb(s3, "S3 Object Store (MinIO)", "MinIO", "DICOM-derived images, segmentation overlays, Grad-CAM heatmaps, report blobs")
}
}
}
Rel(radiologist, pwa, "Uploads DICOM, views overlays, accepts/rejects grades")
Rel(therapist, pwa, "Reviews scan results, annotates feedback")
Rel(pwa, nginx, "HTTPS requests with encrypted DICOM payloads", "HTTPS (Port 443)")
Rel(nginx, fastapi, "Routes API requests", "HTTP/1.1 (Port 8000)")
Rel(fastapi, redis, "Session verification, rate limiting", "TCP (Port 6379)")
Rel(fastapi, postgres, "Metadata, audit ledger, spatial queries", "SQL/TCP (Port 5432)")
Rel(fastapi, s3, "Image and report blob storage", "S3 API")
Rel(fastapi, triton, "Offloads ML inference: angle, inflammation, segmentation", "gRPC (Port 8001)")
Rel(triton, model_store, "Reads model weights", "Local FS")
Rel(prometheus, fastapi, "Request latency, error rate, DB pool", "HTTP /metrics")
Rel(prometheus, triton, "Model latency, VRAM, GPU utilization", "HTTP /metrics")
Rel(grafana, prometheus, "Dashboard queries", "HTTP (Port 9090)")
@enduml
```
---
## 5. Component Diagram (C4) - FastAPI Server Internals (Sprint 1-2)
```plantuml
@startuml "VKIST_MSK_Backend_Components_Sprint1_2"
!include <C4/C4_Component>
title Component Diagram - FastAPI Application Server (Sprint 1-2)
Container_Boundary(backend, "FastAPI Application Server") {
System_Boundary(api_routers, "API Routers") {
Component(analyze_router, "Analysis Router", "FastAPI Router", "/api/analyze - Main ML pipeline endpoint\nHandles angle model selection, inflammation detection, segmentation routing, measurement, severity analysis")
Component(save_router, "Save Router", "FastAPI Router", "/api/save - Patient data persistence\nSanitizes metadata, stores images and PDF reports")
Component(export_router, "PDF Export Router", "FastAPI Router", "/api/export-pdf - On-demand PDF generation")
Component(health_router, "Health Router", "FastAPI Router", "/api/health - Liveness probe")
}
System_Boundary(ml_pipeline, "ML Pipeline Components (Sprint 1-2)") {
Component(angle_classifier, "Angle Classifier", "PyTorch/ONNX", "Classifies ultrasound plane (med-lat, post-trans, sup-trans-flex, sup-up-long)\nModels: ConvNeXt-Tiny, DenseNet-121, ResNet-50, EfficientNet-B2, Swin-V2-S")
Component(inflammation_detector, "Inflammation Detector", "PyTorch/ONNX", "Binary classifier for inflammation presence\nModel: EfficientNet-B0 (2-class)")
Component(segmentation_engine, "Segmentation Engine", "PyTorch/ONNX", "Pixel-wise anatomical segmentation\nModels: DeepLabV3-ResNet50/101, UNet-ResNet101, EfficientFeedback, UNet3Plus-Attention")
Component(measurement_engine, "Measurement Engine", "Python/NumPy/OpenCV", "Calculates synovium thickness in mm, effusion metrics\nPIXEL_TO_MM = 45.0 / 655.0")
Component(severity_analyzer, "Severity Analyzer", "Python", "Classifies synovitis grade (0-3) with combined effusion+synovium scoring\nGenerates Vietnamese clinical descriptions")
Component(preprocessor, "Image Preprocessor", "Python/PIL/OpenCV", "CLAHE contrast enhancement, resize, normalize")
Component(overlay_renderer, "Overlay Renderer", "Python/PIL", "Creates color-coded segmentation overlays with measurement annotations")
}
System_Boundary(knowledge_stack, "Knowledge Stack (Sprint 2)") {
Component(nlp_scrubber, "Patient Data Scrubber", "Microsoft Presidio", "Re-verify edge redaction; refine residual PII; error if unresolvable")
Component(rag_coordinator, "RAG Coordinator", "Python", "Retrieves MOH guideline passages from pgvector (PostgreSQL HNSW index); mandatory pre-generation retrieval for all LLM tiers (browser WebLLM and cloud MedGemma)")
Component(ontology_query, "Ontology Query Engine", "C++ bindings", "ladybugDB graph traversal for anatomical entity relationships")
Component(guardrail_edge, "Edge Guardrail", "Transformers.js BERT", "Client-side hallucination/mal-intention/scope-breach scoring; triggers session termination + cloud mitigate via FastAPI guardrail-check endpoint")
Component(referee, "RAG-Referee", "BERT classifier", "Server-side 3-axis citation contestant validation (attribution, cohesion, factual contestant status)")
}
System_Boundary(reporting, "Reporting & Audit") {
Component(pdf_generator, "PDF Report Generator", "ReportLab", "Bilingual (VI/EN) medical reports per Circular 46/2018/TT-BYT")
Component(audit_logger, "Immutable Audit Logger", "SQLAlchemy/Postgres", "Append-only clinical decision and AI interaction logs\nPrevents UPDATE/DELETE via DB triggers")
}
System_Boundary(infrastructure, "Infrastructure") {
Component(cache_client, "Redis Client", "redis-py", "Session state, JWT tokens, rate limiting")
Component(storage_client, "S3 Client", "boto3", "DICOM images, overlays, reports, Grad-CAM heatmaps")
Component(db_client, "PostgreSQL Client", "SQLAlchemy", "Relational/spatial queries, audit ledger")
Component(triton_client, "Triton gRPC Client", "gRPC", "Inference offloading for heavy ML models")
}
}
' Internal relationships
Component(analyze_router, preprocessor, "Uploads image, applies CLAHE")
Component(analyze_router, angle_classifier, "Classifies angle plane")
Component(analyze_router, inflammation_detector, "Detects inflammation (if post-trans or sup-up-long)")
Component(analyze_router, segmentation_engine, "Runs segmentation (if inflammation detected)")
Component(analyze_router, measurement_engine, "Measures thickness (sup-up-long only)")
Component(analyze_router, severity_analyzer, "Grades synovitis severity")
Component(analyze_router, overlay_renderer, "Generates visualization overlay")
Component(save_router, nlp_scrubber, "Validates scrubbing before save")
Component(save_router, pdf_generator, "Triggers PDF generation")
Component(save_router, audit_logger, "Logs save action immutably")
Component(export_router, pdf_generator, "Generates on-demand PDF")
Component(rag_coordinator, triton_client, "RAG embedding extraction via Triton (EmbeddingGemma)")
Component(rag_coordinator, ontology_query, "Graph traversal for evidence")
Component(rag_coordinator, referee, "Mandatory RAG-Referee validation per axis")
Component(guardrail_edge, "Edge Guardrail", "Transformers.js BERT", "Scores hallucination/mal-intention/scope-breach in WebWorker")
Component(guardrail_edge, referee, "Violation signal → server-side referee gate")
' Infrastructure connections
Component(analyze_router, cache_client, "Session validation")
Component(analyze_router, storage_client, "Image/report I/O")
Component(analyze_router, db_client, "Metadata, audit")
Component(analyze_router, triton_client, "ML inference offload")
@enduml
```
---
## 6. Deployment Diagram (C4) - Sprint 1-2 Hospital LAN Runtime
```plantuml
@startuml "VKIST_MSK_Deployment_Sprint1_2"
!include <C4/C4_Deployment>
title Deployment Diagram - VKIST MSK Platform (Sprint 1-2)
Deployment_Node(hw, "Hospital On-Premise Hardware\n(Dell PowerEdge / Local Server)") {
Deployment_Node(k8s_alt, "K3s Runtime (Sprint 1-2)") {
Deployment_Node(nginx_node, "NGINX Container") {
Container(nginx_c, "NGINX 1.27 + Keepalived", "Reverse proxy, SSL, VIP failover")
}
Deployment_Node(fastapi_node, "FastAPI Container") {
Container(fastapi_c, "FastAPI Uvicorn", "Python app: analysis, save, export, NLP scrub, RAG")
}
Deployment_Node(prom_node, "Prometheus Container") {
Container(prom_c, "Prometheus", "Metrics collection")
}
Deployment_Node(grafana_node, "Grafana Container") {
Container(graf_c, "Grafana", "Dashboards")
}
}
Deployment_Node(vm_db, "Database / Storage VM") {
Deployment_Node(pg_node, "PostgreSQL + pgvector Node") {
ContainerDb(pg_c, "PostgreSQL + PostGIS + pgvector", "Primary DB, vector HNSW index, audit ledger")
}
Deployment_Node(redis_node, "Redis Node") {
ContainerDb(redis_c, "Redis", "Session cache, rate limit")
}
Deployment_Node(s3_node, "MinIO Node") {
ContainerDb(s3_c, "MinIO S3", "Object storage for images and reports")
}
}
Deployment_Node(triton_hw, "Inference Server (GPU Node)") {
Deployment_Node(triton_node, "Triton Container") {
Container(triton_c, "Triton Inference Server", "ONNX/TensorRT models, ensemble pipelines")
}
Deployment_Node(model_vol, "Model Volume") {
ContainerDb(model_c, "Model Weights", "Read-only mount: .pth / .onnx files")
}
}
}
Deployment_Node(client, "Clinician Workstation / Tablet") {
ContainerDb(browser, "Browser PWA", "React, Service Worker, IndexedDB")
}
Rel(browser, nginx_c, "HTTPS (port 443)", "TLS 1.3")
Rel(nginx_c, fastapi_c, "HTTP (port 8000)", "Internal LAN")
Rel(fastapi_c, pg_c, "SQL/TCP (port 5432)", "LAN")
Rel(fastapi_c, s3_c, "S3 API", "LAN")
Rel(fastapi_c, triton_c, "gRPC (port 8001)", "LAN (or same host)")
Rel(prom_c, fastapi_c, "/metrics scrape", "HTTP")
Rel(prom_c, triton_c, "/metrics scrape", "HTTP")
Rel(graf_c, prom_c, "Dashboard", "HTTP")
Rel(triton_c, model_vol, "Loads weights", "Read-only FS")
Rel(browser, browser, "Local-only WebAssembly inference (LiteRT/MediaPipe)", "No network")
@enduml
```
---
## 7. Sprint 1-2 ML Workflow: Ultrasound Processing Pipeline
```plantuml
@startuml "VKIST_MSK_ML_Pipeline_Sprint1_2"
@startuml
title ML Pipeline - Knee Ultrasound Analysis (Sprint 1-2)
start
:Clinician uploads DICOM via PWA;
:PWA encrypts payload, sends over HTTPS;
:NGINX routes to active FastAPI node;
:FastAPI receives image;
:Apply CLAHE preprocessing (contrast enhancement);
:RUN ANGLE CLASSIFICATION;
:Model: ConvNeXt/DenseNet/ResNet/EfficientNet/Swin-V2;
:Classes: med-lat, post-trans, sup-trans-flex, sup-up-long;
if (Angle class?) then (post-trans)
: Inflammation Detection;
:Model: EfficientNet-B0 (binary);
if (Inflammation?) then (yes)
:Post-Trans Segmentation;
:Model: DeepLabV3-ResNet101;
:Classes: fat, tendon, muscle, femur, artery, synovium, baker's cyst;
:Generate segmentation masks;
:Create overlay image;
else (no)
:Skip segmentation;
:Set severity = 0;
endif
elseif (sup-up-long)
: Inflammation Detection;
if (Inflammation?) then (yes)
:Suprapatellar Segmentation;
:Model: UNet3Plus-Attention / DeepLabV3 / EfficientFeedback;
:Classes: effusion, fat, fat-pat, femur, synovium, tendon;
:Generate segmentation masks;
:Measure Synovium Thickness;
:ROI: middle 1/3 of bounding box;
:Unit: mm (PIXEL_TO_MM = 45/655);
:Create overlay with measurement annotations;
:Analyze Severity;
:Combined score: effusion (60%) + synovium (40%);
:Grade: 0 (Rất nhẹ) / 1 (Nhẹ) / 2 (Trung bình) / 3 (Nặng);
else (no)
:Skip segmentation and measurement;
:Set severity = 0;
endif
else (other angles)
:Only angle classification result returned;
endif
:Generate enhanced image (CLAHE);
:Assemble JSON response with overlays, masks, measurements, severity;
if (Save requested?) then (yes)
:NLP Scrubber validates Decree 13 compliance;
:Store images to MinIO S3;
:Store metadata to PostgreSQL;
:Append immutable audit log;
:Generate bilingual PDF report;
endif
:Return response to PWA;
stop
@enduml
```
---
## 8. Data Flow Diagram (Activity View)
```plantuml
@startuml "VKIST_MSK_DataFlow_Sprint1_2"
title Data Flow - VKIST MSK Platform (Sprint 1-2)
|PWA Client|
start
:Upload DICOM image;
:Apply CLAHE preprocessing locally;
:Encrypt payload;
:Send HTTPS POST to /api/analyze;
|NGINX Gateway|
:Receive request;
:SSL termination;
:Route to active FastAPI node;
|FastAPI Server|
:Receive image;
:Validate JWT + RBAC;
:Load angle classifier model;
:Run angle classification;
if (Angle class?) then (post-trans)
:Load inflammation detector;
:Run inflammation detection;
if (Detected?) then (yes)
:Load post-trans segmentation model;
:Run DeepLabV3-ResNet101 inference via Triton;
:Receive masks from Triton;
:Create segmentation overlay;
:Return JSON with overlay + masks;
else (no)
:Return JSON with angle + inflammation only;
endif
elseif (sup-up-long)
:Load inflammation detector;
:Run inflammation detection;
if (Detected?) then (yes)
:Load suprapatellar segmentation model;
:Run UNet3Plus-Attention inference via Triton;
:Receive masks from Triton;
:Measure synovium thickness (ROI middle 1/3);
:Calculate effusion metrics;
:Analyze severity (0-3 combined score);
:Create overlay with measurement annotations;
:Return JSON with overlay + measurement + severity;
else (no)
:Return JSON with angle + inflammation only;
endif
else (other)
:Return angle classification only;
endif
|PWA Client|
:Receive JSON response;
:Display enhanced image + overlays;
:Render measurement annotations;
:Show severity grade with color coding;
if (User action: Save?) then (yes)
|PWA Client|
:Run NLP scrubber (Decree 13);
:Remove PII from metadata;
:Send HTTPS POST to /api/save;
|FastAPI Server|
:Validate scrubbing compliance;
:Store images to MinIO S3;
:Store metadata to PostgreSQL;
:Append immutable audit log;
:Generate bilingual PDF report (Circular 46);
:Return success + folder path;
|PWA Client|
:Confirm save to user;
endif
stop
@enduml
```
---
## 9. Non-Functional Requirements Coverage (Sprint 1-2)
| NFR | Sprint 1-2 Implementation |
| --- | --- |
| **NFR-1** (DICOM Speed ≤ 3.0s) | Local Triton inference + Redis caching of recent sessions |
| **NFR-4** (Client Memory ≤ 150 MB) | React PWA with Zustand; LiteRT WebAssembly (no GPU); Dexie.js for local cache |
| **NFR-5** (Inference ≤ 1.5s) | Triton with TensorRT/OpenVINO quantization; ONNX runtime |
| **NFR-6** (VRAM ≤ 2 GB) | Quantized ONNX models; batch size 1; DeepLabV3-ResNet101 optimized |
| **NFR-7** (UI Refresh ≤ 200ms) | Token streaming via SSE; frontend state updates async |
| **NFR-8** (Fault Tolerance) | IndexedDB local cache; service worker offline mode; session recovery |
| **NFR-9** (Availability ≥ 99.9%) | NGINX + Keepalived active-passive VIP; Docker restart policies |
| **NFR-10** (Generative Safety) | Edge guardrail: prompt rules + BERT detection (hallucination/mal-intention/scope-breach) → session termination → cloud mitigate (Vertex AI). Mandatory RAG pre-processing for all LLM tiers (not optional tool calling). RAG-Referee citation contestant validation (3-axis). Server-side Decree 13 redaction ground-check before Vertex AI egress. |
| **NFR-11** (Onboarding ≤ 45 min) | High-fidelity Figma prototypes in Sprint 1; structured workflows |
| **NFR-13** (Grad-CAM Zero-Click) | Overlay rendered directly on upload; zero extra UI steps |
| **NFR-14** (No client GPU) | PWA falls back to CPU-bound rendering; LiteRT for lightweight inference |
| **NFR-16** (Air-Gapped) | Entire inference and storage stack on-premise via K3s; no external cloud for clinical data. NFR-16a exception: GitLab/Jira on cloud VM with compensating controls. |
| **NFR-17** (Immutable Audit) | PostgreSQL triggers prevent UPDATE/DELETE on audit tables |
| **NFR-18** (RAG Citations) | pgvector retrieves MOH guideline passages (PostgreSQL HNSW index); LLM generates footnoted explanations |
| **NFR-19** (HITL Gate) | Database state machine prevents FINALIZED status without clinician digital signature |
---
## 10. Key Decree 13 / Circular 46 Compliance (Sprint 2)
### 10.1 Decree 13/2023/ND-CP - Personal Data Protection
- Client-side scrubbing via `nlp_scrubber` component before any network transfer
- Regex-based PII removal (names, IDs, phone numbers) in browser
- No PHI stored in Git, Jenkins artifacts, or external systems
### 10.2 Circular 46/2018/TT-BYT - EMR Compliance
- PDF reports generated per official MOH format (bilingual VI/EN)
- Audit trail immutable via database triggers
- Reports stored with cryptographic checksums in MinIO
---
## 11. Infrastructure Decisions (Sprint 1-2)
| Decision | Choice | Rationale |
| --- | --- | --- |
| **Orchestration** | K3s (Kubernetes-certified, lightweight edge distribution) | Chosen over Docker Compose, Docker Swarm, Nomad, ECS Fargate, Cloud Run. NFR-16 requires on-premise, eliminating cloud-only options. Docker Swarm offers lowest PoC cost but is in maintenance mode with highest migration overhead. K3s is already production-grade; scaling path is multi-cluster federation, not platform replacement. |
| **CI/CD** | Jenkins on hospital K3s | Runs inside trusted LAN; connects to cloud-hosted GitLab for source. No external build triggers. |
| **Code Hosting** | Self-hosted GitLab CE on cloud VM (NFR-16a exception) | Reliability over hospital-hardware self-hosting; not SaaS. Compensating controls: no PHI in commits (pre-push hook), SSH-only access with cert pinning, daily RDB backup to hospital MinIO, cloud IAM with 2FA + minimum-role, IP whitelist to hospital networks. Exception reviewed at PoC sign-off. |
| **Issue Tracking** | Self-hosted Jira on same cloud VM (NFR-16a exception) | Clinical feedback and tickets stay within controlled access boundary. No PHI in tickets per team policy. Same compensating controls as GitLab. |
| **Model Serving** | Triton Inference Server | ONNX/TensorRT support; ensemble pipelines; HTTP/gRPC |
| **Reverse Proxy** | NGINX + Keepalived | VIP for high availability; SSL termination; instant failover |
| **Local LLM** | Browser WebLLM (GemmaE2B) + Cloud MedGemma (Vertex AI) | Browser: Vietnamese + clinical language support, local inference (air-gapped). Cloud: MedGemma for NFR-16a fallback + BERT-triggered arbiter. Triton hosts CV models + EmbeddingGemma only. |
| **Vector Search** | pgvector (PostgreSQL HNSW index) | Already deployed with Postgres; zero additional infrastructure; ~15K MOH vectors at ~5-20ms query latency fits NFR-7. Complex SQL filtering for clinical queries. Qdrant deferred to Phase 2. |
| **Ontology DB** | ladybugDB embedded | C++ library embedded in FastAPI process; SNOMED-CT/LOINC mappings |
---
## 12. Technology Stack Summary
| Layer | Technology | Purpose |
| --- | --- | --- |
| Frontend | React, TypeScript, Zustand, Dexie.js, LiteRT, MediaPipe, Transformers.js, OpenRedaction, pii-filter, js-data-anonymizer | PWA with offline support, local inference, and edge guardrail (BERT hallucination/mal-intention detection, Decree 13 PII scrubbing) |
| Guardrail | Transformers.js BERT (WebWorker), OpenRedaction, pii-filter, js-data-anonymizer, FastAPI `phi_scrub` middleware, Vertex AI Model Garden safety filters | Edge behavior control without NeMo/GuardrailsAI; prompt-rule + BERT detection; session termination + cloud mitigate; mandatory RAG pre-processing (not optional tool-call); server-side redaction ground-check before NFR-16a egress |
| Gateway | NGINX 1.27 + Keepalived | SSL termination, VIP, load balancing |
| API | FastAPI, Uvicorn, SQLAlchemy, ReportLab | REST API, ORM, PDF generation |
| ML Inference | Triton, ONNX, TensorRT, OpenVINO | Model serving with quantization |
| Models | ConvNeXt, DenseNet, ResNet, EfficientNet, Swin-V2, UNet3Plus, DeepLabV3 | Angle, inflammation, segmentation |
| Data | PostgreSQL + PostGIS, MinIO, Redis | Relational, object, cache |
| Knowledge | pgvector, ladybugDB, GemmaE2B/MedGemma, EmbeddingGemma, BioClinicalBERT | RAG (pgvector HNSW), ontology (ladybugDB), Vietnamese/clinical LLM, 768-dim RAG embeddings, BERT drift/referee |
| Observability | Prometheus, Grafana | Metrics and dashboards |
| Code Hosting | Self-hosted GitLab CE (cloud VM, NFR-16a exception) | Source control, issue tracking, merge requests, container registry |
| CI/CD | Jenkins on hospital K3s | Build, test, deploy pipeline; connects to cloud-hosted GitLab |
---
## 13. Cross-References
| Document | Relevance |
| --- | --- |
| [SOLUTION_ARCHITECTURE_SPEC.md](../SOLUTION_ARCHITECTURE_SPEC.md) | Main architecture spec, NFR definitions, pattern citations, trade-off analysis |
| [Backend Specification](CODEBASE/backend/spec/backend-spec.md) | FastAPI server internal design, API contracts, RAG coordinator |
| [Knowledge Stack Specification](CODEBASE/knowledge/spec/knowledge_spec.md) | Qdrant/ladybugDB schema, embedding models, LLM endpoints |
| [CI/CD Deployment Pipeline](Design_Material/CI_CD_docs/CI_CD_DEPLOYMENT_PIPELINE.md) | Jenkins pipeline, Docker Compose runtime, offline bundles, rollback |
| [DATA_SPEC.md](CODEBASE/data/spec/data_spec.md) | Data contracts, schema definitions |
| [CONTEXT_VISION_SCOPE.md](../../PROJ_LEVEL_READING/PLAN/CONTEXT_VISION_SCOPE.md) | Project vision, sprint timelines, user personas |
---
## 14. Design Decisions
| # | Decision | Alternatives Considered | Rationale |
| --- | --- | --- | --- |
| 1 | Triton for model serving | TorchServe, FastAPI direct | Triton supports ONNX/TensorRT ensembles, dynamic batching, and concurrent model execution |
| 2 | K3s over Docker Compose/Swarm/Nomad/ECS/Cloud Run | Docker Compose, Docker Swarm, Nomad, ECS Fargate, Cloud Run | NFR-16 requires on-premise, eliminating cloud-only platforms (ECS, Cloud Run). Docker Swarm is in maintenance mode with highest migration cost to production. K3s is already production-grade; scaling to N hospitals is multi-cluster federation, not platform replacement. |
| 3 | MinIO over AWS S3 | Direct filesystem, NFS | S3-compatible API allows future cloud migration; object versioning built-in |
| 4 | pgvector over Qdrant/Pinecone | Qdrant, Pinecone, Weaviate | Postgres already deployed; pgvector adds zero infrastructure overhead. At ~15K MOH guideline vectors, HNSW query latency (~5-20ms in shared_buffers) fits within NFR-7 budget. Qdrant advantage appears at millions of vectors or >500 QPS; not needed at PoC scale. Phase 2: introduce Qdrant if corpus exceeds ~100K vectors. |
| 5 | ladybugDB embedded | Separate graph DB service | Embedded C++ library reduces latency; no separate process to manage |
---
## 15. Open Questions / Future Work (Post Sprint 2)
| ID | Question | Target Sprint |
| --- | --- | --- |
| Q1 | How to handle concurrent clinician sessions with shared Triton GPU? | Sprint 3 |
| Q2 | Should model weights be versioned in PostgreSQL for A/B testing? | Sprint 4 |
| Q3 | How to integrate DICOM C-STORE for automatic PACS ingestion (currently C-MOVE only)? | Sprint 5 |
| Q4 | Will GemmaE2B be swapped for MedGemma or Willa when hospital provides GPU cluster? | Sprint 5 |
| Q5 | Should Prometheus scrape endpoints be authenticated for NFR-17 audit compliance? | Sprint 3 |