Dify 플러그인 개발 SKILL
개요
Dify 플러그인 시스템은 혁신적인 Beehive(벌집) 아키텍처를 채택하여 세 가지 런타임 모드와 네 가지 플러그인 유형을 지원하며, 개발자에게 강력한 확장 기능을 제공합니다. 본 SKILL은 입문부터 숙련까지의 완벽한 플러그인 개발 가이드를 제공합니다.
적용 시나리오
다음과 같은 상황에서 이 SKILL을 사용하세요:
- Dify 플러그인 개발: 도구, 모델, 데이터 소스, 확장 네 가지 유형
- 타사 서비스 통합: API 래핑, OAuth 인증
- 사용자 정의 도구 구현: AI 기능 확장
- Dify 플랫폼 기능 확장: 트리거, 에이전트 전략
- 기존 API를 플러그인으로 마이그레이션: API-플러그인 변환
- 플러그인 성능 디버깅 및 최적화: 성능 튜닝 및 문제 해결
핵심 가치
- Beehive 아키텍처: 분산 스케줄링, 마스터-슬레이브 선출, 자가 치유
- 다중 런타임 지원: 로컬(Local), 디버그(Debug), 서버리스(Serverless) 세 가지 모드
- 완벽한 도구 체인: dify CLI, Python SDK, 디버그 런타임
- 풍부한 생태계: 70+ 모델 플러그인, 20+ 도구 플러그인, 18+ 데이터 소스 플러그인
- 프로덕션 수준 품질: OAuth 2.0, 권한 제어, 모니터링 및 추적
빠른 시작
1. 환경 준비
# Python 3.12+ 설치
python --version # >= 3.12 확인
# uv 패키지 관리자 설치 (권장)
curl -LsSf https://astral.sh/uv/install.sh | sh
# dify CLI 설치
pip install dify-cli
# 설치 확인
dify --version
2. 첫 번째 플러그인 생성
# 플러그인 프로젝트 초기화
dify plugin init
# 플러그인 유형 선택
? Select plugin type: Tool
? Plugin name: my_first_tool
? Author: your_name
? Description: My first Dify tool plugin
# 프로젝트 디렉토리로 이동
cd my_first_tool
# 의존성 설치
uv pip install -r requirements.txt
3. 플러그인 로직 구현
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]:
"""
도구 호출 진입점
"""
# 매개변수 가져오기
text = tool_parameters.get("text", "")
# 처리 로직
result = f"처리 결과: {text.upper()}"
# 결과 반환
yield self.create_text_message(result)
4. 로컬 디버깅
# Debug Runtime 시작
dify plugin run
# Dify 인터페이스에서 플러그인 테스트
# http://localhost:5001 접속
5. 패키징 및 배포
# 플러그인 패키징
dify plugin package
# my_first_tool.difypkg 파일 생성됨
# Dify Marketplace 또는 프라이빗 배포에 업로드
핵심 개념
Beehive 아키텍처
Dify 플러그인 시스템은 Beehive(벌집) 아키텍처를 채택하며, 핵심 특징은 다음과 같습니다:
- 분산 스케줄링: 각 데몬 프로세스 노드가 로컬 플러그인을 독립적으로 관리
- 마스터-슬레이브 선출: Redis 분산 잠금을 통한 자동 마스터 노드 선출
- 상태 동기화: 모든 노드 상태를 Redis에 저장하여 교차 노드 액세스 지원
- 자가 치유: 노드 다운 시 자동으로 플러그인 재할당
자세한 아키텍처 설명은 types/architecture.md를 참조하세요.
세 가지 런타임 모드
| 런타임 모드 | 적용 시나리오 | 통신 방식 | 장점 | 단점 |
|---|---|---|---|---|
| Local Runtime | 로컬 개발, 소규모 배포 | STDIN/STDOUT | 낮은 지연 시간, 리소스 격리 | 높은 메모리 사용량, 확장성 제한 |
| Debug Runtime | 원격 디버깅, 개발 환경 | TCP 전이중 | 개발 친화적, 유연한 배포 | 네트워크 의존성, 상태 관리 복잡 |
| Serverless Runtime | 대규모 프로덕션 환경 | HTTP/SSE | 자동 확장, 비용 최적화 | 콜드 스타트 지연, 디버깅 복잡 |
자세한 비교는 development/runtime-modes.md를 참조하세요.
네 가지 플러그인 유형
1. Tool (도구 플러그인)
AI 기능을 확장하는 도구입니다. 예: 번역, 검색, 데이터 처리 등.
# 예시: Google Translate
type: plugin
plugins:
tools:
- provider/google_translate.yaml
사용 시나리오:
- API 래핑 (REST API → Tool)
- 데이터 처리 (형식 변환, 계산)
- 외부 서비스 통합 (검색, 번역)
2. Model (모델 플러그인)
LLM 제공자(Anthropic, OpenAI, Gemini 등)에 연결합니다.
# 예시: Anthropic Claude
type: plugin
plugins:
models:
- provider/anthropic.yaml
사용 시나리오:
- 새로운 LLM 제공자 연결
- 사용자 정의 모델 구성
- 멀티모달 모델 지원
3. Datasource (데이터 소스 플러그인)
S3, Google Drive, Notion 등 저장 시스템에 연결합니다.
# 예시: AWS S3
type: plugin
plugins:
datasources:
- provider/aws_s3.yaml
사용 시나리오:
- 클라우드 스토리지 통합
- 문서 플랫폼 연결
- 데이터베이스 액세스
4. Extension (확장 플러그인)
Slack, 기업용 WeChat 등 타사 플랫폼 통합과 같은 플랫폼 기능 확장.
# 예시: Slack
type: plugin
plugins:
extensions:
- provider/slack.yaml
사용 시나리오:
- 타사 플랫폼 통합
- 트리거 구현
- 사용자 정의 에이전트 전략
자세한 유형 설명은 types/를 참조하세요.
프로젝트 구조
표준 Dify 플러그인 프로젝트 구조:
my_plugin/
├── manifest.yaml # 플러그인 매니페스트 (필수)
├── main.py # 진입 파일 (필수)
├── requirements.txt # Python 의존성
├── pyproject.toml # 프로젝트 설정
├── README.md # 플러그인 설명
├── PRIVACY.md # 개인정보 처리방침 (OAuth 필요 시)
├── .env.example # 환경 변수 예시
│
├── _assets/ # 리소스 파일
│ └── icon.svg # 플러그인 아이콘
│
├── provider/ # 제공자 설정
│ ├── provider_name.yaml # 제공자 설정 파일
│ └── provider_name.py # 제공자 구현
│
└── tools/ # 도구 구현 (Tool 플러그인)
├── tool_name.yaml # 도구 설정 파일
└── tool_name.py # 도구 구현
핵심 파일 설명
manifest.yaml
플러그인의 기본 정보와 설정을 정의하는 매니페스트 파일:
author: your_name
name: my_plugin
version: 0.0.1
type: plugin
description:
en_US: Plugin description
zh_Hans: 플러그인 설명
label:
en_US: My Plugin
zh_Hans: 내 플러그인
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 # 1MB = 1048576 bytes
permission:
tool:
enabled: true
model:
enabled: true
llm: true
tags:
- utilities
자세한 설정 설명은 development/manifest.md를 참조하세요.
main.py
플러그인 진입 파일:
from dify_plugin import Plugin, DifyPluginEnv
# 플러그인 인스턴스 생성
plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=120))
if __name__ == '__main__':
# 플러그인 시작
plugin.run()
개발 가이드
Tool 플러그인 개발
1. Provider 생성
provider/my_provider.yaml 편집:
identity:
author: your_name
name: my_provider
label:
en_US: My Provider
zh_Hans: 내 제공자
description:
en_US: Provider description
zh_Hans: 제공자 설명
icon: icon.svg
tags:
- utilities
extra:
python:
source: provider/my_provider.py
tools:
- tools/my_tool.yaml
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:
"""
자격 증명 검증 (필요한 경우)
"""
try:
# API Key 또는 기타 자격 증명 검증
api_key = credentials.get("api_key")
if not api_key:
raise ToolProviderCredentialValidationError("API Key is required")
# 자격 증명 유효성 테스트
MyTool().invoke(
tool_parameters={"text": "test"}
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
2. Tool 생성
tools/my_tool.yaml 편집:
identity:
author: your_name
name: my_tool
label:
en_US: My Tool
zh_Hans: 내 도구
description:
human:
en_US: Tool description for humans
zh_Hans: 인간용 도구 설명
llm: LLM용 도구 설명
extra:
python:
source: tools/my_tool.py
parameters:
- name: text
type: string
required: true
label:
en_US: Input Text
zh_Hans: 입력 텍스트
human_description:
en_US: The text to process
zh_Hans: 처리할 텍스트
llm_description: 처리할 입력 텍스트
form: llm
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]:
"""
도구 호출 진입점
Args:
tool_parameters: 도구 매개변수 딕셔너리
Yields:
ToolInvokeMessage: 도구 호출 메시지
"""
# 1. 매개변수 가져오기
text = tool_parameters.get("text", "")
if not text:
yield self.create_text_message("Error: text parameter is required")
return
try:
# 2. 처리 로직
result = self._process_text(text)
# 3. 결과 반환
yield self.create_text_message(result)
except Exception as e:
yield self.create_text_message(f"Error: {str(e)}")
def _process_text(self, text: str) -> str:
"""
텍스트 처리의 구체적인 로직
"""
return text.upper()
자세한 개발 가이드는 development/tool.md를 참조하세요.
OAuth 2.0 통합
OAuth 흐름 개요
Dify는 완전한 OAuth 2.0 인증 흐름을 지원합니다:
- 인증 URL 가져오기: 사용자가 인증 버튼 클릭
- 사용자 인증: 타사 서비스 인증 페이지로 리디렉션
- 인증 코드 가져오기: 사용자 인증 후 인증 코드 반환
- 액세스 토큰 교환: 인증 코드를 사용하여 액세스 토큰 교환
- 토큰 갱신: 액세스 토큰 만료 시 갱신 토큰으로 새 토큰 획득
OAuth 스키마 구성
provider/my_provider.yaml에 OAuth 구성 추가:
oauth_schema:
client_schema:
- name: client_id
type: secret-input
required: true
label:
en_US: Client ID
zh_Hans: 클라이언트 ID
placeholder:
en_US: Enter your OAuth Client ID
zh_Hans: OAuth 클라이언트 ID를 입력하세요
help:
en_US: Get your client ID from the service provider
zh_Hans: 서비스 제공자로부터 클라이언트 ID를 받으세요
url: https://console.example.com/credentials
- name: client_secret
type: secret-input
required: true
label:
en_US: Client Secret
zh_Hans: 클라이언트 시크릿
placeholder:
en_US: Enter your OAuth Client Secret
zh_Hans: OAuth 클라이언트 시크릿을 입력하세요
credentials_schema:
- name: access_token
type: secret-input
label:
en_US: Access Token
zh_Hans: 액세스 토큰
- name: refresh_token
type: secret-input
label:
en_US: Refresh Token
zh_Hans: 리프레시 토큰
OAuth Provider 구현
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:
"""
OAuth 자격 증명 검증
"""
access_token = credentials.get("access_token")
if not access_token:
raise ToolProviderCredentialValidationError(
"Access token is required"
)
# 액세스 토큰 유효성 테스트
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"Invalid access token: {str(e)}"
)
OAuth 토큰 사용
Tool에서 OAuth 토큰 사용:
class MyOAuthTool(Tool):
def _invoke(
self, tool_parameters: dict[str, Any]
) -> Generator[ToolInvokeMessage, None, None]:
# OAuth 자격 증명 가져오기
credentials = self.runtime.credentials
access_token = credentials.get("access_token")
# API 호출
response = requests.get(
"https://api.example.com/data",
headers={"Authorization": f"Bearer {access_token}"}
)
yield self.create_text_message(response.text)
전체 OAuth 통합 가이드는 development/oauth-integration.md를 참조하세요.
테스트 및 디버깅
단위 테스트
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": "hello"}))
assert len(result) == 1
assert "HELLO" in result[0].message
테스트 실행:
pytest tests/
통합 테스트
Dify 시스템과의 플러그인 통합 테스트:
# tests/test_integration.py
def test_plugin_integration():
# 플러그인 설치 테스트
# 도구 호출 테스트
# OAuth 흐름 테스트
pass
Debug Runtime 디버깅
Debug Runtime을 시작하여 원격 디버깅:
# Debug Runtime 시작
dify plugin run --debug
# Dify 인터페이스에서 테스트
# 실시간 로그 및 오류 정보 확인
일반적인 문제 해결
플러그인 시작 실패
- Python 버전 확인 (3.12+ 필요)
- 의존성 설치 확인 (
requirements.txt) manifest.yaml설정 확인- 진입 파일 경로 확인 (
main.py)
권한 검증 실패
credentials설정 확인- OAuth 토큰 만료 확인
- API Key 유효성 확인
- 권한 선언 확인 (
resource.permission)
런타임 오류
- 타임아웃 조정 (
MAX_REQUEST_TIMEOUT) - 메모리 사용 최적화 (
resource.memory) - 네트워크 연결 확인
- 역호출 권한 확인
자세한 문제 해결 가이드는 testing/troubleshooting.md를 참조하세요.
API에서 플러그인으로 마이그레이션
마이그레이션 전략
- API 래핑 모드: REST API를 Tool 플러그인으로 래핑
- SDK 통합 모드: 타사 Python SDK 사용
- OAuth 마이그레이션: OAuth 2.0 인증 흐름 구현
- 배치 작업 최적화: 스트리밍 응답을 사용하여 대용량 데이터 처리
마이그레이션 단계
-
기존 API 분석
- API 엔드포인트 및 매개변수
- 인증 방식 (API Key / OAuth)
- 응답 형식 및 오류 처리
-
플러그인 유형 선택
- REST API → Tool 플러그인
- LLM API → Model 플러그인
- 스토리지 API → Datasource 플러그인
-
플러그인 프로젝트 생성
dify plugin init -
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) -
인증 구성
- API Key:
provider.yaml에서credentials구성 - OAuth:
oauth_schema구성
- API Key:
-
오류 처리 추가
try: response = requests.post(...) response.raise_for_status() except requests.exceptions.RequestException as e: yield self.create_text_message(f"API Error: {str(e)}") -
테스트 작성
def test_api_wrapper(): tool = APIWrapperTool() result = list(tool.invoke(tool_parameters={...})) assert result[0].message -
패키징 및 배포
dify plugin package
실제 사례
- REST API → Tool: Google Translate (인증 없음)
- OAuth API → Tool: Google Contacts (OAuth 2.0)
- LLM API → Model: Anthropic Claude (API Key)
- 스토리지 API → Datasource: AWS S3 (IAM 인증)
자세한 마이그레이션 가이드는 development/migration-guide.md를 참조하세요.
모범 사례
코드 구성
- 단일 책임: 각 Tool은 하나의 작업만 수행
- 오류 처리: 모든 예외를 포착하고 친숙한 오류 메시지 반환
- 매개변수 검증:
_invoke시작 시 모든 매개변수 검증 - 로깅: 구조화된 로깅을 사용하여 주요 작업 기록
성능 최적화
- 스트리밍 응답: 대용량 데이터의 경우 Generator를 사용한 스트리밍 반환
- 캐싱 전략: 자주 액세스하는 데이터 캐싱
- 비동기 처리: I/O 집약적 작업에 async/await 사용
- 리소스 제한:
resource.memory를 적절히 구성
보안 고려 사항
- 입력 검증: 모든 사용자 입력 검증
- 민감 데이터:
secret-input유형을 사용하여 민감 정보 저장 - 권한 제어: 최소 권한 원칙, 필요한 권한만 선언
- 오류 정보: 오류 메시지에 민감 데이터 노출 금지
사용자 경험
- 명확한 설명: 상세한
description및help제공 - 합리적인 기본값: 매개변수에 합리적인 기본값 제공
- 다국어 지원: 최소
en_US및zh_Hans지원 - 친숙한 오류: 사용자가 이해할 수 있는 오류 메시지 반환
배포 및 운영
런타임 모드 선택
| 시나리오 | 권장 모드 | 이유 |
|---|---|---|
| 로컬 개발 | Local Runtime | 낮은 지연 시간, 쉬운 디버깅 |
| 원격 디버깅 | Debug Runtime | 유연함, 원격 지원 |
| 소규모 프로덕션 | Local Runtime | 간단함, 저비용 |
| 대규모 프로덕션 | Serverless Runtime | 자동 확장, 고가용성 |
Serverless 배포
AWS Lambda에 배포:
# 1. 플러그인 패키징
dify plugin package
# 2. Lambda 함수 생성
aws lambda create-function \
--function-name my-plugin \
--runtime python3.12 \
--handler main.handler \
--zip-file fileb://my_plugin.difypkg
# 3. 환경 변수 구성
aws lambda update-function-configuration \
--function-name my-plugin \
--environment Variables={...}
# 4. API Gateway 구성 (HTTP 엔드포인트)
로컬 Runtime 배포
# Local Runtime 시작
dify plugin run
# STDI 환경 통신
# 기본 포트: 5001
모니터링 및 알림
- 로깅: 구조화된 JSON 로그 사용
- 메트릭: 플러그인 성능 지표 수집
- 알림: 오류 임계값 초과 시 알림 설정
- 추적: 분산 추적으로 요청 추적
참고 자료
문서
관련 저장소
- dify-plugin-sdks - Dify 플러그인 SDK
- dify-plugins - 공식 Dify 플러그인
- dify-marketplace - Dify Marketplace
지원
문제가 발생하면 다음 채널을 통해 도움을 받을 수 있습니다: