FDE PulseViệc làm FDE đang mở 314Mới đăng 7 ngày qua 10Chủ đề nổi bật: Đào tạo kỹ năng FDE tại Đông Nam Á

Tờ báo của nghề Forward Deployed Engineer

Bách khoa

HL7 v2, FHIR và Epic: tích hợp AI vào dữ liệu bệnh viện qua hai đường ống

Ở bệnh viện, dữ liệu đến với bạn qua hai đường khác nhau: sự kiện được đẩy qua HL7 v2, còn trạng thái thì phải hỏi qua FHIR. Chọn sai đường từ đầu, agent của bạn hoặc đến trễ, hoặc đọc sai.

Đồ hoạHai đường ống dữ liệu bệnh viện
HL7 v2FHIR
Bản chấtMessage theo sự kiện, gồm các dòng segmentResource và API để hai ứng dụng tương tác
Cách nhận dữ liệuĐược đẩy đến khi có sự kiện, ví dụ ADT^A04Chủ động hỏi qua create, read, search, history...
Nhận diện loại dữ liệuTrigger trong segment MSHLoại resource và endpoint tương ứng
Ở Epic đi qua đâuMọi interface v2 đi qua BridgesFHIR API, client nền dùng backend OAuth 2.0
Bẫy thường gặpSegment tùy chọn có thể bị thiếuResource theo quy tắc 80/20, thiếu nhu cầu hiếm

Dùng HL7 v2 để biết khi nào cần hành động, dùng FHIR để biết hành động dựa trên dữ liệu gì.

Đồ hoạ: FDE Times

Tóm tắt nhanh

  • HL7 v2 là luồng sự kiện: mỗi message là tập segment, loại message do trigger trong MSH quyết định, và ở Epic mọi interface v2 đều đi qua Bridges.
  • FHIR là cách để hỏi trạng thái: gồm resource và API, nhắm tới REST với create, read, update, search, history, transaction.
  • Agent AI chạy service-to-service với Epic nên dùng backend OAuth 2.0; còn parser thì phải chịu được segment tùy chọn bị thiếu.
Chia sẻLinkedInFacebookX

Tuần đầu ở một bệnh viện, bạn được giao một việc nghe rất gọn: “Khi bệnh nhân đến khám, agent tóm tắt hồ sơ cho bác sĩ trước khi họ bước vào phòng.” Rồi bạn hỏi IT bệnh viện lấy dữ liệu ở đâu. Người thì bảo “lấy feed HL7”, người khác bảo “gọi FHIR API của Epic”, và hai người nói về hai thứ khác nhau hoàn toàn.

Đây là ngã rẽ đầu tiên của mọi dự án AI trong y tế. Dữ liệu bệnh viện đi qua hai đường ống với hai triết lý trái ngược nhau. Đường thứ nhất đẩy sự kiện đến bạn ngay khi có chuyện xảy ra. Đường thứ hai để bạn hỏi trạng thái của bệnh nhân vào bất kỳ lúc nào.

Một FDE giỏi không cần thuộc hết đặc tả. Cái bạn cần là biết mỗi bước trong use case nên lấy dữ liệu từ đường nào, và đường đó hay hỏng ở chỗ nào.

HL7 v2 là một chuỗi sự kiện, không phải một cơ sở dữ liệu

Chính Epic gọi HL7 v2 là một trong những chuẩn dữ liệu y tế được triển khai rộng rãi nhất. Một message HL7 v2 là tập hợp các dòng gọi là segment. Loại message phụ thuộc vào sự kiện kích hoạt nó: ví dụ, bệnh nhân được tiếp nhận thì sinh ra message ADT^A04.

Loại message được nhận diện qua trigger nằm trong segment MSH, dòng mở đầu message. Nhóm ADT mang thông tin hành chính và các sự kiện của lượt khám. Vì thế, muốn biết “bệnh nhân vừa đến”, bạn không đi hỏi ai cả. Bạn nghe message ADT, đọc trigger trong MSH và phản ứng.

Với Epic, mọi interface HL7 v2 vào hay ra đều đi qua Bridges, hạ tầng nhắn tin lõi mà Epic dùng làm interface engine, kể cả phần acknowledgement cho message gửi đi. Trong thực tế, “xin feed HL7” nghĩa là đội interface của bệnh viện cấu hình một luồng trên Bridges để đẩy sự kiện đến hệ thống của bạn.

FHIR thì để hỏi: bệnh nhân này hiện ra sao?

FHIR được xây quanh hai phần. Resources là các mô hình thông tin định nghĩa phần tử dữ liệu, ràng buộc và quan hệ giữa chúng. APIs là giao diện để hai ứng dụng nói chuyện với nhau. Đặc tả nhắm tới giao diện RESTful, dù không bắt buộc phải là REST.

Bộ tương tác REST của FHIR gồm create, read, update, search, history và transaction, và các tương tác này được ánh xạ vào những HTTP method quen thuộc. Ví dụ, để tạo resource, bạn gửi HTTP POST tới endpoint của loại resource đó, và client không cần tự cấp id. Với một backend developer, thứ này quen hơn HL7 v2 rất nhiều.

Ở Mỹ, FHIR còn có sức nặng pháp lý. Quy định của ONC, cơ quan quản lý công nghệ thông tin y tế của Mỹ, gắn tiêu chí API chuẩn hóa §170.315(g)(10) với FHIR, buộc nhà phát triển phần mềm được chứng nhận phải cung cấp API dựa trên FHIR cho khách hàng.

Đây là luật Mỹ, không áp dụng mặc nhiên ở nơi khác, nên với khách hàng Mỹ, đặt câu hỏi về FHIR API ngay buổi đầu với đội IT là hợp lý.

Với một developer Việt Nam, bộ ba Epic, HL7 v2 và FHIR nhiều khả năng xuất hiện khi bạn làm cho khách hàng healthtech ở Mỹ hoặc nhận vị trí FDE remote, hơn là ở bệnh viện trong nước. Đó là lý do kỹ năng này đáng đầu tư nếu bạn nhắm tới thị trường đó.

Làm thử: agent tóm tắt hồ sơ khi bệnh nhân đến

Quay lại yêu cầu ban đầu. Thử chia nó thành hai câu hỏi: khi nào agent chạy, và agent đọc gì. Câu thứ nhất là chuyện sự kiện, câu thứ hai là chuyện trạng thái.

Bước một là nghe sự kiện. Đội interface cấu hình Bridges đẩy message ADT về listener của bạn. Một message minh họa, đã rút gọn và chỉ dùng để thấy hình dạng, trông như sau:

MSH|^~\&|<app gửi>|<nơi gửi>|<app nhận>|<nơi nhận>|<thời điểm>||ADT^A04^ADT_A01|...
PID|...|...|<mã bệnh nhân>|...

Listener của bạn tách message theo dòng thành segment, đọc trường loại message (MSH-9) theo đúng vị trí của nó và chỉ xử lý ADT^A04. Hãy so khớp theo tiền tố, vì giá trị thực tế thường dài hơn, như ADT^A04^ADT_A01. Mọi loại message khác thì bỏ qua, và từ segment định danh, bạn lấy mã bệnh nhân rồi đẩy một job vào hàng đợi.

def handle(raw: str):
    segments = {}
    for line in raw.strip().splitlines():
        name = line[:3]
        segments.setdefault(name, []).append(line.split("|"))
    msh = segments.get("MSH")
    if not msh:
        log_missing("MSH", raw)
        return
    fields = msh[0]
    # MSH-1 chính là ký tự "|", nên sau khi split, MSH-9 nằm ở chỉ số 8
    msg_type = fields[8] if len(fields) > 8 else ""
    if not msg_type.startswith("ADT^A04"):
        return  # không phải sự kiện cần xử lý
    pid = segments.get("PID")
    if not pid:
        log_missing("PID", raw)  # segment thiếu: ghi lại, không crash
        return
    enqueue_summary(pid[0])

Bước hai là hỏi trạng thái. Worker lấy job, rồi gọi FHIR API của Epic bằng search và read để kéo những resource agent cần. Chuỗi request minh họa dưới đây cho thấy hình dạng; tên tham số và hệ định danh cụ thể phải lấy từ tài liệu của Epic và từ đội IT bệnh viện:

# 1. search: tìm Patient theo mã bệnh nhân lấy từ PID
GET [base]/Patient?identifier=<hệ định danh>|<mã bệnh nhân>
Authorization: Bearer <access token>

# 2. read: lấy chính xác resource Patient theo id server trả về
GET [base]/Patient/<patient id>

# 3. search: các lượt khám và chẩn đoán của bệnh nhân đó
GET [base]/Encounter?patient=<patient id>
GET [base]/Condition?patient=<patient id>

Điểm cần nắm là thứ tự. Mã trong PID là định danh của bệnh viện, không phải id của resource, nên bạn search trước để đổi nó thành patient id, rồi mới read và search các resource liên quan. Kết quả search thường về dưới dạng một danh sách; worker gom các Encounter và Condition lại, lọc phần cần thiết rồi mới đưa cho mô hình tóm tắt.

Đây là luồng service-to-service, không có bác sĩ nào đăng nhập giữa chừng. Epic khuyến nghị backend OAuth 2.0 cho đúng kiểu client truy vấn này, nên service của bạn tự xác thực và tự lấy token trước khi gửi các request trên.

Bước ba là ghi kết quả, nếu bệnh viện cho phép. Khi đó bạn POST một resource mới tới endpoint tương ứng và để server tự sinh id. Nhưng ở dự án đầu tiên, thường khôn ngoan hơn là chỉ đọc, rồi hiển thị bản tóm tắt ở một giao diện riêng.

Các bước để tự làm ở khách hàng

Đầu tiên, viết use case thành một dòng thời gian và đánh dấu từng bước là “cần biết khi nào” hay “cần biết cái gì”. Bước thuộc loại thứ nhất đi qua HL7 v2, bước thuộc loại thứ hai đi qua FHIR. Mang bản vẽ này đi gặp đội interface ngay trong tuần đầu.

Tiếp theo, xin hai thứ cùng lúc vì chúng có quy trình khác nhau: một luồng ADT trên Bridges về môi trường test, và quyền backend OAuth cho FHIR. Sau đó, xin vài message thật đã khử định danh từ môi trường test để kiểm tra parser, đừng chỉ dựa vào ví dụ trong tài liệu.

Cuối cùng, trước khi viết prompt cho agent, hãy viết bước kiểm tra dữ liệu. Ghi lại xem mỗi nguồn gửi những segment nào, thiếu những segment nào. Bảng thống kê đó sẽ là tài liệu quý nhất khi bàn giao.

Những lỗi khiến dự án trượt

Lỗi phổ biến nhất là coi mọi message đều đủ segment. Trong HL7 v2, có segment bắt buộc và có segment tùy chọn, nên dữ liệu từ các nguồn khác nhau không đồng đều. Parser viết theo một message mẫu “đẹp” sẽ vỡ ngay ngày đầu chạy thật.

Lỗi thứ hai là kỳ vọng FHIR chứa mọi thứ. Resource của FHIR được thiết kế theo quy tắc 80/20, tập trung vào những nhu cầu phổ biến. Nếu use case cần một chi tiết hiếm, bạn phải kiểm tra sớm xem nó có trong resource chuẩn hay không, thay vì phát hiện ra lúc demo.

Lỗi thứ ba là dùng sai kiểu xác thực. Một agent chạy nền mà lại đi mượn phiên đăng nhập của người dùng sẽ vướng ngay ở bước review bảo mật. Lỗi cuối cùng là dùng FHIR để polling thay cho sự kiện: gọi search liên tục để “đoán” bệnh nhân vừa đến vừa tốn tài nguyên vừa trễ, trong khi ADT đã có sẵn để báo cho bạn.

Biến kỹ năng này thành lợi thế khi ứng tuyển

Khi đọc JD của các vị trí FDE trong healthtech, hãy để ý các từ khóa HL7 v2, FHIR, Epic, interface engine và OAuth. Chúng cho biết vị trí đó sẽ đụng vào cả hai đường ống.

Trong CV, đừng chỉ ghi “biết FHIR”. Hãy mô tả một luồng hoàn chỉnh, chẳng hạn “listener ADT^A04 kích hoạt worker gọi FHIR qua backend OAuth, có thống kê segment bị thiếu theo từng nguồn”.

Một dự án cá nhân nhỏ là đủ để chứng minh: một parser HL7 v2 chịu được segment bị thiếu và một client FHIR biết search, read rồi POST. Hãy chuẩn bị sẵn lời giải thích vì sao bạn chọn đường nào cho bước nào. Đó cũng chính là câu hỏi đầu tiên bạn phải trả lời khi đứng trong bệnh viện.

7 nguồn
Đọc tiếp trên lộ trình · Chặng 2: Kỹ thuật rộngThực hành: cho agent thao tác trên web nội bộ không có API bằng Playwright MCPNăm bước để agent dùng phiên đăng nhập nạp sẵn, đọc và điền form trên một trang quản trị không có API, trong khi mọi thao tác ghi dữ liệu vẫn phải qua người duyệt.