Bi-encoder mã hóa truy vấn và tài liệu độc lập thành hai vector rồi so cosine. Vì tài liệu được embed sẵn lúc ingest, lúc truy vấn chỉ cần một lần encode + tìm láng giềng gần — quét hàng triệu tài liệu trong vài chục mili giây.
Cross-encoder nhận cặp (truy vấn, tài liệu) cùng lúc vào một lần forward và cho ra điểm liên quan. Attention chạy chéo giữa từ của truy vấn và từ của tài liệu nên nó thấy được quan hệ mà hai vector rời không mã hóa nổi (phủ định, điều kiện, tham chiếu).
bi-encoder: vec(query) · vec(doc) → precompute doc, O(1) mỗi doc
cross-encoder: score(model([query, doc])) → không precompute được, 1 forward mỗi docĐiểm mấu chốt: cross-encoder không precompute được, vì điểm phụ thuộc vào cả cặp. Chấm 1 triệu tài liệu nghĩa là 1 triệu lần forward — bất khả thi khi truy vấn.
Nên kiến trúc chuẩn là hai tầng: bi-encoder (+BM25) lấy top-50 đến top-100 ưu tiên recall, rồi cross-encoder xếp lại và giữ top-3 đến top-5 ưu tiên precision. Thêm khoảng 100-300ms nhưng thường cải thiện chất lượng câu trả lời rõ hơn mọi thứ chỉnh prompt.