File size: 10,414 Bytes
6cd63d6
 
f44d06d
6cd63d6
 
54eeb97
6cd63d6
 
 
 
 
 
 
 
 
 
 
 
 
f44d06d
6cd63d6
 
 
f44d06d
6cd63d6
 
 
 
 
 
 
 
 
 
 
 
 
f44d06d
 
6cd63d6
 
 
 
f44d06d
 
 
 
 
 
 
 
 
 
 
 
6cd63d6
 
 
f44d06d
6cd63d6
 
 
 
 
 
 
 
 
 
 
f44d06d
6cd63d6
f44d06d
6cd63d6
 
f44d06d
 
 
 
 
 
 
 
 
6cd63d6
 
 
 
 
 
 
 
 
 
 
f44d06d
 
 
6cd63d6
 
f44d06d
 
 
6cd63d6
 
 
 
 
f44d06d
6cd63d6
 
 
 
f44d06d
 
 
 
 
 
6cd63d6
 
 
f44d06d
6cd63d6
 
 
 
 
f44d06d
 
 
 
 
6cd63d6
 
 
 
 
f44d06d
 
6cd63d6
f44d06d
6cd63d6
f44d06d
6cd63d6
f44d06d
6cd63d6
f44d06d
 
 
 
6cd63d6
 
 
 
f44d06d
 
 
6cd63d6
f44d06d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6cd63d6
 
f44d06d
 
 
6cd63d6
f44d06d
 
 
 
 
 
 
 
 
 
 
 
 
 
6cd63d6
 
 
 
f44d06d
 
 
 
 
 
 
 
6cd63d6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f44d06d
6cd63d6
 
 
 
 
 
 
 
 
 
f44d06d
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
import time
import traceback
from typing import List, Dict, Any, Tuple, Optional
from models.schemas import RAGSearchResult
from core.multilingual_manager import MultilingualManager
from core.rag_system import EnhancedRAGSystem

class ConversationalAgent:
    """Lớp xử lý hội thoại với grounding từ RAG"""
    
    def __init__(self, rag_system: EnhancedRAGSystem, multilingual_manager: MultilingualManager):
        self.rag_system = rag_system
        self.multilingual_manager = multilingual_manager
        self.conversation_history = []
        self.max_history_length = 10
        
    def process_query(self, query: str, chat_history: List[Tuple[str, str]] = None) -> Tuple[str, List[Dict]]:
        """Xử lý truy vấn hội thoại với grounding từ RAG"""
        try:
            print(f"🤖 CAG: Xử lý truy vấn: {query}")
            
            # Phát hiện ngôn ngữ
            language = self.multilingual_manager.detect_language(query)
            print(f"🌐 CAG: Ngôn ngữ phát hiện: {language}")
            
            # Tìm kiếm ngữ nghĩa trong RAG
            search_results = self.rag_system.semantic_search(query, top_k=3)
            
            # Tạo context từ kết quả tìm kiếm
            context = self._build_context_from_results(search_results, query, language)
            
            # Cập nhật lịch sử hội thoại
            self._update_conversation_history("user", query)
            
            # Tạo prompt với context
            prompt = self._create_grounded_prompt(query, context, language, chat_history)
            
            # Tạo phản hồi (mô phỏng - có thể tích hợp LLM thực)
            response = self._generate_response(prompt, language, search_results)
            
            # Cập nhật lịch sử
            self._update_conversation_history("assistant", response)
            
            # Convert results to dict
            results_dict = []
            for result in search_results:
                results_dict.append({
                    "id": result.id,
                    "text": result.text[:150] + "..." if len(result.text) > 150 else result.text,
                    "similarity": round(result.similarity, 3),
                    "metadata": result.metadata
                })
            
            print(f"✅ CAG: Đã xử lý, tìm thấy {len(results_dict)} kết quả")
            return response, results_dict
            
        except Exception as e:
            error_msg = f"Xin lỗi, đã có lỗi xảy ra: {str(e)}"
            print(f"❌ CAG Lỗi: {traceback.format_exc()}")
            return error_msg, []
    
    def _build_context_from_results(self, results: List[RAGSearchResult], query: str, language: str) -> str:
        """Xây dựng context từ kết quả tìm kiếm"""
        if not results:
            if language == 'vi':
                return "Không có thông tin liên quan trong cơ sở tri thức."
            else:
                return "No relevant information found in the knowledge base."
        
        if language == 'vi':
            context_parts = [f"Thông tin liên quan đến '{query}':"]
        else:
            context_parts = [f"Relevant information about '{query}':"]
        
        for i, result in enumerate(results[:3]):  # Lấy 3 kết quả đầu
            source = result.metadata.get('source', 'unknown')
            lang = result.metadata.get('language', 'unknown')
            
            if language == 'vi':
                context_parts.append(f"\n{i+1}. {result.text}")
                context_parts.append(f"   (Nguồn: {source}, Ngôn ngữ: {lang})")
            else:
                context_parts.append(f"\n{i+1}. {result.text}")
                context_parts.append(f"   (Source: {source}, Language: {lang})")
        
        return "\n".join(context_parts)
    
    def _create_grounded_prompt(self, query: str, context: str, language: str, 
                               chat_history: List[Tuple[str, str]] = None) -> str:
        """Tạo prompt với grounding từ context"""
        
        # Xây dựng phần lịch sử hội thoại
        history_text = ""
        if chat_history and len(chat_history) > 0:
            if language == 'vi':
                history_parts = ["Lịch sử hội thoại trước:"]
                for user_msg, assistant_msg in chat_history[-2:]:  # Lấy 2 cặp gần nhất
                    history_parts.append(f"Bạn: {user_msg}")
                    history_parts.append(f"Trợ lý: {assistant_msg}")
            else:
                history_parts = ["Previous conversation:"]
                for user_msg, assistant_msg in chat_history[-2:]:
                    history_parts.append(f"You: {user_msg}")
                    history_parts.append(f"Assistant: {assistant_msg}")
            history_text = "\n".join(history_parts) + "\n\n"
        
        # Tạo prompt dựa trên ngôn ngữ
        if language == 'vi':
            prompt = f"""{history_text}THÔNG TIN TỪ CƠ SỞ DỮ LIỆU:
{context}

CÂU HỎI HIỆN TẠI: {query}

YÊU CẦU:
- Trả lời dựa trên thông tin được cung cấp
- Nếu thiếu thông tin, hãy nói rõ
- Trả lời bằng tiếng Việt
- Ngắn gọn, chính xác
- Có thể thêm thông tin bổ sung nếu cần

TRẢ LỜI:"""
        else:
            prompt = f"""{history_text}INFORMATION FROM DATABASE:
{context}

CURRENT QUESTION: {query}

INSTRUCTIONS:
- Answer based on provided information
- If information is insufficient, clearly state that
- Respond in {language}
- Be concise and accurate
- You may add supplementary information if needed

ANSWER:"""
        
        return prompt
    
    def _generate_response(self, prompt: str, language: str, search_results: List[RAGSearchResult]) -> str:
        """Tạo phản hồi từ prompt (mô phỏng)"""
        # Đây là mô phỏng - trong thực tế sẽ gọi API LLM
        time.sleep(0.3)  # Giả lập thời gian xử lý
        
        if not search_results:
            if language == 'vi':
                return "Xin lỗi, tôi không tìm thấy thông tin cụ thể về chủ đề này trong cơ sở dữ liệu hiện tại. Bạn có thể thử diễn đạt khác hoặc thêm thông tin mới."
            else:
                return "Sorry, I couldn't find specific information about this topic in the current database. You might try rephrasing or adding new information."
        
        # Đếm số kết quả
        result_count = len(search_results)
        
        # Tạo phản hồi dựa trên ngôn ngữ
        if language == 'vi':
            responses = [
                f"Dựa trên {result_count} thông tin liên quan tôi tìm thấy:",
                f"Theo {result_count} nguồn thông tin từ cơ sở dữ liệu:",
                f"Căn cứ vào {result_count} kết quả tìm kiếm:"
            ]
            
            # Lấy các chủ đề chính
            sources = []
            for result in search_results[:2]:
                source = result.metadata.get('source', '')
                if source and source not in sources:
                    sources.append(source)
            
            if sources:
                source_text = f" (từ {', '.join(sources)})"
            else:
                source_text = ""
            
            follow_up = f"\n\nĐây là thông tin hữu ích tôi tìm được{source_text}. Bạn có thể xem chi tiết các nguồn thông tin bên phải."
            
        else:
            responses = [
                f"Based on {result_count} relevant information sources:",
                f"According to {result_count} data sources from the database:",
                f"Based on the search results ({result_count} found):"
            ]
            
            # Get main sources
            sources = []
            for result in search_results[:2]:
                source = result.metadata.get('source', '')
                if source and source not in sources:
                    sources.append(source)
            
            if sources:
                source_text = f" (from {', '.join(sources)})"
            else:
                source_text = ""
            
            follow_up = f"\n\nThis is useful information I found{source_text}. You can view the detailed sources on the right."
        
        import random
        base_response = random.choice(responses)
        
        # Thêm nội dung cụ thể từ kết quả đầu tiên
        if search_results and len(search_results[0].text) > 50:
            main_info = search_results[0].text[:200] + "..." if len(search_results[0].text) > 200 else search_results[0].text
            response = f"{base_response}\n\n{main_info}{follow_up}"
        else:
            response = f"{base_response}{follow_up}"
        
        return response
    
    def _update_conversation_history(self, role: str, message: str):
        """Cập nhật lịch sử hội thoại"""
        self.conversation_history.append({
            "role": role,
            "message": message,
            "timestamp": time.time()
        })
        
        # Giới hạn độ dài lịch sử
        if len(self.conversation_history) > self.max_history_length:
            self.conversation_history = self.conversation_history[-self.max_history_length:]
    
    def clear_conversation_history(self):
        """Xóa lịch sử hội thoại"""
        self.conversation_history = []
        print("🧹 CAG: Đã xóa lịch sử hội thoại")
    
    def get_conversation_stats(self) -> Dict:
        """Lấy thống kê hội thoại"""
        user_msgs = [m for m in self.conversation_history if m["role"] == "user"]
        assistant_msgs = [m for m in self.conversation_history if m["role"] == "assistant"]
        
        return {
            "total_messages": len(self.conversation_history),
            "user_messages": len(user_msgs),
            "assistant_messages": len(assistant_msgs),
            "current_session": len(self.conversation_history),
            "last_interaction": time.strftime('%H:%M:%S', time.localtime(
                self.conversation_history[-1]["timestamp"] if self.conversation_history else time.time()
            ))
        }