File size: 9,614 Bytes
ff6980a
 
 
 
d1f023e
ff6980a
 
 
 
 
65f6247
ff6980a
 
 
 
435589f
65f6247
 
 
 
 
ff6980a
 
 
435589f
 
65f6247
 
 
 
 
 
 
435589f
ff6980a
e32e103
65f6247
 
 
 
 
 
 
435589f
e32e103
 
 
 
 
b0e5887
e32e103
 
 
 
65f6247
 
 
 
 
 
 
 
e32e103
 
 
 
 
 
65f6247
435589f
65f6247
ff6980a
65f6247
 
 
 
 
 
 
435589f
65f6247
 
 
 
 
 
 
 
435589f
65f6247
 
435589f
65f6247
 
435589f
65f6247
435589f
65f6247
 
 
 
 
 
 
ff6980a
65f6247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ff6980a
 
 
65f6247
 
 
 
 
 
 
ff6980a
 
435589f
b0e5887
ff6980a
65f6247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ff6980a
65f6247
 
 
ff6980a
e32e103
65f6247
 
e32e103
b0e5887
65f6247
ff6980a
 
 
 
e32e103
65f6247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from smolagents import CodeAgent
import logging
from typing import Optional, List, Dict, Any
from datetime import datetime
import os

logger = logging.getLogger(__name__)

class ResearchAgent(CodeAgent):
    """
    Дослідницький агент для наукового пошуку та аналізу з підтримкою контексту розмови.
    """
    
    def __init__(self, model, tools, **kwargs):
        """
        Ініціалізація дослідницького агента.
        
        Args:
            model: Модель для обробки запитів
            tools: Інструменти для пошуку та обробки інформації
            **kwargs: Додаткові параметри
        """
        super().__init__(model=model, tools=tools, **kwargs)
        self.available_tools = {tool.name: tool for tool in tools}
        self.model_initializer = None
        self.step_callback = None
        # Ініціалізація контексту розмови
        self.conversation_context = {
            'previous_queries': [],      # Історія запитів
            'search_results': {},        # Результати пошуку для кожного запиту
            'generated_content': {},     # Згенерований контент для кожного запиту
            'last_topic': None          # Остання обговорювана тема
        }
        logger.info(f"ResearchAgent ініціалізовано з інструментами: {list(self.available_tools.keys())}")

    def _update_steps(self, step_info: str, step_result: Any = None):
        """
        Оновлення інформації про кроки з результатами.
        
        Args:
            step_info: Інформація про поточний крок
            step_result: Результат виконання кроку
        """
        if self.step_callback:
            step_data = {
                'info': step_info,
                'result': self._format_step_result(step_result)
            }
            self.step_callback(step_data)
            logger.info(f"Step: {step_info}")
            if step_result:
                logger.info(f"Result: {str(step_result)[:200]}...")

    def _format_step_result(self, result: Any) -> str:
        """
        Форматування результату кроку для відображення.
        
        Args:
            result: Результат для форматування
        Returns:
            str: Відформатований результат
        """
        if result is None:
            return ""
        if isinstance(result, dict):
            return "\n".join([f"{k}: {str(v)}" for k, v in result.items()])
        elif isinstance(result, list):
            return "\n".join([str(item) for item in result])
        return str(result)

    def _is_related_query(self, current_query: str, previous_context: Dict) -> bool:
        """
        Визначає, чи є поточний запит пов'язаним з попереднім контекстом.
        
        Args:
            current_query: Поточний запит
            previous_context: Попередній контекст
        Returns:
            bool: True якщо запит пов'язаний з попереднім контекстом
        """
        if not previous_context['last_topic']:
            return False

        prompt = f"""Визначте, чи є це уточнюючим запитом до попередньої теми.
        Попередня тема: {previous_context['last_topic']}
        Новий запит: {current_query}
        Відповідь має бути лише True або False."""

        try:
            response = self.model(prompt)
            return 'true' in response.lower()
        except Exception as e:
            logger.error(f"Помилка при визначенні зв'язку запитів: {e}")
            return False

    def _build_context_prompt(self, query: str, context: Dict) -> str:
        """
        Формує промпт з урахуванням попереднього контексту.
        
        Args:
            query: Поточний запит
            context: Контекст розмови
        Returns:
            str: Сформований промпт
        """
        prompt_parts = []
        
        if context['previous_queries']:
            prompt_parts.append("Попередні запити та результати:")
            for prev_query in context['previous_queries'][-3:]:
                prompt_parts.append(f"Запит: {prev_query}")
                if prev_query in context['search_results']:
                    prompt_parts.append("Знайдена інформація:")
                    prompt_parts.append(context['search_results'][prev_query])
                if prev_query in context['generated_content']:
                    prompt_parts.append("Згенерований контент:")
                    prompt_parts.append(context['generated_content'][prev_query])
                prompt_parts.append("---")

        prompt_parts.append(f"Поточний запит: {query}")
        prompt_parts.append("На основі всієї наведеної інформації створіть детальну відповідь.")
        
        return "\n".join(prompt_parts)

    def _update_context(self, query: str, search_results: str, generated_content: str):
        """
        Оновлює контекст розмови.
        
        Args:
            query: Поточний запит
            search_results: Результати пошуку
            generated_content: Згенерований контент
        """
        self.conversation_context['previous_queries'].append(query)
        self.conversation_context['search_results'][query] = search_results
        self.conversation_context['generated_content'][query] = generated_content
        self.conversation_context['last_topic'] = query

    def process_query(self, query: str, available_files: Optional[List[str]] = None) -> str:
        """
        Обробка дослідницького запиту з підтримкою контексту.
        
        Args:
            query: Запит для обробки
            available_files: Список доступних файлів
        Returns:
            str: Результат обробки запиту
        """
        try:
            logger.info(f"Обробка дослідницького запиту: {query}")
            self._update_steps("Початок обробки запиту")
            
            # Перевіряємо зв'язок з попереднім контекстом
            is_related = self._is_related_query(query, self.conversation_context)
            self._update_steps("Аналіз зв'язку з попереднім контекстом", 
                             {'is_related': is_related})

            # Виконуємо пошук
            self._update_steps("Пошук інформації в інтернеті")
            search_results = self.available_tools['web_search'](query=query)
            self._update_steps("Отримано результати пошуку", search_results)

            # Формуємо промпт
            if is_related:
                prompt = self._build_context_prompt(query, self.conversation_context)
                self._update_steps("Формування запиту з урахуванням контексту", 
                                 {'context_length': len(self.conversation_context['previous_queries'])})
            else:
                prompt = f"Task: {query}\n\nSearch Results:\n{search_results}"
                self._update_steps("Формування нового запиту")

            # Генеруємо відповідь
            self._update_steps("Генерація відповіді")
            result = self.model(prompt)
            
            # Оновлюємо контекст
            self._update_steps("Оновлення контексту розмови")
            self._update_context(query, search_results, result)
            
            self._update_steps("Запит успішно оброблено", {
                'context_size': len(self.conversation_context['previous_queries']),
                'is_related_query': is_related
            })
            
            return result
            
        except Exception as e:
            error_msg = f"Помилка при обробці запиту: {str(e)}"
            logger.error(error_msg)
            self._update_steps(f"Помилка", {'error': error_msg})
            return error_msg

    def clear_context(self):
        """
        Очищення контексту розмови.
        
        Returns:
            str: Повідомлення про очищення контексту
        """
        self.conversation_context = {
            'previous_queries': [],
            'search_results': {},
            'generated_content': {},
            'last_topic': None
        }
        logger.info("Контекст розмови очищено")
        return "Контекст розмови очищено"