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 AI | Official OpenAI | Proxy/Relay Services |
|---|---|---|---|
| Giá GPT-4o | $2/MTok (tiết kiệm 85%+) | $15/MTok | $3-8/MTok |
| Thanh toán | WeChat, Alipay, USDT | Visa/MasterCard | Hạn chế |
| Độ trễ trung bình | < 50ms | 100-300ms | 80-200ms |
| Tín dụng miễn phí | Có ($5-10) | $5 | Ít hoặc không |
| Tỷ giá | ¥1 = $1 | Thanh toán USD | Khác nhau |
| Hỗ trợ LlamaIndex | 100% tương thích | 100% | 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:
- Baseline trước khi tối ưu: Đo Hit Rate, MRR baseline trước khi thay đổi bất kỳ component nào.
- Test với real queries: Synthetic data tốt cho prototyping, nhưng production evaluation cần real user queries.
- Monitor theo thời gian: Retrieval quality thay đổi khi documents được thêm/sửa/xóa.
- 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
| Model | Giá (2026) | Use Case |
|---|---|---|
| GPT-4.1 | $8/MTok | Complex reasoning, evaluation |
| Claude Sonnet 4.5 | $15/MTok | High-quality generation |
| Gemini 2.5 Flash | $2.50/MTok | Fast evaluation, batch processing |
| DeepSeek V3.2 | $0.42/MTok | Cost-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%.