№ 83
Công cụ AI rà soát đơn thuốc và đề xuất liệu trình dựa trên tài liệu bệnh viện
№ 83
Demo Day Zine
Trợ lý lâm sàng rà soát đơn thuốc & đề xuất kháng sinh, grounded trên knowledge graph thật. Nhận đơn thuốc → tra tương tác thuốc–thuốc (có mức độ + xử trí) → ground trên KG thuốc (DrugBank) → tóm tắt & hỏi đáp bằng Gemini 2.5 Flash (streaming, không mock).
| Live demo | https://c2-app-099.up.railway.app |
| Tech stack | FastAPI · Jinja2 · HTMX · Alpine.js · Tailwind · PostgreSQL · Redis · Neo4j Aura · DDInter SQLite · Gemini 2.5 Flash |
| Quy mô code | ~6.6k LOC (app/), 42 file test / 251 test function, 283 commit |
| Trạng thái | MVP đã deploy production; nhiều tính năng Phase 2 đã hoàn tất |
Bệnh nhân cao tuổi hoặc đa bệnh lý (polypharmacy) thường dùng nhiều thuốc đồng thời, dẫn tới nguy cơ cao:
Rà soát thủ công tốn thời gian và dễ bỏ sót. Ở chiều ngược lại, hệ thống cảnh báo tự động quá mức sẽ gây "alert fatigue" — nhân viên y tế quen với cảnh báo nhiễu rồi bỏ qua cả cảnh báo quan trọng.
Ngoài ra, việc chọn kháng sinh kinh nghiệm (empiric) đúng ổ nhiễm, đúng nhóm nguy cơ bệnh nhân, đúng liều có hiệu chỉnh theo chức năng thận là một quyết định phức tạp, cần tra cứu nhiều tài liệu (Hướng dẫn sử dụng kháng sinh — Bộ Y tế 2015).
Xây công cụ hỗ trợ quyết định lâm sàng (clinical decision support), đối chiếu đơn thuốc với cơ sở tri thức để cảnh báo có giải thích bằng chứng và đề xuất phác đồ có dẫn nguồn, giúp dược sĩ/bác sĩ quyết định nhanh và an toàn hơn — phán đoán lâm sàng.
| Người dùng | Nhu cầu chính |
|---|---|
| Dược sĩ lâm sàng (chính) | Rà soát nhanh 30–60 đơn/ngày, ưu tiên cảnh báo nặng, cần bằng chứng |
| Bác sĩ kê đơn | Kiểm tra trước khi kê cho bệnh nhân đa bệnh lý; đề xuất kháng sinh |
| (Tương lai) HIS/EMR | Tích hợp rà soát tự động vào quy trình kê đơn |
Ba nguyên tắc xuyên suốt:
PharmaCheck là một monolith FastAPI server-rendered (HTMX + Alpine, không SPA), dùng nhiều backing store chuyên biệt theo loại truy vấn, với một lớp LLM grounded trên cùng.
┌─ Trình duyệt (HTMX + Alpine + Tailwind) ─────────────────────────┐
│ Soạn đơn · Tra cứu · Kháng sinh · Trợ lý AI · Dashboard · Account│
└──────────────────────────┬───────────────────────────────────────┘
│ HTTP / HTMX partials / SSE streaming
┌──────────────────────────▼───────────────────────────────────────┐
│ FastAPI (gunicorn/uvicorn) — app.main + routers │
│ │
│ ┌─ PostgreSQL ──┐ ┌─ Redis ──┐ ┌─ DDInter SQLite ─────────┐ │
│ │ tenant, user, │ │ cache │ │ tương tác thuốc–thuốc │ │
│ │ patient (PII │ │ (fail- │ │ (mức độ + xử trí), │ │
│ │ mã hoá), đơn, │ │ open) │ │ food, disease, dup │ │
│ │ audit, billing│ └──────────┘ │ (bake trong image, RO) │ │
│ └───────────────┘ └──────────────────────────┘ │
└──────────┬──────────────────────────────┬────────────────────────┘
│ │
┌──────────▼─────────┐ ┌──────────▼──────────────┐
│ Neo4j Aura │ │ Google Gemini 2.5 Flash │
│ KG ontology thuốc │ │ (grounded, SSE stream) │
│ (DrugBank + KG │ │ — chỉ diễn giải, không │
│ kháng sinh YAML) │ │ thêm dữ kiện ngoài KG │
└────────────────────┘ └─────────────────────────┘
| Store | Dữ liệu | Lý do chọn |
|---|---|---|
| Neo4j Aura | Ontology thuốc (ATC, target, enzyme, transporter, brand, synonym) + KG kháng sinh (ổ nhiễm → tác nhân → phác đồ) | Truy vấn quan hệ/đa nhãn & traversal; tri thức dùng chung, read-only |
| DDInter SQLite | Tương tác Thuốc–Thuốc có mức độ + xử trí, food/disease/duplication | Tra cặp có index (quan hệ, không traversal); file portable, không cần server |
| PostgreSQL | PII & vận hành: user, đơn, bệnh nhân, audit, billing | Giao dịch ACID, đa tenant, dữ liệu lớn (triệu bản ghi) |
| Redis | Cache search / interaction / AI | Tăng tốc, fail-open (sập vẫn chạy) |
| Gemini | LLM tóm tắt / hỏi đáp / OCR | Grounded — chỉ diễn giải dữ liệu được đưa vào |
Ghi chú deploy: cạnh
INTERACTS_WITH(~657k) không nạp lên Aura (vượt free tier và dư thừa — nguồn tương tác thật có mức độ là DDInter). KG vẫn đủ cho search + chi tiết
- AI grounding.
warfarin → typeahead truy vấn Neo4j (qua cache Redis) → chọn từ dropdown.aspirin → bấm Kiểm tương tác → review_service.check() tra DDInter →
sidebar "Rà soát an toàn" hiện mức Major (chảy máu) + xử trí.Điểm khác biệt thiết kế quan trọng nhất: mọi nội dung lâm sàng (thuốc, liều, phác đồ, mức tương tác) đến tất định từ truy vấn dữ liệu (Cypher/SQL), KHÔNG từ LLM. LLM chỉ nhận context đã được code lắp ráp và diễn giải thành văn bản dễ đọc.
coverage_gaps ("chưa có
trong dữ liệu") thay vì để LLM tự điền.| Phân hệ | Mô tả | Module chính |
|---|---|---|
| Soạn đơn | Pad kê đơn (Alpine), typeahead, kiểm tương tác, tóm tắt AI streaming, lưu/in | main.py (routes /soan-don/*), review_service |
| Tra cứu thuốc | Tìm theo tên/brand/synonym, chi tiết (chỉ định/cơ chế/ATC/enzyme), thay thế, graph quan hệ, AI giải thích | db.py (Neo4j), routes /tra-cuu/* |
| Đề xuất kháng sinh | Chọn ổ nhiễm + yếu tố BN → phác đồ empiric (ưu tiên/thay thế) + liều + cờ hiệu chỉnh thận/gan + cảnh báo dị ứng/tương tác, lọc theo nhóm nguy cơ | abx_service, abx_db, abx_rules, routers/abx.py |
| Trợ lý AI | Chat lâm sàng grounded theo ngữ cảnh (đơn/thuốc/bệnh nhân), lưu thread, SSE per-message | routers/assistant.py, chat_service, graphrag.py |
| OCR đơn thuốc | Upload ảnh → Gemini bóc tách tên thuốc/liều → khớp DB → điền vào pad | OCR/ocr_service.py, route /soan-don/ocr |
| Dashboard | Thống kê tương tác theo khoa/thời gian, log quyết định, quản lý user (admin) | main.py (routes /dashboard/*) |
| Auth | Đăng nhập Zitadel Cloud OIDC (Authorization Code + PKCE), đăng nhập Google qua scope deep-link, JIT provisioning, đa tenant theo Org | routers/auth.py, core/oidc.py, services/auth_service.py |
| Billing | Metering token AI theo gói (free/individual/business), thanh toán PayOS qua webhook | billing_service, payos_service, routers/billing.py |
core/crypto.py, encrypted SQLAlchemy types) + blind index để tra cứu không lộ
plaintext.tenant_id; test
test_tenant_isolation.py chặn rò rỉ chéo.SecurityHeadersMiddleware dựng Content-Security-Policy
(whitelist Zitadel/PayOS/PostHog cho form-action, connect-src), HSTS ở production.core/ratelimit.py).SECRET_KEY thật, thiếu
cấu hình Zitadel, hoặc redirect URI còn http://.upgrade head chạy ở entrypoint container khi boot.docs/superpowers/.| Khía cạnh | Bằng chứng |
|---|---|
| Vận hành end-to-end | Đã deploy production trên Railway (app + Postgres + Redis), KG trên Neo4j Aura, demo công khai chạy được toàn luồng |
| Dữ liệu thật, quy mô lớn | KG ~112k node / ~840k quan hệ; DDInter 8.466 cơ chế / 302k cặp / 8.359 tương tác thuốc–bệnh |
| LLM grounded thật | Gemini 2.5 Flash streaming SSE, không mock; kiến trúc cấm bịa đã giảm rủi ro hallucination ở mức thiết kế |
| Chi phí hạ tầng thấp | Chạy được trên free/low tier (Aura Free, Railway), DDInter là file SQLite bake trong image — không cần server riêng |
| Sẵn sàng thương mại hoá | Có sẵn auth đa tenant, metering token, cổng thanh toán PayOS, gói cước |
| Rủi ro | Hiện trạng / Giảm thiểu |
|---|---|
| Không phải thiết bị y tế chứng nhận | Tuyên bố rõ vai trò "hỗ trợ, không thay thế"; human-in-the-loop; disclaimer mọi đề xuất |
| Dữ liệu thuốc thiếu (stub) | Một số thuốc DrugBank chỉ có tên, không hồ sơ đầy đủ → đánh dấu rõ, không suy luận quá mức |
| Liều kháng sinh phần lớn trống trong KG | Hiện liều đúng như KG; chỗ trống ghi coverage_gaps; CrCl + cờ hiệu chỉnh định tính (không bịa con số) |
| Phủ nhóm nguy cơ chưa đủ | Mới curate for_risk_group cho 3/6 ổ nhiễm có bảng trong tài liệu; 3 ổ còn lại hiển thị dạng "chung" |
| Bản quyền dữ liệu | DDInter 2.0 là CC BY-NC-SA 4.0 (phi thương mại) — cần thay/đàm phán nguồn nếu thương mại hoá; DrugBank academic |
| OCR còn sơ khai | Dựa Gemini vision, độ chính xác phụ thuộc chất lượng ảnh; cần human xác nhận trước khi điền |
| Chưa tích hợp HIS/EMR | Hiện là ứng dụng độc lập; tích hợp viện là việc lớn ngoài phạm vi hiện tại |
| PayOS không auto-charge định kỳ | "Định kỳ" = người dùng bấm gia hạn thủ công mỗi kỳ |
Khả thi cao về mặt kỹ thuật và sản phẩm MVP. Hệ đã chạy thật end-to-end với dữ liệu thật và LLM thật, có nền tảng production (auth, bảo mật, billing, CI/CD, test). Rào cản chính cho thương mại hoá thực tế không phải kỹ thuật mà là: (1) bản quyền dữ liệu tương tác (DDInter phi thương mại), (2) hoàn thiện độ phủ liều/nhóm nguy cơ, và (3) xác thực lâm sàng (validation với bộ ca kiểm thử + chuyên gia) trước khi đưa vào quy trình kê đơn thật.
docs/kg-architecture.md): mỗi cơ chế tương tác là một :Interaction node →
truy vấn được "các cặp cùng cơ chế", gắn references một lần, một nguồn sự thật.for_risk_group cho cả 6 ổ nhiễm + bổ sung bảng ngưỡng liều theo
CrCl để chuyển từ cảnh báo định tính sang liều đã hiệu chỉnh cụ thể.drug_disease, drug_food chưa khai thác hết).therapeutic_duplication).:Interaction "Chuyển hoá" tới :Enzyme (CYP3A4…)
để trả lời "vì sao tương tác" bằng đường đi Drug → Enzyme ← Drug (Kiến trúc ③).| Tài liệu | Nội dung |
|---|---|
| README.md | Hướng dẫn chạy local/deploy, biến môi trường, luồng demo |
| docs/architecture.md | Sơ đồ component + data flow + sequence (Mermaid) |
| docs/kg-architecture.md | Thiết kế hợp nhất KG DrugBank + DDInter |
| docs/brief.md · docs/prd.md | Product brief & yêu cầu sản phẩm |
| docs/billing.md | Mô hình quota token + thanh toán PayOS |
| docs/production.md | Quy trình deploy Railway + cấu hình Zitadel |
| docs/superpowers/specs/ | Design doc chi tiết từng tính năng |
Tài liệu này mô tả tổng quan repo PharmaCheck (C2-App-099) — bài toán, giải pháp kỹ thuật, tính khả thi và hướng phát triển. Cập nhật theo trạng thái code tại thời điểm 2026-06-28.