M11 · Kỹ thuật phần mềm cho mô hình kinh tế y tế

Biến mô hình lao đa kháng của M02 thành một công cụ có cấu trúc rõ, được kiểm thử, quản lý phiên bản, đóng gói và có giao diện tương tác

Vì sao bạn nên quan tâm?

Theo ước tính của WHO, mỗi năm Việt Nam có khoảng 9,400 người mắc lao kháng rifampicin mới (World Health Organization 2026). Giá một đợt BPaLM (phác đồ 6 tháng gồm bedaquiline, pretomanid, linezolid và moxifloxacin) của Global Drug Facility (GDF) đã giảm từ US$588 (12/2022) xuống US$284 (9/2025) (European AIDS Treatment Group 2025b; ITPC-EECA 2025), tức từ khoảng 2.8 xuống 1.3 tháng lương tối thiểu vùng I (quy đổi bằng tỷ giá chung của khóa học) (Chính phủ 2025). Mỗi lần giá thuốc, bảng giá BHYT hay bằng chứng đổi, một mô hình viết vội phải sửa tay ở nhiều chỗ, và chỉ cần sót một chỗ là ra con số sai mà không ai hay. M11 giúp bạn quyết định tổ chức, kiểm thử, ghi phiên bản và chia sẻ mô hình thế nào để mọi con số gửi Chương trình Chống lao Quốc gia hay cơ quan BHYT đều truy được nguồn và chạy lại được.

Tổng quan

Mô hình kinh tế y tế là phần mềm, và phần mềm có lỗi. Module này dạy những thói quen kỹ thuật phần mềm giúp một mô hình đáng tin hơn, dễ kiểm tra, dễ chia sẻ và dễ cập nhật. Ta không xây mô hình mới. Ta lấy một bản sao của mô hình Markov đoàn hệ ở M02, so sánh ba phác đồ cho lao đa kháng/kháng rifampicin (MDR/RR-TB) có tính cả điều trị lại, rồi lần lượt: tái cấu trúc mã thành ba lớp đầu vào–mô hình–đầu ra, viết bộ kiểm thử tự động, quản lý phiên bản bằng Git, đóng gói thành gói R mdrtbvn, và dựng một ứng dụng Shiny nhỏ. Bài cuối giới thiệu mô hình “sống”: mô hình tự chạy lại và tự kiểm tra mỗi khi có bằng chứng mới. Mọi bước đều chạy được trên máy của bạn; M02 không bị sửa.

Mục tiêu học tập

Sau module này, bạn có thể:

  1. Tổ chức mã mô hình thành các hàm nhỏ theo ba lớp đầu vào–mô hình–đầu ra, theo một khung chuẩn viết mã mô hình đã công bố, với một bảng tham số duy nhất chứa mọi thứ mô hình đọc.
  2. Viết kiểm thử tự động bằng testthat cho từng lĩnh vực của bảng kiểm TECH-VER, và dùng kiểm thử đột biến để tìm chỗ thiếu kiểm thử.
  3. Quản lý phiên bản mô hình bằng Git, gắn mỗi kết quả với một phiên bản chạy lại được, và ghi lại môi trường gói bằng renv.
  4. Đóng gói mô hình thành một gói R tối giản vượt qua R CMD check.
  5. Dựng và kiểm thử một ứng dụng Shiny gọi đúng các hàm của mô hình, và mô tả quy trình của một mô hình “sống”.

Kiến thức cần có

  • M00: R cơ bản, hàm, vòng lặp, ggplot2; quy trình làm việc tái lập được.
  • M02: cấu trúc mô hình MDR/RR-TB (trạng thái đường hầm (tunnel state), trạng thái điều trị lại, chiết khấu, lợi ích ròng quy ra tiền (NMB), đường biên hiệu quả) và bộ kiểm tra xác minh ở Bài 5 của M02.
  • M03 (nên có): phân tích độ nhạy, vì M11 nhắc đến kiểm thử cho phần phân tích bất định.
  • Một chút làm quen với dòng lệnh (terminal) giúp Bài 3 dễ hơn, nhưng mọi lệnh Git trong bài đều chạy từ R.

Ví dụ xuyên suốt

Một nhóm phân tích nhận mô hình M02 và cần biến nó thành công cụ dùng lâu dài: đồng nghiệp chạy lại được, người duyệt kiểm tra được, cán bộ chương trình thử được vài giả định, và cập nhật được khi giá thuốc hay bảng giá BHYT đổi. Mô hình theo một người bệnh 45 tuổi điều trị ngoại trú: mỗi tháng nhận thuốc, đi khám, soi đờm, nuôi cấy và xét nghiệm máu. Ba phác đồ được so sánh: phác đồ dài hạn 18 tháng, phác đồ ngắn toàn thuốc uống có bedaquiline 9 tháng, và BPaLM 6 tháng. Kết quả điều trị của hai phác đồ đầu lấy từ số liệu toàn quốc 2021–2022 (Nguyen và c.s. 2026); hiệu quả của BPaLM lấy từ kết quả cuối của thử nghiệm TB-PRACTECAL (Nyang’wa và c.s. 2024). Người thất bại hay bỏ điều trị có thể điều trị lại. Giá thuốc lấy từ GDF, giá theo dõi từ bảng giá BHYT của một bệnh viện hạng đặc biệt, bảng sống của WHO; nguồn từng tham số nằm ở M02 và ở kb/_shared.

Ở phân tích cơ sở, chi phí có chiết khấu cho mỗi người bệnh (giá năm 2025) là 29.32 triệu VNĐ với phác đồ dài hạn, 21.69 triệu với phác đồ ngắn và 15.02 triệu với BPaLM; số năm sống điều chỉnh theo chất lượng (QALY) lần lượt là 12.489, 12.911 và 13.390. So với phác đồ ngắn, BPaLM chiếm ưu thế (ΔC -6.68 triệu VNĐ, ΔQALY 0.479, ΔNMB 66.8 triệu VNĐ ở ngưỡng 125.5 triệu VNĐ/QALY), và BPaLM có NMB cao nhất. Bản tái cấu trúc cho đúng các con số của M02; Bài 1 kiểm tra điều này trên nhiều bộ tham số.

flowchart LR
  S["kb/_shared<br/>ngưỡng, chiết khấu,<br/>giá BHYT, giá GDF,<br/>bảng sống WHO"] --> A["Bảng tham số đầy đủ<br/>assemble_params()"]
  M["M02<br/>params/mdrtb.csv"] --> A
  A --> V["Kiểm tra đầu vào<br/>validate_params()"]
  V --> MO["Mô hình<br/>prepare_inputs()<br/>simulate_regimen()"]
  MO --> O["Đầu ra<br/>summarise_regimen()<br/>run_model()"]
  O --> R["Ba phác đồ: C, QALY, NMB<br/>đường biên, BPaLM so với phác đồ ngắn"]
  T["Kiểm thử testthat<br/>(Bài 2)"] -.-> V
  T -.-> MO
  T -.-> O
  O --> AP["Ứng dụng Shiny<br/>(Bài 5)"]
  G["Git + renv<br/>(Bài 3)"] -.-> A
  K["Gói mdrtbvn<br/>(Bài 4)"] -.-> MO

Giới hạn cần nhớ. M11 dạy kỹ thuật, không thay đổi nội dung khoa học của mô hình. Mọi giới hạn của M02 vẫn còn nguyên: trong 55 tham số của M02 có 9 lựa chọn mô hình hóa hay giá trị minh họa (ví dụ thời gian chờ điều trị lại sau thất bại, mức giảm thỏa dụng khi có biến cố bất lợi); hiệu quả của BPaLM là nguy cơ tương đối (RR) của thử nghiệm áp lên số liệu Việt Nam; giá theo dõi lấy ở bệnh viện hạng đặc biệt nên có thể cao hơn ở bệnh viện tỉnh. Kiểm thử cho bằng chứng rằng mã làm đúng điều ta định làm (xác minh); nó không cho biết điều đó có đúng với thực tế hay không (thẩm định, xem M08).

Bản đồ bài học

Bài Chủ đề Bạn sẽ làm được Code
1 Cấu trúc mã cho mô hình tách ba lớp; gom một bảng tham số đầy đủ; viết hàm thuần; tìm phụ thuộc ẩn; tái cấu trúc có kiểm thử đối chiếu code/l01-code-structure.R
2 Xác minh và kiểm thử kiểm tra đầu vào; testthat; ánh xạ TECH-VER; kiểm thử đột biến code/l02-testing.R, tests/testthat/
3 Git, GitHub và renv commit, diff, tag, nhánh; kiểm tra bản cập nhật trước khi commit; chạy lại một phiên bản cũ; tệp khóa renv.lock code/l03-version-control.R
4 Đóng gói thành gói R DESCRIPTION, NAMESPACE, roxygen2; R CMD check; mang theo bảng tham số; cài và dùng gói code/l04-r-package.R
5 Shiny và mô hình “sống” ứng dụng gọi đúng hàm mô hình; testServer(); quy trình cập nhật có cổng kiểm tra code/l05-shiny-living.R, app/app.R

Mô hình tái cấu trúc nằm trong code/model.R (chỉ định nghĩa hàm, không chạy gì khi được nạp, chỉ dùng base R). Bộ kiểm thử gồm 54 kiểm thử trong tests/testthat/. Mỗi script bài học chạy độc lập từ thư mục module, ví dụ Rscript code/l02-testing.R. Kho Git (Bài 3) và gói R (Bài 4) được tạo trong thư mục tạm của máy, không nằm trong thư mục khóa học.

Tham số

Mô hình đọc một bảng tham số do assemble_params() ghép lại: 55 dòng của M02 (params/mdrtb.csv, xem trang M02) và 28 dòng lấy từ kb/_shared (ngưỡng chi phí-hiệu quả, tỷ lệ chiết khấu, chi phí thuốc và theo dõi tính từ bảng giá chung, hệ số CPI, 19 nhóm tuổi của bảng sống WHO). M11 không định nghĩa lại giá trị chung nào. Thư mục params/ của M11 chỉ chứa lịch sử giá BPaLM của GDF (dùng trong bài học) và ba bản cập nhật bằng chứng theo đúng cấu trúc bảng tham số, dùng ở Bài 3 (mỗi bản là một commit) và Bài 5 (quy trình cập nhật của mô hình “sống”): giá bedaquiline giảm tháng 9/2025, bảng giá BHYT 2026 của Bệnh viện Bạch Mai, và một bản có lỗi nhập liệu cố ý để bước kiểm tra đầu vào chặn lại.

Tệp Tên Tham số Giá trị Đơn vị Nguồn
gdf-prices.csv gdf_bpalm_2022_12 Giá Global Drug Facility (GDF) của BPaLM khi WHO khuyến cáo phác đồ (12/2022) 588 USD/đợt 6 tháng (European AIDS Treatment Group 2025b)
gdf-prices.csv gdf_bpalm_2025_02 Giá GDF của BPaLM sau đợt giảm giá bedaquiline (2/2025) 364 USD/đợt 6 tháng (European AIDS Treatment Group 2025a)
gdf-prices.csv gdf_bpalm_2025_04 Giá GDF của BPaLM sau đợt giảm giá pretomanid (4/2025) 310 USD/đợt 6 tháng (European AIDS Treatment Group 2025b)
gdf-prices.csv gdf_bdq_2025_02 Giá GDF một đợt bedaquiline 6 tháng (Lupin, Macleods), 2/2025 90 USD/đợt 6 tháng (European AIDS Treatment Group 2025a)
gdf-prices.csv gdf_bdq_2025_09 Giá GDF một đợt bedaquiline 6 tháng (Lupin), 9/2025 63 USD/đợt 6 tháng (ITPC-EECA 2025)
update-01-price.csv c_drug_short Chi phí thuốc phác đồ ngắn 9 tháng mỗi tháng, sau đợt giảm giá bedaquiline 9/2025 (cập nhật 1) 782,393 VNĐ/tháng (2025) (ITPC-EECA 2025)
update-01-price.csv c_drug_long Chi phí thuốc phác đồ dài hạn mỗi tháng, sau đợt giảm giá bedaquiline 9/2025 (cập nhật 1) 639,509 VNĐ/tháng (2025) (ITPC-EECA 2025)
update-02-monitoring.csv c_monitor Chi phí theo dõi mỗi tháng điều trị theo bảng giá BHYT mới (cập nhật 2) 587,700 VNĐ/tháng (giá 2026) (Bộ Y tế 2026)
update-02-monitoring.csv c_eot Chi phí đánh giá cuối điều trị theo bảng giá BHYT mới (cập nhật 2) 516,100 VNĐ/lần (giá 2026) (Bộ Y tế 2026)
update-02-monitoring.csv c_failed Chi phí mỗi tháng ở trạng thái Thất bại theo bảng giá BHYT mới (cập nhật 2) 52,600 VNĐ/tháng (giá 2026) (Bộ Y tế 2026)
update-03-typo.csv ae_bpalm Biến cố bất lợi độ 3 trở lên hoặc nghiêm trọng, BPaLM (cập nhật 3, có lỗi gõ cố ý) 23 tỷ lệ (Nyang’wa và c.s. 2024)

Cả 11 dòng đều có nguồn công khai; dấu “≈” trong cột ghi chú của tệp CSV đánh dấu giá trị gần đúng (ví dụ giá phác đồ sau đợt giảm giá bedaquiline được suy ra từ giá cả đợt và mức giảm của một đợt bedaquiline). Dòng ae_bpalm = 23 là lỗi gõ cố ý của tỷ lệ 0.23, không bao giờ được dùng làm kết quả.

Gói R

Mô hình chỉ dùng các gói đi kèm R (base, stats, tools, utils), nhờ đó cùng một tệp vừa dùng trong bài học vừa trở thành mã của gói. Bài 2 dùng thêm parallel (cũng đi kèm R) để chạy kiểm thử đột biến song song. Các bài dùng thêm testthat (kiểm thử), codetools (phát hiện phụ thuộc ẩn; có sẵn trong bản cài R), renv (ghi phiên bản gói), roxygen2 (tài liệu của gói), shiny (ứng dụng), ggplot2 và knitr. Module được kiểm tra với R version 4.5.3 (2026-03-11), testthat 3.3.2, shiny 1.13.0, roxygen2 7.3.3, renv 1.2.0 và Git 2.51.2. Các gói usethis và devtools thường dùng để tự động hóa việc tạo và kiểm tra gói; module này làm từng bước bằng base R để bạn thấy rõ chúng làm gì.

Tài liệu tham khảo