Spaces:
Sleeping
Sleeping
| # core/research_loop.py | |
| import re | |
| import time | |
| import uuid | |
| from typing import Dict, Any | |
| from core.memory import MemoryTier | |
| class ResearchLoop: | |
| def __init__(self, llm_engine, memory, executor): | |
| self.llm = llm_engine | |
| self.memory = memory | |
| self.executor = executor # execute_safe | |
| self.error_tree = None # sera injecté plus tard (via app.py) | |
| self.publications = [] | |
| def set_error_tree(self, error_tree): | |
| self.error_tree = error_tree | |
| async def generate_hypothesis(self, domain: str) -> str: | |
| if not self.llm: | |
| return f"L'utilisation d'un cache LRU réduit le temps d'exécution de 20% pour des appels répétés dans {domain}." | |
| prompt = ( | |
| f"Propose une hypothèse précise et testable sur l'optimisation de {domain}. " | |
| "Elle doit être formulée en une phrase et contenir un indicateur quantifiable (ex: 'réduit de X%', 'accélère de Y ms'). " | |
| "Exemple : 'L'indexation des colonnes fréquemment jointes réduit le temps de requête SQL de 30%'. " | |
| "Réponse (une phrase) :" | |
| ) | |
| try: | |
| resp = await self.llm.call( | |
| agent="research", | |
| system="Tu es un chercheur en optimisation.", | |
| user=prompt, | |
| max_tokens=80, | |
| temperature=0.3, | |
| use_cache=False | |
| ) | |
| raw = resp.content if hasattr(resp, "content") else str(resp) | |
| raw = raw.strip().split('\n')[0] | |
| raw = re.sub(r'^(Answer|Hypothesis|Réponse|Hypothèse):\s*', '', raw, flags=re.IGNORECASE) | |
| if len(raw) < 10: | |
| return f"L'optimisation de {domain} améliore les performances de 15%." | |
| return raw | |
| except Exception as e: | |
| # 🔥 Enregistrer l'échec de génération dans Harbor | |
| if self.error_tree is not None: | |
| try: | |
| self.error_tree.add_error(str(e), f"ResearchLoop generate_hypothesis domain={domain}") | |
| except Exception: | |
| pass | |
| return f"L'optimisation de {domain} améliore les performances de 15%." | |
| async def run_experiment(self, hypothesis: str) -> float: | |
| try: | |
| # 1. Générer un code de test basé sur l'hypothèse | |
| code_prompt = ( | |
| f"Écris un court code Python qui teste l'hypothèse suivante : {hypothesis}\n" | |
| "Le code doit définir une fonction `run_test()` qui retourne un score entre 0 et 1 " | |
| "(0 = échec, 1 = succès parfait). Exemple :\n" | |
| "def run_test():\n" | |
| " # ... test ...\n" | |
| " return 0.85\n" | |
| "Ne retourne que le code, sans commentaires." | |
| ) | |
| resp = await self.llm.call( | |
| agent="research", | |
| system="Tu es un ingénieur de test.", | |
| user=code_prompt, | |
| max_tokens=200, | |
| temperature=0.2, | |
| use_cache=False | |
| ) | |
| code = resp.content if hasattr(resp, "content") else str(resp) | |
| code = re.sub(r'```(?:python)?\n?', '', code).replace('```', '').strip() | |
| # 2. Exécuter le code dans le sandbox | |
| result = self.executor(code, timeout=10) | |
| if not result.get("success"): | |
| if self.error_tree is not None: | |
| try: | |
| self.error_tree.add_error( | |
| f"Exécution échouée: {result.get('error', '')}", | |
| f"Hypothèse: {hypothesis[:100]}" | |
| ) | |
| except Exception: | |
| pass | |
| return 0.0 | |
| # 3. Récupérer la sortie | |
| output = result.get("result", "").strip() | |
| match = re.search(r'(\d+\.?\d*)', output) | |
| if match: | |
| score = float(match.group(1)) | |
| score = max(0.0, min(1.0, score)) | |
| return score | |
| else: | |
| return 0.0 | |
| except Exception as e: | |
| if self.error_tree is not None: | |
| try: | |
| self.error_tree.add_error(str(e), f"ResearchLoop run_experiment") | |
| except Exception: | |
| pass | |
| return 0.0 | |
| async def publish(self, hypothesis: str, score: float): | |
| pub = { | |
| "id": str(uuid.uuid4())[:8], | |
| "hypothesis": hypothesis, | |
| "score": round(score, 3), | |
| "timestamp": time.time() | |
| } | |
| self.publications.append(pub) | |
| self.memory.ingest( | |
| f"Research: {hypothesis[:150]} (score={score:.3f})", | |
| MemoryTier.SEMANTIC, | |
| "research_loop", | |
| importance=score, | |
| confidence=score | |
| ) | |
| async def run_one_cycle(self, domain: str) -> Dict[str, Any]: | |
| hypothesis = await self.generate_hypothesis(domain) | |
| score = await self.run_experiment(hypothesis) | |
| await self.publish(hypothesis, score) | |
| return { | |
| "hypothesis": hypothesis, | |
| "score": round(score, 3), | |
| "domain": domain | |
| } |