feat(agent_chat): Ajouter mode Agent Libre avec planification LLM
- Nouveau module autonomous_planner.py pour planification intelligente - Utilise Qwen via Ollama pour décomposer les tâches en actions - Actions supportées: open_url, click, type_text, hotkey, scroll, wait - Intégration OWL-v2 et VLM pour détection visuelle intelligente - Nouvelle interface chat conversationnelle (chat.html) - Prompt LLM générique adaptable à toute demande - Endpoints API: /api/agent/plan, /api/agent/execute, /api/agent/status Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,7 @@ from .intent_parser import IntentParser, IntentType, get_intent_parser
|
||||
from .confirmation import ConfirmationLoop, ConfirmationStatus, RiskLevel, get_confirmation_loop
|
||||
from .response_generator import ResponseGenerator, get_response_generator
|
||||
from .conversation_manager import ConversationManager, get_conversation_manager
|
||||
from .autonomous_planner import AutonomousPlanner, get_autonomous_planner, ExecutionPlan
|
||||
|
||||
# GPU Resource Manager (optional)
|
||||
try:
|
||||
@@ -74,6 +75,7 @@ intent_parser: Optional[IntentParser] = None
|
||||
confirmation_loop: Optional[ConfirmationLoop] = None
|
||||
response_generator: Optional[ResponseGenerator] = None
|
||||
conversation_manager: Optional[ConversationManager] = None
|
||||
autonomous_planner: Optional[AutonomousPlanner] = None
|
||||
|
||||
# Execution components
|
||||
workflow_pipeline = None
|
||||
@@ -95,6 +97,7 @@ def init_system():
|
||||
"""Initialiser tous les composants du système."""
|
||||
global matcher, gpu_manager
|
||||
global intent_parser, confirmation_loop, response_generator, conversation_manager
|
||||
global autonomous_planner
|
||||
|
||||
# 1. SemanticMatcher
|
||||
try:
|
||||
@@ -178,6 +181,24 @@ def init_system():
|
||||
else:
|
||||
logger.info("ℹ Mode simulation (composants d'exécution non disponibles)")
|
||||
|
||||
# 5. Autonomous Planner (Agent Libre)
|
||||
try:
|
||||
autonomous_planner = get_autonomous_planner(llm_model="qwen2.5:7b")
|
||||
|
||||
# Configurer les callbacks pour l'exécution
|
||||
if screen_capturer:
|
||||
autonomous_planner.set_screen_capturer(screen_capturer.capture)
|
||||
|
||||
def progress_callback(data):
|
||||
socketio.emit('agent_progress', data)
|
||||
|
||||
autonomous_planner.set_progress_callback(progress_callback)
|
||||
|
||||
logger.info(f"✓ AutonomousPlanner initialisé (LLM: {autonomous_planner.llm_available})")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠ AutonomousPlanner: {e}")
|
||||
autonomous_planner = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Routes Web
|
||||
@@ -185,7 +206,13 @@ def init_system():
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""Page principale."""
|
||||
"""Page principale - nouvelle interface chat."""
|
||||
return render_template('chat.html')
|
||||
|
||||
|
||||
@app.route('/classic')
|
||||
def classic():
|
||||
"""Ancienne interface (fallback)."""
|
||||
return render_template('command.html')
|
||||
|
||||
|
||||
@@ -614,6 +641,174 @@ def api_llm_set_model():
|
||||
return jsonify({"success": False, "error": "IntentParser non initialisé"})
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# API Agent Libre (Autonomous Mode)
|
||||
# =============================================================================
|
||||
|
||||
@app.route('/api/agent/plan', methods=['POST'])
|
||||
def api_agent_plan():
|
||||
"""
|
||||
Génère un plan d'exécution pour une tâche en langage naturel.
|
||||
|
||||
Le mode "Agent Libre" permet d'exécuter des tâches sans workflow pré-enregistré.
|
||||
Le LLM (Qwen) décompose la demande en étapes d'actions.
|
||||
"""
|
||||
if not autonomous_planner:
|
||||
return jsonify({"error": "Agent autonome non disponible"}), 503
|
||||
|
||||
data = request.json
|
||||
user_request = data.get('request', '').strip()
|
||||
|
||||
if not user_request:
|
||||
return jsonify({"error": "Requête vide"}), 400
|
||||
|
||||
try:
|
||||
# Contexte optionnel (écran actuel, etc.)
|
||||
context = data.get('context', {})
|
||||
|
||||
# Générer le plan
|
||||
plan = autonomous_planner.plan(user_request, context)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"plan": {
|
||||
"task": plan.task_description,
|
||||
"steps": [
|
||||
{
|
||||
"step": s.step_number,
|
||||
"action": s.action_type.value,
|
||||
"description": s.description,
|
||||
"target": s.target,
|
||||
"params": s.parameters,
|
||||
"expected_result": s.expected_result
|
||||
}
|
||||
for s in plan.steps
|
||||
],
|
||||
"estimated_seconds": plan.estimated_duration_seconds,
|
||||
"risk_level": plan.risk_level,
|
||||
"requires_confirmation": plan.requires_confirmation
|
||||
},
|
||||
"llm_available": autonomous_planner.llm_available
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Agent plan error: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/agent/execute', methods=['POST'])
|
||||
def api_agent_execute():
|
||||
"""
|
||||
Exécute un plan d'agent autonome.
|
||||
|
||||
Attend un objet plan (généré par /api/agent/plan) et l'exécute étape par étape.
|
||||
"""
|
||||
if not autonomous_planner:
|
||||
return jsonify({"error": "Agent autonome non disponible"}), 503
|
||||
|
||||
data = request.json
|
||||
plan_data = data.get('plan')
|
||||
|
||||
if not plan_data:
|
||||
return jsonify({"error": "Plan manquant"}), 400
|
||||
|
||||
try:
|
||||
# Reconstruire le plan depuis les données
|
||||
from .autonomous_planner import PlannedAction, ActionType
|
||||
|
||||
steps = []
|
||||
for step_data in plan_data.get('steps', []):
|
||||
action_type_str = step_data.get('action', 'click')
|
||||
action_type_map = {
|
||||
'open_app': ActionType.OPEN_APP,
|
||||
'open_url': ActionType.OPEN_URL,
|
||||
'click': ActionType.CLICK,
|
||||
'type_text': ActionType.TYPE_TEXT,
|
||||
'hotkey': ActionType.HOTKEY,
|
||||
'scroll': ActionType.SCROLL,
|
||||
'wait': ActionType.WAIT,
|
||||
'screenshot': ActionType.SCREENSHOT
|
||||
}
|
||||
|
||||
steps.append(PlannedAction(
|
||||
step_number=step_data.get('step', len(steps) + 1),
|
||||
action_type=action_type_map.get(action_type_str, ActionType.CLICK),
|
||||
description=step_data.get('description', ''),
|
||||
target=step_data.get('target'),
|
||||
parameters=step_data.get('params', {}),
|
||||
expected_result=step_data.get('expected_result')
|
||||
))
|
||||
|
||||
plan = ExecutionPlan(
|
||||
task_description=plan_data.get('task', ''),
|
||||
steps=steps,
|
||||
estimated_duration_seconds=plan_data.get('estimated_seconds', 30),
|
||||
risk_level=plan_data.get('risk_level', 'low')
|
||||
)
|
||||
|
||||
# Exécuter en arrière-plan
|
||||
socketio.start_background_task(execute_agent_plan, plan)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "Exécution démarrée",
|
||||
"steps_count": len(steps)
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Agent execute error: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/agent/status')
|
||||
def api_agent_status():
|
||||
"""Statut de l'agent autonome."""
|
||||
return jsonify({
|
||||
"available": autonomous_planner is not None,
|
||||
"llm_available": autonomous_planner.llm_available if autonomous_planner else False,
|
||||
"llm_model": autonomous_planner.llm_model if autonomous_planner else None
|
||||
})
|
||||
|
||||
|
||||
def execute_agent_plan(plan: ExecutionPlan):
|
||||
"""Exécute un plan d'agent en arrière-plan."""
|
||||
import asyncio
|
||||
|
||||
try:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
results = loop.run_until_complete(autonomous_planner.execute_plan(plan))
|
||||
|
||||
loop.close()
|
||||
|
||||
# Envoyer le résultat final
|
||||
success_count = sum(1 for r in results if r.success)
|
||||
total = len(results)
|
||||
|
||||
socketio.emit('execution_completed', {
|
||||
"success": success_count == total,
|
||||
"workflow": plan.task_description,
|
||||
"message": f"{success_count}/{total} étapes réussies",
|
||||
"results": [
|
||||
{
|
||||
"step": r.action.step_number,
|
||||
"success": r.success,
|
||||
"message": r.message
|
||||
}
|
||||
for r in results
|
||||
]
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Agent execution error: {e}")
|
||||
socketio.emit('execution_completed', {
|
||||
"success": False,
|
||||
"workflow": plan.task_description,
|
||||
"message": f"Erreur: {str(e)}"
|
||||
})
|
||||
|
||||
|
||||
@app.route('/api/help')
|
||||
def api_help():
|
||||
"""Aide et mode d'emploi."""
|
||||
|
||||
Reference in New Issue
Block a user