feat: integra Planning system nel ReActAgent
- run() crea e traccia task nel planner - run_plan() esegue sequenze di goal con progresso - decompose_goal() scompone goal complessi via LLM - Task tracciati con stato: pending->in_progress->completed/failed
This commit is contained in:
1 parent
1c7781a68f
commit
f847b92b0a
1 file changed
+151
-17
+151
-17
@@ -147,12 +147,13 @@ Goal: Qual è il contenuto del file?
|
||||
|
||||
RISPOSTA:"""
|
||||
|
||||
def run(self, goal: str) -> Dict[str, Any]:
|
||||
def run(self, goal: str, track_in_planner: bool = True) -> Dict[str, Any]:
|
||||
"""
|
||||
Esegue un goal attraverso il loop ReAct.
|
||||
|
||||
Args:
|
||||
goal: Goal da raggiungere
|
||||
track_in_planner: Se True, traccia il task nel planner
|
||||
|
||||
Returns:
|
||||
Dict con success, result, steps, iterations
|
||||
@@ -161,12 +162,25 @@ RISPOSTA:"""
|
||||
self.context = f"GOAL: {goal}\n"
|
||||
self.steps = []
|
||||
|
||||
# Traccia nel planner se richiesto
|
||||
task_id = None
|
||||
if track_in_planner and self.planner:
|
||||
task_id = self.planner.create_task(
|
||||
description=goal,
|
||||
priority=TaskPriority.MEDIUM
|
||||
)
|
||||
self.planner.start_task(task_id)
|
||||
if self.verbose:
|
||||
print(f"📋 Task creato: {task_id}")
|
||||
|
||||
if self.verbose:
|
||||
print(f"\n🎯 GOAL: {goal}")
|
||||
print("="*60)
|
||||
|
||||
# Esegui prima azione
|
||||
current_input = goal
|
||||
success = False
|
||||
final_result = ""
|
||||
|
||||
for iteration in range(1, self.max_iterations + 1):
|
||||
# 1. THINK - analizza input corrente
|
||||
@@ -180,11 +194,16 @@ RISPOSTA:"""
|
||||
if thought.strip().upper().startswith("SUCCESS"):
|
||||
result = thought[len("SUCCESS"):].strip()
|
||||
self._add_step(AgentState.DONE, thought)
|
||||
return self._build_result(True, result, f"Goal raggiunto in {iteration} step")
|
||||
success = True
|
||||
final_result = result
|
||||
break
|
||||
|
||||
if thought.strip().upper().startswith("FAIL"):
|
||||
reason = thought[len("FAIL:"):].strip()
|
||||
self._add_step(AgentState.FAILED, thought)
|
||||
final_result = self.context
|
||||
if task_id:
|
||||
self.planner.fail_task(task_id, reason)
|
||||
return self._build_result(False, self.context, reason)
|
||||
|
||||
# 2. ACT - parse e esegui tool
|
||||
@@ -200,26 +219,37 @@ RISPOSTA:"""
|
||||
self.context += f"\n{observation}"
|
||||
self._add_step(AgentState.OBSERVING, thought, action_result, observation)
|
||||
|
||||
# Se l'azione era un tool, verifica se il goal è raggiunto
|
||||
# Se l'azione era un tool, goal semplice raggiunto
|
||||
if action_result.action_name != "respond":
|
||||
# Goal semplice raggiunto - tool ha dato la risposta
|
||||
# Restituisci SUCCESS con il risultato
|
||||
self._add_step(AgentState.DONE, thought, action_result)
|
||||
return self._build_result(
|
||||
True,
|
||||
action_result.result,
|
||||
f"Goal raggiunto in {iteration} step con tool {action_result.action_name}"
|
||||
)
|
||||
success = True
|
||||
final_result = action_result.result
|
||||
break
|
||||
|
||||
# 4. EVALUATE - solo per risposte dirette
|
||||
self._add_step(AgentState.EVALUATING, thought, action_result, observation)
|
||||
current_input = f"Risultato: {action_result.result}\n\nGoal: {goal}\nGoal raggiunto?"
|
||||
|
||||
# Max iterations reached
|
||||
return self._build_result(
|
||||
False,
|
||||
self.context,
|
||||
f"Raggiunto massimo numero di iterazioni ({self.max_iterations})"
|
||||
)
|
||||
# Aggiorna planner
|
||||
if task_id:
|
||||
if success:
|
||||
self.planner.complete_task(task_id, final_result)
|
||||
if self.verbose:
|
||||
print(f"✅ Task {task_id} completato")
|
||||
else:
|
||||
self.planner.fail_task(task_id, "Max iterations reached")
|
||||
|
||||
if success:
|
||||
return self._build_result(
|
||||
True,
|
||||
final_result,
|
||||
f"Goal raggiunto in {iteration} step"
|
||||
)
|
||||
else:
|
||||
return self._build_result(
|
||||
False,
|
||||
self.context,
|
||||
f"Raggiunto massimo numero di iterazioni ({self.max_iterations})"
|
||||
)
|
||||
|
||||
def _think(self, goal: str) -> str:
|
||||
"""LLM decide la prossima azione basata su contesto e goal"""
|
||||
@@ -359,6 +389,110 @@ RISPOSTA:"""
|
||||
"total_tasks": len(self.planner.tasks),
|
||||
"plans": len(self.planner.plans)
|
||||
}
|
||||
|
||||
def run_plan(self, goals: List[str], plan_name: str = "multi_step_plan") -> Dict[str, Any]:
|
||||
"""
|
||||
Esegue un piano con più goal in sequenza.
|
||||
|
||||
Args:
|
||||
goals: Lista di goal da eseguire in ordine
|
||||
plan_name: Nome del piano
|
||||
|
||||
Returns:
|
||||
Dict con risultati per ogni goal e progresso complessivo
|
||||
"""
|
||||
# Crea piano nel planner
|
||||
plan_id = self.planner.create_plan(plan_name, goals)
|
||||
|
||||
if self.verbose:
|
||||
print(f"\n📋 PIANO: {plan_name}")
|
||||
print(f" Goal: {len(goals)}")
|
||||
print("="*60)
|
||||
|
||||
results = []
|
||||
completed = 0
|
||||
failed = 0
|
||||
|
||||
for i, goal in enumerate(goals):
|
||||
if self.verbose:
|
||||
print(f"\n--- Goal {i+1}/{len(goals)} ---")
|
||||
|
||||
result = self.run(goal, track_in_planner=True)
|
||||
results.append({
|
||||
"goal": goal,
|
||||
"success": result["success"],
|
||||
"result": result["result"][:200] if result["result"] else None,
|
||||
"error": result.get("error")
|
||||
})
|
||||
|
||||
if result["success"]:
|
||||
completed += 1
|
||||
else:
|
||||
failed += 1
|
||||
|
||||
# Risultato complessivo
|
||||
overall_success = failed == 0
|
||||
|
||||
if self.verbose:
|
||||
print(f"\n{'='*60}")
|
||||
print(f"📊 PIANO COMPLETATO")
|
||||
print(f" Totale: {len(goals)}")
|
||||
print(f" Completati: {completed}")
|
||||
print(f" Falliti: {failed}")
|
||||
|
||||
return {
|
||||
"success": overall_success,
|
||||
"plan_id": plan_id,
|
||||
"total_goals": len(goals),
|
||||
"completed": completed,
|
||||
"failed": failed,
|
||||
"results": results,
|
||||
"plan_progress": self.planner.get_plan_progress(plan_id)
|
||||
}
|
||||
|
||||
def decompose_goal(self, complex_goal: str) -> List[str]:
|
||||
"""
|
||||
Decompone un goal complesso in sotto-goal usando LLM.
|
||||
|
||||
Args:
|
||||
complex_goal: Goal complesso da decomporre
|
||||
|
||||
Returns:
|
||||
Lista di sotto-goal
|
||||
"""
|
||||
prompt = f"""Scomponi questo goal in passi sequenziali semplici.
|
||||
|
||||
Goal complesso: {complex_goal}
|
||||
|
||||
Elenca i passi necessari, uno per riga. Ogni passo deve essere un'azione concreta.
|
||||
Non usare numeri o bullet, solo testo puro.
|
||||
|
||||
ESEMPIO:
|
||||
Goal: Prepara report vendite mensile
|
||||
Output:
|
||||
Leggi i dati di vendita dal database
|
||||
Analizza i dati e calcola totali
|
||||
Genera il report in formato markdown
|
||||
Salva il report come report_mese.md"""
|
||||
|
||||
try:
|
||||
response = self.llm.invoke([HumanMessage(content=prompt)])
|
||||
content = response.content if hasattr(response, 'content') else str(response)
|
||||
|
||||
# Parse output - una linea per passo
|
||||
lines = [l.strip() for l in content.split('\n') if l.strip()]
|
||||
# Filtra linee vuote e possibili header
|
||||
steps = [l for l in lines if len(l) > 10 and not l.startswith('#')]
|
||||
|
||||
if self.verbose:
|
||||
print(f"\n🔬 Goal decomposed into {len(steps)} steps")
|
||||
|
||||
return steps
|
||||
except Exception as e:
|
||||
if self.verbose:
|
||||
print(f"⚠️ Decomposition failed: {e}")
|
||||
# Fallback: ritorna goal originale
|
||||
return [complex_goal]
|
||||
|
||||
|
||||
def create_react_agent(
|
||||
|
||||
Reference in new issue
Block a user