Compétence Développement de Plugins Dify
Aperçu
Le système de plugins Dify utilise l'architecture innovante Beehive (Ruche), prend en charge trois modes d'exécution et propose quatre types de plugins, offrant aux développeurs de puissantes capacités d'extension. Cette compétence fournit un guide complet de développement de plugins, du débutant au niveau expert.
Cas d'Utilisation
Utilisez cette compétence lorsque vous avez besoin de :
- Développer des plugins Dify : les quatre types : Outil, Modèle, Source de données, Extension
- Intégrer des services tiers : Wrapper API, Autorisation OAuth
- Implémenter des outils personnalisés : Étendre les capacités de l'IA
- Étendre les fonctionnalités de la plateforme Dify : Déclencheurs, Stratégies d'Agent
- Migrer des API existantes vers des plugins : Conversion d'API en plugin
- Déboguer et optimiser les performances des plugins : Ajustement des performances et résolution de problèmes
Valeur Fondamentale
- Architecture Beehive : Ordonnancement décentralisé, élection maître-esclave, auto-réparation
- Prise en charge de plusieurs modes d'exécution : Local, Debug, Serverless
- Chaîne d'outils complète : CLI dify, SDK Python, Runtime de débogage
- Écosystème riche : 70+ plugins de modèle, 20+ plugins d'outil, 18+ plugins de source de données
- Qualité de production : OAuth 2.0, Contrôle d'accès, Surveillance et traçage
Démarrage Rapide
1. Préparation de l'Environnement
# Installer Python 3.12+
python --version # Assurez-vous >= 3.12
# Installer le gestionnaire de paquets uv (recommandé)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Installer la CLI dify
pip install dify-cli
# Vérifier l'installation
dify --version
2. Créer le Premier Plugin
# Initialiser un projet de plugin
dify plugin init
# Choisir le type de plugin
? Select plugin type: Tool
? Plugin name: my_first_tool
? Author: your_name
? Description: My first Dify tool plugin
# Entrer dans le répertoire du projet
cd my_first_tool
# Installer les dépendances
uv pip install -r requirements.txt
3. Implémenter la Logique du Plugin
Éditez tools/my_tool.py :
from typing import Any, Generator
from dify_plugin.entities.tool import ToolInvokeMessage
from dify_plugin import Tool
class MyTool(Tool):
def _invoke(
self, tool_parameters: dict[str, Any]
) -> Generator[ToolInvokeMessage, None, None]:
"""
Point d'entrée de l'appel de l'outil
"""
# Récupérer les paramètres
text = tool_parameters.get("text", "")
# Logique de traitement
result = f"Résultat du traitement : {text.upper()}"
# Retourner le résultat
yield self.create_text_message(result)
4. Débogage Local
# Démarrer le Runtime de Débogage
dify plugin run
# Tester le plugin dans l'interface Dify
# Accéder à http://localhost:5001
5. Empaquetage et Déploiement
# Empaqueter le plugin
dify plugin package
# Générer le fichier my_first_tool.difypkg
# Télécharger sur le Dify Marketplace ou un déploiement privé
Concepts Fondamentaux
Architecture Beehive
Le système de plugins Dify utilise l'architecture Beehive (Ruche), dont les caractéristiques principales sont :
- Ordonnancement Décentralisé : Chaque nœud du processus de garde gère les plugins localement de manière indépendante
- Élection Maître-Esclave : Élection automatique du nœud maître via un verrou distribué Redis
- Synchronisation d'État : L'état de tous les nœuds est stocké dans Redis, permettant un accès inter-nœuds
- Auto-Réparation : Réattribution automatique des plugins en cas de panne d'un nœud
Pour une description détaillée de l'architecture, veuillez vous référer à : types/architecture.md
Trois Modes d'Exécution
| Mode d'Exécution | Cas d'Utilisation | Méthode de Communication | Avantages | Inconvénients |
|---|---|---|---|---|
| Runtime Local | Développement local, déploiement à petite échelle | STDIN/STDOUT | Faible latence, isolement des ressources | Utilisation mémoire élevée, extensibilité limitée |
| Runtime de Débogage | Débogage à distance, environnement de développement | TCP Full-Duplex | Conviviale pour le développement, déploiement flexible | Dépendance réseau, gestion d'état complexe |
| Runtime Serverless | Environnement de production à grande échelle | HTTP/SSE | Mise à l'échelle automatique, optimisation des coûts | Latence de démarrage à froid, débogage complexe |
Pour une comparaison détaillée, veuillez vous référer à : development/runtime-modes.md
Quatre Types de Plugins
1. Tool (Plugin d'Outil)
Outils pour étendre les capacités de l'IA, tels que la traduction, la recherche, le traitement de données, etc.
# Exemple : Google Traduction
type: plugin
plugins:
tools:
- provider/google_translate.yaml
Cas d'Utilisation :
- Wrapper d'API (API REST → Outil)
- Traitement de données (conversion de format, calcul)
- Intégration de services externes (recherche, traduction)
2. Model (Plugin de Modèle)
Intégration de fournisseurs LLM, tels qu'Anthropic, OpenAI, Gemini, etc.
# Exemple : Anthropic Claude
type: plugin
plugins:
models:
- provider/anthropic.yaml
Cas d'Utilisation :
- Intégration d'un nouveau fournisseur LLM
- Configuration personnalisée du modèle
- Prise en charge des modèles multimodaux
3. Datasource (Plugin de Source de Données)
Connexion aux systèmes de stockage, tels que S3, Google Drive, Notion, etc.
# Exemple : AWS S3
type: plugin
plugins:
datasources:
- provider/aws_s3.yaml
Cas d'Utilisation :
- Intégration de stockage cloud
- Connexion à des plateformes documentaires
- Accès aux bases de données
4. Extension (Plugin d'Extension)
Extension des fonctionnalités de la plateforme, telle que l'intégration avec des plateformes tierces comme Slack, WeChat Work, etc.
# Exemple : Slack
type: plugin
plugins:
extensions:
- provider/slack.yaml
Cas d'Utilisation :
- Intégration de plateformes tierces
- Implémentation de déclencheurs
- Stratégies d'agent personnalisées
Pour une description détaillée des types, veuillez vous référer à : types/
Structure du Projet
Structure standard d'un projet de plugin Dify :
my_plugin/
├── manifest.yaml # Manifeste du plugin (obligatoire)
├── main.py # Point d'entrée (obligatoire)
├── requirements.txt # Dépendances Python
├── pyproject.toml # Configuration du projet
├── README.md # Description du plugin
├── PRIVACY.md # Politique de confidentialité (si OAuth requis)
├── .env.example # Exemple de variables d'environnement
│
├── _assets/ # Fichiers de ressources
│ └── icon.svg # Icône du plugin
│
├── provider/ # Configuration du fournisseur
│ ├── provider_name.yaml # Fichier de configuration du fournisseur
│ └── provider_name.py # Implémentation du fournisseur
│
└── tools/ # Implémentation des outils (Plugin Tool)
├── tool_name.yaml # Fichier de configuration de l'outil
└── tool_name.py # Implémentation de l'outil
Description des Fichiers Principaux
manifest.yaml
Fichier de manifeste du plugin, définissant les informations et la configuration de base :
author: your_name
name: my_plugin
version: 0.0.1
type: plugin
description:
en_US: Plugin description
zh_Hans: Description du plugin
label:
en_US: My Plugin
zh_Hans: Mon plugin
icon: icon.svg
meta:
version: 0.0.1
arch:
- amd64
- arm64
runner:
language: python
version: '3.12'
entrypoint: main
plugins:
tools:
- provider/my_provider.yaml
resource:
memory: 1048576 # 1 Mo = 1048576 octets
permission:
tool:
enabled: true
model:
enabled: true
llm: true
tags:
- utilities
Pour une description détaillée de la configuration, veuillez vous référer à : development/manifest.md
main.py
Fichier d'entrée du plugin :
from dify_plugin import Plugin, DifyPluginEnv
# Créer une instance du plugin
plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=120))
if __name__ == '__main__':
# Démarrer le plugin
plugin.run()
Guide de Développement
Développement d'un Plugin Tool
1. Créer le Fournisseur
Éditez provider/my_provider.yaml :
identity:
author: your_name
name: my_provider
label:
en_US: My Provider
zh_Hans: Mon fournisseur
description:
en_US: Provider description
zh_Hans: Description du fournisseur
icon: icon.svg
tags:
- utilities
extra:
python:
source: provider/my_provider.py
tools:
- tools/my_tool.yaml
Éditez provider/my_provider.py :
from typing import Any
from dify_plugin.errors.tool import ToolProviderCredentialValidationError
from dify_plugin import ToolProvider
from tools.my_tool import MyTool
class MyProvider(ToolProvider):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
"""
Valider les identifiants (si nécessaire)
"""
try:
# Valider la clé API ou d'autres identifiants
api_key = credentials.get("api_key")
if not api_key:
raise ToolProviderCredentialValidationError("La clé API est obligatoire")
# Tester la validité des identifiants
MyTool().invoke(
tool_parameters={"text": "test"}
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
2. Créer l'Outil
Éditez tools/my_tool.yaml :
identity:
author: your_name
name: my_tool
label:
en_US: My Tool
zh_Hans: Mon outil
description:
human:
en_US: Tool description for humans
zh_Hans: Description de l'outil pour les humains
llm: Description de l'outil pour LLM
extra:
python:
source: tools/my_tool.py
parameters:
- name: text
type: string
required: true
label:
en_US: Texte d'entrée
zh_Hans: Texte d'entrée
human_description:
en_US: The text to process
zh_Hans: Le texte à traiter
llm_description: Le texte d'entrée à traiter
form: llm
Éditez tools/my_tool.py :
from typing import Any, Generator
from dify_plugin.entities.tool import ToolInvokeMessage
from dify_plugin import Tool
class MyTool(Tool):
def _invoke(
self, tool_parameters: dict[str, Any]
) -> Generator[ToolInvokeMessage, None, None]:
"""
Point d'entrée de l'appel de l'outil
Args:
tool_parameters: Dictionnaire des paramètres de l'outil
Yields:
ToolInvokeMessage: Message d'appel de l'outil
"""
# 1. Récupérer les paramètres
text = tool_parameters.get("text", "")
if not text:
yield self.create_text_message("Erreur : le paramètre text est obligatoire")
return
try:
# 2. Logique de traitement
result = self._process_text(text)
# 3. Retourner le résultat
yield self.create_text_message(result)
except Exception as e:
yield self.create_text_message(f"Erreur : {str(e)}")
def _process_text(self, text: str) -> str:
"""
Logique spécifique de traitement du texte
"""
return text.upper()
Pour un guide de développement détaillé, veuillez vous référer à : development/tool.md
Intégration OAuth 2.0
Aperçu du Flux OAuth
Dify prend en charge le flux d'autorisation complet OAuth 2.0 :
- Obtenir l'URL d'autorisation : L'utilisateur clique sur le bouton d'autorisation
- Autorisation de l'utilisateur : Redirection vers la page d'autorisation du service tiers
- Obtenir le code d'autorisation : Retour du code d'autorisation après autorisation de l'utilisateur
- Échanger le jeton d'accès : Utiliser le code d'autorisation pour échanger un jeton d'accès
- Rafraîchir le jeton : Utiliser le jeton de rafraîchissement pour obtenir un nouveau jeton d'accès lorsque celui-ci expire
Configuration du Schéma OAuth
Ajoutez la configuration OAuth dans provider/my_provider.yaml :
oauth_schema:
client_schema:
- name: client_id
type: secret-input
required: true
label:
en_US: ID Client
zh_Hans: ID Client
placeholder:
en_US: Saisissez votre ID Client OAuth
zh_Hans: Saisissez votre ID Client OAuth
help:
en_US: Obtenez votre ID client auprès du fournisseur de services
zh_Hans: Obtenez votre ID client auprès du fournisseur de services
url: https://console.example.com/credentials
- name: client_secret
type: secret-input
required: true
label:
en_US: Secret Client
zh_Hans: Secret Client
placeholder:
en_US: Saisissez votre Secret Client OAuth
zh_Hans: Saisissez votre Secret Client OAuth
credentials_schema:
- name: access_token
type: secret-input
label:
en_US: Jeton d'Accès
zh_Hans: Jeton d'Accès
- name: refresh_token
type: secret-input
label:
en_US: Jeton de Rafraîchissement
zh_Hans: Jeton de Rafraîchissement
Implémentation du Fournisseur OAuth
from typing import Any
from dify_plugin import ToolProvider
from dify_plugin.errors.tool import ToolProviderCredentialValidationError
import requests
class MyOAuthProvider(ToolProvider):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
"""
Valider les identifiants OAuth
"""
access_token = credentials.get("access_token")
if not access_token:
raise ToolProviderCredentialValidationError(
"Le jeton d'accès est obligatoire"
)
# Tester la validité du jeton d'accès
try:
response = requests.get(
"https://api.example.com/user",
headers={"Authorization": f"Bearer {access_token}"}
)
response.raise_for_status()
except Exception as e:
raise ToolProviderCredentialValidationError(
f"Jeton d'accès invalide : {str(e)}"
)
Utilisation du Jeton OAuth
Utilisez le jeton OAuth dans un outil :
class MyOAuthTool(Tool):
def _invoke(
self, tool_parameters: dict[str, Any]
) -> Generator[ToolInvokeMessage, None, None]:
# Récupérer les identifiants OAuth
credentials = self.runtime.credentials
access_token = credentials.get("access_token")
# Appeler l'API
response = requests.get(
"https://api.example.com/data",
headers={"Authorization": f"Bearer {access_token}"}
)
yield self.create_text_message(response.text)
Pour un guide complet d'intégration OAuth, veuillez vous référer à : development/oauth-integration.md
Tests et Débogage
Tests Unitaires
Écrivez des tests unitaires avec pytest :
# tests/test_my_tool.py
import pytest
from tools.my_tool import MyTool
def test_my_tool():
tool = MyTool()
result = list(tool.invoke(tool_parameters={"text": "bonjour"}))
assert len(result) == 1
assert "BONJOUR" in result[0].message
Exécutez les tests :
pytest tests/
Tests d'Intégration
Testez l'intégration du plugin avec le système Dify :
# tests/test_integration.py
def test_plugin_integration():
# Tester l'installation du plugin
# Tester l'appel de l'outil
# Tester le flux OAuth
pass
Débogage avec Debug Runtime
Démarrez le Debug Runtime pour le débogage à distance :
# Démarrer le Debug Runtime
dify plugin run --debug
# Tester dans l'interface Dify
# Voir les logs en temps réel et les messages d'erreur
Résolution des Problèmes Courants
Échec de Démarrage du Plugin
- Vérifier la version de Python (nécessite 3.12+)
- Vérifier l'installation des dépendances (
requirements.txt) - Vérifier la configuration de
manifest.yaml - Vérifier le chemin du fichier d'entrée (
main.py)
Échec de Validation des Autorisations
- Vérifier la configuration des
credentials - Vérifier si le jeton OAuth a expiré
- Vérifier si la clé API est valide
- Vérifier les déclarations d'autorisation (
resource.permission)
Erreurs d'Exécution
- Ajuster le délai d'expiration (
MAX_REQUEST_TIMEOUT) - Optimiser l'utilisation de la mémoire (
resource.memory) - Vérifier la connexion réseau
- Vérifier les autorisations d'appel inversé
Pour un guide de dépannage détaillé, veuillez vous référer à : testing/troubleshooting.md
Migration d'API vers Plugin
Stratégie de Migration
- Mode Wrapper API : Encapsuler une API REST en plugin Tool
- Mode Intégration SDK : Utiliser un SDK Python tiers
- Migration OAuth : Implémenter le flux d'autorisation OAuth 2.0
- Optimisation des Opérations par Lots : Utiliser des réponses en flux pour traiter de gros volumes de données
Étapes de Migration
-
Analyser l'API Existante
- Points de terminaison et paramètres de l'API
- Méthode d'authentification (clé API / OAuth)
- Format de réponse et gestion des erreurs
-
Choisir le Type de Plugin
- API REST → Plugin Tool
- API LLM → Plugin Model
- API de stockage → Plugin Datasource
-
Créer le Projet de Plugin
dify plugin init -
Implémenter l'Appel API
import requests class APIWrapperTool(Tool): def _invoke(self, tool_parameters: dict[str, Any]): response = requests.post( "https://api.example.com/endpoint", json=tool_parameters, headers={"Authorization": f"Bearer {api_key}"} ) yield self.create_text_message(response.text) -
Configurer l'Authentification
- Clé API : Configurer
credentialsdansprovider.yaml - OAuth : Configurer
oauth_schema
- Clé API : Configurer
-
Ajouter la Gestion des Erreurs
try: response = requests.post(...) response.raise_for_status() except requests.exceptions.RequestException as e: yield self.create_text_message(f"Erreur API : {str(e)}") -
Écrire les Tests
def test_api_wrapper(): tool = APIWrapperTool() result = list(tool.invoke(tool_parameters={...})) assert result[0].message -
Empaqueter et Déployer
dify plugin package
Cas Concrets
- API REST → Tool : Google Traduction (sans authentification)
- API OAuth → Tool : Google Contacts (OAuth 2.0)
- API LLM → Model : Anthropic Claude (clé API)
- API de stockage → Datasource : AWS S3 (Authentification IAM)
Pour un guide de migration détaillé, veuillez vous référer à : development/migration-guide.md
Bonnes Pratiques
Organisation du Code
- Responsabilité Unique : Chaque outil ne fait qu'une seule chose
- Gestion des Erreurs : Capturer toutes les exceptions et retourner des messages d'erreur conviviaux
- Validation des Paramètres : Valider tous les paramètres au début de
_invoke - Journalisation : Utiliser une journalisation structurée pour les opérations clés
Optimisation des Performances
- Réponses en Flux : Utiliser un générateur (Generator) pour les gros volumes de données
- Stratégie de Cache : Mettre en cache les données fréquemment consultées
- Traitement Asynchrone : Utiliser async/await pour les opérations I/O intensives
- Limites de Ressources : Configurer
resource.memoryde manière appropriée
Considérations de Sécurité
- Validation des Entrées : Valider toutes les entrées utilisateur
- Données Sensibles : Utiliser le type
secret-inputpour stocker les informations sensibles - Contrôle d'Accès : Principe du moindre privilège, ne déclarer que les autorisations nécessaires
- Messages d'Erreur : Ne pas divulguer de données sensibles dans les messages d'erreur
Expérience Utilisateur
- Description Claire : Fournir des
descriptionethelpdétaillés - Valeurs par Défaut Raisonnables : Fournir des valeurs par défaut raisonnables pour les paramètres
- Prise en Charge Multilingue : Prendre en charge au moins
en_USetzh_Hans - Erreurs Conviviales : Retourner des messages d'erreur compréhensibles par l'utilisateur
Déploiement et Exploitation
Choix du Mode d'Exécution
| Scénario | Mode Recommandé | Raison |
|---|---|---|
| Développement local | Runtime Local | Faible latence, débogage facile |
| Débogage à distance | Runtime de Débogage | Flexible, prise en charge à distance |
| Production à petite échelle | Runtime Local | Simple, faible coût |
| Production à grande échelle | Runtime Serverless | Mise à l'échelle automatique, haute disponibilité |
Déploiement Serverless
Déploiement sur AWS Lambda :
# 1. Empaqueter le plugin
dify plugin package
# 2. Créer la fonction Lambda
aws lambda create-function \
--function-name my-plugin \
--runtime python3.12 \
--handler main.handler \
--zip-file fileb://my_plugin.difypkg
# 3. Configurer les variables d'environnement
aws lambda update-function-configuration \
--function-name my-plugin \
--environment Variables={MAX_REQUEST_TIMEOUT=120}
# 4. Configurer le point de terminaison API Gateway
aws apigateway create-rest-api --name my-plugin-api
Surveillance et Journalisation
import logging
logger = logging.getLogger(__name__)
class MonOutilLogger(Tool):
def _invoke(self, tool_parameters: dict[str, Any]):
logger.info(f"Début du traitement : {tool_parameters}")
try:
result = self._process(tool_parameters)
logger.info(f"Traitement terminé : {result}")
yield self.create_text_message(result)
except Exception as e:
logger.error(f"Erreur de traitement : {str(e)}")
raise
Gestion des Versions
# Mettre à jour la version
dify plugin version bump minor
# Voir l'historique des versions
dify plugin version list
# Revenir à une version précédente
dify plugin version rollback 0.0.2
Références
Documentation Officielle
Références Architecture
Guides de Développement
- Développement de Plugin Tool
- Développement de Plugin Model
- Développement de Plugin Datasource
- Développement de Plugin Extension