RAG với dữ liệu y tế là cách kết hợp truy xuất thông tin với mô hình ngôn ngữ để trả lời dựa trên hồ sơ, hướng dẫn chuyên môn hoặc tài liệu lâm sàng cụ thể. So với RAG trên tài liệu thông thường, miền y tế yêu cầu kiểm soát dữ liệu và nguồn chặt chẽ hơn vì một đoạn thông tin sai bệnh nhân hoặc sai thời điểm có thể làm thay đổi hoàn toàn ý nghĩa của câu trả lời.
Bài viết này tiếp tục từ kiến trúc RAG cơ bản và tập trung vào phần khó hơn: biến dữ liệu y tế không đồng nhất thành một kho tri thức có thể truy xuất, đồng thời hạn chế việc mô hình suy diễn vượt quá bằng chứng được cung cấp.
Vì sao RAG trên dữ liệu y tế khó hơn tìm kiếm tài liệu?
Một kho dữ liệu y tế có thể chứa ghi chú khám bệnh, kết quả xét nghiệm, đơn thuốc, chẩn đoán, báo cáo hình ảnh, tài liệu hướng dẫn và nhiều loại dữ liệu khác. Hai đoạn văn có thể rất giống nhau về ngữ nghĩa nhưng thuộc hai bệnh nhân khác nhau hoặc được ghi nhận ở hai thời điểm khác nhau.
Vì vậy, tìm kiếm dựa trên embedding đơn thuần thường chưa đủ. Hệ thống cần kết hợp semantic retrieval với metadata như mã bệnh nhân, loại tài liệu, ngày ghi nhận, cơ sở y tế, chuyên khoa và quyền truy cập.
Thứ tự này rất quan trọng. Quyền truy cập và metadata nên được áp dụng trước hoặc trong retrieval thay vì truy xuất toàn bộ dữ liệu rồi mới cố loại bỏ thông tin nhạy cảm sau đó.
Chuẩn hóa dữ liệu trước khi tạo embedding
Dữ liệu lâm sàng thường không được viết như một tài liệu hoàn chỉnh. Một bản ghi có thể chứa nhiều trường riêng biệt như triệu chứng, thuốc đang dùng, kết quả xét nghiệm và đánh giá của bác sĩ. Nếu ghép mọi thứ thành một chuỗi dài rồi chia chunk theo số ký tự, hệ thống dễ làm mất cấu trúc quan trọng.
Với RAG y tế, một chiến lược tốt hơn là tạo chunk theo đơn vị ngữ nghĩa. Ví dụ, một kết quả xét nghiệm có thể là một chunk; một phần Assessment trong ghi chú lâm sàng có thể là chunk khác; danh sách thuốc có thể được lưu như một nhóm riêng.
Metadata có vai trò gần như ngang với embedding. Một record tối giản có thể chứa nội dung văn bản cùng các thuộc tính như patient_id, document_type, encounter_date và source_id.
documents = [
{
"text": "HbA1c: 7.1%. Mẫu được lấy trong lần khám ngoại trú.",
"patient_id": "P001",
"document_type": "laboratory",
"encounter_date": "2026-01-10",
"source_id": "LAB-001"
},
{
"text": "Đang sử dụng metformin theo đơn hiện tại.",
"patient_id": "P001",
"document_type": "medication",
"encounter_date": "2026-01-10",
"source_id": "MED-001"
}
]
Dữ liệu trên chỉ là ví dụ giả lập. Trong hệ thống thật, thông tin nhận dạng và dữ liệu sức khỏe cần được xử lý theo yêu cầu bảo mật, quyền truy cập và quy định áp dụng cho tổ chức triển khai.
Lọc metadata trước khi tính độ tương đồng
Giả sử bác sĩ đang xem hồ sơ bệnh nhân P001 và hỏi: “Kết quả HbA1c gần đây là bao nhiêu?”. Nếu vector database chứa hàng triệu đoạn từ nhiều bệnh nhân, không nên tìm top-k trên toàn bộ kho dữ liệu.
Hệ thống nên giới hạn tập ứng viên theo patient_id và quyền truy cập trước, sau đó mới tính similarity. Đây là khác biệt quan trọng giữa một demo RAG và kiến trúc có thể áp dụng cho dữ liệu nhạy cảm.
def filter_documents(
documents,
patient_id,
allowed_types=None
):
filtered = [
doc
for doc in documents
if doc["patient_id"] == patient_id
]
if allowed_types:
filtered = [
doc
for doc in filtered
if doc["document_type"] in allowed_types
]
return filtered
candidates = filter_documents(
documents,
patient_id="P001",
allowed_types={"laboratory"}
)
Trong production, logic này thường được thực hiện ngay trong vector database hoặc tầng truy cập dữ liệu. Danh sách patient_id hay document_type không nên được tin tưởng chỉ vì chúng xuất hiện trong câu hỏi của người dùng; hệ thống phải lấy phạm vi truy cập từ phiên đăng nhập hoặc cơ chế phân quyền đáng tin cậy.
Kết hợp retrieval với Gemini để trả lời có căn cứ
Sau khi retrieval trả về các đoạn phù hợp, chúng được đưa vào Gemini dưới dạng context. Với dữ liệu y tế, prompt nên tách rõ câu hỏi, bằng chứng và quy tắc tạo câu trả lời.
Ví dụ dưới đây sử dụng Gemini thông qua SDK Python. Tên model được lấy từ biến môi trường để ứng dụng có thể dùng model được cấu hình cho project thay vì phụ thuộc vào một model ID cố định.
import os
from google import genai
client = genai.Client(
api_key=os.environ["GEMINI_API_KEY"]
)
MODEL_NAME = os.environ["GEMINI_MODEL"]
def build_context(retrieved_docs):
blocks = []
for doc in retrieved_docs:
blocks.append(
f"""SOURCE_ID: {doc["source_id"]}
DATE: {doc["encounter_date"]}
TYPE: {doc["document_type"]}
CONTENT: {doc["text"]}"""
)
return "\n\n".join(blocks)
def answer_question(question, retrieved_docs):
context = build_context(retrieved_docs)
prompt = f"""Bạn đang hỗ trợ truy xuất thông tin từ hồ sơ y tế.
Chỉ sử dụng thông tin trong EVIDENCE.
Không tự bổ sung dữ kiện không xuất hiện trong EVIDENCE.
Nếu dữ liệu không đủ, hãy nói rõ là chưa đủ thông tin.
Khi nêu một dữ kiện, hãy kèm SOURCE_ID tương ứng.
EVIDENCE:
{context}
QUESTION:
{question}
"""
response = client.models.generate_content(
model=MODEL_NAME,
contents=prompt
)
return response.text
Việc đưa source_id vào context giúp câu trả lời có thể tham chiếu trở lại bản ghi đã được truy xuất. Đây không phải là bảo đảm tuyệt đối rằng mọi câu trả lời đều chính xác, nhưng nó tạo điều kiện để ứng dụng kiểm tra và hiển thị nguồn cho người dùng.
Thời gian và trạng thái bệnh nhân phải trở thành một phần của retrieval
Trong dữ liệu y tế, một thông tin đúng ở thời điểm trước có thể không còn đúng ở hiện tại. Danh sách thuốc, chẩn đoán, kết quả xét nghiệm và kế hoạch điều trị đều có thể thay đổi theo thời gian.
Vì vậy, câu hỏi “Bệnh nhân đang dùng thuốc gì?” khác với “Bệnh nhân từng dùng thuốc gì?”. Câu đầu yêu cầu ưu tiên trạng thái hiện tại; câu sau có thể cần toàn bộ lịch sử.
Một retriever tốt nên hiểu hoặc suy ra loại truy vấn trước khi tìm kiếm. Có thể áp dụng filter theo ngày, sắp xếp theo encounter_date hoặc ưu tiên các record đánh dấu là active. Nếu dữ liệu nguồn đã có cấu trúc rõ ràng, truy vấn cơ sở dữ liệu có cấu trúc đôi khi phù hợp hơn vector search.
| Câu hỏi | Retrieval phù hợp |
|---|---|
| Kết quả xét nghiệm gần nhất là gì? | Lọc theo loại xét nghiệm, ưu tiên record mới nhất. |
| Các triệu chứng được ghi nhận trong lần khám trước? | Xác định encounter cụ thể rồi truy xuất ghi chú của lần khám đó. |
| Có ghi nhận dị ứng thuốc không? | Ưu tiên dữ liệu dị ứng có cấu trúc, dùng semantic search như lớp bổ sung. |
Điều này dẫn đến một nguyên tắc quan trọng: không phải mọi câu hỏi đều nên được giải quyết bằng cùng một vector search. RAG y tế thực tế thường là hybrid retrieval, kết hợp dữ liệu có cấu trúc, metadata, tìm kiếm từ khóa và semantic search.
Ba lớp kiểm soát không nên bỏ qua
Khi RAG được dùng với thông tin sức khỏe, chất lượng câu trả lời chỉ là một phần của bài toán. Hệ thống còn cần kiểm soát dữ liệu nào được truy xuất, dữ liệu nào được gửi tới mô hình và kết quả nào được phép hiển thị.
- Access control: xác thực người dùng và giới hạn phạm vi dữ liệu trước khi retrieval. Vector database không nên trở thành cách vượt qua quyền truy cập của hệ thống hồ sơ gốc.
- Grounding: yêu cầu Gemini dựa trên evidence, đồng thời giữ source_id để câu trả lời có thể được kiểm tra lại.
- Output validation: kiểm tra citation, định dạng và những trường dữ liệu quan trọng trước khi hiển thị kết quả cho người dùng hoặc chuyển sang hệ thống khác.
Ngoài ra, cần có cơ chế logging phù hợp để đánh giá retrieval và điều tra lỗi. Log không nên vô tình sao chép toàn bộ dữ liệu nhạy cảm sang một hệ thống giám sát không được thiết kế để lưu loại thông tin đó.
RAG cũng không biến một mô hình ngôn ngữ thành hệ thống chẩn đoán đáng tin cậy một cách tự động. Nếu ứng dụng ảnh hưởng đến quyết định lâm sàng, cần có quy trình đánh giá, kiểm thử, giám sát và con người chịu trách nhiệm phù hợp với mục đích triển khai.
Đánh giá RAG y tế nên tách retrieval và generation
Khi câu trả lời sai, cần biết lỗi đến từ đâu. Nếu tài liệu đúng không xuất hiện trong top-k, đó là lỗi retrieval. Nếu tài liệu đúng đã được đưa vào context nhưng Gemini vẫn trả lời sai, vấn đề nằm ở bước generation hoặc prompt.
Vì vậy, bộ test nên chứa câu hỏi cùng tài liệu hoặc record được kỳ vọng truy xuất. Có thể đánh giá xem evidence cần thiết có xuất hiện trong top-k hay không trước khi đánh giá câu trả lời cuối.
Ở tầng generation, nên kiểm tra ít nhất ba yếu tố: câu trả lời có được evidence hỗ trợ hay không, có bỏ sót thông tin quan trọng không và citation có trỏ đúng source hay không. Với những trường nhạy cảm như liều thuốc, giá trị xét nghiệm hoặc ngày tháng, kiểm tra có cấu trúc thường đáng tin cậy hơn việc chỉ dùng một mô hình khác để chấm điểm tự động.
RAG cho dữ liệu y tế không chỉ là thêm Gemini vào một vector database. Kiến trúc tốt phải kết hợp semantic retrieval với metadata, thời gian, quyền truy cập và nguồn dữ liệu có thể kiểm chứng. Khi những lớp này được thiết kế ngay từ đầu, RAG mới trở thành một nền tảng đáng tin cậy hơn cho các ứng dụng hỏi đáp, tìm kiếm hồ sơ và hỗ trợ khai thác tri thức y khoa.
Nội dung gốc thuộc tác giả. Vui lòng không đăng lại toàn văn khi chưa được phép, trừ trường hợp pháp luật hoặc giấy phép áp dụng cho phép. Nội dung của bên thứ ba tuân theo quyền và giấy phép tương ứng.
Nguồn gốc: Kiến trúc RAG cho dữ liệu y tế