Khi xây dựng hệ thống RAG (Retrieval-Augmented Generation) cho production, việc đánh giá hiệu quả không chỉ dừng ở việc "hệ thống có hoạt động không" mà còn phải đo lường được retrieval quality và generation quality một cách có hệ thống. Trong bài viết này, tôi sẽ chia sẻ kinh nghiệm thực chiến khi đội ngũ của tôi di chuyển từ hạ tầng OpenAI/Anthropic sang HolySheep AI — nền tảng với tỷ giá ¥1=$1 và độ trễ trung bình dưới 50ms — đồng thời hướng dẫn chi tiết cách triển khai evaluation framework hoàn chỉnh.
Vì Sao Cần Đánh Giá RAG System?
Trước khi đi vào chi tiết kỹ thuật, hãy hiểu rõ bối cảnh: trong dự án chatbot hỗ trợ khách hàng của tôi, chúng tôi phải xử lý 50,000+ truy vấn mỗi ngày. Ban đầu, hệ thống dùng OpenAI với chi phí $0.03/1K tokens cho embedding và $0.03/1K tokens cho GPT-3.5-turbo. Sau 6 tháng, hóa đơn hàng tháng lên tới $4,200 — một con số khiến CTO phải yêu cầu tối ưu hóa ngay lập tức.
Quyết định chuyển sang HolySheep không chỉ vì giá rẻ. Họ cung cấp:
- Tỷ giá ¥1=$1 — Tiết kiệm 85%+ so với API chính thức
- Hỗ trợ WeChat/Alipay — Thuận tiện cho đội ngũ Trung Quốc
- Độ trễ <50ms — Đáp ứng yêu cầu real-time
- Tín dụng miễn phí khi đăng ký — Test trước khi cam kết
Retrieval Metrics: Đo Lường Chất Lượng Tìm Kiếm
1. Precision@K và Recall@K
Precision@K đo tỷ lệ documents được trả về thực sự relevant. Recall@K đo tỷ lệ relevant documents được retrieve ra. Công thức:
import numpy as np
from typing import List, Dict, Tuple
class RetrievalMetrics:
"""Evaluation framework cho RAG retrieval component"""
def __init__(self, k_values: List[int] = [1, 3, 5, 10]):
self.k_values = k_values
def precision_at_k(
self,
retrieved_docs: List[str],
relevant_docs: List[str],
k: int
) -> float:
"""Tính Precision@K
Args:
retrieved_docs: Danh sách documents được retrieve (đã sort theo score)
relevant_docs: Danh sách ground truth relevant documents
k: Số lượng documents lấy top-K
Returns:
Precision score tại K
"""
if k <= 0:
return 0.0
top_k = retrieved_docs[:k]
if not top_k:
return 0.0
relevant_set = set(relevant_docs)
num_relevant = sum(1 for doc in top_k if doc in relevant_set)
return num_relevant / k
def recall_at_k(
self,
retrieved_docs: List[str],
relevant_docs: List[str],
k: int
) -> float:
"""Tính Recall@K"""
if not relevant_docs:
return 0.0
top_k = retrieved_docs[:k]
relevant_set = set(relevant_docs)
num_relevant = sum(1 for doc in top_k if doc in relevant_set)
return num_relevant / len(relevant_docs)
def mean_reciprocal_rank(
self,
retrieved_docs: List[str],
relevant_docs: List[str]
) -> float:
"""MRR - Mean Reciprocal Rank
Đo vị trí của relevant document đầu tiên
MRR = 1/N * Σ(1/rank_i)
"""
relevant_set = set(relevant_docs)
for idx, doc in enumerate(retrieved_docs, 1):
if doc in relevant_set:
return 1.0 / idx
return 0.0
def ndcg_at_k(
self,
retrieved_docs: List[str],
relevance_scores: Dict[str, float],
k: int
) -> float:
"""NDCG@K - Normalized Discounted Cumulative Gain
Đánh giá có tính đến thứ tự và mức độ relevance
"""
# DCG@K = Σ(rel_i / log2(i+1))
dcg = 0.0
for idx, doc in enumerate(retrieved_docs[:k], 1):
rel = relevance_scores.get(doc, 0.0)
dcg += rel / np.log2(idx + 1)
# IDCG@K = optimal DCG (sắp xếp relevance giảm dần)
sorted_relevance = sorted(relevance_scores.values(), reverse=True)[:k]
idcg = sum(rel / np.log2(idx + 1) for idx, rel in enumerate(sorted_relevance, 1))
if idcg == 0:
return 0.0
return dcg / idcg
def hit_rate_at_k(
self,
retrieved_docs: List[str],
relevant_docs: List[str],
k: int
) -> float:
"""Hit Rate@K - Có ít nhất 1 relevant document trong top-K?"""
top_k = retrieved_docs[:k]
relevant_set = set(relevant_docs)
return 1.0 if any(doc in relevant_set for doc in top_k) else 0.0
def evaluate_batch(
self,
test_cases: List[Dict]
) -> Dict[str, float]:
"""Đánh giá batch với nhiều queries
Args:
test_cases: List of {
'query': str,
'retrieved': List[str],
'relevant': List[str],
'relevance_scores': Dict[str, float]
}
"""
results = {f'precision@{k}': [] for k in self.k_values}
results.update({f'recall@{k}': [] for k in self.k_values})
results['mrr'] = []
results['ndcg'] = []
results['hit_rate'] = []
for case in test_cases:
retrieved = case['retrieved']
relevant = case['relevant']
relevance_scores = case.get('relevance_scores', {d: 1.0 for d in relevant})
for k in self.k_values:
results[f'precision@{k}'].append(
self.precision_at_k(retrieved, relevant, k)
)
results[f'recall@{k}'].append(
self.recall_at_k(retrieved, relevant, k)
)
results['mrr'].append(self.mean_reciprocal_rank(retrieved, relevant))
results['ndcg'].append(self.ndcg_at_k(retrieved, relevance_scores, max(self.k_values)))
results['hit_rate'].append(self.hit_rate_at_k(retrieved, relevant, max(self.k_values)))
# Tính mean
summary = {metric: np.mean(scores) for metric, scores in results.items()}
return summary
Demo usage
if __name__ == "__main__":
metrics = RetrievalMetrics(k_values=[1, 3, 5, 10])
test_cases = [
{
'query': 'Cách đổi mật khẩu?',
'retrieved': ['doc_password_change', 'doc_login', 'doc_security', 'doc_faq'],
'relevant': ['doc_password_change', 'doc_security'],
'relevance_scores': {
'doc_password_change': 1.0,
'doc_security': 0.7,
'doc_login': 0.3,
'doc_faq': 0.1
}
},
{
'query': 'Liên hệ hỗ trợ',
'retrieved': ['doc_contact', 'doc_email', 'doc_phone', 'doc_ticket'],
'relevant': ['doc_contact', 'doc_email'],
'relevance_scores': {
'doc_contact': 1.0,
'doc_email': 0.9,
'doc_phone': 0.5,
'doc_ticket': 0.4
}
}
]
summary = metrics.evaluate_batch(test_cases)
print("=== Retrieval Evaluation Summary ===")
for metric, score in summary.items():
print(f"{metric}: {score:.4f}")
2. Embedding Quality với HolySheep API
Để đo lường retrieval quality, trước tiên cần có embeddings chất lượng. Dưới đây là integration với HolySheep:
import requests
import numpy as np
from typing import List, Union, Optional
import time
class HolySheepEmbeddingClient:
"""HolySheep AI Embedding Client với monitoring
Pricing (2026):
- text-embedding-3-small: $0.42/1M tokens
- text-embedding-3-large: $1.67/1M tokens
Compare: OpenAI $0.02/1K = $20/1M (tiết kiệm 85%+)
"""
def __init__(
self,
api_key: str,
base_url: str = "https://api.holysheep.ai/v1",
model: str = "text-embedding-3-small",
timeout: int = 30
):
self.api_key = api_key
self.base_url = base_url.rstrip('/')
self.model = model
self.timeout = timeout
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
# Metrics tracking
self.request_count = 0
self.total_tokens = 0
self.latencies = []
def embed(
self,
texts: Union[str, List[str]],
batch_size: int = 100,
show_progress: bool = True
) -> List[np.ndarray]:
"""Tạo embeddings với batching và monitoring
Args:
texts: Chuỗi hoặc list chuỗi cần embed
batch_size: Số lượng texts xử lý mỗi batch
show_progress: Hiển thị progress
Returns:
List của numpy arrays (embeddings)
"""
if isinstance(texts, str):
texts = [texts]
all_embeddings = []
total_batches = (len(texts) + batch_size - 1) // batch_size
for i in range(0, len(texts), batch_size):
batch = texts[i:i + batch_size]
start_time = time.time()
response = self._make_request(batch)
elapsed_ms = (time.time() - start_time) * 1000
self.latencies.append(elapsed_ms)
embeddings = [
np.array(item['embedding'], dtype=np.float32)
for item in response['data']
]
all_embeddings.extend(embeddings)
# Track metrics
self.request_count += 1
self.total_tokens += response.get('usage', {}).get('total_tokens', 0)
if show_progress:
batch_num = i // batch_size + 1
print(f"Batch {batch_num}/{total_batches} | "
f"Latency: {elapsed_ms:.1f}ms | "
f"Tokens: {response.get('usage', {}).get('total_tokens', 0)}")
return all_embeddings
def _make_request(self, texts: List[str]) -> dict:
"""Thực hiện API request với retry logic"""
url = f"{self.base_url}/embeddings"
payload = {
"model": self.model,
"input": texts
}
for attempt in range(3):
try:
response = self.session.post(
url,
json=payload,
timeout=self.timeout
)
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
print(f"Timeout attempt {attempt + 1}/3")
if attempt == 2:
raise RuntimeError("Embedding request timeout after 3 retries")
except requests.exceptions.HTTPError as e:
if response.status_code == 429:
time.sleep(2 ** attempt)
else:
raise RuntimeError(f"HTTP Error: {e}")
raise RuntimeError("Max retries exceeded")
def get_stats(self) -> dict:
"""Lấy statistics của API usage"""
avg_latency = np.mean(self.latencies) if self.latencies else 0
p95_latency = np.percentile(self.latencies, 95) if self.latencies else 0
# Calculate cost (HolySheep pricing)
cost_per_million = 0.42 if 'small' in self.model else 1.67
estimated_cost = (self.total_tokens / 1_000_000) * cost_per_million
return {
'total_requests': self.request_count,
'total_tokens': self.total_tokens,
'avg_latency_ms': round(avg_latency, 2),
'p95_latency_ms': round(p95_latency, 2),
'estimated_cost_usd': round(estimated_cost, 4)
}
def cosine_similarity(self, a: np.ndarray, b: np.ndarray) -> float:
"""Tính cosine similarity giữa 2 vectors"""
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
==================== INTEGRATION EXAMPLE ====================
def demo_rag_evaluation():
"""Demo: Evaluate RAG retrieval với HolySheep"""
client = HolySheepEmbeddingClient(
api_key="YOUR_HOLYSHEEP_API_KEY",
model="text-embedding-3-small"
)
# Corpus documents
corpus = [
"Cách đổi mật khẩu tài khoản",
"Quy trình đăng nhập hệ thống",
"Chính sách bảo mật và quyền riêng tư",
"Hướng dẫn sử dụng API",
"Cách liên hệ bộ phận hỗ trợ khách hàng",
"Điều khoản sử dụng dịch vụ"
]
# Test queries
queries = [
"làm sao đổi password?",
"cách liên hệ support"
]
# Ground truth (relevant docs for each query)
ground_truth = {
"làm sao đổi password?": ["Cách đổi mật khẩu tài khoản"],
"cách liên hệ support": ["Cách liên hệ bộ phận hỗ trợ khách hàng"]
}
print("=== Embedding Corpus ===")
corpus_embeddings = client.embed(corpus, batch_size=10)
print(f"\nCorpus embeddings shape: {len(corpus_embeddings)} x {corpus_embeddings[0].shape[0]}")
print("\n=== Embedding Queries ===")
query_embeddings = client.embed(queries, batch_size=10)
print("\n=== Similarity Search ===")
evaluator = RetrievalMetrics(k_values=[1, 3])
for i, query in enumerate(queries):
query_emb = query_embeddings[i]
# Tính similarity với tất cả corpus
similarities = [
client.cosine_similarity(query_emb, doc_emb)
for doc_emb in corpus_embeddings
]
# Sort theo similarity
sorted_indices = np.argsort(similarities)[::-1]
ranked_docs = [corpus[idx] for idx in sorted_indices]
print(f"\nQuery: '{query}'")
print("Top 3 retrieved:")
for rank, idx in enumerate(sorted_indices[:3], 1):
print(f" {rank}. {corpus[idx]} (sim: {similarities[idx]:.4f})")
# Evaluate
relevant = ground_truth[query]
precision = evaluator.precision_at_k(ranked_docs, relevant, 3)
recall = evaluator.recall_at_k(ranked_docs, relevant, 3)
mrr = evaluator.mean_reciprocal_rank(ranked_docs, relevant)
print(f" → Precision@3: {precision:.2f}")
print(f" → Recall@3: {recall:.2f}")
print(f" → MRR: {mrr:.2f}")
# Print usage stats
print("\n=== HolySheep Usage Stats ===")
stats = client.get_stats()
print(f"Total requests: {stats['total_requests']}")
print(f"Total tokens: {stats['total_tokens']}")
print(f"Average latency: {stats['avg_latency_ms']}ms")
print(f"P95 latency: {stats['p95_latency_ms']}ms")
print(f"Estimated cost: ${stats['estimated_cost_usd']}")
# Compare with OpenAI pricing
openai_cost = (stats['total_tokens'] / 1_000_000) * 20 # $20/1M
savings = ((openai_cost - stats['estimated_cost_usd']) / openai_cost) * 100
print(f"\n💰 Savings vs OpenAI: {savings:.1f}%")
if __name__ == "__main__":
# Thay YOUR_HOLYSHEEP_API_KEY bằng API key thực tế
# Đăng ký tại: https://www.holysheep.ai/register
demo_rag_evaluation()
Generation Quality: Đánh Giá Output của Generator
Sau khi đã đánh giá retrieval, bước tiếp theo là đánh giá chất lượng sinh của LLM. Tôi sử dụng combination của automated metrics và human evaluation:
import requests
import json
import time
from typing import List, Dict, Optional, Callable
from dataclasses import dataclass
import re
@dataclass
class GenerationMetrics:
"""Metrics cho generation quality evaluation"""
bleu_score: float = 0.0
rouge_l_score: float = 0.0
semantic_similarity: float = 0.0
response_length: int = 0
generation_time_ms: float = 0.0
class HolySheepLLMClient:
"""HolySheep AI LLM Client với generation evaluation
Pricing (2026/1M tokens):
- GPT-4.1: $8.00
- Claude Sonnet 4.5: $15.00
- Gemini 2.5 Flash: $2.50
- DeepSeek V3.2: $0.42
Compare: OpenAI GPT-4o $15/1M → Tiết kiệm 85%+ với DeepSeek V3.2
"""
def __init__(
self,
api_key: str,
base_url: str = "https://api.holysheep.ai/v1",
default_model: str = "deepseek-v3.2"
):
self.api_key = api_key
self.base_url = base_url.rstrip('/')
self.default_model = default_model
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
# Pricing lookup (USD per 1M tokens)
self.pricing = {
'gpt-4.1': 8.00,
'claude-sonnet-4.5': 15.00,
'gemini-2.5-flash': 2.50,
'deepseek-v3.2': 0.42,
'gpt-4o': 15.00,
'gpt-4o-mini': 0.60
}
# Metrics tracking
self.request_log = []
def generate(
self,
prompt: str,
system_prompt: Optional[str] = None,
model: Optional[str] = None,
temperature: float = 0.7,
max_tokens: int = 2048,
stop: Optional[List[str]] = None,
**kwargs
) -> Dict:
"""Generate response với full metrics tracking
Returns:
Dict chứa response, usage stats, và latency
"""
model = model or self.default_model
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
messages.append({"role": "user", "content": prompt})
payload = {
"model": model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens,
}
if stop:
payload["stop"] = stop
payload.update(kwargs)
start_time = time.time()
try:
response = self.session.post(
f"{self.base_url}/chat/completions",
json=payload,
timeout=60
)
response.raise_for_status()
result = response.json()
elapsed_ms = (time.time() - start_time) * 1000
# Extract response
choice = result['choices'][0]
generated_text = choice['message']['content']
# Usage stats
usage = result.get('usage', {})
prompt_tokens = usage.get('prompt_tokens', 0)
completion_tokens = usage.get('completion_tokens', 0)
total_tokens = usage.get('total_tokens', 0)
# Calculate cost
cost_per_million = self.pricing.get(model, 0.42)
estimated_cost = (total_tokens / 1_000_000) * cost_per_million
log_entry = {
'timestamp': time.time(),
'model': model,
'prompt_tokens': prompt_tokens,
'completion_tokens': completion_tokens,
'total_tokens': total_tokens,
'latency_ms': elapsed_ms,
'cost_usd': estimated_cost,
'finish_reason': choice.get('finish_reason', 'unknown')
}
self.request_log.append(log_entry)
return {
'text': generated_text,
'usage': {
'prompt_tokens': prompt_tokens,
'completion_tokens': completion_tokens,
'total_tokens': total_tokens
},
'latency_ms': elapsed_ms,
'cost_usd': estimated_cost,
'finish_reason': choice.get('finish_reason', 'unknown'),
'model': model
}
except requests.exceptions.HTTPError as e:
error_detail = response.json().get('error', {}).get('message', str(e))
raise RuntimeError(f"API Error: {error_detail}")
def batch_generate(
self,
prompts: List[str],
system_prompt: Optional[str] = None,
model: Optional[str] = None,
**kwargs
) -> List[Dict]:
"""Batch generate cho evaluation (tuần tự)"""
results = []
for i, prompt in enumerate(prompts):
print(f"Generating {i+1}/{len(prompts)}...")
result = self.generate(
prompt,
system_prompt=system_prompt,
model=model,
**kwargs
)
results.append(result)
time.sleep(0.1) # Rate limiting
return results
def get_cost_summary(self) -> Dict:
"""Tổng hợp chi phí"""
if not self.request_log:
return {'total_cost': 0, 'total_tokens': 0, 'total_requests': 0}
total_cost = sum(log['cost_usd'] for log in self.request_log)
total_tokens = sum(log['total_tokens'] for log in self.request_log)
avg_latency = sum(log['latency_ms'] for log in self.request_log) / len(self.request_log)
return {
'total_cost': round(total_cost, 6),
'total_tokens': total_tokens,
'total_requests': len(self.request_log),
'avg_latency_ms': round(avg_latency, 2)
}
class RAGEvaluator:
"""RAG System Evaluator - kết hợp retrieval + generation"""
def __init__(
self,
embedding_client: HolySheepEmbeddingClient,
llm_client: HolySheepLLMClient
):
self.embedding = embedding_client
self.llm = llm_client
def evaluate_rag_response(
self,
query: str,
context_docs: List[str],
generated_response: str,
reference_response: Optional[str] = None
) -> Dict:
"""Đánh giá toàn diện RAG response
Metrics:
- Faithfulness: Response có faithful với context không?
- Answer Relevancy: Response có relevant với query không?
- Context Precision: Context có chính xác không?
- Hallucination Score: Có hallucination không?
"""
# 1. Faithfulness Score (kiểm tra response có dựa trên context)
faithfulness_prompt = f"""Đánh giá xem câu trả lời sau có dựa trên các tài liệu context không.
Trả lời CHỈ bằng số từ 0-100 (0 = hoàn toàn không dựa, 100 = hoàn toàn dựa).
Context:
{chr(10).join(f"- {doc}" for doc in context_docs)}
Câu trả lời:
{generated_response}
Score:"""
faithfulness_result = self.llm.generate(
faithfulness_prompt,
model="deepseek-v3.2", # Model rẻ nhất cho scoring
max_tokens=10,
temperature=0.1
)
try:
faithfulness_score = float(
re.search(r'\d+', faithfulness_result['text']).group()
) / 100
except:
faithfulness_score = 0.5
# 2. Answer Relevancy (response có trả lời query không)
relevancy_prompt = f"""Đánh giá xem câu trả lời sau có giải quyết câu hỏi không.
Trả lời CHỈ bằng số từ 0-100.
Câu hỏi: {query}
Câu trả lời: {generated_response}
Score:"""
relevancy_result = self.llm.generate(
relevancy_prompt,
model="deepseek-v3.2",
max_tokens=10,
temperature=0.1
)
try:
relevancy_score = float(
re.search(r'\d+', relevancy_result['text']).group()
) / 100
except:
relevancy_score = 0.5
# 3. Hallucination Detection
hallucination_prompt = f"""Kiểm tra câu trả lời sau có thông tin sai lệch hoặc bị bịa đặt không.
Trả lời CHỈ bằng số 0-100 (0 = có nhiều hallucination, 100 = không có hallucination).
Context:
{chr(10).join(f"- {doc}" for doc in context_docs)}
Câu trả lời:
{generated_response}
Score:"""
hallucination_result = self.llm.generate(
hallucination_prompt,
model="deepseek-v3.2",
max_tokens=10,
temperature=0.1
)
try:
hallucination_score = float(
re.search(r'\d+', hallucination_result['text']).group()
) / 100
except:
hallucination_score = 0.5
# 4. Overall Quality Score (weighted average)
overall_score = (
faithfulness_score * 0.4 +
relevancy_score * 0.4 +
hallucination_score * 0.2
)
return {
'faithfulness': round(faithfulness_score, 3),
'relevancy': round(relevancy_score, 0.85),
'hallucination': round(hallucination_score, 3),
'overall_score': round(overall_score, 3),
'response_length': len(generated_response),
'generation_latency_ms': self.llm.request_log[-1]['latency_ms'] if self.llm.request_log else 0
}
def run_evaluation_benchmark(
self,
test_dataset: List[Dict]
) -> Dict:
"""Run full evaluation benchmark
Args:
test_dataset: List of {
'query': str,
'context': List[str],
'reference': Optional[str]
}
"""
results = []
print(f"Running evaluation on {len(test_dataset)} test cases...\n")
for i, case in enumerate(test_dataset):
query = case['query']
context = case['context']
# Generate RAG response
rag_prompt = f"""Dựa trên các tài liệu sau, trả lời câu hỏi:
Tài liệu:
{chr(10).join(f"- {doc}" for doc in context)}
Câu hỏi: {query}
Câu trả:"""
response = self.llm.generate(
rag_prompt,
model="deepseek-v3.2",
temperature=0.3,
max_tokens=500
)
# Evaluate
evaluation = self.evaluate_rag_response(
query=query,
context_docs=context,
generated_response=response['text'],
reference_response=case.get('reference')
)
results.append({
'query': query,
'response': response['text'],
'metrics': evaluation
})
print(f"Case {i+1}/{len(test_dataset)}")
print(f" Query: {query[:50]}...")
print(f" Faithfulness: {evaluation['faithfulness']:.2f}")
print(f" Relevancy: {evaluation['relevancy']:.2f}")
print(f" Overall: {evaluation['overall_score']:.2f}")
print()
# Aggregate results
avg_faithfulness = sum(r['metrics']['faithfulness'] for r in results) / len(results)
avg_relevancy = sum(r['metrics']['relevancy'] for r in results) / len(results)
avg_hallucination = sum(r['metrics']['hallucination'] for r in results) / len(results)
avg_overall = sum(r['metrics']['overall_score'] for r in results) / len(results)
cost_summary = self.llm.get_cost_summary()
return {
'n_test_cases': len(results),
'metrics': {
'avg_faithfulness': round(avg_faithfulness, 3),
'avg_relevancy': round(avg_relevancy, 3),
'avg_hallucination': round(avg_hallucination, 3),
'avg_overall_score': round(avg_overall, 3)
},
'cost_summary': cost_summary,
'detailed_results': results
}
==================== DEMO ====================
def demo_rag_evaluation():
"""Demo RAG evaluation pipeline"""
# Initialize clients
embedding_client = HolySheepEmbeddingClient(
api_key="YOUR_HOLYSHEEP_API_KEY",
model="text-embedding-3-small"
)
llm_client = HolySheepLLMClient(
api_key="YOUR_HOLYSHEEP_API_KEY",
model="deepseek-v3.2" # $0.42/1M tokens - tiết kiệm 85%+
)
# Test dataset
test_dataset = [
{
'query': 'Cách đổi mật khẩu?',
'context': [
'Để đổi mật khẩu, vào phần Cài đặt tài khoản.',
'Nhấn vào "Bảo mật" và chọn "Đổi mật khẩu".',
'Nhập mật khẩu cũ và mật khẩu mới 2 lần.',
'Mật khẩu ph