Spaces:
Sleeping
Sleeping
Update agent.py
Browse files
agent.py
CHANGED
@@ -11,30 +11,6 @@ from smolagents import CodeAgent, MultiStepAgent, AgentError, PythonInterpreterT
|
|
11 |
from models import ModelManager
|
12 |
from tools import search_web, scrape_website, read_file
|
13 |
|
14 |
-
# --- CLASSE ORCHESTRATEUR COMPLÈTE ---
|
15 |
-
class OrchestratorAgent(MultiStepAgent):
|
16 |
-
"""
|
17 |
-
Agent orchestrateur qui hérite de MultiStepAgent et implémente
|
18 |
-
les méthodes requises pour le prompt système et la réponse finale.
|
19 |
-
"""
|
20 |
-
def initialize_system_prompt(self) -> str:
|
21 |
-
"""Définit le prompt système pour l'agent orchestrateur."""
|
22 |
-
return (
|
23 |
-
"You are a world-class autonomous agent. Your goal is to fully answer the user's question. "
|
24 |
-
"To do so, you have access to a set of tools. "
|
25 |
-
"First, think step-by-step and lay out a plan to solve the problem. "
|
26 |
-
"Then, execute the plan by calling the tools in the required sequence. "
|
27 |
-
"Analyze the results of each tool call. If the plan is not working, reassess and create a new plan. "
|
28 |
-
"When you have the final answer, present it clearly to the user."
|
29 |
-
)
|
30 |
-
|
31 |
-
def render_final_answer(self, final_context: dict, final_response: str) -> str:
|
32 |
-
"""
|
33 |
-
Formate et retourne la réponse finale de l'agent.
|
34 |
-
"""
|
35 |
-
# Pour l'instant, nous retournons simplement la réponse brute du modèle.
|
36 |
-
return final_response
|
37 |
-
|
38 |
class UltraAgent:
|
39 |
"""
|
40 |
Agent ultra-puissant avec orchestration multi-modèles.
|
@@ -46,48 +22,91 @@ class UltraAgent:
|
|
46 |
print("🚀 Initialisation de l'UltraAgent...")
|
47 |
|
48 |
self.hf_token = hf_token or os.getenv("HF_TOKEN")
|
|
|
|
|
49 |
self.model_manager = ModelManager(self.hf_token)
|
50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
self._init_specialized_agents()
|
|
|
|
|
52 |
self.conversation_history = []
|
|
|
53 |
print("✅ UltraAgent initialisé avec succès!")
|
54 |
|
55 |
def _init_specialized_agents(self):
|
56 |
-
"""Initialise les agents spécialisés."""
|
57 |
try:
|
58 |
-
|
|
|
59 |
model=self.model_manager.get_orchestrator(),
|
60 |
tools=self.tools,
|
61 |
max_steps=15,
|
62 |
planning_interval=3
|
63 |
)
|
|
|
|
|
64 |
self.code_agent = CodeAgent(
|
65 |
model=self.model_manager.get_code_agent(),
|
66 |
tools=[PythonInterpreterTool()],
|
67 |
additional_authorized_imports=["requests", "pandas", "numpy", "matplotlib", "seaborn", "scipy", "sklearn"]
|
68 |
)
|
|
|
69 |
print("✅ Agents spécialisés initialisés")
|
|
|
70 |
except Exception as e:
|
71 |
print(f"❌ Erreur lors de l'initialisation des agents: {e}")
|
72 |
raise
|
73 |
|
74 |
def __call__(self, question: str) -> str:
|
75 |
-
"""
|
|
|
|
|
|
|
76 |
print("\n" + "="*80)
|
77 |
-
print(
|
|
|
78 |
print("="*80)
|
79 |
|
80 |
try:
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
strategy = self._analyze_question(question)
|
83 |
print(f"📋 Stratégie sélectionnée: {strategy['type']}")
|
|
|
|
|
84 |
response = self._execute_strategy(question, strategy)
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
print(f"✅ Réponse générée avec succ��s ({len(response)} caractères)")
|
87 |
return response
|
|
|
88 |
except Exception as e:
|
89 |
error_msg = f"❌ Erreur critique dans l'UltraAgent: {e}"
|
90 |
print(error_msg)
|
|
|
|
|
91 |
try:
|
92 |
print("🔄 Tentative de récupération avec le modèle de raisonnement...")
|
93 |
fallback_response = self._fallback_reasoning(question, str(e))
|
@@ -98,65 +117,189 @@ class UltraAgent:
|
|
98 |
return final_error
|
99 |
|
100 |
def _analyze_question(self, question: str) -> Dict[str, Any]:
|
101 |
-
"""
|
102 |
-
|
|
|
103 |
question_lower = question.lower()
|
104 |
-
|
|
|
|
|
105 |
code_keywords = ['code', 'program', 'script', 'algorithm', 'python', 'javascript', 'sql', 'debug', 'programmer']
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
|
112 |
def _execute_strategy(self, question: str, strategy: Dict[str, Any]) -> str:
|
113 |
-
"""Exécute la stratégie déterminée."""
|
114 |
strategy_type = strategy["type"]
|
|
|
115 |
try:
|
116 |
if strategy_type == "vision":
|
117 |
return self._handle_vision_task(question)
|
118 |
elif strategy_type == "code":
|
119 |
return self._handle_code_task(question)
|
120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
return self._handle_general_task(question)
|
|
|
122 |
except Exception as e:
|
123 |
print(f"❌ Erreur dans l'exécution de la stratégie {strategy_type}: {e}")
|
124 |
-
|
|
|
125 |
|
126 |
def _handle_vision_task(self, question: str) -> str:
|
127 |
-
"""Gère les tâches
|
128 |
print("👁️ Traitement avec le modèle de vision...")
|
129 |
-
|
130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
|
132 |
def _handle_code_task(self, question: str) -> str:
|
133 |
-
"""Gère les tâches de code."""
|
134 |
print("💻 Traitement avec l'agent de code...")
|
135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
|
137 |
def _handle_general_task(self, question: str) -> str:
|
138 |
-
"""Gère
|
139 |
print("🎯 Traitement général avec l'orchestrateur...")
|
140 |
-
|
141 |
-
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
def _fallback_reasoning(self, question: str, error: str) -> str:
|
145 |
-
"""
|
146 |
-
|
147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
|
149 |
def _get_conversation_context(self, max_exchanges: int = 3) -> str:
|
150 |
-
"""Récupère le contexte des échanges récents."""
|
151 |
-
if not self.conversation_history:
|
|
|
|
|
152 |
recent_history = self.conversation_history[-max_exchanges*2:]
|
153 |
context_parts = ["Contexte de la conversation récente:"]
|
154 |
for entry in recent_history:
|
155 |
role = "Utilisateur" if entry["role"] == "user" else "Assistant"
|
156 |
-
content = str(entry["content"])[:200]
|
157 |
context_parts.append(f"{role}: {content}")
|
|
|
158 |
return "\n".join(context_parts)
|
159 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
|
161 |
class BasicAgent:
|
162 |
"""
|
|
|
11 |
from models import ModelManager
|
12 |
from tools import search_web, scrape_website, read_file
|
13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
class UltraAgent:
|
15 |
"""
|
16 |
Agent ultra-puissant avec orchestration multi-modèles.
|
|
|
22 |
print("🚀 Initialisation de l'UltraAgent...")
|
23 |
|
24 |
self.hf_token = hf_token or os.getenv("HF_TOKEN")
|
25 |
+
|
26 |
+
# Gestionnaire de modèles
|
27 |
self.model_manager = ModelManager(self.hf_token)
|
28 |
+
|
29 |
+
# Outils disponibles
|
30 |
+
self.tools = [
|
31 |
+
search_web,
|
32 |
+
scrape_website,
|
33 |
+
read_file,
|
34 |
+
PythonInterpreterTool()
|
35 |
+
]
|
36 |
+
|
37 |
+
# Agents spécialisés
|
38 |
self._init_specialized_agents()
|
39 |
+
|
40 |
+
# Historique des interactions
|
41 |
self.conversation_history = []
|
42 |
+
|
43 |
print("✅ UltraAgent initialisé avec succès!")
|
44 |
|
45 |
def _init_specialized_agents(self):
|
46 |
+
"""Initialise les agents spécialisés comme l'orchestrateur et l'agent de code."""
|
47 |
try:
|
48 |
+
# Agent principal (orchestrateur)
|
49 |
+
self.orchestrator = MultiStepAgent(
|
50 |
model=self.model_manager.get_orchestrator(),
|
51 |
tools=self.tools,
|
52 |
max_steps=15,
|
53 |
planning_interval=3
|
54 |
)
|
55 |
+
|
56 |
+
# Agent de code
|
57 |
self.code_agent = CodeAgent(
|
58 |
model=self.model_manager.get_code_agent(),
|
59 |
tools=[PythonInterpreterTool()],
|
60 |
additional_authorized_imports=["requests", "pandas", "numpy", "matplotlib", "seaborn", "scipy", "sklearn"]
|
61 |
)
|
62 |
+
|
63 |
print("✅ Agents spécialisés initialisés")
|
64 |
+
|
65 |
except Exception as e:
|
66 |
print(f"❌ Erreur lors de l'initialisation des agents: {e}")
|
67 |
raise
|
68 |
|
69 |
def __call__(self, question: str) -> str:
|
70 |
+
"""
|
71 |
+
Point d'entrée principal de l'agent.
|
72 |
+
Analyse la question, détermine la stratégie et exécute la tâche.
|
73 |
+
"""
|
74 |
print("\n" + "="*80)
|
75 |
+
print("🧠 ULTRA-AGENT - Nouvelle question reçue")
|
76 |
+
print(f"Question: {question[:200]}{'...' if len(question) > 200 else ''}")
|
77 |
print("="*80)
|
78 |
|
79 |
try:
|
80 |
+
# Ajoute à l'historique
|
81 |
+
self.conversation_history.append({
|
82 |
+
"role": "user",
|
83 |
+
"content": question,
|
84 |
+
"timestamp": time.time()
|
85 |
+
})
|
86 |
+
|
87 |
+
# Analyse de la question et choix de la stratégie
|
88 |
strategy = self._analyze_question(question)
|
89 |
print(f"📋 Stratégie sélectionnée: {strategy['type']}")
|
90 |
+
|
91 |
+
# Exécution selon la stratégie
|
92 |
response = self._execute_strategy(question, strategy)
|
93 |
+
|
94 |
+
# Ajoute la réponse à l'historique
|
95 |
+
self.conversation_history.append({
|
96 |
+
"role": "assistant",
|
97 |
+
"content": response,
|
98 |
+
"strategy": strategy,
|
99 |
+
"timestamp": time.time()
|
100 |
+
})
|
101 |
+
|
102 |
print(f"✅ Réponse générée avec succ��s ({len(response)} caractères)")
|
103 |
return response
|
104 |
+
|
105 |
except Exception as e:
|
106 |
error_msg = f"❌ Erreur critique dans l'UltraAgent: {e}"
|
107 |
print(error_msg)
|
108 |
+
|
109 |
+
# Tentative de récupération avec le modèle de raisonnement
|
110 |
try:
|
111 |
print("🔄 Tentative de récupération avec le modèle de raisonnement...")
|
112 |
fallback_response = self._fallback_reasoning(question, str(e))
|
|
|
117 |
return final_error
|
118 |
|
119 |
def _analyze_question(self, question: str) -> Dict[str, Any]:
|
120 |
+
"""
|
121 |
+
Analyse la question pour déterminer la meilleure stratégie à adopter.
|
122 |
+
"""
|
123 |
question_lower = question.lower()
|
124 |
+
|
125 |
+
# Détection de mots-clés pour différents types de tâches
|
126 |
+
vision_keywords = ['image', 'photo', 'picture', 'visual', 'voir', 'regarder', 'analyser l\'image', 'screenshot', 'chess position', 'position']
|
127 |
code_keywords = ['code', 'program', 'script', 'algorithm', 'python', 'javascript', 'sql', 'debug', 'programmer']
|
128 |
+
search_keywords = ['search', 'find', 'look up', 'recherche', 'chercher', 'trouver', 'internet', 'web', 'actualité', 'wikipedia', 'youtube', 'studio albums']
|
129 |
+
file_keywords = ['file', 'document', 'pdf', 'excel', 'csv', 'json', 'read', 'fichier', 'lire', 'ouvrir']
|
130 |
+
reasoning_keywords = ['complex', 'analyze', 'think', 'reason', 'solve', 'problem', 'complexe', 'analyser', 'résoudre', 'problème']
|
131 |
+
|
132 |
+
# Calcul des scores
|
133 |
+
scores = {
|
134 |
+
"vision": sum(1 for kw in vision_keywords if kw in question_lower),
|
135 |
+
"code": sum(1 for kw in code_keywords if kw in question_lower),
|
136 |
+
"search_web": sum(1 for kw in search_keywords if kw in question_lower),
|
137 |
+
"file_processing": sum(1 for kw in file_keywords if kw in question_lower),
|
138 |
+
"reasoning": sum(1 for kw in reasoning_keywords if kw in question_lower),
|
139 |
+
}
|
140 |
+
|
141 |
+
# Détection d'URL pour forcer la recherche web
|
142 |
+
if any(domain in question_lower for domain in ['http', 'www', '.com', '.fr', '.org', 'youtube.com', 'wikipedia']):
|
143 |
+
scores["search_web"] += 2
|
144 |
+
|
145 |
+
# Analyse de la complexité
|
146 |
+
complexity = len(question.split()) + len(re.findall(r'[.!?]', question))
|
147 |
+
|
148 |
+
# Détermination de la stratégie
|
149 |
+
if scores["vision"] > 0:
|
150 |
+
return {"type": "vision", "priority": scores["vision"], "complexity": complexity}
|
151 |
+
if scores["code"] > 1:
|
152 |
+
return {"type": "code", "priority": scores["code"], "complexity": complexity}
|
153 |
+
if scores["search_web"] > 0:
|
154 |
+
return {"type": "search_web", "priority": scores["search_web"], "complexity": complexity}
|
155 |
+
if scores["file_processing"] > 0:
|
156 |
+
return {"type": "file_processing", "priority": scores["file_processing"], "complexity": complexity}
|
157 |
+
if complexity > 50 or scores["reasoning"] > 0:
|
158 |
+
return {"type": "reasoning", "priority": scores["reasoning"], "complexity": complexity}
|
159 |
+
|
160 |
+
return {"type": "general", "priority": 0, "complexity": complexity}
|
161 |
|
162 |
def _execute_strategy(self, question: str, strategy: Dict[str, Any]) -> str:
|
163 |
+
"""Exécute la stratégie déterminée pour répondre à la question."""
|
164 |
strategy_type = strategy["type"]
|
165 |
+
|
166 |
try:
|
167 |
if strategy_type == "vision":
|
168 |
return self._handle_vision_task(question)
|
169 |
elif strategy_type == "code":
|
170 |
return self._handle_code_task(question)
|
171 |
+
elif strategy_type == "search_web":
|
172 |
+
return self._handle_web_search_task(question)
|
173 |
+
elif strategy_type == "file_processing":
|
174 |
+
return self._handle_file_task(question)
|
175 |
+
elif strategy_type == "reasoning":
|
176 |
+
return self._handle_reasoning_task(question)
|
177 |
+
else: # general
|
178 |
return self._handle_general_task(question)
|
179 |
+
|
180 |
except Exception as e:
|
181 |
print(f"❌ Erreur dans l'exécution de la stratégie {strategy_type}: {e}")
|
182 |
+
# Fallback vers l'orchestrateur général en cas d'échec
|
183 |
+
return self._handle_general_task(question)
|
184 |
|
185 |
def _handle_vision_task(self, question: str) -> str:
|
186 |
+
"""Gère les tâches impliquant la vision."""
|
187 |
print("👁️ Traitement avec le modèle de vision...")
|
188 |
+
|
189 |
+
try:
|
190 |
+
vision_prompt = f"Analyse visuelle experte requise pour la question suivante : {question}. Si une image est mentionnée ou nécessaire, décris précisément ce que tu dois analyser."
|
191 |
+
|
192 |
+
# CORRECTION: Appel direct du modèle avec le prompt seulement
|
193 |
+
response = self.model_manager.get_vision_model()(vision_prompt)
|
194 |
+
return f"🔍 Analyse visuelle:\n{response}"
|
195 |
+
except Exception as e:
|
196 |
+
print(f"❌ Erreur vision: {e}")
|
197 |
+
return self._handle_general_task(question)
|
198 |
|
199 |
def _handle_code_task(self, question: str) -> str:
|
200 |
+
"""Gère les tâches de programmation avec l'agent de code."""
|
201 |
print("💻 Traitement avec l'agent de code...")
|
202 |
+
try:
|
203 |
+
response = self.code_agent.run(question)
|
204 |
+
return f"💻 Solution de code:\n{response}"
|
205 |
+
except Exception as e:
|
206 |
+
print(f"❌ Erreur code agent: {e}")
|
207 |
+
return self._handle_general_task(question)
|
208 |
+
|
209 |
+
def _handle_web_search_task(self, question: str) -> str:
|
210 |
+
"""Gère les tâches nécessitant une recherche web."""
|
211 |
+
print("🌐 Traitement avec recherche web...")
|
212 |
+
try:
|
213 |
+
search_prompt = f"Répondez à cette question en utilisant une recherche web approfondie. Utilisez les outils de recherche et de scraping, et citez vos sources. Question : {question}"
|
214 |
+
response = self.orchestrator.run(search_prompt)
|
215 |
+
return response
|
216 |
+
except Exception as e:
|
217 |
+
print(f"❌ Erreur web search: {e}")
|
218 |
+
return self._handle_general_task(question)
|
219 |
+
|
220 |
+
def _handle_file_task(self, question: str) -> str:
|
221 |
+
"""Gère les tâches impliquant la lecture de fichiers."""
|
222 |
+
print("📁 Traitement de fichiers...")
|
223 |
+
try:
|
224 |
+
file_prompt = f"Traitez les fichiers nécessaires pour répondre à cette question. Utilisez l'outil `read_file` pour analyser les fichiers mentionnés et extraire les informations pertinentes. Question : {question}"
|
225 |
+
response = self.orchestrator.run(file_prompt)
|
226 |
+
return response
|
227 |
+
except Exception as e:
|
228 |
+
print(f"❌ Erreur file processing: {e}")
|
229 |
+
return self._handle_general_task(question)
|
230 |
+
|
231 |
+
def _handle_reasoning_task(self, question: str) -> str:
|
232 |
+
"""Gère les tâches complexes nécessitant un raisonnement approfondi."""
|
233 |
+
print("🧠 Traitement avec le modèle de raisonnement...")
|
234 |
+
try:
|
235 |
+
reasoning_prompt = f"En tant qu'expert en résolution de problèmes complexes, analysez et répondez de manière structurée à la question suivante : {question}. Décomposez le problème, analysez chaque partie et proposez une solution étape par étape."
|
236 |
+
|
237 |
+
# CORRECTION: Appel direct du modèle avec le prompt seulement
|
238 |
+
response = self.model_manager.get_reasoning_model()(reasoning_prompt)
|
239 |
+
return f"🧠 Analyse approfondie:\n{response}"
|
240 |
+
except Exception as e:
|
241 |
+
print(f"❌ Erreur reasoning: {e}")
|
242 |
+
return self._handle_general_task(question)
|
243 |
|
244 |
def _handle_general_task(self, question: str) -> str:
|
245 |
+
"""Gère les tâches générales avec l'orchestrateur principal."""
|
246 |
print("🎯 Traitement général avec l'orchestrateur...")
|
247 |
+
try:
|
248 |
+
# CORRECTION: Utilisation directe du modèle orchestrateur au lieu de l'agent MultiStep
|
249 |
+
context = self._get_conversation_context()
|
250 |
+
enhanced_prompt = f"{context}\nQuestion actuelle: {question}\nInstructions: Analysez et répondez de manière complète à cette question."
|
251 |
+
|
252 |
+
# Appel direct du modèle au lieu de l'orchestrateur MultiStep
|
253 |
+
response = self.model_manager.get_orchestrator()(enhanced_prompt)
|
254 |
+
return response
|
255 |
+
except Exception as e:
|
256 |
+
print(f"❌ Erreur general task: {e}")
|
257 |
+
try:
|
258 |
+
# Dernière tentative simple
|
259 |
+
simple_response = self.model_manager.get_orchestrator()(question)
|
260 |
+
return simple_response
|
261 |
+
except Exception as final_e:
|
262 |
+
raise final_e # Propage l'erreur au handler principal
|
263 |
|
264 |
def _fallback_reasoning(self, question: str, error: str) -> str:
|
265 |
+
"""Utilise le modèle de raisonnement en cas d'erreur pour fournir une réponse de secours."""
|
266 |
+
try:
|
267 |
+
fallback_prompt = f"Une erreur s'est produite en traitant la question. Question: '{question}'. Erreur: '{error}'. Fournissez la meilleure réponse possible en expliquant les limitations dues à l'erreur."
|
268 |
+
|
269 |
+
# CORRECTION: Appel direct du modèle avec le prompt seulement
|
270 |
+
response = self.model_manager.get_reasoning_model()(fallback_prompt)
|
271 |
+
return f"⚠️ Réponse de récupération:\n{response}"
|
272 |
+
except Exception as e:
|
273 |
+
raise e
|
274 |
|
275 |
def _get_conversation_context(self, max_exchanges: int = 3) -> str:
|
276 |
+
"""Récupère le contexte des échanges récents pour l'injecter dans le prompt."""
|
277 |
+
if not self.conversation_history:
|
278 |
+
return ""
|
279 |
+
|
280 |
recent_history = self.conversation_history[-max_exchanges*2:]
|
281 |
context_parts = ["Contexte de la conversation récente:"]
|
282 |
for entry in recent_history:
|
283 |
role = "Utilisateur" if entry["role"] == "user" else "Assistant"
|
284 |
+
content = str(entry["content"])[:200] + "..." if len(str(entry["content"])) > 200 else str(entry["content"])
|
285 |
context_parts.append(f"{role}: {content}")
|
286 |
+
|
287 |
return "\n".join(context_parts)
|
288 |
|
289 |
+
def get_status(self) -> Dict[str, Any]:
|
290 |
+
"""Retourne le statut actuel de l'agent."""
|
291 |
+
return {
|
292 |
+
"status": "active",
|
293 |
+
"conversation_length": len(self.conversation_history),
|
294 |
+
"available_tools": [tool.__name__ for tool in self.tools if hasattr(tool, '__name__')],
|
295 |
+
"models_status": self.model_manager.test_models(),
|
296 |
+
}
|
297 |
+
|
298 |
+
def reset_conversation(self):
|
299 |
+
"""Réinitialise l'historique de la conversation."""
|
300 |
+
self.conversation_history = []
|
301 |
+
print("🔄 Historique de conversation remis à zéro.")
|
302 |
+
|
303 |
|
304 |
class BasicAgent:
|
305 |
"""
|