Hệ khuyến nghị 7 — Kiến trúc production & serving
Từ mô hình tốt đến hệ thống chạy được
Sáu bài trước xây được mô hình biết chấm điểm mức phù hợp giữa khách và sản phẩm — từ collaborative filtering, matrix factorization, hybrid đến deep learning và ranking/đánh giá. Nhưng một mô hình chạy trong notebook không phải một hệ khuyến nghị. Sản phẩm thật phải trả lời được câu hỏi khắc nghiệt: "Cho khách hàng X đang mở app lúc 21h03, trong 50 mili-giây, chọn 5 sản phẩm tốt nhất từ 3.000 sản phẩm — và không được vi phạm quy định."
Khoảng cách giữa "mô hình" và "hệ thống" chính là kiến trúc serving. Đây là nơi mọi thứ hội tụ: độ trễ, quy mô, tính nhất quán offline–online, tuân thủ nghiệp vụ, và vòng phản hồi để mô hình tự cải thiện. Bài này mô tả kiến trúc production đã trở thành chuẩn công nghiệp và cách áp dụng vào bối cảnh ngân hàng NCB.
Vấn đề cốt lõi là ma sát giữa độ chính xác và độ trễ. Mô hình càng chính xác thường càng nặng. Nhưng ta không thể chấm điểm 3.000 sản phẩm bằng mô hình nặng trong 50ms. Lời giải của cả ngành là: chia nhỏ bài toán thành nhiều giai đoạn, mỗi giai đoạn thu hẹp không gian ứng viên và tăng dần độ tinh vi.
Kiến trúc nhiều giai đoạn (multi-stage) — xương sống
Đây là mô hình chuẩn ở YouTube, Netflix, Amazon và mọi hệ recommender quy mô lớn. Dòng chảy đi từ tập item khổng lồ, lọc dần qua từng tầng, đến vài kết quả cuối cùng hiển thị cho khách.
Nguyên tắc thiết kế: giai đoạn sớm ưu tiên tốc độ (recall cao), giai đoạn sau ưu tiên độ chính xác (precision cao). Candidate generation dùng mô hình rẻ để không bỏ sót; ranking dùng mô hình đắt trên tập nhỏ để chọn tinh; re-rank áp quy tắc để đảm bảo an toàn nghiệp vụ.
Giai đoạn 1 — Candidate Generation (retrieval)
Nhiệm vụ: từ hàng nghìn đến hàng triệu item, lọc nhanh còn vài trăm ứng viên tiềm năng. Yêu cầu độ trễ vài mili-giây, nên không thể chấm điểm từng item bằng mô hình nặng. Ta cần một phép truy hồi (retrieval) gần như O(log n) hoặc O(1).
Các nguồn candidate thường kết hợp nhiều kênh:
- ANN trên embedding (chủ lực): dùng two-tower model để precompute embedding cho mọi item, dựng chỉ mục ANN (approximate nearest neighbor — láng giềng gần đúng, xem vec-01). Lúc serve, tính user embedding rồi truy top-K item gần nhất trong không gian vector. Đây là kênh cho recall diện rộng.
- Co-occurrence / item-to-item: "khách mua sản phẩm A thường tiếp cận B" — bảng đồng xuất hiện precompute từ lịch sử, tra cứu tức thì.
- Rule / heuristic: item mới ra mắt, sản phẩm chiến dịch đang chạy, sản phẩm phổ biến (popularity) — chèn thẳng để đảm bảo phủ.
Kết quả các kênh được hợp nhất (union) và khử trùng lặp thành ~vài trăm ứng viên. Điểm mấu chốt: giai đoạn này không cần xếp hạng đúng, chỉ cần không bỏ sót item tốt. Nếu candidate generation đã loại nhầm item lý tưởng, không tầng nào phía sau cứu được.
# Minh hoạ (KHÔNG phải SQL chạy được) — pseudocode retrieval đa kênh
def generate_candidates(customer, k=300):
u = user_tower(customer.features) # user embedding, tính real-time
ann_hits = ann_index.search(u, top_k=200) # từ item embedding precompute
cooc_hits = cooccurrence_table.get(customer.recent_items, limit=100)
rule_hits = campaign_items() + popular_items(limit=50)
return dedup(ann_hits + cooc_hits + rule_hits)[:k]
Giai đoạn 2 — Ranking
Nhiệm vụ: chấm điểm chính xác vài trăm ứng viên và sắp thứ hạng. Vì tập ứng viên đã nhỏ, ta được phép dùng mô hình nặng và giàu feature — gradient boosting (LambdaMART), Wide&Deep, DeepFM, hay mạng ranking chuyên dụng (xem rec-06).
Đầu vào ranking là (user × item × context): đặc trưng khách (phân khúc, thu nhập, lịch sử), đặc trưng item (loại sản phẩm, lãi suất, điều kiện), và đặc trưng ngữ cảnh (kênh, thời điểm, thiết bị, chiến dịch). Đầu ra là một điểm phản ánh mục tiêu kinh doanh — với ngân hàng thường là xác suất chuyển đổi (conversion) hoặc kỳ vọng giá trị (expected value), chứ không chỉ "click".
Chi phí tính toán cao hơn nhiều lần candidate generation, nhưng chỉ chạy trên vài trăm item nên tổng độ trễ vẫn kiểm soát được. Ranking là nơi "trí tuệ thật" của hệ thống nằm — nó quyết định thứ tự khách thực sự nhìn thấy.
Giai đoạn 3 — Re-ranking & Business Rules
Điểm ranking cao nhất chưa chắc là gợi ý được phép hiển thị. Giai đoạn re-ranking điều chỉnh danh sách theo ràng buộc nghiệp vụ — cực kỳ quan trọng với ngân hàng:
- Lọc tuân thủ & đủ điều kiện (eligibility/compliance): loại sản phẩm khách không đủ điều kiện (ví dụ khách dưới 18 tuổi không được gợi thẻ tín dụng; khách đã có khoản vay quá hạn không gợi vay mới). Đây là hard filter — không thoả hiệp, xem gov-07 và sec-08.
- Đa dạng hoá (diversification): tránh 5 gợi ý cùng một dòng sản phẩm. Kỹ thuật MMR (maximal marginal relevance) cân bằng độ liên quan với độ khác biệt để danh sách không đơn điệu.
- Quy tắc nghiệp vụ & chiến dịch: ưu tiên/ghim sản phẩm chiến dịch, áp trần số lần gợi cùng một sản phẩm (frequency cap), loại sản phẩm khách vừa từ chối.
- Loại item không phù hợp: đã sở hữu, đang trong quá trình xử lý, hết hạn, ngừng bán.
# Minh hoạ (KHÔNG phải SQL chạy được) — pipeline re-rank
def rerank(customer, scored_items):
items = [i for i in scored_items if is_eligible(customer, i)] # hard filter tuân thủ
items = [i for i in items if not already_owns(customer, i)]
items = diversify_mmr(items, lambda_=0.7) # đa dạng hoá
items = apply_business_rules(customer, items) # ghim chiến dịch, freq cap
return items[:5]
Thứ tự rất quan trọng: hard filter tuân thủ chạy trước, tối ưu trải nghiệm chạy sau. Không bao giờ để một quy tắc "đẹp mắt" đẩy một item vi phạm quy định lọt ra ngoài.
Online vs Batch vs Near-real-time
Không phải hệ khuyến nghị nào cũng cần trả lời trong 50ms. Chọn chế độ serving theo yêu cầu nghiệp vụ:
| Chế độ | Cách hoạt động | Độ trễ | Khi nào dùng |
|---|---|---|---|
| Batch | Tính trước gợi ý cho toàn bộ khách theo lịch (hằng đêm/tuần), lưu vào bảng, ứng dụng chỉ tra cứu | Truy vấn ~ms, độ tươi trễ giờ/ngày | Nhiều gợi ý ngân hàng: NBA hiển thị khi khách vào app/gọi tổng đài, danh sách chào bán qua email/SMS |
| Online (real-time) | Tính gợi ý ngay lúc có request, chạy đủ 3 giai đoạn tại chỗ | 10–100ms | Cần phản ứng theo hành vi tức thời, ngữ cảnh phiên (session) |
| Near-real-time | Feature cập nhật qua stream (Kafka), gợi ý tính lại trong vài giây/phút | giây–phút | Cân bằng: bắt được sự kiện mới mà không cần chi phí online đầy đủ |
Điểm thực chiến cho ngân hàng: đừng mặc định online. Rất nhiều gợi ý sản phẩm (Next Best Action) chỉ cần batch định kỳ là đủ dùng — khách hàng ngân hàng không đổi nhu cầu vay/tiết kiệm theo phút. Batch rẻ hơn nhiều, dễ kiểm toán hơn (danh sách gợi ý cố định trong ngày, dễ review tuân thủ), và độ trễ tra cứu gần như bằng 0. Chỉ dùng online cho phần thực sự cần phản ứng theo phiên.
Kiến trúc lai (hybrid) rất phổ biến: batch tính danh sách candidate + điểm cơ sở hằng đêm, còn phần re-rank theo ngữ cảnh (kênh, thời điểm, chiến dịch đang chạy) làm online lúc hiển thị. Vừa rẻ vừa tươi.
Feature Store & Embedding Store — nhất quán offline–online
Đây là hạ tầng làm hoặc phá cả hệ thống. Vấn đề kinh điển: train/serving skew — feature tính lúc train (offline, trên warehouse) khác feature tính lúc serve (online, real-time), khiến mô hình chính xác trong offline nhưng lệch trong production.
Feature store (mlops-03) giải quyết bằng cách định nghĩa feature một lần, phục vụ hai chế độ từ cùng logic: offline store (cho train) và online store (key-value độ trễ thấp cho serve). Cùng công thức, cùng giá trị.
Với recommender, thêm một thành phần đặc thù — embedding store / ANN index:
Nguyên tắc: item embedding precompute và đánh chỉ mục offline (item ít đổi, tính lại theo batch); user embedding tính online (phụ thuộc ngữ cảnh phiên). ANN index được nạp lại (hot-swap) khi có bản embedding mới, tránh gián đoạn serving. Chi tiết ANN xem vec-01.
MLOps cho recommender
Recommender là mô hình sống — dữ liệu và hành vi trôi (drift) liên tục, nên cần vòng đời MLOps đầy đủ, không chỉ deploy một lần:
- Pipeline train/retrain: lịch retrain định kỳ (item mới, hành vi mới) và trigger theo tín hiệu drift. Item embedding thường retrain thường xuyên hơn kiến trúc mô hình.
- Model registry & deploy: quản lý phiên bản mô hình + phiên bản embedding, deploy an toàn qua mlops-05. Với rec cần versioning cả ba thứ đồng bộ: mô hình ranking, embedding item, và ANN index — lệch phiên bản giữa chúng gây kết quả sai âm thầm.
- Giám sát (monitoring): theo dõi cả sức khoẻ kỹ thuật (độ trễ p99, tỉ lệ lỗi, coverage của candidate) lẫn chất lượng nghiệp vụ (CTR, conversion online) — xem phần dưới.
- Rollback: khi mô hình mới tụt chỉ số online, phải quay về phiên bản cũ trong phút, không phải ngày.
Feedback loop & logging
Recommender tự cải thiện nhờ chính dữ liệu nó sinh ra. Nhưng đây cũng là nơi ẩn cạm bẫy nguy hiểm nhất.
Logging đúng cách: log không chỉ tương tác (click/convert) mà cả bối cảnh gợi ý — đã hiển thị item nào, ở vị trí nào, phiên bản mô hình nào, điểm bao nhiêu. Không có log "đã hiển thị gì" thì không thể phân biệt "khách không thích" với "khách không được thấy".
Cạm bẫy feedback loop bias: mô hình chỉ học từ item nó đã hiển thị. Item không bao giờ được gợi thì không có tương tác dương → mô hình càng bỏ quên → vòng xoáy tự củng cố (rich-get-richer). Hệ quả: mất đa dạng, đuôi dài (long tail) chết dần, và position bias — khách click nhiều ở vị trí trên chỉ vì nó ở trên, không phải vì tốt hơn. Cách giảm nhẹ: dành một tỉ lệ nhỏ exploration (gợi ngẫu nhiên có kiểm soát), ghi lại propensity (xác suất hiển thị) để hiệu chỉnh, và dùng inverse propensity weighting khi train.
A/B testing cho recommender
Offline metric (NDCG, recall@k ở rec-06) chỉ là chỉ báo — online A/B test mới là trọng tài cuối. Nhiều mô hình "thắng offline" lại thua online vì offline không mô phỏng được vòng phản hồi và ngữ cảnh thật.
Nguyên tắc A/B cho rec (nền tảng ở stat-07):
- Randomization đúng đơn vị: chia theo customer, không theo request — nếu không cùng một khách rơi vào cả hai nhánh, gây rò rỉ.
- Chọn metric kinh doanh làm chính: với ngân hàng là conversion / doanh thu / số hợp đồng, không phải CTR đơn thuần (CTR cao mà không chuyển đổi là vô ích).
- Guardrail metric: canh chỉ số không được xấu đi (khiếu nại, opt-out, đa dạng sản phẩm).
- Đủ thời gian & cỡ mẫu: hành vi ngân hàng có độ trễ dài (khách cân nhắc vay hàng tuần), test ngắn dễ cho kết luận sai — coi chừng các bẫy ở stat-08.
Caching, fallback và cold start
Production phải chịu tải và không bao giờ được trả về màn hình trống.
- Caching: cache kết quả gợi ý theo khách trong TTL ngắn (vài phút–giờ) giảm tải mô hình đáng kể, đặc biệt với batch-hybrid. Cache cả ANN result cho user embedding ổn định.
- Fallback nhiều tầng: nếu mô hình ranking timeout → dùng điểm cơ sở đã precompute; nếu vẫn lỗi → popularity fallback (sản phẩm phổ biến theo phân khúc). Luôn có đáy để rơi vào.
- Cold start: khách mới không có lịch sử → không có collaborative signal. Chiến lược: gợi theo content/demographic (phân khúc, tuổi, thu nhập) và popularity của nhóm tương tự, chuyển dần sang cá nhân hoá khi tích luỹ hành vi. Xem rec-04.
Giám sát chất lượng online
Đo liên tục để phát hiện suy giảm trước khi khách phàn nàn:
| Nhóm | Chỉ số | Cảnh báo khi |
|---|---|---|
| Kỹ thuật | Độ trễ p99, tỉ lệ lỗi/timeout | Vượt SLA (ví dụ p99 > 100ms) |
| Chất lượng | CTR, conversion online, coverage | Tụt so với baseline / nhánh cũ |
| Sức khoẻ hệ | Tỉ lệ fallback, catalog coverage, diversity | Fallback tăng đột biến, coverage co lại |
| Rủi ro | Tỉ lệ item bị filter tuân thủ, opt-out | Item vi phạm lọt qua, opt-out tăng |
Coverage và diversity co lại là dấu hiệu sớm của feedback loop bias — bắt được ở đây rẻ hơn nhiều so với để mô hình thoái hoá vài tháng.
Use case thực tế
Bối cảnh: NCB triển khai Next Best Action (NBA) gợi sản phẩm cho ~2 triệu khách hàng bán lẻ, catalog ~120 sản phẩm/gói, hiển thị khi khách mở mobile app và làm gợi ý cho tổng đài viên.
Kiến trúc chọn (batch-hybrid):
- Batch hằng đêm: chạy pipeline candidate generation cho toàn bộ 2 triệu khách. Kênh ANN (two-tower embedding, ~64 chiều) + co-occurrence + rule chiến dịch → mỗi khách ~150 ứng viên. Ranking bằng gradient boosting chấm theo xác suất chuyển đổi 30 ngày. Lưu top-20 (đã có điểm) vào bảng
nba_candidatestrong feature store. - Online lúc hiển thị: tra 20 candidate từ bảng (~2ms), re-rank theo ngữ cảnh — lọc eligibility (KYC, tuổi, tình trạng nợ), loại sản phẩm đã sở hữu, đa dạng hoá, ghim sản phẩm chiến dịch đang chạy → xuất top-5. Tổng độ trễ < 20ms.
- Logging & feedback: log 5 item hiển thị + vị trí + điểm + phiên bản mô hình vào data lake; ghép với tương tác (mở chi tiết, đăng ký) để train vòng sau.
Kết quả A/B (minh hoạ): nhánh multi-stage vs baseline rule-based cũ, chia theo customer, chạy 6 tuần. Conversion trên gợi ý tăng từ 2,1% lên 3,4% (+62% tương đối), guardrail opt-out không tăng có ý nghĩa. Tỉ lệ fallback popularity giữ dưới 3%. Quyết định: rollout 100%. Bài học rút ra: phần lớn lợi ích đến từ re-ranking eligibility đúng (giảm gợi sai đối tượng) hơn là từ mô hình ranking phức tạp hơn — đúng tinh thần "tối ưu tầng đắt nhất chưa chắc đáng nhất".
Ghi nhớ
- Kiến trúc nhiều giai đoạn là chuẩn: candidate generation (nhanh, recall cao, ANN) → ranking (đắt, chính xác, trên tập nhỏ) → re-rank & business rules. Tốc độ ở đầu, chính xác ở giữa, an toàn nghiệp vụ ở cuối.
- Candidate generation không cần xếp đúng, chỉ cần không bỏ sót; loại nhầm ở đây thì không tầng nào cứu được.
- Re-ranking là bắt buộc với ngân hàng: hard filter tuân thủ/eligibility chạy trước, tối ưu trải nghiệm (đa dạng hoá, chiến dịch) chạy sau.
- Đừng mặc định online: nhiều NBA ngân hàng chỉ cần batch định kỳ — rẻ, dễ kiểm toán, độ trễ tra cứu ~0. Batch-hybrid (batch tính điểm + online re-rank ngữ cảnh) là điểm ngọt.
- Feature/embedding store đảm bảo nhất quán offline–online, tránh train/serving skew. Item embedding precompute offline + ANN index; user embedding tính online.
- Versioning đồng bộ cả ba: mô hình ranking, embedding item, ANN index — lệch phiên bản gây sai âm thầm.
- Feedback loop tự cải thiện nhưng gây bias: log cả "đã hiển thị gì", cảnh giác position bias và vòng xoáy rich-get-richer; giữ chút exploration.
- Online A/B test là trọng tài cuối, không phải offline metric; chia theo customer, metric chính là conversion/doanh thu, có guardrail.
- Luôn có fallback nhiều tầng (điểm precompute → popularity) và chiến lược cold start (content/demographic) để không bao giờ trả màn hình trống.
Bài viết liên quan
Phân biệt AI/ML/DL, các kiểu học máy, quy trình ML end-to-end và thuật ngữ nền tảng.
Chất lượng dữ liệu, xử lý thiếu/ngoại lai, mã hoá, chuẩn hoá, tạo đặc trưng và tránh data leakage.
Mô hình ngôn ngữ lớn hoạt động ra sao, token, context window, tham số sinh, và kỹ thuật viết prompt.
Retrieval-Augmented Generation, vector database, tool calling, agent và MCP để xây ứng dụng LLM thực tế.