DoAn / core /rag /generator.py
hungnha's picture
Thay đổi promt
92c9b4d
from __future__ import annotations
from typing import Any, Dict, List, TYPE_CHECKING
if TYPE_CHECKING:
from core.rag.retrieval import Retriever
# System prompt for LLM (exported for gradio/eval usage)
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()
# Build metadata line
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)
# Truncate if exceeds limit
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]:
# Search for relevant documents
results = self._retriever.flexible_search(question, k=k, initial_k=initial_k, mode=mode)
# No results found
if not results:
return {
"results": [],
"contexts": [],
"context_text": "",
"prompt": "",
}
# Build context and 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,
}
# Backward compatibility alias
RAGGenerator = RAGContextBuilder