Mở đầu — vì sao bài này quan trọng
Trong hầu hết sự nghiệp làm kiến trúc, bạn thiết kế hệ thống dựa trên những quy tắc rõ ràng: input đi vào, logic xử lý, output đi ra. Nhưng khi machine learning (ML) bước vào hệ thống, mọi giả định quen thuộc bị đảo lộn. Model không cho ra kết quả cố định — nó "dự đoán". Kết quả hôm nay đúng, ba tháng sau có thể sai vì hành vi người dùng đổi. Model cần được huấn luyện lại, cần dữ liệu, cần phiên bản, cần rollback. Đây không còn là câu chuyện "gọi một hàm" mà là câu chuyện vận hành cả một vòng đời dữ liệu và model.
Là người ra quyết định kiến trúc, bạn hiếm khi phải tự tay train model — đó là việc của data scientist. Nhưng bạn PHẢI quyết định: ML sẽ nằm ở đâu trong hệ thống, gọi nó đồng bộ hay bất đồng bộ, chịu độ trễ bao nhiêu, xử lý ra sao khi model "chết" hoặc trả kết quả rác. Rất nhiều dự án ML thất bại không phải vì model kém, mà vì kiến trúc xung quanh model quá yếu: không có đường rollback, không giám sát được chất lượng dự đoán, hoặc coupling quá chặt khiến mỗi lần đổi model là phải deploy lại cả hệ thống.
Bài này tập trung vào các pattern kiến trúc để tích hợp ML vào hệ thống production — cụ thể là ba nơi ML thường xuất hiện nhất trong sản phẩm thực tế (recommendation, fraud detection, dynamic pricing) và các mẫu tích hợp phổ biến. Chúng ta bàn về kiến trúc, không bàn về thuật toán bên trong model.
Khái niệm cốt lõi
Ba vị trí ML thường xuất hiện trong sản phẩm
Trước khi nói pattern, hãy nhìn ba "chỗ" mà ML gần như luôn xuất hiện trong sản phẩm số. Nắm được đặc điểm kiến trúc của từng chỗ quan trọng hơn nhớ tên thuật toán.
Recommendation (gợi ý) — xuất hiện ở trang sản phẩm, feed nội dung, kết quả tìm kiếm. Đặc điểm kiến trúc: khối lượng dự đoán cực lớn (mỗi lần load trang là hàng chục gợi ý), nhưng chấp nhận được độ trễ vài trăm mili-giây, và quan trọng — sai một chút không chết ai. Nếu gợi ý dở, người dùng chỉ bỏ qua. Đây là loại ML "chịu lỗi cao", cho phép kiến trúc đơn giản hơn, thậm chí precompute (tính trước) rồi cache.
Fraud detection (phát hiện gian lận) — nằm ở luồng thanh toán, đăng ký tài khoản, đăng nhập. Đặc điểm ngược hoàn toàn với recommendation: sai là mất tiền thật. Một giao dịch gian lận lọt qua có thể mất hàng chục triệu; một giao dịch thật bị chặn nhầm làm mất khách. Loại này thường yêu cầu dự đoán đồng bộ, độ trễ thấp (giao dịch không thể đợi 5 giây), và bắt buộc phải có cơ chế fallback rõ ràng khi model không phản hồi.
Pricing (định giá động) — thấy ở gọi xe, đặt phòng, vé máy bay, sàn thương mại điện tử. Đặc điểm: kết quả ảnh hưởng trực tiếp doanh thu và trải nghiệm, nhạy cảm về mặt kinh doanh và đôi khi cả pháp lý. Định giá sai lệch (ví dụ tăng giá vô lý giờ cao điểm) có thể gây khủng hoảng truyền thông. Loại này cần guardrail (chặn trên/chặn dưới) bao quanh output của model — không bao giờ để model tự do quyết định giá cuối cùng.
Hai chế độ phục vụ model: online serving và batch/offline
Đây là quyết định kiến trúc cốt lõi nhất khi đưa ML vào hệ thống.
Online serving (dự đoán thời gian thực): hệ thống gọi model ngay tại thời điểm cần, thường qua một model-serving service riêng (được bọc sau một API). Dùng khi input chỉ có tại lúc chạy — ví dụ đặc trưng của giao dịch fraud chỉ biết được khi giao dịch xảy ra. Ưu điểm: luôn dùng dữ liệu mới nhất. Nhược điểm: thêm độ trễ, thêm điểm hỏng, phải lo scaling cho service model.
Batch/offline (dự đoán theo lô): bạn chạy model định kỳ (ví dụ mỗi đêm), tính sẵn kết quả cho hàng triệu người dùng, lưu vào một kho (Redis, database) rồi lúc runtime chỉ việc tra cứu. Recommendation kiểu "sản phẩm gợi ý cho bạn" thường dùng cách này. Ưu điểm: runtime cực nhanh (chỉ là lookup), rẻ, ổn định. Nhược điểm: kết quả "cũ" tới vài giờ, không phản ánh hành vi vừa xảy ra.
Rất nhiều hệ thống thực tế dùng hybrid: batch tính phần nền, online tính phần điều chỉnh theo ngữ cảnh tức thời.
Pattern tách biệt: ML như một service độc lập
Nguyên tắc kiến trúc quan trọng nhất: coi model như một dependency bên ngoài, không phải một hàm nhúng trong code nghiệp vụ. Bạn bọc model sau một interface ổn định (một API nội bộ), để phần nghiệp vụ chỉ biết "gọi và nhận kết quả", không biết model bên trong là phiên bản nào, framework gì. Điều này cho phép data scientist thay model mà không đụng vào code nghiệp vụ, và cho phép bạn A/B test hai model song song.
Đi kèm là ba cơ chế bắt buộc: fallback (khi model lỗi/chậm, có giá trị mặc định để hệ thống không sập), shadow mode (chạy model mới song song, ghi log kết quả nhưng chưa dùng, để so sánh trước khi bật thật), và monitoring chất lượng (theo dõi model drift — hiện tượng model xấu dần theo thời gian vì dữ liệu thực tế đổi khác so với lúc train).
Tình huống thực tế
Ví dụ 1 — Recommendation ở một sàn TMĐT Việt Nam (batch + hybrid)
Một sàn thương mại điện tử tại Việt Nam (giả định tên "ChợViet") có khoảng 8 triệu người dùng hoạt động và 2 triệu sản phẩm. Ban đầu đội ngũ định làm recommendation online — mỗi lần user mở app thì gọi model tính gợi ý real-time. Kết quả: model-serving service quá tải giờ cao điểm (20h–22h), độ trễ vọt lên 3–4 giây, trang chủ load ì ạch.
Họ chuyển sang kiến trúc hybrid. Mỗi đêm, một job batch chạy model tính sẵn top 200 sản phẩm gợi ý cho từng user, đẩy vào Redis (key theo user_id). Lúc runtime, trang chủ chỉ lookup Redis — dưới 10ms. Phần "real-time" chỉ dùng cho một dải hẹp: nếu user vừa xem một sản phẩm trong phiên hiện tại, một service nhẹ sẽ chèn vài sản phẩm cùng danh mục lên đầu danh sách đã precompute. Kết quả: p99 độ trễ trang chủ giảm từ 3.5s xuống dưới 150ms, chi phí GPU giảm mạnh vì chỉ chạy batch 1 lần/đêm thay vì phục vụ liên tục.
Bài học: với ML chịu lỗi cao như recommendation, đừng mặc định chọn online serving. Precompute + cache thường vừa nhanh vừa rẻ hơn nhiều. Chỉ đưa phần real-time vào đúng chỗ tạo ra giá trị rõ rệt.
Ví dụ 2 — Fraud detection ở ví điện tử (online + fallback bắt buộc)
Một ví điện tử khu vực Đông Nam Á xử lý khoảng 1.5 triệu giao dịch/ngày. Họ đặt một fraud model vào luồng thanh toán: mỗi giao dịch, trước khi duyệt, gọi model chấm điểm rủi ro (0–1). Điểm cao thì chặn hoặc yêu cầu xác thực thêm.
Vấn đề kiến trúc lộ ra vào một đêm khi model-serving service bị treo do rò rỉ bộ nhớ. Vì luồng thanh toán gọi model đồng bộ và không có fallback, mọi giao dịch bị treo theo — toàn hệ thống thanh toán đứng gần 20 phút. Đây là bài học kinh điển: một thành phần "phụ trợ" như fraud model lại trở thành điểm hỏng chí mạng của luồng chính.
Họ thiết kế lại theo nguyên tắc fail-open có kiểm soát: đặt timeout cứng 200ms cho lời gọi model. Nếu model không trả kịp, hệ thống dùng một rule-based fallback đơn giản (chặn giao dịch quá lớn bất thường, hoặc từ thiết bị lạ) và cho phần còn lại đi qua, đồng thời gắn cờ để xét lại sau. Họ còn bọc lời gọi model bằng circuit breaker: nếu model lỗi liên tục, tự ngắt và chuyển hẳn sang rule cho tới khi model hồi phục. Từ đó, model có sập thì luồng thanh toán vẫn sống.
Bài học: với ML nằm trên critical path, câu hỏi thiết kế quan trọng nhất không phải "model chính xác bao nhiêu" mà là "khi model chết thì hệ thống làm gì". Luôn có fallback, timeout, và circuit breaker.
Ví dụ 3 — Dynamic pricing ở nền tảng gọi xe (guardrail quanh model)
Một nền tảng gọi xe dùng ML để định giá động theo cung–cầu. Model nhận số tài xế rảnh, số cuốc đang chờ, thời tiết, giờ trong ngày rồi đề xuất hệ số nhân giá. Trong một trận mưa lớn ở TP.HCM, do thiếu tài xế đột ngột, model đẩy hệ số lên 4.8x — một cuốc bình thường 60.000đ vọt lên gần 290.000đ. Người dùng phản ứng dữ dội trên mạng xã hội, gây khủng hoảng truyền thông trong ngày.
Vấn đề không phải model sai về mặt toán học — cung cầu đúng là mất cân bằng. Vấn đề là kiến trúc để model tự quyết giá cuối cùng, không có guardrail. Họ sửa bằng cách chèn một lớp "policy" giữa output của model và giá hiển thị: hệ số nhân bị chặn cứng ở mức tối đa 2.0x bất kể model đề xuất bao nhiêu, có ngoại lệ theo vùng và được ban lãnh đạo phê duyệt bằng cấu hình, không phải bằng model. Họ cũng thêm shadow mode: model mới chạy song song, ghi log giá "nếu áp dụng" để đội kinh doanh xem trước khi bật thật.
Bài học: với ML ảnh hưởng doanh thu và danh tiếng, output của model phải đi qua một lớp business guardrail do con người kiểm soát. Model đề xuất, chính sách quyết định.
Hướng dẫn từng bước
Khi bạn đứng trước quyết định "đưa ML vào hệ thống thế nào", đi theo trình tự sau:
- Xác định ML thuộc loại rủi ro nào. Chịu lỗi cao (recommendation) hay critical path (fraud, pricing)? Câu trả lời này chi phối toàn bộ các quyết định sau. Sai lệch của một gợi ý chỉ gây khó chịu; sai lệch của một quyết định chặn giao dịch gây mất tiền.
- Chọn chế độ phục vụ: online, batch, hay hybrid. Hỏi: input có sẵn trước không? Nếu có, ưu tiên batch để precompute. Nếu input chỉ xuất hiện lúc chạy (như đặc trưng giao dịch), buộc phải online. Cần vừa nhanh vừa cập nhật thì hybrid.
- Đặt yêu cầu độ trễ và timeout rõ ràng. Model có bao nhiêu mili-giây để trả lời trước khi hệ thống bỏ qua? Con số này phải nằm trong tài liệu thiết kế, không để mặc định vô tận.
- Thiết kế fallback trước khi thiết kế happy path. Viết ra: khi model timeout/lỗi/trả kết quả vô lý thì hệ thống dùng giá trị gì? Rule đơn giản? Giá trị mặc định? Kết quả cache cũ? Không có câu trả lời rõ thì chưa được deploy.
- Bọc model sau một interface ổn định. Phần nghiệp vụ chỉ gọi một API nội bộ, không phụ thuộc framework hay phiên bản model. Điều này cho phép thay model, A/B test, rollback độc lập.
- Thêm guardrail cho ML nhạy cảm kinh doanh. Với pricing hay bất cứ output nào ảnh hưởng tiền/pháp lý, chèn lớp policy chặn trên/chặn dưới do con người cấu hình.
- Bật monitoring và shadow mode. Giám sát phân phối output theo thời gian để phát hiện drift. Model mới luôn chạy shadow (ghi log, chưa dùng) trước khi bật thật.
- Lập kế hoạch retrain và rollback. Model sẽ xấu đi theo thời gian. Ai chịu trách nhiệm train lại? Bao lâu một lần? Đường rollback về model cũ có được kiểm thử chưa?
Lỗi thường gặp & mẹo
Lỗi 1 — Coi model như một hàm bình thường. Nhúng thẳng model vào code nghiệp vụ, mỗi lần đổi model phải deploy lại cả service. Mẹo: luôn tách model ra sau một interface, coi nó như dependency ngoài.
Lỗi 2 — Không có fallback trên critical path. Đây là lỗi gây sập hệ thống nghiêm trọng nhất, như ví dụ ví điện tử. Mẹo: mỗi lời gọi model trên luồng chính đều phải có timeout + fallback + circuit breaker.
Lỗi 3 — Để model tự quyết định cuối cùng cho vấn đề nhạy cảm. Pricing, xét duyệt, chặn tài khoản. Mẹo: model đề xuất, lớp policy do người cấu hình mới ra quyết định cuối.
Lỗi 4 — Mặc định chọn online serving. Nhiều đội mặc định real-time cho mọi thứ, tốn kém và mong manh. Mẹo: hỏi "có precompute được không" trước khi chọn online.
Lỗi 5 — Deploy model mới thẳng lên production. Không shadow, không A/B test. Mẹo: mọi model mới đi qua shadow mode ít nhất một chu kỳ để so sánh với model đang chạy.
Lỗi 6 — Quên model drift. Deploy xong bỏ mặc, vài tháng sau chất lượng tụt mà không ai biết. Mẹo: monitor phân phối output và một vài metric chất lượng, đặt cảnh báo khi lệch.
Mẹo tổng quát: với vai trò kiến trúc, giá trị lớn nhất bạn tạo ra không phải làm model tốt hơn, mà là làm cho hệ thống quanh model an toàn, thay được, giám sát được và rollback được.
Bài tập thực hành
Bài 1 — Phân loại. Với ba tính năng sau, hãy xác định mỗi cái thuộc loại "chịu lỗi cao" hay "critical path", nên dùng online/batch/hybrid, và cần fallback gì: (a) gợi ý bài viết ở cuối một trang tin tức; (b) chấm điểm rủi ro khi mở tài khoản ngân hàng số; (c) đề xuất mức phí giao hàng theo thời điểm cho app đặt đồ ăn.
Bài 2 — Thiết kế fallback. Cho luồng fraud detection ở ví điện tử trong ví dụ 2. Hãy viết ra (dạng gạch đầu dòng) chính sách fallback đầy đủ: timeout bao nhiêu, khi model chết thì dùng rule nào, circuit breaker mở/đóng theo điều kiện gì, và giao dịch bị fallback được xử lý hậu kỳ ra sao.
Bài 3 — Guardrail. Giả sử bạn phụ trách dynamic pricing cho một nền tảng đặt phòng khách sạn dịp lễ. Hãy đề xuất các guardrail quanh output của model: chặn trên/chặn dưới, ngoại lệ theo vùng/sự kiện, và cơ chế để đội kinh doanh xem trước tác động (shadow) trước khi áp dụng thật.
Bài 4 — Viết mini-ADR. Chọn một trong ba tính năng ở Bài 1, viết một ADR ngắn (bối cảnh, quyết định chọn online/batch/hybrid, các phương án đã cân nhắc, hệ quả) cho quyết định tích hợp ML đó.
Tóm tắt
- ML trong kiến trúc thường xuất hiện ở ba chỗ: recommendation (chịu lỗi cao), fraud detection (critical path, sai là mất tiền), pricing (nhạy cảm doanh thu và truyền thông). Đặc điểm rủi ro của từng chỗ chi phối toàn bộ thiết kế.
- Quyết định cốt lõi là chế độ phục vụ: online (real-time, cập nhật nhất nhưng thêm độ trễ và điểm hỏng), batch (precompute, nhanh và rẻ nhưng cũ), hay hybrid.
- Luôn coi model như dependency bên ngoài sau một interface ổn định — để thay, A/B test và rollback độc lập với code nghiệp vụ.
- Trên critical path, thiết kế fallback trước happy path: timeout, giá trị mặc định/rule, circuit breaker. Câu hỏi quan trọng nhất là "khi model chết thì hệ thống làm gì".
- Với ML nhạy cảm kinh doanh, chèn guardrail do con người kiểm soát giữa output model và quyết định cuối. Model đề xuất, chính sách quyết định.
- Bật shadow mode cho model mới và monitoring drift cho model đang chạy. Model sẽ xấu đi theo thời gian — phải có kế hoạch retrain và rollback đã kiểm thử.