feat(agent_chat): Ajout des composants conversationnels

Nouveaux composants pour l'agent conversationnel :
- IntentParser: Analyse des intentions utilisateur (règles + LLM optionnel)
- ConfirmationLoop: Validation avant actions critiques (niveaux de risque)
- ResponseGenerator: Génération de réponses en langage naturel
- ConversationManager: Gestion du contexte multi-tour

Endpoint /api/chat ajouté pour le flux conversationnel complet.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Dom
2026-01-15 15:20:05 +01:00
parent c6a857b96b
commit bc096a3891
6 changed files with 2271 additions and 8 deletions

View File

@@ -1,14 +1,22 @@
#!/usr/bin/env python3
"""
RPA Vision V3 - Interface de Commande Web
RPA Vision V3 - Agent Chat
Interface web légère pour communiquer avec le système RPA.
Interface conversationnelle pour communiquer avec le système RPA.
Style "Spotlight/Alfred" - minimaliste et efficace.
Composants intégrés:
- IntentParser: Compréhension des intentions utilisateur
- ConfirmationLoop: Validation avant actions critiques
- ResponseGenerator: Réponses en langage naturel
- ConversationManager: Contexte multi-tour
Usage:
python command_interface/app.py
python agent_chat/app.py
Puis ouvrir: http://localhost:5002
Auteur: Dom - Janvier 2026
"""
import asyncio
@@ -27,6 +35,12 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
from core.workflow import SemanticMatcher, VariableManager
# Import des composants conversationnels
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
# GPU Resource Manager (optional)
try:
from core.gpu import get_gpu_resource_manager, ExecutionMode
@@ -44,6 +58,11 @@ socketio = SocketIO(app, cors_allowed_origins="*")
# Global state
matcher: Optional[SemanticMatcher] = None
gpu_manager = None
intent_parser: Optional[IntentParser] = None
confirmation_loop: Optional[ConfirmationLoop] = None
response_generator: Optional[ResponseGenerator] = None
conversation_manager: Optional[ConversationManager] = None
execution_status = {
"running": False,
"workflow": None,
@@ -57,7 +76,8 @@ command_history: List[Dict[str, Any]] = []
def init_system():
"""Initialiser tous les composants du système."""
global matcher, gpu_manager
global intent_parser, confirmation_loop, response_generator, conversation_manager
# 1. SemanticMatcher
try:
matcher = SemanticMatcher("data/workflows")
@@ -65,7 +85,7 @@ def init_system():
except Exception as e:
logger.error(f"✗ SemanticMatcher: {e}")
matcher = None
# 2. GPU Resource Manager
if GPU_AVAILABLE:
try:
@@ -75,6 +95,21 @@ def init_system():
logger.warning(f"⚠ GPU Resource Manager: {e}")
gpu_manager = None
# 3. Composants conversationnels
try:
intent_parser = get_intent_parser(use_llm=False) # LLM optionnel
confirmation_loop = get_confirmation_loop()
response_generator = get_response_generator()
conversation_manager = get_conversation_manager()
logger.info("✓ Composants conversationnels initialisés")
except Exception as e:
logger.error(f"✗ Composants conversationnels: {e}")
# Fallback aux composants de base
intent_parser = IntentParser(use_llm=False)
confirmation_loop = ConfirmationLoop()
response_generator = ResponseGenerator()
conversation_manager = ConversationManager()
# =============================================================================
# Routes Web
@@ -241,6 +276,192 @@ def api_history():
return jsonify({"history": command_history[-20:]})
@app.route('/api/chat', methods=['POST'])
def api_chat():
"""
Endpoint conversationnel principal.
Utilise le flux complet:
1. IntentParser: Analyse l'intention
2. ConversationManager: Gère le contexte multi-tour
3. ConfirmationLoop: Valide les actions sensibles
4. ResponseGenerator: Génère la réponse
"""
data = request.json
message = data.get('message', '').strip()
session_id = data.get('session_id')
if not message:
return jsonify({"error": "Message vide"}), 400
# 1. Obtenir ou créer la session
session = conversation_manager.get_or_create_session(session_id=session_id)
# 2. Parser l'intention
intent = intent_parser.parse(message)
# 3. Résoudre les références anaphoriques (ex: "le même", "celui-ci")
intent = conversation_manager.resolve_references(session, intent)
# 4. Construire le contexte
context = conversation_manager.get_context_summary(session)
context["execution_status"] = execution_status
# 5. Traiter selon le type d'intention
result = {}
action_taken = None
if intent.intent_type == IntentType.CONFIRM:
# Confirmer une action en attente
pending = conversation_manager.get_pending_confirmation(session)
if pending:
confirmation_loop.confirm(pending.id)
conversation_manager.clear_pending_confirmation(session)
result = {"confirmed": True, "workflow": pending.workflow_name}
action_taken = "confirmed"
# Lancer l'exécution
socketio.start_background_task(
execute_workflow_from_confirmation, pending, session.session_id
)
else:
result = {"confirmed": False}
elif intent.intent_type == IntentType.DENY:
# Refuser une action en attente
pending = conversation_manager.get_pending_confirmation(session)
if pending:
confirmation_loop.deny(pending.id)
conversation_manager.clear_pending_confirmation(session)
result = {"denied": True}
action_taken = "denied"
elif intent.intent_type == IntentType.EXECUTE:
# Exécuter un workflow
if matcher and intent.workflow_hint:
match = matcher.find_workflow(intent.workflow_hint, min_confidence=0.2)
if match:
# Évaluer le risque
risk = confirmation_loop.evaluate_risk(
match.workflow_name,
{**match.extracted_params, **intent.parameters}
)
if confirmation_loop.requires_confirmation(risk):
# Créer une demande de confirmation
conf = confirmation_loop.create_confirmation_request(
workflow_name=match.workflow_name,
parameters={**match.extracted_params, **intent.parameters},
action_type="execute",
risk_level=risk
)
conversation_manager.set_pending_confirmation(session, conf)
# Générer la réponse de confirmation
response = response_generator.generate_confirmation_request(conf)
result = {"needs_confirmation": True, "confirmation": conf.to_dict()}
action_taken = "confirmation_requested"
else:
# Exécuter directement
all_params = {**match.extracted_params, **intent.parameters}
result = {
"success": True,
"workflow": match.workflow_name,
"params": all_params,
"confidence": match.confidence
}
action_taken = "executed"
socketio.start_background_task(execute_workflow, match, all_params)
else:
result = {"not_found": True, "query": intent.workflow_hint}
else:
result = {"error": "Pas de workflow spécifié"}
elif intent.intent_type == IntentType.LIST:
# Lister les workflows
if matcher:
workflows = [
{"name": wf.name, "description": wf.description}
for wf in matcher.get_all_workflows()
]
result = {"workflows": workflows}
else:
result = {"workflows": []}
action_taken = "listed"
elif intent.intent_type == IntentType.STATUS:
result = {"execution": execution_status}
action_taken = "status_checked"
elif intent.intent_type == IntentType.CANCEL:
if execution_status.get("running"):
execution_status["running"] = False
execution_status["message"] = "Annulé"
result = {"cancelled": True}
else:
result = {"cancelled": False}
action_taken = "cancelled"
elif intent.intent_type == IntentType.HISTORY:
result = {"history": command_history[-10:]}
action_taken = "history_shown"
elif intent.intent_type == IntentType.HELP:
result = {}
action_taken = "help_shown"
elif intent.clarification_needed:
result = {"clarification_needed": True}
action_taken = "clarification_requested"
# 6. Générer la réponse (si pas déjà fait pour confirmation)
if action_taken != "confirmation_requested":
response = response_generator.generate(intent, context, result)
# 7. Enregistrer le tour dans la conversation
conversation_manager.add_turn(
session=session,
user_message=message,
intent=intent,
response=response.message,
action_taken=action_taken,
result=result
)
# 8. Retourner la réponse
return jsonify({
"session_id": session.session_id,
"intent": intent.to_dict(),
"response": response.to_dict(),
"result": result,
"context": {
"current_workflow": session.context.current_workflow,
"has_pending_confirmation": session.context.pending_confirmation is not None
}
})
def execute_workflow_from_confirmation(confirmation, session_id):
"""Exécuter un workflow après confirmation."""
global execution_status
if not matcher:
return
# Trouver le workflow
match = matcher.find_workflow(confirmation.workflow_name, min_confidence=0.1)
if not match:
return
# Utiliser les paramètres confirmés (ou modifiés)
params = confirmation.modified_parameters or confirmation.parameters
execute_workflow(match, params)
@app.route('/api/gpu/<action>', methods=['POST'])
def api_gpu_action(action):
"""Contrôler le GPU Resource Manager."""