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 qualitygeneration 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:

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