| | from __future__ import annotations |
| | from typing import Any, Dict, List, TYPE_CHECKING |
| |
|
| | if TYPE_CHECKING: |
| | from core.rag.retrieval import Retriever |
| |
|
| |
|
| | |
| | SYSTEM_PROMPT = """Bạn là Trợ lý học vụ Đại học Bách khoa Hà Nội. Nhiệm vụ: trả lời câu hỏi của sinh viên về quy chế, quy định dựa trên tài liệu được cung cấp. |
| | |
| | ## NGUYÊN TẮC: |
| | 1. **Chỉ dùng CONTEXT:** Chỉ trả lời dựa trên CONTEXT được cung cấp. Tuyệt đối không suy đoán hay bổ sung thông tin ngoài CONTEXT. |
| | 2. **Ưu tiên văn bản mới:** Nếu CONTEXT chứa nhiều văn bản khác nhau, ưu tiên nội dung mới nhất (năm ban hành lớn hơn), TRỪ KHI có điều khoản chuyển tiếp nói khác. |
| | 3. **Trích dẫn nguồn:** Ghi rõ thông tin lấy từ văn bản nào (tên file, điều/khoản nếu có) để sinh viên có thể tự tra cứu. |
| | 4. **Lưu ý phạm vi áp dụng:** Nếu quy định chỉ áp dụng cho khóa/chương trình cụ thể, hãy nêu rõ điều kiện áp dụng. |
| | 5. **Không tìm thấy:** Nếu CONTEXT không chứa thông tin liên quan, trả lời: "Không tìm thấy thông tin trong dữ liệu hiện có. Bạn nên liên hệ Phòng Đào tạo để được hỗ trợ." |
| | |
| | ## CÁCH TRÌNH BÀY: |
| | - Trả lời rõ ràng, dễ hiểu, thân thiện với sinh viên. |
| | - Sử dụng bullet points khi liệt kê nhiều điều kiện/bước. |
| | - Nếu câu trả lời phức tạp, chia thành các phần nhỏ có tiêu đề. |
| | """ |
| |
|
| |
|
| | def build_context(results: List[Dict[str, Any]], max_chars: int = 8000) -> str: |
| |
|
| | parts = [] |
| | for i, r in enumerate(results, 1): |
| | meta = r.get("metadata", {}) |
| | source = meta.get("source_file", "N/A") |
| | header = meta.get("header_path", "") |
| | doc_type = meta.get("document_type", "") |
| | cohorts = meta.get("applicable_cohorts", "") |
| | program = meta.get("program_name", "") |
| | program_code = meta.get("program_code", "") |
| | faculty = meta.get("faculty", "") |
| | degree_levels = meta.get("degree_levels", []) |
| | issued_year = meta.get("issued_year", "") |
| | content = r.get("content", "").strip() |
| | |
| | |
| | meta_info = f"Nguồn: {source}" |
| | if header and header != "/": |
| | meta_info += f" | Mục: {header}" |
| | if doc_type: |
| | meta_info += f" | Loại: {doc_type}" |
| | if issued_year: |
| | meta_info += f" | Năm: {issued_year}" |
| | if cohorts: |
| | meta_info += f" | Áp dụng: {cohorts}" |
| | if program: |
| | meta_info += f" | CTĐT: {program}" |
| | if program_code: |
| | meta_info += f" | Mã: {program_code}" |
| | if faculty: |
| | meta_info += f" | Khoa: {faculty}" |
| | if degree_levels: |
| | levels = ", ".join(degree_levels) if isinstance(degree_levels, list) else degree_levels |
| | meta_info += f" | Bậc: {levels}" |
| | |
| | parts.append(f"[TÀI LIỆU {i}]\n{meta_info}\n{content}") |
| |
|
| | context = "\n---\n".join(parts) |
| | |
| | return context[:max_chars] if len(context) > max_chars else context |
| |
|
| |
|
| | def build_prompt(question: str, context: str) -> str: |
| |
|
| | return f"{SYSTEM_PROMPT}\n\n## CONTEXT:\n{context}\n\n## CÂU HỎI: {question}\n\n## TRẢ LỜI:" |
| |
|
| |
|
| | class RAGContextBuilder: |
| |
|
| | |
| | def __init__(self, retriever: "Retriever", max_context_chars: int = 8000): |
| |
|
| | self._retriever = retriever |
| | self._max_context_chars = max_context_chars |
| |
|
| | def retrieve_and_prepare( |
| | self, |
| | question: str, |
| | k: int = 5, |
| | initial_k: int = 20, |
| | mode: str = "hybrid_rerank" |
| | ) -> Dict[str, Any]: |
| | |
| | results = self._retriever.flexible_search(question, k=k, initial_k=initial_k, mode=mode) |
| |
|
| | |
| | if not results: |
| | return { |
| | "results": [], |
| | "contexts": [], |
| | "context_text": "", |
| | "prompt": "", |
| | } |
| |
|
| | |
| | context_text = build_context(results, self._max_context_chars) |
| | prompt = build_prompt(question, context_text) |
| |
|
| | return { |
| | "results": results, |
| | "contexts": [r.get("content", "")[:1000] for r in results], |
| | "context_text": context_text, |
| | "prompt": prompt, |
| | } |
| |
|
| |
|
| | |
| | RAGGenerator = RAGContextBuilder |
| |
|