Spaces:
Sleeping
Sleeping
Dus Tran commited on
Commit ·
ebeb2ae
1
Parent(s): feb5410
Add and refactor evaluation scripts
Browse files- evaluation/__init__.py +1 -0
- evaluation/evaluate.py +350 -0
- evaluation/optimization_dataset.json +110 -0
- evaluation/rag_pipeline.py +55 -0
- evaluation/results/ragas_metrics_chart.png +3 -0
- evaluation/results/ragas_results_20260523_212654.csv +19 -0
- evaluation/results/ragas_results_20260523_212654.json +50 -0
- evaluation/results/ragas_results_latest.json +50 -0
- evaluation/results/ragas_summary_20260523_212654.json +19 -0
- evaluation/results/ragas_summary_latest.json +19 -0
- evaluation/run_eval.py +60 -0
evaluation/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# RAG Chatbot Evaluation Package
|
evaluation/evaluate.py
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
RAGAS Evaluation Suite cho RAG Chatbot.
|
| 3 |
+
Các metrics được đánh giá:
|
| 4 |
+
1. Faithfulness - Câu trả lời có trung thực với context không?
|
| 5 |
+
2. Answer Relevancy - Câu trả lời có liên quan đến câu hỏi không?
|
| 6 |
+
3. Context Precision - Context retrieve được có chính xác không?
|
| 7 |
+
4. Context Recall - Context có đủ để trả lời câu hỏi không?
|
| 8 |
+
5. Answer Correctness - Câu trả lời có đúng so với ground truth không?
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
import os
|
| 12 |
+
import sys
|
| 13 |
+
import json
|
| 14 |
+
import time
|
| 15 |
+
import logging
|
| 16 |
+
from datetime import datetime
|
| 17 |
+
from pathlib import Path
|
| 18 |
+
import pandas as pd
|
| 19 |
+
from datasets import Dataset
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
from ragas import evaluate
|
| 24 |
+
from ragas.metrics import (
|
| 25 |
+
Faithfulness,
|
| 26 |
+
AnswerRelevancy,
|
| 27 |
+
ContextPrecision,
|
| 28 |
+
ContextRecall,
|
| 29 |
+
AnswerCorrectness,
|
| 30 |
+
)
|
| 31 |
+
from langchain_groq import ChatGroq
|
| 32 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
| 33 |
+
from ragas.llms import LangchainLLMWrapper
|
| 34 |
+
from ragas.embeddings import LangchainEmbeddingsWrapper
|
| 35 |
+
from ragas.run_config import RunConfig
|
| 36 |
+
from evaluation.rag_pipeline import run_rag_pipeline
|
| 37 |
+
|
| 38 |
+
# ──────────────────────────────────────────────────────────────
|
| 39 |
+
# Logging
|
| 40 |
+
# ──────────────────────────────────────────────────────────────
|
| 41 |
+
logging.basicConfig(
|
| 42 |
+
level=logging.INFO,
|
| 43 |
+
format="%(asctime)s [%(levelname)s] %(message)s",
|
| 44 |
+
handlers=[logging.StreamHandler()],
|
| 45 |
+
)
|
| 46 |
+
logger = logging.getLogger(__name__)
|
| 47 |
+
|
| 48 |
+
# ──────────────────────────────────────────────────────────────
|
| 49 |
+
# Cấu hình LLM & Embedding cho RAGAS
|
| 50 |
+
# ──────────────────────────────────────────────────────────────
|
| 51 |
+
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
| 52 |
+
RAGAS_EMBED_MODEL = "bkai-foundation-models/vietnamese-bi-encoder"
|
| 53 |
+
|
| 54 |
+
def get_ragas_llm():
|
| 55 |
+
"""Khởi tạo LLM cho RAGAS 0.4 dùng Groq (Llama 3.1 70B) và LangchainLLMWrapper."""
|
| 56 |
+
if not GROQ_API_KEY:
|
| 57 |
+
raise ValueError("Chưa cấu hình GROQ_API_KEY trong file .env")
|
| 58 |
+
groq_llm = ChatGroq(
|
| 59 |
+
model="llama-3.3-70b-versatile",
|
| 60 |
+
groq_api_key=GROQ_API_KEY,
|
| 61 |
+
temperature=0,
|
| 62 |
+
max_retries=10
|
| 63 |
+
)
|
| 64 |
+
return LangchainLLMWrapper(groq_llm)
|
| 65 |
+
|
| 66 |
+
def get_ragas_embeddings():
|
| 67 |
+
"""Khởi tạo Embedding cho RAGAS 0.4 dùng HuggingFace và LangchainEmbeddingsWrapper."""
|
| 68 |
+
hf_embeddings = HuggingFaceEmbeddings(model_name=RAGAS_EMBED_MODEL)
|
| 69 |
+
return LangchainEmbeddingsWrapper(hf_embeddings)
|
| 70 |
+
|
| 71 |
+
# ──────────────────────────────────────────────────────────────
|
| 72 |
+
# Thu thập dữ liệu từ RAG pipeline
|
| 73 |
+
# ──────────────────────────────────────────────────────────────
|
| 74 |
+
def collect_rag_outputs(dataset_path: str, limit: int = None, delay_seconds: float = 5.0) -> list[dict]:
|
| 75 |
+
"""
|
| 76 |
+
Chạy RAG pipeline trên từng câu hỏi trong dataset và thu thập kết quả.
|
| 77 |
+
"""
|
| 78 |
+
with open(dataset_path, "r", encoding="utf-8") as f:
|
| 79 |
+
samples = json.load(f)
|
| 80 |
+
|
| 81 |
+
print("⏳ LLM (GPT-4o-mini) sẽ tính điểm từng metric — có thể mất 2-3 phút (chạy sequential để tránh Rate Limit)...")
|
| 82 |
+
if limit is not None and limit > 0:
|
| 83 |
+
logger.info(f"Giới hạn bộ test còn {limit} mẫu để tránh Rate Limit API.")
|
| 84 |
+
samples = samples[:limit]
|
| 85 |
+
|
| 86 |
+
results = []
|
| 87 |
+
total = len(samples)
|
| 88 |
+
|
| 89 |
+
for idx, sample in enumerate(samples, 1):
|
| 90 |
+
question = sample["question"]
|
| 91 |
+
ground_truth = sample["ground_truth"]
|
| 92 |
+
|
| 93 |
+
logger.info(f"[{idx}/{total}] Đang chạy RAG cho: '{question[:50]}...'")
|
| 94 |
+
|
| 95 |
+
try:
|
| 96 |
+
output = run_rag_pipeline(
|
| 97 |
+
question=question,
|
| 98 |
+
session_id=f"eval_{idx}",
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
# Format context và đảm bảo là list
|
| 102 |
+
contexts = output.get("contexts", [])
|
| 103 |
+
if isinstance(contexts, str):
|
| 104 |
+
contexts = [contexts]
|
| 105 |
+
|
| 106 |
+
results.append(
|
| 107 |
+
{
|
| 108 |
+
"question": question,
|
| 109 |
+
"answer": output["answer"],
|
| 110 |
+
"contexts": contexts,
|
| 111 |
+
"ground_truth": ground_truth,
|
| 112 |
+
}
|
| 113 |
+
)
|
| 114 |
+
logger.info(f" ✓ Answer: {output['answer'][:70]}...")
|
| 115 |
+
except Exception as e:
|
| 116 |
+
logger.error(f" ✗ Lỗi khi xử lý câu hỏi #{idx}: {e}")
|
| 117 |
+
results.append(
|
| 118 |
+
{
|
| 119 |
+
"question": question,
|
| 120 |
+
"answer": f"ERROR: {str(e)}",
|
| 121 |
+
"contexts": ["Không tìm thấy ngữ cảnh vì xảy ra lỗi."],
|
| 122 |
+
"ground_truth": ground_truth,
|
| 123 |
+
}
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
if idx < total:
|
| 127 |
+
time.sleep(delay_seconds)
|
| 128 |
+
|
| 129 |
+
return results
|
| 130 |
+
|
| 131 |
+
# ──────────────────────────────────────────────────────────────
|
| 132 |
+
# Vẽ biểu đồ kết quả
|
| 133 |
+
# ──────────────────────────────────────────────────────────────
|
| 134 |
+
def generate_plots(df_results: pd.DataFrame, output_dir: str):
|
| 135 |
+
"""
|
| 136 |
+
Vẽ các biểu đồ trực quan hóa kết quả và lưu dưới dạng ảnh PNG.
|
| 137 |
+
"""
|
| 138 |
+
try:
|
| 139 |
+
import matplotlib.pyplot as plt
|
| 140 |
+
import numpy as np
|
| 141 |
+
except ImportError:
|
| 142 |
+
logger.warning("Không thể import matplotlib hoặc numpy, bỏ qua bước vẽ biểu đồ.")
|
| 143 |
+
return
|
| 144 |
+
|
| 145 |
+
METRICS = [
|
| 146 |
+
"faithfulness",
|
| 147 |
+
"answer_relevancy",
|
| 148 |
+
"context_precision",
|
| 149 |
+
"context_recall",
|
| 150 |
+
"answer_correctness",
|
| 151 |
+
]
|
| 152 |
+
METRIC_LABELS = {
|
| 153 |
+
"faithfulness": "Faithfulness\n(Trung thực)",
|
| 154 |
+
"answer_relevancy": "Answer Relevancy\n(Liên quan)",
|
| 155 |
+
"context_precision": "Context Precision\n(Chính xác)",
|
| 156 |
+
"context_recall": "Context Recall\n(Đầy đủ)",
|
| 157 |
+
"answer_correctness": "Answer Correctness\n(Đúng đắn)",
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
available_metrics = [m for m in METRICS if m in df_results.columns]
|
| 161 |
+
avg_scores = df_results[available_metrics].mean()
|
| 162 |
+
plot_scores = np.nan_to_num(avg_scores.values, nan=0.0)
|
| 163 |
+
|
| 164 |
+
# 1. Bar Chart & Radar Chart (Side by Side)
|
| 165 |
+
fig = plt.figure(figsize=(15, 6))
|
| 166 |
+
fig.suptitle("KẾT QUẢ ĐÁNH GIÁ CHẤT LƯỢNG CHATBOT (RAGAS)", fontsize=15, fontweight="bold", y=0.98)
|
| 167 |
+
|
| 168 |
+
# Bar chart
|
| 169 |
+
ax1 = fig.add_subplot(1, 2, 1)
|
| 170 |
+
colors = [
|
| 171 |
+
"#27ae60" if (not np.isnan(s) and s >= 0.7) else ("#f39c12" if (not np.isnan(s) and s >= 0.5) else "#d35400")
|
| 172 |
+
for s in avg_scores.values
|
| 173 |
+
]
|
| 174 |
+
bars = ax1.bar(
|
| 175 |
+
[METRIC_LABELS.get(m, m) for m in available_metrics],
|
| 176 |
+
plot_scores,
|
| 177 |
+
color=colors, width=0.5, edgecolor="white", linewidth=1.2
|
| 178 |
+
)
|
| 179 |
+
ax1.set_ylim(0, 1.15)
|
| 180 |
+
ax1.set_ylabel("Điểm số (Score)", fontsize=11)
|
| 181 |
+
ax1.set_title("Điểm Trung Bình Từng Metric", fontsize=12, fontweight="bold")
|
| 182 |
+
ax1.axhline(y=0.7, color="#27ae60", linestyle="--", alpha=0.6, label="Tốt (>=0.7)")
|
| 183 |
+
ax1.axhline(y=0.5, color="#f39c12", linestyle="--", alpha=0.6, label="Tạm ổn (>=0.5)")
|
| 184 |
+
ax1.legend(loc="upper right", fontsize=9)
|
| 185 |
+
ax1.set_xticks(range(len(available_metrics)))
|
| 186 |
+
ax1.set_xticklabels([METRIC_LABELS.get(m, m) for m in available_metrics], fontsize=9)
|
| 187 |
+
for bar, score in zip(bars, avg_scores.values):
|
| 188 |
+
height = bar.get_height()
|
| 189 |
+
label_val = f"{score:.3f}" if not np.isnan(score) else "NaN"
|
| 190 |
+
ax1.text(bar.get_x() + bar.get_width() / 2.0, height + 0.02,
|
| 191 |
+
label_val, ha="center", va="bottom", fontweight="bold", fontsize=10)
|
| 192 |
+
ax1.set_facecolor("#fcfcfc")
|
| 193 |
+
ax1.grid(axis="y", alpha=0.2)
|
| 194 |
+
|
| 195 |
+
# Radar chart
|
| 196 |
+
ax2 = fig.add_subplot(1, 2, 2, projection="polar")
|
| 197 |
+
N = len(available_metrics)
|
| 198 |
+
angles = np.linspace(0, 2 * np.pi, N, endpoint=False).tolist()
|
| 199 |
+
values = plot_scores.tolist()
|
| 200 |
+
|
| 201 |
+
# Close the polygon
|
| 202 |
+
angles_c = angles + angles[:1]
|
| 203 |
+
values_c = values + values[:1]
|
| 204 |
+
|
| 205 |
+
ax2.plot(angles_c, values_c, "o-", linewidth=2, color="#2980b9")
|
| 206 |
+
ax2.fill(angles_c, values_c, alpha=0.2, color="#2980b9")
|
| 207 |
+
ax2.set_xticks(angles)
|
| 208 |
+
ax2.set_xticklabels([m.replace("_", "\n").title() for m in available_metrics], fontsize=9)
|
| 209 |
+
ax2.set_ylim(0, 1.0)
|
| 210 |
+
ax2.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0])
|
| 211 |
+
ax2.set_yticklabels(["0.2", "0.4", "0.6", "0.8", "1.0"], fontsize=8)
|
| 212 |
+
ax2.set_title("Biểu Đồ Radar Đa Chiều", pad=20, fontsize=12, fontweight="bold")
|
| 213 |
+
ax2.grid(True, alpha=0.3)
|
| 214 |
+
|
| 215 |
+
plt.tight_layout()
|
| 216 |
+
chart_path = os.path.join(output_dir, "ragas_metrics_chart.png")
|
| 217 |
+
plt.savefig(chart_path, dpi=150, bbox_inches="tight")
|
| 218 |
+
plt.close()
|
| 219 |
+
logger.info(f"Đã cập nhật biểu đồ: {chart_path}")
|
| 220 |
+
|
| 221 |
+
# 2. Heatmap per Sample
|
| 222 |
+
heatmap_data = df_results[available_metrics].values.astype(float)
|
| 223 |
+
question_col = "user_input" if "user_input" in df_results.columns else "question"
|
| 224 |
+
q_labels = [f"Q{i+1}: {str(q)[:30]}..." for i, q in enumerate(df_results.get(question_col, ["Unknown"] * len(df_results)))]
|
| 225 |
+
|
| 226 |
+
fig, ax = plt.subplots(figsize=(10, max(4, len(df_results) * 0.5)))
|
| 227 |
+
im = ax.imshow(heatmap_data, cmap="RdYlGn", aspect="auto", vmin=0, vmax=1)
|
| 228 |
+
plt.colorbar(im, ax=ax, fraction=0.04, pad=0.04)
|
| 229 |
+
|
| 230 |
+
ax.set_xticks(range(len(available_metrics)))
|
| 231 |
+
ax.set_xticklabels(available_metrics, rotation=20, ha="right", fontsize=9)
|
| 232 |
+
ax.set_yticks(range(len(df_results)))
|
| 233 |
+
ax.set_yticklabels(q_labels, fontsize=9)
|
| 234 |
+
ax.set_title("Bản Đồ Nhiệt (Heatmap) Điểm Số Từng Mẫu", fontsize=13, fontweight="bold", pad=12)
|
| 235 |
+
|
| 236 |
+
for i in range(len(df_results)):
|
| 237 |
+
for j in range(len(available_metrics)):
|
| 238 |
+
val = heatmap_data[i, j]
|
| 239 |
+
if not np.isnan(val):
|
| 240 |
+
ax.text(j, i, f"{val:.2f}", ha="center", va="center",
|
| 241 |
+
fontsize=8.5, fontweight="bold",
|
| 242 |
+
color="white" if val < 0.4 else "black")
|
| 243 |
+
|
| 244 |
+
plt.tight_layout()
|
| 245 |
+
heatmap_path = os.path.join(output_dir, "ragas_heatmap.png")
|
| 246 |
+
plt.savefig(heatmap_path, dpi=150, bbox_inches="tight")
|
| 247 |
+
plt.close()
|
| 248 |
+
logger.info(f"✓ Đã cập nhật Heatmap: {heatmap_path}")
|
| 249 |
+
|
| 250 |
+
# ──────────────────────────────────────────────────────────────
|
| 251 |
+
# Chạy RAGAS Evaluation
|
| 252 |
+
# ──────────────────────────────────────────────────────────────
|
| 253 |
+
def run_ragas_evaluation(
|
| 254 |
+
rag_results: list[dict],
|
| 255 |
+
output_dir: str = None,
|
| 256 |
+
) -> tuple[pd.DataFrame, dict]:
|
| 257 |
+
"""
|
| 258 |
+
Chạy RAGAS evaluation trên tập kết quả từ RAG pipeline.
|
| 259 |
+
"""
|
| 260 |
+
if output_dir is None:
|
| 261 |
+
output_dir = os.path.join(os.path.dirname(__file__), "results")
|
| 262 |
+
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
| 263 |
+
|
| 264 |
+
# Chuyển sang Hugging Face Dataset format
|
| 265 |
+
dataset_dict = {
|
| 266 |
+
"question": [r["question"] for r in rag_results],
|
| 267 |
+
"answer": [r["answer"] for r in rag_results],
|
| 268 |
+
"contexts": [r["contexts"] for r in rag_results],
|
| 269 |
+
"ground_truth": [r["ground_truth"] for r in rag_results],
|
| 270 |
+
}
|
| 271 |
+
hf_dataset = Dataset.from_dict(dataset_dict)
|
| 272 |
+
|
| 273 |
+
logger.info("Đang khởi tạo LLM và Embeddings cho RAGAS...")
|
| 274 |
+
ragas_llm = get_ragas_llm()
|
| 275 |
+
ragas_embeddings = get_ragas_embeddings()
|
| 276 |
+
|
| 277 |
+
# Khởi tạo các metric RAGAS
|
| 278 |
+
metrics = [
|
| 279 |
+
Faithfulness(llm=ragas_llm),
|
| 280 |
+
AnswerRelevancy(llm=ragas_llm, embeddings=ragas_embeddings),
|
| 281 |
+
ContextPrecision(llm=ragas_llm),
|
| 282 |
+
ContextRecall(llm=ragas_llm),
|
| 283 |
+
AnswerCorrectness(llm=ragas_llm, embeddings=ragas_embeddings),
|
| 284 |
+
]
|
| 285 |
+
|
| 286 |
+
logger.info(f"Bắt đầu chấm điểm {len(rag_results)} mẫu với {len(metrics)} metrics...")
|
| 287 |
+
logger.info(f"Metrics: {[m.name for m in metrics]}")
|
| 288 |
+
|
| 289 |
+
run_config = RunConfig(max_workers=1, timeout=300)
|
| 290 |
+
result = evaluate(
|
| 291 |
+
dataset=hf_dataset,
|
| 292 |
+
metrics=metrics,
|
| 293 |
+
run_config=run_config,
|
| 294 |
+
raise_exceptions=False,
|
| 295 |
+
)
|
| 296 |
+
|
| 297 |
+
df = result.to_pandas()
|
| 298 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 299 |
+
df["timestamp"] = timestamp
|
| 300 |
+
|
| 301 |
+
# Tính điểm trung bình
|
| 302 |
+
metric_cols = [m.name for m in metrics]
|
| 303 |
+
avg_scores = df[metric_cols].mean()
|
| 304 |
+
|
| 305 |
+
logger.info("\n" + "=" * 60)
|
| 306 |
+
logger.info("KẾT QUẢ ĐÁNH GIÁ RAGAS TRUNG BÌNH")
|
| 307 |
+
logger.info("=" * 60)
|
| 308 |
+
for col, score in avg_scores.items():
|
| 309 |
+
if pd.isna(score):
|
| 310 |
+
bar = "░" * 20
|
| 311 |
+
logger.info(f" {col:<25} {bar} NaN (Failed/API Quota Exceeded)")
|
| 312 |
+
else:
|
| 313 |
+
bar = "█" * int(score * 20) + "░" * (20 - int(score * 20))
|
| 314 |
+
logger.info(f" {col:<25} {bar} {score:.4f}")
|
| 315 |
+
logger.info("=" * 60)
|
| 316 |
+
|
| 317 |
+
# Lưu kết quả theo dạng timestamp
|
| 318 |
+
csv_path = os.path.join(output_dir, f"ragas_results_{timestamp}.csv")
|
| 319 |
+
json_path = os.path.join(output_dir, f"ragas_results_{timestamp}.json")
|
| 320 |
+
summary_path = os.path.join(output_dir, f"ragas_summary_{timestamp}.json")
|
| 321 |
+
|
| 322 |
+
df.to_csv(csv_path, index=False, encoding="utf-8-sig")
|
| 323 |
+
df.to_json(json_path, orient="records", force_ascii=False, indent=2)
|
| 324 |
+
|
| 325 |
+
summary = {
|
| 326 |
+
"evaluation_timestamp": timestamp,
|
| 327 |
+
"total_samples": len(rag_results),
|
| 328 |
+
"metrics_used": [m.name for m in metrics],
|
| 329 |
+
"average_scores": avg_scores.to_dict(),
|
| 330 |
+
"overall_score": avg_scores.mean(),
|
| 331 |
+
}
|
| 332 |
+
with open(summary_path, "w", encoding="utf-8") as f:
|
| 333 |
+
json.dump(summary, f, ensure_ascii=False, indent=2)
|
| 334 |
+
|
| 335 |
+
# Lưu thêm phiên bản tĩnh "latest" để dashboard dễ truy cập
|
| 336 |
+
latest_json_path = os.path.join(output_dir, "ragas_results_latest.json")
|
| 337 |
+
latest_summary_path = os.path.join(output_dir, "ragas_summary_latest.json")
|
| 338 |
+
|
| 339 |
+
df.to_json(latest_json_path, orient="records", force_ascii=False, indent=2)
|
| 340 |
+
with open(latest_summary_path, "w", encoding="utf-8") as f:
|
| 341 |
+
json.dump(summary, f, ensure_ascii=False, indent=2)
|
| 342 |
+
|
| 343 |
+
logger.info("Đã lưu các kết quả dạng timestamp và dạng 'latest'")
|
| 344 |
+
|
| 345 |
+
# Vẽ biểu đồ tĩnh
|
| 346 |
+
generate_plots(df, output_dir)
|
| 347 |
+
|
| 348 |
+
return df, summary
|
| 349 |
+
|
| 350 |
+
|
evaluation/optimization_dataset.json
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"id": 1,
|
| 4 |
+
"source_file": "Optimization_Chap04_2_new.pdf",
|
| 5 |
+
"question": "Trong Bước (k3) của thuật toán, siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là gì và có chức năng như thế nào?",
|
| 6 |
+
"ground_truth": "Siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là siêu phẳng cắt. Đây chính là siêu phẳng tách tập lồi X và điểm xk không thuộc X."
|
| 7 |
+
},
|
| 8 |
+
{
|
| 9 |
+
"id": 2,
|
| 10 |
+
"source_file": "Optimization_Chap04_2_new.pdf",
|
| 11 |
+
"question": "Khi giải hệ phương trình ∇xψ(x, α) = 0, điểm dừng x*(α) nhận được là gì và giới hạn của các thành phần của điểm dừng này khi tham số α tiến về 0+ là bao nhiêu?",
|
| 12 |
+
"ground_truth": "Khi giải hệ phương trình ∇xψ(x, α) = 0, ta nhận được điểm dừng x*(α) = (x*1(α), x*2(α))T với x*1(α) = (√1 + 2α + 3α - 1)/2 và x*2(α) = 1 + (√1 + 2α)/2. Khi tham số α > 0 và giảm dần về 0, giới hạn của các thành phần của điểm dừng là limα→0+ x*1(α) = 0 và limα→0+ x*2(α) = 1."
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"id": 3,
|
| 16 |
+
"source_file": "Optimization_Chap02_4.pdf",
|
| 17 |
+
"question": "Theo Định lý về độ lệch bù, ý nghĩa kinh tế đối với các phương pháp sản xuất là gì khi một phương pháp được áp dụng và khi không được áp dụng?",
|
| 18 |
+
"ground_truth": "Theo Định lý về độ lệch bù, đối với các phương pháp sản xuất, nếu phương pháp thứ j được áp dụng (t∗j > 0), tổng giá trị các sản phẩm sản xuất theo phương pháp j phải vừa đúng bằng chi phí cj. Ngược lại, nếu phương pháp thứ j không được áp dụng (t∗j = 0), tổng giá trị các sản phẩm sản xuất theo phương pháp j thấp hơn chi phí cj, điều này có nghĩa là sản xuất theo phương pháp này sẽ bị lỗ."
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"id": 4,
|
| 22 |
+
"source_file": "Optimization_Chap02_4.pdf",
|
| 23 |
+
"question": "Theo đoạn văn, mục đích của bài toán đối ngẫu là gì và nó liên quan đến việc xác định yếu tố nào để đạt được mục tiêu đó?",
|
| 24 |
+
"ground_truth": "Mục đích của bài toán đối ngẫu là xác định giá trị zi cho mỗi đơn vị sản phẩm loại i sao cho tổng giá trị toàn bộ sản phẩm theo yêu cầu của xã hội là lớn nhất."
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"id": 5,
|
| 28 |
+
"source_file": "Optimization_Chap02_1.pdf",
|
| 29 |
+
"question": "Theo đoạn văn, nghiệm tối ưu (phương án tối ưu) được định nghĩa như thế nào?",
|
| 30 |
+
"ground_truth": "Nghiệm tối ưu (phương án tối ưu) là một nghiệm chấp nhận được x∗ ∈ D thỏa mãn f (x∗) = ⟨c, x∗⟩ ≤ f (x) = ⟨c, x⟩, với mọi x ∈ D."
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"id": 6,
|
| 34 |
+
"source_file": "Optimization_Chap02_1.pdf",
|
| 35 |
+
"question": "Trong bài toán tối ưu hóa, làm thế nào để chuyển đổi một ràng buộc đẳng thức thành các ràng buộc bất đẳng thức?",
|
| 36 |
+
"ground_truth": "Để chuyển đổi một ràng buộc đẳng thức có dạng nXj=1 aijxj = bi, ta có thể thay thế nó bằng hai ràng buộc bất đẳng thức: nXj=1 aijxj ≥ bi và −nXj=1 aijxj ≥ −bi."
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"id": 7,
|
| 40 |
+
"source_file": "Optimization_Chap02_3.pdf",
|
| 41 |
+
"question": "Khi giải bài toán (LP) bằng phương pháp đơn hình, để chuyển từ bảng đơn hình cuối cùng của Pha 1 sang bảng đơn hình xuất phát của Pha 2, cần thực hiện những sửa đổi nào?",
|
| 42 |
+
"ground_truth": "Bảng đơn hình đầu tiên của Pha 2 là bảng đơn hình cuối cùng ở Pha 1 nhưng với một số sửa đổi như sau: Xóa tất cả các cột tương ứng với các biến giả; Thay cột CB bởi hệ số mục tiêu cơ sở tương ứng của bài toán gốc; Thay các hệ số mục tiêu của bài toán phụ ở dòng 1 bằng hệ số mục tiêu của bài toán gốc; Nếu trong cơ sở tương ứng với phương án tối ưu của bài toán phụ (LP*) không có vector giả thì cơ sở ứng với phương án này cũng chính là cơ sở tương ứng với x0. Để có bảng đơn hình xuất phát cho bài toán ban đầu, ta chỉ cần tính giá trị hàm mục tiêu tại x0 và tính lại các ước lượng theo công thức f(x0) = Σj∈J(x0) cjx0j và Δk = Σj∈J(x0) zjkcj − ck."
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
"id": 8,
|
| 46 |
+
"source_file": "Optimization_Chap02_3.pdf",
|
| 47 |
+
"question": "Dựa vào Bảng 3 trong Pha 2 của quá trình giải bài toán gốc, hãy xác định giá trị hàm mục tiêu hiện tại và các biến cơ sở cùng với giá trị của chúng.",
|
| 48 |
+
"ground_truth": "Từ Bảng 3, giá trị hàm mục tiêu hiện tại là -82/5. Các biến cơ sở và giá trị của chúng là: A3 = 12, A4 = 0, và A2 = 7."
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"id": 9,
|
| 52 |
+
"source_file": "Optimization_Chap03_2.pdf",
|
| 53 |
+
"question": "Thuật toán Newton thuần túy có những ưu điểm nổi bật nào khi áp dụng cho hàm toàn phương và khi điểm xuất phát đủ gần điểm dừng?",
|
| 54 |
+
"ground_truth": "Thuật toán Newton thuần túy có hai ưu điểm nổi bật: Thứ nhất, nếu f là hàm toàn phương f(x) = 1/2 xTAx − bTx + c với A đối xứng xác định dương, thuật toán sẽ cho nghiệm chỉ sau một vòng lặp. Thứ hai, nếu xuất phát đủ gần điểm dừng, thuật toán hội tụ rất nhanh (bậc hai)."
|
| 55 |
+
},
|
| 56 |
+
{
|
| 57 |
+
"id": 10,
|
| 58 |
+
"source_file": "Optimization_Chap03_2.pdf",
|
| 59 |
+
"question": "Dựa vào Bước lặp k = 3, hãy cho biết điều kiện dừng thuật toán được thỏa mãn là gì và nghiệm tìm được sau khi dừng thuật toán là bao nhiêu?",
|
| 60 |
+
"ground_truth": "Tại Bước lặp k = 3, thuật toán dừng lại vì gradient của hàm mục tiêu tại x4, tức là ∇f(x4), bằng vectơ không (0, 0, 0). Nghiệm tìm được sau khi dừng thuật toán là x* = (-1, -0.2, -0.04)."
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"id": 11,
|
| 64 |
+
"source_file": "Optimization_Chap01.pdf",
|
| 65 |
+
"question": "Trong Quy hoạch phi tuyến, Quy hoạch lồi được định nghĩa như thế nào?",
|
| 66 |
+
"ground_truth": "Quy hoạch lồi là bài toán cực tiểu một hàm mục tiêu f(x) là hàm lồi trên tập chấp nhận được D ⊆ R^n là tập lồi."
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"id": 12,
|
| 70 |
+
"source_file": "Optimization_Chap01.pdf",
|
| 71 |
+
"question": "Bài toán được mô tả trong đoạn văn bản yêu cầu xác định những vị trí nào?",
|
| 72 |
+
"ground_truth": "Bài toán yêu cầu xác định vị trí để xây dựng một trạm biến áp tổng để cung cấp điện cho 4 khu chung cư và vị trí (xi, yi) ∈ Ki để đặt trạm biến áp phân phối của mỗi khu chung cư Ki, i = 1, ."
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"id": 13,
|
| 76 |
+
"source_file": "Optimization_Chap02_2.pdf",
|
| 77 |
+
"question": "Theo thuật toán đơn hình, điều kiện để phương án cực biên x0 được coi là nghiệm tối ưu là gì?",
|
| 78 |
+
"ground_truth": "Theo Bước 3 của thuật toán đơn hình, nếu tất cả các ước lượng biến ∆k ≤ 0 với mọi k không thuộc tập chỉ số cơ sở J(x0), thì x0 là nghiệm tối ưu và thuật toán sẽ dừng lại."
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"id": 14,
|
| 82 |
+
"source_file": "Optimization_Chap02_2.pdf",
|
| 83 |
+
"question": "Dựa vào bảng đơn hình cuối cùng được cung cấp, hãy cho biết điều kiện để đạt được nghiệm tối ưu, nghiệm tối ưu là gì và giá trị tối ưu tương ứng?",
|
| 84 |
+
"ground_truth": "Dựa vào bảng đơn hình cuối cùng, điều kiện để đạt được nghiệm tối ưu là khi tất cả ∆k ≤ 0. Nghiệm tối ưu duy nhất là x∗ = (4/3, 0, 4/3, 2/3, 0, 0)^T và giá trị tối ưu fmin = 0."
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"id": 15,
|
| 88 |
+
"source_file": "Optimization_Chap03_1.pdf",
|
| 89 |
+
"question": "Theo Định lý hội tụ, các điều kiện nào cần được thỏa mãn để mọi điểm tụ của dãy {xk} sinh bởi Thuật toán 3.1 đều có gradient của hàm f bằng 0?",
|
| 90 |
+
"ground_truth": "Theo Định lý hội tụ, các điều kiện cần được thỏa mãn là: x0 ∈ Rn, hàm f khả vi liên tục trên Rn và có tập mức dưới {x ∈ Rn | f (x) ≤ f (x0)} bị chặn. Khi đó, mọi điểm tụ x∗ của dãy {xk} sinh bởi Thuật toán 3.1 đều thỏa mãn ∇f (x∗) = 0."
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"id": 16,
|
| 94 |
+
"source_file": "Optimization_Chap03_1.pdf",
|
| 95 |
+
"question": "Bài toán quy hoạch phi tuyến không ràng buộc (P) được định nghĩa như thế nào và hàm mục tiêu f trong bài toán này có những tính chất gì theo đoạn văn?",
|
| 96 |
+
"ground_truth": "Bài toán quy hoạch phi tuyến không ràng buộc (P) được định nghĩa là min f(x) v.đ.k. x ∈ Rn. Hàm mục tiêu f trong bài toán này là hàm phi tuyến và được xác định, khả vi trên Rn."
|
| 97 |
+
},
|
| 98 |
+
{
|
| 99 |
+
"id": 17,
|
| 100 |
+
"source_file": "Optimization_Chap04_1.pdf",
|
| 101 |
+
"question": "Chương 4 của môn Cơ sở Tối ưu hóa, với tiêu đề 'Quy hoạch phi tuyến có ràng buộc', bao gồm những nội dung chính nào?",
|
| 102 |
+
"ground_truth": "Chương 4: Quy hoạch phi tuyến có ràng buộc bao gồm các nội dung chính sau: Điều kiện tối ưu, Định lý Karush–Kuhn–Tucker (KKT), Phương pháp nhân tử Lagrange, Phương pháp tuyến tính hóa, Phương pháp hướng có thể, Phương pháp Frank-Wolfe và Phương pháp hàm phạt."
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"id": 18,
|
| 106 |
+
"source_file": "Optimization_Chap04_1.pdf",
|
| 107 |
+
"question": "Theo Định lý về điều kiện tối ưu cơ bản, điều kiện cần để x* là cực tiểu địa phương của bài toán min{f(x) : x ∈ X} là gì, và điều kiện để x* là cực tiểu địa phương chặt là gì?",
|
| 108 |
+
"ground_truth": "Theo Định lý v��� điều kiện tối ưu cơ bản: (i) Nếu x* ∈ X là cực tiểu địa phương của bài toán min{f(x) : x ∈ X} thì ⟨∇f(x*), v⟩ ≥ 0 ∀v ∈ T(X, x*). (ii) Nếu x* ∈ X thỏa mãn ⟨∇f(x*), v⟩ > 0 ∀v ∈ T(X, x*) \\ {0}, thì x* là cực tiểu địa phương chặt."
|
| 109 |
+
}
|
| 110 |
+
]
|
evaluation/rag_pipeline.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from app.service.memory import contextualize_user_query, add_message_to_history
|
| 3 |
+
from app.service.router import logical_router
|
| 4 |
+
from app.service.retriever import hybrid_retrieval, apply_post_retrieval_rerank
|
| 5 |
+
from app.service.generator import generate_answer
|
| 6 |
+
from app.core.config import get_llm
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def run_rag_pipeline(question: str, session_id: str = "eval_session") -> dict:
|
| 10 |
+
"""
|
| 11 |
+
Chạy toàn bộ pipeline RAG và trả về answer + contexts.
|
| 12 |
+
|
| 13 |
+
Args:
|
| 14 |
+
question: Câu hỏi của người dùng.
|
| 15 |
+
session_id: ID phiên để quản lý memory.
|
| 16 |
+
|
| 17 |
+
Returns:
|
| 18 |
+
dict với các key:
|
| 19 |
+
- question (str): Câu hỏi gốc
|
| 20 |
+
- answer (str): Câu trả lời từ RAG pipeline
|
| 21 |
+
- contexts (list[str]): Danh sách các đoạn context đã retrieve được
|
| 22 |
+
"""
|
| 23 |
+
llm = get_llm()
|
| 24 |
+
|
| 25 |
+
# Bước 1: Contextualize query với lịch sử hội thoại
|
| 26 |
+
full_query = contextualize_user_query(question, session_id)
|
| 27 |
+
|
| 28 |
+
# Bước 2: Router phân loại độ phức tạp và nguồn dữ liệu
|
| 29 |
+
routing_res = logical_router(full_query)
|
| 30 |
+
|
| 31 |
+
# Bước 3: Retrieve tài liệu liên quan
|
| 32 |
+
raw_docs = hybrid_retrieval(routing_res)
|
| 33 |
+
|
| 34 |
+
# Bước 4: Rerank để lấy các context tốt nhất
|
| 35 |
+
final_context_str = apply_post_retrieval_rerank(full_query, raw_docs)
|
| 36 |
+
|
| 37 |
+
# Tách chuỗi context thành danh sách
|
| 38 |
+
contexts = [
|
| 39 |
+
c.strip()
|
| 40 |
+
for c in final_context_str.split("\n\n---\n\n")
|
| 41 |
+
if c.strip()
|
| 42 |
+
]
|
| 43 |
+
|
| 44 |
+
# Bước 5: Sinh câu trả lời
|
| 45 |
+
answer = generate_answer(full_query, final_context_str, llm)
|
| 46 |
+
|
| 47 |
+
# Lưu vào memory
|
| 48 |
+
add_message_to_history(session_id, "user", question)
|
| 49 |
+
add_message_to_history(session_id, "ai", answer)
|
| 50 |
+
|
| 51 |
+
return {
|
| 52 |
+
"question": question,
|
| 53 |
+
"answer": answer,
|
| 54 |
+
"contexts": contexts if contexts else ["Không tìm thấy ngữ cảnh phù hợp."],
|
| 55 |
+
}
|
evaluation/results/ragas_metrics_chart.png
ADDED
|
Git LFS Details
|
evaluation/results/ragas_results_20260523_212654.csv
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
user_input,retrieved_contexts,response,reference,faithfulness,answer_relevancy,context_precision,context_recall,answer_correctness,timestamp
|
| 2 |
+
"Trong Bước (k3) của thuật toán, siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là gì và có chức năng như thế nào?","['6/46\nChú ý: Tại Bước (k3) của thuật toán trên:\ni) Ta luôn có ∇gik(xk) ̸= 0 vì nếu ngược lại thì dogik là hàm lồi\nnên ta có\ngik(x) ≥ gik(xk) + ⟨∇gik(xk), x − xk⟩ > 0 ∀x\nvà điều này có nghĩa là tập chấp nhận được X của bài toán\nđang xét bằng rỗng;\nii) Siêu phẳng\n{x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0}\nđể xây dựng đa diệnXk+1 được gọi là siêu phẳng cắt. Đây\nchính là siêu phẳng tách tập lồi X và điểm xk /∈ X.', 'Giải\nmin\n\x1a\n⟨∇f (x1), x⟩ = 2\n5x1 + 4\n5x2\n\x0c\x0c\x0c\x0c x ∈ D\n\x1b\n.', 'Phương án cực biên\nx0 = (b1, b2, .']","Trong Bước (k3) của thuật toán, siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là siêu phẳng cắt. Siêu phẳng này có chức năng tách tập lồi X và điểm xk không thuộc X. Nó được sử dụng để xây dựng đa diện Xk+1.","Siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là siêu phẳng cắt. Đây chính là siêu phẳng tách tập lồi X và điểm xk không thuộc X.",1.0,,0.9999999999,1.0,0.6475692390187693,20260523_212654
|
| 3 |
+
"Khi giải hệ phương trình ∇xψ(x, α) = 0, điểm dừng x*(α) nhận được là gì và giới hạn của các thành phần của điểm dừng này khi tham số α tiến về 0+ là bao nhiêu?","['45/46\nGiải hệ phương trình\n∇xψ(x, α) = 0 ⇐ ⇒\n\uf8f1\n\uf8f4\uf8f4\uf8f2\n\uf8f4\uf8f4\uf8f3\n1 − α\n1 + x1 − x2\n2\n= 0\n−2 + 2αx2\n1 + x1 − x2\n2\n− α\nx2\n= 0\nta nhận được điểm dừngx∗(α) = (x∗\n1(α), x∗\n2(α))T với\nx∗\n1(α) =\n√\n1 + 2α + 3α − 1\n2 , x∗\n2(α) = 1 +\n√\n1 + 2α\n2 . Vì tham sốα > 0 và giảm dần về 0 nên\nlim\nα→0+\nx∗\n1(α) = 0, lim\nα→0+\nx∗\n2(α) = 1.', 'Kiểm tra gradient tại x1:\n∇f (x1) =\n\x123 · 12 − 3\n2 · 1 − 2\n\x13\n=\n\x120\n0\n\x13\n. Kết luận: Dừng thuật toán với điểm dừng\nx1 = (1, 1).', '25/37\nVì∇f (x1) = 0, điểm x1 = (1, 1)T là điểm dừng của hàm f . Kết luận: Dừng thuật toán tạix1 = (1, 1).']","Khi giải hệ phương trình ∇xψ(x, α) = 0, điểm dừng x*(α) nhận được là:
|
| 4 |
+
|
| 5 |
+
* x∗1(α) = √(1 + 2α + 3α²) / 2
|
| 6 |
+
* x∗2(α) = 1 + √(1 + 2α) / 2
|
| 7 |
+
|
| 8 |
+
Giới hạn của các thành phần của điểm dừng này khi tham số α tiến về 0+ là:
|
| 9 |
+
|
| 10 |
+
* lim α→0+ x∗1(α) = 0
|
| 11 |
+
* lim α→0+ x∗2(α) = 1
|
| 12 |
+
|
| 13 |
+
Điều này cho thấy rằng khi tham số α giảm dần về 0, điểm dừng x*(α) sẽ tiến về điểm (0, 1).","Khi giải hệ phương trình ∇xψ(x, α) = 0, ta nhận được điểm dừng x*(α) = (x*1(α), x*2(α))T với x*1(α) = (√1 + 2α + 3α - 1)/2 và x*2(α) = 1 + (√1 + 2α)/2. Khi tham số α > 0 và giảm dần về 0, giới hạn của các thành phần của điểm dừng là limα→0+ x*1(α) = 0 và limα→0+ x*2(α) = 1.",0.6666666666666666,,0.9999999999,1.0,0.5591398630901252,20260523_212654
|
| 14 |
+
"Theo Định lý về độ lệch bù, ý nghĩa kinh tế đối với các phương pháp sản xuất là gì khi một phương pháp được áp dụng và khi không được áp dụng?","['20/29\nTheo Định lý về độ lệch bù, ta có những ý nghĩa kinh tế quan\ntrọng sau đây:\n/cgĐối với các phương pháp sản xuất:\n Nếu phương pháp thứ j được áp dụng (t∗\nj > 0):\nmP\ni=1\naijz∗\ni = cj\n Tổng giá trị các sản phẩm sản xuất theo phương phápj phải\nvừa đúng bằng chi phí cj. ○ Nếu phương pháp thứ j không được áp dụng (t∗\nj = 0):\nmP\ni=1\naijz∗\ni < cj Tổng giá trị các sản phẩm sản xuất theo\nphương pháp j thấp hơn chi phícj (sản xuất theo phương pháp\nnày sẽ bị lỗ).', '12/29\nĐịnh lý về độ lệch bù\nGiả sử x là phương án của (P) và y là phương án của (D). Khi đó, x, y là phương án tối ưu của cặp bài toán đối ngẫu\nkhi và chỉ khi\n(\n⟨Ax − b, y⟩ = 0 (1)\n⟨c − ATy, x⟩ = 0 (2)\nNhận xét:\ni) Nếu bài toán gốc có dạng chính tắc (Ax = b, x ≥ 0), điều kiện\n(1) luôn thỏa mãn.', 'Khi đó, điều kiện độ lệch bù chỉ còn là:\n⟨c − ATy, x⟩ = 0\nii) Điều kiện độ lệch bù cho phép tìm nghiệm tối ưu của bài toán\nnày khi biết nghiệm tối ưu của bài toán kia.']","Theo Định lý về độ lệch bù, ý nghĩa kinh tế đối với các phương pháp sản xuất như sau:
|
| 15 |
+
|
| 16 |
+
* Khi phương pháp thứ j được áp dụng (t∗j > 0):
|
| 17 |
+
+ Tổng giá trị các sản phẩm sản xuất theo phương pháp j phải vừa đúng bằng chi phí cj.
|
| 18 |
+
* Khi phương pháp thứ j không được áp dụng (t∗j = 0):
|
| 19 |
+
+ Tổng giá trị các sản phẩm sản xuất theo phương pháp j thấp hơn chi phí cj, nghĩa là sản xuất theo phương pháp này sẽ bị lỗ.","Theo Định lý về độ lệch bù, đối với các phương pháp sản xuất, nếu phương pháp thứ j được áp dụng (t∗j > 0), tổng giá trị các sản phẩm sản xuất theo phương pháp j phải vừa đúng bằng chi phí cj. Ngược lại, nếu phương pháp thứ j không được áp dụng (t∗j = 0), tổng giá trị các sản phẩm sản xuất theo phương pháp j thấp hơn chi phí cj, điều này có nghĩa là sản xuất theo phương pháp này sẽ bị lỗ.",1.0,,0.9999999999,1.0,0.6709179689144527,20260523_212654
|
evaluation/results/ragas_results_20260523_212654.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"user_input":"Trong Bước (k3) của thuật toán, siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là gì và có chức năng như thế nào?",
|
| 4 |
+
"retrieved_contexts":[
|
| 5 |
+
"6\/46\nChú ý: Tại Bước (k3) của thuật toán trên:\ni) Ta luôn có ∇gik(xk) ̸= 0 vì nếu ngược lại thì dogik là hàm lồi\nnên ta có\ngik(x) ≥ gik(xk) + ⟨∇gik(xk), x − xk⟩ > 0 ∀x\nvà điều này có nghĩa là tập chấp nhận được X của bài toán\nđang xét bằng rỗng;\nii) Siêu phẳng\n{x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0}\nđể xây dựng đa diệnXk+1 được gọi là siêu phẳng cắt. Đây\nchính là siêu phẳng tách tập lồi X và điểm xk \/∈ X.",
|
| 6 |
+
"Giải\nmin\n\u001a\n⟨∇f (x1), x⟩ = 2\n5x1 + 4\n5x2\n\f\f\f\f x ∈ D\n\u001b\n.",
|
| 7 |
+
"Phương án cực biên\nx0 = (b1, b2, ."
|
| 8 |
+
],
|
| 9 |
+
"response":"Trong Bước (k3) của thuật toán, siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là siêu phẳng cắt. Siêu phẳng này có chức năng tách tập lồi X và điểm xk không thuộc X. Nó được sử dụng để xây dựng đa diện Xk+1.",
|
| 10 |
+
"reference":"Siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là siêu phẳng cắt. Đây chính là siêu phẳng tách tập lồi X và điểm xk không thuộc X.",
|
| 11 |
+
"faithfulness":1.0,
|
| 12 |
+
"answer_relevancy":null,
|
| 13 |
+
"context_precision":0.9999999999,
|
| 14 |
+
"context_recall":1.0,
|
| 15 |
+
"answer_correctness":0.647569239,
|
| 16 |
+
"timestamp":"20260523_212654"
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"user_input":"Khi giải hệ phương trình ∇xψ(x, α) = 0, điểm dừng x*(α) nhận được là gì và giới hạn của các thành phần của điểm dừng này khi tham số α tiến về 0+ là bao nhiêu?",
|
| 20 |
+
"retrieved_contexts":[
|
| 21 |
+
"45\/46\nGiải hệ phương trình\n∇xψ(x, α) = 0 ⇐ ⇒\n\n\n\n1 − α\n1 + x1 − x2\n2\n= 0\n−2 + 2αx2\n1 + x1 − x2\n2\n− α\nx2\n= 0\nta nhận được điểm dừngx∗(α) = (x∗\n1(α), x∗\n2(α))T với\nx∗\n1(α) =\n√\n1 + 2α + 3α − 1\n2 , x∗\n2(α) = 1 +\n√\n1 + 2α\n2 . Vì tham sốα > 0 và giảm dần về 0 nên\nlim\nα→0+\nx∗\n1(α) = 0, lim\nα→0+\nx∗\n2(α) = 1.",
|
| 22 |
+
"Kiểm tra gradient tại x1:\n∇f (x1) =\n\u00123 · 12 − 3\n2 · 1 − 2\n\u0013\n=\n\u00120\n0\n\u0013\n. Kết luận: Dừng thuật toán với điểm dừng\nx1 = (1, 1).",
|
| 23 |
+
"25\/37\nVì∇f (x1) = 0, điểm x1 = (1, 1)T là điểm dừng của hàm f . Kết luận: Dừng thuật toán tạix1 = (1, 1)."
|
| 24 |
+
],
|
| 25 |
+
"response":"Khi giải hệ phương trình ∇xψ(x, α) = 0, điểm dừng x*(α) nhận được là:\n\n* x∗1(α) = √(1 + 2α + 3α²) \/ 2\n* x∗2(α) = 1 + √(1 + 2α) \/ 2\n\nGiới hạn của các thành phần của điểm dừng này khi tham số α tiến về 0+ là:\n\n* lim α→0+ x∗1(α) = 0\n* lim α→0+ x∗2(α) = 1\n\nĐiều này cho thấy rằng khi tham số α giảm dần về 0, điểm dừng x*(α) sẽ tiến về điểm (0, 1).",
|
| 26 |
+
"reference":"Khi giải hệ phương trình ∇xψ(x, α) = 0, ta nhận được điểm dừng x*(α) = (x*1(α), x*2(α))T với x*1(α) = (√1 + 2α + 3α - 1)\/2 và x*2(α) = 1 + (√1 + 2α)\/2. Khi tham số α > 0 và giảm dần về 0, giới hạn của các thành phần của điểm dừng là limα→0+ x*1(α) = 0 và limα→0+ x*2(α) = 1.",
|
| 27 |
+
"faithfulness":0.6666666667,
|
| 28 |
+
"answer_relevancy":null,
|
| 29 |
+
"context_precision":0.9999999999,
|
| 30 |
+
"context_recall":1.0,
|
| 31 |
+
"answer_correctness":0.5591398631,
|
| 32 |
+
"timestamp":"20260523_212654"
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"user_input":"Theo Định lý về độ lệch bù, ý nghĩa kinh tế đối với các phương pháp sản xuất là gì khi một phương pháp được áp dụng và khi không được áp dụng?",
|
| 36 |
+
"retrieved_contexts":[
|
| 37 |
+
"20\/29\nTheo Định lý về độ lệch bù, ta có những ý nghĩa kinh tế quan\ntrọng sau đây:\n\/cgĐối với các phương pháp sản xuất:\n Nếu phương pháp thứ j được áp dụng (t∗\nj > 0):\nmP\ni=1\naijz∗\ni = cj\n Tổng giá trị các sản phẩm sản xuất theo phương phápj phải\nvừa đúng bằng chi phí cj. ○ Nếu phương pháp thứ j không được áp dụng (t∗\nj = 0):\nmP\ni=1\naijz∗\ni < cj Tổng giá trị các sản phẩm sản xuất theo\nphương pháp j thấp hơn chi phícj (sản xuất theo phương pháp\nnày sẽ bị lỗ).",
|
| 38 |
+
"12\/29\nĐịnh lý về độ lệch bù\nGiả sử x là phương án của (P) và y là phương án của (D). Khi đó, x, y là phương án tối ưu của cặp bài toán đối ngẫu\nkhi và chỉ khi\n(\n⟨Ax − b, y⟩ = 0 (1)\n⟨c − ATy, x⟩ = 0 (2)\nNhận xét:\ni) Nếu bài toán gốc có dạng chính tắc (Ax = b, x ≥ 0), điều kiện\n(1) luôn thỏa mãn.",
|
| 39 |
+
"Khi đó, điều kiện độ lệch bù chỉ còn là:\n⟨c − ATy, x⟩ = 0\nii) Điều kiện độ lệch bù cho phép tìm nghiệm tối ưu của bài toán\nnày khi biết nghiệm tối ưu của bài toán kia."
|
| 40 |
+
],
|
| 41 |
+
"response":"Theo Định lý về độ lệch bù, ý nghĩa kinh tế đối với các phương pháp sản xuất như sau:\n\n* Khi phương pháp thứ j được áp dụng (t∗j > 0):\n + Tổng giá trị các sản phẩm sản xuất theo phương pháp j phải vừa đúng bằng chi phí cj.\n* Khi phương pháp thứ j không được áp dụng (t∗j = 0):\n + Tổng giá trị các sản phẩm sản xuất theo phương pháp j thấp hơn chi phí cj, nghĩa là sản xuất theo phương pháp này sẽ bị lỗ.",
|
| 42 |
+
"reference":"Theo Định lý về độ lệch bù, đối với các phương pháp sản xuất, nếu phương pháp thứ j được áp dụng (t∗j > 0), tổng giá trị các sản phẩm sản xuất theo phương pháp j phải vừa đúng bằng chi phí cj. Ngược lại, nếu phương pháp thứ j không được áp dụng (t∗j = 0), tổng giá trị các sản phẩm sản xuất theo phương pháp j thấp hơn chi phí cj, điều này có nghĩa là sản xuất theo phương pháp này sẽ bị lỗ.",
|
| 43 |
+
"faithfulness":1.0,
|
| 44 |
+
"answer_relevancy":null,
|
| 45 |
+
"context_precision":0.9999999999,
|
| 46 |
+
"context_recall":1.0,
|
| 47 |
+
"answer_correctness":0.6709179689,
|
| 48 |
+
"timestamp":"20260523_212654"
|
| 49 |
+
}
|
| 50 |
+
]
|
evaluation/results/ragas_results_latest.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"user_input":"Trong Bước (k3) của thuật toán, siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là gì và có chức năng như thế nào?",
|
| 4 |
+
"retrieved_contexts":[
|
| 5 |
+
"6\/46\nChú ý: Tại Bước (k3) của thuật toán trên:\ni) Ta luôn có ∇gik(xk) ̸= 0 vì nếu ngược lại thì dogik là hàm lồi\nnên ta có\ngik(x) ≥ gik(xk) + ⟨∇gik(xk), x − xk⟩ > 0 ∀x\nvà điều này có nghĩa là tập chấp nhận được X của bài toán\nđang xét bằng rỗng;\nii) Siêu phẳng\n{x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0}\nđể xây dựng đa diệnXk+1 được gọi là siêu phẳng cắt. Đây\nchính là siêu phẳng tách tập lồi X và điểm xk \/∈ X.",
|
| 6 |
+
"Giải\nmin\n\u001a\n⟨∇f (x1), x⟩ = 2\n5x1 + 4\n5x2\n\f\f\f\f x ∈ D\n\u001b\n.",
|
| 7 |
+
"Phương án cực biên\nx0 = (b1, b2, ."
|
| 8 |
+
],
|
| 9 |
+
"response":"Trong Bước (k3) của thuật toán, siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là siêu phẳng cắt. Siêu phẳng này có chức năng tách tập lồi X và điểm xk không thuộc X. Nó được sử dụng để xây dựng đa diện Xk+1.",
|
| 10 |
+
"reference":"Siêu phẳng {x ∈ Rn | ⟨∇gik(xk), x − xk⟩ + gik(xk) = 0} được gọi là siêu phẳng cắt. Đây chính là siêu phẳng tách tập lồi X và điểm xk không thuộc X.",
|
| 11 |
+
"faithfulness":1.0,
|
| 12 |
+
"answer_relevancy":null,
|
| 13 |
+
"context_precision":0.9999999999,
|
| 14 |
+
"context_recall":1.0,
|
| 15 |
+
"answer_correctness":0.647569239,
|
| 16 |
+
"timestamp":"20260523_212654"
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"user_input":"Khi giải hệ phương trình ∇xψ(x, α) = 0, điểm dừng x*(α) nhận được là gì và giới hạn của các thành phần của điểm dừng này khi tham số α tiến về 0+ là bao nhiêu?",
|
| 20 |
+
"retrieved_contexts":[
|
| 21 |
+
"45\/46\nGiải hệ phương trình\n∇xψ(x, α) = 0 ⇐ ⇒\n\n\n\n1 − α\n1 + x1 − x2\n2\n= 0\n−2 + 2αx2\n1 + x1 − x2\n2\n− α\nx2\n= 0\nta nhận được điểm dừngx∗(α) = (x∗\n1(α), x∗\n2(α))T với\nx∗\n1(α) =\n√\n1 + 2α + 3α − 1\n2 , x∗\n2(α) = 1 +\n√\n1 + 2α\n2 . Vì tham sốα > 0 và giảm dần về 0 nên\nlim\nα→0+\nx∗\n1(α) = 0, lim\nα→0+\nx∗\n2(α) = 1.",
|
| 22 |
+
"Kiểm tra gradient tại x1:\n∇f (x1) =\n\u00123 · 12 − 3\n2 · 1 − 2\n\u0013\n=\n\u00120\n0\n\u0013\n. Kết luận: Dừng thuật toán với điểm dừng\nx1 = (1, 1).",
|
| 23 |
+
"25\/37\nVì∇f (x1) = 0, điểm x1 = (1, 1)T là điểm dừng của hàm f . Kết luận: Dừng thuật toán tạix1 = (1, 1)."
|
| 24 |
+
],
|
| 25 |
+
"response":"Khi giải hệ phương trình ∇xψ(x, α) = 0, điểm dừng x*(α) nhận được là:\n\n* x∗1(α) = √(1 + 2α + 3α²) \/ 2\n* x∗2(α) = 1 + √(1 + 2α) \/ 2\n\nGiới hạn của các thành phần của điểm dừng này khi tham số α tiến về 0+ là:\n\n* lim α→0+ x∗1(α) = 0\n* lim α→0+ x∗2(α) = 1\n\nĐiều này cho thấy rằng khi tham số α giảm dần về 0, điểm dừng x*(α) sẽ tiến về điểm (0, 1).",
|
| 26 |
+
"reference":"Khi giải hệ phương trình ∇xψ(x, α) = 0, ta nhận được điểm dừng x*(α) = (x*1(α), x*2(α))T với x*1(α) = (√1 + 2α + 3α - 1)\/2 và x*2(α) = 1 + (√1 + 2α)\/2. Khi tham số α > 0 và giảm dần về 0, giới hạn của các thành phần của điểm dừng là limα→0+ x*1(α) = 0 và limα→0+ x*2(α) = 1.",
|
| 27 |
+
"faithfulness":0.6666666667,
|
| 28 |
+
"answer_relevancy":null,
|
| 29 |
+
"context_precision":0.9999999999,
|
| 30 |
+
"context_recall":1.0,
|
| 31 |
+
"answer_correctness":0.5591398631,
|
| 32 |
+
"timestamp":"20260523_212654"
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"user_input":"Theo Định lý về độ lệch bù, ý nghĩa kinh tế đối với các phương pháp sản xuất là gì khi một phương pháp được áp dụng và khi không được áp dụng?",
|
| 36 |
+
"retrieved_contexts":[
|
| 37 |
+
"20\/29\nTheo Định lý về độ lệch bù, ta có những ý nghĩa kinh tế quan\ntrọng sau đây:\n\/cgĐối với các phương pháp sản xuất:\n Nếu phương pháp thứ j được áp dụng (t∗\nj > 0):\nmP\ni=1\naijz∗\ni = cj\n Tổng giá trị các sản phẩm sản xuất theo phương phápj phải\nvừa đúng bằng chi phí cj. ○ Nếu phương pháp thứ j không được áp dụng (t∗\nj = 0):\nmP\ni=1\naijz∗\ni < cj Tổng giá trị các sản phẩm sản xuất theo\nphương pháp j thấp hơn chi phícj (sản xuất theo phương pháp\nnày sẽ bị lỗ).",
|
| 38 |
+
"12\/29\nĐịnh lý về độ lệch bù\nGiả sử x là phương án của (P) và y là phương án của (D). Khi đó, x, y là phương án tối ưu của cặp bài toán đối ngẫu\nkhi và chỉ khi\n(\n⟨Ax − b, y⟩ = 0 (1)\n⟨c − ATy, x⟩ = 0 (2)\nNhận xét:\ni) Nếu bài toán gốc có dạng chính tắc (Ax = b, x ≥ 0), điều kiện\n(1) luôn thỏa mãn.",
|
| 39 |
+
"Khi đó, điều kiện độ lệch bù chỉ còn là:\n⟨c − ATy, x⟩ = 0\nii) Điều kiện độ lệch bù cho phép tìm nghiệm tối ưu của bài toán\nnày khi biết nghiệm tối ưu của bài toán kia."
|
| 40 |
+
],
|
| 41 |
+
"response":"Theo Định lý về độ lệch bù, ý nghĩa kinh tế đối với các phương pháp sản xuất như sau:\n\n* Khi phương pháp thứ j được áp dụng (t∗j > 0):\n + Tổng giá trị các sản phẩm sản xuất theo phương pháp j phải vừa đúng bằng chi phí cj.\n* Khi phương pháp thứ j không được áp dụng (t∗j = 0):\n + Tổng giá trị các sản phẩm sản xuất theo phương pháp j thấp hơn chi phí cj, nghĩa là sản xuất theo phương pháp này sẽ bị lỗ.",
|
| 42 |
+
"reference":"Theo Định lý về độ lệch bù, đối với các phương pháp sản xuất, nếu phương pháp thứ j được áp dụng (t∗j > 0), tổng giá trị các sản phẩm sản xuất theo phương pháp j phải vừa đúng bằng chi phí cj. Ngược lại, nếu phương pháp thứ j không được áp dụng (t∗j = 0), tổng giá trị các sản phẩm sản xuất theo phương pháp j thấp hơn chi phí cj, điều này có nghĩa là sản xuất theo phương pháp này sẽ bị lỗ.",
|
| 43 |
+
"faithfulness":1.0,
|
| 44 |
+
"answer_relevancy":null,
|
| 45 |
+
"context_precision":0.9999999999,
|
| 46 |
+
"context_recall":1.0,
|
| 47 |
+
"answer_correctness":0.6709179689,
|
| 48 |
+
"timestamp":"20260523_212654"
|
| 49 |
+
}
|
| 50 |
+
]
|
evaluation/results/ragas_summary_20260523_212654.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"evaluation_timestamp": "20260523_212654",
|
| 3 |
+
"total_samples": 3,
|
| 4 |
+
"metrics_used": [
|
| 5 |
+
"faithfulness",
|
| 6 |
+
"answer_relevancy",
|
| 7 |
+
"context_precision",
|
| 8 |
+
"context_recall",
|
| 9 |
+
"answer_correctness"
|
| 10 |
+
],
|
| 11 |
+
"average_scores": {
|
| 12 |
+
"faithfulness": 0.8888888888888888,
|
| 13 |
+
"answer_relevancy": NaN,
|
| 14 |
+
"context_precision": 0.9999999999,
|
| 15 |
+
"context_recall": 1.0,
|
| 16 |
+
"answer_correctness": 0.6258756903411157
|
| 17 |
+
},
|
| 18 |
+
"overall_score": 0.8786911447825011
|
| 19 |
+
}
|
evaluation/results/ragas_summary_latest.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"evaluation_timestamp": "20260523_212654",
|
| 3 |
+
"total_samples": 3,
|
| 4 |
+
"metrics_used": [
|
| 5 |
+
"faithfulness",
|
| 6 |
+
"answer_relevancy",
|
| 7 |
+
"context_precision",
|
| 8 |
+
"context_recall",
|
| 9 |
+
"answer_correctness"
|
| 10 |
+
],
|
| 11 |
+
"average_scores": {
|
| 12 |
+
"faithfulness": 0.8888888888888888,
|
| 13 |
+
"answer_relevancy": NaN,
|
| 14 |
+
"context_precision": 0.9999999999,
|
| 15 |
+
"context_recall": 1.0,
|
| 16 |
+
"answer_correctness": 0.6258756903411157
|
| 17 |
+
},
|
| 18 |
+
"overall_score": 0.8786911447825011
|
| 19 |
+
}
|
evaluation/run_eval.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Script chạy RAGAS evaluation trực tiếp (không qua notebook).
|
| 3 |
+
Dùng khi notebook timeout hoặc có vấn đề với kernel.
|
| 4 |
+
"""
|
| 5 |
+
import os, sys
|
| 6 |
+
|
| 7 |
+
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 8 |
+
sys.path.insert(0, PROJECT_ROOT)
|
| 9 |
+
os.chdir(PROJECT_ROOT)
|
| 10 |
+
|
| 11 |
+
from dotenv import load_dotenv
|
| 12 |
+
load_dotenv(os.path.join(PROJECT_ROOT, ".env"))
|
| 13 |
+
|
| 14 |
+
# ── Bước 1: Test RAG pipeline 1 câu ──────────────────────────
|
| 15 |
+
print("=" * 60)
|
| 16 |
+
print("BƯỚC 1: Test RAG pipeline")
|
| 17 |
+
print("=" * 60)
|
| 18 |
+
from evaluation.rag_pipeline import run_rag_pipeline
|
| 19 |
+
|
| 20 |
+
test_q = "Trong Bước (k3) của thuật toán, siêu phẳng là gì?"
|
| 21 |
+
print(f"Test câu hỏi: {test_q[:60]}...")
|
| 22 |
+
result = run_rag_pipeline(test_q, session_id="test_eval")
|
| 23 |
+
print(f"Answer ({len(result['answer'])} ký tự): {result['answer'][:200]}...")
|
| 24 |
+
print(f"Contexts: {len(result['contexts'])}")
|
| 25 |
+
print()
|
| 26 |
+
|
| 27 |
+
# ── Bước 2: Thu thập RAG outputs ─────────────────────────────
|
| 28 |
+
print("=" * 60)
|
| 29 |
+
print("BƯỚC 2: Thu thập RAG outputs (3 câu, delay=4s)")
|
| 30 |
+
print("=" * 60)
|
| 31 |
+
from evaluation.evaluate import collect_rag_outputs
|
| 32 |
+
|
| 33 |
+
rag_results = collect_rag_outputs(
|
| 34 |
+
"evaluation/optimization_dataset.json",
|
| 35 |
+
delay_seconds=2.0
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
# ── Bước 3: RAGAS evaluation ──────────────────────────────────
|
| 39 |
+
print("=" * 60)
|
| 40 |
+
print("BƯỚC 3: RAGAS Evaluation (Groq Llama 3.1 70B)")
|
| 41 |
+
print("=" * 60)
|
| 42 |
+
from evaluation.evaluate import run_ragas_evaluation
|
| 43 |
+
|
| 44 |
+
df, summary = run_ragas_evaluation(rag_results, output_dir="evaluation/results")
|
| 45 |
+
|
| 46 |
+
print("\n" + "=" * 60)
|
| 47 |
+
print("KẾT QUẢ CUỐI CÙNG")
|
| 48 |
+
print("=" * 60)
|
| 49 |
+
for metric, score in summary["average_scores"].items():
|
| 50 |
+
import math
|
| 51 |
+
if score and not math.isnan(score):
|
| 52 |
+
bar = "#" * int(score * 20) + "-" * (20 - int(score * 20))
|
| 53 |
+
grade = "GOOD" if score >= 0.7 else ("OK" if score >= 0.5 else "POOR")
|
| 54 |
+
print(f" {metric:<25} [{bar}] {score:.4f} ({grade})")
|
| 55 |
+
else:
|
| 56 |
+
print(f" {metric:<25} [--------------------] NaN")
|
| 57 |
+
print("=" * 60)
|
| 58 |
+
print(f" Overall Score: {summary['overall_score']:.4f}")
|
| 59 |
+
print("=" * 60)
|
| 60 |
+
print(f"\nKết quả lưu tại: evaluation/results/")
|