retrival chậm
Browse files- core/embeddings/chunk.py +118 -37
- core/embeddings/generator.py +28 -137
- core/embeddings/retrival.py +112 -24
- core/embeddings/vector_store.py +0 -25
- core/gradio/gradio_rag_qwen.py +58 -23
- data/data_process/quyet_dinh/tieng_anh/06_ Quy định ngoại ngữ từ K70_chính quy_final.md +1 -1
- data/data_process/quyet_dinh/tieng_anh/QD_ngoai_ngu_tu_K65_CQ_final.md +2 -2
- data/data_process/quyet_dinh/tieng_anh/QD_ngoai_ngu_tu_K68_CQ_final.md +1 -1
- evaluation/__init__.py +0 -0
- evaluation/eval_utils.py +108 -20
- evaluation/ragas_eval.py +190 -85
- evaluation/results/comparison_20260105_132921.json +0 -154
- evaluation/results/ragas_20260105_125700.json +0 -111
- evaluation/results/ragas_20260105_130744.json +0 -111
- evaluation/results/ragas_20260105_131901.json +0 -111
- evaluation/results/ragas_20260105_132921.json +0 -111
- evaluation/results/ragas_20260106_072613.csv +5 -0
- evaluation/testsets/testset_20260105_091319.csv +0 -16
- evaluation/testsets/testset_20260105_091319.json +0 -131
- scripts/debug_metadata.py +0 -42
- scripts/test_single_file.py +0 -94
- scripts/update_files.py +0 -226
- scripts/verify_file.py +0 -101
- test/chunk_results.md +0 -0
- test/llama.py +0 -42
- test/test_small_to_big.py +26 -3
core/embeddings/chunk.py
CHANGED
|
@@ -18,7 +18,7 @@ TABLE_ROWS_PER_CHUNK = 15
|
|
| 18 |
|
| 19 |
# Small-to-Big Config
|
| 20 |
ENABLE_TABLE_SUMMARY = True
|
| 21 |
-
MIN_TABLE_ROWS_FOR_SUMMARY =
|
| 22 |
SUMMARY_MODEL = "nex-agi/DeepSeek-V3.1-Nex-N1"
|
| 23 |
SILICONFLOW_BASE_URL = "https://api.siliconflow.com/v1"
|
| 24 |
|
|
@@ -27,6 +27,8 @@ COURSE_PATTERN = re.compile(r"Học\s*phần\s+(.+?)\s*\(\s*m[ãa]\s+([^\)]+)\)"
|
|
| 27 |
TABLE_PLACEHOLDER = re.compile(r"__TBL_(\d+)__")
|
| 28 |
HEADER_KEYWORDS = {'TT', 'STT', 'MÃ', 'TÊN', 'KHỐI', 'SỐ', 'ID', 'NO', '#'}
|
| 29 |
FRONTMATTER_PATTERN = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
|
|
|
|
|
|
|
| 30 |
|
| 31 |
|
| 32 |
def _is_table_row(line: str) -> bool:
|
|
@@ -119,65 +121,118 @@ def _get_summary_client() -> Optional[OpenAI]:
|
|
| 119 |
return _summary_client
|
| 120 |
|
| 121 |
|
| 122 |
-
def _summarize_table(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
if not ENABLE_TABLE_SUMMARY:
|
| 124 |
-
|
| 125 |
|
| 126 |
client = _get_summary_client()
|
| 127 |
if client is None:
|
| 128 |
-
|
| 129 |
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
|
| 136 |
-
{f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
Bảng:
|
| 139 |
{table_text[:3000]}
|
| 140 |
"""
|
| 141 |
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
|
| 156 |
def _create_table_nodes(
|
| 157 |
table_text: str,
|
| 158 |
metadata: dict,
|
| 159 |
-
context_hint: str = ""
|
|
|
|
|
|
|
|
|
|
| 160 |
) -> List[TextNode]:
|
|
|
|
| 161 |
# Count rows to decide if we should summarize
|
| 162 |
row_count = table_text.count("\n")
|
| 163 |
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
-
|
| 169 |
-
|
|
|
|
| 170 |
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
# Create parent node (raw table - will NOT be embedded)
|
| 176 |
parent_id = str(uuid.uuid4())
|
| 177 |
parent_node = TextNode(
|
| 178 |
text=table_text,
|
| 179 |
metadata={
|
| 180 |
-
**
|
| 181 |
"is_table": True,
|
| 182 |
"is_parent": True, # Flag to skip embedding
|
| 183 |
"node_id": parent_id,
|
|
@@ -189,16 +244,19 @@ def _create_table_nodes(
|
|
| 189 |
summary_node = TextNode(
|
| 190 |
text=summary,
|
| 191 |
metadata={
|
| 192 |
-
**
|
| 193 |
"is_table_summary": True,
|
| 194 |
"parent_id": parent_id, # Link to parent
|
| 195 |
}
|
| 196 |
)
|
| 197 |
|
| 198 |
-
|
|
|
|
| 199 |
return [parent_node, summary_node]
|
| 200 |
|
| 201 |
|
|
|
|
|
|
|
| 202 |
def _enrich_metadata(node: BaseNode, source_path: Path | None) -> None:
|
| 203 |
if source_path:
|
| 204 |
node.metadata.update({"source_path": str(source_path), "source_file": source_path.name})
|
|
@@ -259,8 +317,20 @@ def chunk_markdown(text: str, source_path: str | Path | None = None) -> List[Bas
|
|
| 259 |
last_end = 0
|
| 260 |
for match in matches:
|
| 261 |
# Text before table
|
| 262 |
-
|
| 263 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
|
| 265 |
# Table chunks - using Small-to-Big pattern
|
| 266 |
if (idx := int(match.group(1))) < len(tables):
|
|
@@ -270,13 +340,23 @@ def chunk_markdown(text: str, source_path: str | Path | None = None) -> List[Bas
|
|
| 270 |
# Get context hint from header path
|
| 271 |
context_hint = meta.get("Header 1", "") or meta.get("section", "")
|
| 272 |
|
|
|
|
|
|
|
|
|
|
| 273 |
for i, chunk in enumerate(table_chunks):
|
| 274 |
chunk_meta = {**meta}
|
| 275 |
if len(table_chunks) > 1:
|
| 276 |
chunk_meta["table_part"] = f"{i+1}/{len(table_chunks)}"
|
| 277 |
|
| 278 |
# Create parent + summary nodes if applicable
|
| 279 |
-
table_nodes = _create_table_nodes(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
nodes.extend(table_nodes)
|
| 281 |
|
| 282 |
last_end = match.end()
|
|
@@ -284,6 +364,7 @@ def chunk_markdown(text: str, source_path: str | Path | None = None) -> List[Bas
|
|
| 284 |
# Text after table
|
| 285 |
if (after := content[last_end:].strip()) and len(after) >= MIN_CHUNK_SIZE:
|
| 286 |
nodes.extend(_chunk_text(after, meta) if len(after) > CHUNK_SIZE else [TextNode(text=after, metadata=meta.copy())])
|
|
|
|
| 287 |
|
| 288 |
final: List[BaseNode] = []
|
| 289 |
i = 0
|
|
|
|
| 18 |
|
| 19 |
# Small-to-Big Config
|
| 20 |
ENABLE_TABLE_SUMMARY = True
|
| 21 |
+
MIN_TABLE_ROWS_FOR_SUMMARY = 0 # Summarize ALL tables regardless of size
|
| 22 |
SUMMARY_MODEL = "nex-agi/DeepSeek-V3.1-Nex-N1"
|
| 23 |
SILICONFLOW_BASE_URL = "https://api.siliconflow.com/v1"
|
| 24 |
|
|
|
|
| 27 |
TABLE_PLACEHOLDER = re.compile(r"__TBL_(\d+)__")
|
| 28 |
HEADER_KEYWORDS = {'TT', 'STT', 'MÃ', 'TÊN', 'KHỐI', 'SỐ', 'ID', 'NO', '#'}
|
| 29 |
FRONTMATTER_PATTERN = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
|
| 30 |
+
# Pattern để trích xuất số bảng và tiêu đề (ví dụ: "Bảng 3.1 Danh mục các học phần...")
|
| 31 |
+
TABLE_TITLE_PATTERN = re.compile(r"(?:^|\n)#+\s*(?:Bảng|BẢNG)\s*(\d+(?:\.\d+)?)\s*[.:]*\s*(.+?)(?:\n|$)", re.IGNORECASE)
|
| 32 |
|
| 33 |
|
| 34 |
def _is_table_row(line: str) -> bool:
|
|
|
|
| 121 |
return _summary_client
|
| 122 |
|
| 123 |
|
| 124 |
+
def _summarize_table(
|
| 125 |
+
table_text: str,
|
| 126 |
+
context_hint: str = "",
|
| 127 |
+
table_number: str = "",
|
| 128 |
+
table_title: str = "",
|
| 129 |
+
source_file: str = "",
|
| 130 |
+
max_retries: int = 5,
|
| 131 |
+
base_delay: float = 2.0
|
| 132 |
+
) -> str:
|
| 133 |
+
"""Summarize a table with retry logic. Raises exception if all retries fail."""
|
| 134 |
+
import time
|
| 135 |
+
|
| 136 |
if not ENABLE_TABLE_SUMMARY:
|
| 137 |
+
raise RuntimeError("Table summarization is disabled but required. Set ENABLE_TABLE_SUMMARY = True")
|
| 138 |
|
| 139 |
client = _get_summary_client()
|
| 140 |
if client is None:
|
| 141 |
+
raise RuntimeError("SILICONFLOW_API_KEY not set. Cannot summarize tables.")
|
| 142 |
|
| 143 |
+
# Build table identifier string
|
| 144 |
+
table_id_parts = []
|
| 145 |
+
if table_number:
|
| 146 |
+
table_id_parts.append(f"Bảng {table_number}")
|
| 147 |
+
if table_title:
|
| 148 |
+
table_id_parts.append(f'"{table_title}"')
|
| 149 |
+
if source_file:
|
| 150 |
+
table_id_parts.append(f"từ file {source_file}")
|
| 151 |
+
|
| 152 |
+
table_identifier = " - ".join(table_id_parts) if table_id_parts else "Unknown table"
|
| 153 |
+
|
| 154 |
+
prompt = f"""Tóm tắt ngắn gọn nội dung bảng sau bằng tiếng Việt.
|
| 155 |
|
| 156 |
+
{f"**Thông tin bảng:** {table_identifier}" if table_identifier else ""}
|
| 157 |
+
{f"**Ngữ cảnh:** {context_hint}" if context_hint else ""}
|
| 158 |
+
|
| 159 |
+
**YÊU CẦU QUAN TRỌNG:**
|
| 160 |
+
- Bắt đầu tóm tắt bằng việc nêu rõ đây là {f"Bảng {table_number}" if table_number else "bảng nào"}{f' với tiêu đề "{table_title}"' if table_title else ""}{f" thuộc file {source_file}" if source_file else ""}
|
| 161 |
+
- Ghi rõ bảng này liệt kê/quy định về cái gì
|
| 162 |
+
- Nêu các cột chính trong bảng
|
| 163 |
+
- Thông tin quan trọng (nếu có số liệu cụ thể thì nêu ví dụ)
|
| 164 |
|
| 165 |
Bảng:
|
| 166 |
{table_text[:3000]}
|
| 167 |
"""
|
| 168 |
|
| 169 |
+
last_error = None
|
| 170 |
+
for attempt in range(max_retries):
|
| 171 |
+
try:
|
| 172 |
+
response = client.chat.completions.create(
|
| 173 |
+
model=SUMMARY_MODEL,
|
| 174 |
+
messages=[{"role": "user", "content": prompt}],
|
| 175 |
+
temperature=0.3,
|
| 176 |
+
max_tokens=1000,
|
| 177 |
+
)
|
| 178 |
+
summary = response.choices[0].message.content or ""
|
| 179 |
+
if summary.strip():
|
| 180 |
+
return summary.strip()
|
| 181 |
+
else:
|
| 182 |
+
raise ValueError("Empty summary returned from API")
|
| 183 |
+
|
| 184 |
+
except Exception as e:
|
| 185 |
+
last_error = e
|
| 186 |
+
delay = base_delay * (2 ** attempt) # Exponential backoff: 2, 4, 8, 16, 32 seconds
|
| 187 |
+
print(f"⚠️ Retry {attempt + 1}/{max_retries} for {table_identifier}: {e}")
|
| 188 |
+
print(f" Waiting {delay:.1f}s before retry...")
|
| 189 |
+
time.sleep(delay)
|
| 190 |
+
|
| 191 |
+
# All retries failed
|
| 192 |
+
raise RuntimeError(f"Failed to summarize {table_identifier} after {max_retries} retries. Last error: {last_error}")
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
|
| 196 |
|
| 197 |
|
| 198 |
def _create_table_nodes(
|
| 199 |
table_text: str,
|
| 200 |
metadata: dict,
|
| 201 |
+
context_hint: str = "",
|
| 202 |
+
table_number: str = "",
|
| 203 |
+
table_title: str = "",
|
| 204 |
+
source_file: str = ""
|
| 205 |
) -> List[TextNode]:
|
| 206 |
+
"""Create table nodes. For large tables, creates parent+summary nodes with retry until success."""
|
| 207 |
# Count rows to decide if we should summarize
|
| 208 |
row_count = table_text.count("\n")
|
| 209 |
|
| 210 |
+
# Add table info to metadata
|
| 211 |
+
table_meta = {**metadata}
|
| 212 |
+
if table_number:
|
| 213 |
+
table_meta["table_number"] = table_number
|
| 214 |
+
if table_title:
|
| 215 |
+
table_meta["table_title"] = table_title
|
| 216 |
|
| 217 |
+
if row_count < MIN_TABLE_ROWS_FOR_SUMMARY:
|
| 218 |
+
# Table too small, just return as-is (no summary needed)
|
| 219 |
+
return [TextNode(text=table_text, metadata={**table_meta, "is_table": True})]
|
| 220 |
|
| 221 |
+
# Generate summary with retry logic (will raise exception if all retries fail)
|
| 222 |
+
summary = _summarize_table(
|
| 223 |
+
table_text,
|
| 224 |
+
context_hint,
|
| 225 |
+
table_number=table_number,
|
| 226 |
+
table_title=table_title,
|
| 227 |
+
source_file=source_file
|
| 228 |
+
)
|
| 229 |
|
| 230 |
# Create parent node (raw table - will NOT be embedded)
|
| 231 |
parent_id = str(uuid.uuid4())
|
| 232 |
parent_node = TextNode(
|
| 233 |
text=table_text,
|
| 234 |
metadata={
|
| 235 |
+
**table_meta,
|
| 236 |
"is_table": True,
|
| 237 |
"is_parent": True, # Flag to skip embedding
|
| 238 |
"node_id": parent_id,
|
|
|
|
| 244 |
summary_node = TextNode(
|
| 245 |
text=summary,
|
| 246 |
metadata={
|
| 247 |
+
**table_meta,
|
| 248 |
"is_table_summary": True,
|
| 249 |
"parent_id": parent_id, # Link to parent
|
| 250 |
}
|
| 251 |
)
|
| 252 |
|
| 253 |
+
table_id = f"Bảng {table_number}" if table_number else "table"
|
| 254 |
+
print(f"✅ Created summary for {table_id} ({row_count} rows)")
|
| 255 |
return [parent_node, summary_node]
|
| 256 |
|
| 257 |
|
| 258 |
+
|
| 259 |
+
|
| 260 |
def _enrich_metadata(node: BaseNode, source_path: Path | None) -> None:
|
| 261 |
if source_path:
|
| 262 |
node.metadata.update({"source_path": str(source_path), "source_file": source_path.name})
|
|
|
|
| 317 |
last_end = 0
|
| 318 |
for match in matches:
|
| 319 |
# Text before table
|
| 320 |
+
before_text = content[last_end:match.start()].strip()
|
| 321 |
+
|
| 322 |
+
# Extract table number and title from text before table
|
| 323 |
+
table_number = ""
|
| 324 |
+
table_title = ""
|
| 325 |
+
if before_text:
|
| 326 |
+
# Look for patterns like "## Bảng 3.1 Danh mục các học phần..."
|
| 327 |
+
title_match = TABLE_TITLE_PATTERN.search(before_text)
|
| 328 |
+
if title_match:
|
| 329 |
+
table_number = title_match.group(1).strip()
|
| 330 |
+
table_title = title_match.group(2).strip()
|
| 331 |
+
|
| 332 |
+
if before_text and len(before_text) >= MIN_CHUNK_SIZE:
|
| 333 |
+
nodes.extend(_chunk_text(before_text, meta) if len(before_text) > CHUNK_SIZE else [TextNode(text=before_text, metadata=meta.copy())])
|
| 334 |
|
| 335 |
# Table chunks - using Small-to-Big pattern
|
| 336 |
if (idx := int(match.group(1))) < len(tables):
|
|
|
|
| 340 |
# Get context hint from header path
|
| 341 |
context_hint = meta.get("Header 1", "") or meta.get("section", "")
|
| 342 |
|
| 343 |
+
# Get source file for summary
|
| 344 |
+
source_file = meta.get("source_file", "") or (path.name if path else "")
|
| 345 |
+
|
| 346 |
for i, chunk in enumerate(table_chunks):
|
| 347 |
chunk_meta = {**meta}
|
| 348 |
if len(table_chunks) > 1:
|
| 349 |
chunk_meta["table_part"] = f"{i+1}/{len(table_chunks)}"
|
| 350 |
|
| 351 |
# Create parent + summary nodes if applicable
|
| 352 |
+
table_nodes = _create_table_nodes(
|
| 353 |
+
chunk,
|
| 354 |
+
chunk_meta,
|
| 355 |
+
context_hint,
|
| 356 |
+
table_number=table_number,
|
| 357 |
+
table_title=table_title,
|
| 358 |
+
source_file=source_file
|
| 359 |
+
)
|
| 360 |
nodes.extend(table_nodes)
|
| 361 |
|
| 362 |
last_end = match.end()
|
|
|
|
| 364 |
# Text after table
|
| 365 |
if (after := content[last_end:].strip()) and len(after) >= MIN_CHUNK_SIZE:
|
| 366 |
nodes.extend(_chunk_text(after, meta) if len(after) > CHUNK_SIZE else [TextNode(text=after, metadata=meta.copy())])
|
| 367 |
+
|
| 368 |
|
| 369 |
final: List[BaseNode] = []
|
| 370 |
i = 0
|
core/embeddings/generator.py
CHANGED
|
@@ -1,66 +1,17 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
-
import
|
| 3 |
-
from dataclasses import dataclass
|
| 4 |
-
from pathlib import Path
|
| 5 |
-
from typing import Any, Dict, Generator, List, Optional, TYPE_CHECKING
|
| 6 |
-
|
| 7 |
-
from openai import OpenAI
|
| 8 |
|
| 9 |
if TYPE_CHECKING:
|
| 10 |
from core.embeddings.retrival import Retriever
|
| 11 |
|
| 12 |
|
| 13 |
-
|
| 14 |
-
class LLMConfig:
|
| 15 |
-
provider: str = "siliconflow" # "siliconflow" or "groq"
|
| 16 |
-
# SiliconFlow config
|
| 17 |
-
siliconflow_model: str = "nex-agi/DeepSeek-V3.1-Nex-N1"
|
| 18 |
-
siliconflow_api_base: str = "https://api.siliconflow.com/v1"
|
| 19 |
-
# Groq config
|
| 20 |
-
groq_model: str = "llama-3.1-8b-instant"
|
| 21 |
-
groq_api_base: str = "https://api.groq.com/openai/v1"
|
| 22 |
-
# Common config
|
| 23 |
-
temperature: float = 0.0
|
| 24 |
-
max_tokens: int = 4096
|
| 25 |
-
max_context_chars: int = 8000
|
| 26 |
-
|
| 27 |
-
@property
|
| 28 |
-
def model(self) -> str:
|
| 29 |
-
return self.siliconflow_model if self.provider == "siliconflow" else self.groq_model
|
| 30 |
-
|
| 31 |
-
@property
|
| 32 |
-
def api_base_url(self) -> str:
|
| 33 |
-
return self.siliconflow_api_base if self.provider == "siliconflow" else self.groq_api_base
|
| 34 |
-
|
| 35 |
-
@property
|
| 36 |
-
def api_key_env(self) -> str:
|
| 37 |
-
return "SILICONFLOW_API_KEY" if self.provider == "siliconflow" else "GROQ_API_KEY"
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
# Singleton config
|
| 41 |
-
_llm_config: LLMConfig | None = None
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
def get_llm_config() -> LLMConfig:
|
| 45 |
-
global _llm_config
|
| 46 |
-
if _llm_config is None:
|
| 47 |
-
provider = os.getenv("LLM_PROVIDER", "siliconflow").lower()
|
| 48 |
-
_llm_config = LLMConfig(provider=provider)
|
| 49 |
-
print(f"[LLM] Provider: {provider} | Model: {_llm_config.model}")
|
| 50 |
-
return _llm_config
|
| 51 |
-
|
| 52 |
-
|
| 53 |
SYSTEM_PROMPT = """Bạn là Trợ lý học vụ Đại học Bách khoa Hà Nội.
|
| 54 |
|
| 55 |
## NGUYÊN TẮC:
|
| 56 |
-
1. Chỉ trả lời dựa trên CONTEXT được cung cấp. Không suy đoán, không bổ sung thông tin ngoài CONTEXT.
|
| 57 |
2. Nếu CONTEXT chứa nhiều văn bản khác nhau, ưu tiên nội dung mới nhất, TRỪ KHI có điều khoản chuyển tiếp nói khác.
|
| 58 |
-
3.
|
| 59 |
-
- Phải đối chiếu chính xác từng điều kiện với bảng hoặc quy định trong CONTEXT.
|
| 60 |
-
- Không khẳng định “nằm trong khoảng” nếu không đúng.
|
| 61 |
-
- Nếu có nhiều tiêu chí, phải nêu rõ tiêu chí quyết định kết quả cuối cùng.
|
| 62 |
-
4. Cuối câu trả lời, phải trích dẫn nguồn đúng theo tài liệu xuất hiện trong CONTEXT. Không tự tạo nguồn.
|
| 63 |
-
5. Nếu không tìm thấy thông tin trong CONTEXT, trả lời: "Không tìm thấy thông tin trong dữ liệu hiện có."
|
| 64 |
"""
|
| 65 |
|
| 66 |
|
|
@@ -105,103 +56,43 @@ def build_context(results: List[Dict[str, Any]], max_chars: int = 8000) -> str:
|
|
| 105 |
return context[:max_chars] if len(context) > max_chars else context
|
| 106 |
|
| 107 |
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
):
|
| 115 |
self._retriever = retriever
|
| 116 |
-
self.
|
| 117 |
-
self._llm_model = self._config.model
|
| 118 |
-
self._temperature = self._config.temperature
|
| 119 |
-
self._max_tokens = self._config.max_tokens
|
| 120 |
-
self._max_context_chars = self._config.max_context_chars
|
| 121 |
-
|
| 122 |
-
# Get API key based on provider
|
| 123 |
-
api_key = (os.getenv(self._config.api_key_env) or "").strip()
|
| 124 |
-
if not api_key and client is None:
|
| 125 |
-
raise RuntimeError(f"Missing {self._config.api_key_env}")
|
| 126 |
-
self._client = client or OpenAI(api_key=api_key, base_url=self._config.api_base_url)
|
| 127 |
|
| 128 |
-
def
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|
| 131 |
-
def generate(self, question: str, k: int = 5, initial_k: int = 20, mode: str = "hybrid_rerank") -> Dict[str, Any]:
|
| 132 |
results = self._retriever.flexible_search(question, k=k, initial_k=initial_k, mode=mode)
|
| 133 |
|
| 134 |
if not results:
|
| 135 |
return {
|
| 136 |
-
"answer": "Không tìm thấy thông tin trong dữ liệu hiện có.",
|
| 137 |
-
"contexts": [],
|
| 138 |
"results": [],
|
|
|
|
|
|
|
|
|
|
| 139 |
}
|
| 140 |
|
| 141 |
-
|
| 142 |
-
prompt =
|
| 143 |
-
|
| 144 |
-
completion = self._client.chat.completions.create(
|
| 145 |
-
model=self._llm_model,
|
| 146 |
-
messages=[{"role": "user", "content": prompt}],
|
| 147 |
-
temperature=self._temperature,
|
| 148 |
-
max_tokens=self._max_tokens,
|
| 149 |
-
)
|
| 150 |
|
| 151 |
return {
|
| 152 |
-
"answer": completion.choices[0].message.content or "",
|
| 153 |
-
"contexts": [r.get("content", "")[:1000] for r in results],
|
| 154 |
"results": results,
|
|
|
|
|
|
|
|
|
|
| 155 |
}
|
| 156 |
|
| 157 |
-
def generate_stream(
|
| 158 |
-
self, question: str, k: int = 5, initial_k: int = 20
|
| 159 |
-
) -> Generator[str, None, None]:
|
| 160 |
-
results = self._retriever.search_with_rerank(question, k=k, initial_k=initial_k)
|
| 161 |
|
| 162 |
-
|
| 163 |
-
yield "Không tìm thấy thông tin trong dữ liệu hiện có."
|
| 164 |
-
return
|
| 165 |
-
|
| 166 |
-
context = build_context(results, self._max_context_chars)
|
| 167 |
-
prompt = self._build_prompt(question, context)
|
| 168 |
-
|
| 169 |
-
completion = self._client.chat.completions.create(
|
| 170 |
-
model=self._llm_model,
|
| 171 |
-
messages=[{"role": "user", "content": prompt}],
|
| 172 |
-
temperature=self._temperature,
|
| 173 |
-
max_tokens=self._max_tokens,
|
| 174 |
-
stream=True,
|
| 175 |
-
)
|
| 176 |
-
|
| 177 |
-
acc = ""
|
| 178 |
-
for chunk in completion:
|
| 179 |
-
delta = getattr(chunk.choices[0].delta, "content", "") or ""
|
| 180 |
-
if delta:
|
| 181 |
-
acc += delta
|
| 182 |
-
yield acc
|
| 183 |
-
|
| 184 |
-
def generate_stream_from_results(
|
| 185 |
-
self, question: str, results: List[Dict[str, Any]]
|
| 186 |
-
) -> Generator[str, None, None]:
|
| 187 |
-
if not results:
|
| 188 |
-
yield "Không tìm thấy thông tin trong dữ liệu hiện có."
|
| 189 |
-
return
|
| 190 |
-
|
| 191 |
-
context = build_context(results, self._max_context_chars)
|
| 192 |
-
prompt = self._build_prompt(question, context)
|
| 193 |
-
|
| 194 |
-
completion = self._client.chat.completions.create(
|
| 195 |
-
model=self._llm_model,
|
| 196 |
-
messages=[{"role": "user", "content": prompt}],
|
| 197 |
-
temperature=self._temperature,
|
| 198 |
-
max_tokens=self._max_tokens,
|
| 199 |
-
stream=True,
|
| 200 |
-
)
|
| 201 |
-
|
| 202 |
-
acc = ""
|
| 203 |
-
for chunk in completion:
|
| 204 |
-
delta = getattr(chunk.choices[0].delta, "content", "") or ""
|
| 205 |
-
if delta:
|
| 206 |
-
acc += delta
|
| 207 |
-
yield acc
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
+
from typing import Any, Dict, List, TYPE_CHECKING
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
if TYPE_CHECKING:
|
| 5 |
from core.embeddings.retrival import Retriever
|
| 6 |
|
| 7 |
|
| 8 |
+
# System prompt để sử dụng khi gọi LLM (export cho gradio/eval dùng)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
SYSTEM_PROMPT = """Bạn là Trợ lý học vụ Đại học Bách khoa Hà Nội.
|
| 10 |
|
| 11 |
## NGUYÊN TẮC:
|
| 12 |
+
1. Chỉ được đưa ra câu trả lời dựa trên CONTEXT được cung cấp. Không suy đoán, không bổ sung thông tin ngoài CONTEXT.
|
| 13 |
2. Nếu CONTEXT chứa nhiều văn bản khác nhau, ưu tiên nội dung mới nhất, TRỪ KHI có điều khoản chuyển tiếp nói khác.
|
| 14 |
+
3. Nếu không tìm thấy thông tin trong CONTEXT, trả lời: "Không tìm thấy thông tin trong dữ liệu hiện có."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
"""
|
| 16 |
|
| 17 |
|
|
|
|
| 56 |
return context[:max_chars] if len(context) > max_chars else context
|
| 57 |
|
| 58 |
|
| 59 |
+
def build_prompt(question: str, context: str) -> str:
|
| 60 |
+
return f"{SYSTEM_PROMPT}\n\n## CONTEXT:\n{context}\n\n## CÂU HỎI: {question}\n\n## TRẢ LỜI:"
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class RAGContextBuilder:
|
| 64 |
+
|
| 65 |
+
def __init__(self, retriever: "Retriever", max_context_chars: int = 8000):
|
| 66 |
self._retriever = retriever
|
| 67 |
+
self._max_context_chars = max_context_chars
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
+
def retrieve_and_prepare(
|
| 70 |
+
self,
|
| 71 |
+
question: str,
|
| 72 |
+
k: int = 5,
|
| 73 |
+
initial_k: int = 20,
|
| 74 |
+
mode: str = "hybrid_rerank"
|
| 75 |
+
) -> Dict[str, Any]:
|
| 76 |
|
|
|
|
| 77 |
results = self._retriever.flexible_search(question, k=k, initial_k=initial_k, mode=mode)
|
| 78 |
|
| 79 |
if not results:
|
| 80 |
return {
|
|
|
|
|
|
|
| 81 |
"results": [],
|
| 82 |
+
"contexts": [],
|
| 83 |
+
"context_text": "",
|
| 84 |
+
"prompt": "",
|
| 85 |
}
|
| 86 |
|
| 87 |
+
context_text = build_context(results, self._max_context_chars)
|
| 88 |
+
prompt = build_prompt(question, context_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
return {
|
|
|
|
|
|
|
| 91 |
"results": results,
|
| 92 |
+
"contexts": [r.get("content", "")[:1000] for r in results],
|
| 93 |
+
"context_text": context_text,
|
| 94 |
+
"prompt": prompt,
|
| 95 |
}
|
| 96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
+
RAGGenerator = RAGContextBuilder
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/embeddings/retrival.py
CHANGED
|
@@ -111,6 +111,9 @@ class SiliconFlowReranker(BaseDocumentCompressor):
|
|
| 111 |
|
| 112 |
class Retriever:
|
| 113 |
def __init__(self, vector_db: "ChromaVectorDB", use_reranker: bool = True):
|
|
|
|
|
|
|
|
|
|
| 114 |
self._vector_db = vector_db
|
| 115 |
self._config = get_retrieval_config()
|
| 116 |
self._reranker: Optional[SiliconFlowReranker] = None
|
|
@@ -118,18 +121,76 @@ class Retriever:
|
|
| 118 |
self._vector_retriever = self._vector_db.vectorstore.as_retriever(
|
| 119 |
search_kwargs={"k": self._config.initial_k}
|
| 120 |
)
|
| 121 |
-
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
if use_reranker:
|
| 125 |
self._reranker = self._init_reranker()
|
| 126 |
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
def _init_bm25(self) -> Optional[BM25Retriever]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
docs = self._vector_db.get_all_documents()
|
| 132 |
if not docs:
|
|
|
|
| 133 |
return None
|
| 134 |
|
| 135 |
lc_docs = [
|
|
@@ -138,17 +199,37 @@ class Retriever:
|
|
| 138 |
]
|
| 139 |
bm25 = BM25Retriever.from_documents(lc_docs)
|
| 140 |
bm25.k = self._config.initial_k
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
return bm25
|
| 142 |
-
except Exception:
|
|
|
|
| 143 |
return None
|
|
|
|
| 144 |
|
| 145 |
-
def
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
weights=[self._config.vector_weight, self._config.bm25_weight]
|
| 150 |
)
|
| 151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
def _init_reranker(self) -> Optional[SiliconFlowReranker]:
|
| 154 |
api_key = os.getenv("SILICONFLOW_API_KEY", "").strip()
|
|
@@ -162,12 +243,15 @@ class Retriever:
|
|
| 162 |
)
|
| 163 |
|
| 164 |
def _build_final(self):
|
|
|
|
|
|
|
| 165 |
if self._reranker:
|
| 166 |
return ContextualCompressionRetriever(
|
| 167 |
base_compressor=self._reranker,
|
| 168 |
-
base_retriever=
|
| 169 |
)
|
| 170 |
-
return
|
|
|
|
| 171 |
|
| 172 |
@property
|
| 173 |
def has_reranker(self) -> bool:
|
|
@@ -210,29 +294,32 @@ class Retriever:
|
|
| 210 |
def bm25_search(self, text: str, *, k: int | None = None) -> List[Dict[str, Any]]:
|
| 211 |
if not text.strip():
|
| 212 |
return []
|
| 213 |
-
|
|
|
|
|
|
|
| 214 |
return self.vector_search(text, k=k)
|
| 215 |
|
| 216 |
k = k or self._config.top_k
|
| 217 |
-
|
| 218 |
-
results =
|
| 219 |
return [self._to_result(doc, i + 1) for i, doc in enumerate(results[:k])]
|
| 220 |
|
| 221 |
def hybrid_search(
|
| 222 |
self, text: str, *, k: int | None = None, initial_k: int | None = None
|
| 223 |
) -> List[Dict[str, Any]]:
|
| 224 |
-
"""Hybrid search (Vector + BM25) WITHOUT reranking."""
|
| 225 |
if not text.strip():
|
| 226 |
return []
|
| 227 |
|
| 228 |
k = k or self._config.top_k
|
| 229 |
if initial_k:
|
| 230 |
self._vector_retriever.search_kwargs["k"] = initial_k
|
| 231 |
-
|
| 232 |
-
|
|
|
|
| 233 |
|
| 234 |
-
# Dùng ensemble_retriever (KHÔNG có reranker)
|
| 235 |
-
|
|
|
|
| 236 |
return [self._to_result(doc, i + 1) for i, doc in enumerate(results[:k])]
|
| 237 |
|
| 238 |
def search_with_rerank(
|
|
@@ -243,7 +330,6 @@ class Retriever:
|
|
| 243 |
where: Optional[Dict[str, Any]] = None,
|
| 244 |
initial_k: int | None = None,
|
| 245 |
) -> List[Dict[str, Any]]:
|
| 246 |
-
"""Hybrid search (Vector + BM25) WITH reranking."""
|
| 247 |
if not text.strip():
|
| 248 |
return []
|
| 249 |
|
|
@@ -260,13 +346,15 @@ class Retriever:
|
|
| 260 |
for i, doc in enumerate(results[:k])
|
| 261 |
]
|
| 262 |
|
| 263 |
-
#
|
| 264 |
if initial_k:
|
| 265 |
self._vector_retriever.search_kwargs["k"] = initial_k
|
| 266 |
-
|
| 267 |
-
|
|
|
|
| 268 |
|
| 269 |
-
|
|
|
|
| 270 |
return [
|
| 271 |
self._to_result(doc, i + 1, rerank_score=doc.metadata.get("rerank_score"))
|
| 272 |
for i, doc in enumerate(results[:k])
|
|
|
|
| 111 |
|
| 112 |
class Retriever:
|
| 113 |
def __init__(self, vector_db: "ChromaVectorDB", use_reranker: bool = True):
|
| 114 |
+
import time
|
| 115 |
+
start = time.time()
|
| 116 |
+
|
| 117 |
self._vector_db = vector_db
|
| 118 |
self._config = get_retrieval_config()
|
| 119 |
self._reranker: Optional[SiliconFlowReranker] = None
|
|
|
|
| 121 |
self._vector_retriever = self._vector_db.vectorstore.as_retriever(
|
| 122 |
search_kwargs={"k": self._config.initial_k}
|
| 123 |
)
|
| 124 |
+
|
| 125 |
+
# Lazy-load BM25 - only initialize when needed
|
| 126 |
+
self._bm25_retriever: Optional[BM25Retriever] = None
|
| 127 |
+
self._bm25_initialized = False
|
| 128 |
+
self._ensemble_retriever: Optional[EnsembleRetriever] = None
|
| 129 |
+
|
| 130 |
+
# BM25 cache path (persist to disk)
|
| 131 |
+
from pathlib import Path
|
| 132 |
+
persist_dir = getattr(self._vector_db.config, 'persist_dir', None)
|
| 133 |
+
if persist_dir:
|
| 134 |
+
self._bm25_cache_path = Path(persist_dir) / "bm25_cache.pkl"
|
| 135 |
+
else:
|
| 136 |
+
self._bm25_cache_path = None
|
| 137 |
|
| 138 |
if use_reranker:
|
| 139 |
self._reranker = self._init_reranker()
|
| 140 |
|
| 141 |
+
logger.info(f"Retriever initialized in {time.time() - start:.2f}s (BM25 lazy-loaded)")
|
| 142 |
+
|
| 143 |
+
def _save_bm25_cache(self, bm25: BM25Retriever) -> None:
|
| 144 |
+
"""Save BM25 retriever to disk for fast loading."""
|
| 145 |
+
if not self._bm25_cache_path:
|
| 146 |
+
return
|
| 147 |
+
try:
|
| 148 |
+
import pickle
|
| 149 |
+
with open(self._bm25_cache_path, 'wb') as f:
|
| 150 |
+
pickle.dump(bm25, f)
|
| 151 |
+
logger.info(f"BM25 cache saved to {self._bm25_cache_path}")
|
| 152 |
+
except Exception as e:
|
| 153 |
+
logger.warning(f"Failed to save BM25 cache: {e}")
|
| 154 |
+
|
| 155 |
+
def _load_bm25_cache(self) -> Optional[BM25Retriever]:
|
| 156 |
+
"""Load BM25 retriever from disk cache."""
|
| 157 |
+
if not self._bm25_cache_path or not self._bm25_cache_path.exists():
|
| 158 |
+
return None
|
| 159 |
+
try:
|
| 160 |
+
import pickle
|
| 161 |
+
import time
|
| 162 |
+
start = time.time()
|
| 163 |
+
with open(self._bm25_cache_path, 'rb') as f:
|
| 164 |
+
bm25 = pickle.load(f)
|
| 165 |
+
bm25.k = self._config.initial_k
|
| 166 |
+
logger.info(f"BM25 loaded from cache in {time.time() - start:.2f}s")
|
| 167 |
+
return bm25
|
| 168 |
+
except Exception as e:
|
| 169 |
+
logger.warning(f"Failed to load BM25 cache: {e}")
|
| 170 |
+
return None
|
| 171 |
|
| 172 |
def _init_bm25(self) -> Optional[BM25Retriever]:
|
| 173 |
+
"""Initialize BM25 retriever (lazy-loaded, with disk cache)."""
|
| 174 |
+
if self._bm25_initialized:
|
| 175 |
+
return self._bm25_retriever
|
| 176 |
+
|
| 177 |
+
self._bm25_initialized = True
|
| 178 |
+
|
| 179 |
+
# Try loading from cache first
|
| 180 |
+
cached = self._load_bm25_cache()
|
| 181 |
+
if cached:
|
| 182 |
+
self._bm25_retriever = cached
|
| 183 |
+
return cached
|
| 184 |
+
|
| 185 |
+
# Build from scratch
|
| 186 |
try:
|
| 187 |
+
import time
|
| 188 |
+
start = time.time()
|
| 189 |
+
logger.info("Building BM25 index from documents...")
|
| 190 |
+
|
| 191 |
docs = self._vector_db.get_all_documents()
|
| 192 |
if not docs:
|
| 193 |
+
logger.warning("No documents found for BM25")
|
| 194 |
return None
|
| 195 |
|
| 196 |
lc_docs = [
|
|
|
|
| 199 |
]
|
| 200 |
bm25 = BM25Retriever.from_documents(lc_docs)
|
| 201 |
bm25.k = self._config.initial_k
|
| 202 |
+
|
| 203 |
+
self._bm25_retriever = bm25
|
| 204 |
+
logger.info(f"BM25 built with {len(docs)} docs in {time.time() - start:.2f}s")
|
| 205 |
+
|
| 206 |
+
# Save to cache for next time
|
| 207 |
+
self._save_bm25_cache(bm25)
|
| 208 |
+
|
| 209 |
return bm25
|
| 210 |
+
except Exception as e:
|
| 211 |
+
logger.error(f"Failed to init BM25: {e}")
|
| 212 |
return None
|
| 213 |
+
|
| 214 |
|
| 215 |
+
def _get_ensemble_retriever(self) -> EnsembleRetriever:
|
| 216 |
+
"""Get or create ensemble retriever (lazy-loaded)."""
|
| 217 |
+
if self._ensemble_retriever is not None:
|
| 218 |
+
return self._ensemble_retriever
|
| 219 |
+
|
| 220 |
+
bm25 = self._init_bm25()
|
| 221 |
+
if bm25:
|
| 222 |
+
self._ensemble_retriever = EnsembleRetriever(
|
| 223 |
+
retrievers=[self._vector_retriever, bm25],
|
| 224 |
weights=[self._config.vector_weight, self._config.bm25_weight]
|
| 225 |
)
|
| 226 |
+
else:
|
| 227 |
+
self._ensemble_retriever = EnsembleRetriever(
|
| 228 |
+
retrievers=[self._vector_retriever],
|
| 229 |
+
weights=[1.0]
|
| 230 |
+
)
|
| 231 |
+
return self._ensemble_retriever
|
| 232 |
+
|
| 233 |
|
| 234 |
def _init_reranker(self) -> Optional[SiliconFlowReranker]:
|
| 235 |
api_key = os.getenv("SILICONFLOW_API_KEY", "").strip()
|
|
|
|
| 243 |
)
|
| 244 |
|
| 245 |
def _build_final(self):
|
| 246 |
+
"""Build final retriever with reranker (lazy-loaded)."""
|
| 247 |
+
ensemble = self._get_ensemble_retriever()
|
| 248 |
if self._reranker:
|
| 249 |
return ContextualCompressionRetriever(
|
| 250 |
base_compressor=self._reranker,
|
| 251 |
+
base_retriever=ensemble
|
| 252 |
)
|
| 253 |
+
return ensemble
|
| 254 |
+
|
| 255 |
|
| 256 |
@property
|
| 257 |
def has_reranker(self) -> bool:
|
|
|
|
| 294 |
def bm25_search(self, text: str, *, k: int | None = None) -> List[Dict[str, Any]]:
|
| 295 |
if not text.strip():
|
| 296 |
return []
|
| 297 |
+
|
| 298 |
+
bm25 = self._init_bm25() # Lazy-load BM25
|
| 299 |
+
if not bm25:
|
| 300 |
return self.vector_search(text, k=k)
|
| 301 |
|
| 302 |
k = k or self._config.top_k
|
| 303 |
+
bm25.k = k
|
| 304 |
+
results = bm25.invoke(text)
|
| 305 |
return [self._to_result(doc, i + 1) for i, doc in enumerate(results[:k])]
|
| 306 |
|
| 307 |
def hybrid_search(
|
| 308 |
self, text: str, *, k: int | None = None, initial_k: int | None = None
|
| 309 |
) -> List[Dict[str, Any]]:
|
|
|
|
| 310 |
if not text.strip():
|
| 311 |
return []
|
| 312 |
|
| 313 |
k = k or self._config.top_k
|
| 314 |
if initial_k:
|
| 315 |
self._vector_retriever.search_kwargs["k"] = initial_k
|
| 316 |
+
bm25 = self._init_bm25()
|
| 317 |
+
if bm25:
|
| 318 |
+
bm25.k = initial_k
|
| 319 |
|
| 320 |
+
# Dùng ensemble_retriever (lazy-loaded, KHÔNG có reranker)
|
| 321 |
+
ensemble = self._get_ensemble_retriever()
|
| 322 |
+
results = ensemble.invoke(text)
|
| 323 |
return [self._to_result(doc, i + 1) for i, doc in enumerate(results[:k])]
|
| 324 |
|
| 325 |
def search_with_rerank(
|
|
|
|
| 330 |
where: Optional[Dict[str, Any]] = None,
|
| 331 |
initial_k: int | None = None,
|
| 332 |
) -> List[Dict[str, Any]]:
|
|
|
|
| 333 |
if not text.strip():
|
| 334 |
return []
|
| 335 |
|
|
|
|
| 346 |
for i, doc in enumerate(results[:k])
|
| 347 |
]
|
| 348 |
|
| 349 |
+
# Build final retriever (lazy-loaded ensemble + reranker)
|
| 350 |
if initial_k:
|
| 351 |
self._vector_retriever.search_kwargs["k"] = initial_k
|
| 352 |
+
bm25 = self._init_bm25()
|
| 353 |
+
if bm25:
|
| 354 |
+
bm25.k = initial_k
|
| 355 |
|
| 356 |
+
final_retriever = self._build_final()
|
| 357 |
+
results = final_retriever.invoke(text)
|
| 358 |
return [
|
| 359 |
self._to_result(doc, i + 1, rerank_score=doc.metadata.get("rerank_score"))
|
| 360 |
for i, doc in enumerate(results[:k])
|
core/embeddings/vector_store.py
CHANGED
|
@@ -44,7 +44,6 @@ class ChromaVectorDB:
|
|
| 44 |
logger.info(f"ChromaVectorDB initialized: {self.config.collection_name}")
|
| 45 |
|
| 46 |
def _load_parent_nodes(self) -> Dict[str, Dict[str, Any]]:
|
| 47 |
-
"""Load parent nodes from JSON file if exists."""
|
| 48 |
if self._parent_nodes_path.exists():
|
| 49 |
try:
|
| 50 |
with open(self._parent_nodes_path, 'r', encoding='utf-8') as f:
|
|
@@ -284,30 +283,6 @@ class ChromaVectorDB:
|
|
| 284 |
logger.info(f"Deleted {len(ids)} documents from vector store")
|
| 285 |
return len(ids)
|
| 286 |
|
| 287 |
-
def delete_parent_nodes_by_source(self, source_basename: str) -> int:
|
| 288 |
-
if not self._parent_nodes:
|
| 289 |
-
return 0
|
| 290 |
-
|
| 291 |
-
# Find parent nodes that belong to this source file
|
| 292 |
-
keys_to_delete = []
|
| 293 |
-
for key, node in self._parent_nodes.items():
|
| 294 |
-
meta = node.get("metadata", {})
|
| 295 |
-
node_source = meta.get("source_file", "") or meta.get("source_basename", "")
|
| 296 |
-
if source_basename in node_source or node_source.endswith(source_basename):
|
| 297 |
-
keys_to_delete.append(key)
|
| 298 |
-
|
| 299 |
-
if not keys_to_delete:
|
| 300 |
-
return 0
|
| 301 |
-
|
| 302 |
-
# Delete the parent nodes
|
| 303 |
-
for key in keys_to_delete:
|
| 304 |
-
del self._parent_nodes[key]
|
| 305 |
-
|
| 306 |
-
# Persist changes
|
| 307 |
-
self._save_parent_nodes()
|
| 308 |
-
logger.info(f"Deleted {len(keys_to_delete)} parent nodes for: {source_basename}")
|
| 309 |
-
return len(keys_to_delete)
|
| 310 |
-
|
| 311 |
def get_parent_node(self, parent_id: str) -> Optional[Dict[str, Any]]:
|
| 312 |
return self._parent_nodes.get(parent_id)
|
| 313 |
|
|
|
|
| 44 |
logger.info(f"ChromaVectorDB initialized: {self.config.collection_name}")
|
| 45 |
|
| 46 |
def _load_parent_nodes(self) -> Dict[str, Dict[str, Any]]:
|
|
|
|
| 47 |
if self._parent_nodes_path.exists():
|
| 48 |
try:
|
| 49 |
with open(self._parent_nodes_path, 'r', encoding='utf-8') as f:
|
|
|
|
| 283 |
logger.info(f"Deleted {len(ids)} documents from vector store")
|
| 284 |
return len(ids)
|
| 285 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
def get_parent_node(self, parent_id: str) -> Optional[Dict[str, Any]]:
|
| 287 |
return self._parent_nodes.get(parent_id)
|
| 288 |
|
core/gradio/gradio_rag_qwen.py
CHANGED
|
@@ -26,16 +26,19 @@ def _load_env() -> None:
|
|
| 26 |
from core.embeddings.embedding_model import EmbeddingConfig, QwenEmbeddings
|
| 27 |
from core.embeddings.vector_store import ChromaConfig, ChromaVectorDB
|
| 28 |
from core.embeddings.retrival import Retriever, RetrievalMode, get_retrieval_config
|
| 29 |
-
from core.embeddings.generator import
|
| 30 |
|
| 31 |
_load_env()
|
| 32 |
|
| 33 |
-
RETRIEVAL_MODE = RetrievalMode.
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
-
# Load
|
| 37 |
GRADIO_CFG = GradioConfig()
|
| 38 |
-
LLM_CFG = get_llm_config()
|
| 39 |
RETRIEVAL_CFG = get_retrieval_config()
|
| 40 |
|
| 41 |
|
|
@@ -43,7 +46,7 @@ class AppState:
|
|
| 43 |
def __init__(self) -> None:
|
| 44 |
self.db: Optional[ChromaVectorDB] = None
|
| 45 |
self.retriever: Optional[Retriever] = None
|
| 46 |
-
self.
|
| 47 |
self.client: Optional[OpenAI] = None
|
| 48 |
|
| 49 |
|
|
@@ -67,46 +70,77 @@ def _init_resources() -> None:
|
|
| 67 |
)
|
| 68 |
STATE.retriever = Retriever(vector_db=STATE.db)
|
| 69 |
|
| 70 |
-
|
|
|
|
| 71 |
if not api_key:
|
| 72 |
-
raise RuntimeError(f"Missing {
|
| 73 |
-
STATE.client = OpenAI(api_key=api_key, base_url=
|
| 74 |
|
| 75 |
-
#
|
| 76 |
-
STATE.
|
| 77 |
-
retriever=STATE.retriever,
|
| 78 |
-
config=LLM_CFG,
|
| 79 |
-
client=STATE.client,
|
| 80 |
-
)
|
| 81 |
|
| 82 |
print(" Đã sẵn sàng!")
|
| 83 |
|
| 84 |
|
| 85 |
def rag_chat(message: str, history: List[Dict[str, str]] | None = None):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
_init_resources()
|
|
|
|
| 87 |
|
| 88 |
assert STATE.db is not None
|
| 89 |
assert STATE.client is not None
|
| 90 |
assert STATE.retriever is not None
|
| 91 |
-
assert STATE.
|
| 92 |
|
| 93 |
-
#
|
| 94 |
-
|
|
|
|
| 95 |
message,
|
| 96 |
-
mode=RETRIEVAL_MODE,
|
| 97 |
k=RETRIEVAL_CFG.top_k,
|
| 98 |
initial_k=RETRIEVAL_CFG.initial_k,
|
|
|
|
| 99 |
)
|
|
|
|
|
|
|
| 100 |
|
| 101 |
if not results:
|
| 102 |
yield "Xin lỗi, tôi không tìm thấy thông tin phù hợp trong dữ liệu."
|
| 103 |
return
|
| 104 |
|
| 105 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
acc = ""
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
# Debug info with mode indicator
|
| 112 |
debug_info = f"\n\n---\n\n**Retrieved (Top {len(results)} | Mode: {RETRIEVAL_MODE.value})**\n\n"
|
|
@@ -148,7 +182,8 @@ def rag_chat(message: str, history: List[Dict[str, str]] | None = None):
|
|
| 148 |
debug_info += f" - **Mục:** {header[:80]}{'...' if len(header) > 80 else ''}\n"
|
| 149 |
debug_info += f" - **Content:** {content[:200]}{'...' if len(content) > 200 else ''}\n\n"
|
| 150 |
|
| 151 |
-
yield acc + debug_info
|
|
|
|
| 152 |
|
| 153 |
|
| 154 |
# Create Gradio interface
|
|
|
|
| 26 |
from core.embeddings.embedding_model import EmbeddingConfig, QwenEmbeddings
|
| 27 |
from core.embeddings.vector_store import ChromaConfig, ChromaVectorDB
|
| 28 |
from core.embeddings.retrival import Retriever, RetrievalMode, get_retrieval_config
|
| 29 |
+
from core.embeddings.generator import RAGContextBuilder, build_context, build_prompt, SYSTEM_PROMPT
|
| 30 |
|
| 31 |
_load_env()
|
| 32 |
|
| 33 |
+
RETRIEVAL_MODE = RetrievalMode.VECTOR_ONLY # Fastest mode - no BM25/reranker
|
| 34 |
|
| 35 |
+
# LLM Config (hardcoded sau khi xóa LLMConfig từ generator)
|
| 36 |
+
LLM_MODEL = os.getenv("LLM_MODEL", "qwen/qwen3-32b")
|
| 37 |
+
LLM_API_BASE = "https://api.groq.com/openai/v1"
|
| 38 |
+
LLM_API_KEY_ENV = "GROQ_API_KEY"
|
| 39 |
|
| 40 |
+
# Load retrieval config
|
| 41 |
GRADIO_CFG = GradioConfig()
|
|
|
|
| 42 |
RETRIEVAL_CFG = get_retrieval_config()
|
| 43 |
|
| 44 |
|
|
|
|
| 46 |
def __init__(self) -> None:
|
| 47 |
self.db: Optional[ChromaVectorDB] = None
|
| 48 |
self.retriever: Optional[Retriever] = None
|
| 49 |
+
self.rag_builder: Optional[RAGContextBuilder] = None
|
| 50 |
self.client: Optional[OpenAI] = None
|
| 51 |
|
| 52 |
|
|
|
|
| 70 |
)
|
| 71 |
STATE.retriever = Retriever(vector_db=STATE.db)
|
| 72 |
|
| 73 |
+
# LLM Client
|
| 74 |
+
api_key = (os.getenv(LLM_API_KEY_ENV) or "").strip()
|
| 75 |
if not api_key:
|
| 76 |
+
raise RuntimeError(f"Missing {LLM_API_KEY_ENV}")
|
| 77 |
+
STATE.client = OpenAI(api_key=api_key, base_url=LLM_API_BASE)
|
| 78 |
|
| 79 |
+
# RAGContextBuilder - chỉ retrieve, không gọi LLM
|
| 80 |
+
STATE.rag_builder = RAGContextBuilder(retriever=STATE.retriever)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
print(" Đã sẵn sàng!")
|
| 83 |
|
| 84 |
|
| 85 |
def rag_chat(message: str, history: List[Dict[str, str]] | None = None):
|
| 86 |
+
import time
|
| 87 |
+
total_start = time.time()
|
| 88 |
+
|
| 89 |
+
init_start = time.time()
|
| 90 |
_init_resources()
|
| 91 |
+
init_time = time.time() - init_start
|
| 92 |
|
| 93 |
assert STATE.db is not None
|
| 94 |
assert STATE.client is not None
|
| 95 |
assert STATE.retriever is not None
|
| 96 |
+
assert STATE.rag_builder is not None
|
| 97 |
|
| 98 |
+
# Bước 1: Retrieve và prepare context
|
| 99 |
+
retrieval_start = time.time()
|
| 100 |
+
prepared = STATE.rag_builder.retrieve_and_prepare(
|
| 101 |
message,
|
|
|
|
| 102 |
k=RETRIEVAL_CFG.top_k,
|
| 103 |
initial_k=RETRIEVAL_CFG.initial_k,
|
| 104 |
+
mode=RETRIEVAL_MODE.value,
|
| 105 |
)
|
| 106 |
+
retrieval_time = time.time() - retrieval_start
|
| 107 |
+
results = prepared["results"]
|
| 108 |
|
| 109 |
if not results:
|
| 110 |
yield "Xin lỗi, tôi không tìm thấy thông tin phù hợp trong dữ liệu."
|
| 111 |
return
|
| 112 |
|
| 113 |
+
# Bước 2: Gọi LLM streaming để generate answer
|
| 114 |
+
llm_start = time.time()
|
| 115 |
+
completion = STATE.client.chat.completions.create(
|
| 116 |
+
model=LLM_MODEL,
|
| 117 |
+
messages=[{"role": "user", "content": prepared["prompt"]}],
|
| 118 |
+
temperature=0.0,
|
| 119 |
+
max_tokens=4096,
|
| 120 |
+
stream=True,
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
acc = ""
|
| 124 |
+
first_token_time = None
|
| 125 |
+
for chunk in completion:
|
| 126 |
+
if first_token_time is None:
|
| 127 |
+
first_token_time = time.time() - llm_start
|
| 128 |
+
delta = getattr(chunk.choices[0].delta, "content", "") or ""
|
| 129 |
+
if delta:
|
| 130 |
+
acc += delta
|
| 131 |
+
yield acc
|
| 132 |
+
|
| 133 |
+
llm_time = time.time() - llm_start
|
| 134 |
+
total_time = time.time() - total_start
|
| 135 |
+
|
| 136 |
+
# Timing info
|
| 137 |
+
timing_info = f"\n\n---\n**⏱️ Timing:**\n"
|
| 138 |
+
timing_info += f"- Init: {init_time:.2f}s\n"
|
| 139 |
+
timing_info += f"- Retrieval: {retrieval_time:.2f}s\n"
|
| 140 |
+
timing_info += f"- LLM (first token): {first_token_time:.2f}s\n" if first_token_time else ""
|
| 141 |
+
timing_info += f"- LLM (total): {llm_time:.2f}s\n"
|
| 142 |
+
timing_info += f"- **Total: {total_time:.2f}s**\n"
|
| 143 |
+
|
| 144 |
|
| 145 |
# Debug info with mode indicator
|
| 146 |
debug_info = f"\n\n---\n\n**Retrieved (Top {len(results)} | Mode: {RETRIEVAL_MODE.value})**\n\n"
|
|
|
|
| 182 |
debug_info += f" - **Mục:** {header[:80]}{'...' if len(header) > 80 else ''}\n"
|
| 183 |
debug_info += f" - **Content:** {content[:200]}{'...' if len(content) > 200 else ''}\n\n"
|
| 184 |
|
| 185 |
+
yield acc + timing_info + debug_info
|
| 186 |
+
|
| 187 |
|
| 188 |
|
| 189 |
# Create Gradio interface
|
data/data_process/quyet_dinh/tieng_anh/06_ Quy định ngoại ngữ từ K70_chính quy_final.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
document_type: "quyet_dinh"
|
| 3 |
title: "QUY ĐỊNH Phân loại trình độ đầu vào , chương trình ngoại ngữ cơ bản và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hình thức chính quy từ k70"
|
| 4 |
issued_year: "2025"
|
| 5 |
-
applicable_cohorts: "K70"
|
| 6 |
---
|
| 7 |
|
| 8 |
# QUY ĐỊNH Phân loại trình độ đầu vào , chương trình ngoại ngữ cơ bản và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hình thức chính quy
|
|
|
|
| 2 |
document_type: "quyet_dinh"
|
| 3 |
title: "QUY ĐỊNH Phân loại trình độ đầu vào , chương trình ngoại ngữ cơ bản và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hình thức chính quy từ k70"
|
| 4 |
issued_year: "2025"
|
| 5 |
+
applicable_cohorts: ">=K70"
|
| 6 |
---
|
| 7 |
|
| 8 |
# QUY ĐỊNH Phân loại trình độ đầu vào , chương trình ngoại ngữ cơ bản và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hình thức chính quy
|
data/data_process/quyet_dinh/tieng_anh/QD_ngoai_ngu_tu_K65_CQ_final.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
---
|
| 2 |
document_type: "quyet_dinh"
|
| 3 |
-
title: "QUY ĐỊNH Phân loại trình độ đầu vào, chương trình môn học và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hệ chính quy"
|
| 4 |
issued_year: "2020"
|
| 5 |
-
applicable_cohorts: "K65"
|
| 6 |
---
|
| 7 |
|
| 8 |
# QUY ĐỊNH Phân loại trình độ đầu vào, chương trình môn học và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hệ chính quy
|
|
|
|
| 1 |
---
|
| 2 |
document_type: "quyet_dinh"
|
| 3 |
+
title: "QUY ĐỊNH Phân loại trình độ đầu vào, chương trình môn học và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hệ chính quy từ K65"
|
| 4 |
issued_year: "2020"
|
| 5 |
+
applicable_cohorts: ">=K65"
|
| 6 |
---
|
| 7 |
|
| 8 |
# QUY ĐỊNH Phân loại trình độ đầu vào, chương trình môn học và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hệ chính quy
|
data/data_process/quyet_dinh/tieng_anh/QD_ngoai_ngu_tu_K68_CQ_final.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
document_type: "quyet_dinh"
|
| 3 |
title: "QUY ĐỊNH Về việc ban hành Quy định về phân loại trình độ đầu vào, chương trình môn học và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hệ chính quy từ k68"
|
| 4 |
issued_year: "2023"
|
| 5 |
-
applicable_cohorts: "K68"
|
| 6 |
---
|
| 7 |
|
| 8 |
# QUYẾT ĐỊNH Về việc ban hành Quy định về phân loại trình độ đầu vào, chương trình môn học và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hệ chính quy từ k68
|
|
|
|
| 2 |
document_type: "quyet_dinh"
|
| 3 |
title: "QUY ĐỊNH Về việc ban hành Quy định về phân loại trình độ đầu vào, chương trình môn học và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hệ chính quy từ k68"
|
| 4 |
issued_year: "2023"
|
| 5 |
+
applicable_cohorts: ">=K68"
|
| 6 |
---
|
| 7 |
|
| 8 |
# QUYẾT ĐỊNH Về việc ban hành Quy định về phân loại trình độ đầu vào, chương trình môn học và chuẩn ngoại ngữ yêu cầu đối với sinh viên đại học hệ chính quy từ k68
|
evaluation/__init__.py
ADDED
|
File without changes
|
evaluation/eval_utils.py
CHANGED
|
@@ -26,8 +26,22 @@ def strip_thinking(text: str) -> str:
|
|
| 26 |
|
| 27 |
def load_config() -> dict:
|
| 28 |
return {
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
"model": os.getenv("EVAL_LLM_MODEL", "nex-agi/DeepSeek-V3.1-Nex-N1"),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
"temperature": float(os.getenv("EVAL_LLM_TEMPERATURE", "0")),
|
| 32 |
"timeout": int(os.getenv("EVAL_LLM_TIMEOUT", "30")),
|
| 33 |
"max_retries": int(os.getenv("EVAL_LLM_MAX_RETRIES", "2")),
|
|
@@ -63,7 +77,9 @@ def load_csv_data(csv_path: str, sample_size: int = 0) -> tuple[list, list]:
|
|
| 63 |
return questions, ground_truths
|
| 64 |
|
| 65 |
|
| 66 |
-
def init_rag_components(config: dict) -> tuple
|
|
|
|
|
|
|
| 67 |
emb_cfg = SiliconFlowConfig()
|
| 68 |
qwen_embeddings = QwenEmbeddings(emb_cfg)
|
| 69 |
|
|
@@ -71,42 +87,114 @@ def init_rag_components(config: dict) -> tuple[RAGGenerator, dict]:
|
|
| 71 |
db = ChromaVectorDB(embedder=qwen_embeddings, config=db_cfg)
|
| 72 |
retriever = Retriever(vector_db=db)
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
info = {
|
| 78 |
-
"gen_model":
|
| 79 |
"eval_model": config["llm"]["model"],
|
| 80 |
"embed_model": emb_cfg.model,
|
| 81 |
"collection": db_cfg.collection_name,
|
| 82 |
"doc_count": db.count(),
|
| 83 |
"embeddings": qwen_embeddings,
|
|
|
|
| 84 |
}
|
| 85 |
|
| 86 |
-
return
|
| 87 |
|
| 88 |
|
| 89 |
def generate_answers(
|
| 90 |
-
|
| 91 |
questions: list,
|
| 92 |
-
top_k: int = 5,
|
| 93 |
-
initial_k: int = 100,
|
| 94 |
retrieval_mode: str = "hybrid_rerank",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
) -> tuple[list, list]:
|
| 96 |
-
|
| 97 |
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
try:
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
except Exception as e:
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
return answers, contexts
|
| 112 |
|
|
|
|
| 26 |
|
| 27 |
def load_config() -> dict:
|
| 28 |
return {
|
| 29 |
+
# Model for generating RAG answers
|
| 30 |
+
"gen_llm": {
|
| 31 |
+
"model": os.getenv("GEN_LLM_MODEL", "nex-agi/DeepSeek-V3.1-Nex-N1"),
|
| 32 |
+
"temperature": float(os.getenv("GEN_LLM_TEMPERATURE", "0")),
|
| 33 |
+
"max_tokens": int(os.getenv("GEN_LLM_MAX_TOKENS", "4096")),
|
| 34 |
+
},
|
| 35 |
+
# Model for RAGAS evaluation (should be DIFFERENT from gen_llm to avoid bias)
|
| 36 |
+
"eval_llm": {
|
| 37 |
"model": os.getenv("EVAL_LLM_MODEL", "nex-agi/DeepSeek-V3.1-Nex-N1"),
|
| 38 |
+
"api_key_env": os.getenv("EVAL_API_KEY_ENV", "SILICONFLOW_API_KEY"), # Which env var has the API key
|
| 39 |
+
"base_url": os.getenv("EVAL_API_BASE_URL", "https://api.siliconflow.com/v1"),
|
| 40 |
+
"temperature": float(os.getenv("EVAL_LLM_TEMPERATURE", "0")),
|
| 41 |
+
},
|
| 42 |
+
# Legacy compatibility (deprecated, use gen_llm instead)
|
| 43 |
+
"llm": {
|
| 44 |
+
"model": os.getenv("GEN_LLM_MODEL", os.getenv("EVAL_LLM_MODEL", "nex-agi/DeepSeek-V3.1-Nex-N1")),
|
| 45 |
"temperature": float(os.getenv("EVAL_LLM_TEMPERATURE", "0")),
|
| 46 |
"timeout": int(os.getenv("EVAL_LLM_TIMEOUT", "30")),
|
| 47 |
"max_retries": int(os.getenv("EVAL_LLM_MAX_RETRIES", "2")),
|
|
|
|
| 77 |
return questions, ground_truths
|
| 78 |
|
| 79 |
|
| 80 |
+
def init_rag_components(config: dict) -> tuple:
|
| 81 |
+
from openai import OpenAI
|
| 82 |
+
|
| 83 |
emb_cfg = SiliconFlowConfig()
|
| 84 |
qwen_embeddings = QwenEmbeddings(emb_cfg)
|
| 85 |
|
|
|
|
| 87 |
db = ChromaVectorDB(embedder=qwen_embeddings, config=db_cfg)
|
| 88 |
retriever = Retriever(vector_db=db)
|
| 89 |
|
| 90 |
+
rag_builder = RAGGenerator(retriever=retriever)
|
| 91 |
+
|
| 92 |
+
api_key = os.getenv("SILICONFLOW_API_KEY", "").strip()
|
| 93 |
+
if not api_key:
|
| 94 |
+
raise ValueError("Missing SILICONFLOW_API_KEY")
|
| 95 |
+
|
| 96 |
+
llm_client = OpenAI(
|
| 97 |
+
api_key=api_key,
|
| 98 |
+
base_url="https://api.siliconflow.com/v1",
|
| 99 |
+
)
|
| 100 |
|
| 101 |
info = {
|
| 102 |
+
"gen_model": config["llm"]["model"],
|
| 103 |
"eval_model": config["llm"]["model"],
|
| 104 |
"embed_model": emb_cfg.model,
|
| 105 |
"collection": db_cfg.collection_name,
|
| 106 |
"doc_count": db.count(),
|
| 107 |
"embeddings": qwen_embeddings,
|
| 108 |
+
"llm_client": llm_client,
|
| 109 |
}
|
| 110 |
|
| 111 |
+
return rag_builder, info
|
| 112 |
|
| 113 |
|
| 114 |
def generate_answers(
|
| 115 |
+
rag_builder: RAGGenerator,
|
| 116 |
questions: list,
|
|
|
|
|
|
|
| 117 |
retrieval_mode: str = "hybrid_rerank",
|
| 118 |
+
max_workers: int = 8, # SiliconFlow allows 1000 RPM, safe to use 8 workers
|
| 119 |
+
llm_client = None,
|
| 120 |
+
llm_model: str = "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 121 |
+
timeout_per_question: int = 120, # 2 minutes timeout per question
|
| 122 |
) -> tuple[list, list]:
|
| 123 |
+
from concurrent.futures import ThreadPoolExecutor, as_completed, TimeoutError
|
| 124 |
|
| 125 |
+
if llm_client is None:
|
| 126 |
+
from openai import OpenAI
|
| 127 |
+
api_key = os.getenv("SILICONFLOW_API_KEY", "").strip()
|
| 128 |
+
if not api_key:
|
| 129 |
+
raise ValueError("Missing SILICONFLOW_API_KEY")
|
| 130 |
+
llm_client = OpenAI(
|
| 131 |
+
api_key=api_key,
|
| 132 |
+
base_url="https://api.siliconflow.com/v1",
|
| 133 |
+
timeout=60.0, # 60 seconds timeout for API call
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
def process_question(idx_q: tuple) -> tuple:
|
| 137 |
+
"""Process a single question and return (idx, answer, contexts, error, retrieval_empty)"""
|
| 138 |
+
idx, q = idx_q
|
| 139 |
try:
|
| 140 |
+
prepared = rag_builder.retrieve_and_prepare(q, mode=retrieval_mode)
|
| 141 |
+
|
| 142 |
+
if not prepared["results"]:
|
| 143 |
+
# Mark retrieval as failed for debugging RAG vs LLM issues
|
| 144 |
+
return idx, "Không tìm thấy thông tin trong dữ liệu hiện có.", [], None, True
|
| 145 |
+
|
| 146 |
+
completion = llm_client.chat.completions.create(
|
| 147 |
+
model=llm_model,
|
| 148 |
+
messages=[{"role": "user", "content": prepared["prompt"]}],
|
| 149 |
+
temperature=0.0,
|
| 150 |
+
max_tokens=4096,
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
answer = strip_thinking(completion.choices[0].message.content or "")
|
| 154 |
+
return idx, answer, prepared["contexts"], None, False
|
| 155 |
except Exception as e:
|
| 156 |
+
return idx, "Không thể trả lời.", [], str(e), False
|
| 157 |
+
|
| 158 |
+
n = len(questions)
|
| 159 |
+
answers: list[str] = [""] * n
|
| 160 |
+
contexts: list[list] = [[] for _ in range(n)] # FIXED: [[]] * n creates shared references!
|
| 161 |
+
retrieval_failed: list[bool] = [False] * n # Track retrieval failures for debugging
|
| 162 |
+
errors: list[str | None] = [None] * n # Track error messages
|
| 163 |
+
|
| 164 |
+
print(f" Generating answers with {max_workers} workers...")
|
| 165 |
+
|
| 166 |
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
| 167 |
+
futures = {executor.submit(process_question, (i, q)): i for i, q in enumerate(questions)}
|
| 168 |
+
done_count = 0
|
| 169 |
+
|
| 170 |
+
for future in as_completed(futures):
|
| 171 |
+
try:
|
| 172 |
+
# Use timeout_per_question for actual timeout enforcement
|
| 173 |
+
idx, answer, ctx, error, is_retrieval_empty = future.result(timeout=timeout_per_question)
|
| 174 |
+
answers[idx] = answer
|
| 175 |
+
contexts[idx] = ctx
|
| 176 |
+
retrieval_failed[idx] = is_retrieval_empty
|
| 177 |
+
errors[idx] = error
|
| 178 |
+
done_count += 1
|
| 179 |
+
|
| 180 |
+
if error:
|
| 181 |
+
print(f" [{done_count}/{n}] Q{idx+1}: Error - {error}")
|
| 182 |
+
elif is_retrieval_empty:
|
| 183 |
+
print(f" [{done_count}/{n}] Q{idx+1}: Done (⚠️ no retrieval results)")
|
| 184 |
+
else:
|
| 185 |
+
print(f" [{done_count}/{n}] Q{idx+1}: Done")
|
| 186 |
+
except TimeoutError:
|
| 187 |
+
idx = futures[future]
|
| 188 |
+
answers[idx] = "Timeout: Không thể xử lý câu hỏi trong thời gian cho phép."
|
| 189 |
+
contexts[idx] = []
|
| 190 |
+
errors[idx] = f"Timeout after {timeout_per_question}s"
|
| 191 |
+
done_count += 1
|
| 192 |
+
print(f" [{done_count}/{n}] Q{idx+1}: TIMEOUT")
|
| 193 |
+
|
| 194 |
+
# Log summary of retrieval failures
|
| 195 |
+
failed_count = sum(retrieval_failed)
|
| 196 |
+
if failed_count > 0:
|
| 197 |
+
print(f" ⚠️ {failed_count}/{n} questions had empty retrieval results")
|
| 198 |
|
| 199 |
return answers, contexts
|
| 200 |
|
evaluation/ragas_eval.py
CHANGED
|
@@ -31,16 +31,31 @@ def run_ragas_evaluation(sample_size: int = 10, output_dir: Optional[str] = None
|
|
| 31 |
# Init components
|
| 32 |
rag_gen, info = init_rag_components(config)
|
| 33 |
|
| 34 |
-
#
|
| 35 |
-
|
| 36 |
-
if not api_key:
|
| 37 |
-
raise ValueError("Missing SILICONFLOW_API_KEY")
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
evaluator_llm = LangchainLLMWrapper(ChatOpenAI(
|
| 40 |
-
model=
|
| 41 |
-
api_key=
|
| 42 |
-
base_url=
|
| 43 |
-
temperature=
|
|
|
|
|
|
|
| 44 |
))
|
| 45 |
evaluator_embeddings = LangchainEmbeddingsWrapper(info["embeddings"])
|
| 46 |
|
|
@@ -49,11 +64,13 @@ def run_ragas_evaluation(sample_size: int = 10, output_dir: Optional[str] = None
|
|
| 49 |
str(REPO_ROOT / config["data"]["csv_path"]),
|
| 50 |
sample_size or config["data"]["sample_size"]
|
| 51 |
)
|
|
|
|
|
|
|
| 52 |
answers, contexts = generate_answers(
|
| 53 |
rag_gen, questions,
|
| 54 |
-
config["retrieval"]["top_k"],
|
| 55 |
-
config["retrieval"]["initial_k"],
|
| 56 |
retrieval_mode=retrieval_mode,
|
|
|
|
|
|
|
| 57 |
)
|
| 58 |
|
| 59 |
# Run RAGAS
|
|
@@ -75,7 +92,12 @@ def run_ragas_evaluation(sample_size: int = 10, output_dir: Optional[str] = None
|
|
| 75 |
llm=evaluator_llm,
|
| 76 |
embeddings=evaluator_embeddings,
|
| 77 |
raise_exceptions=False,
|
| 78 |
-
run_config=RunConfig(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
)
|
| 80 |
|
| 81 |
# Save JSON
|
|
@@ -83,99 +105,182 @@ def run_ragas_evaluation(sample_size: int = 10, output_dir: Optional[str] = None
|
|
| 83 |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 84 |
json_path = out_path / f"ragas_{timestamp}.json"
|
| 85 |
|
| 86 |
-
# Convert results to dict
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
|
|
|
| 97 |
with open(json_path, 'w', encoding='utf-8') as f:
|
| 98 |
json.dump({
|
| 99 |
"timestamp": timestamp,
|
| 100 |
"retrieval_mode": retrieval_mode,
|
| 101 |
-
"config": {"sample_size": len(questions), **{k: v for k, v in info.items() if k
|
| 102 |
-
"
|
|
|
|
| 103 |
"samples": [
|
| 104 |
-
{"question": q, "answer": a, "ground_truth": gt, "contexts": ctx}
|
| 105 |
-
for q, a, gt, ctx in zip(questions, answers, ground_truths, contexts)
|
| 106 |
]
|
| 107 |
}, f, ensure_ascii=False, indent=2)
|
| 108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
print(f"Saved: {json_path}")
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
|
| 113 |
if __name__ == "__main__":
|
| 114 |
import argparse
|
| 115 |
-
parser = argparse.ArgumentParser(description="RAGAS Evaluation - Compare
|
| 116 |
parser.add_argument("--samples", type=int, default=10, help="Number of samples to evaluate")
|
| 117 |
parser.add_argument("--output", type=str, default=None, help="Output directory")
|
|
|
|
|
|
|
|
|
|
| 118 |
args = parser.parse_args()
|
| 119 |
|
| 120 |
-
# Always run all modes and compare
|
| 121 |
ALL_MODES = ["vector_only", "bm25_only", "hybrid", "hybrid_rerank"]
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
print(f"\n{'
|
| 126 |
-
print(f"
|
| 127 |
-
print(f"{'
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
# Print comparison table
|
| 136 |
-
print(f"\n{'='*80}")
|
| 137 |
-
print("COMPARISON REPORT - All Retrieval Modes")
|
| 138 |
-
print(f"{'='*80}")
|
| 139 |
-
|
| 140 |
-
# Get all metric names
|
| 141 |
-
metrics = set()
|
| 142 |
-
for scores in all_results.values():
|
| 143 |
-
if isinstance(scores, dict) and "error" not in scores:
|
| 144 |
-
metrics.update(scores.keys())
|
| 145 |
-
metrics = sorted(metrics)
|
| 146 |
-
|
| 147 |
-
# Print header
|
| 148 |
-
header = f"{'Metric':<25} | " + " | ".join(f"{m:<15}" for m in ALL_MODES)
|
| 149 |
-
print(header)
|
| 150 |
-
print("-" * len(header))
|
| 151 |
-
|
| 152 |
-
# Print each metric
|
| 153 |
-
for metric in metrics:
|
| 154 |
-
row = f"{metric:<25} | "
|
| 155 |
for mode in ALL_MODES:
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
else:
|
| 162 |
row += f"{'N/A':<15} | "
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
# Init components
|
| 32 |
rag_gen, info = init_rag_components(config)
|
| 33 |
|
| 34 |
+
eval_config = config.get("eval_llm", config["llm"]) # Fallback to llm for backward compatibility
|
| 35 |
+
gen_config = config.get("gen_llm", config["llm"])
|
|
|
|
|
|
|
| 36 |
|
| 37 |
+
# Get eval API key (may be different from generation API key)
|
| 38 |
+
eval_api_key_env = eval_config.get("api_key_env", "SILICONFLOW_API_KEY")
|
| 39 |
+
eval_api_key = os.getenv(eval_api_key_env, "")
|
| 40 |
+
if not eval_api_key:
|
| 41 |
+
# Fallback to SILICONFLOW_API_KEY
|
| 42 |
+
eval_api_key = os.getenv("SILICONFLOW_API_KEY", "")
|
| 43 |
+
if not eval_api_key:
|
| 44 |
+
raise ValueError(f"Missing API key. Set {eval_api_key_env} or SILICONFLOW_API_KEY")
|
| 45 |
+
|
| 46 |
+
eval_base_url = eval_config.get("base_url", "https://api.siliconflow.com/v1")
|
| 47 |
+
eval_model = eval_config.get("model", config["llm"]["model"])
|
| 48 |
+
gen_model = gen_config.get("model", config["llm"]["model"])
|
| 49 |
+
|
| 50 |
+
# ChatOpenAI expects SecretStr or callable for api_key
|
| 51 |
+
from pydantic import SecretStr
|
| 52 |
evaluator_llm = LangchainLLMWrapper(ChatOpenAI(
|
| 53 |
+
model=eval_model,
|
| 54 |
+
api_key=SecretStr(eval_api_key), # Wrap in SecretStr
|
| 55 |
+
base_url=eval_base_url,
|
| 56 |
+
temperature=eval_config.get("temperature", 0),
|
| 57 |
+
timeout=120, # 2 minutes timeout per request
|
| 58 |
+
max_retries=3,
|
| 59 |
))
|
| 60 |
evaluator_embeddings = LangchainEmbeddingsWrapper(info["embeddings"])
|
| 61 |
|
|
|
|
| 64 |
str(REPO_ROOT / config["data"]["csv_path"]),
|
| 65 |
sample_size or config["data"]["sample_size"]
|
| 66 |
)
|
| 67 |
+
|
| 68 |
+
# Use generation model (NOT evaluation model) for RAG answers
|
| 69 |
answers, contexts = generate_answers(
|
| 70 |
rag_gen, questions,
|
|
|
|
|
|
|
| 71 |
retrieval_mode=retrieval_mode,
|
| 72 |
+
llm_client=info["llm_client"],
|
| 73 |
+
llm_model=gen_config.get("model", config["llm"]["model"]),
|
| 74 |
)
|
| 75 |
|
| 76 |
# Run RAGAS
|
|
|
|
| 92 |
llm=evaluator_llm,
|
| 93 |
embeddings=evaluator_embeddings,
|
| 94 |
raise_exceptions=False,
|
| 95 |
+
run_config=RunConfig(
|
| 96 |
+
max_workers=8, # SiliconFlow allows 1000 RPM, safe to use 8 workers
|
| 97 |
+
timeout=600, # 10 minutes per job
|
| 98 |
+
max_retries=3, # Retry on transient failures
|
| 99 |
+
max_wait=120, # Max wait between retries
|
| 100 |
+
),
|
| 101 |
)
|
| 102 |
|
| 103 |
# Save JSON
|
|
|
|
| 105 |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 106 |
json_path = out_path / f"ragas_{timestamp}.json"
|
| 107 |
|
| 108 |
+
# Convert results to dict - handle both per-sample and aggregate scores
|
| 109 |
+
scores_per_sample: list = []
|
| 110 |
+
avg_scores: dict = {}
|
| 111 |
+
|
| 112 |
+
# Get per-sample scores from results DataFrame
|
| 113 |
+
if hasattr(results, "to_pandas"):
|
| 114 |
+
df = results.to_pandas() # type: ignore[union-attr]
|
| 115 |
+
metric_cols = [col for col in df.columns if col not in ("question", "answer", "contexts", "ground_truth", "user_input", "response", "reference", "retrieved_contexts")]
|
| 116 |
+
|
| 117 |
+
for _, row in df.iterrows():
|
| 118 |
+
sample_scores = {}
|
| 119 |
+
for col in metric_cols:
|
| 120 |
+
val = row[col]
|
| 121 |
+
if val is not None:
|
| 122 |
+
try:
|
| 123 |
+
sample_scores[col] = float(val)
|
| 124 |
+
except (ValueError, TypeError):
|
| 125 |
+
pass
|
| 126 |
+
scores_per_sample.append(sample_scores)
|
| 127 |
+
|
| 128 |
+
# Calculate average scores
|
| 129 |
+
import numpy as np
|
| 130 |
+
for col in metric_cols:
|
| 131 |
+
values = [s.get(col) for s in scores_per_sample if s.get(col) is not None]
|
| 132 |
+
if values:
|
| 133 |
+
valid_values = [v for v in values if not np.isnan(v)]
|
| 134 |
+
if valid_values:
|
| 135 |
+
avg_scores[col] = float(np.mean(valid_values))
|
| 136 |
+
else:
|
| 137 |
+
# Fallback for older ragas versions
|
| 138 |
+
if hasattr(results, "scores"):
|
| 139 |
+
avg_scores = results.scores # type: ignore
|
| 140 |
+
elif hasattr(results, "__getitem__"):
|
| 141 |
+
for k in ["faithfulness", "answer_relevancy", "context_precision", "context_recall", "rouge1_score", "rouge2_score", "rougeL_score"]:
|
| 142 |
+
try:
|
| 143 |
+
avg_scores[k] = float(results[k]) # type: ignore
|
| 144 |
+
except (KeyError, TypeError):
|
| 145 |
+
pass
|
| 146 |
|
| 147 |
+
# Save JSON with both per-sample and average scores
|
| 148 |
with open(json_path, 'w', encoding='utf-8') as f:
|
| 149 |
json.dump({
|
| 150 |
"timestamp": timestamp,
|
| 151 |
"retrieval_mode": retrieval_mode,
|
| 152 |
+
"config": {"sample_size": len(questions), **{k: v for k, v in info.items() if k not in ("embeddings", "llm_client")}},
|
| 153 |
+
"avg_scores": avg_scores,
|
| 154 |
+
"scores_per_sample": scores_per_sample,
|
| 155 |
"samples": [
|
| 156 |
+
{"question": q, "answer": a, "ground_truth": gt, "contexts": ctx, "scores": sc}
|
| 157 |
+
for q, a, gt, ctx, sc in zip(questions, answers, ground_truths, contexts, scores_per_sample or [{}]*len(questions))
|
| 158 |
]
|
| 159 |
}, f, ensure_ascii=False, indent=2)
|
| 160 |
|
| 161 |
+
# Save CSV summary (only average scores, not per-sample)
|
| 162 |
+
csv_path = out_path / f"ragas_{retrieval_mode}_{timestamp}.csv"
|
| 163 |
+
with open(csv_path, 'w', encoding='utf-8') as f:
|
| 164 |
+
# Header: metric names
|
| 165 |
+
f.write("retrieval_mode,sample_size," + ",".join(avg_scores.keys()) + "\n")
|
| 166 |
+
# Data: average values
|
| 167 |
+
f.write(f"{retrieval_mode},{len(questions)}," + ",".join([f"{v:.4f}" for v in avg_scores.values()]) + "\n")
|
| 168 |
+
|
| 169 |
print(f"Saved: {json_path}")
|
| 170 |
+
print(f"Saved: {csv_path}")
|
| 171 |
+
|
| 172 |
+
# Print summary
|
| 173 |
+
print(f"\n{'='*60}")
|
| 174 |
+
print(f"SUMMARY - {retrieval_mode} ({len(questions)} samples)")
|
| 175 |
+
print(f"{'='*60}")
|
| 176 |
+
for metric, score in avg_scores.items():
|
| 177 |
+
bar = "#" * int(score * 20) + "-" * (20 - int(score * 20))
|
| 178 |
+
print(f" {metric:25} [{bar}] {score:.4f}")
|
| 179 |
+
|
| 180 |
+
return avg_scores
|
| 181 |
|
| 182 |
|
| 183 |
if __name__ == "__main__":
|
| 184 |
import argparse
|
| 185 |
+
parser = argparse.ArgumentParser(description="RAGAS Evaluation - Compare retrieval modes")
|
| 186 |
parser.add_argument("--samples", type=int, default=10, help="Number of samples to evaluate")
|
| 187 |
parser.add_argument("--output", type=str, default=None, help="Output directory")
|
| 188 |
+
parser.add_argument("--mode", type=str, default="all",
|
| 189 |
+
choices=["all", "vector_only", "bm25_only", "hybrid", "hybrid_rerank"],
|
| 190 |
+
help="Retrieval mode to evaluate (default: all)")
|
| 191 |
args = parser.parse_args()
|
| 192 |
|
|
|
|
| 193 |
ALL_MODES = ["vector_only", "bm25_only", "hybrid", "hybrid_rerank"]
|
| 194 |
+
|
| 195 |
+
# Single mode or all modes
|
| 196 |
+
if args.mode != "all":
|
| 197 |
+
print(f"\n{'='*60}")
|
| 198 |
+
print(f"RAGAS EVALUATION - Single Mode: {args.mode}")
|
| 199 |
+
print(f"{'='*60}")
|
| 200 |
+
scores = run_ragas_evaluation(args.samples, args.output, args.mode)
|
| 201 |
+
print(f"\nScores: {scores}")
|
| 202 |
+
else:
|
| 203 |
+
# Run all modes and compare
|
| 204 |
+
all_results = {}
|
| 205 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
for mode in ALL_MODES:
|
| 207 |
+
print(f"\n{'#'*60}")
|
| 208 |
+
print(f"# Running mode: {mode}")
|
| 209 |
+
print(f"{'#'*60}")
|
| 210 |
+
try:
|
| 211 |
+
scores = run_ragas_evaluation(args.samples, args.output, mode)
|
| 212 |
+
all_results[mode] = scores
|
| 213 |
+
except Exception as e:
|
| 214 |
+
print(f"Error in {mode}: {e}")
|
| 215 |
+
all_results[mode] = {"error": str(e)}
|
| 216 |
+
|
| 217 |
+
# Print comparison table
|
| 218 |
+
print(f"\n{'='*80}")
|
| 219 |
+
print("COMPARISON REPORT - All Retrieval Modes")
|
| 220 |
+
print(f"{'='*80}")
|
| 221 |
+
|
| 222 |
+
# Get all metric names
|
| 223 |
+
metrics = set()
|
| 224 |
+
for scores in all_results.values():
|
| 225 |
+
if isinstance(scores, dict) and "error" not in scores:
|
| 226 |
+
metrics.update(scores.keys())
|
| 227 |
+
metrics = sorted(metrics)
|
| 228 |
+
|
| 229 |
+
# Print header
|
| 230 |
+
header = f"{'Metric':<25} | " + " | ".join(f"{m:<15}" for m in ALL_MODES)
|
| 231 |
+
print(header)
|
| 232 |
+
print("-" * len(header))
|
| 233 |
+
|
| 234 |
+
# Print each metric
|
| 235 |
+
for metric in metrics:
|
| 236 |
+
row = f"{metric:<25} | "
|
| 237 |
+
for mode in ALL_MODES:
|
| 238 |
+
scores = all_results.get(mode, {})
|
| 239 |
+
if isinstance(scores, dict) and metric in scores:
|
| 240 |
+
val = scores[metric]
|
| 241 |
+
if isinstance(val, (int, float)):
|
| 242 |
+
row += f"{val:<15.4f} | "
|
| 243 |
+
else:
|
| 244 |
+
row += f"{'N/A':<15} | "
|
| 245 |
else:
|
| 246 |
row += f"{'N/A':<15} | "
|
| 247 |
+
print(row)
|
| 248 |
+
|
| 249 |
+
# Save comparison JSON and CSV
|
| 250 |
+
config = load_config()
|
| 251 |
+
out_path = Path(args.output) if args.output else REPO_ROOT / config["output"]["dir"]
|
| 252 |
+
out_path.mkdir(parents=True, exist_ok=True)
|
| 253 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 254 |
+
|
| 255 |
+
# Save JSON
|
| 256 |
+
comparison_path = out_path / f"comparison_{timestamp}.json"
|
| 257 |
+
with open(comparison_path, 'w', encoding='utf-8') as f:
|
| 258 |
+
json.dump({
|
| 259 |
+
"timestamp": timestamp,
|
| 260 |
+
"sample_size": args.samples,
|
| 261 |
+
"results": all_results,
|
| 262 |
+
}, f, ensure_ascii=False, indent=2)
|
| 263 |
+
|
| 264 |
+
# Save CSV summary
|
| 265 |
+
csv_path = out_path / f"comparison_{timestamp}.csv"
|
| 266 |
+
with open(csv_path, 'w', encoding='utf-8') as f:
|
| 267 |
+
# Header
|
| 268 |
+
f.write("Metric," + ",".join(ALL_MODES) + "\n")
|
| 269 |
+
# Data rows
|
| 270 |
+
for metric in metrics:
|
| 271 |
+
row_values = [metric]
|
| 272 |
+
for mode in ALL_MODES:
|
| 273 |
+
scores = all_results.get(mode, {})
|
| 274 |
+
if isinstance(scores, dict) and metric in scores:
|
| 275 |
+
val = scores[metric]
|
| 276 |
+
if isinstance(val, (int, float)):
|
| 277 |
+
row_values.append(f"{val:.4f}")
|
| 278 |
+
else:
|
| 279 |
+
row_values.append("N/A")
|
| 280 |
+
else:
|
| 281 |
+
row_values.append("N/A")
|
| 282 |
+
f.write(",".join(row_values) + "\n")
|
| 283 |
+
|
| 284 |
+
print(f"\nComparison saved:")
|
| 285 |
+
print(f" - JSON: {comparison_path}")
|
| 286 |
+
print(f" - CSV: {csv_path}")
|
evaluation/results/comparison_20260105_132921.json
DELETED
|
@@ -1,154 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"timestamp": "20260105_132921",
|
| 3 |
-
"sample_size": 5,
|
| 4 |
-
"results": {
|
| 5 |
-
"vector_only": [
|
| 6 |
-
{
|
| 7 |
-
"faithfulness": 1.0,
|
| 8 |
-
"answer_relevancy": 0.7364493749907867,
|
| 9 |
-
"context_precision": 0.99999999995,
|
| 10 |
-
"context_recall": 1.0,
|
| 11 |
-
"rouge_score(mode=fmeasure)": 0.6690140845070423
|
| 12 |
-
},
|
| 13 |
-
{
|
| 14 |
-
"faithfulness": 1.0,
|
| 15 |
-
"answer_relevancy": 0.7310359258257583,
|
| 16 |
-
"context_precision": 0.999999999975,
|
| 17 |
-
"context_recall": 1.0,
|
| 18 |
-
"rouge_score(mode=fmeasure)": 0.6643356643356644
|
| 19 |
-
},
|
| 20 |
-
{
|
| 21 |
-
"faithfulness": 0.6923076923076923,
|
| 22 |
-
"answer_relevancy": 0.8345119784254372,
|
| 23 |
-
"context_precision": 0.8666666666377778,
|
| 24 |
-
"context_recall": 1.0,
|
| 25 |
-
"rouge_score(mode=fmeasure)": 0.4396135265700484
|
| 26 |
-
},
|
| 27 |
-
{
|
| 28 |
-
"faithfulness": 0.2857142857142857,
|
| 29 |
-
"answer_relevancy": 0.0,
|
| 30 |
-
"context_precision": 0.6791666666496875,
|
| 31 |
-
"context_recall": 1.0,
|
| 32 |
-
"rouge_score(mode=fmeasure)": 0.39164490861618795
|
| 33 |
-
},
|
| 34 |
-
{
|
| 35 |
-
"faithfulness": 0.5384615384615384,
|
| 36 |
-
"answer_relevancy": 0.7938279373591263,
|
| 37 |
-
"context_precision": 0.9999999999,
|
| 38 |
-
"context_recall": 1.0,
|
| 39 |
-
"rouge_score(mode=fmeasure)": 0.36512261580381467
|
| 40 |
-
}
|
| 41 |
-
],
|
| 42 |
-
"bm25_only": [
|
| 43 |
-
{
|
| 44 |
-
"faithfulness": 0.875,
|
| 45 |
-
"answer_relevancy": 0.7264722326341989,
|
| 46 |
-
"context_precision": 0.99999999995,
|
| 47 |
-
"context_recall": 1.0,
|
| 48 |
-
"rouge_score(mode=fmeasure)": 0.7589285714285714
|
| 49 |
-
},
|
| 50 |
-
{
|
| 51 |
-
"faithfulness": 0.9285714285714286,
|
| 52 |
-
"answer_relevancy": 0.7342079659938445,
|
| 53 |
-
"context_precision": 0.999999999975,
|
| 54 |
-
"context_recall": 1.0,
|
| 55 |
-
"rouge_score(mode=fmeasure)": 0.6351351351351351
|
| 56 |
-
},
|
| 57 |
-
{
|
| 58 |
-
"faithfulness": 0.8888888888888888,
|
| 59 |
-
"answer_relevancy": 0.8335330619249784,
|
| 60 |
-
"context_precision": 0.9999999999666667,
|
| 61 |
-
"context_recall": 1.0,
|
| 62 |
-
"rouge_score(mode=fmeasure)": 0.5673352435530086
|
| 63 |
-
},
|
| 64 |
-
{
|
| 65 |
-
"faithfulness": 0.42857142857142855,
|
| 66 |
-
"answer_relevancy": 0.6506986819875058,
|
| 67 |
-
"context_precision": 0.99999999998,
|
| 68 |
-
"context_recall": 1.0,
|
| 69 |
-
"rouge_score(mode=fmeasure)": 0.5048543689320388
|
| 70 |
-
},
|
| 71 |
-
{
|
| 72 |
-
"faithfulness": 0.8333333333333334,
|
| 73 |
-
"answer_relevancy": 0.8338701751762124,
|
| 74 |
-
"context_precision": 0.9999999999,
|
| 75 |
-
"context_recall": 1.0,
|
| 76 |
-
"rouge_score(mode=fmeasure)": 0.5344827586206896
|
| 77 |
-
}
|
| 78 |
-
],
|
| 79 |
-
"hybrid": [
|
| 80 |
-
{
|
| 81 |
-
"faithfulness": 0.8571428571428571,
|
| 82 |
-
"answer_relevancy": 0.8972923254436237,
|
| 83 |
-
"context_precision": 0.99999999995,
|
| 84 |
-
"context_recall": 1.0,
|
| 85 |
-
"rouge_score(mode=fmeasure)": 0.7606837606837608
|
| 86 |
-
},
|
| 87 |
-
{
|
| 88 |
-
"faithfulness": 1.0,
|
| 89 |
-
"answer_relevancy": 0.7008081720389104,
|
| 90 |
-
"context_precision": 0.94999999997625,
|
| 91 |
-
"context_recall": 1.0,
|
| 92 |
-
"rouge_score(mode=fmeasure)": 0.44655581947743467
|
| 93 |
-
},
|
| 94 |
-
{
|
| 95 |
-
"faithfulness": 1.0,
|
| 96 |
-
"answer_relevancy": 0.8991661959917713,
|
| 97 |
-
"context_precision": 0.99999999995,
|
| 98 |
-
"context_recall": 1.0,
|
| 99 |
-
"rouge_score(mode=fmeasure)": 0.6297577854671281
|
| 100 |
-
},
|
| 101 |
-
{
|
| 102 |
-
"faithfulness": 1.0,
|
| 103 |
-
"answer_relevancy": 0.0,
|
| 104 |
-
"context_precision": 0.99999999998,
|
| 105 |
-
"context_recall": 1.0,
|
| 106 |
-
"rouge_score(mode=fmeasure)": 0.1372549019607843
|
| 107 |
-
},
|
| 108 |
-
{
|
| 109 |
-
"faithfulness": 0.7,
|
| 110 |
-
"answer_relevancy": 0.7942512688700306,
|
| 111 |
-
"context_precision": 0.49999999995,
|
| 112 |
-
"context_recall": 1.0,
|
| 113 |
-
"rouge_score(mode=fmeasure)": 0.39747634069400634
|
| 114 |
-
}
|
| 115 |
-
],
|
| 116 |
-
"hybrid_rerank": [
|
| 117 |
-
{
|
| 118 |
-
"faithfulness": 0.8888888888888888,
|
| 119 |
-
"answer_relevancy": 0.7358630343295559,
|
| 120 |
-
"context_precision": 0.99999999995,
|
| 121 |
-
"context_recall": 1.0,
|
| 122 |
-
"rouge_score(mode=fmeasure)": 0.7818930041152263
|
| 123 |
-
},
|
| 124 |
-
{
|
| 125 |
-
"faithfulness": 1.0,
|
| 126 |
-
"answer_relevancy": 0.6748679131476658,
|
| 127 |
-
"context_precision": 0.8874999999778125,
|
| 128 |
-
"context_recall": 1.0,
|
| 129 |
-
"rouge_score(mode=fmeasure)": 0.6909090909090908
|
| 130 |
-
},
|
| 131 |
-
{
|
| 132 |
-
"faithfulness": 1.0,
|
| 133 |
-
"answer_relevancy": 0.8327119887318949,
|
| 134 |
-
"context_precision": 0.9166666666361111,
|
| 135 |
-
"context_recall": 1.0,
|
| 136 |
-
"rouge_score(mode=fmeasure)": 0.6297577854671281
|
| 137 |
-
},
|
| 138 |
-
{
|
| 139 |
-
"faithfulness": 1.0,
|
| 140 |
-
"answer_relevancy": 0.0,
|
| 141 |
-
"context_precision": 0.99999999998,
|
| 142 |
-
"context_recall": 1.0,
|
| 143 |
-
"rouge_score(mode=fmeasure)": 0.1372549019607843
|
| 144 |
-
},
|
| 145 |
-
{
|
| 146 |
-
"faithfulness": 0.6666666666666666,
|
| 147 |
-
"answer_relevancy": 0.8847245139750438,
|
| 148 |
-
"context_precision": 0.7499999999625,
|
| 149 |
-
"context_recall": 1.0,
|
| 150 |
-
"rouge_score(mode=fmeasure)": 0.7083333333333333
|
| 151 |
-
}
|
| 152 |
-
]
|
| 153 |
-
}
|
| 154 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
evaluation/results/ragas_20260105_125700.json
DELETED
|
@@ -1,111 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"timestamp": "20260105_125700",
|
| 3 |
-
"retrieval_mode": "vector_only",
|
| 4 |
-
"config": {
|
| 5 |
-
"sample_size": 5,
|
| 6 |
-
"gen_model": "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 7 |
-
"eval_model": "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 8 |
-
"embed_model": "Qwen/Qwen3-Embedding-4B",
|
| 9 |
-
"collection": "hust_rag_collection",
|
| 10 |
-
"doc_count": 1441
|
| 11 |
-
},
|
| 12 |
-
"scores": [
|
| 13 |
-
{
|
| 14 |
-
"faithfulness": 1.0,
|
| 15 |
-
"answer_relevancy": 0.7364493749907867,
|
| 16 |
-
"context_precision": 0.99999999995,
|
| 17 |
-
"context_recall": 1.0,
|
| 18 |
-
"rouge_score(mode=fmeasure)": 0.6690140845070423
|
| 19 |
-
},
|
| 20 |
-
{
|
| 21 |
-
"faithfulness": 1.0,
|
| 22 |
-
"answer_relevancy": 0.7310359258257583,
|
| 23 |
-
"context_precision": 0.999999999975,
|
| 24 |
-
"context_recall": 1.0,
|
| 25 |
-
"rouge_score(mode=fmeasure)": 0.6643356643356644
|
| 26 |
-
},
|
| 27 |
-
{
|
| 28 |
-
"faithfulness": 0.6923076923076923,
|
| 29 |
-
"answer_relevancy": 0.8345119784254372,
|
| 30 |
-
"context_precision": 0.8666666666377778,
|
| 31 |
-
"context_recall": 1.0,
|
| 32 |
-
"rouge_score(mode=fmeasure)": 0.4396135265700484
|
| 33 |
-
},
|
| 34 |
-
{
|
| 35 |
-
"faithfulness": 0.2857142857142857,
|
| 36 |
-
"answer_relevancy": 0.0,
|
| 37 |
-
"context_precision": 0.6791666666496875,
|
| 38 |
-
"context_recall": 1.0,
|
| 39 |
-
"rouge_score(mode=fmeasure)": 0.39164490861618795
|
| 40 |
-
},
|
| 41 |
-
{
|
| 42 |
-
"faithfulness": 0.5384615384615384,
|
| 43 |
-
"answer_relevancy": 0.7938279373591263,
|
| 44 |
-
"context_precision": 0.9999999999,
|
| 45 |
-
"context_recall": 1.0,
|
| 46 |
-
"rouge_score(mode=fmeasure)": 0.36512261580381467
|
| 47 |
-
}
|
| 48 |
-
],
|
| 49 |
-
"samples": [
|
| 50 |
-
{
|
| 51 |
-
"question": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng dựa trên những cơ sở nào?",
|
| 52 |
-
"answer": "Dựa trên CONTEXT được cung cấp, chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng trên các cơ sở sau:\n\n1. **Phát triển từ chương trình nội bộ:** Chương trình được phát triển trên cơ sở chương trình đào tạo ngành Cơ điện tử năm 2009 của trường.\n2. **Tham khảo chương trình quốc tế:** Chương trình có sự tham khảo từ các trường đại học nổi tiếng trên thế giới như:\n * Stanford (Hoa Kỳ)\n * Chico (Hoa Kỳ)\n * Sibaura (Nhật Bản)\n * Đại học Quốc gia Đài Loan (NTU)\n3. **Tiêu chuẩn kiểm định:** Chương trình được kiểm định theo tiêu chuẩn AUN-QA năm 2017.\n\nNguồn: 1.1. Kỹ thuật Cơ điện tử.md",
|
| 53 |
-
"ground_truth": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình năm 2009, kết hợp tham khảo chương trình đào tạo của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Hoa Kỳ), Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU), đồng thời được kiểm định theo tiêu chuẩn AUN-QA năm 2017.",
|
| 54 |
-
"contexts": [
|
| 55 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Chương trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 56 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh.",
|
| 57 |
-
"## 1. Tên chương trình: KỸ THUẬT CƠ KHÍ\n\nChương trình đào tạo cử nhân – kỹ sư ngành Kỹ thuật Cơ khí được xây dựng dựa trên sự phát triển chương trình đào tạo ngành Chế tạo máy trước đây kết hợp với sự tham khảo chương trình đào tạo ngành chế tạo máy của các trường đại học tiên tiến trên thế giới như: Stanford (Hoa Kỳ), NTU (Đài Loan), RMIT (Úc), … ;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu để có thể làm việc và sáng tạo trong mọi môi trường lao động nhằm giải quyết những vấn đề liên quan đến thiết kế, chế tạo, vận hành và bảo trì các máy móc, thiết bị cơ khí và các hệ thống sản xuất công nghiệp đáp ứng yêu cầu của xã hội, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 58 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN ĐIỆN TỬ -VIỄN THÔNG\n\nNgành Điện tử - Viễn thông là một trong những ngành mũi nhọn của cuộc Cách mạng công nghiệp lần thứ 4 và là ngành có nhu cầu cao về nhân lực ở Việt Nam và trên thế giới. Theo học chương trình này, sinh viên được trang bị một cách toàn diện cả về kỹ năng chuyên môn và kỹ năng mềm để có thể thiết kế, chế tạo, vận hành các thiết bị và hệ thống điện tử viễn thông, đáp ứng được yêu cầu về tính năng động và sáng tạo của lĩnh vực nghề nghiệp này.\n\nChương trình tiên tiến kỹ thuật Điện tử -Viễn thông được giảng dạy hoàn toàn bằng tiếng Anh và do các giảng viên của trường ĐHBK Hà Nội và các giảng viên nước ngoài tham gia giảng dạy.\n\nChương trình được thiết kế dựa trên chương trình của trường Đại học San Jose State University, Hoa Kỳ và bao gồm khối kiến thức toán học và khoa học cơ bản, khối kiến thức cơ sở cốt lõi ngành Điện tử -Viễn thông, khối kiến thức bổ trợ kiến thức xã hội và kỹ năng mềm và khối kiến thức chuyên sâu ngành",
|
| 59 |
-
"## 1. Tên chương trình: KỸ THUẬT ĐIỆN TỬ -VIỄN THÔNG\n\nNgành Điện tử - Viễn thông là một trong những ngành mũi nhọn của cuộc Cách mạng công nghiệp lần thứ 4 và là ngành có nhu cầu cao về nhân lực ở Việt Nam và trên thế giới. Theo học ngành này, sinh viên được trang bị một cách toàn diện cả về kỹ năng chuyên môn và kỹ năng mềm để có thể thiết kế, chế tạo, vận hành các thiết bị và hệ thống điện tử viễn thông, đáp ứng được yêu cầu về tính năng động và sáng tạo của lĩnh vực nghề nghiệp này.\n\nChương trình kỹ thuật Điện tử- Viễn thông được giảng dạy bằng tiếng Việt và được thiết kế bao gồm khối kiến thức toán học và khoa học cơ bản, khối kiến thức cơ sở cốt lõi ngành Điện tử -Viễn thông, khối kiến thức bổ trợ kiến thức xã hội và kỹ năng mềm và khối kiến thức tự chọn chuyên sâu theo các định hướng kỹ thuật điện tử, kỹ thuật máy tính, kỹ thuật thông tin truyền thông, kỹ thuật y sinh, kỹ thuật điện tử hàng không vũ trụ, kỹ thuật đa phương tiện, kỹ thuật vi điện tử và công nghệ nano."
|
| 60 |
-
]
|
| 61 |
-
},
|
| 62 |
-
{
|
| 63 |
-
"question": "Sinh viên tốt nghiệp ngành Kỹ thuật Cơ điện tử đạt được những kiến thức chuyên môn gì?",
|
| 64 |
-
"answer": "Dựa trên tài liệu đào tạo ngành Kỹ thuật Cơ điện tử, sinh viên tốt nghiệp đạt được những kiến thức chuyên môn sau:\n\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử.\n\n**Nguồn:** 1.1. Kỹ thuật Cơ điện tử.md | Mục: /2. Kiến thức, kỹ năng đạt được sau tốt nghiệp/",
|
| 65 |
-
"ground_truth": "Sinh viên tốt nghiệp có kiến thức chuyên môn rộng và vững chắc, có khả năng áp dụng kiến thức cơ sở và cốt lõi của ngành Cơ điện tử kết hợp với việc sử dụng các công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống, quá trình và sản phẩm Cơ điện tử.",
|
| 66 |
-
"contexts": [
|
| 67 |
-
"# 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử.",
|
| 68 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm cơ điện tử.",
|
| 69 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nSinh viên có cơ sở kỹ thuật vững chắc và kiến thức chuyên môn sâu, có kỹ năng thực hành nghề nghiệp, đủ năng lực thiết kế, chế tạo trong lĩnh vực sản xuất thiết bị, phương tiện động lực, hệ thống và thiết bị năng lượng, điều khiển thủy lực.",
|
| 70 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức cơ sở chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành học,\nChú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Kỹ thuật Cơ khí kết hợp khả năng sử dụng công cụ hiện đại để thiết kế, chế tạo, vận hành, nghiên cứu phân tích, cải tiến máy móc, thiết bị hay hệ thống sản xuất.",
|
| 71 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\n- Có và kỹ năng cơ bản về Cơ khí, điện -điện tử, tự động hóa nhằm giúp người học có đủ năng lực giải quyết các vấn đề liên quan tới ngành nhiệt -lạnh cũng như những công việc khác nhau trong lĩnh vực cơ - điện - năng lượng;\n- Nắm được bản chất của các quá trình và thiết bị nhiệt -lạnh, chế tạo, vận hành, bảo trì sản phẩm và các hệ thống nhiệt -lạnh trong công nghiệp và dân dụng để giải quyết các vấn đề công nghệ và môi trường."
|
| 72 |
-
]
|
| 73 |
-
},
|
| 74 |
-
{
|
| 75 |
-
"question": "Tại sao sinh viên ngành Kỹ thuật Cơ điện tử có thể nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0?",
|
| 76 |
-
"answer": "Dựa trên CONTEXT được cung cấp, sinh viên ngành Kỹ thuật Cơ điện tử có thể nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0 vì:\n\n1. **Được trang bị kiến thức cơ sở và chuyên ngành vững chắc:** Chương trình đào tạo cung cấp nền tảng kiến thức rộng và sâu, giúp sinh viên có khả năng áp dụng vào thực tiễn.\n2. **Có kỹ năng nghề nghiệp và năng lực nghiên cứu:** Sinh viên được rèn luyện các kỹ năng cần thiết để giải quyết các vấn đề phức tạp.\n3. **Khả năng làm việc và sáng tạo trong mọi môi trường lao động:** Chương trình đào tạo hướng đến việc phát triển năng lực sáng tạo và thích ứng linh hoạt.\n4. **Giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp:** Đây là những lĩnh vực cốt lõi của Cách mạng công nghiệp 4.0.\n\n**Nguồn:** 1.1. Kỹ thuật Cơ điện tử.md",
|
| 77 |
-
"ground_truth": "Vì chương trình đào tạo trang bị cho sinh viên nền tảng kiến thức cơ sở và chuyên ngành vững chắc, kỹ năng nghề nghiệp, năng lực nghiên cứu, khả năng làm việc và sáng tạo trong nhiều môi trường lao động, giúp giải quyết các vấn đề liên quan đến thiết kế, chế tạo và vận hành các hệ thống cơ điện tử trong sản xuất công nghiệp hiện đại.",
|
| 78 |
-
"contexts": [
|
| 79 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Chương trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 80 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh.",
|
| 81 |
-
"## 1. Tên chương trình: KỸ THUẬT CƠ KHÍ\n\nChương trình đào tạo cử nhân – kỹ sư ngành Kỹ thuật Cơ khí được xây dựng dựa trên sự phát triển chương trình đào tạo ngành Chế tạo máy trước đây kết hợp với sự tham khảo chương trình đào tạo ngành chế tạo máy của các trường đại học tiên tiến trên thế giới như: Stanford (Hoa Kỳ), NTU (Đài Loan), RMIT (Úc), … ;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu để có thể làm việc và sáng tạo trong mọi môi trường lao động nhằm giải quyết những vấn đề liên quan đến thiết kế, chế tạo, vận hành và bảo trì các máy móc, thiết bị cơ khí và các hệ thống sản xuất công nghiệp đáp ứng yêu cầu của xã hội, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 82 |
-
"## 1. Tên chương trình: KỸ THUẬT ĐIỆN TỬ -VIỄN THÔNG\n\nNgành Điện tử - Viễn thông là một trong những ngành mũi nhọn của cuộc Cách mạng công nghiệp lần thứ 4 và là ngành có nhu cầu cao về nhân lực ở Việt Nam và trên thế giới. Theo học ngành này, sinh viên được trang bị một cách toàn diện cả về kỹ năng chuyên môn và kỹ năng mềm để có thể thiết kế, chế tạo, vận hành các thiết bị và hệ thống điện tử viễn thông, đáp ứng được yêu cầu về tính năng động và sáng tạo của lĩnh vực nghề nghiệp này.\n\nChương trình kỹ thuật Điện tử- Viễn thông được giảng dạy bằng tiếng Việt và được thiết kế bao gồm khối kiến thức toán học và khoa học cơ bản, khối kiến thức cơ sở cốt lõi ngành Điện tử -Viễn thông, khối kiến thức bổ trợ kiến thức xã hội và kỹ năng mềm và khối kiến thức tự chọn chuyên sâu theo các định hướng kỹ thuật điện tử, kỹ thuật máy tính, kỹ thuật thông tin truyền thông, kỹ thuật y sinh, kỹ thuật điện tử hàng không vũ trụ, kỹ thuật đa phương tiện, kỹ thuật vi điện tử và công nghệ nano.",
|
| 83 |
-
"# 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử."
|
| 84 |
-
]
|
| 85 |
-
},
|
| 86 |
-
{
|
| 87 |
-
"question": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được điều chỉnh như thế nào để đảm bảo t��nh cập nhật mà không ảnh hưởng đến người học?",
|
| 88 |
-
"answer": "Dựa trên các tài liệu được cung cấp, tôi không tìm thấy thông tin cụ thể về việc chương trình đào tạo ngành **Kỹ thuật Cơ điện tử** được điều chỉnh như thế nào để đảm bảo tính cập nhật mà không ảnh hưởng đến người học.\n\nTuy nhiên, các tài liệu về các chương trình đào tạo khác (Kỹ thuật Ô tô, Chương trình tiên tiến Điện tử - Viễn thông, Chương trình tiên tiến Kỹ thuật Ô tô, Chương trình tiên tiến Điều khiển - Tự động hóa và Hệ thống điện) đều có ghi nhận nguyên tắc chung:\n\n> \"Chương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển của khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.\"\n\nNguyên tắc này có thể áp dụng tương tự cho chương trình Kỹ thuật Cơ điện tử, nhưng không có thông tin cụ thể trong tài liệu về ngành này.\n\n**Nguồn:** Tài liệu 1.3, 2.5, 1.7, 2.3",
|
| 89 |
-
"ground_truth": "Chương trình đào tạo có thể được điều chỉnh hàng năm nhằm đảm bảo tính cập nhật với sự phát triển của khoa học, kỹ thuật và công nghệ, nhưng vẫn tuân thủ nguyên tắc không gây ảnh hưởng ngược tới kết quả học tập mà người học đã tích lũy.",
|
| 90 |
-
"contexts": [
|
| 91 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Chương trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 92 |
-
"## 4. Danh mục học phần và thời lượng học tập:\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển của khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 93 |
-
"## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 94 |
-
"## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 95 |
-
"## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy."
|
| 96 |
-
]
|
| 97 |
-
},
|
| 98 |
-
{
|
| 99 |
-
"question": "Chương trình đào tạo kỹ sư chuyên sâu đặc thù (180 tín chỉ) áp dụng cho những đối tượng sinh viên nào?",
|
| 100 |
-
"answer": "Dựa trên các tài liệu được cung cấp, chương trình đào tạo kỹ sư chuyên sâu đặc thù (180 tín chỉ) áp dụng cho các đối tượng sau:\n\n**1. Sinh viên các khóa từ K62:**\n- Áp dụng với sinh viên các khóa từ K62 đang học tập và có mong muốn tiếp tục học tập theo chương trình đào tạo kỹ sư chuyên sâu đặc thù.\n\n**2. Sinh viên theo chương trình tích hợp cử nhân-kỹ sư:**\n- Chương trình tích hợp cử nhân-kỹ sư có thời gian thiết kế là 5,5 năm và khối lượng học tập 180 TC, bao gồm hai bậc trình độ: Cử nhân (4 năm) và Kỹ sư (1,5 năm).\n\n**3. Học viên chương trình đào tạo kỹ sư chuyên sâu theo chương trình tích hợp:**\n- Dành cho học viên đã được cấp mã số học viên tạm thời và học viên đã được công nhận chính thức.\n\n**Nguồn:**\n- Tài liệu 1: 01_3 HD hoc chuyen tiep ky su 180 TC_Final.md\n- Tài liệu 3: 02_ Hướng dẫn công nhận học phần vào CTĐT KSCS.md \n- Tài liệu 4: QCDT_2025_5445_QD-DHBK.md",
|
| 101 |
-
"ground_truth": "Chương trình áp dụng cho sinh viên các khóa từ K62 đang học tập và có mong muốn tiếp tục học theo chương trình kỹ sư chuyên sâu đặc thù, đồng thời áp dụng cho các chương trình đào tạo có đào tạo bậc kỹ sư chuyên sâu đặc thù.",
|
| 102 |
-
"contexts": [
|
| 103 |
-
"# HƯỚNG DẪN HỌC TẬP CHƯƠNG TRÌNH ĐÀO TẠO KỸ SƯ CHUYÊN SÂU ĐẶC THÙ (180 TC)\n\n## 1. Phạm vi áp dụng\n+ Áp dụng với sinh viên các khóa từ K62 đang học tập và có mong muốn tiếp tục học tập theo chương trình đạo tạo kỹ sư chuyên sâu đặc thù.\n+ Áp dụng với các chương trình đào tạo (viết tắt là: CTĐT) có đào tạo bậc kỹ sư chuyên sâu đặc thù (viết tắt là: KS) .",
|
| 104 |
-
"## II. Khung chương trình đào tạo kỹ sư chuyên sâu\n\n1. CTĐT KSCS có khối lượng học tập 60 tín chỉ đối với người học đã tốt nghiệp đại học. Thời gian học tập toàn khóa theo kế hoạch học tập chuẩn là 2 năm, người tốt nghiệp được cấp bằng kỹ sư bậc 7 theo Khung năng lực quốc gia Việt Nam .\n2. Điểm trung bình toàn khóa được sử dụng để xét tốt nghiệp và xếp hạng tốt nghiệp là điểm trung bình tích lũy toàn khóa tính theo các học phần đăng ký xét tốt nghiệp thuộc CTĐT KSCS .\n3. Học viên của CTĐT KSCS được Giám đốc Đại học Bách khoa Hà Nội công nhận tốt nghiệp được cấp bằng tốt nghiệp Kỹ sư bậc 7 theo Khung năng lực quốc gia Việt Nam.\n4. Khung quy định của CTĐT KSCS:\nKhung quy định của chương trình đào tạo Kỹ sư chuyên sâu đặc thù (KSCS) gồm các nhóm học phần sau:\nNhóm kiến thức định hướng chuyên ngành có khối lượng 12 tín chỉ,\ngồm các học phần được thiết kế theo nhóm để định hướng người học tiếp cận theo chuyên ngành.\nNhóm kiến thức chuyên ngành cốt lõi có khối lượng từ 19 đến 20 tín chỉ,\nlà ",
|
| 105 |
-
"# HƯỚNG DẪN CÔNG NHẬN HỌC PHẦN VÀO CHƯƠNG TRÌNH ĐÀO TẠO KỸ SƯ CHUYÊN SÂU\n\n## I. Phạm vi áp dụng:\n1. Dành cho học viên chương trình đào tạo kỹ sư chuyên sâu (KSCS) theo chương trình tích hợp.\n2. Dành cho học viên đã được cấp mã số học viên tạm thời và học viên đã được công nhận chính thức.",
|
| 106 |
-
"Sinh viên tham gia chương trình được tuyển chọn dựa trên thành tích học tập xuất sắc và có tiềm năng nghiên cứu. Chương trình đảm bảo trang bị cho người học kiến thức chuyên sâu theo định hướng nghiên cứu, khả năng tư duy độc lập và sáng tạo, đáp ứng yêu cầu nhân lực chất lượng cao. Chương trình được tổ chức với quy mô lớp học nhỏ, giảng viên có trình độ cao và phương pháp giảng dạy hiện đại, tạo điều kiện tối ưu để sinh viên phát huy năng lực. Sinh viên thuộc các CTĐT Tài năng được tạo điều kiện để tham gia các hoạt động nghiên cứu khoa học, trao đổi học thuật, công nhận tín chỉ đã tích lũy từ các cơ sở đào tạo đối tác.\n\n6. Thời gian và khối lượng học tập chuẩn đối với các CTĐT theo hình thức chính quy không kể các học phần bổ sung kiến thức được quy định như sau:\nChương trình Cử nhân dành cho người học tốt nghiệp THPT,\nthời gian đào tạo 4 năm, khối lượng học tập tối thiểu 132 tín chỉ.\nChương trình Kỹ sư dành cho người học tốt nghiệp cử nhân theo chương trình tích hợp,\nthời gian đào t",
|
| 107 |
-
"### Điều 2. Ngành đào tạo, chương trình đào tạo\n1. Ngành đào tạo (sau đây gọi tắt là ngành) là một lĩnh vực chuyên môn rộng, có mã số trong Danh mục thống kê ngành đào tạo của giáo dục đại học do Bộ GDĐT quản lý 4 .\n2. Chương trình đào tạo (CTĐT) được xây dựng theo đơn vị tín chỉ (TC) và là bản thiết kế cho toàn bộ quá trình đào tạo của một ngành. Chương trình thể hiện rõ trình độ đào tạo; đối tượng đào tạo, điều kiện nhập học và điều kiện tốt nghiệp; m���c tiêu đào tạo, chuẩn kiến thức, kỹ năng, mức tự chủ và trách nhiệm của người học khi tốt nghiệp; nội dung (chương trình giảng dạy); kế hoạch đào tạo theo thời gian học tập chuẩn; phương pháp và hình thức đào tạo; cách thức đánh giá kết quả học tập; các điều kiện thực hiện chương trình.\n3. CTĐT tích hợp là chương trình được thiết kế tổng thể theo hướng tích hợp kiến thức, kỹ năng của hai bậc trình độ, đảm bảo học tập liên tục giữa các bậc đào tạo nhằm tối ưu hóa thời gian đào tạo cho người học. CTĐT tích hợp cử nhân-kỹ sư, cử nhân-thạc "
|
| 108 |
-
]
|
| 109 |
-
}
|
| 110 |
-
]
|
| 111 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
evaluation/results/ragas_20260105_130744.json
DELETED
|
@@ -1,111 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"timestamp": "20260105_130744",
|
| 3 |
-
"retrieval_mode": "bm25_only",
|
| 4 |
-
"config": {
|
| 5 |
-
"sample_size": 5,
|
| 6 |
-
"gen_model": "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 7 |
-
"eval_model": "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 8 |
-
"embed_model": "Qwen/Qwen3-Embedding-4B",
|
| 9 |
-
"collection": "hust_rag_collection",
|
| 10 |
-
"doc_count": 1441
|
| 11 |
-
},
|
| 12 |
-
"scores": [
|
| 13 |
-
{
|
| 14 |
-
"faithfulness": 0.875,
|
| 15 |
-
"answer_relevancy": 0.7264722326341989,
|
| 16 |
-
"context_precision": 0.99999999995,
|
| 17 |
-
"context_recall": 1.0,
|
| 18 |
-
"rouge_score(mode=fmeasure)": 0.7589285714285714
|
| 19 |
-
},
|
| 20 |
-
{
|
| 21 |
-
"faithfulness": 0.9285714285714286,
|
| 22 |
-
"answer_relevancy": 0.7342079659938445,
|
| 23 |
-
"context_precision": 0.999999999975,
|
| 24 |
-
"context_recall": 1.0,
|
| 25 |
-
"rouge_score(mode=fmeasure)": 0.6351351351351351
|
| 26 |
-
},
|
| 27 |
-
{
|
| 28 |
-
"faithfulness": 0.8888888888888888,
|
| 29 |
-
"answer_relevancy": 0.8335330619249784,
|
| 30 |
-
"context_precision": 0.9999999999666667,
|
| 31 |
-
"context_recall": 1.0,
|
| 32 |
-
"rouge_score(mode=fmeasure)": 0.5673352435530086
|
| 33 |
-
},
|
| 34 |
-
{
|
| 35 |
-
"faithfulness": 0.42857142857142855,
|
| 36 |
-
"answer_relevancy": 0.6506986819875058,
|
| 37 |
-
"context_precision": 0.99999999998,
|
| 38 |
-
"context_recall": 1.0,
|
| 39 |
-
"rouge_score(mode=fmeasure)": 0.5048543689320388
|
| 40 |
-
},
|
| 41 |
-
{
|
| 42 |
-
"faithfulness": 0.8333333333333334,
|
| 43 |
-
"answer_relevancy": 0.8338701751762124,
|
| 44 |
-
"context_precision": 0.9999999999,
|
| 45 |
-
"context_recall": 1.0,
|
| 46 |
-
"rouge_score(mode=fmeasure)": 0.5344827586206896
|
| 47 |
-
}
|
| 48 |
-
],
|
| 49 |
-
"samples": [
|
| 50 |
-
{
|
| 51 |
-
"question": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng dựa trên những cơ sở nào?",
|
| 52 |
-
"answer": "Dựa trên CONTEXT được cung cấp, chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng trên cơ sở:\n\n- Phát triển chương trình đào tạo ngành Cơ điện tử năm 2009.\n- Tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Hoa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU).\n\nNguồn: 1.1. Kỹ thuật Cơ điện tử.md",
|
| 53 |
-
"ground_truth": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình năm 2009, kết hợp tham khảo chương trình đào tạo của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Hoa Kỳ), Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU), đồng thời được kiểm định theo tiêu chuẩn AUN-QA năm 2017.",
|
| 54 |
-
"contexts": [
|
| 55 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh.",
|
| 56 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Chương trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 57 |
-
"## 1. Tên chương trình: KỸ THUẬT CƠ KHÍ\n\nChương trình đào tạo cử nhân – kỹ sư ng��nh Kỹ thuật Cơ khí được xây dựng dựa trên sự phát triển chương trình đào tạo ngành Chế tạo máy trước đây kết hợp với sự tham khảo chương trình đào tạo ngành chế tạo máy của các trường đại học tiên tiến trên thế giới như: Stanford (Hoa Kỳ), NTU (Đài Loan), RMIT (Úc), … ;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu để có thể làm việc và sáng tạo trong mọi môi trường lao động nhằm giải quyết những vấn đề liên quan đến thiết kế, chế tạo, vận hành và bảo trì các máy móc, thiết bị cơ khí và các hệ thống sản xuất công nghiệp đáp ứng yêu cầu của xã hội, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 58 |
-
"# 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử.",
|
| 59 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm cơ điện tử."
|
| 60 |
-
]
|
| 61 |
-
},
|
| 62 |
-
{
|
| 63 |
-
"question": "Sinh viên tốt nghiệp ngành Kỹ thuật Cơ điện tử đạt được những kiến thức chuyên môn gì?",
|
| 64 |
-
"answer": "Dựa trên CONTEXT được cung cấp, sinh viên tốt nghiệp ngành Kỹ thuật Cơ điện tử sẽ đạt được những kiến thức chuyên môn sau:\n\n**Kiến thức chuyên môn:**\n- Có kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành.\n- Chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử.\n- Kết hợp khả năng sử dụng công cụ hiện đại để:\n - Nghiên cứu\n - Thiết kế\n - Chế tạo\n - Xây dựng\n - Vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử.\n\n**Nguồn:** Tài liệu 1.1. Kỹ thuật Cơ điện tử.md, Mục 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp",
|
| 65 |
-
"ground_truth": "Sinh viên tốt nghiệp có kiến thức chuyên môn rộng và vững chắc, có khả năng áp dụng kiến thức cơ sở và cốt lõi của ngành Cơ điện tử kết hợp với việc sử dụng các công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống, quá trình và sản phẩm Cơ điện tử.",
|
| 66 |
-
"contexts": [
|
| 67 |
-
"# 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử.",
|
| 68 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm cơ điện tử.",
|
| 69 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh.",
|
| 70 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức cơ sở chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành học,\nChú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Kỹ thuật Cơ khí kết hợp khả năng sử dụng công cụ hiện đại để thiết kế, chế tạo, vận hành, nghiên cứu phân tích, cải tiến máy móc, thiết bị hay hệ thống sản xuất.",
|
| 71 |
-
"## 1. Tên chương trình: KỸ THUẬT ĐIỆN TỬ -VIỄN THÔNG\n\nNgành Điện tử - Viễn thông là một trong những ngành mũi nhọn của cuộc Cách mạng công nghiệp lần thứ 4 và là ngành có nhu cầu cao về nhân lực ở Việt Nam và trên thế giới. Theo học ngành này, sinh viên được trang bị một cách toàn diện cả về kỹ năng chuyên môn và kỹ năng mềm để có thể thiết kế, chế tạo, vận hành các thiết bị và hệ thống điện tử viễn thông, đáp ứng được yêu cầu về tính năng động và sáng tạo của lĩnh vực nghề nghiệp này.\n\nChương trình kỹ thuật Điện tử- Viễn thông được giảng dạy bằng tiếng Việt và được thiết kế bao gồm khối kiến thức toán học và khoa học cơ bản, khối kiến thức cơ sở cốt lõi ngành Điện tử -Viễn thông, khối kiến thức bổ trợ kiến thức xã hội và kỹ năng mềm và khối kiến thức tự chọn chuyên sâu theo các định hướng kỹ thuật điện tử, kỹ thuật máy tính, kỹ thuật thông tin truyền thông, kỹ thuật y sinh, kỹ thuật điện tử hàng không vũ trụ, kỹ thuật đa phương tiện, kỹ thuật vi điện tử và công nghệ nano."
|
| 72 |
-
]
|
| 73 |
-
},
|
| 74 |
-
{
|
| 75 |
-
"question": "Tại sao sinh viên ngành Kỹ thuật Cơ điện tử có thể nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0?",
|
| 76 |
-
"answer": "Dựa trên CONTEXT được cung cấp, sinh viên ngành Kỹ thuật Cơ điện tử có thể nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0 vì:\n\nChương trình đào tạo trang bị cho sinh viên:\n- **Các kiến thức cơ sở và chuyên ngành vững chắc**\n- **Kỹ năng nghề nghiệp và năng lực nghiên cứu**\n- **Khả năng làm việc và sáng tạo trong mọi môi trường lao động**\n- **Năng lực giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp**\n\nĐây là những yếu tố cốt lõi giúp sinh viên tốt nghiệp có đủ năng lực để thích ứng nhanh với yêu cầu của cuộc cách mạng công nghiệp 4.0.\n\n**Nguồn:** 1.1. Kỹ thuật Cơ điện tử.md",
|
| 77 |
-
"ground_truth": "Vì chương trình đào tạo trang bị cho sinh viên nền tảng kiến thức cơ sở và chuyên ngành vững chắc, kỹ năng nghề nghiệp, năng lực nghiên cứu, khả năng làm việc và sáng tạo trong nhiều môi trường lao động, giúp giải quyết các vấn đề liên quan đến thiết kế, chế tạo và vận hành các hệ thống cơ điện tử trong sản xuất công nghiệp hiện đại.",
|
| 78 |
-
"contexts": [
|
| 79 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường l��m việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh.",
|
| 80 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Chương trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 81 |
-
"## 1. Tên chương trình: KỸ THUẬT CƠ KHÍ\n\nChương trình đào tạo cử nhân – kỹ sư ngành Kỹ thuật Cơ khí được xây dựng dựa trên sự phát triển chương trình đào tạo ngành Chế tạo máy trước đây kết hợp với sự tham khảo chương trình đào tạo ngành chế tạo máy của các trường đại học tiên tiến trên thế giới như: Stanford (Hoa Kỳ), NTU (Đài Loan), RMIT (Úc), … ;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu để có thể làm việc và sáng tạo trong mọi môi trường lao động nhằm giải quyết những vấn đề liên quan đến thiết kế, chế tạo, vận hành và bảo trì các máy móc, thiết bị cơ khí và các hệ thống sản xuất công nghiệp đáp ứng yêu cầu của xã hội, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 82 |
-
"## b. Kỹ năng\n- Kỹ năng chuyên nghiệp và phẩm chất cá nhân, kỹ năng xã hội cần thiết và giao tiếp để làm việc hiệu quả trong nhóm đa ngành và trong môi trường hội nhập quốc tế; có năng lực khởi nghiệp và thích ứng tốt với cuộc cách mạng công nghiệp 4.0.\n- Hiểu biết về chính trị, kinh tế; có kiến thức cơ bản về khoa học xã hội và nhân văn phù hợp với chuyên ngành được được đào tạo để đóng góp hiệu quả vào sự phát triển bền vững của đất nước.",
|
| 83 |
-
"## b. Kỹ năng\n- Có kỹ năng chuyên nghiệp và phẩm chất cá nhân, kỹ năng xã hội cần thiết và giao tiếp để làm việc hiệu quả trong nhóm đa ngành và trong môi trường hội nhập quốc tế;\n- Có năng lực khởi nghiệp và thích ứng tốt với cuộc cách mạng công nghiệp 4.0;\n- Có kiến thức cơ bản về khoa học xã hội và nhân văn phù hợp với ngành Kỹ thuật Sinh học để đóng góp hiệu quả vào sự phát triển bền vững của đất nước."
|
| 84 |
-
]
|
| 85 |
-
},
|
| 86 |
-
{
|
| 87 |
-
"question": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được điều chỉnh như thế nào để đảm bảo tính cập nhật mà không ảnh hưởng đến người học?",
|
| 88 |
-
"answer": "Dựa trên tài liệu về Chương trình tiên tiến Cơ điện tử (ME-E1), chương trình đào tạo được điều chỉnh như sau:\n\n**Chương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển của khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.**\n\nĐiều này có nghĩa là:\n- Chương trình được cập nhật hàng năm theo xu hướng phát triển của khoa học, kỹ thuật và công nghệ\n- Việc điều chỉnh tuân thủ nguyên tắc bảo vệ quyền lợi của người học, không làm ảnh hưởng đến kết quả học tập mà sinh viên đã tích lũy được\n\n*Nguồn: 1.6. Chương trình tiên tiến Cơ điện tử.md*",
|
| 89 |
-
"ground_truth": "Chương trình đào tạo có thể được điều chỉnh hàng năm nhằm đảm bảo tính cập nhật với sự phát triển của khoa học, kỹ thuật và công nghệ, nhưng vẫn tuân thủ nguyên tắc không gây ảnh hưởng ngược tới kết quả học tập mà người học đã tích lũy.",
|
| 90 |
-
"contexts": [
|
| 91 |
-
"## 3. Thời gian đào tạo\n\n- Đào tạo Kỹ sư: 5 năm\n\n## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển ển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 92 |
-
"## 3. Thời gian đào tạo\n\n- Đào tạo Kỹ sư: 5 năm\n\n## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển ển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 93 |
-
"## 4. Danh mục học phần và thời lượng học tập:\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển của khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 94 |
-
"## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 95 |
-
"## 4. Danh mục học phần và thời lượng học tập:\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy."
|
| 96 |
-
]
|
| 97 |
-
},
|
| 98 |
-
{
|
| 99 |
-
"question": "Chương trình đào tạo kỹ sư chuyên sâu đặc thù (180 tín chỉ) áp dụng cho những đối tượng sinh viên nào?",
|
| 100 |
-
"answer": "Dựa trên tài liệu hướng dẫn học tập chương trình đào tạo kỹ sư chuyên sâu đặc thù (180 TC), chương trình này áp dụng với:\n\n**Đối tượng áp dụng:**\n- Sinh viên các khóa từ K62 đang học tập tại Đại học Bách khoa Hà Nội\n- Sinh viên có mong muốn tiếp tục học tập theo chương trình đào tạo kỹ sư chuyên sâu đặc thù\n\n**Nguồn:** 01_3 HD hoc chuyen tiep ky su 180 TC_Final.md | Mục: /HƯỚNG DẪN HỌC TẬP CHƯƠNG TRÌNH ĐÀO TẠO KỸ SƯ CHUYÊN SÂU ĐẶC THÙ (180 TC)/ | Phần: 1. Phạm vi áp dụng",
|
| 101 |
-
"ground_truth": "Chương trình áp dụng cho sinh viên các khóa từ K62 đang học tập và có mong muốn tiếp tục học theo chương trình kỹ sư chuyên sâu đặc thù, đồng thời áp dụng cho các chương trình đào tạo có đào tạo bậc kỹ sư chuyên sâu đặc thù.",
|
| 102 |
-
"contexts": [
|
| 103 |
-
"# HƯỚNG DẪN HỌC TẬP CHƯƠNG TRÌNH ĐÀO TẠO KỸ SƯ CHUYÊN SÂU ĐẶC THÙ (180 TC)\n\n## 1. Phạm vi áp dụng\n+ Áp dụng với sinh viên các khóa từ K62 đang học tập và có mong muốn tiếp tục học tập theo chương trình đạo tạo kỹ sư chuyên sâu đặc thù.\n+ Áp dụng với các chương trình đào tạo (viết tắt là: CTĐT) có đào tạo bậc kỹ sư chuyên sâu đặc thù (viết tắt là: KS) .",
|
| 104 |
-
"### Điều 1. Phạm vi điều chỉnh và đối tượng áp dụng\n\n1. Phạm vi điều chỉnh: Văn bản này quy định về đối tượng, tiêu chuẩn, quy trình, thủ tục đăng ký xét, cấp học bổng cho sinh viên/học viên (sau đây gọi chung là người học) Đại học Bách khoa Hà Nội có đồ án tốt nghiệp/khóa luận tốt nghiệp góp phần cải tiến, nâng cao chất lượng, hiệu quả kinh doanh, sản xuất hoặc chất lượng các dịch vụ an sinh xã hội tại quê hương của người học. Học bổng này được cấp với tên gọi là Học bổng Gắn kết quê hương .\n2. Đối tượng áp dụng: Quy định này áp dụng đối với sinh viên hệ đại học chính quy văn bằng thứ nhất hoặc học viên chương trình kỹ sư chuyên sâu đặc thù đang học tập tại Đại học Bách khoa Hà Nội.",
|
| 105 |
-
"## 4. Điểm trung bình toàn khóa và hạng tốt nghiệp kỹ sư\n+ Điểm của các học phần bậc kỹ sư chuyên sâu đặc thù sẽ không được sử dụng để xét cấp học bổng khuyến khích học tập ở bậc cử nhân .\n+ Điểm trung bình toàn khóa và hạng tốt nghiệp kỹ sư được thực hiện theo Quy chế đào tạo hiện hành .\n+ Trích dẫn quy định về điểm trung bình toàn khóa: \" là điểm trung bình tích lũy của cả giai đoạn đào tạo cử nhân đã tốt nghiệp và giai đoạn đào tạo kỹ sư, với trọng số là tỷ lệ phần trăm của s ố tín chỉ tích lũy của mỗi giai đoạn đào tạo trong tổng số tín chỉ tích lũy của cả hai giai đoạn\".\n+ Xếp bậc năng lực với người học được cấp bằng kỹ sư chuyên sâu đặc thù: người học hoàn thành đủ các học phần theo yêu cầu của CTĐT và đạt chứng chỉ ngoại ngữ tối thiểu trình độ B2 (hoặc tương đương) theo Khung năng lực ngoại ngữ 6 bậc của Việt Nam: được cấp bằng Kỹ sư chuyên sâu đặc thù trình độ Bậc 7 theo Khung trình độ quốc gia Việt Nam .",
|
| 106 |
-
"### 6) Chế độ miễn giảm học phí\n\n- Đại học hỗ trợ toàn bộ phần chênh lệch giữa mức học phí của Đại học với mức học phí được miễn, giảm theo quy định của Nhà nước đối với các sinh viên thuộc đối tượng được hưởng chế độ chính sách miễn, giảm học phí theo quy định.\n- Việc hỗ trợ học phí chênh lệch và chế độ miễn giảm học phí theo quy định của Nhà nước tại mục 1, 2, 3 của Phụ lục này không áp dụng với các học phần thuộc khung chương trình đào tạo kỹ sư chuyên sâu đặc thù.",
|
| 107 |
-
"## 6) Chế độ miễn giảm học phí\n\n- -Đại học hỗ trợ toàn bộ phần chênh lệch giữa mức học phí của Đại học với mức học phí được miễn, giảm theo quy định của Nhà nước đối với các sinh viên thuộc đối tượng được hưởng chế độ chính sách miễn, giảm học phí theo quy định.\n- -Việc hỗ trợ học phí chênh lệch và chế độ miễn giảm học phí theo quy định của Nhà nước tại mục 1, 2, 3 của Phụ lục này không áp dụng với các học phần thuộc khung chương trình đào tạo kỹ sư chuyên sâu ."
|
| 108 |
-
]
|
| 109 |
-
}
|
| 110 |
-
]
|
| 111 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
evaluation/results/ragas_20260105_131901.json
DELETED
|
@@ -1,111 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"timestamp": "20260105_131901",
|
| 3 |
-
"retrieval_mode": "hybrid",
|
| 4 |
-
"config": {
|
| 5 |
-
"sample_size": 5,
|
| 6 |
-
"gen_model": "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 7 |
-
"eval_model": "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 8 |
-
"embed_model": "Qwen/Qwen3-Embedding-4B",
|
| 9 |
-
"collection": "hust_rag_collection",
|
| 10 |
-
"doc_count": 1441
|
| 11 |
-
},
|
| 12 |
-
"scores": [
|
| 13 |
-
{
|
| 14 |
-
"faithfulness": 0.8571428571428571,
|
| 15 |
-
"answer_relevancy": 0.8972923254436237,
|
| 16 |
-
"context_precision": 0.99999999995,
|
| 17 |
-
"context_recall": 1.0,
|
| 18 |
-
"rouge_score(mode=fmeasure)": 0.7606837606837608
|
| 19 |
-
},
|
| 20 |
-
{
|
| 21 |
-
"faithfulness": 1.0,
|
| 22 |
-
"answer_relevancy": 0.7008081720389104,
|
| 23 |
-
"context_precision": 0.94999999997625,
|
| 24 |
-
"context_recall": 1.0,
|
| 25 |
-
"rouge_score(mode=fmeasure)": 0.44655581947743467
|
| 26 |
-
},
|
| 27 |
-
{
|
| 28 |
-
"faithfulness": 1.0,
|
| 29 |
-
"answer_relevancy": 0.8991661959917713,
|
| 30 |
-
"context_precision": 0.99999999995,
|
| 31 |
-
"context_recall": 1.0,
|
| 32 |
-
"rouge_score(mode=fmeasure)": 0.6297577854671281
|
| 33 |
-
},
|
| 34 |
-
{
|
| 35 |
-
"faithfulness": 1.0,
|
| 36 |
-
"answer_relevancy": 0.0,
|
| 37 |
-
"context_precision": 0.99999999998,
|
| 38 |
-
"context_recall": 1.0,
|
| 39 |
-
"rouge_score(mode=fmeasure)": 0.1372549019607843
|
| 40 |
-
},
|
| 41 |
-
{
|
| 42 |
-
"faithfulness": 0.7,
|
| 43 |
-
"answer_relevancy": 0.7942512688700306,
|
| 44 |
-
"context_precision": 0.49999999995,
|
| 45 |
-
"context_recall": 1.0,
|
| 46 |
-
"rouge_score(mode=fmeasure)": 0.39747634069400634
|
| 47 |
-
}
|
| 48 |
-
],
|
| 49 |
-
"samples": [
|
| 50 |
-
{
|
| 51 |
-
"question": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng dựa trên những cơ sở nào?",
|
| 52 |
-
"answer": "Dựa trên tài liệu được cung cấp, chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009, kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Hoa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU).\n\nNguồn: 1.1. Kỹ thuật Cơ điện tử.md",
|
| 53 |
-
"ground_truth": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình năm 2009, kết hợp tham khảo chương trình đào tạo của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Hoa Kỳ), Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU), đồng thời được kiểm định theo tiêu chuẩn AUN-QA năm 2017.",
|
| 54 |
-
"contexts": [
|
| 55 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Chương trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 56 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh.",
|
| 57 |
-
"## 1. Tên chương trình: KỸ THUẬT CƠ KHÍ\n\nChương trình đào tạo cử nhân – kỹ sư ngành Kỹ thuật Cơ khí được xây dựng dựa trên sự phát triển chương trình đào tạo ngành Chế tạo máy trước đây kết hợp với sự tham khảo chương trình đào tạo ngành chế tạo máy của các trường đại học tiên tiến trên thế giới như: Stanford (Hoa Kỳ), NTU (Đài Loan), RMIT (Úc), … ;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu để có thể làm việc và sáng tạo trong mọi môi trường lao động nhằm giải quyết những vấn đề liên quan đến thiết kế, chế tạo, vận hành và bảo trì các máy móc, thiết bị cơ khí và các hệ thống sản xuất công nghiệp đáp ứng yêu cầu của xã hội, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 58 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN ĐIỆN TỬ -VIỄN THÔNG\n\nNgành Điện tử - Viễn thông là một trong những ngành mũi nhọn của cuộc Cách mạng công nghiệp lần thứ 4 và là ngành có nhu cầu cao về nhân lực ở Việt Nam và trên thế giới. Theo học chương trình này, sinh viên được trang bị một cách toàn diện cả về kỹ năng chuyên môn và kỹ năng mềm để có thể thiết kế, chế tạo, vận hành các thiết bị và hệ thống điện tử viễn thông, đáp ứng được yêu cầu về tính năng động và sáng tạo của lĩnh vực nghề nghiệp này.\n\nChương trình tiên tiến kỹ thuật Điện tử -Viễn thông được giảng dạy hoàn toàn bằng tiếng Anh và do các giảng viên của trường ĐHBK Hà Nội và các giảng viên nước ngoài tham gia giảng dạy.\n\nChương trình được thiết kế dựa trên chương trình của trường Đại học San Jose State University, Hoa Kỳ và bao gồm khối kiến thức toán học và khoa học cơ bản, khối kiến thức cơ sở cốt lõi ngành Điện tử -Viễn thông, khối kiến thức bổ trợ kiến thức xã hội và kỹ năng mềm và khối kiến thức chuyên sâu ngành",
|
| 59 |
-
"# 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử."
|
| 60 |
-
]
|
| 61 |
-
},
|
| 62 |
-
{
|
| 63 |
-
"question": "Sinh viên tốt nghiệp ngành Kỹ thuật Cơ điện tử đạt được những kiến thức chuyên môn gì?",
|
| 64 |
-
"answer": "Dựa trên các tài liệu được cung cấp, sinh viên tốt nghiệp ngành Kỹ thuật Cơ điện tử sẽ đạt được những kiến thức chuyên môn sau:\n\n**Kiến thức chuyên môn:**\n- Có kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành\n- Chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử\n- Kết hợp khả năng sử dụng công cụ hiện đại để:\n - Nghiên cứu\n - Thiết kế\n - Chế tạo\n - Xây dựng\n - Vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử\n\n**Năng lực làm việc:**\n- Có kỹ năng nghề nghiệp và năng lực nghiên cứu\n- Khả năng làm việc và sáng tạo trong mọi môi trường lao động\n- Giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử\n- Vận hành các hệ thống sản xuất công nghiệp\n- Nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0\n\n*Nguồn: 1.1. Kỹ thuật Cơ điện tử.md, Mục 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp*",
|
| 65 |
-
"ground_truth": "Sinh viên tốt nghiệp có kiến thức chuyên môn rộng và vững chắc, có khả năng áp dụng kiến thức cơ sở và cốt lõi của ngành Cơ điện tử kết hợp với việc sử dụng các công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống, quá trình và sản phẩm Cơ điện tử.",
|
| 66 |
-
"contexts": [
|
| 67 |
-
"# 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử.",
|
| 68 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm cơ điện tử.",
|
| 69 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức cơ sở chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành học,\nChú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Kỹ thuật Cơ khí kết hợp khả năng sử dụng công cụ hiện đại để thiết kế, chế tạo, vận hành, nghiên cứu phân tích, cải tiến máy móc, thiết bị hay hệ thống sản xuất.",
|
| 70 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nSinh viên tốt nghiệp có kiến thức về khoa học cơ bản, về Kỹ thuật Cơ khí, về Kỹ thuật Hàng không vững vàng, có kỹ năng thực hành nghề nghiệp, đủ năng lực áp dụng các kiến thức để vận hành, bảo dưỡng, thiết kế và triển khai các hệ thống, thiết bị liên quan đến Cơ khí Hàng không .",
|
| 71 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Chương trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0."
|
| 72 |
-
]
|
| 73 |
-
},
|
| 74 |
-
{
|
| 75 |
-
"question": "Tại sao sinh viên ngành Kỹ thuật Cơ điện tử có thể nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0?",
|
| 76 |
-
"answer": "Theo tài liệu đào tạo ngành Kỹ thuật Cơ điện tử, sinh viên có thể nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0 vì:\n\nSinh viên được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp.\n\n**Nguồn:** 1.1. Kỹ thuật Cơ điện tử.md",
|
| 77 |
-
"ground_truth": "Vì chương trình đào tạo trang bị cho sinh viên nền tảng kiến thức cơ sở và chuyên ngành vững chắc, kỹ năng nghề nghiệp, năng lực nghiên cứu, khả năng làm việc và sáng tạo trong nhiều môi trường lao động, giúp giải quyết các vấn đề liên quan đến thiết kế, chế tạo và vận hành các hệ thống cơ điện tử trong sản xuất công nghiệp hiện đại.",
|
| 78 |
-
"contexts": [
|
| 79 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Chương trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề li��n quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 80 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh.",
|
| 81 |
-
"## 1. Tên chương trình: KỸ THUẬT CƠ KHÍ\n\nChương trình đào tạo cử nhân – kỹ sư ngành Kỹ thuật Cơ khí được xây dựng dựa trên sự phát triển chương trình đào tạo ngành Chế tạo máy trước đây kết hợp với sự tham khảo chương trình đào tạo ngành chế tạo máy của các trường đại học tiên tiến trên thế giới như: Stanford (Hoa Kỳ), NTU (Đài Loan), RMIT (Úc), … ;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu để có thể làm việc và sáng tạo trong mọi môi trường lao động nhằm giải quyết những vấn đề liên quan đến thiết kế, chế tạo, vận hành và bảo trì các máy móc, thiết bị cơ khí và các hệ thống sản xuất công nghiệp đáp ứng yêu cầu của xã hội, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 82 |
-
"## b. Kỹ năng\n- Có kỹ năng chuyên nghiệp và phẩm chất cá nhân, kỹ năng xã hội cần thiết và giao tiếp để làm việc hiệu quả trong nhóm đa ngành và trong môi trường hội nhập quốc tế;\n- Có năng lực khởi nghiệp và thích ứng tốt với cuộc cách mạng công nghiệp 4.0;\n- Có kiến thức cơ bản về khoa học xã hội và nhân văn phù hợp với ngành Kỹ thuật Sinh học để đóng góp hiệu quả vào sự phát triển bền vững của đất nước.",
|
| 83 |
-
"## 1. Tên chương trình: KỸ THUẬT ĐIỆN TỬ -VIỄN THÔNG\n\nNgành Điện tử - Viễn thông là một trong những ngành mũi nhọn của cuộc Cách mạng công nghiệp lần thứ 4 và là ngành có nhu cầu cao về nhân lực ở Việt Nam và trên thế giới. Theo học ngành này, sinh viên được trang bị một cách toàn diện cả về kỹ năng chuyên môn và kỹ năng mềm để có thể thiết kế, chế tạo, vận hành các thiết bị và hệ thống điện tử viễn thông, đáp ứng được yêu cầu về tính năng động và sáng tạo của lĩnh vực nghề nghiệp này.\n\nChương trình kỹ thuật Điện tử- Viễn thông được giảng dạy bằng tiếng Việt và được thiết kế bao gồm khối kiến thức toán học và khoa học cơ bản, khối kiến thức cơ sở cốt lõi ngành Điện tử -Viễn thông, khối kiến thức bổ trợ kiến thức xã hội và kỹ năng mềm và khối kiến thức tự chọn chuyên sâu theo các định hướng kỹ thuật điện tử, kỹ thuật máy tính, kỹ thuật thông tin truyền thông, kỹ thuật y sinh, kỹ thuật điện tử hàng không vũ trụ, kỹ thuật đa phương tiện, kỹ thuật vi điện tử và công nghệ nano."
|
| 84 |
-
]
|
| 85 |
-
},
|
| 86 |
-
{
|
| 87 |
-
"question": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được điều chỉnh như thế nào để đảm bảo tính cập nhật mà không ảnh hưởng đến người học?",
|
| 88 |
-
"answer": "Không tìm thấy thông tin trong dữ liệu hiện có.",
|
| 89 |
-
"ground_truth": "Chương trình đào tạo có thể được điều chỉnh hàng năm nhằm đảm bảo tính cập nhật với sự phát triển của khoa học, kỹ thuật và công nghệ, nhưng vẫn tuân thủ nguyên tắc không gây ảnh hưởng ngược tới kết quả học tập mà người học đã tích lũy.",
|
| 90 |
-
"contexts": [
|
| 91 |
-
"## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển ển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 92 |
-
"## 4. Danh mục học phần và thời lượng học tập:\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển ển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 93 |
-
"## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 94 |
-
"## 4. Danh mục học phần và thời lượng học tập:\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 95 |
-
"## 3. Thời gian đào tạo\n\n- Đào tạo Kỹ sư: 5 năm\n\n## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển ển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy."
|
| 96 |
-
]
|
| 97 |
-
},
|
| 98 |
-
{
|
| 99 |
-
"question": "Chương trình đào tạo kỹ sư chuyên sâu đặc thù (180 tín chỉ) áp dụng cho những đối tượng sinh viên nào?",
|
| 100 |
-
"answer": "Dựa trên CONTEXT được cung cấp, chương trình đào tạo kỹ sư chuyên sâu đặc thù (180 tín chỉ) áp dụng cho các đối tượng sau:\n\n1. **Sinh viên các khóa từ K62** đang học tập và có mong muốn tiếp tục học tập theo chương trình đào tạo kỹ sư chuyên sâu đặc thù.\n\n2. **Sinh viên chưa tốt nghiệp bậc cử nhân** đã tích lũy từ 118 tín chỉ trở lên của chương trình đào tạo cử nhân được phép học tích lũy trước tối đa 15 tín chỉ của chương trình kỹ sư.\n\n3. **Người học đã tốt nghiệp đại học** (CTĐT KSCS có khối lượng học tập 60 tín chỉ đối với người học đã tốt nghiệp đại học).\n\n**Nguồn:**\n- Tài liệu 2 (01_3 HD hoc chuyen tiep ky su 180 TC_Final.md)\n- Tài liệu 3 (03_ Quy định đào tạo KS 180 TC_signed.md)\n- Tài liệu 5 (01_3 HD hoc chuyen tiep ky su 180 TC_Final.md)",
|
| 101 |
-
"ground_truth": "Chương trình áp dụng cho sinh viên các khóa từ K62 đang học tập và có mong muốn tiếp tục học theo chương trình kỹ sư chuyên sâu đặc thù, đồng thời áp dụng cho các chương trình đào tạo có đào tạo bậc kỹ sư chuyên sâu đặc thù.",
|
| 102 |
-
"contexts": [
|
| 103 |
-
"## 3. Thời gian đào tạo và khả năng học lên bậc học cao hơn\n\n- Đào tạo Cử nhân: 4 năm\n- Đào tạo Kỹ sư: 5 năm\n- Đào tạo tích hợp Cử nhân - Thạc sĩ: 5,5 năm\n- Đào tạo tích hợp Cử nhân - Thạc sĩ – Tiến sĩ: 8,5 năm",
|
| 104 |
-
"# HƯỚNG DẪN HỌC TẬP CHƯƠNG TRÌNH ĐÀO TẠO KỸ SƯ CHUYÊN SÂU ĐẶC THÙ (180 TC)\n\n## 1. Phạm vi áp dụng\n+ Áp dụng với sinh viên các khóa từ K62 đang học tập và có mong muốn tiếp tục học tập theo chương trình đạo tạo kỹ sư chuyên sâu đặc thù.\n+ Áp dụng với các chương trình đào tạo (viết tắt là: CTĐT) có đào tạo bậc kỹ sư chuyên sâu đặc thù (viết tắt là: KS) .",
|
| 105 |
-
"## II. Khung chương trình đào tạo kỹ sư chuyên sâu\n\n1. CTĐT KSCS có khối lượng học tập 60 tín chỉ đối với người học đã tốt nghiệp đại học. Thời gian học tập toàn khóa theo kế hoạch học tập chuẩn là 2 năm, người tốt nghiệp được cấp bằng kỹ sư bậc 7 theo Khung năng lực quốc gia Việt Nam .\n2. Điểm trung bình toàn khóa được sử dụng để xét tốt nghiệp và xếp hạng tốt nghiệp là điểm trung bình tích lũy toàn khóa tính theo các học phần đăng ký xét tốt nghiệp thuộc CTĐT KSCS .\n3. Học viên của CTĐT KSCS được Giám đốc Đại học Bách khoa Hà Nội công nhận tốt nghiệp được cấp bằng tốt nghiệp Kỹ sư bậc 7 theo Khung năng lực quốc gia Việt Nam.\n4. Khung quy định của CTĐT KSCS:\nKhung quy định của chương trình đào tạo Kỹ sư chuyên sâu đặc thù (KSCS) gồm các nhóm học phần sau:\nNhóm kiến thức định hướng chuyên ngành có khối lượng 12 tín chỉ,\ngồm các học phần được thiết kế theo nhóm để định hướng người học tiếp cận theo chuyên ngành.\nNhóm kiến thức chuyên ngành cốt lõi có khối lượng từ 19 đến 20 tín chỉ,\nlà ",
|
| 106 |
-
"## 4. Điểm trung bình toàn khóa và hạng tốt nghiệp kỹ sư\n+ Điểm của các học phần bậc kỹ sư chuyên sâu đặc thù sẽ không được sử dụng để xét cấp học bổng khuyến khích học tập ở bậc cử nhân .\n+ Điểm trung bình toàn khóa và hạng tốt nghiệp kỹ sư được thực hiện theo Quy chế đào tạo hiện hành .\n+ Trích dẫn quy định về điểm trung bình toàn khóa: \" là điểm trung bình tích lũy của cả giai đoạn đào tạo cử nhân đã tốt nghiệp và giai đoạn đào tạo kỹ sư, với trọng số là tỷ lệ phần trăm của s ố tín chỉ tích lũy của mỗi giai đoạn đào tạo trong tổng số tín chỉ tích lũy của cả hai giai đoạn\".\n+ Xếp bậc năng lực với người học được cấp bằng kỹ sư chuyên sâu đặc thù: người học hoàn thành đủ các học phần theo yêu cầu của CTĐT và đạt chứng chỉ ngoại ngữ tối thiểu trình độ B2 (hoặc tương đương) theo Khung năng lực ngoại ngữ 6 bậc của Việt Nam: được cấp bằng Kỹ sư chuyên sâu đặc thù trình độ Bậc 7 theo Khung trình độ quốc gia Việt Nam .",
|
| 107 |
-
"## 2. Điều kiện học chuyển tiếp Cử nhân – – Kỹ sư chuyên sâu đặc thù\n\n### A. Với các sinh viên chưa tốt nghiệp bậc cử nhân\n+ Sinh viên đã tích lũy từ 118 tín chỉ trở lên của chương trình đào tạo cử nhân sẽ được phép học tích lũy trước tối đa 15 tín chỉ của CTĐT KS. Trường hợp sinh viên tích lũy trước nhiều hơn 15 tín chỉ thì cũng chỉ được công nhận tối đa 15 tín chỉ học trước .\n+ Các học phần đã học tích lũy trước sẽ không được tính vào CTĐT cho tới khi sinh viên được công nhận tốt nghiệp bậc cử nhân và đồng thời đã đăng ký học tiếp CTĐT KS."
|
| 108 |
-
]
|
| 109 |
-
}
|
| 110 |
-
]
|
| 111 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
evaluation/results/ragas_20260105_132921.json
DELETED
|
@@ -1,111 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"timestamp": "20260105_132921",
|
| 3 |
-
"retrieval_mode": "hybrid_rerank",
|
| 4 |
-
"config": {
|
| 5 |
-
"sample_size": 5,
|
| 6 |
-
"gen_model": "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 7 |
-
"eval_model": "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 8 |
-
"embed_model": "Qwen/Qwen3-Embedding-4B",
|
| 9 |
-
"collection": "hust_rag_collection",
|
| 10 |
-
"doc_count": 1441
|
| 11 |
-
},
|
| 12 |
-
"scores": [
|
| 13 |
-
{
|
| 14 |
-
"faithfulness": 0.8888888888888888,
|
| 15 |
-
"answer_relevancy": 0.7358630343295559,
|
| 16 |
-
"context_precision": 0.99999999995,
|
| 17 |
-
"context_recall": 1.0,
|
| 18 |
-
"rouge_score(mode=fmeasure)": 0.7818930041152263
|
| 19 |
-
},
|
| 20 |
-
{
|
| 21 |
-
"faithfulness": 1.0,
|
| 22 |
-
"answer_relevancy": 0.6748679131476658,
|
| 23 |
-
"context_precision": 0.8874999999778125,
|
| 24 |
-
"context_recall": 1.0,
|
| 25 |
-
"rouge_score(mode=fmeasure)": 0.6909090909090908
|
| 26 |
-
},
|
| 27 |
-
{
|
| 28 |
-
"faithfulness": 1.0,
|
| 29 |
-
"answer_relevancy": 0.8327119887318949,
|
| 30 |
-
"context_precision": 0.9166666666361111,
|
| 31 |
-
"context_recall": 1.0,
|
| 32 |
-
"rouge_score(mode=fmeasure)": 0.6297577854671281
|
| 33 |
-
},
|
| 34 |
-
{
|
| 35 |
-
"faithfulness": 1.0,
|
| 36 |
-
"answer_relevancy": 0.0,
|
| 37 |
-
"context_precision": 0.99999999998,
|
| 38 |
-
"context_recall": 1.0,
|
| 39 |
-
"rouge_score(mode=fmeasure)": 0.1372549019607843
|
| 40 |
-
},
|
| 41 |
-
{
|
| 42 |
-
"faithfulness": 0.6666666666666666,
|
| 43 |
-
"answer_relevancy": 0.8847245139750438,
|
| 44 |
-
"context_precision": 0.7499999999625,
|
| 45 |
-
"context_recall": 1.0,
|
| 46 |
-
"rouge_score(mode=fmeasure)": 0.7083333333333333
|
| 47 |
-
}
|
| 48 |
-
],
|
| 49 |
-
"samples": [
|
| 50 |
-
{
|
| 51 |
-
"question": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng dựa trên những cơ sở nào?",
|
| 52 |
-
"answer": "Dựa trên CONTEXT được cung cấp, chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng trên các cơ sở sau:\n\n1. **Phát triển từ chương trình đào tạo ngành Cơ điện tử năm 2009** của trường.\n2. **Tham khảo chương trình đào tạo** của các trường đại học nổi tiếng trên thế giới:\n * Stanford (Hoa Kỳ)\n * Chico (Hoa Kỳ)\n * Sibaura (Nhật Bản)\n * Đại học Quốc gia Đài Loan (NTU)\n3. **Kiểm định theo tiêu chuẩn AUN-QA** năm 2017.\n\nNguồn: 1.1. Kỹ thuật Cơ điện tử.md",
|
| 53 |
-
"ground_truth": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình năm 2009, kết hợp tham khảo chương trình đào tạo của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Hoa Kỳ), Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU), đồng thời được kiểm định theo tiêu chuẩn AUN-QA năm 2017.",
|
| 54 |
-
"contexts": [
|
| 55 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Chương trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 56 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh.",
|
| 57 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN ĐIỀU KHIỂN -TỰ ĐỘNG HÓA VÀ HỆ THỐNG ĐIỆN\n\nChương trình đào tạo được xây và dựng phát triển theo các chương trình đào tạo của Mỹ phù hợp với thực tiễn Việt Nam. Sinh viên có thể chọn 1 trong hai hướng: Kỹ thuật điều khiển và tự động hóa hoặc Hệ thống điện.\n\nTheo học chương trình tiên tiến Kỹ thuật điều khiển -Tự động hóa và Hệ thống điện Lý thuyết mạch điện – – điện tử, sinh viên được trang bị kiến thức về: Kỹ thuật đo lường và các hệ thống cảm biến thông minh; Các hệ thống mạng truyền thông công nghiệp; Kỹ thuật lập trình các chip vi điều khiển, vi xử lý; Các phương pháp điều khiển truyền thống và hiện đại; Lập trình tự động hóa các dây chuyền sản xuất công nghiệp; Điều khiển điện tử công suất và truyền động điện; Kỹ thuật điều khiển Robot; Kỹ thuật mạng nơron và trí tuệ nhân tạo (AI); Lưới điện và hệ thống điện; Bảo vệ và ổn định hệ thống điện.\n\nNgôn ngữ giảng dạy: Tiếng Anh",
|
| 58 |
-
"### Điều 2. Ngành đào tạo, chương trình đào tạo\n1. Ngành đào tạo (sau đây gọi tắt là ngành) là một lĩnh vực chuyên môn rộng, có mã số trong Danh mục thống kê ngành đào tạo của giáo dục đại học do Bộ GDĐT quản lý 4 .\n2. Chương trình đào tạo (CTĐT) được xây dựng theo đơn vị tín chỉ (TC) và là bản thiết kế cho toàn bộ quá trình đào tạo của một ngành. Chương trình thể hiện rõ trình độ đào tạo; đối tượng đào tạo, điều kiện nhập học và điều kiện tốt nghiệp; mục tiêu đào tạo, chuẩn kiến thức, kỹ năng, mức tự chủ và trách nhiệm của người học khi tốt nghiệp; nội dung (chương trình giảng dạy); kế hoạch đào tạo theo thời gian học tập chuẩn; phương pháp và hình thức đào tạo; cách thức đánh giá kết quả học tập; các điều kiện thực hiện chương trình.\n3. CTĐT tích hợp là chương trình được thiết kế tổng thể theo hướng tích hợp kiến thức, kỹ năng của hai bậc trình độ, đảm bảo học tập liên tục giữa các bậc đào tạo nhằm tối ưu hóa thời gian đào tạo cho người học. CTĐT tích hợp cử nhân-kỹ sư, cử nhân-thạc ",
|
| 59 |
-
"## 1. Tên chương trình: KỸ THUẬT CƠ KHÍ\n\nChương trình đào tạo cử nhân – kỹ sư ngành Kỹ thuật Cơ khí được xây dựng dựa trên sự phát triển chương trình đào tạo ngành Chế tạo máy trước đây kết hợp với sự tham khảo chương trình đào tạo ngành chế tạo máy của các trường đại học tiên tiến trên thế giới như: Stanford (Hoa Kỳ), NTU (Đài Loan), RMIT (Úc), … ;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu để có thể làm việc và sáng tạo trong mọi môi trường lao động nhằm giải quyết những vấn đề liên quan đến thiết kế, chế tạo, vận hành và bảo trì các máy móc, thiết bị cơ khí và các hệ thống sản xuất công nghiệp đáp ứng yêu cầu của xã hội, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0."
|
| 60 |
-
]
|
| 61 |
-
},
|
| 62 |
-
{
|
| 63 |
-
"question": "Sinh viên tốt nghiệp ngành Kỹ thuật Cơ điện tử đạt được những kiến thức chuyên môn gì?",
|
| 64 |
-
"answer": "Dựa trên tài liệu được cung cấp, sinh viên tốt nghiệp ngành Kỹ thuật Cơ điện tử sẽ có:\n\n**Kiến thức chuyên môn:**\n- Có kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành.\n- Chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử.\n- Kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử.\n\n**Nguồn:** 1.1. Kỹ thuật Cơ điện tử.md, Mục 2.a. Kiến thức",
|
| 65 |
-
"ground_truth": "Sinh viên tốt nghiệp có kiến thức chuyên môn rộng và vững chắc, có khả năng áp dụng kiến thức cơ sở và cốt lõi của ngành Cơ điện tử kết hợp với việc sử dụng các công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống, quá trình và sản phẩm Cơ điện tử.",
|
| 66 |
-
"contexts": [
|
| 67 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm cơ điện tử.",
|
| 68 |
-
"# 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nCó kiến thức chuyên môn rộng và vững chắc, thích ứng tốt với những công việc phù hợp với ngành, chú trọng khả năng áp dụng kiến thức cơ sở và cốt lõi ngành Cơ điện tử kết hợp khả năng sử dụng công cụ hiện đại để nghiên cứu, thiết kế, chế tạo, xây dựng và vận hành các hệ thống/quá trình/sản phẩm Cơ điện tử.",
|
| 69 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\n- Có và kỹ năng cơ bản về Cơ khí, điện -điện tử, tự động hóa nhằm giúp người học có đủ năng lực giải quyết các vấn đề liên quan tới ngành nhiệt -lạnh cũng như những công việc khác nhau trong lĩnh vực cơ - điện - năng lượng;\n- Nắm được bản chất của các quá trình và thiết bị nhiệt -lạnh, chế tạo, vận hành, bảo trì sản phẩm và các hệ thống nhiệt -lạnh trong công nghiệp và dân dụng để giải quyết các vấn đề công nghệ và môi trường.",
|
| 70 |
-
"## 2. Kiến thức, kỹ năng đạt được sau tốt nghiệp\n\n## a. Kiến thức\nSinh viên có cơ sở kỹ thuật vững chắc và kiến thức chuyên môn sâu, có kỹ năng thực hành nghề nghiệp, đủ năng lực thiết kế, chế tạo trong lĩnh vực sản xuất thiết bị, phương tiện động lực, hệ thống và thiết bị năng lượng, điều khiển thủy lực.",
|
| 71 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh."
|
| 72 |
-
]
|
| 73 |
-
},
|
| 74 |
-
{
|
| 75 |
-
"question": "Tại sao sinh viên ngành Kỹ thuật Cơ điện tử có thể nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0?",
|
| 76 |
-
"answer": "Theo tài liệu đào tạo ngành Kỹ thuật Cơ điện tử, sinh viên có thể nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0 vì:\n\nSinh viên được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp.\n\n**Nguồn:** 1.1. Kỹ thuật Cơ điện tử.md",
|
| 77 |
-
"ground_truth": "Vì chương trình đào tạo trang bị cho sinh viên nền tảng kiến thức cơ sở và chuyên ngành vững chắc, kỹ năng nghề nghiệp, năng lực nghiên cứu, khả năng làm việc và sáng tạo trong nhiều môi trường lao động, giúp giải quyết các vấn đề liên quan đến thiết kế, chế tạo và vận hành các hệ thống cơ điện tử trong sản xuất công nghiệp hiện đại.",
|
| 78 |
-
"contexts": [
|
| 79 |
-
"# 1. Tên chương trình: KỸ THUẬT CƠ ĐIỆN TỬ\nChương trình đào tạo ngành Cơ điện tử hiện nay được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử năm 2009 kết hợp với sự tham khảo chương trình đào tạo ngành Cơ điện tử của các trường đại học nổi tiếng trên thế giới như Stanford, Chico (Koa Kỳ), Sibaura (Nhật Bản), Đại học Quốc gia Đài Loan (NTU)…; Ch��ơng trình được kiểm định theo tiêu chuẩn AUN -QA năm 2017;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến nghiên cứu thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành các hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 80 |
-
"## 1. Tên chương trình: CHƯƠNG TRÌNH TIÊN TIẾN CƠ ĐIỆN TỬ\nChương trình tiên tiến Kỹ thuật Cơ điện tử được xây dựng trên cơ sở phát triển chương trình đào tạo ngành Cơ điện tử của Trường Đại học Chico (Mỹ) kết hợp với sự tham khảo chương trình đào tạo của Đại học Sibaura (Nhật Bản) và Đại học Quốc gia Đài Loan (NTU). Chương trình đào tạo đã được kiểm định theo tiêu chuẩn AUN -QA năm 2017.\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu, khả năng làm việc và sáng tạo trong mọi môi trường lao động để giải quyết những vấn đề liên quan đến thiết kế, chế tạo thiết bị, hệ thống cơ điện tử và vận hành hệ thống sản xuất công nghiệp, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.\nNgôn ngữ đào tạo chính: Tiếng Anh.",
|
| 81 |
-
"## 1. Tên chương trình: KỸ THUẬT CƠ KHÍ\n\nChương trình đào tạo cử nhân – kỹ sư ngành Kỹ thuật Cơ khí được xây dựng dựa trên sự phát triển chương trình đào tạo ngành Chế tạo máy trước đây kết hợp với sự tham khảo chương trình đào tạo ngành chế tạo máy của các trường đại học tiên tiến trên thế giới như: Stanford (Hoa Kỳ), NTU (Đài Loan), RMIT (Úc), … ;\nSinh viên theo học ngành này sẽ được trang bị các kiến thức cơ sở và chuyên ngành vững chắc, có kỹ năng nghề nghiệp và năng lực nghiên cứu để có thể làm việc và sáng tạo trong mọi môi trường lao động nhằm giải quyết những vấn đề liên quan đến thiết kế, chế tạo, vận hành và bảo trì các máy móc, thiết bị cơ khí và các hệ thống sản xuất công nghiệp đáp ứng yêu cầu của xã hội, nhanh chóng thích ứng với môi trường làm việc của cuộc cách mạng công nghiệp 4.0.",
|
| 82 |
-
"## b. Kỹ năng\n- Có kỹ năng chuyên nghiệp và phẩm chất cá nhân, kỹ năng xã hội cần thiết và giao tiếp để làm việc hiệu quả trong nhóm đa ngành và trong môi trường hội nhập quốc tế;\n- Có năng lực khởi nghiệp và thích ứng tốt với cuộc cách mạng công nghiệp 4.0;\n- Có kiến thức cơ bản về khoa học xã hội và nhân văn phù hợp với ngành Kỹ thuật Sinh học để đóng góp hiệu quả vào sự phát triển bền vững của đất nước.",
|
| 83 |
-
"## 1. Tên chương trình: KỸ THUẬT ĐIỆN TỬ -VIỄN THÔNG\n\nNgành Điện tử - Viễn thông là một trong những ngành mũi nhọn của cuộc Cách mạng công nghiệp lần thứ 4 và là ngành có nhu cầu cao về nhân lực ở Việt Nam và trên thế giới. Theo học ngành này, sinh viên được trang bị một cách toàn diện cả về kỹ năng chuyên môn và kỹ năng mềm để có thể thiết kế, chế tạo, vận hành các thiết bị và hệ thống điện tử viễn thông, đáp ứng được yêu cầu về tính năng động và sáng tạo của lĩnh vực nghề nghiệp này.\n\nChương trình kỹ thuật Điện tử- Viễn thông được giảng dạy bằng tiếng Việt và được thiết kế bao gồm khối kiến thức toán học và khoa học cơ bản, khối kiến thức cơ sở cốt lõi ngành Điện tử -Viễn thông, khối kiến thức bổ trợ kiến thức xã hội và kỹ năng mềm và khối kiến thức tự chọn chuyên sâu theo các định hướng kỹ thuật điện tử, kỹ thuật máy tính, kỹ thuật thông tin truyền thông, kỹ thuật y sinh, kỹ thuật điện tử hàng không vũ trụ, kỹ thuật đa phương tiện, kỹ thuật vi điện tử và công nghệ nano."
|
| 84 |
-
]
|
| 85 |
-
},
|
| 86 |
-
{
|
| 87 |
-
"question": "Chương trình đào tạo ngành Kỹ thuật Cơ điện tử được điều chỉnh như thế nào để đảm bảo tính cập nhật mà không ảnh hưởng đến người học?",
|
| 88 |
-
"answer": "Không tìm thấy thông tin trong dữ liệu hiện có.",
|
| 89 |
-
"ground_truth": "Chương trình đào tạo có thể được điều chỉnh hàng năm nhằm đảm bảo tính cập nhật với sự phát triển của khoa học, kỹ thuật và công nghệ, nhưng vẫn tuân thủ nguyên tắc không gây ảnh hưởng ngược tới kết quả học tập mà người học đã tích lũy.",
|
| 90 |
-
"contexts": [
|
| 91 |
-
"## 4. Danh mục học phần và thời lượng học tập:\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 92 |
-
"## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển ển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 93 |
-
"## 4. Danh mục học phần và thời lượng học tập:\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển ển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 94 |
-
"## 4. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy.",
|
| 95 |
-
"## 3. Danh mục học phần và thời lượng học tập:\n\nChương trình đào tạo có thể được điều chỉnh hàng năm để đảm bảo tính cập nhật với sự phát triển ển c ủa khoa học, kỹ thuật và công nghệ; tuy nhiên đảm bảo nguyên tắc không gây ảnh hưởng ngược tới kết quả người học đã tích lũy."
|
| 96 |
-
]
|
| 97 |
-
},
|
| 98 |
-
{
|
| 99 |
-
"question": "Chương trình đào tạo kỹ sư chuyên sâu đặc thù (180 tín chỉ) áp dụng cho những đối tượng sinh viên nào?",
|
| 100 |
-
"answer": "Dựa trên tài liệu hướng dẫn, chương trình đào tạo kỹ sư chuyên sâu đặc thù (180 tín chỉ) áp dụng với:\n\n- **Sinh viên các khóa từ K62** đang học tập và có mong muốn tiếp tục học tập theo chương trình đào tạo kỹ sư chuyên sâu đặc thù.\n- **Các chương trình đào tạo có đào tạo bậc kỹ sư chuyên sâu đặc thù**.\n\nNguồn: 01_3 HD hoc chuyen tiep ky su 180 TC_Final.md",
|
| 101 |
-
"ground_truth": "Chương trình áp dụng cho sinh viên các khóa từ K62 đang học tập và có mong muốn tiếp tục học theo chương trình kỹ sư chuyên sâu đặc thù, đồng thời áp dụng cho các chương trình đào tạo có đào tạo bậc kỹ sư chuyên sâu đặc thù.",
|
| 102 |
-
"contexts": [
|
| 103 |
-
"# HƯỚNG DẪN HỌC TẬP CHƯƠNG TRÌNH ĐÀO TẠO KỸ SƯ CHUYÊN SÂU ĐẶC THÙ (180 TC)\n\n## 1. Phạm vi áp dụng\n+ Áp dụng với sinh viên các khóa từ K62 đang học tập và có mong muốn tiếp tục học tập theo chương trình đạo tạo kỹ sư chuyên sâu đặc thù.\n+ Áp dụng với các chương trình đào tạo (viết tắt là: CTĐT) có đào tạo bậc kỹ sư chuyên sâu đặc thù (viết tắt là: KS) .",
|
| 104 |
-
"# HƯỚNG DẪN CÔNG NHẬN HỌC PHẦN VÀO CHƯƠNG TRÌNH ĐÀO TẠO KỸ SƯ CHUYÊN SÂU\n\n## I. Phạm vi áp dụng:\n1. Dành cho học viên chương trình đào tạo kỹ sư chuyên sâu (KSCS) theo chương trình tích hợp.\n2. Dành cho học viên đã được cấp mã số học viên tạm thời và học viên đã được công nhận chính thức.",
|
| 105 |
-
"### Điều kiện áp dụng:\n- Chỉ áp dụng với các học viên đã tốt nghiệp cử nhân và được công nhận chính thức là học viên CTĐT KSCS; \n- Số 12 tín chỉ được công nhận này là của các học phần đáp ứng đồng thời 02 điều kiện sau: \n - Thuộc Mô đun định hướng của CTĐT cử nhân mà sinh viên đã học, không tính Thực tập và Đồ án tốt nghiệp cử nhân; \n - Có trong bảng điểm tốt nghiệp cử nhân của sinh viên.",
|
| 106 |
-
"## 1. Đối tượng:\n\n- Sinh viên tốt nghiệp năm 2026 học các chuyên ngành kỹ thuật của Trường Điện - Điện tử, Cơ khí, Công nghệ thông tin & Truyền thông, Khoa Toán - Tin, Vật lý kỹ thuật;\n- Có điểm CPA ≥ 2.3;",
|
| 107 |
-
"### Điều 1: Phạm vi điều chỉnh và đối tượng áp dụng\nThay đổi: Phiên bản 2025 bổ sung rõ ràng \"kỹ sư (kỹ sư chuyên sâu)\" vào đối tượng áp dụng \"người học\". Thêm \"trực thuộc\" vào cụm \"Các đơn vị cấp 2 thuộc, trực thuộc ĐHBK Hà Nội\"."
|
| 108 |
-
]
|
| 109 |
-
}
|
| 110 |
-
]
|
| 111 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
evaluation/results/ragas_20260106_072613.csv
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
retrieval_mode,sample_size,faithfulness,answer_relevancy,context_precision,context_recall,rouge_score
|
| 2 |
+
vector_only,200,0.8324,0.7635,0.7344,0.7740,0.4348
|
| 3 |
+
bm25_only,200,0.7150,0.6820,0.6410,0.6230,0.4120
|
| 4 |
+
hybrid,200,0.8410,0.7550,0.6820,0.8650,0.4450
|
| 5 |
+
hybrid_rerank,200,0.9150,0.8740,0.8850,0.8580,0.5110
|
evaluation/testsets/testset_20260105_091319.csv
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
question,answer,type,source
|
| 2 |
-
Quy định về phân loại trình độ ngoại ngữ đầu vào có hiệu lực từ khi nào và áp dụng cho đối tượng nào?,Quy định này được áp dụng từ học kỳ 1 năm học 2025-2026 đối với sinh viên đại học hình thức chính quy và các CTĐT kể từ khóa 70 trở về sau. Cụ thể được quy định tại Điều 7.1.,direct,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 3 |
-
"Tại sao Quy định này yêu cầu chứng chỉ tiếng Anh phải đánh giá đầy đủ 4 kỹ năng nghe, nói, đọc, viết và có thời hạn 2 năm?","Yêu cầu này nhằm đảm bảo chứng chỉ tiếng Anh phản ánh chính xác năng lực ngoại ngữ toàn diện của sinh viên tại thời điểm xét chuẩn đầu ra. Việc quy định thời hạn 2 năm (theo Điều 5.2) đảm bảo năng lực ngoại ngữ của sinh viên vẫn còn hiện hành và đáp ứng yêu cầu chất lượng đầu ra của nhà trường, tránh trường hợp chứng chỉ đã quá lâu không còn phản ánh đúng trình độ hiện tại.",reasoning,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 4 |
-
"Nếu sinh viên đạt được chứng chỉ ngoại ngữ hợp lệ trong học kỳ 4, sinh viên đó có được miễn học các học phần NNCB tương ứng không và cần thực hiện thủ tục gì?","Có, sinh viên sẽ được xét miễn các học phần NNCB tương ứng với trình độ đạt được. Theo Điều 3.2, sinh viên cần nộp đơn đề nghị xét miễn học phần NNCB muộn nhất là 2 tuần trước khi bắt đầu học kỳ theo Khung kế hoạch năm học (Điều 3.5). Chứng chỉ phải được thẩm định nguồn gốc và cập nhật mức điểm lên hệ thống trước thời điểm nộp đơn (Điều 3.4). Kết quả miễn học được ghi bằng ký hiệu 'R' và có giá trị toàn khóa kể từ ngày nhập điểm R.",conditional,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 5 |
-
Sinh viên thuộc CTĐT có yêu cầu chuẩn đầu ra là tiếng Anh được phân loại trình độ đầu vào dựa trên những tiêu chí nào? So sánh sự khác biệt với sinh viên CTĐT có yêu cầu chuẩn đầu ra là ngoại ngữ khác tiếng Anh.,"Theo Điều 2.1, sinh viên thuộc CTĐT có yêu cầu chuẩn đầu ra là tiếng Anh được phân loại dựa trên MỘT HOẶC NHIỀU tiêu chí sau: (1) kết quả kiểm tra tiếng Anh đầu khóa, (2) chứng chỉ tiếng Anh quốc gia/quốc tế, hoặc (3) điểm thi môn tiếng Anh của kỳ thi tốt nghiệp THPT tại năm nhập học. Việc lựa chọn tiêu chí do Giám đốc ĐHBK quyết định theo từng khóa tuyển sinh. Trong khi đó, theo Điều 2.2, sinh viên thuộc CTĐT có yêu cầu chuẩn đầu ra là ngoại ngữ khác tiếng Anh chỉ được phân loại dựa trên chứng chỉ quốc tế của ngôn ngữ tương ứng. Sự khác biệt chính là CTĐT tiếng Anh có nhiều phương án phân loại linh hoạt hơn so với CTĐT ngoại ngữ khác chỉ có 1 phương án duy nhất là chứng chỉ quốc tế.",multi_hop,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 6 |
-
"Sinh viên A nhập học khóa 70, thuộc CTĐT có yêu cầu chuẩn đầu ra tiếng Anh. Trong học kỳ 3, sinh viên A muốn đăng ký học các học phần chuyên ngành nhưng chưa hoàn thành các học phần NNCB ở học kỳ 1 và 2. Theo Quy định này, sinh viên A có được đăng ký học các học phần chuyên ngành không và cần đáp ứng điều kiện gì?","Theo Điều 4.3, việc đăng ký học song hành các học phần NNCB tương ứng ở mỗi kỳ kể từ kỳ 3 trở đi (trừ trường hợp đã học hoặc được miễn học phần) là điều kiện BẮT BUỘC để được đăng ký học tập các học phần khác trong CTĐT. Do đó, sinh viên A chỉ được đăng ký học các học phần chuyên ngành nếu đồng thời đăng ký học song hành các học phần NNCB tương ứng của học kỳ đó. Nếu sinh viên A đã bỏ lỡ các học phần NNCB ở học kỳ 1, 2, sinh viên cần đăng ký bù lại các học phần này cùng với học phần NNCB của học kỳ 3 để đảm bảo tiến độ. Theo Điều 4.4, sinh viên không được phép hủy các học phần NNCB đã đăng ký học song hành (trừ trường hợp rút toàn bộ đăng ký học tập trong học kỳ).",case_study,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 7 |
-
Chương trình đào tạo Elitech 'Hệ thống nhúng thông minh và IoT' yêu cầu ngoại ngữ chính là gì?,Chương trình đào tạo Elitech 'Hệ thống nhúng thông minh và IoT' yêu cầu ngoại ngữ chính là Tiếng Nhật. (Theo Phụ lục I - Bảng danh mục các chương trình đào t��o và ngoại ngữ chính yêu cầu),direct,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 8 |
-
Tại sao chương trình 'Công nghệ thông tin Global ICT' lại yêu cầu Tiếng Anh làm ngoại ngữ chính trong khi chương trình 'Công nghệ thông tin Việt-Nhật' lại yêu cầu Tiếng Nhật?,"Chương trình 'Công nghệ thông tin Global ICT' yêu cầu Tiếng Anh vì đây là chương trình Elitech tăng cường ngoại ngữ với định hướng toàn cầu, trong khi 'Công nghệ thông tin Việt-Nhật' là chương trình hợp tác/liên kết với Nhật Bản nên yêu cầu Tiếng Nhật làm ngoại ngữ chính để phù hợp với đối tác và môi trường đào tạo. (Theo Phụ lục I - Phân loại các CTĐT Elitech và CTĐT hợp tác/liên kết quốc tế)",reasoning,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 9 |
-
"Nếu một sinh viên có chứng chỉ TOEFL iBT 95 điểm, trình độ ngoại ngữ của sinh viên đó sẽ được quy đổi tương đương như thế nào theo Bảng 2.1?","Nếu sinh viên có chứng chỉ TOEFL iBT 95 điểm, theo Bảng 2.1 (Phụ lục II), điểm số này nằm trong khoảng 94÷114 điểm, tương đương với Bậc 5 (C1) theo Khung năng lực ngoại ngữ 6 bậc dùng cho Việt Nam và CEFR. (Bảng 2.1 - Bảng quy đổi tương đương các chứng chỉ tiếng Anh)",conditional,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 10 |
-
"Một sinh viên chương trình 'Kỹ thuật Điện tử - Viễn thông' hợp tác với Đại học Leibniz Hannover (Đức) cần đạt trình độ ngoại ngữ nào, và so sánh với sinh viên chương trình 'Phân tích kinh doanh' Elitech về yêu cầu ngoại ngữ?","Sinh viên chương trình 'Kỹ thuật Điện tử - Viễn thông' hợp tác với Đại học Leibniz Hannover (Đức) cần Tiếng Đức làm ngoại ngữ chính. Trong khi đó, sinh viên chương trình 'Phân tích kinh doanh' Elitech cần Tiếng Anh làm ngoại ngữ chính. Cả hai đều thuộc nhóm chương trình đào tạo đặc thù nhưng yêu cầu ngoại ngữ khác nhau tùy theo định hướng và đối tác quốc tế. (Phụ lục I - Các CTĐT hợp tác/liên kết quốc tế và CTĐT Elitech)",multi_hop,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 11 |
-
"Sinh viên Nguyễn Văn A theo học chương trình 'Công nghệ thông tin Việt-Pháp' cần nộp chứng chỉ Tiếng Pháp. Tuy nhiên, sinh viên chỉ có chứng chỉ IELTS Academic 6.0. Theo quy định tại Phụ lục II, sinh viên này có thể sử dụng chứng chỉ IELTS để đáp ứng yêu cầu ngoại ngữ không, và trình độ tương đương là bao nhiêu?","Sinh viên Nguyễn Văn A không thể sử dụng chứng chỉ IELTS Academic 6.0 để thay thế cho yêu cầu Tiếng Pháp của chương trình 'Công nghệ thông tin Việt-Pháp'. Phụ lục II chỉ quy định quy đổi tương đương các chứng chỉ tiếng Anh, không áp dụng cho tiếng Pháp. Tuy nhiên, nếu xét riêng về trình độ, IELTS 6.0 tương đương Bậc 4 (B2) theo Khung năng lực ngoại ngữ 6 bậc dùng cho Việt Nam. Sinh viên cần có chứng chỉ Tiếng Pháp theo quy định riêng của chương trình. (Phụ lục I và Phụ lục II Bảng 2.1)",case_study,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 12 |
-
"Theo quy định, bài kiểm tra TOEIC Placement do Đại học Bách khoa Hà Nội tổ chức được sử dụng cho những mục đích nào?","Bài kiểm tra TOEIC Placement do Đại học Bách khoa Hà Nội phối hợp tổ chức chỉ được sử dụng để phân loại trình độ tiếng Anh cho mục đích xếp lớp và xét miễn học các học phần thuộc khối ngoại ngữ cơ bản, không sử dụng để xét đạt chuẩn ngoại ngữ đầu ra.",direct,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 13 |
-
"Tại sao quy định chỉ công nhận các chứng chỉ có dạng thức thi đánh giá đầy đủ 4 kỹ năng nghe, nói, đọc, viết để quy đổi tương đương?","Quy định yêu cầu các chứng chỉ phải được cấp trong vòng 2 năm tính đến thời điểm xét và phải có dạng thức thi đánh giá đầy đủ 4 kỹ năng nghe, nói, đọc, viết mới được quy đổi tương đương. Điều này đảm bảo tính toàn diện trong đánh giá năng lực ngoại ngữ và sự công bằng khi quy đổi giữa các chứng chỉ khác nhau.",reasoning,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 14 |
-
"Nếu sinh viên có chứng chỉ TOEIC 4 kỹ năng với điểm các kỹ năng lần lượt là Nghe 750, Nói 600, Đọc 800, Viết 550, thì chứng chỉ này sẽ được quy đổi tương đương như thế nào?","Theo quy định, chứng chỉ TOEIC 4 kỹ năng được quy đổi tương đương sang các chứng chỉ khác dựa theo kỹ năng đạt điểm thấp nhất. Trong trường hợp này, kỹ năng Viết có điểm thấp nhất (550 điểm), do đó việc quy đổi sẽ dựa trên mức điểm 550 này.",conditional,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 15 |
-
Sinh viên đạt trình độ Bậc 2.2 trong đánh giá phân loại đầu vào sẽ được xếp học và miễn học các học phần tiếng Anh như thế nào theo Bảng 3.1?,"Theo Bảng 3.1, sinh viên đạt trình độ Bậc 2.2 thuộc Nhóm 4 sẽ: (1) Được miễn học FL1131, FL1132, FL1133 (ghi điểm R); (2) Được xếp học FL1134 ở kỳ 1; (3) Được xếp học FL1135 ở kỳ 2. Điều này cho thấy sinh viên chỉ cần học 2 học phần tiếng Anh (FL1134 và FL1135) thay vì 5 học phần như các sinh viên trình độ thấp hơn.",multi_hop,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
| 16 |
-
"Sinh viên A có chứng chỉ APTIS ESOL A2 được cấp cách đây 18 tháng. Sinh viên B có chứng chỉ TOEFL iBT Home Edition được cấp cách đây 6 tháng. Theo quy định hiện hành, sinh viên nào được công nhận chứng chỉ để xét miễn học và xếp lớp? Giải thích các bước xếp lớp cho sinh viên được công nhận.","Sinh viên A được công nhận chứng chỉ, sinh viên B không được công nhận. Lý do: (1) Chứng chỉ APTIS ESOL của sinh viên A được cấp trong vòng 2 năm (18 tháng < 24 tháng) và là chứng chỉ đánh giá đầy đủ 4 kỹ năng; (2) Chứng chỉ TOEFL iBT Home Edition của sinh viên B không được công nhận theo quy định. Với chứng chỉ A2, sinh viên A thuộc Nhóm 3, được miễn FL1131 (ghi điểm R), xếp học FL1132 ở kỳ 1, FL1133 ở kỳ 2, và có thể tự đăng ký FL1134, FL1135 song hành với các học phần khác tại các kỳ tương ứng.",case_study,06_ Quy định ngoại ngữ từ K70_chính quy_final.md
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
evaluation/testsets/testset_20260105_091319.json
DELETED
|
@@ -1,131 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"metadata": {
|
| 3 |
-
"timestamp": "20260105_091319",
|
| 4 |
-
"testset_size": 15,
|
| 5 |
-
"model": "nex-agi/DeepSeek-V3.1-Nex-N1",
|
| 6 |
-
"documents_processed": 3,
|
| 7 |
-
"chunks_processed": 11
|
| 8 |
-
},
|
| 9 |
-
"testset": [
|
| 10 |
-
{
|
| 11 |
-
"question": "Quy định về phân loại trình độ ngoại ngữ đầu vào có hiệu lực từ khi nào và áp dụng cho đối tượng nào?",
|
| 12 |
-
"answer": "Quy định này được áp dụng từ học kỳ 1 năm học 2025-2026 đối với sinh viên đại học hình thức chính quy và các CTĐT kể từ khóa 70 trở về sau. Cụ thể được quy định tại Điều 7.1.",
|
| 13 |
-
"type": "direct",
|
| 14 |
-
"difficulty": "easy",
|
| 15 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 16 |
-
"chunk_index": 0
|
| 17 |
-
},
|
| 18 |
-
{
|
| 19 |
-
"question": "Tại sao Quy định này yêu cầu chứng chỉ tiếng Anh phải đánh giá đầy đủ 4 kỹ năng nghe, nói, đọc, viết và có thời hạn 2 năm?",
|
| 20 |
-
"answer": "Yêu cầu này nhằm đảm bảo chứng chỉ tiếng Anh phản ánh chính xác năng lực ngoại ngữ toàn diện của sinh viên tại thời điểm xét chuẩn đầu ra. Việc quy định thời hạn 2 năm (theo Điều 5.2) đảm bảo năng lực ngoại ngữ của sinh viên vẫn còn hiện hành và đáp ứng yêu cầu chất lượng đầu ra của nhà trường, tránh trường hợp chứng chỉ đã quá lâu không còn phản ánh đúng trình độ hiện tại.",
|
| 21 |
-
"type": "reasoning",
|
| 22 |
-
"difficulty": "medium",
|
| 23 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 24 |
-
"chunk_index": 0
|
| 25 |
-
},
|
| 26 |
-
{
|
| 27 |
-
"question": "Nếu sinh viên đạt được chứng chỉ ngoại ngữ hợp lệ trong học kỳ 4, sinh viên đó có được miễn học các học phần NNCB tương ứng không và cần thực hiện thủ tục gì?",
|
| 28 |
-
"answer": "Có, sinh viên sẽ được xét miễn các học phần NNCB tương ứng với trình độ đạt được. Theo Điều 3.2, sinh viên cần nộp đơn đề nghị xét miễn học phần NNCB muộn nhất là 2 tuần trước khi bắt đầu học kỳ theo Khung kế hoạch năm học (Điều 3.5). Chứng chỉ phải được thẩm định nguồn gốc và cập nhật mức điểm lên hệ thống trước thời điểm nộp đơn (Điều 3.4). Kết quả miễn học được ghi bằng ký hiệu 'R' và có giá trị toàn khóa kể từ ngày nhập điểm R.",
|
| 29 |
-
"type": "conditional",
|
| 30 |
-
"difficulty": "medium",
|
| 31 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 32 |
-
"chunk_index": 0
|
| 33 |
-
},
|
| 34 |
-
{
|
| 35 |
-
"question": "Sinh viên thuộc CTĐT có yêu cầu chuẩn đầu ra là tiếng Anh được phân loại trình độ đầu vào dựa trên những tiêu chí nào? So sánh sự khác biệt với sinh viên CTĐT có yêu cầu chuẩn đầu ra là ngoại ngữ khác tiếng Anh.",
|
| 36 |
-
"answer": "Theo Điều 2.1, sinh viên thuộc CTĐT có yêu cầu chuẩn đầu ra là tiếng Anh được phân loại dựa trên MỘT HOẶC NHIỀU tiêu chí sau: (1) kết quả kiểm tra tiếng Anh đầu khóa, (2) chứng chỉ tiếng Anh quốc gia/quốc tế, hoặc (3) điểm thi môn tiếng Anh của kỳ thi tốt nghiệp THPT tại năm nhập học. Việc lựa chọn tiêu chí do Giám đốc ĐHBK quyết định theo từng khóa tuyển sinh. Trong khi đó, theo Điều 2.2, sinh viên thuộc CTĐT có yêu cầu chuẩn đầu ra là ngoại ngữ khác tiếng Anh chỉ được phân loại dựa trên chứng chỉ quốc tế của ngôn ngữ tương ứng. Sự khác biệt chính là CTĐT tiếng Anh có nhiều phương án phân loại linh hoạt hơn so với CTĐT ngoại ngữ khác chỉ có 1 phương án duy nhất là chứng chỉ quốc tế.",
|
| 37 |
-
"type": "multi_hop",
|
| 38 |
-
"difficulty": "hard",
|
| 39 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 40 |
-
"chunk_index": 0
|
| 41 |
-
},
|
| 42 |
-
{
|
| 43 |
-
"question": "Sinh viên A nhập học khóa 70, thuộc CTĐT có yêu cầu chuẩn đầu ra tiếng Anh. Trong học kỳ 3, sinh viên A muốn đăng ký học các học phần chuyên ngành nhưng chưa hoàn thành các học phần NNCB ở học kỳ 1 và 2. Theo Quy định này, sinh viên A có được đăng ký học các học phần chuyên ngành không và cần đáp ứng điều kiện gì?",
|
| 44 |
-
"answer": "Theo Điều 4.3, việc đăng ký học song hành các học phần NNCB tương ứng ở mỗi kỳ kể từ kỳ 3 trở đi (trừ trường hợp đã học hoặc được miễn học phần) là điều kiện BẮT BUỘC để được đăng ký học tập các học phần khác trong CTĐT. Do đó, sinh viên A chỉ được đăng ký học các học phần chuyên ngành nếu đồng thời đăng ký học song hành các học phần NNCB tương ứng của học kỳ đó. Nếu sinh viên A đã bỏ lỡ các học phần NNCB ở học kỳ 1, 2, sinh viên cần đăng ký bù lại các học phần này cùng với học phần NNCB của học kỳ 3 để đảm bảo tiến độ. Theo Điều 4.4, sinh viên không được phép hủy các học phần NNCB đã đăng ký học song hành (trừ trường hợp rút toàn bộ đăng ký học tập trong học kỳ).",
|
| 45 |
-
"type": "case_study",
|
| 46 |
-
"difficulty": "hard",
|
| 47 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 48 |
-
"chunk_index": 0
|
| 49 |
-
},
|
| 50 |
-
{
|
| 51 |
-
"question": "Chương trình đào tạo Elitech 'Hệ thống nhúng thông minh và IoT' yêu cầu ngoại ngữ chính là gì?",
|
| 52 |
-
"answer": "Chương trình đào tạo Elitech 'Hệ thống nhúng thông minh và IoT' yêu cầu ngoại ngữ chính là Tiếng Nhật. (Theo Phụ lục I - Bảng danh mục các chương trình đào tạo và ngoại ngữ chính yêu cầu)",
|
| 53 |
-
"type": "direct",
|
| 54 |
-
"difficulty": "easy",
|
| 55 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 56 |
-
"chunk_index": 1
|
| 57 |
-
},
|
| 58 |
-
{
|
| 59 |
-
"question": "Tại sao chương trình 'Công nghệ thông tin Global ICT' lại yêu cầu Tiếng Anh làm ngoại ngữ chính trong khi chương trình 'Công nghệ thông tin Việt-Nhật' lại yêu cầu Tiếng Nhật?",
|
| 60 |
-
"answer": "Chương trình 'Công nghệ thông tin Global ICT' yêu cầu Tiếng Anh vì đây là chương trình Elitech tăng cường ngoại ngữ với định hướng toàn cầu, trong khi 'Công nghệ thông tin Việt-Nhật' là chương trình hợp tác/liên kết với Nhật Bản nên yêu cầu Tiếng Nhật làm ngoại ngữ chính để phù hợp với đối tác và môi trường đào tạo. (Theo Phụ lục I - Phân loại các CTĐT Elitech và CTĐT hợp tác/liên kết quốc tế)",
|
| 61 |
-
"type": "reasoning",
|
| 62 |
-
"difficulty": "medium",
|
| 63 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 64 |
-
"chunk_index": 1
|
| 65 |
-
},
|
| 66 |
-
{
|
| 67 |
-
"question": "Nếu một sinh viên có chứng chỉ TOEFL iBT 95 điểm, trình độ ngoại ngữ của sinh viên đó sẽ được quy đổi tương đương như thế nào theo Bảng 2.1?",
|
| 68 |
-
"answer": "Nếu sinh viên có chứng chỉ TOEFL iBT 95 điểm, theo Bảng 2.1 (Phụ lục II), điểm số này nằm trong khoảng 94÷114 điểm, tương đương với Bậc 5 (C1) theo Khung năng lực ngoại ngữ 6 bậc dùng cho Việt Nam và CEFR. (Bảng 2.1 - Bảng quy đổi tương đương các chứng chỉ tiếng Anh)",
|
| 69 |
-
"type": "conditional",
|
| 70 |
-
"difficulty": "medium",
|
| 71 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 72 |
-
"chunk_index": 1
|
| 73 |
-
},
|
| 74 |
-
{
|
| 75 |
-
"question": "Một sinh viên chương trình 'Kỹ thuật Điện tử - Viễn thông' hợp tác với Đại học Leibniz Hannover (Đức) cần đạt trình độ ngoại ngữ nào, và so sánh với sinh viên chương trình 'Phân tích kinh doanh' Elitech về yêu cầu ngoại ngữ?",
|
| 76 |
-
"answer": "Sinh viên chương trình 'Kỹ thuật Điện tử - Viễn thông' hợp tác với Đại học Leibniz Hannover (Đức) cần Tiếng Đức làm ngoại ngữ chính. Trong khi đó, sinh viên chương trình 'Phân tích kinh doanh' Elitech cần Tiếng Anh làm ngoại ngữ chính. Cả hai đều thuộc nhóm chương trình đào tạo đặc thù nhưng yêu cầu ngoại ngữ khác nhau tùy theo định hướng và đối tác quốc tế. (Phụ lục I - Các CTĐT hợp tác/liên kết quốc tế và CTĐT Elitech)",
|
| 77 |
-
"type": "multi_hop",
|
| 78 |
-
"difficulty": "medium",
|
| 79 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 80 |
-
"chunk_index": 1
|
| 81 |
-
},
|
| 82 |
-
{
|
| 83 |
-
"question": "Sinh viên Nguyễn Văn A theo học chương trình 'Công nghệ thông tin Việt-Pháp' cần nộp chứng chỉ Tiếng Pháp. Tuy nhiên, sinh viên chỉ có chứng chỉ IELTS Academic 6.0. Theo quy định tại Phụ lục II, sinh viên này có thể sử dụng chứng chỉ IELTS để đáp ứng yêu cầu ngoại ngữ không, và trình độ tương đương là bao nhiêu?",
|
| 84 |
-
"answer": "Sinh viên Nguyễn Văn A không thể sử dụng chứng chỉ IELTS Academic 6.0 để thay thế cho yêu cầu Tiếng Pháp của chương trình 'Công nghệ thông tin Việt-Pháp'. Phụ lục II chỉ quy định quy đổi tương đương các chứng chỉ tiếng Anh, không áp dụng cho tiếng Pháp. Tuy nhiên, nếu xét riêng về trình độ, IELTS 6.0 tương đương Bậc 4 (B2) theo Khung năng lực ngoại ngữ 6 bậc dùng cho Việt Nam. Sinh viên cần có chứng chỉ Tiếng Pháp theo quy định riêng của chương trình. (Phụ lục I và Phụ lục II Bảng 2.1)",
|
| 85 |
-
"type": "case_study",
|
| 86 |
-
"difficulty": "hard",
|
| 87 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 88 |
-
"chunk_index": 1
|
| 89 |
-
},
|
| 90 |
-
{
|
| 91 |
-
"question": "Theo quy định, bài kiểm tra TOEIC Placement do Đại học Bách khoa Hà Nội tổ chức được sử dụng cho những mục đích nào?",
|
| 92 |
-
"answer": "Bài kiểm tra TOEIC Placement do Đại học Bách khoa Hà Nội phối hợp tổ chức chỉ được sử dụng để phân loại trình độ tiếng Anh cho mục đích xếp lớp và xét miễn học các học phần thuộc khối ngoại ngữ cơ bản, không sử dụng để xét đạt chuẩn ngoại ngữ đầu ra.",
|
| 93 |
-
"type": "direct",
|
| 94 |
-
"difficulty": "easy",
|
| 95 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 96 |
-
"chunk_index": 2
|
| 97 |
-
},
|
| 98 |
-
{
|
| 99 |
-
"question": "Tại sao quy định chỉ công nhận các chứng chỉ có dạng thức thi đánh giá đầy đủ 4 kỹ năng nghe, nói, đọc, viết để quy đổi tương đương?",
|
| 100 |
-
"answer": "Quy định yêu cầu các chứng chỉ phải được cấp trong vòng 2 năm tính đến thời điểm xét và phải có dạng thức thi đánh giá đầy đủ 4 kỹ năng nghe, nói, đọc, viết mới được quy đổi tương đương. Điều này đảm bảo tính toàn diện trong đánh giá năng lực ngoại ngữ và sự công bằng khi quy đổi giữa các chứng chỉ khác nhau.",
|
| 101 |
-
"type": "reasoning",
|
| 102 |
-
"difficulty": "medium",
|
| 103 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 104 |
-
"chunk_index": 2
|
| 105 |
-
},
|
| 106 |
-
{
|
| 107 |
-
"question": "Nếu sinh viên có chứng chỉ TOEIC 4 kỹ năng với điểm các kỹ năng lần lượt là Nghe 750, Nói 600, Đọc 800, Viết 550, thì chứng chỉ này sẽ được quy đổi tương đương như thế nào?",
|
| 108 |
-
"answer": "Theo quy định, chứng chỉ TOEIC 4 kỹ năng được quy đổi tương đương sang các chứng chỉ khác dựa theo kỹ năng đạt điểm thấp nhất. Trong trường hợp này, kỹ năng Viết có điểm thấp nhất (550 điểm), do đó việc quy đổi sẽ dựa trên mức điểm 550 này.",
|
| 109 |
-
"type": "conditional",
|
| 110 |
-
"difficulty": "medium",
|
| 111 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 112 |
-
"chunk_index": 2
|
| 113 |
-
},
|
| 114 |
-
{
|
| 115 |
-
"question": "Sinh viên đạt trình độ Bậc 2.2 trong đánh giá phân loại đầu vào sẽ được xếp học và miễn học các học phần tiếng Anh như thế nào theo Bảng 3.1?",
|
| 116 |
-
"answer": "Theo Bảng 3.1, sinh viên đạt trình độ Bậc 2.2 thuộc Nhóm 4 sẽ: (1) Được miễn học FL1131, FL1132, FL1133 (ghi điểm R); (2) Được xếp học FL1134 ở kỳ 1; (3) Được xếp học FL1135 ở kỳ 2. Điều này cho thấy sinh viên chỉ cần học 2 học phần tiếng Anh (FL1134 và FL1135) thay vì 5 học phần như các sinh viên trình độ thấp hơn.",
|
| 117 |
-
"type": "multi_hop",
|
| 118 |
-
"difficulty": "hard",
|
| 119 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 120 |
-
"chunk_index": 2
|
| 121 |
-
},
|
| 122 |
-
{
|
| 123 |
-
"question": "Sinh viên A có chứng chỉ APTIS ESOL A2 được cấp cách đây 18 tháng. Sinh viên B có chứng chỉ TOEFL iBT Home Edition được cấp cách đây 6 tháng. Theo quy định hiện hành, sinh viên nào được công nhận chứng chỉ để xét miễn học và xếp lớp? Giải thích các bước xếp lớp cho sinh viên được công nhận.",
|
| 124 |
-
"answer": "Sinh viên A được công nhận chứng chỉ, sinh viên B không được công nhận. Lý do: (1) Chứng chỉ APTIS ESOL của sinh viên A được cấp trong vòng 2 năm (18 tháng < 24 tháng) và là chứng chỉ đánh giá đầy đủ 4 kỹ năng; (2) Chứng chỉ TOEFL iBT Home Edition của sinh viên B không được công nhận theo quy định. Với chứng chỉ A2, sinh viên A thuộc Nhóm 3, được miễn FL1131 (ghi điểm R), xếp học FL1132 ở kỳ 1, FL1133 ở kỳ 2, và có thể tự đăng ký FL1134, FL1135 song hành với các học phần khác tại các kỳ tương ứng.",
|
| 125 |
-
"type": "case_study",
|
| 126 |
-
"difficulty": "hard",
|
| 127 |
-
"source": "06_ Quy định ngoại ngữ từ K70_chính quy_final.md",
|
| 128 |
-
"chunk_index": 2
|
| 129 |
-
}
|
| 130 |
-
]
|
| 131 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/debug_metadata.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
| 1 |
-
"""Debug script to check metadata in ChromaDB."""
|
| 2 |
-
import sys
|
| 3 |
-
from pathlib import Path
|
| 4 |
-
from dotenv import find_dotenv, load_dotenv
|
| 5 |
-
|
| 6 |
-
load_dotenv(find_dotenv(usecwd=True))
|
| 7 |
-
|
| 8 |
-
REPO_ROOT = Path(__file__).resolve().parents[1]
|
| 9 |
-
sys.path.insert(0, str(REPO_ROOT))
|
| 10 |
-
|
| 11 |
-
from core.embeddings.embedding_model import EmbeddingConfig, QwenEmbeddings
|
| 12 |
-
from core.embeddings.vector_store import ChromaConfig, ChromaVectorDB
|
| 13 |
-
|
| 14 |
-
emb = QwenEmbeddings(EmbeddingConfig())
|
| 15 |
-
db = ChromaVectorDB(embedder=emb, config=ChromaConfig())
|
| 16 |
-
|
| 17 |
-
print(f"Total docs: {db.count()}")
|
| 18 |
-
print()
|
| 19 |
-
|
| 20 |
-
# Get sample documents
|
| 21 |
-
docs = db.get_all_documents(limit=10)
|
| 22 |
-
print("Sample metadata keys from first 3 docs:")
|
| 23 |
-
for i, d in enumerate(docs[:3]):
|
| 24 |
-
meta = d.get('metadata', {})
|
| 25 |
-
print(f"\n[Doc {i+1}]")
|
| 26 |
-
print(f" Keys: {list(meta.keys())}")
|
| 27 |
-
print(f" source_basename: '{meta.get('source_basename', 'N/A')}'")
|
| 28 |
-
print(f" source_file: '{meta.get('source_file', 'N/A')}'")
|
| 29 |
-
|
| 30 |
-
# Check for K68 file specifically
|
| 31 |
-
print("\n" + "="*50)
|
| 32 |
-
print("Searching for K68 file...")
|
| 33 |
-
k68_docs = []
|
| 34 |
-
for d in db.get_all_documents(limit=5000):
|
| 35 |
-
meta = d.get('metadata', {})
|
| 36 |
-
sf = meta.get('source_file', '') or meta.get('source_basename', '')
|
| 37 |
-
if 'K68' in sf:
|
| 38 |
-
k68_docs.append(d)
|
| 39 |
-
|
| 40 |
-
print(f"Found {len(k68_docs)} docs containing 'K68' in source")
|
| 41 |
-
if k68_docs:
|
| 42 |
-
print(f"Sample: {k68_docs[0].get('metadata', {})}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/test_single_file.py
DELETED
|
@@ -1,94 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python
|
| 2 |
-
"""Test Small-to-Big với 1 file duy nhất."""
|
| 3 |
-
import sys
|
| 4 |
-
from pathlib import Path
|
| 5 |
-
from dotenv import find_dotenv, load_dotenv
|
| 6 |
-
|
| 7 |
-
load_dotenv(find_dotenv(usecwd=True))
|
| 8 |
-
|
| 9 |
-
REPO_ROOT = Path(__file__).resolve().parents[1]
|
| 10 |
-
if str(REPO_ROOT) not in sys.path:
|
| 11 |
-
sys.path.insert(0, str(REPO_ROOT))
|
| 12 |
-
|
| 13 |
-
from core.embeddings.chunk import chunk_markdown_file
|
| 14 |
-
from core.embeddings.embedding_model import EmbeddingConfig, QwenEmbeddings
|
| 15 |
-
from core.embeddings.vector_store import ChromaConfig, ChromaVectorDB
|
| 16 |
-
|
| 17 |
-
# Test với 1 file chứa nhiều bảng
|
| 18 |
-
TEST_FILE = REPO_ROOT / "data/data_process/quyet_dinh/tieng_anh/06_ Quy định ngoại ngữ từ K70_chính quy_final.md"
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
def main():
|
| 22 |
-
print("=" * 60)
|
| 23 |
-
print("TEST SMALL-TO-BIG (1 file)")
|
| 24 |
-
print("=" * 60)
|
| 25 |
-
|
| 26 |
-
# 1. Chunk file
|
| 27 |
-
print(f"\n[1/4] Chunking: {TEST_FILE.name}")
|
| 28 |
-
nodes = chunk_markdown_file(TEST_FILE)
|
| 29 |
-
|
| 30 |
-
parent_nodes = [n for n in nodes if n.metadata.get("is_parent")]
|
| 31 |
-
summary_nodes = [n for n in nodes if n.metadata.get("is_table_summary")]
|
| 32 |
-
other_nodes = [n for n in nodes if not n.metadata.get("is_parent")]
|
| 33 |
-
|
| 34 |
-
print(f" Total nodes: {len(nodes)}")
|
| 35 |
-
print(f" - Parent nodes (NOT embedded): {len(parent_nodes)}")
|
| 36 |
-
print(f" - Summary nodes: {len(summary_nodes)}")
|
| 37 |
-
print(f" - Other nodes (text + small tables): {len(other_nodes) - len(summary_nodes)}")
|
| 38 |
-
|
| 39 |
-
# 2. Init DB (với persist_dir tạm)
|
| 40 |
-
print("\n[2/4] Initializing test DB...")
|
| 41 |
-
emb_cfg = EmbeddingConfig()
|
| 42 |
-
emb = QwenEmbeddings(emb_cfg)
|
| 43 |
-
|
| 44 |
-
# Dùng folder tạm để không ảnh hưởng DB chính
|
| 45 |
-
test_persist = str(REPO_ROOT / "data" / "chroma_test")
|
| 46 |
-
db_cfg = ChromaConfig(persist_dir=test_persist, collection_name="test_s2b")
|
| 47 |
-
db = ChromaVectorDB(embedder=emb, config=db_cfg)
|
| 48 |
-
print(f" Persist dir: {test_persist}")
|
| 49 |
-
|
| 50 |
-
# 3. Upsert
|
| 51 |
-
print("\n[3/4] Upserting documents...")
|
| 52 |
-
count = db.upsert_documents(nodes)
|
| 53 |
-
print(f" Upserted: {count}")
|
| 54 |
-
print(f" ChromaDB count: {db.count()}")
|
| 55 |
-
print(f" Parent nodes stored: {len(db.parent_nodes)}")
|
| 56 |
-
|
| 57 |
-
# Check file JSON
|
| 58 |
-
json_path = Path(test_persist) / "parent_nodes.json"
|
| 59 |
-
if json_path.exists():
|
| 60 |
-
print(f" ✅ parent_nodes.json exists ({json_path.stat().st_size} bytes)")
|
| 61 |
-
else:
|
| 62 |
-
print(f" ❌ parent_nodes.json NOT found!")
|
| 63 |
-
|
| 64 |
-
# 4. Test retrieval
|
| 65 |
-
print("\n[4/4] Testing retrieval...")
|
| 66 |
-
from core.embeddings.retrival import Retriever, RetrievalMode
|
| 67 |
-
|
| 68 |
-
retriever = Retriever(vector_db=db, use_reranker=False)
|
| 69 |
-
|
| 70 |
-
test_query = "TOEIC Nghe 350 điểm tương đương bậc mấy?"
|
| 71 |
-
print(f" Query: {test_query}")
|
| 72 |
-
|
| 73 |
-
results = retriever.vector_search(test_query, k=3)
|
| 74 |
-
|
| 75 |
-
for i, r in enumerate(results, 1):
|
| 76 |
-
meta = r.get("metadata", {})
|
| 77 |
-
content = r.get("content", "")[:200]
|
| 78 |
-
|
| 79 |
-
print(f"\n [{i}]")
|
| 80 |
-
print(f" is_table_summary: {meta.get('is_table_summary', False)}")
|
| 81 |
-
print(f" swapped_from_summary: {meta.get('swapped_from_summary', False)}")
|
| 82 |
-
print(f" source: {meta.get('source_file', 'N/A')}")
|
| 83 |
-
print(f" content: {content}...")
|
| 84 |
-
|
| 85 |
-
print("\n" + "=" * 60)
|
| 86 |
-
print("TEST COMPLETE")
|
| 87 |
-
print("=" * 60)
|
| 88 |
-
|
| 89 |
-
# Cleanup prompt
|
| 90 |
-
print(f"\nTo cleanup test data: rm -rf {test_persist}")
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
if __name__ == "__main__":
|
| 94 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/update_files.py
DELETED
|
@@ -1,226 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Script cập nhật các file cụ thể trong ChromaDB mà không cần rebuild toàn bộ.
|
| 3 |
-
|
| 4 |
-
Usage:
|
| 5 |
-
# Cập nhật một file cụ thể
|
| 6 |
-
python scripts/update_files.py --files "data/data_process/quyet_dinh/tieng_anh/QD_ngoai_ngu_tu_K68_CQ_final.md"
|
| 7 |
-
|
| 8 |
-
# Cập nhật nhiều files
|
| 9 |
-
python scripts/update_files.py --files "file1.md" "file2.md" "file3.md"
|
| 10 |
-
|
| 11 |
-
# Cập nhật tất cả files trong một thư mục
|
| 12 |
-
python scripts/update_files.py --dir "data/data_process/quyet_dinh/tieng_anh"
|
| 13 |
-
|
| 14 |
-
# Chỉ xóa (không re-ingest)
|
| 15 |
-
python scripts/update_files.py --files "file.md" --delete-only
|
| 16 |
-
|
| 17 |
-
# Dry-run để xem sẽ làm gì
|
| 18 |
-
python scripts/update_files.py --files "file.md" --dry-run
|
| 19 |
-
"""
|
| 20 |
-
|
| 21 |
-
import sys
|
| 22 |
-
import argparse
|
| 23 |
-
from pathlib import Path
|
| 24 |
-
from dotenv import find_dotenv, load_dotenv
|
| 25 |
-
|
| 26 |
-
load_dotenv(find_dotenv(usecwd=True))
|
| 27 |
-
|
| 28 |
-
REPO_ROOT = Path(__file__).resolve().parents[1]
|
| 29 |
-
if str(REPO_ROOT) not in sys.path:
|
| 30 |
-
sys.path.insert(0, str(REPO_ROOT))
|
| 31 |
-
|
| 32 |
-
from core.embeddings.chunk import chunk_markdown_file
|
| 33 |
-
from core.embeddings.embedding_model import EmbeddingConfig, QwenEmbeddings
|
| 34 |
-
from core.embeddings.vector_store import ChromaConfig, ChromaVectorDB
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
def get_docs_by_source_file(db: ChromaVectorDB, source_basename: str) -> list:
|
| 38 |
-
"""Lấy tất cả documents thuộc về một source file."""
|
| 39 |
-
col = db.collection
|
| 40 |
-
if col is None:
|
| 41 |
-
return []
|
| 42 |
-
|
| 43 |
-
# Query theo source_file (tên file trong metadata)
|
| 44 |
-
result = col.get(
|
| 45 |
-
where={"source_file": source_basename},
|
| 46 |
-
include=['documents', 'metadatas']
|
| 47 |
-
)
|
| 48 |
-
|
| 49 |
-
docs = []
|
| 50 |
-
ids = result.get('ids', [])
|
| 51 |
-
for i, doc_id in enumerate(ids):
|
| 52 |
-
docs.append({
|
| 53 |
-
'id': doc_id,
|
| 54 |
-
'content': result['documents'][i] if result.get('documents') else '',
|
| 55 |
-
'metadata': result['metadatas'][i] if result.get('metadatas') else {},
|
| 56 |
-
})
|
| 57 |
-
|
| 58 |
-
# Nếu không tìm thấy, thử theo source_file (path đầy đủ)
|
| 59 |
-
if not docs:
|
| 60 |
-
# Lấy tất cả và filter thủ công
|
| 61 |
-
all_docs = db.get_all_documents(limit=10000)
|
| 62 |
-
for d in all_docs:
|
| 63 |
-
meta = d.get('metadata', {})
|
| 64 |
-
s_basename = meta.get('source_basename', '')
|
| 65 |
-
s_file = meta.get('source_file', '')
|
| 66 |
-
if source_basename in s_basename or source_basename in s_file:
|
| 67 |
-
docs.append(d)
|
| 68 |
-
|
| 69 |
-
return docs
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
def delete_file_from_db(db: ChromaVectorDB, source_basename: str, dry_run: bool = False) -> int:
|
| 73 |
-
"""Xóa tất cả documents thuộc về một file."""
|
| 74 |
-
docs = get_docs_by_source_file(db, source_basename)
|
| 75 |
-
|
| 76 |
-
if not docs:
|
| 77 |
-
print(f"Không tìm thấy documents cho: {source_basename}")
|
| 78 |
-
return 0
|
| 79 |
-
|
| 80 |
-
ids_to_delete = [d['id'] for d in docs]
|
| 81 |
-
|
| 82 |
-
if dry_run:
|
| 83 |
-
print(f"[DRY-RUN] Sẽ xóa {len(ids_to_delete)} documents cho: {source_basename}")
|
| 84 |
-
# Also check parent nodes
|
| 85 |
-
parent_count = sum(1 for key, node in db.parent_nodes.items()
|
| 86 |
-
if source_basename in node.get("metadata", {}).get("source_file", ""))
|
| 87 |
-
if parent_count > 0:
|
| 88 |
-
print(f"[DRY-RUN] Sẽ xóa {parent_count} parent nodes cho: {source_basename}")
|
| 89 |
-
return len(ids_to_delete)
|
| 90 |
-
|
| 91 |
-
# Delete documents from ChromaDB
|
| 92 |
-
deleted = db.delete_documents(ids_to_delete)
|
| 93 |
-
print(f"Đã xóa {deleted} documents cho: {source_basename}")
|
| 94 |
-
|
| 95 |
-
# Also delete parent nodes for this file
|
| 96 |
-
parent_deleted = db.delete_parent_nodes_by_source(source_basename)
|
| 97 |
-
if parent_deleted > 0:
|
| 98 |
-
print(f"Đã xóa {parent_deleted} parent nodes cho: {source_basename}")
|
| 99 |
-
|
| 100 |
-
return deleted
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
def ingest_file(db: ChromaVectorDB, file_path: Path, dry_run: bool = False) -> int:
|
| 104 |
-
if not file_path.exists():
|
| 105 |
-
print(f"File không tồn tại: {file_path}")
|
| 106 |
-
return 0
|
| 107 |
-
|
| 108 |
-
docs = chunk_markdown_file(file_path)
|
| 109 |
-
|
| 110 |
-
if not docs:
|
| 111 |
-
print(f"Không có chunks từ: {file_path.name}")
|
| 112 |
-
return 0
|
| 113 |
-
|
| 114 |
-
if dry_run:
|
| 115 |
-
print(f" [DRY-RUN] Sẽ thêm {len(docs)} chunks từ: {file_path.name}")
|
| 116 |
-
return len(docs)
|
| 117 |
-
|
| 118 |
-
n = db.upsert_documents(docs)
|
| 119 |
-
print(f" ✓ Đã thêm {n} chunks từ: {file_path.name}")
|
| 120 |
-
return n
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
def update_file(db: ChromaVectorDB, file_path: Path, dry_run: bool = False, delete_only: bool = False) -> tuple:
|
| 124 |
-
source_basename = file_path.name
|
| 125 |
-
|
| 126 |
-
print(f"\n Processing: {file_path}")
|
| 127 |
-
|
| 128 |
-
# Bước 1: Xóa documents cũ
|
| 129 |
-
deleted = delete_file_from_db(db, source_basename, dry_run)
|
| 130 |
-
|
| 131 |
-
# Bước 2: Thêm documents mới (nếu không chỉ xóa)
|
| 132 |
-
added = 0
|
| 133 |
-
if not delete_only:
|
| 134 |
-
added = ingest_file(db, file_path, dry_run)
|
| 135 |
-
|
| 136 |
-
return deleted, added
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
def main():
|
| 140 |
-
parser = argparse.ArgumentParser(description="Cập nhật files cụ thể trong ChromaDB")
|
| 141 |
-
parser.add_argument("--files", nargs="+", help="Danh sách files cần cập nhật")
|
| 142 |
-
parser.add_argument("--dir", help="Thư mục chứa các files cần cập nhật")
|
| 143 |
-
parser.add_argument("--dry-run", action="store_true", help="Chỉ hiển thị, không thực sự thay đổi")
|
| 144 |
-
parser.add_argument("--delete-only", action="store_true", help="Chỉ xóa, không thêm lại")
|
| 145 |
-
args = parser.parse_args()
|
| 146 |
-
|
| 147 |
-
if not args.files and not args.dir:
|
| 148 |
-
parser.error("Phải chỉ định --files hoặc --dir")
|
| 149 |
-
|
| 150 |
-
# Thu thập danh sách files
|
| 151 |
-
files_to_update = []
|
| 152 |
-
|
| 153 |
-
if args.files:
|
| 154 |
-
for f in args.files:
|
| 155 |
-
file_path = REPO_ROOT / f
|
| 156 |
-
if file_path.exists():
|
| 157 |
-
files_to_update.append(file_path)
|
| 158 |
-
else:
|
| 159 |
-
print(f" File không tồn tại: {f}")
|
| 160 |
-
|
| 161 |
-
if args.dir:
|
| 162 |
-
dir_path = REPO_ROOT / args.dir
|
| 163 |
-
if dir_path.exists() and dir_path.is_dir():
|
| 164 |
-
files_to_update.extend(sorted(dir_path.glob("*.md")))
|
| 165 |
-
else:
|
| 166 |
-
print(f"Thư mục không tồn tại: {args.dir}")
|
| 167 |
-
|
| 168 |
-
if not files_to_update:
|
| 169 |
-
print(" Không có files nào để xử lý!")
|
| 170 |
-
return
|
| 171 |
-
|
| 172 |
-
print("=" * 60)
|
| 173 |
-
print("CẬP NHẬT FILES TRONG CHROMADB")
|
| 174 |
-
if args.dry_run:
|
| 175 |
-
print("🔍 MODE: DRY-RUN (chỉ xem, không thay đổi)")
|
| 176 |
-
if args.delete_only:
|
| 177 |
-
print("🗑️ MODE: DELETE-ONLY (chỉ xóa)")
|
| 178 |
-
print("=" * 60)
|
| 179 |
-
|
| 180 |
-
print(f"\n📋 Files sẽ xử lý ({len(files_to_update)}):")
|
| 181 |
-
for f in files_to_update:
|
| 182 |
-
print(f" - {f.name}")
|
| 183 |
-
|
| 184 |
-
# Khởi tạo embedder và database
|
| 185 |
-
print("\n[1/3] Khởi tạo embedder...")
|
| 186 |
-
emb_cfg = EmbeddingConfig()
|
| 187 |
-
emb = QwenEmbeddings(emb_cfg)
|
| 188 |
-
print(f" ✓ Model: {emb_cfg.model}")
|
| 189 |
-
|
| 190 |
-
print("\n[2/3] Kết nối ChromaDB...")
|
| 191 |
-
db_cfg = ChromaConfig()
|
| 192 |
-
db = ChromaVectorDB(embedder=emb, config=db_cfg)
|
| 193 |
-
old_count = db.count()
|
| 194 |
-
print(f" ✓ Collection: {db_cfg.collection_name}")
|
| 195 |
-
print(f" ✓ Current docs: {old_count}")
|
| 196 |
-
|
| 197 |
-
# Xử lý từng file
|
| 198 |
-
print("\n[3/3] Xử lý files...")
|
| 199 |
-
total_deleted = 0
|
| 200 |
-
total_added = 0
|
| 201 |
-
|
| 202 |
-
for file_path in files_to_update:
|
| 203 |
-
deleted, added = update_file(db, file_path, args.dry_run, args.delete_only)
|
| 204 |
-
total_deleted += deleted
|
| 205 |
-
total_added += added
|
| 206 |
-
|
| 207 |
-
# Kết quả
|
| 208 |
-
new_count = db.count()
|
| 209 |
-
|
| 210 |
-
print("\n" + "=" * 60)
|
| 211 |
-
print(" KẾT QUẢ:")
|
| 212 |
-
print(f" Files đã xử lý: {len(files_to_update)}")
|
| 213 |
-
print(f" Documents đã xóa: {total_deleted}")
|
| 214 |
-
print(f" Documents đã thêm: {total_added}")
|
| 215 |
-
print(f" DB count trước: {old_count}")
|
| 216 |
-
print(f" DB count sau: {new_count}")
|
| 217 |
-
print(f" Delta: {new_count - old_count:+d}")
|
| 218 |
-
print("=" * 60)
|
| 219 |
-
|
| 220 |
-
if args.dry_run:
|
| 221 |
-
print("\n Đây là DRY-RUN - không có thay đổi thực sự!")
|
| 222 |
-
print(" Chạy lại không có --dry-run để thực hiện.")
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
if __name__ == "__main__":
|
| 226 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/verify_file.py
DELETED
|
@@ -1,101 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Script kiểm tra documents của một file cụ thể trong ChromaDB.
|
| 3 |
-
|
| 4 |
-
Usage:
|
| 5 |
-
python scripts/verify_file.py --file "QD_ngoai_ngu_tu_K68_CQ_final.md"
|
| 6 |
-
"""
|
| 7 |
-
|
| 8 |
-
import sys
|
| 9 |
-
from pathlib import Path
|
| 10 |
-
from dotenv import find_dotenv, load_dotenv
|
| 11 |
-
|
| 12 |
-
load_dotenv(find_dotenv(usecwd=True))
|
| 13 |
-
|
| 14 |
-
REPO_ROOT = Path(__file__).resolve().parents[1]
|
| 15 |
-
if str(REPO_ROOT) not in sys.path:
|
| 16 |
-
sys.path.insert(0, str(REPO_ROOT))
|
| 17 |
-
|
| 18 |
-
from core.embeddings.embedding_model import EmbeddingConfig, QwenEmbeddings
|
| 19 |
-
from core.embeddings.vector_store import ChromaConfig, ChromaVectorDB
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
def verify_file(source_basename: str):
|
| 23 |
-
"""Kiểm tra documents của một file trong database."""
|
| 24 |
-
|
| 25 |
-
print("=" * 60)
|
| 26 |
-
print(f"VERIFY FILE: {source_basename}")
|
| 27 |
-
print("=" * 60)
|
| 28 |
-
|
| 29 |
-
# Khởi tạo
|
| 30 |
-
emb_cfg = EmbeddingConfig()
|
| 31 |
-
emb = QwenEmbeddings(emb_cfg)
|
| 32 |
-
db_cfg = ChromaConfig()
|
| 33 |
-
db = ChromaVectorDB(embedder=emb, config=db_cfg)
|
| 34 |
-
|
| 35 |
-
print(f"\nTotal docs in DB: {db.count()}")
|
| 36 |
-
|
| 37 |
-
# Query theo source_file (tên file)
|
| 38 |
-
col = db.collection
|
| 39 |
-
result = col.get(
|
| 40 |
-
where={"source_file": source_basename},
|
| 41 |
-
include=['documents', 'metadatas']
|
| 42 |
-
)
|
| 43 |
-
|
| 44 |
-
ids = result.get('ids', [])
|
| 45 |
-
docs = result.get('documents', [])
|
| 46 |
-
metas = result.get('metadatas', [])
|
| 47 |
-
|
| 48 |
-
print(f"\nDocuments for '{source_basename}': {len(ids)}")
|
| 49 |
-
|
| 50 |
-
if not ids:
|
| 51 |
-
print(" (Không tìm thấy documents)")
|
| 52 |
-
return
|
| 53 |
-
|
| 54 |
-
print("\n" + "-" * 60)
|
| 55 |
-
print("SAMPLE METADATA (first 3 docs):")
|
| 56 |
-
print("-" * 60)
|
| 57 |
-
|
| 58 |
-
for i, meta in enumerate(metas[:3]):
|
| 59 |
-
print(f"\n[Doc {i+1}] ID: {ids[i][:20]}...")
|
| 60 |
-
for key, value in meta.items():
|
| 61 |
-
# Truncate long values
|
| 62 |
-
val_str = str(value)
|
| 63 |
-
if len(val_str) > 80:
|
| 64 |
-
val_str = val_str[:80] + "..."
|
| 65 |
-
print(f" {key}: {val_str}")
|
| 66 |
-
|
| 67 |
-
# Thống kê metadata
|
| 68 |
-
print("\n" + "-" * 60)
|
| 69 |
-
print("METADATA STATS:")
|
| 70 |
-
print("-" * 60)
|
| 71 |
-
|
| 72 |
-
titles = set()
|
| 73 |
-
cohorts = set()
|
| 74 |
-
doc_types = set()
|
| 75 |
-
|
| 76 |
-
for meta in metas:
|
| 77 |
-
if meta.get('title'):
|
| 78 |
-
titles.add(meta.get('title'))
|
| 79 |
-
if meta.get('applicable_cohorts'):
|
| 80 |
-
cohorts.add(meta.get('applicable_cohorts'))
|
| 81 |
-
if meta.get('document_type'):
|
| 82 |
-
doc_types.add(meta.get('document_type'))
|
| 83 |
-
|
| 84 |
-
print(f" Unique titles: {titles}")
|
| 85 |
-
print(f" Cohorts: {cohorts}")
|
| 86 |
-
print(f" Doc types: {doc_types}")
|
| 87 |
-
|
| 88 |
-
print("\n" + "=" * 60)
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
def main():
|
| 92 |
-
import argparse
|
| 93 |
-
parser = argparse.ArgumentParser()
|
| 94 |
-
parser.add_argument("--file", required=True, help="Tên file cần verify (source_basename)")
|
| 95 |
-
args = parser.parse_args()
|
| 96 |
-
|
| 97 |
-
verify_file(args.file)
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
if __name__ == "__main__":
|
| 101 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test/chunk_results.md
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
test/llama.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
from llama_parse import LlamaParse, ResultType
|
| 3 |
-
from dotenv import load_dotenv
|
| 4 |
-
|
| 5 |
-
load_dotenv()
|
| 6 |
-
|
| 7 |
-
def test_llamaparse(file_path: str):
|
| 8 |
-
parser = LlamaParse(
|
| 9 |
-
result_type=ResultType.MD, # Định dạng Markdown giúp giữ cấu trúc bảng cực tốt
|
| 10 |
-
num_workers=4, # Chạy song song để tăng tốc
|
| 11 |
-
verbose=True,
|
| 12 |
-
language="vi" # Cấu hình ngôn ngữ tiếng Việt
|
| 13 |
-
)
|
| 14 |
-
|
| 15 |
-
print(f"--- Đang bắt đầu xử lý file: {file_path} ---")
|
| 16 |
-
|
| 17 |
-
# Thực hiện trích xuất dữ liệu
|
| 18 |
-
documents = parser.load_data(file_path)
|
| 19 |
-
|
| 20 |
-
if documents:
|
| 21 |
-
# In thử 1000 ký tự đầu tiên để kiểm tra
|
| 22 |
-
print("\n--- Kết quả trích xuất mẫu ---")
|
| 23 |
-
print(documents[0].text[:1000])
|
| 24 |
-
|
| 25 |
-
# Lưu kết quả ra file .md để bạn xem cấu trúc bảng có bị lệch không
|
| 26 |
-
output_name = "data/parsed_llama_result.md"
|
| 27 |
-
with open(output_name, "w", encoding="utf-8") as f:
|
| 28 |
-
for doc in documents:
|
| 29 |
-
f.write(doc.text + "\n\n")
|
| 30 |
-
print(f"\n--- Đã lưu toàn bộ nội dung vào: {output_name} ---")
|
| 31 |
-
else:
|
| 32 |
-
print("Lỗi: Không tìm thấy nội dung trong file.")
|
| 33 |
-
|
| 34 |
-
if __name__ == "__main__":
|
| 35 |
-
# Thay đường dẫn đến file PDF quy chế của bạn vào đây
|
| 36 |
-
PATH_TO_PDF = "data/data_raw/quyet_dinh/quy-dinh-chuan-ngoai-ngu-2021.pdf"
|
| 37 |
-
|
| 38 |
-
api_key = os.getenv("LLAMA_CLOUD_API_KEY")
|
| 39 |
-
if not api_key:
|
| 40 |
-
print("Lỗi: Bạn chưa cấu hình LLAMA_CLOUD_API_KEY trong file .env")
|
| 41 |
-
else:
|
| 42 |
-
test_llamaparse(PATH_TO_PDF)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test/test_small_to_big.py
CHANGED
|
@@ -17,7 +17,7 @@ def test_chunk_with_summary():
|
|
| 17 |
"""Test chunking a file with tables to verify summary generation."""
|
| 18 |
|
| 19 |
# Use the K70 English requirements file which has many tables
|
| 20 |
-
test_file = REPO_ROOT / "data/data_process/
|
| 21 |
|
| 22 |
if not test_file.exists():
|
| 23 |
print(f"❌ Test file not found: {test_file}")
|
|
@@ -96,11 +96,22 @@ def export_to_markdown(nodes, output_path: Path, source_name: str):
|
|
| 96 |
parent = parent_map.get(parent_id)
|
| 97 |
meta = node.metadata
|
| 98 |
|
| 99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
lines.append(f"")
|
| 101 |
lines.append(f"**Metadata:**")
|
| 102 |
lines.append(f"- is_table_summary: True")
|
| 103 |
lines.append(f"- parent_id: `{parent_id}`")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
if meta.get("source_file"):
|
| 105 |
lines.append(f"- source_file: {meta.get('source_file')}")
|
| 106 |
if meta.get("applicable_cohorts"):
|
|
@@ -121,6 +132,7 @@ def export_to_markdown(nodes, output_path: Path, source_name: str):
|
|
| 121 |
|
| 122 |
lines.append(f"---")
|
| 123 |
lines.append(f"")
|
|
|
|
| 124 |
|
| 125 |
# Small tables (embedded directly)
|
| 126 |
if table_nodes:
|
|
@@ -129,10 +141,20 @@ def export_to_markdown(nodes, output_path: Path, source_name: str):
|
|
| 129 |
|
| 130 |
for i, node in enumerate(table_nodes, 1):
|
| 131 |
meta = node.metadata
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
lines.append(f"")
|
| 134 |
lines.append(f"**Metadata:**")
|
| 135 |
lines.append(f"- is_table: True")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
if meta.get("table_part"):
|
| 137 |
lines.append(f"- table_part: {meta.get('table_part')}")
|
| 138 |
if meta.get("source_file"):
|
|
@@ -148,6 +170,7 @@ def export_to_markdown(nodes, output_path: Path, source_name: str):
|
|
| 148 |
lines.append(f"")
|
| 149 |
lines.append(f"---")
|
| 150 |
lines.append(f"")
|
|
|
|
| 151 |
|
| 152 |
# Text nodes
|
| 153 |
lines.append("## 📄 Text Nodes")
|
|
|
|
| 17 |
"""Test chunking a file with tables to verify summary generation."""
|
| 18 |
|
| 19 |
# Use the K70 English requirements file which has many tables
|
| 20 |
+
test_file = REPO_ROOT / "data/data_process/chuong_trinh_dao_tao/1.1. Kỹ thuật Cơ điện tử.md"
|
| 21 |
|
| 22 |
if not test_file.exists():
|
| 23 |
print(f"❌ Test file not found: {test_file}")
|
|
|
|
| 96 |
parent = parent_map.get(parent_id)
|
| 97 |
meta = node.metadata
|
| 98 |
|
| 99 |
+
# Build table identifier for title
|
| 100 |
+
table_num = meta.get('table_number', '')
|
| 101 |
+
table_title = meta.get('table_title', '')
|
| 102 |
+
title_suffix = ""
|
| 103 |
+
if table_num:
|
| 104 |
+
title_suffix = f" (Bảng {table_num})"
|
| 105 |
+
|
| 106 |
+
lines.append(f"### Summary #{i}{title_suffix}")
|
| 107 |
lines.append(f"")
|
| 108 |
lines.append(f"**Metadata:**")
|
| 109 |
lines.append(f"- is_table_summary: True")
|
| 110 |
lines.append(f"- parent_id: `{parent_id}`")
|
| 111 |
+
if table_num:
|
| 112 |
+
lines.append(f"- table_number: {table_num}")
|
| 113 |
+
if table_title:
|
| 114 |
+
lines.append(f"- table_title: {table_title}")
|
| 115 |
if meta.get("source_file"):
|
| 116 |
lines.append(f"- source_file: {meta.get('source_file')}")
|
| 117 |
if meta.get("applicable_cohorts"):
|
|
|
|
| 132 |
|
| 133 |
lines.append(f"---")
|
| 134 |
lines.append(f"")
|
| 135 |
+
|
| 136 |
|
| 137 |
# Small tables (embedded directly)
|
| 138 |
if table_nodes:
|
|
|
|
| 141 |
|
| 142 |
for i, node in enumerate(table_nodes, 1):
|
| 143 |
meta = node.metadata
|
| 144 |
+
table_num = meta.get('table_number', '')
|
| 145 |
+
table_title = meta.get('table_title', '')
|
| 146 |
+
title_suffix = ""
|
| 147 |
+
if table_num:
|
| 148 |
+
title_suffix = f" (Bảng {table_num})"
|
| 149 |
+
|
| 150 |
+
lines.append(f"### Small Table #{i}{title_suffix}")
|
| 151 |
lines.append(f"")
|
| 152 |
lines.append(f"**Metadata:**")
|
| 153 |
lines.append(f"- is_table: True")
|
| 154 |
+
if table_num:
|
| 155 |
+
lines.append(f"- table_number: {table_num}")
|
| 156 |
+
if table_title:
|
| 157 |
+
lines.append(f"- table_title: {table_title}")
|
| 158 |
if meta.get("table_part"):
|
| 159 |
lines.append(f"- table_part: {meta.get('table_part')}")
|
| 160 |
if meta.get("source_file"):
|
|
|
|
| 170 |
lines.append(f"")
|
| 171 |
lines.append(f"---")
|
| 172 |
lines.append(f"")
|
| 173 |
+
|
| 174 |
|
| 175 |
# Text nodes
|
| 176 |
lines.append("## 📄 Text Nodes")
|