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:
Dom
2026-01-19 22:47:54 +01:00
parent 2922b6dda8
commit 7ea5d6b992
4 changed files with 2412 additions and 1 deletions

View File

@@ -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."""