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:
@@ -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."""
|
||||
|
||||
Reference in New Issue
Block a user