Mở đầu: Bài toán thực tế từ dự án E-commerce

Tháng 9 năm ngoái, tôi nhận được một cuộc gọi từ đội ngũ data của một sàn thương mại điện tử lớn tại Việt Nam. Họ đang gặp vấn đề nan giải: hệ thống BI hiện tại với Power BI và Tableau tạo ra hàng ngàn báo cáo, nhưng đội ngũ kinh doanh phải mất 2-3 ngày để hiểu và đưa ra quyết định từ dữ liệu. "Chúng tôi cần một trợ lý AI có thể trả lời câu hỏi kinh doanh bằng ngôn ngữ tự nhiên, không phải SQL hay DAX", đó là yêu cầu của Head of Analytics. Sau 3 tháng triển khai, hệ thống RAG (Retrieval-Augmented Generation) tích hợp với Power BI và Tableau của họ xử lý hơn 50,000 truy vấn mỗi ngày với độ trễ trung bình chỉ 120ms. Bài viết này sẽ chia sẻ toàn bộ kiến trúc, code mẫu, và bài học kinh nghiệm thực chiến từ dự án đó.

Tại sao cần AI cho Business Intelligence

Traditional BI tools như Power BI và Tableau đã giải quyết được bài toán trực quan hóa dữ liệu, nhưng chúng vẫn đòi hỏi người dùng phải có kiến thức kỹ thuật nhất định. Với sự bùng nổ của LLM (Large Language Model), chúng ta có thể tạo ra một lớp giao diện ngôn ngữ tự nhiên giúp:

Kiến trúc tổng thể: RAG + BI Integration

Đây là kiến trúc tôi đã triển khai thành công cho nhiều doanh nghiệp:

┌─────────────────────────────────────────────────────────────────┐
│                      USER INTERFACE LAYER                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │   Chatbot   │  │   Voice     │  │   Power BI / Tableau    │  │
│  │   Web App   │  │   Assistant │  │   Embedded Q&A          │  │
│  └─────────────┘  └─────────────┘  └─────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                      API GATEWAY LAYER                           │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │              Flask/FastAPI + Rate Limiting + Auth            ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    LLM ORCHESTRATION LAYER                       │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────┐   │
│  │  Query       │  │  Context     │  │  Response            │   │
│  │  Understanding│  │  Retrieval  │  │  Generation          │   │
│  └──────────────┘  └──────────────┘  └──────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
                              │
              ┌───────────────┼───────────────┐
              ▼               ▼               ▼
    ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐
    │ HolySheep   │  │ Power BI    │  │ Tableau         │
    │ LLM API     │  │ REST API    │  │ Hyper API       │
    │ <50ms      │  │             │  │                 │
    └─────────────┘  └─────────────┘  └─────────────────┘

Tích hợp Power BI với LLM API

Cài đặt môi trường và cấu hình

# Cài đặt các thư viện cần thiết
pip install powerbiclient requests azure-identity python-dotenv fastapi uvicorn

Cấu trúc thư mục dự án

project/ ├── app/ │ ├── __init__.py │ ├── main.py │ ├── config.py │ ├── routes/ │ │ ├── __init__.py │ │ ├── powerbi.py │ │ └── llm.py │ ├── services/ │ │ ├── __init__.py │ │ ├── llm_service.py │ │ └── powerbi_service.py │ └── utils/ │ ├── __init__.py │ └── helpers.py ├── requirements.txt └── .env

Code tích hợp Power BI + LLM hoàn chỉnh

Đây là code production-ready mà tôi đã sử dụng cho dự án thương mại điện tử:

# config.py - Cấu hình HolySheep API
import os
from dotenv import load_dotenv

load_dotenv()

class Config:
    # HolySheep AI Configuration
    HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
    HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
    HOLYSHEEP_MODEL = "gpt-4.1"  # Hoặc deepseek-v3.2 cho chi phí thấp hơn
    
    # Power BI Configuration
    POWERBI_TENANT_ID = os.getenv("POWERBI_TENANT_ID")
    POWERBI_CLIENT_ID = os.getenv("POWERBI_CLIENT_ID")
    POWERBI_CLIENT_SECRET = os.getenv("POWERBI_CLIENT_SECRET")
    POWERBI_WORKSPACE_ID = os.getenv("POWERBI_WORKSPACE_ID")
    POWERBI_REPORT_ID = os.getenv("POWERBI_REPORT_ID")
    
    # Cấu hình khác
    EMBEDDING_MODEL = "text-embedding-3-small"
    VECTOR_DB_PATH = "./data/vector_store"
    MAX_TOKENS = 2000
    TEMPERATURE = 0.7

config = Config()
# services/llm_service.py - Service gọi HolySheep LLM API
import requests
import json
import time
from typing import Dict, List, Optional
from config import config

class LLMService:
    """Service giao tiếp với HolySheep AI API - Độ trễ <50ms"""
    
    def __init__(self):
        self.base_url = config.HOLYSHEEP_BASE_URL
        self.api_key = config.HOLYSHEEP_API_KEY
        self.model = config.HOLYSHEEP_MODEL
        self.headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
    
    def chat_completion(
        self, 
        messages: List[Dict], 
        model: Optional[str] = None,
        temperature: float = 0.7,
        max_tokens: int = 2000
    ) -> Dict:
        """
        Gọi API chat completion từ HolySheep
        
        Ví dụ pricing 2026:
        - GPT-4.1: $8.00/1M tokens
        - DeepSeek V3.2: $0.42/1M tokens (Tiết kiệm 85%+)
        """
        url = f"{self.base_url}/chat/completions"
        
        payload = {
            "model": model or self.model,
            "messages": messages,
            "temperature": temperature,
            "max_tokens": max_tokens
        }
        
        start_time = time.time()
        
        try:
            response = requests.post(
                url, 
                headers=self.headers, 
                json=payload,
                timeout=30
            )
            response.raise_for_status()
            
            latency_ms = (time.time() - start_time) * 1000
            
            result = response.json()
            result['latency_ms'] = round(latency_ms, 2)
            result['cost_estimate'] = self._estimate_cost(
                result.get('usage', {}).get('total_tokens', 0),
                model or self.model
            )
            
            return {
                "success": True,
                "data": result,
                "latency_ms": result['latency_ms'],
                "cost": result['cost_estimate']
            }
            
        except requests.exceptions.RequestException as e:
            return {
                "success": False,
                "error": str(e),
                "latency_ms": round((time.time() - start_time) * 1000, 2)
            }
    
    def generate_embeddings(self, texts: List[str]) -> List[List[float]]:
        """Tạo embeddings cho RAG"""
        url = f"{self.base_url}/embeddings"
        
        payload = {
            "model": config.EMBEDDING_MODEL,
            "input": texts
        }
        
        start_time = time.time()
        
        response = requests.post(
            url,
            headers=self.headers,
            json=payload
        )
        response.raise_for_status()
        
        result = response.json()
        embeddings = [item['embedding'] for item in result['data']]
        
        return {
            "success": True,
            "embeddings": embeddings,
            "latency_ms": round((time.time() - start_time) * 1000, 2),
            "cost": self._estimate_cost(
                sum(len(t.split()) for t in texts) * 2,  # Ước tính tokens
                config.EMBEDDING_MODEL
            )
        }
    
    def _estimate_cost(self, tokens: int, model: str) -> float:
        """Ước tính chi phí theo model"""
        pricing = {
            "gpt-4.1": 8.00,          # $8/1M tokens
            "claude-sonnet-4.5": 15.00, # $15/1M tokens
            "gemini-2.5-flash": 2.50,   # $2.50/1M tokens
            "deepseek-v3.2": 0.42,      # $0.42/1M tokens
            "text-embedding-3-small": 0.02  # $0.02/1M tokens
        }
        
        price_per_million = pricing.get(model, 1.0)
        return round(tokens / 1_000_000 * price_per_million, 6)

Singleton instance

llm_service = LLMService()
# services/powerbi_service.py - Service tích hợp Power BI
import requests
from azure.identity import ClientSecretCredential
from msal import ConfidentialClientApplication
from typing import Dict, List, Optional
from config import config

class PowerBIService:
    """Service tích hợp Power BI API để truy vấn datasets"""
    
    def __init__(self):
        self.tenant_id = config.POWERBI_TENANT_ID
        self.client_id = config.POWERBI_CLIENT_ID
        self.client_secret = config.POWERBI_CLIENT_SECRET
        self.workspace_id = config.POWERBI_WORKSPACE_ID
        self.report_id = config.POWERBI_REPORT_ID
        self._access_token = None
    
    def _get_access_token(self) -> str:
        """Lấy access token từ Azure AD"""
        if self._access_token:
            return self._access_token
            
        app = ConfidentialClientApplication(
            client_id=self.client_id,
            client_credential=self.client_secret,
            authority=f"https://login.microsoftonline.com/{self.tenant_id}"
        )
        
        result = app.acquire_token_for_client(
            scopes=["https://analysis.windows.net/powerbi/api/.default"]
        )
        
        self._access_token = result['access_token']
        return self._access_token
    
    def execute_dax_query(self, dataset_id: str, dax_query: str) -> Dict:
        """
        Thực thi DAX query trên Power BI Dataset
        
        Ví dụ query: 
        EVALUATE SUMMARIZECOLUMNS('Sales'[Product], 'Sales'[Region], 
               "Total Sales", SUM('Sales'[Amount]))
        """
        url = f"https://api.powerbi.com/v1.0/myorg/groups/{self.workspace_id}/datasets/{dataset_id}/executeQueries"
        
        headers = {
            "Authorization": f"Bearer {self._get_access_token()}",
            "Content-Type": "application/json"
        }
        
        payload = {
            "queries": [
                {
                    "query": dax_query
                }
            ],
            "serializerSettings": {
                "includeNulls": True
            }
        }
        
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()
        
        return response.json()
    
    def get_dataset_tables(self, dataset_id: str) -> List[str]:
        """Lấy danh sách các bảng trong dataset"""
        url = f"https://api.powerbi.com/v1.0/myorg/groups/{self.workspace_id}/datasets/{dataset_id}/tables"
        
        headers = {
            "Authorization": f"Bearer {self._get_access_token()}"
        }
        
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        
        return [table['name'] for table in response.json()['value']]
    
    def get_dataset_schema(self, dataset_id: str) -> Dict:
        """Lấy schema của dataset để hiểu cấu trúc dữ liệu"""
        tables = self.get_dataset_tables(dataset_id)
        schema = {}
        
        for table in tables:
            # Lấy sample data để infer schema
            query = f"""
            EVALUATE
            TOPN(1, '{table}')
            """
            result = self.execute_dax_query(dataset_id, query)
            
            if result.get('results'):
                columns = result['results'][0]['tables'][0]['rows'][0].keys()
                schema[table] = list(columns)
        
        return schema

Singleton instance

powerbi_service = PowerBIService()
# routes/powerbi.py - API endpoints cho Power BI Q&A
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List, Dict, Optional
import json

from services.llm_service import llm_service
from services.powerbi_service import powerbi_service

router = APIRouter(prefix="/api/powerbi", tags=["Power BI"])

class QuestionRequest(BaseModel):
    question: str
    dataset_id: str
    model: Optional[str] = "deepseek-v3.2"  # Model tiết kiệm chi phí

class AnswerResponse(BaseModel):
    answer: str
    sql_query: str
    raw_data: Optional[Dict] = None
    latency_ms: float
    cost_usd: float

Prompt template cho việc chuyển đổi câu hỏi sang DAX

DAX_PROMPT_TEMPLATE = """Bạn là một chuyên gia Power BI và DAX. Dựa trên câu hỏi của người dùng, hãy tạo một DAX query để trả lời. Schema của dataset: {schema} Câu hỏi: {question} Hãy trả về JSON với format: {{ "dax_query": "EVALUATE ...", "explanation": "Giải thích query" }} CHỈ trả về JSON, không giải thích thêm.""" def _get_dataset_context(dataset_id: str, user_question: str) -> str: """Lấy context từ dataset schema""" try: schema = powerbi_service.get_dataset_schema(dataset_id) schema_text = json.dumps(schema, indent=2) # Tạo prompt để LLM hiểu context messages = [ {"role": "system", "content": "Bạn là một data analyst chuyên nghiệp."}, {"role": "user", "content": f"Dataset có các bảng:\n{schema_text}\n\nCâu hỏi: {user_question}"} ] # Dùng DeepSeek V3.2 cho chi phí thấp result = llm_service.chat_completion( messages=messages, model="deepseek-v3.2", temperature=0.3, max_tokens=500 ) if result['success']: return result['data']['choices'][0]['message']['content'] return schema_text except Exception as e: return f"Không thể lấy schema: {str(e)}" @router.post("/ask", response_model=AnswerResponse) async def ask_powerbi(request: QuestionRequest): """ Endpoint chính để hỏi Power BI bằng ngôn ngữ tự nhiên Ví dụ request: { "question": "Doanh thu theo sản phẩm tháng này", "dataset_id": "abc-123", "model": "deepseek-v3.2" } """ try: # Bước 1: Lấy schema context schema_context = _get_dataset_context(request.dataset_id, request.question) # Bước 2: Tạo DAX query từ câu hỏi dax_prompt = DAX_PROMPT_TEMPLATE.format( schema=schema_context, question=request.question ) messages = [ {"role": "user", "content": dax_prompt} ] dax_result = llm_service.chat_completion( messages=messages, model=request.model, temperature=0.1, max_tokens=1000 ) if not dax_result['success']: raise HTTPException(status_code=500, detail=dax_result['error']) # Parse DAX query từ response try: dax_data = json.loads(dax_result['data']['choices'][0]['message']['content']) dax_query = dax_data.get('dax_query', '') except json.JSONDecodeError: dax_query = dax_result['data']['choices'][0]['message']['content'] # Bước 3: Thực thi DAX query try: query_result = powerbi_service.execute_dax_query( request.dataset_id, dax_query ) except Exception as query_error: # Nếu query lỗi, thử với query đơn giản hơn dax_query = f"EVALUATE SUMMARIZECOLUMNS('Sales', 'Sales'[Product], \"Value\", SUM('Sales'[Amount]))" query_result = powerbi_service.execute_dax_query( request.dataset_id, dax_query ) # Bước 4: Tạo câu trả lời tự nhiên từ kết quả answer_prompt = f"""Dựa trên kết quả data sau, hãy trả lời câu hỏi một cách tự nhiên bằng tiếng Việt. Câu hỏi: {request.question} Kết quả: {json.dumps(query_result, indent=2)} Trả lời ngắn gọn, có số liệu cụ thể.""" messages = [ {"role": "user", "content": answer_prompt} ] answer_result = llm_service.chat_completion( messages=messages, model=request.model, temperature=0.7, max_tokens=500 ) answer = answer_result['data']['choices'][0]['message']['content'] if answer_result['success'] else "Xin lỗi, không thể tạo câu trả lời." total_cost = (dax_result.get('cost', 0) + answer_result.get('cost', 0)) total_latency = (dax_result.get('latency_ms', 0) + answer_result.get('latency_ms', 0)) return AnswerResponse( answer=answer, sql_query=dax_query, raw_data=query_result, latency_ms=total_latency, cost_usd=total_cost ) except Exception as e: raise HTTPException(status_code=500, detail=str(e))

Tích hợp Tableau với LLM API

Tableau tích hợp có một số khác biệt so với Power BI. Tôi sử dụng Tableau Server REST API và Hyper API:

# services/tableau_service.py - Service tích hợp Tableau
import tableauserverclient as TSC
from tableauhyperapi import HyperProcess, Connection, Telemetry, TableDefinition, SqlType, Inserter
from typing import Dict, List
import os

class TableauService:
    """Service tích hợp Tableau Server và Hyper API"""
    
    def __init__(self):
        self.server_url = os.getenv("TABLEAU_SERVER_URL")
        self.site_id = os.getenv("TABLEAU_SITE_ID")
        self.token_name = os.getenv("TABLEAU_TOKEN_NAME")
        self.token_secret = os.getenv("TABLEAU_TOKEN_SECRET")
        self._server = None
        self._auth = None
    
    def connect(self):
        """Kết nối đến Tableau Server"""
        self._server = TSC.Server(self.server_url, use_server_version=True)
        
        tableau_auth = TSC.TableauAuth(
            token_name=self.token_name,
            personal_access_token= self.token_secret,
            site_id=self.site_id
        )
        
        self._server.auth.sign_in(tableau_auth)
        return self
    
    def get_workbooks(self) -> List[Dict]:
        """Lấy danh sách workbooks"""
        if not self._server:
            self.connect()
        
        workbooks = []
        for wb in TSC.Pager(self._server.workbooks):
            workbooks.append({
                "id": wb.id,
                "name": wb.name,
                "project_id": wb.project_id,
                "views": [v.name for v in wb.views]
            })
        
        return workbooks
    
    def query_view_data(self, view_id: str, max_rows: int = 100) -> Dict:
        """Lấy data từ một view cụ thể"""
        if not self._server:
            self.connect()
        
        # Query data từ view
        view_item = self._server.views.get_by_id(view_id)
        
        # Get CSV data
        view_data = self._server.views.get_view_data(view_item, "csv")
        
        return {
            "view_id": view_id,
            "view_name": view_item.name,
            "data": view_data.decode('utf-8') if view_data else "",
            "format": "csv"
        }
    
    def create_hyper_file(self, data: List[Dict], output_path: str):
        """Tạo Hyper file từ data (cho việc ghi dữ liệu mới)"""
        with HyperProcess(Telemetry.SEND_USAGE_DATA_TO_TABLEAU) as hyper:
            with Connection(hyper.endpoint, output_path) as connection:
                # Define schema
                if data:
                    columns = []
                    for key in data[0].keys():
                        columns.append(TableDefinition.Column(
                            name=key,
                            type=SqlType.varchar(255)
                        ))
                    
                    table_def = TableDefinition.TableName("Extract", "Extract")
                    connection.execute_command(
                        command=table_def.to_command_create_table()
                    )
                    
                    # Insert data
                    with Inserter(connection, table_def) as inserter:
                        for row in data:
                            inserter.add_row([row[k] for k in row.keys()])
                        inserter.execute()
    
    def disconnect(self):
        """Ngắt kết nối"""
        if self._server:
            self._server.auth.sign_out()

Singleton instance

tableau_service = TableauService()
# routes/tableau.py - API endpoints cho Tableau Q&A
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import Optional, List
import json

from services.llm_service import llm_service
from services.tableau_service import tableau_service

router = APIRouter(prefix="/api/tableau", tags=["Tableau"])

class TableauQuestionRequest(BaseModel):
    question: str
    view_id: Optional[str] = None
    workbook_id: Optional[str] = None
    model: Optional[str] = "deepseek-v3.2"

class TableauAnswerResponse(BaseModel):
    answer: str
    view_data: Optional[str] = None
    recommended_dashboard: Optional[str] = None
    latency_ms: float
    cost_usd: float

@router.post("/ask", response_model=TableauAnswerResponse)
async def ask_tableau(request: TableauQuestionRequest):
    """Endpoint để hỏi Tableau bằng ngôn ngữ tự nhiên"""
    
    try:
        # Lấy data từ view nếu được chỉ định
        view_data = None
        if request.view_id:
            result = tableau_service.query_view_data(request.view_id)
            view_data = result['data']
        
        # Tạo câu trả lời với LLM
        context = f"View data:\n{view_data}" if view_data else "Chưa có data cụ thể"
        
        prompt = f"""Bạn là một chuyên gia phân tích dữ liệu Tableau.
Hãy trả lời câu hỏi dựa trên data được cung cấp.

{context}

Câu hỏi: {request.question}

Trả lời bằng tiếng Việt, ngắn gọn, có số liệu cụ thể. 
Nếu cần tạo visualization, hãy đề xuất chart type phù hợp."""
        
        messages = [{"role": "user", "content": prompt}]
        
        result = llm_service.chat_completion(
            messages=messages,
            model=request.model,
            temperature=0.7,
            max_tokens=800
        )
        
        if not result['success']:
            raise HTTPException(status_code=500, detail=result['error'])
        
        answer = result['data']['choices'][0]['message']['content']
        
        return TableauAnswerResponse(
            answer=answer,
            view_data=view_data[:1000] if view_data else None,  # Limit data
            latency_ms=result['latency_ms'],
            cost_usd=result['cost']
        )
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/workbooks")
async def get_workbooks():
    """Lấy danh sách workbooks"""
    try:
        workbooks = tableau_service.get_workbooks()
        return {"success": True, "workbooks": workbooks}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

Xây dựng hệ thống RAG cho BI

Để tạo một BI assistant thông minh, tôi khuyên xây dựng hệ thống RAG (Retrieval-Augmented Generation) với:

# services/rag_service.py - Service RAG cho BI
import json
from typing import List, Dict, Tuple
from services.llm_service import llm_service

class RAGService:
    """Hệ thống RAG để tăng cường câu trả lời cho BI queries"""
    
    def __init__(self):
        self.embeddings = []
        self.documents = []
        self.chunks = []
    
    def add_documents(self, documents: List[Dict]):
        """
        Thêm documents vào vector store
        
        documents = [
            {
                "content": "Doanh thu tháng = Tổng các giao dịch trong tháng",
                "metadata": {"type": "definition", "kpi": "revenue"}
            },
            ...
        ]
        """
        for doc in documents:
            self.documents.append(doc)
            self.chunks.append({
                "content": doc["content"],
                "metadata": doc.get("metadata", {})
            })
        
        # Tạo embeddings
        texts = [chunk["content"] for chunk in self.chunks]
        result = llm_service.generate_embeddings(texts)
        
        if result['success']:
            for i, embedding in enumerate(result['embeddings']):
                self.chunks[i]["embedding"] = embedding
    
    def retrieve(self, query: str, top_k: int = 5) -> List[Dict]:
        """Tìm kiếm documents liên quan đến query"""
        # Tạo embedding cho query
        result = llm_service.generate_embeddings([query])
        
        if not result['success']:
            return []
        
        query_embedding = result['embeddings'][0]
        
        # Tính cosine similarity
        similarities = []
        for chunk in self.chunks:
            if 'embedding' not in chunk:
                continue
            
            similarity = self._cosine_similarity(query_embedding, chunk['embedding'])
            similarities.append({
                "content": chunk['content'],
                "metadata": chunk['metadata'],
                "score": similarity
            })
        
        # Sắp xếp theo similarity và lấy top_k
        similarities.sort(key=lambda x: x['score'], reverse=True)
        
        return similarities[:top_k]
    
    def _cosine_similarity(self, vec1: List[float], vec2: List[float]) -> float:
        """Tính cosine similarity giữa 2 vectors"""
        dot_product = sum(a * b for a, b in zip(vec1, vec2))
        magnitude1 = sum(a * a for a in vec1) ** 0.5
        magnitude2 = sum(b * b for b in vec2) ** 0.5
        
        if magnitude1 == 0 or magnitude2 == 0:
            return 0
        
        return dot_product / (magnitude1 * magnitude2)
    
    def generate_answer(self, question: str, context: List[Dict]) -> Dict:
        """Tạo câu trả lời với RAG context"""
        
        # Build context string
        context_str = "\n\n".join([
            f"- {item['content']}" 
            for item in context
        ])
        
        prompt = f"""Dựa trên thông tin sau từ hệ thống BI, hãy trả lời câu hỏi:

--- CONTEXT ---
{context_str}

--- QUESTION ---
{question}

Trả lời ngắn gọn, chính xác, có tham chiếu đến source data."""
        
        messages = [{"role": "user", "content": prompt}]
        
        result = llm_service.chat_completion(
            messages=messages,
            model="deepseek-v3.2",
            temperature=0.5,
            max_tokens=1000
        )
        
        return {
            "answer": result['data']['choices'][0]['message']['content'] if result['success'] else "",
            "sources": [item['content'] for item in context],
            "latency_ms": result.get('latency_ms', 0),
            "cost": result.get('cost', 0)
        }

Khởi tạo và thêm sample documents

rag_service = RAGService()

Thêm business glossary

rag_service.add_documents([ { "content": "DAU (Daily Active Users) = Số người dùng đăng nhập và sử dụng app ít nhất 1 lần/ngày", "metadata": {"type": "metric", "category": "user_engagement"} },