Một hệ thống hỏi đáp trả lời thiếu thông tin, nhưng nguyên nhân có thể nằm trước bước sinh câu trả lời: retriever chưa đưa tài liệu cần thiết vào ngữ cảnh. Nếu chỉ sửa prompt, bạn có thể đang tối ưu một bước không nhận đủ dữ liệu để làm việc.
Bài này xây phép tính nhỏ cho danh sách ID được truy hồi. Dữ liệu hoàn toàn tự tạo, không gọi model hay dịch vụ tìm kiếm. Code đã chạy bằng Python 3.9.6; tài liệu được đối chiếu ngày 26/10/2026. Kết quả bên dưới chỉ kiểm tra công thức, không phải benchmark RAG thực tế.
1. Tách độ bao phủ khỏi vị trí kết quả đầu tiên
Tài liệu đánh giá xếp hạng của Elastic mô tả Recall@k và mean reciprocal rank. Recall@k đo phần tài liệu liên quan xuất hiện trong k kết quả đầu so với tập liên quan chuẩn. Reciprocal rank lấy nghịch đảo vị trí tài liệu liên quan đầu tiên; MRR là trung bình qua các query.
Trong bài, cả hai đều được cắt tại k. Nếu không có kết quả liên quan trong top k, reciprocal rank bằng 0. Chúng ta gọi trung bình đó là MRR@k để không nhầm với phép đánh giá nhìn toàn bộ danh sách.
Hai cấu hình có thể tìm đủ cùng một tập tài liệu nhưng đưa kết quả hữu ích đầu tiên lên các vị trí khác nhau. Ngược lại, có một kết quả tốt đứng đầu chưa chắc đã lấy đủ các tài liệu cần cho câu hỏi nhiều phần. Vì vậy, cần ghi mục tiêu trước khi chọn metric chính.
2. Tạo fixture có tập liên quan rõ ràng
Lưu thành retrieval_metrics.py rồi chạy bằng python3 retrieval_metrics.py:
def at_k(ranked, relevant, k):
if type(k) is not int or k <= 0:
raise ValueError("k phai la so nguyen duong")
if not isinstance(relevant, set) or not relevant:
raise ValueError("Can tap nhan lien quan khong rong")
if any(not isinstance(x, str) or not x for x in ranked + list(relevant)):
raise ValueError("ID phai la chuoi khong rong")
if len(ranked) != len(set(ranked)):
raise ValueError("Danh sach xep hang dang lap ID")
top = ranked[:k]
recall = len(set(top) & relevant) / len(relevant)
reciprocal_rank = next(
(1 / position for position, item in enumerate(top, 1)
if item in relevant),
0.0,
)
return recall, reciprocal_rank
fixtures = [
(["doc-x", "doc-a", "doc-b"], {"doc-a", "doc-b"}),
(["doc-c", "doc-y"], {"doc-c"}),
(["doc-x", "doc-y"], {"doc-z"}),
]
results = [at_k(ranked, relevant, 3) for ranked, relevant in fixtures]
print(results)
print("mean Recall@3:", round(sum(x[0] for x in results) / len(results), 4))
print("MRR@3:", round(sum(x[1] for x in results) / len(results), 4))
Kết quả đã kiểm tra của ba query lần lượt là (1.0, 0.5), (1.0, 1.0) và (0.0, 0.0). Trung bình Recall@3 là 0.6667, MRR@3 là 0.5. Đây là trung bình theo query: mỗi query có trọng số như nhau, không phải gộp toàn bộ số hit rồi chia một lần.
Hàm từ chối ID lặp để một lỗi của pipeline không làm kết quả khó giải thích. Nó cũng không tự chấm query có tập relevant rỗng. Nếu bộ dữ liệu có loại query không có câu trả lời, hãy thiết kế phép đánh giá và chính sách tính điểm riêng cho loại đó trước khi chạy.
3. Kiểm tra metric bằng thay đổi nhỏ
Ở query đầu, chuyển doc-a lên vị trí thứ nhất nhưng vẫn giữ cả doc-a và doc-b trong top 3. Recall không đổi, reciprocal rank tăng từ 0.5 lên 1. Đây là cách tự kiểm tra rằng code phân biệt được bao phủ và thứ tự.
Tiếp theo giảm k xuống 1 với danh sách gốc. Không có ID liên quan trong phần được giữ lại, nên cả hai điểm bằng 0. Cuối cùng thử danh sách truy hồi rỗng với tập nhãn vẫn có tài liệu: đó là một ca bỏ sót cần được nhìn thấy, không phải lý do tự bỏ query khỏi mẫu số.
4. Xác định đúng đơn vị được đánh giá
Nếu một tài liệu được chia thành nhiều chunk, hãy quyết định nhãn chuẩn ở cấp tài liệu hay chunk. Không tính nhiều chunk của cùng một tài liệu như nhiều tài liệu liên quan nếu mục tiêu là đo bao phủ tài liệu. Khi đổi cách chunk, các ID và bộ nhãn cũng cần được quản lý tương ứng.
Với kho tài liệu nội bộ, có thể bắt đầu bằng một nhóm câu hỏi đã được phép sử dụng, danh sách tài liệu cần có và người rà nhãn. Ghi lại những câu hỏi cần nhiều nguồn, những câu chỉ có một nguồn chính và những câu không nên được trả lời từ kho hiện tại.
Kiểm tra trong pipeline thật
Lưu query ID, danh sách ID theo thứ tự và cấu hình truy hồi, sau đó tính điểm ngoài luồng phục vụ người dùng. Giữ cùng bộ query và nhãn khi so sánh hai cấu hình. Rà từng query bị giảm điểm để biết thay đổi đến từ thiếu tài liệu hay xếp hạng.
Đừng nâng k tùy ý chỉ để Recall đẹp hơn. Thêm tài liệu còn ảnh hưởng lượng ngữ cảnh và khả năng đưa thông tin không cần thiết vào bước sau. Bài không có số liệu để chọn k tốt cho sản phẩm của bạn; đó là bước thử nghiệm cần thêm bằng chứng.
Lỗi thường gặp và bước tiếp theo
Nhầm chunk ID với document ID, bỏ query không có hit khỏi mẫu số hoặc gọi reciprocal rank của một query là MRR đều làm báo cáo sai ý nghĩa. Ngoài metric, hãy đọc lại tài liệu được lấy ra: nhãn chuẩn sai hoặc thiếu khiến điểm số không phản ánh nhu cầu thật.
Khi tầng truy hồi đã được đánh giá rõ, tiếp tục kiểm tra câu trả lời có dùng đúng nguồn hay không. Một điểm retrieval tốt chưa tự chứng minh câu trả lời được hỗ trợ đầy đủ. Tách hai bước giúp bạn sửa đúng nơi thay vì chỉ đổi prompt theo cảm giác.

