Compétence d'Optimisation des Performances Dify
Aperçu
Cette compétence fournit un guide complet pour l'optimisation des performances des workflows et plugins Dify, couvrant l'optimisation de la structure des graphes, le contrôle des coûts LLM, le réglage du pool de workers, l'optimisation du pool de variables, le traitement parallèle et la surveillance des performances.
Scénarios d'application
Utilisez cette compétence lorsque vous rencontrez les situations suivantes :
- ⏱️ Le temps d'exécution du workflow est trop long, nécessitant une optimisation
- 💰 Les coûts d'API LLM sont trop élevés, nécessitant une réduction de la consommation de tokens
- 🚀 La capacité de traitement parallèle est insuffisante, nécessitant une augmentation du débit
- 📊 L'utilisation des ressources système est faible, nécessitant une optimisation de la configuration
- 📈 Goulots d'étranglement de performance pour le traitement de données à grande échelle
- 🔍 Besoin de diagnostic et de surveillance des performances
Valeur fondamentale
- Effets d'optimisation quantifiables : Réduction de 40 à 60 % du temps d'exécution des branches parallèles, réduction de 30 à 50 % des appels LLM grâce au cache
- Basé sur des cas concrets : 5 cas d'optimisation réels avec comparaison avant/après
- Stratégies d'optimisation complètes : Couvre tous les niveaux (workflow, LLM, plugins, surveillance)
- Outils de diagnostic rapide : Liste de contrôle de diagnostic sur 5 dimensions
Liste de contrôle de diagnostic rapide
Avant de commencer l'optimisation, utilisez la liste suivante pour localiser rapidement les problèmes de performance :
📊 Diagnostic de la structure du graphe
- Existe-t-il trop de dépendances séquentielles ?
- Est-il possible d'utiliser des branches parallèles ? (v0.8.0+)
- Y a-t-il des nœuds et connexions inutiles ?
- Le nombre de nœuds dépasse-t-il 50 ?
- Utilisez-vous un modèle de conception approprié ?
🤖 Diagnostic d'utilisation LLM
- Le choix du modèle est-il adapté ? (tâches légères avec modèles légers)
- Le prompt est-il trop long ? (peut-il être simplifié ?)
- Le cache de réponse LLM est-il activé ?
- Utilisez-vous une sortie structurée ? (JSON Schema)
- La consommation de tokens est-elle dans le budget ?
⚙️ Diagnostic du pool de workers
- La configuration du nombre de workers est-elle raisonnable ? (2-8 threads)
- La profondeur de la file d'attente est-elle trop grande ?
- Y a-t-il du gaspillage de workers inactifs ?
- La stratégie de mise à l'échelle est-elle raisonnable ?
💾 Diagnostic du pool de variables
- Stockez-vous de grands objets dans le pool de variables ?
- Les variables sont-elles nettoyées rapidement ?
- Utilisez-vous des références plutôt que des copies ?
- La portée des variables est-elle raisonnable ?
🔄 Diagnostic du traitement parallèle
- L'itérateur est-il configuré avec parallel_nums ?
- Utilisez-vous la réponse en streaming ?
- Les tâches asynchrones sont-elles correctement configurées ?
- Existe-t-il des opérations bloquantes ?
Première partie : Optimisation de la structure du graphe
1.1 Réduire les dépendances séquentielles
Problème : Trop de dépendances séquentielles entraînent une croissance linéaire du temps d'exécution du workflow.
Stratégie d'optimisation :
- Identifier les nœuds parallélisables : Analyser les dépendances entre nœuds pour trouver ceux qui peuvent être exécutés en parallèle
- Utiliser des branches parallèles : Dify v0.8.0+ supporte les branches parallèles
- Reconstruire le workflow : Transformer les flux séquentiels en mode parallèle + agrégation
Exemple :
# Avant optimisation : Exécution séquentielle (temps total = 3 + 2 + 4 = 9 secondes)
Début → Appel API 1 (3s) → Appel API 2 (2s) → Appel API 3 (4s) → Fin
# Après optimisation : Exécution parallèle (temps total = max(3,2,4) = 4 secondes)
Début → [Appel API 1 (3s), Appel API 2 (2s), Appel API 3 (4s)] → Agrégation de variables → Fin
Effet d'optimisation : Réduction du temps d'exécution de 40 à 60 %
1.2 Fonctionnalité de branches parallèles (v0.8.0+)
Dify v0.8.0 introduit la fonctionnalité de branches parallèles, permettant à plusieurs branches de s'exécuter simultanément.
Utilisation :
- Créer plusieurs branches dans l'éditeur de workflow
- S'assurer qu'il n'y a pas de dépendances de données entre les branches
- Utiliser un agrégateur de variables pour collecter les résultats
Scénarios d'application :
- Appels parallèles de plusieurs nœuds HTTP Request
- Exécution parallèle de plusieurs nœuds Tool
- Traitement parallèle de la récupération de base de connaissances + appel API
- Inférence parallèle multi-modèles (scénario de comparaison de modèles)
Exemple de configuration :
# Configuration des branches parallèles
parallel_branches:
- branch_1:
nodes: [http_request_1, code_1]
- branch_2:
nodes: [http_request_2, code_2]
- branch_3:
nodes: [tool_1, llm_1]
1.3 Éviter les nœuds inutiles
Problèmes courants :
- Nœuds de transformation de données redondants
- Nœuds de variables intermédiaires inutiles
- Nœuds de conditions excessifs
Recommandations d'optimisation :
- Fusionner les logiques de traitement similaires
- Utiliser des nœuds de code pour remplacer plusieurs nœuds simples
- Simplifier la logique des conditions
1.4 Modèles de conception de la structure du graphe
Choisir le modèle de conception approprié en fonction du type de tâche :
- Pipeline séquentiel : Début → LLM → Fin (tâches simples)
- Branche conditionnelle : Début → LLM → Si-Sinon → Chemins multiples (tâches de décision)
- Traitement itératif : Début → Code → Itération → LLM (tâches par lots)
- Récupération de connaissances : Début → Récupération de connaissances → LLM (applications RAG)
- Agrégation multi-sources : Plusieurs Tool/HTTP → Agrégateur de variables → LLM (agrégation de données)
Documentation détaillée : Voir workflow-optimization/graph-structure.md
Deuxième partie : Stratégies d'optimisation LLM
2.1 Stratégie de sélection de modèle
Le choix du modèle est crucial pour l'optimisation des coûts.
Matrice de sélection de modèle :
| Type de tâche | Modèle recommandé | Coût en tokens | Scénario d'application |
|---|---|---|---|
| Classification simple | Qwen-Turbo, Gemini Flash | Faible | Classification de texte, analyse de sentiments |
| Génération de texte | GPT-3.5-turbo, Qwen-Plus | Moyen | Génération de contenu, résumé |
| Raisonnement complexe | GPT-4, Claude Opus | Élevé | Analyse complexe, décision |
| Déploiement local | Ollama (Llama 3, Qwen) | Coût API nul | Scénarios sensibles à la vie privée |
Comparaison des coûts :
- GPT-4 : 0,03 $/1K tokens (entrée) + 0,06 $/1K tokens (sortie)
- GPT-3.5-turbo : 0,0015 $/1K tokens (entrée) + 0,002 $/1K tokens (sortie)
- Qwen-Turbo : 0,0008 ¥/1K tokens (environ 20 fois la différence de coût)
Recommandations d'optimisation :
- Utiliser des modèles légers pour les tâches légères (Qwen/Gemini)
- Utiliser des modèles puissants pour les tâches complexes (GPT-4/Claude)
- Envisager d'utiliser des modèles locaux (Ollama) pour réduire les coûts
2.2 Techniques d'optimisation des prompts
Simplifier le prompt :
# Avant optimisation (150 tokens)
"""
Vous êtes un assistant de service client professionnel. Veuillez lire attentivement la question client ci-dessous et, en fonction de notre base de connaissances produits, fournir une réponse détaillée, précise et amicale. Dans votre réponse, veuillez noter les points suivants :
1. Gardez un ton professionnel et poli
2. Fournissez des solutions spécifiques
3. Si vous n'êtes pas sûr, indiquez-le
4. Citez la documentation produit pertinente
Question client : {query}
"""
# Après optimisation (50 tokens)
"""
En tant qu'assistant de service client, répondez à la question client en fonction de la base de connaissances, en restant professionnel et poli.
Question : {query}
"""
Économie de tokens : 66 % (150 → 50 tokens)
Autres techniques d'optimisation :
- Supprimer les explications et exemples redondants
- Utiliser des instructions concises
- Éviter les contextes répétés
- Utiliser une sortie structurée pour réduire les coûts d'analyse
2.3 Stratégie de cache
Cache de réponse LLM :
Utiliser Redis pour mettre en cache les réponses LLM, évitant les appels répétés.
Implémentation :
import hashlib
import redis
# Connexion à Redis
r = redis.Redis(host='localhost', port=6379, db=0)
def get_llm_response_with_cache(prompt, model="gpt-3.5-turbo"):
# Générer la clé de cache
cache_key = f"llm:{model}:{hashlib.md5(prompt.encode()).hexdigest()}"
# Vérifier le cache
cached = r.get(cache_key)
if cached:
return cached.decode('utf-8')
# Appeler le LLM
response = call_llm_api(prompt, model)
# Stocker le cache (TTL : 1 heure)
r.setex(cache_key, 3600, response)
return response
Stratégies de cache :
- Cache de réponse LLM : Même prompt retourne le résultat en cache
- Cache de récupération de base de connaissances : Même requête retourne les vecteurs en cache
- Cache de résultat de nœud : Évite l'exécution répétée du même nœud
Effet d'optimisation : Réduction de 30 à 50 % des appels API
2.4 Sortie structurée
Utiliser JSON Schema pour garantir la stabilité de la sortie LLM, réduisant les erreurs d'analyse et les tentatives.
Exemple de configuration :
{
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["Technique", "Commercial", "Après-vente"]
},
"priority": {
"type": "integer",
"minimum": 1,
"maximum": 5
},
"summary": {
"type": "string",
"maxLength": 100
}
},
"required": ["category", "priority", "summary"]
}
Effet d'optimisation :
- Réduction du nombre de tokens de sortie
- Évite les erreurs d'analyse et les tentatives
- Améliore l'efficacité du traitement des nœuds en aval
Documentation détaillée : Voir llm-optimization/
Troisième partie : Réglage du pool de workers
3.1 Pool de workers dynamique
Dify utilise un pool de workers dynamique pour exécuter les nœuds en parallèle, avec une configuration par défaut de 2 à 8 threads de travail.
Architecture du pool de workers :
GraphEngine
├── ReadyQueue (File d'attente des nœuds prêts)
├── EventQueue (File d'attente des événements)
├── Dispatcher (Distributeur d'événements)
└── WorkerPool (2-8 threads de travail)
├── Worker 1
├── Worker 2
├── ...
└── Worker N
3.2 Paramètres de configuration
Configuration clé :
| Paramètre | Valeur par défaut | Description | Recommandation d'optimisation |
|---|---|---|---|
| min_workers | 2 | Nombre minimal de workers | Définir en fonction des besoins de concurrence minimale |
| max_workers | 8 | Nombre maximal de workers | Définir en fonction du nombre de cœurs CPU |
| queue_depth_threshold | 10 | Seuil de déclenchement de l'expansion | Ajuster en fonction de la profondeur moyenne de la file d'attente |
| idle_timeout | 60s | Temps d'inactivité avant suppression d'un worker | Ajuster en fonction de la fréquence des tâches |
Stratégie de mise à l'échelle :
# Condition d'expansion
if queue_depth > queue_depth_threshold and workers < max_workers:
add_worker()
# Condition de réduction
if worker_idle_time > idle_timeout and workers > min_workers:
remove_worker()
3.3 Référence de performance
Scénario de test : Workflow de 100 nœuds
| Nombre de workers | Temps d'exécution | Débit | Utilisation CPU |
|---|---|---|---|
| 2 | 45s | 2,2 nœuds/s | 40 % |
| 4 | 25s | 4,0 nœuds/s | 70 % |
| 8 | 15s | 6,7 nœuds/s | 95 % |
Effet d'optimisation : Passage de 2 à 8 workers, amélioration du débit de 3x
3.4 Recommandations d'optimisation
- Définir max_workers en fonction du nombre de cœurs CPU : Généralement 1 à 2 fois le nombre de cœurs CPU
- Surveiller la profondeur de la file d'attente : Si la profondeur reste élevée, augmenter max_workers
- Éviter une expansion excessive : Trop de workers entraîne des frais généraux de changement de contexte
- Utiliser des E/S asynchrones : Pour les tâches intensives en E/S, utiliser le traitement asynchrone pour améliorer l'efficacité
Documentation détaillée : Voir workflow-optimization/parallel-execution.md
Quatrième partie : Optimisation du pool de variables
4.1 Éviter de stocker de grands objets
Problème : Stocker de grands objets (comme des fichiers volumineux, de longs textes) dans le pool de variables consomme beaucoup de mémoire.
Stratégie d'optimisation :
# ❌ Non recommandé : Stocker de grands objets
variables = {
"large_file": open("large_file.pdf", "rb").read(), # 10 Mo
"long_text": "..." * 100000 # 1 Mo
}
# ✅ Recommandé : Stocker une référence
variables = {
"file_path": "/chemin/vers/large_file.pdf",
"text_summary": resume(long_text) # Stocker uniquement le résumé
}
4.2 Utiliser des références plutôt que des copies
Problème : La copie de variables augmente l'utilisation de la mémoire et le temps de traitement.
Stratégie d'optimisation :
# ❌ Non recommandé : Copier la variable
resultat = traiter_donnees(data.copy())
# ✅ Recommandé : Utiliser une référence
resultat = traiter_donnees(data) # Utiliser une référence pour les opérations en lecture seule
4.3 Nettoyer les variables rapidement
Problème : Les variables non utilisées consomment de la mémoire.
Stratégie d'optimisation :
# Dans un nœud de code, nettoyer les variables non utilisées
def main(args):
# Traiter les données
large_data = traiter_grandes_donnees(args["input"])
resultat = extraire_resultat(large_data)
# Nettoyer les grands objets
del large_data
return {"resultat": resultat}
4.4 Gestion de la portée des variables
Types de variables :
- Variables système :
sys.query,sys.files,sys.conversation_id - Variables de nœud :
{{#node_id.output#}} - Variables d'environnement : Gestion de la configuration et des clés
- Variables d'itération : Élément et index actuels dans une boucle
Recommandations d'optimisation :
- Utiliser la portée minimale
- Éviter la pollution des variables globales
- Libérer les variables temporaires rapidement
Documentation détaillée : Voir workflow-optimization/variable-pool.md
Cinquième partie : Traitement parallèle et réponse en streaming
5.1 Traitement parallèle de l'itérateur
Utiliser le paramètre parallel_nums de l'itérateur pour traiter les données de tableau en parallèle.
Exemple de configuration :
iteration_node:
type: iteration
inputs:
items: "{{#code_node.output.items#}}"
config:
parallel_nums: 5 # Traiter 5 éléments en parallèle
Comparaison des performances :
| Volume de données | Traitement séquentiel | Traitement parallèle (5) | Amélioration | |------------------|----------------------|-------------------------| | 10 éléments | 20s | 5s | 4x | | 50 éléments | 100s | 25s | 4x | | 100 éléments | 200s | 50s | 4x |
5.2 Réponse en streaming
Utiliser la réponse en streaming pour améliorer l'expérience utilisateur et réduire le temps jusqu'au premier octet (TTFB).
Implémentation :
# Activer la réponse en streaming pour l'appel API
reponse = requests.post(
"https://api.dify.ai/v1/workflows/run",
headers={"Authorization": f"Bearer {api_key}"},
json={"inputs": {...}, "response_mode": "streaming"},
stream=True
)
for ligne in reponse.iter_lines():
if ligne:
evenement = json.loads(ligne)
print(evenement["data"])
Effet d'optimisation :
- Réduction du TTFB de 80 %
- Réduction du temps de réponse perçu par l'utilisateur de 50 %
5.3 Traitement des tâches asynchrones
Utiliser Celery pour traiter de manière asynchrone les tâches de longue durée.
Architecture :
Serveur API → File d'attente Celery → Pool de workers → Exécution des tâches
Scénarios d'application :
- Traitement de fichiers volumineux
- Importation de données par lots
- Appels API de longue durée
- Tâches planifiées
5.4 Optimisation de l'architecture pilotée par les événements
Dify adopte une architecture pilotée par les événements, où l'exécution des nœuds produit un flux d'événements.
Stratégies d'optimisation :
- Utiliser un traitement d'événements non bloquant
- Traiter la file d'attente d'événements par lots
- Optimiser la logique de distribution des événements
Documentation détaillée : Voir workflow-optimization/parallel-execution.md
Sixième partie : Cas d'optimisation des performances
Cas 1 : Optimisation des requêtes de base de données (problème N+1)
Scénario d'application :
- Le workflow nécessite l'interrogation de plusieurs données connexes
- Présence de requêtes en boucle vers la base de données
- Le temps d'exécution augmente linéairement avec le volume de données
Caractéristiques du problème :
- Nombre de requêtes = 1 + N (N est le nombre d'enregistrements)
- Connexions fréquentes à la base de données
- Temps d'exécution trop long
État avant optimisation :
# Récupérer la liste des utilisateurs
utilisateurs = query("SELECT * FROM users LIMIT 100")
# Pour chaque utilisateur, interroger les commandes (problème N+1)
for utilisateur in utilisateurs:
commandes = query(f"SELECT * FROM orders WHERE user_id = {utilisateur.id}")
utilisateur.commandes = commandes
- Nombre de requêtes : 101 (1 + 100)
- Temps d'exécution : 10,5 s
- Charge de la base de données : Élevée
Solution d'optimisation :
# Interroger toutes les données en une seule fois
utilisateurs = query("SELECT * FROM users LIMIT 100")
ids_utilisateurs = [utilisateur.id for utilisateur in utilisateurs]
# Interroger les commandes par lots
commandes = query(f"SELECT * FROM orders WHERE user_id IN ({','.join(map(str, ids_utilisateurs))})")
# Associer les données en mémoire
commandes_par_utilisateur = {}
for commande in commandes:
if commande.user_id not in commandes_par_utilisateur:
commandes_par_utilisateur[commande.user_id] = []
commandes_par_utilisateur[commande.user_id].append(commande)
for utilisateur in utilisateurs:
utilisateur.commandes = commandes_par_utilisateur.get(utilisateur.id, [])
Résultat après optimisation :
- Nombre de requêtes : 2 (réduction de 98 %)
- Temps d'exécution : 2,1 s (réduction de 80 %)
- Charge de la base de données : Faible
Bénéfices attendus :
- Court terme : Réduction de 80 % du temps d'exécution, réduction de la charge de la base de données
- Long terme : Prise en charge de volumes de données plus importants, amélioration de l'évolutivité du système
- ROI : Coût de développement de 2 heures, économie mensuelle de 30 % sur les coûts de serveur
Cas 2 : Optimisation du rendu de graphes à grande échelle (100+ nœuds)
Scénario d'application :
- Le workflow contient 100+ nœuds
- Rendu frontal saccadé
- Réponse lente de l'éditeur
Caractéristiques du problème :
- Temps de chargement de la page > 5 s
- Latence notable lors du glisser-déposer de nœuds
- Utilisation mémoire élevée du navigateur
État avant optimisation :
- Nombre de nœuds : 150
- Temps de chargement de la page : 8,5 s
- Utilisation mémoire : 500 Mo
- Expérience utilisateur : Saccades graves
Solution d'optimisation :
- Défilement virtuel : Ne rendre que les nœuds dans la zone visible
- Chargement paresseux des nœuds : Charger les détails des nœuds à la demande
- Optimisation Canvas : Utiliser Canvas au lieu du rendu DOM
- Optimisation de la gestion d'état : Réduire les re-rendus inutiles
Résultat après optimisation :
- Temps de chargement de la page : 2,1 s (réduction de 75 %)
- Utilisation mémoire : 150 Mo (réduction de 70 %)
- Expérience utilisateur : Fluide
Bénéfices attendus :
- Court terme : Amélioration significative de l'expérience utilisateur
- Long terme : Prise en charge de conceptions de workflow plus complexes
- ROI : Coût de développement d'une semaine, augmentation de 40 % de la satisfaction utilisateur
Cas 3 : Optimisation de la suppression des logs de workflow
Scénario d'application :
- Nécessité de nettoyer périodiquement les logs historiques
- Volume important de logs (millions)
- Opération de suppression longue
Caractéristiques du problème :
- L'opération de suppression bloque la base de données
- Impact sur les opérations normales
- Temps d'exécution imprévisible
État avant optimisation :
- Nombre de logs : 5 millions
- Temps de suppression : 45 minutes
- Verrou de base de données : Verrouillage de table prolongé
- Impact sur les opérations : Grave
Solution d'optimisation :
# Avant optimisation : Suppression en une seule fois
DELETE FROM workflow_logs WHERE created_at < '2024-01-01';
# Après optimisation : Suppression par lots
taille_lot = 10000
while True:
supprime = DELETE FROM workflow_logs
WHERE created_at < '2024-01-01'
LIMIT taille_lot;
if supprime < taille_lot:
break
# Petite pause pour libérer le verrou de la base de données
sleep(0.1)
Résultat après optimisation :
- Temps de suppression : 50 minutes (légèrement augmenté, mais ne bloque pas les opérations)
- Verrou de base de données : Verrouillage court (0,1 s)
- Impact sur les opérations : Aucun
Bénéfices attendus :
- Court terme : Les opérations ne sont pas affectées
- Long terme : Stabilité accrue de la base de données
- ROI : Coût de développement de 4 heures, évite les pertes dues aux interruptions d'activité
Cas 4 : Intégration du stockage distribué TiDB
Scénario d'application :
- Volume important de données de base de connaissances (niveau To)
- Goulot d'étranglement de performance avec PostgreSQL monolithique
- Besoin de capacité de mise à l'échelle horizontale
Caractéristiques du problème :
- Temps de réponse des requêtes > 5 s
- Utilisation CPU de la base de données > 90 %
- Impossible d'étendre davantage
État avant optimisation :
- Base de données : PostgreSQL monolithique
- Volume de données : 2 To
- Temps de réponse des requêtes : 8,5 s
- Capacité d'extension : Limitée
Solution d'optimisation :
- Migration vers TiDB : Base de données SQL distribuée
- Optimisation des index vectoriels : Utiliser TiDB Vector pour accélérer la récupération
- Séparation lecture/écriture : Distribuer les requêtes de lecture vers plusieurs réplicas
- Partitionnement de tables : Partitionner par temps pour améliorer l'efficacité des requêtes
Résultat après optimisation :
- Temps de réponse des requêtes : 1,2 s (réduction de 86 %)
- CPU de la base de données : 40 % (réduction de 50 %)
- Capacité d'extension : Prise en charge de 10x le volume de données
Bénéfices attendus :
- Court terme : Amélioration significative des performances des requêtes
- Long terme : Prise en charge de la croissance rapide de l'activité
- ROI : Coût de migration de 2 semaines, prise en charge de 10x la croissance de l'activité
Cas 5 : Déploiement haute disponibilité avec Higress AI Gateway
Scénario d'application :
- L'environnement de production nécessite une haute disponibilité
- Équilibrage de charge pour plusieurs modèles LLM
- Besoin de capacité de basculement
Caractéristiques du problème :
- Risque de point de défaillance unique
- Appels API LLM instables
- Incapacité à faire face aux pics de trafic
État avant optimisation :
- Architecture : Instance unique Dify
- Disponibilité : 95 %
- Temps de récupération après panne : 10 minutes
- Pic de trafic : Incapable de gérer
Solution d'optimisation :
# Configuration de Higress AI Gateway
apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: dify-gateway
spec:
replicas: 3 # 3 instances
loadBalancer:
algorithm: round-robin
healthCheck:
enabled: true
interval: 10s
failover:
enabled: true
maxRetries: 3
Architecture :
Requêtes utilisateur → Higress Gateway → [Instance Dify 1, Instance Dify 2, Instance Dify 3]
↓
Équilibrage de charge + Basculement
Résultat après optimisation :
- Disponibilité : 99,9 %
- Temps de récupération après panne : < 1 seconde (basculement automatique)
- Pic de trafic : Prise en charge de 10x le trafic
Bénéfices attendus :
- Court terme : Amélioration significative de la stabilité du système
- Long terme : Prise en charge des applications critiques pour l'activité
- ROI : Coût de déploiement de 3 jours, évite les pertes dues aux interruptions d'activité
Septième partie : Tests de performance et surveillance
7.1 Système d'indicateurs de performance
Indicateurs d'exécution du workflow :
| Indicateur | Valeur cible | Description |
|---|---|---|
| Temps d'exécution moyen | < 2s | Temps d'exécution complet du workflow |
| Temps d'exécution P95 | < 5s | Temps d'exécution pour 95% des requêtes |
| Temps d'exécution P99 | < 10s | Temps d'exécution pour 99% des requêtes |
| Taux de succès des nœuds | > 99% | Proportion de nœuds exécutés avec succès |
| Taux de succès des appels API | > 99,5% | Proportion de succès des appels API externes |
| Capacité de traitement parallèle | > 100 QPS | Nombre de requêtes traitées par seconde |
Indicateurs d'utilisation des ressources :
| Indicateur | Valeur cible | Description |
|---|---|---|
| Utilisation CPU | < 70% | Utilisation moyenne du CPU |
| Utilisation mémoire | < 80% | Utilisation moyenne de la mémoire |
| Connexions base de données | < 80% | Utilisation du pool de connexions |
| Mémoire Redis | < 70% | Utilisation de la mémoire Redis |
Indicateurs de coût :
| Indicateur | Valeur cible | Description |
|---|---|---|
| Coût d'utilisation des tokens | < Budget | Coût de l'API LLM |
| Coût d'infrastructure | < Budget | Coût des serveurs, base de données |
| Coût par utilisateur | En baisse continue | Coût par utilisateur |
7.2 Outils de surveillance
Surveillance LLMOps :
# Surveillance intégrée Dify
from dify import Monitor
moniteur = Monitor()
# Enregistrer l'exécution du workflow
moniteur.log_workflow_execution(
workflow_id="xxx",
execution_time=2.5,
node_count=10,
token_usage=1500,
cost=0.003
)
# Interroger les statistiques
stats = moniteur.get_workflow_stats(
workflow_id="xxx",
time_range="7d"
)
Traçage d'exécution :
- Historique d'exécution du workflow
- Statistiques des étapes d'exécution des nœuds
- Statistiques d'utilisation LLM
- Logs d'erreur et enregistrements de tentatives
Surveillance des ressources :
# Prometheus + Grafana
# Surveiller le CPU, la mémoire, le réseau, le disque
# Exemple de requête
rate(dify_workflow_execution_total[5m]) # Taux d'exécution du workflow
histogram_quantile(0.95, dify_workflow_duration_seconds) # Temps d'exécution P95
7.3 Méthodes de test de performance
Test de charge :
# Utiliser Locust pour les tests de charge
from locust import HttpUser, task, between
class UtilisateurDify(HttpUser):
wait_time = between(1, 3)
@task
def executer_workflow(self):
self.client.post(
"/v1/workflows/run",
headers={"Authorization": f"Bearer {self.api_key}"},
json={"inputs": {"query": "Question de test"}}
)
# Exécuter le test
# locust -f load_test.py --users 100 --spawn-rate 10
Test de stress :
# Utiliser Apache Bench
ab -n 1000 -c 50 -H "Authorization: Bearer xxx" \
-p request.json \
https://api.dify.ai/v1/workflows/run
Test de référence :
# Test de performance de référence
import time
def benchmark_workflow(workflow_id, iterations=100):
times = []
for i in range(iterations):
start = time.time()
run_workflow(workflow_id)
end = time.time()
times.append(end - start)
return {
"avg": sum(times) / len(times),
"p50": sorted(times)[len(times) // 2],
"p95": sorted(times)[int(len(times) * 0.95)],
"p99": sorted(times)[int(len(times) * 0.99)]
}
7.4 Outils d'analyse de performance
Profileur Python :
import cProfile
import pstats
def profiler_workflow():
# ...
# Exemple de profileur
cProfile.run('profiler_workflow()', 'profile_stats')
p = pstats.Stats('profile_stats')
p.sort_stats('cumulative').print_stats(10)
Analyse des performances de Dify :
# Utiliser les endpoints de diagnostique intégrés de Dify
curl -X GET http://localhost:5001/health
curl -X GET http://localhost:5001/metrics
Documentation détaillée : Voir monitoring/