Create conversational_agent.py
Browse files- core/conversational_agent.py +195 -0
core/conversational_agent.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
import traceback
|
| 3 |
+
from typing import List, Dict, Any, Tuple
|
| 4 |
+
from sentence_transformers import SentenceTransformer
|
| 5 |
+
from models.schemas import RAGSearchResult
|
| 6 |
+
from core.multilingual_manager import MultilingualManager
|
| 7 |
+
from core.enhanced_rag import EnhancedRAGSystem
|
| 8 |
+
|
| 9 |
+
class ConversationalAgent:
|
| 10 |
+
"""Lớp xử lý hội thoại với grounding từ RAG"""
|
| 11 |
+
|
| 12 |
+
def __init__(self, rag_system: EnhancedRAGSystem, multilingual_manager: MultilingualManager):
|
| 13 |
+
self.rag_system = rag_system
|
| 14 |
+
self.multilingual_manager = multilingual_manager
|
| 15 |
+
self.conversation_history = []
|
| 16 |
+
self.max_history_length = 10
|
| 17 |
+
|
| 18 |
+
def process_query(self, query: str, chat_history: List[Tuple[str, str]] = None) -> Tuple[str, List[Dict]]:
|
| 19 |
+
"""Xử lý truy vấn hội thoại với grounding từ RAG"""
|
| 20 |
+
try:
|
| 21 |
+
print(f"🤖 Xử lý truy vấn: {query}")
|
| 22 |
+
|
| 23 |
+
# Phát hiện ngôn ngữ
|
| 24 |
+
language = self.multilingual_manager.detect_language(query)
|
| 25 |
+
print(f"🌐 Ngôn ngữ phát hiện: {language}")
|
| 26 |
+
|
| 27 |
+
# Tìm kiếm ngữ nghĩa trong RAG
|
| 28 |
+
search_results = self.rag_system.semantic_search(query, top_k=3)
|
| 29 |
+
|
| 30 |
+
# Tạo context từ kết quả tìm kiếm
|
| 31 |
+
context = self._build_context_from_results(search_results, query, language)
|
| 32 |
+
|
| 33 |
+
# Cập nhật lịch sử hội thoại
|
| 34 |
+
self._update_conversation_history("user", query)
|
| 35 |
+
|
| 36 |
+
# Tạo prompt với context
|
| 37 |
+
prompt = self._create_grounded_prompt(query, context, language, chat_history)
|
| 38 |
+
|
| 39 |
+
# Tạo phản hồi (ở đây có thể tích hợp với LLM thực tế)
|
| 40 |
+
response = self._generate_response(prompt, language)
|
| 41 |
+
|
| 42 |
+
# Cập nhật lịch sử
|
| 43 |
+
self._update_conversation_history("assistant", response)
|
| 44 |
+
|
| 45 |
+
# Trả về response và search results để hiển thị
|
| 46 |
+
return response, search_results
|
| 47 |
+
|
| 48 |
+
except Exception as e:
|
| 49 |
+
error_msg = f"Xin lỗi, đã có lỗi xảy ra: {str(e)}"
|
| 50 |
+
print(f"❌ Lỗi trong ConversationalAgent: {traceback.format_exc()}")
|
| 51 |
+
return error_msg, []
|
| 52 |
+
|
| 53 |
+
def _build_context_from_results(self, results: List[RAGSearchResult], query: str, language: str) -> str:
|
| 54 |
+
"""Xây dựng context từ kết quả tìm kiếm"""
|
| 55 |
+
if not results:
|
| 56 |
+
if language == 'vi':
|
| 57 |
+
return "Không có thông tin liên quan trong cơ sở tri thức."
|
| 58 |
+
else:
|
| 59 |
+
return "No relevant information found in the knowledge base."
|
| 60 |
+
|
| 61 |
+
if language == 'vi':
|
| 62 |
+
context_parts = [f"Dựa trên câu hỏi '{query}', đây là thông tin liên quan từ cơ sở tri thức:"]
|
| 63 |
+
else:
|
| 64 |
+
context_parts = [f"Based on the question '{query}', here is relevant information from the knowledge base:"]
|
| 65 |
+
|
| 66 |
+
for i, result in enumerate(results[:3]): # Lấy 3 kết quả đầu
|
| 67 |
+
context_parts.append(f"\n{i+1}. {result.text}")
|
| 68 |
+
if result.metadata.get('source'):
|
| 69 |
+
if language == 'vi':
|
| 70 |
+
context_parts.append(f" (Nguồn: {result.metadata.get('source')})")
|
| 71 |
+
else:
|
| 72 |
+
context_parts.append(f" (Source: {result.metadata.get('source')})")
|
| 73 |
+
|
| 74 |
+
return "\n".join(context_parts)
|
| 75 |
+
|
| 76 |
+
def _create_grounded_prompt(self, query: str, context: str, language: str,
|
| 77 |
+
chat_history: List[Tuple[str, str]] = None) -> str:
|
| 78 |
+
"""Tạo prompt với grounding từ context"""
|
| 79 |
+
|
| 80 |
+
# Xây dựng phần lịch sử hội thoại
|
| 81 |
+
history_text = ""
|
| 82 |
+
if chat_history and len(chat_history) > 0:
|
| 83 |
+
if language == 'vi':
|
| 84 |
+
history_parts = ["Lịch sử hội thoại gần đây:"]
|
| 85 |
+
for user_msg, assistant_msg in chat_history[-3:]: # Lấy 3 cặp gần nhất
|
| 86 |
+
history_parts.append(f"Người dùng: {user_msg}")
|
| 87 |
+
history_parts.append(f"Trợ lý: {assistant_msg}")
|
| 88 |
+
else:
|
| 89 |
+
history_parts = ["Recent conversation history:"]
|
| 90 |
+
for user_msg, assistant_msg in chat_history[-3:]:
|
| 91 |
+
history_parts.append(f"User: {user_msg}")
|
| 92 |
+
history_parts.append(f"Assistant: {assistant_msg}")
|
| 93 |
+
history_text = "\n".join(history_parts) + "\n\n"
|
| 94 |
+
|
| 95 |
+
# Tạo prompt dựa trên ngôn ngữ
|
| 96 |
+
if language == 'vi':
|
| 97 |
+
prompt = f"""{history_text}CONTEXT TỪ CƠ SỞ TRI THỨC:
|
| 98 |
+
{context}
|
| 99 |
+
|
| 100 |
+
CÂU HỎI HIỆN TẠI: {query}
|
| 101 |
+
|
| 102 |
+
HƯỚNG DẪN:
|
| 103 |
+
1. Trả lời dựa trên context được cung cấp
|
| 104 |
+
2. Nếu context không đủ thông tin, hãy nói rõ
|
| 105 |
+
3. Trả lời bằng tiếng Việt
|
| 106 |
+
4. Giữ câu trả lời ngắn gọn, chính xác
|
| 107 |
+
5. Có thể thêm thông tin bổ sung nếu cần
|
| 108 |
+
|
| 109 |
+
TRẢ LỜI:"""
|
| 110 |
+
else:
|
| 111 |
+
prompt = f"""{history_text}CONTEXT FROM KNOWLEDGE BASE:
|
| 112 |
+
{context}
|
| 113 |
+
|
| 114 |
+
CURRENT QUESTION: {query}
|
| 115 |
+
|
| 116 |
+
INSTRUCTIONS:
|
| 117 |
+
1. Answer based on the provided context
|
| 118 |
+
2. If context doesn't have enough information, clearly state that
|
| 119 |
+
3. Respond in {language}
|
| 120 |
+
4. Keep answers concise and accurate
|
| 121 |
+
5. You may add supplementary information if needed
|
| 122 |
+
|
| 123 |
+
ANSWER:"""
|
| 124 |
+
|
| 125 |
+
return prompt
|
| 126 |
+
|
| 127 |
+
def _generate_response(self, prompt: str, language: str) -> str:
|
| 128 |
+
"""Tạo phản hồi từ prompt (có thể tích hợp với LLM thực)"""
|
| 129 |
+
# Đây là mô phỏng - trong thực tế sẽ gọi API LLM
|
| 130 |
+
time.sleep(0.5) # Giả lập thời gian xử lý
|
| 131 |
+
|
| 132 |
+
# Phân tích prompt để tạo phản hồi phù hợp
|
| 133 |
+
no_info_vi = "Không có thông tin liên quan trong cơ sở tri thức"
|
| 134 |
+
no_info_en = "No relevant information found in the knowledge base"
|
| 135 |
+
|
| 136 |
+
if no_info_vi in prompt or no_info_en in prompt:
|
| 137 |
+
if language == 'vi':
|
| 138 |
+
return "Xin lỗi, tôi không tìm thấy thông tin cụ thể về câu hỏi này trong cơ sở tri thức hiện tại. Bạn có thể thử diễn đạt câu hỏi khác hoặc thêm thông tin vào cơ sở tri thức."
|
| 139 |
+
else:
|
| 140 |
+
return "Sorry, I couldn't find specific information about this question in the current knowledge base. You might try rephrasing your question or adding more information to the knowledge base."
|
| 141 |
+
|
| 142 |
+
# Tạo phản hồi dựa trên ngôn ngữ
|
| 143 |
+
if language == 'vi':
|
| 144 |
+
responses = [
|
| 145 |
+
"Dựa trên thông tin trong cơ sở tri thức, tôi có thể cung cấp cho bạn thông tin sau:",
|
| 146 |
+
"Theo dữ liệu có sẵn, đây là thông tin liên quan:",
|
| 147 |
+
"Dựa vào nguồn thông tin được cung cấp, tôi có thể trả lời như sau:"
|
| 148 |
+
]
|
| 149 |
+
follow_up = "\n\nTôi đã tìm thấy một số thông tin hữu ích liên quan đến câu hỏi của bạn. Bạn có thể xem chi tiết các nguồn thông tin bên dưới."
|
| 150 |
+
else:
|
| 151 |
+
responses = [
|
| 152 |
+
"Based on the information in the knowledge base, I can provide you with the following:",
|
| 153 |
+
"According to the available data, here is the relevant information:",
|
| 154 |
+
"Based on the provided sources, I can answer as follows:"
|
| 155 |
+
]
|
| 156 |
+
follow_up = "\n\nI've found some useful information related to your question. You can view the detailed sources below."
|
| 157 |
+
|
| 158 |
+
import random
|
| 159 |
+
base_response = random.choice(responses)
|
| 160 |
+
|
| 161 |
+
return f"{base_response}{follow_up}"
|
| 162 |
+
|
| 163 |
+
def _update_conversation_history(self, role: str, message: str):
|
| 164 |
+
"""Cập nhật lịch sử hội thoại"""
|
| 165 |
+
self.conversation_history.append({
|
| 166 |
+
"role": role,
|
| 167 |
+
"message": message,
|
| 168 |
+
"timestamp": time.time()
|
| 169 |
+
})
|
| 170 |
+
|
| 171 |
+
# Giới hạn độ dài lịch sử
|
| 172 |
+
if len(self.conversation_history) > self.max_history_length:
|
| 173 |
+
self.conversation_history = self.conversation_history[-self.max_history_length:]
|
| 174 |
+
|
| 175 |
+
def clear_conversation_history(self):
|
| 176 |
+
"""Xóa lịch sử hội thoại"""
|
| 177 |
+
self.conversation_history = []
|
| 178 |
+
print("🧹 Đã xóa lịch sử hội thoại")
|
| 179 |
+
|
| 180 |
+
def get_conversation_stats(self) -> Dict:
|
| 181 |
+
"""Lấy thống kê hội thoại"""
|
| 182 |
+
user_msgs = [m for m in self.conversation_history if m["role"] == "user"]
|
| 183 |
+
assistant_msgs = [m for m in self.conversation_history if m["role"] == "assistant"]
|
| 184 |
+
|
| 185 |
+
return {
|
| 186 |
+
"total_messages": len(self.conversation_history),
|
| 187 |
+
"user_messages": len(user_msgs),
|
| 188 |
+
"assistant_messages": len(assistant_msgs),
|
| 189 |
+
"last_user_message": user_msgs[-1]["message"] if user_msgs else None,
|
| 190 |
+
"last_activity": self.conversation_history[-1]["timestamp"] if self.conversation_history else None
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
def get_recent_conversation(self, limit: int = 5) -> List[Dict]:
|
| 194 |
+
"""Lấy lịch sử hội thoại gần đây"""
|
| 195 |
+
return self.conversation_history[-limit:] if self.conversation_history else []
|