Khi xây dựng hệ thống RAG (Retrieval-Augmented Generation) với LlamaIndex, việc đánh giá chất lượng truy xuất (retrieval) là yếu tố quyết định hiệu suất của toàn bộ pipeline. Trong bài viết này, HolySheep AI sẽ hướng dẫn bạn từ cơ bản đến nâng cao về các metrics đánh giá retrieval quality trong LlamaIndex, kèm theo code thực chiến và những kinh nghiệm xương máu từ các dự án production.

Bảng so sánh: HolySheep vs Official API vs Relay Services

Tiêu chíHolySheep AIOfficial OpenAIProxy/Relay Services
Giá GPT-4o$2/MTok (tiết kiệm 85%+)$15/MTok$3-8/MTok
Thanh toánWeChat, Alipay, USDTVisa/MasterCardHạn chế
Độ trễ trung bình< 50ms100-300ms80-200ms
Tín dụng miễn phíCó ($5-10)$5Ít hoặc không
Tỷ giá¥1 = $1Thanh toán USDKhác nhau
Hỗ trợ LlamaIndex100% tương thích100%Phụ thuộc

📌 Đăng ký tại đây để nhận tín dụng miễn phí và trải nghiệm API nhanh nhất cho các dự án LlamaIndex của bạn.

1. Tổng quan về Retrieval Quality Metrics trong LlamaIndex

Trong quá trình phát triển hệ thống RAG, tôi đã thử nghiệm nhiều phương pháp đánh giá và nhận thấy rằng việc hiểu đúng các metrics giúp tiết kiệm hàng tuần debug. LlamaIndex cung cấp bộ công cụ đánh giá tích hợp thông qua module evaluation.

1.1. Các Metrics cốt lõi


Cài đặt dependencies cần thiết

pip install llama-index llama-index-llms-holysheep llama-index-embeddings-holysheep

Import các metrics cần thiết

from llama_index.core.evaluation import ( RetrievalEvaluator, RetrieverEvaluator, BatchRetrieverEvaluator, EvaluationResult ) from llama_index.core.evaluation.retrieval.metrics import ( HitRate, MRR, Precision, Recall, F1, NDCG ) from llama_index.core.retrievers import VectorIndexRetriever from llama_index.core import VectorStoreIndex

1.2. Thiết lập HolySheep AI cho LlamaIndex


import os
from llama_index.llms.holysheep import HolySheep
from llama_index.embeddings.holysheep import HolySheepEmbedding

Cấu hình HolySheep API -base_url bắt buộc theo format HolySheep

os.environ["HOLYSHEEP_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY" llm = HolySheep( model="gpt-4o", api_key=os.environ["HOLYSHEEP_API_KEY"], base_url="https://api.holysheep.ai/v1" # ⚠️ BẮT BUỘC )

Embedding model cho retrieval

embed_model = HolySheepEmbedding( model="text-embedding-3-small", api_key=os.environ["HOLYSHEEP_API_KEY"], base_url="https://api.holysheep.ai/v1" # ⚠️ BẮT BUỘC )

Từ kinh nghiệm thực chiến, việc cấu hình đúng base_url là điều tối quan trọng. Nhiều developer mới sử dụng sai endpoint dẫn đến lỗi authentication kéo dài hàng giờ.

2. Hit Rate (Tỷ lệ truy xuất thành công)

Hit Rate là metric đơn giản nhất - đếm số lần relevant documents xuất hiện trong top-k kết quả trả về. Công thức:


from llama_index.core.evaluation.retrieval.metrics import HitRate

def calculate_hit_rate(retriever, eval_dataset, k_values=[1, 3, 5, 10]):
    """
    Tính Hit Rate@K cho retrieval system
    
    Args:
        retriever: LlamaIndex retriever instance
        eval_dataset: Dataset chứa queries và relevant docs
        k_values: List các giá trị K cần đánh giá
    
    Returns:
        Dict[str, float]: Hit Rate tại mỗi K
    """
    hit_rates = {}
    
    for k in k_values:
        hits = 0
        total_queries = len(eval_dataset)
        
        for item in eval_dataset:
            query = item["query"]
            relevant_docs = set(item["relevant_docs"])
            
            # Thực hiện retrieval
            retrieved_nodes = retriever.retrieve(query)
            retrieved_ids = {node.node_id for node in retrieved_nodes[:k]}
            
            # Kiểm tra có ít nhất 1 hit không
            if retrieved_ids & relevant_docs:  # Intersection
                hits += 1
        
        hit_rates[f"hit_rate@{k}"] = hits / total_queries
    
    return hit_rates

Ví dụ sử dụng với HolySheep

sample_dataset = [ { "query": "Cách triển khai RAG với LlamaIndex?", "relevant_docs": ["doc_123", "doc_456", "doc_789"] }, { "query": "Tối ưu hóa embedding model cho tiếng Việt", "relevant_docs": ["doc_234", "doc_567"] } ] results = calculate_hit_rate(my_retriever, sample_dataset, k_values=[1, 3, 5]) print(results)

Output: {'hit_rate@1': 0.5, 'hit_rate@3': 1.0, 'hit_rate@5': 1.0}

Trong thực tế, tôi thường đặt ngưỡng Hit Rate@3 >= 0.85 cho production systems. Nếu thấp hơn, cần kiểm tra lại chunking strategy hoặc embedding quality.

3. Mean Reciprocal Rank (MRR)

MRR đo lường vị trí của document relevant đầu tiên. Nếu document relevant nằm ở vị trí thứ 3, MRR = 1/3 = 0.333. Metric này quan trọng khi thứ tự kết quả ảnh hưởng đến trải nghiệm người dùng.


def calculate_mrr(retriever, eval_dataset):
    """
    Tính Mean Reciprocal Rank (MRR)
    
    MRR = (1/N) * Σ(1/rank_i)
    trong đó rank_i là vị trí của document relevant đầu tiên
    """
    reciprocal_ranks = []
    
    for item in eval_dataset:
        query = item["query"]
        relevant_docs = set(item["relevant_docs"])
        
        retrieved_nodes = retriever.retrieve(query)
        
        # Tìm vị trí của document relevant đầu tiên
        rank = None
        for idx, node in enumerate(retrieved_nodes, start=1):
            if node.node_id in relevant_docs:
                rank = idx
                break
        
        # Nếu có hit, tính reciprocal rank
        if rank:
            reciprocal_ranks.append(1.0 / rank)
        else:
            reciprocal_ranks.append(0.0)
    
    mrr = sum(reciprocal_ranks) / len(reciprocal_ranks)
    return mrr

Sử dụng với LlamaIndex Evaluation Module

from llama_index.core.evaluation import generate_qa_embedding_pairs

Tạo evaluation dataset tự động

qa_pairs = generate_qa_embedding_pairs( nodes=your_documents, # List of Document nodes llm=llm, # HolySheep LLM instance num_questions_per_chunk=3 )

Đánh giá với built-in MRR metric

evaluator = RetrievalEvaluator( retriever=my_retriever, metrics=[MRR()] ) eval_results = await evaluator.aevaluate(qa_pairs) print(f"MRR Score: {eval_results.score}")

4. Precision và Recall tại K

Hai metrics kinh điển nhưng vẫn cực kỳ quan trọng trong đánh giá retrieval:


from typing import List, Set

def calculate_precision_recall_at_k(
    retrieved_docs: List[str],
    relevant_docs: Set[str],
    k: int
) -> tuple[float, float]:
    """
    Tính Precision@K và Recall@K
    
    Precision@K = |Retrieved_K ∩ Relevant| / K
    Recall@K = |Retrieved_K ∩ Relevant| / |Relevant|
    """
    retrieved_k = set(retrieved_docs[:k])
    
    true_positives = len(retrieved_k & relevant_docs)
    
    precision = true_positives / k if k > 0 else 0.0
    recall = true_positives / len(relevant_docs) if relevant_docs else 0.0
    
    return precision, recall

def comprehensive_retrieval_evaluation(retriever, eval_dataset, k=10):
    """
    Đánh giá toàn diện retrieval system với nhiều metrics
    """
    results = {
        "precision_at_k": [],
        "recall_at_k": [],
        "f1_at_k": [],
        "hit_count": 0
    }
    
    for item in eval_dataset:
        query = item["query"]
        relevant_docs = set(item["relevant_docs"])
        
        retrieved_nodes = retriever.retrieve(query)
        retrieved_ids = [node.node_id for node in retrieved_nodes[:k]]
        
        precision, recall = calculate_precision_recall_at_k(
            retrieved_ids, relevant_docs, k
        )
        
        results["precision_at_k"].append(precision)
        results["recall_at_k"].append(recall)
        
        # F1 score
        if precision + recall > 0:
            f1 = 2 * (precision * recall) / (precision + recall)
            results["f1_at_k"].append(f1)
        
        # Hit count
        if set(retrieved_ids) & relevant_docs:
            results["hit_count"] += 1
    
    # Tính trung bình
    avg_precision = sum(results["precision_at_k"]) / len(results["precision_at_k"])
    avg_recall = sum(results["recall_at_k"]) / len(results["recall_at_k"])
    avg_f1 = sum(results["f1_at_k"]) / len(results["f1_at_k"]) if results["f1_at_k"] else 0
    
    return {
        "precision@10": round(avg_precision, 4),
        "recall@10": round(avg_recall, 4),
        "f1@10": round(avg_f1, 4),
        "hit_rate@10": round(results["hit_count"] / len(eval_dataset), 4),
        "total_queries": len(eval_dataset)
    }

Chạy evaluation với HolySheep-powered retriever

evaluation_results = comprehensive_retrieval_evaluation( retriever=my_retriever, eval_dataset=eval_dataset, k=10 ) print("=" * 50) print("RETRIEVAL QUALITY EVALUATION RESULTS") print("=" * 50) for metric, value in evaluation_results.items(): print(f"{metric}: {value}")

5. NDCG (Normalized Discounted Cumulative Gain)

NDCG là metric tinh vi hơn, tính đến mức độ relevance của từng document (không chỉ binary relevant/not-relevant). Đây là metric tôi khuyên dùng cho production systems vì nó phản ánh chất lượng ranking thực sự.


import math

def calculate_dcg(relevance_scores: List[float]) -> float:
    """Tính Discounted Cumulative Gain"""
    dcg = 0.0
    for i, rel in enumerate(relevance_scores, start=1):
        dcg += rel / math.log2(i + 1)  # i+1 vì position bắt đầu từ 1
    return dcg

def calculate_ndcg(retriever, eval_dataset, k=10) -> float:
    """
    Tính NDCG@K (Normalized Discounted Cumulative Gain)
    
    NDCG = DCG / IDCG
    trong đó IDCG = DCG của ranking lý tưởng (sắp xếp relevance giảm dần)
    """
    ndcg_scores = []
    
    for item in eval_dataset:
        query = item["query"]
        relevant_docs = set(item["relevant_docs"])
        relevance_mapping = item.get("relevance_scores", {})
        
        retrieved_nodes = retriever.retrieve(query)
        
        # Tính relevance scores cho retrieved docs
        retrieved_relevance = []
        for node in retrieved_nodes[:k]:
            # Relevance có thể là binary (0/1) hoặc graded (0-5)
            rel_score = relevance_mapping.get(node.node_id, 0)
            retrieved_relevance.append(rel_score)
        
        # DCG của retrieval thực tế
        dcg = calculate_dcg(retrieved_relevance)
        
        # IDCG: sắp xếp relevance giảm dần
        ideal_relevance = sorted(
            [relevance_mapping.get(doc_id, 0) for doc_id in relevant_docs],
            reverse=True
        )[:k]
        idcg = calculate_dcg(ideal_relevance)
        
        # NDCG
        ndcg = dcg / idcg if idcg > 0 else 0.0
        ndcg_scores.append(ndcg)
    
    return sum(ndcg_scores) / len(ndcg_scores)

Sử dụng LlamaIndex Evaluation với custom metrics

from llama_index.core.evaluation import ( EmbeddingQAFinetuneEvaluator, generate_question_context_pairs )

Tạo evaluation pairs với relevance scores

eval_pairs = generate_question_context_pairs( nodes=documents, llm=llm, num_questions_per_chunk=2, with_scores=True # Yêu cầu relevance scores ) ndcg_score = calculate_ndcg(my_retriever, eval_pairs, k=10) print(f"NDCG@10: {ndcg_score:.4f}")

6. Batch Evaluation với HolySheep AI

Để đánh giá hiệu quả trên large-scale datasets, sử dụng batch evaluation với parallel processing:


import asyncio
from concurrent.futures import ThreadPoolExecutor
from llama_index.core.evaluation import BatchRetrieverEvaluator

async def comprehensive_batch_evaluation(
    retriever,
    eval_queries: List[dict],
    holy_sheep_llm: HolySheep
) -> dict:
    """
    Đánh giá batch với HolySheep AI cho LLM-based evaluation
    """
    evaluator = BatchRetrieverEvaluator(
        retriever=retriever,
        metrics=[HitRate(), MRR(), Precision(), Recall()],
        llm=holy_sheep_llm
    )
    
    # Chạy evaluation
    results = await evaluator.aevaluate_batch(eval_queries)
    
    # Tổng hợp kết quả
    summary = {
        "total_queries": len(results),
        "avg_hit_rate": sum(r.hit_rate for r in results) / len(results),
        "avg_mrr": sum(r.mrr for r in results) / len(results),
        "avg_precision": sum(r.precision for r in results) / len(results),
        "avg_recall": sum(r.recall for r in results) / len(results)
    }
    
    return summary

Ví dụ thực tế với HolySheep

async def main(): # Khởi tạo HolySheep llm = HolySheep( model="gpt-4o", api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1" ) # Tạo test dataset (1000 queries) test_queries = [ {"query": q, "relevant_docs": get_relevant_docs(q)} for q in load_test_queries() # Hàm load từ file/database ] # Chạy evaluation results = await comprehensive_batch_evaluation( retriever=my_vector_retriever, eval_queries=test_queries, holy_sheep_llm=llm ) print(f"Batch Evaluation Complete!") print(f"Total queries: {results['total_queries']}") print(f"Average Hit Rate: {results['avg_hit_rate']:.2%}") print(f"Average MRR: {results['avg_mrr']:.4f}") return results

Chạy với asyncio

asyncio.run(main())

Lỗi thường gặp và cách khắc phục

Lỗi 1: AuthenticationError khi sử dụng HolySheep API


❌ SAI - Sử dụng endpoint không đúng

llm = HolySheep( api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.openai.com/v1" # ❌ SAI: Endpoint OpenAI )

✅ ĐÚNG - Sử dụng HolySheep endpoint

llm = HolySheep( api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1" # ✅ ĐÚNG )

Hoặc set qua environment variable

os.environ["HOLYSHEEP_API_BASE"] = "https://api.holysheep.ai/v1"

Nguyên nhân: Many tutorials online use OpenAI endpoints as examples, causing confusion. Cách khắc phục: Luôn verify base_url là https://api.holysheep.ai/v1 cho mọi request.

Lỗi 2: Chunking strategy không phù hợp dẫn đến recall thấp


❌ SAI - Chunk quá lớn hoặc quá nhỏ không tối ưu

from llama_index.core import SimpleDirectoryReader

Chunk quá lớn (2048 tokens) - khó tìm thấy chính xác

node_parser = SimpleNodeParser.from_defaults( chunk_size=2048, chunk_overlap=0 )

Chunk quá nhỏ (128 tokens) - mất context

node_parser = SimpleNodeParser.from_defaults( chunk_size=128, chunk_overlap=16 )

✅ ĐÚNG - Tối ưu theo use case

from llama_index.core.node_parser import SentenceSplitter

Với QA systems: 256-512 tokens

node_parser = SentenceSplitter( chunk_size=512, chunk_overlap=50, # 10% overlap để tránh mất thông tin separator="\n\n" )

Với semantic search: 256-384 tokens với overlap cao hơn

node_parser = SentenceSplitter( chunk_size=384, chunk_overlap=76, # 20% overlap separator=["\n", ". ", "? "] )

Nguyên nhân: Chunk size ảnh hưởng trực tiếp đến precision/recall tradeoff. Cách khắc phục: Test với nhiều chunk sizes (128, 256, 384, 512, 768) và chọn based on your specific use case. Sử dụng code evaluation ở trên để benchmark.

Lỗi 3: Embedding model không phù hợp với ngôn ngữ/tài liệu


❌ SAI - Dùng embedding mặc định không tối ưu cho tiếng Việt

from llama_index.core import VectorStoreIndex index = VectorStoreIndex.from_documents( documents, embed_model="text-embedding-ada-002" # ❌ Không tối ưu cho tiếng Việt )

✅ ĐÚNG - Sử dụng embedding model tương thích

from llama_index.embeddings.holysheep import HolySheepEmbedding

text-embedding-3-small: 1536 dimensions, hỗ trợ đa ngôn ngữ tốt

embed_model = HolySheepEmbedding( model="text-embedding-3-small", api_key=os.environ["HOLYSHEEP_API_KEY"], base_url="https://api.holysheep.ai/v1", dimensions=1536 # Tối ưu cho semantic search )

Hoặc sử dụng multilingual embedding

embed_model = HolySheepEmbedding( model="text-embedding-3-large", # Hỗ trợ 100+ ngôn ngữ api_key=os.environ["HOLYSHEEP_API_KEY"], base_url="https://api.holysheep.ai/v1", dimensions=3072 )

Build index với embedding tối ưu

index = VectorStoreIndex.from_documents( documents, embed_model=embed_model )

Nguyên nhân: Default embedding models thường được tối ưu cho English. Cách khắc phục: Sử dụng multilingual embedding models và test lại với evaluation metrics để verify improvement.

Lỗi 4: Không xử lý đúng khi không có relevant documents trong top-K


❌ SAI - Gây divide by zero hoặc crash

def naive_mrr_calculator(retriever, query, relevant_docs): retrieved = retriever.retrieve(query) for idx, node in enumerate(retrieved, 1): if node.node_id in relevant_docs: return 1.0 / idx # OK return 1.0 / len(retrieved) # ❌ Sai: không có relevant docs

✅ ĐÚNG - Handle edge cases

def robust_mrr_calculator(retriever, query, relevant_docs, max_k=10): retrieved = retriever.retrieve(query)[:max_k] if not relevant_docs: return 0.0 # Không có ground truth - skip if not retrieved: return 0.0 # Không có kết quả - edge case for idx, node in enumerate(retrieved, 1): if node.node_id in relevant_docs: return 1.0 / idx return 0.0 # Relevant docs không nằm trong top-K

Tương tự cho NDCG - luôn kiểm tra edge cases

def safe_ndcg_calculator(dcg, idcg): if idcg == 0: return 0.0 # Không có ideal results return dcg / idcg

Nguyên nhân: Edge cases trong evaluation gây crash hoặc incorrect scores. Cách khắc phục: Luôn validate inputs và handle empty/null cases trước khi tính toán metrics.

Kết luận và khuyến nghị

Qua hàng trăm experiments với LlamaIndex evaluation metrics, tôi rút ra được những best practices sau:

  1. Baseline trước khi tối ưu: Đo Hit Rate, MRR baseline trước khi thay đổi bất kỳ component nào.
  2. Test với real queries: Synthetic data tốt cho prototyping, nhưng production evaluation cần real user queries.
  3. Monitor theo thời gian: Retrieval quality thay đổi khi documents được thêm/sửa/xóa.
  4. Sử dụng HolySheep AI: Với chi phí chỉ từ $0.42-8/MTok (so với $15+ của OpenAI), bạn có thể chạy extensive evaluation mà không lo về budget.

Thông tin giá và API

ModelGiá (2026)Use Case
GPT-4.1$8/MTokComplex reasoning, evaluation
Claude Sonnet 4.5$15/MTokHigh-quality generation
Gemini 2.5 Flash$2.50/MTokFast evaluation, batch processing
DeepSeek V3.2$0.42/MTokCost-effective embedding, simple tasks

💡 Mẹo từ thực chiến: Sử dụng DeepSeek V3.2 cho embedding evaluation và Gemini 2.5 Flash cho batch quality assessment để tối ưu chi phí evaluation pipeline lên đến 90%.

👉 Đăng ký HolySheep AI — nhận tín dụng miễn phí khi đăng ký