Dify Plugin Daemon 是一个用 Go 语言编写的服务,负责管理插件的完整生命周期。它支持三种不同的运行时模式,每种模式针对不同的使用场景进行了优化。
运行时类型 | 通信方式 | 适用场景 | 特点 |
|---|---|---|---|
「Local Runtime」 | STDIN/STDOUT | 生产环境 | 进程隔离、资源可控 |
「Debug Runtime」 | TCP | 开发调试 | 热重载、实时调试 |
「Serverless Runtime」 | HTTP | 云端部署 | 弹性扩展、按需计费 |
┌─────────────────────────────────────┐
│ Dify API Server │
│ (Python/Flask) │
└──────────────┬────────────────────┘
│ HTTP Request
▼
┌────────────────────────────────────────────────────────────────────────────┐
│ Plugin Daemon (Go) │
├────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ HTTP │ │ Session │ │ Plugin │ │ Backwards │ │
│ │ Server │──│ Manager │──│ Manager │──│ Invocation │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │
│ │ ┌─────────────────────┼─────────────────────┐ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Control │ │ Local │ │ Debug │ │ Serverless │ │
│ │ Panel │ │ Runtime │ │ Runtime │ │ Runtime │ │
│ └──────────────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└──────────────────────────┼─────────────────┼─────────────────┼────────────┘
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Plugin │ │ TCP │ │ HTTP │
│ Process │ │ Client │ │ Endpoint │
│ (Python) │ │ (Dev IDE) │ │ (Lambda) │
└────────────┘ └────────────┘ └────────────┘
组件 | 职责 |
|---|---|
「HTTP Server」 | 接收来自 Dify API 的请求 |
「Session Manager」 | 管理插件执行会话和状态 |
「Plugin Manager」 | 插件生命周期管理(安装、启动、停止) |
「Control Panel」 | 统一控制各运行时的信号和调用 |
「Backwards Invocation」 | 处理插件回调 Dify API 的请求 |
本地运行时是生产环境的默认模式,通过子进程方式启动插件,使用标准输入输出进行通信。
┌─────────────────────────────────────────────────────────────────┐
│ Plugin Daemon Process │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Plugin Manager │ │ IO Tunnel │ │
│ │ │ │ │ │
│ │ - Install │ │ - Message │ │
│ │ - Uninstall │ │ Encoding │ │
│ │ - Start/Stop │ │ - Message │ │
│ │ - Health Check │ │ Decoding │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ │ ┌───────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Process Spawner │ │
│ │ │ │
│ │ os/exec.Command("python", "-m", "main") │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ STDIN │ │ STDOUT │ │ │
│ │ │ (Write) │ │ (Read) │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ │ │
│ └─────────┼────────────────┼──────────────────┘ │
└────────────┼────────────────┼───────────────────────────────────┘
│ │
│ Pipe │ Pipe
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Plugin Process (Python) │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ sys.stdin │ │ sys.stdout │ │
│ │ (Read) │ │ (Write) │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Message Handler │ │
│ │ │ │
│ │ - Parse Request │ │
│ │ - Route to Tool/Model/Extension │ │
│ │ - Execute Plugin Logic │ │
│ │ - Format Response │ │
│ └─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Plugin Implementation │ │
│ │ │ │
│ │ - Tool: GoogleSearchTool._invoke() │ │
│ │ - Model: OpenAILLM._invoke() │ │
│ │ - Extension: MyExtension.handle() │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
1. Plugin Daemon 接收启动请求
│
▼
2. Plugin Manager 检查插件状态
│
▼
3. 准备 Python 运行环境
- 设置 PYTHON_INTERPRETER_PATH
- 配置虚拟环境
- 安装依赖 (uv/pip)
│
▼
4. 创建子进程
cmd := exec.Command(pythonPath, "-m", "main")
cmd.Stdin = stdinPipe
cmd.Stdout = stdoutPipe
cmd.Stderr = stderrPipe
│
▼
5. 启动进程并建立通信通道
cmd.Start()
│
▼
6. 注册到 Session Manager
│
▼
7. 返回启动成功
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Dify │ │ Plugin │ │ IO │ │ Plugin │
│ API │ │ Daemon │ │ Tunnel │ │ Process │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ HTTP Request │ │ │
│───────────────►│ │ │
│ │ │ │
│ │ Encode Msg │ │
│ │───────────────►│ │
│ │ │ │
│ │ │ Write STDIN │
│ │ │───────────────►│
│ │ │ │
│ │ │ │ Execute
│ │ │ │ Plugin
│ │ │ │ Logic
│ │ │ │
│ │ │ Read STDOUT │
│ │ │◄───────────────│
│ │ │ │
│ │ Decode Msg │ │
│ │◄───────────────│ │
│ │ │ │
│ HTTP Response │ │ │
│◄───────────────│ │ │
│ │ │ │
本地运行时使用基于行的 JSON 消息协议:
{
"type": "invoke",
"session_id": "uuid-session-id",
"request_id": "uuid-request-id",
"plugin_type": "tool",
"action": "invoke",
"data": {
"tool_name": "google_search",
"parameters": {
"query": "Dify AI platform"
},
"credentials": {
"api_key": "encrypted_key"
}
}
}
{
"type": "response",
"session_id": "uuid-session-id",
"request_id": "uuid-request-id",
"status": "success",
"data": {
"type": "text",
"content": "Search results..."
}
}
{"type": "stream_start", "session_id": "...", "request_id": "..."}
{"type": "stream_chunk", "data": {"content": "Hello"}}
{"type": "stream_chunk", "data": {"content": " World"}}
{"type": "stream_end", "data": {"total_tokens": 100}}
// 伪代码示意
func (r *LocalRuntime) HealthCheck() error {
// 发送心跳消息
heartbeat := Message{Type: "heartbeat"}
r.stdin.Write(heartbeat.Encode())
// 等待响应,超时时间 5 秒
select {
case resp := <-r.stdout:
if resp.Type == "heartbeat_ack" {
returnnil
}
case <-time.After(5 * time.Second):
return ErrHealthCheckTimeout
}
}
# manifest.yaml 中的资源配置
resource:
memory: 268435456 # 256MB 内存限制
permission:
tool:
enabled: true
storage:
enabled: true
size: 1048576 # 1MB 存储限制
# Plugin SDK 中的本地运行时处理
import sys
import json
class LocalRuntimeHandler:
def __init__(self):
self.stdin = sys.stdin
self.stdout = sys.stdout
def run(self):
"""主循环:读取请求,处理,返回响应"""
whileTrue:
# 从 STDIN 读取一行 JSON
line = self.stdin.readline()
ifnot line:
break
try:
request = json.loads(line)
response = self.handle_request(request)
# 写入响应到 STDOUT
self.stdout.write(json.dumps(response) + "\n")
self.stdout.flush()
except Exception as e:
self.send_error(str(e))
def handle_request(self, request: dict) -> dict:
"""路由请求到对应的处理器"""
action = request.get("action")
if action == "invoke":
return self.invoke_plugin(request)
elif action == "validate":
return self.validate_credentials(request)
elif action == "heartbeat":
return {"type": "heartbeat_ack"}
else:
raise ValueError(f"Unknown action: {action}")
调试运行时用于插件开发阶段,通过 TCP 连接实现远程调试。
┌─────────────────────────────────────────────────────────────────┐
│ Dify Cloud / Self-hosted │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Plugin Daemon │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Debug Runtime Server │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────┐ ┌─────────────────────────┐ │ │ │
│ │ │ │ TCP Server │ │ Connection Manager │ │ │ │
│ │ │ │ Port: 5003 │ │ │ │ │ │
│ │ │ │ │ │ - Auth Validation │ │ │ │
│ │ │ │ Listen() │ │ - Session Tracking │ │ │ │
│ │ │ │ Accept() │ │ - Message Routing │ │ │ │
│ │ │ └──────┬──────┘ └────────────┬────────────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └────────────┬───────────┘ │ │ │
│ │ │ │ │ │ │
│ │ └──────────────────────┼───────────────────────────┘ │ │
│ │ │ │ │
│ └─────────────────────────┼───────────────────────────────┘ │
│ │ │
└────────────────────────────┼─────────────────────────────────────┘
│
│ TCP Connection
│ (Full Duplex)
│
┌────────────────────────────┼─────────────────────────────────────┐
│ │ │
│ ┌─────────────────────────▼───────────────────────────────┐ │
│ │ Debug Plugin Client │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ TCP Client │ │ │
│ │ │ │ │ │
│ │ │ Connect(host:port) │ │ │
│ │ │ Authenticate(api_key) │ │ │
│ │ │ SendReceive(messages) │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Plugin Implementation │ │ │
│ │ │ │ │ │
│ │ │ - Hot Reload Support │ │ │
│ │ │ - Breakpoint Debugging │ │ │
│ │ │ - Real-time Logging │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ Developer's Local Machine │
└──────────────────────────────────────────────────────────────────┘
┌──────────┐ ┌──────────┐
│ Debug │ │ Plugin │
│ Client │ │ Daemon │
└────┬─────┘ └────┬─────┘
│ │
│ 1. TCP Connect (host:5003) │
│────────────────────────────────────────►│
│ │
│ 2. Connection Accepted │
│◄────────────────────────────────────────│
│ │
│ 3. Auth Request │
│ { │
│ "type": "auth", │
│ "api_key": "debug-key-xxx", │
│ "plugin_id": "my-plugin" │
│ } │
│────────────────────────────────────────►│
│ │
│ 4. Validate Key │
│ 5. Register │
│ Session │
│ │
│ 6. Auth Response │
│ { │
│ "type": "auth_success", │
│ "session_id": "uuid" │
│ } │
│◄────────────────────────────────────────│
│ │
│ 7. Ready for Requests │
│◄───────────────────────────────────────►│
│ │
# .env 文件
INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=debug-plugin.dify.dev # 或自托管地址
REMOTE_INSTALL_PORT=5003
REMOTE_INSTALL_KEY=your-debug-api-key
调试运行时使用 TCP 全双工通信,消息格式与本地运行时类似,但增加了调试相关的消息类型:
// 日志消息
{
"type": "log",
"level": "debug",
"message": "Processing request...",
"timestamp": "2024-01-01T00:00:00Z"
}
// 断点命中
{
"type": "breakpoint",
"file": "tools/search.py",
"line": 42,
"variables": {
"query": "test search"
}
}
// 热重载通知
{
"type": "reload",
"status": "success",
"message": "Plugin reloaded successfully"
}
import socket
import json
import threading
class DebugRuntimeClient:
def __init__(self, host: str, port: int, api_key: str):
self.host = host
self.port = port
self.api_key = api_key
self.socket = None
self.session_id = None
def connect(self):
"""建立 TCP 连接并认证"""
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((self.host, self.port))
# 发送认证请求
auth_msg = {
"type": "auth",
"api_key": self.api_key,
"plugin_id": self.plugin_id
}
self._send(auth_msg)
# 等待认证响应
response = self._receive()
if response.get("type") == "auth_success":
self.session_id = response.get("session_id")
print(f"Connected with session: {self.session_id}")
else:
raise AuthenticationError("Authentication failed")
def run(self):
"""主循环:处理来自服务器的请求"""
whileTrue:
try:
request = self._receive()
if request:
response = self.handle_request(request)
self._send(response)
except ConnectionError:
self.reconnect()
def _send(self, data: dict):
"""发送消息"""
message = json.dumps(data) + "\n"
self.socket.sendall(message.encode())
def _receive(self) -> dict:
"""接收消息"""
buffer = b""
whileb"\n"notin buffer:
chunk = self.socket.recv(4096)
ifnot chunk:
raise ConnectionError("Connection closed")
buffer += chunk
line, _ = buffer.split(b"\n", 1)
return json.loads(line.decode())
特性 | 说明 |
|---|---|
「热重载」 | 修改代码后自动重新加载,无需重启 |
「实时日志」 | 日志实时传输到 Dify 平台显示 |
「断点调试」 | 支持在 IDE 中设置断点 |
「变量检查」 | 查看运行时变量状态 |
「请求追踪」 | 追踪每个请求的完整执行路径 |
无服务器运行时支持将插件部署到 AWS Lambda 等云函数平台。
┌─────────────────────────────────────────────────────────────────┐
│ Dify Platform │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Plugin Daemon │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Serverless Connector │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────┐ ┌─────────────────────────┐ │ │ │
│ │ │ │ HTTP Client │ │ Request Builder │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ POST() │ │ - Serialize Request │ │ │ │
│ │ │ │ Timeout │ │ - Add Auth Headers │ │ │ │
│ │ │ │ Retry │ │ - Handle Response │ │ │ │
│ │ │ └──────┬──────┘ └────────────┬────────────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └────────────┬───────────┘ │ │ │
│ │ │ │ │ │ │
│ │ └──────────────────────┼───────────────────────────┘ │ │
│ │ │ │ │
│ └─────────────────────────┼───────────────────────────────┘ │
│ │ │
└────────────────────────────┼─────────────────────────────────────┘
│
│ HTTPS Request
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ AWS Lambda │ │ Google Cloud │ │ Azure │
│ │ │ Functions │ │ Functions │
│ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │
│ │ Plugin │ │ │ │ Plugin │ │ │ │ Plugin │ │
│ │ Handler│ │ │ │ Handler│ │ │ │ Handler│ │
│ └────────┘ │ │ └────────┘ │ │ └────────┘ │
└──────────────┘ └──────────────┘ └──────────────┘
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Dify │ │ Plugin │ │ Serverless│ │ Lambda │
│ API │ │ Daemon │ │ Connector │ │ Function │
└────┬─────┘ └────┬─────┘ └────┬──────┘ └────┬─────┘
│ │ │ │
│ Plugin Invoke │ │ │
│───────────────►│ │ │
│ │ │ │
│ │ Build Request │ │
│ │───────────────►│ │
│ │ │ │
│ │ │ HTTP POST │
│ │ │───────────────►│
│ │ │ │
│ │ │ │ Cold Start
│ │ │ │ (if needed)
│ │ │ │
│ │ │ │ Execute
│ │ │ │ Handler
│ │ │ │
│ │ │ HTTP Response │
│ │ │◄───────────────│
│ │ │ │
│ │ Parse Response│ │
│ │◄───────────────│ │
│ │ │ │
│ Plugin Result │ │ │
│◄───────────────│ │ │
│ │ │ │
POST /invoke HTTP/1.1
Host: lambda-endpoint.amazonaws.com
Content-Type: application/json
X-Dify-Plugin-Session: session-id
X-Dify-Plugin-Signature: hmac-signature
{
"action": "invoke",
"plugin_type": "tool",
"tool_name": "google_search",
"parameters": {
"query": "Dify AI"
},
"credentials": {
"api_key": "..."
},
"context": {
"user_id": "user-123",
"app_id": "app-456"
}
}
{
"status": "success",
"data": {
"type": "text",
"content": "Search results..."
},
"usage": {
"execution_time_ms": 1500,
"memory_used_mb": 128
}
}
对于需要流式输出的场景,使用 Server-Sent Events (SSE):
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
event: start
data: {"session_id": "..."}
event: chunk
data: {"content": "Hello"}
event: chunk
data: {"content": " World"}
event: end
data: {"total_tokens": 100}
# lambda_handler.py
import json
from dify_plugin import Tool
def lambda_handler(event, context):
"""AWS Lambda 入口函数"""
try:
# 解析请求
body = json.loads(event.get('body', '{}'))
action = body.get('action')
if action == 'invoke':
return handle_invoke(body)
elif action == 'validate':
return handle_validate(body)
else:
return {
'statusCode': 400,
'body': json.dumps({'error': 'Unknown action'})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}
def handle_invoke(body: dict) -> dict:
"""处理插件调用"""
tool_name = body.get('tool_name')
parameters = body.get('parameters', {})
credentials = body.get('credentials', {})
# 实例化工具并执行
tool = get_tool_instance(tool_name)
result = tool._invoke(parameters, credentials)
return {
'statusCode': 200,
'body': json.dumps({
'status': 'success',
'data': result.to_dict()
})
}
# serverless.yml
service:dify-plugin-example
provider:
name:aws
runtime:python3.12
region:us-east-1
memorySize:256
timeout:30
functions:
plugin:
handler:lambda_handler.lambda_handler
events:
-http:
path:/invoke
method:post
cors:true
plugins:
-serverless-python-requirements
custom:
pythonRequirements:
dockerizePip:true
特性 | Local Runtime | Debug Runtime | Serverless Runtime |
|---|---|---|---|
「冷启动」 | 无 | 无 | 有(首次调用) |
「扩展性」 | 单机 | 单机 | 自动扩展 |
「成本」 | 固定 | 固定 | 按调用计费 |
「延迟」 | 最低 | 中等 | 较高(冷启动时) |
「调试」 | 困难 | 最佳 | 困难 |
「部署」 | 简单 | 开发用 | 复杂 |
┌─────────────────┐
│ 开始选择 │
│ 运行时模式 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 是否在开发 │
│ 调试阶段? │
└────────┬────────┘
│
┌──────────────┴──────────────┐
│ Yes │ No
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Debug Runtime │ │ 需要弹性 │
│ │ │ 扩展吗? │
│ - 热重载 │ └────────┬────────┘
│ - 实时日志 │ │
│ - 断点调试 │ ┌──────────┴──────────┐
└─────────────────┘ │ Yes │ No
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Serverless │ │ Local Runtime │
│ Runtime │ │ │
│ │ │ - 低延迟 │
│ - 自动扩展 │ │ - 资源可控 │
│ - 按需计费 │ │ - 简单部署 │
└─────────────────┘ └─────────────────┘
场景 | 推荐运行时 | 原因 |
|---|---|---|
插件开发 | Debug | 支持热重载和调试 |
小规模生产 | Local | 简单稳定,延迟低 |
大规模生产 | Serverless | 自动扩展,成本优化 |
私有部署 | Local | 数据安全,完全控制 |
突发流量 | Serverless | 弹性扩展应对峰值 |
插件可以回调 Dify API 获取额外信息:
class MyTool(Tool):
def _invoke(self, parameters: dict) -> ToolInvokeMessage:
# 回调获取应用信息
app_info = self.session.app.get_info()
# 回调获取用户信息
user_info = self.session.user.get_info()
# 回调存储数据
self.session.storage.set("key", "value")
return self.create_text_message("Done")
// Session Manager 核心功能
type SessionManager struct {
sessions map[string]*Session
mutex sync.RWMutex
}
type Session struct {
ID string
PluginID string
Runtime RuntimeType
CreatedAt time.Time
LastActive time.Time
State SessionState
}
func (sm *SessionManager) CreateSession(pluginID string, runtime RuntimeType) *Session {
session := &Session{
ID: uuid.New().String(),
PluginID: pluginID,
Runtime: runtime,
CreatedAt: time.Now(),
State: StateActive,
}
sm.mutex.Lock()
sm.sessions[session.ID] = session
sm.mutex.Unlock()
return session
}
# 插件端错误处理
class PluginError(Exception):
"""插件基础错误"""
pass
class CredentialsValidationError(PluginError):
"""凭证验证失败"""
pass
class RateLimitError(PluginError):
"""速率限制"""
pass
class TimeoutError(PluginError):
"""执行超时"""
pass
# 错误响应格式
{
"status": "error",
"error": {
"type": "CredentialsValidationError",
"message": "Invalid API key",
"code": "INVALID_CREDENTIALS"
}
}
# 环境变量配置
PLUGIN_MAX_EXECUTION_TIMEOUT: 2400 # 最大执行超时(秒)
PYTHON_ENV_INIT_TIMEOUT: 640 # Python 环境初始化超时
PLUGIN_WORKER_NUM: 4 # 工作进程数
// HTTP 连接池配置(Serverless Runtime)
var httpClient = &http.Client{
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
Timeout: 30 * time.Second,
}
// 插件资源缓存
type AssetCache struct {
cache *lru.Cache
size int
}
func (c *AssetCache) Get(key string) ([]byte, bool) {
if val, ok := c.cache.Get(key); ok {
return val.([]byte), true
}
return nil, false
}
// Debug Runtime 认证
func (s *DebugServer) Authenticate(conn net.Conn, apiKey string) error {
// 验证 API Key
if !s.validateAPIKey(apiKey) {
return ErrInvalidAPIKey
}
// 检查速率限制
if s.isRateLimited(conn.RemoteAddr()) {
return ErrRateLimited
}
return nil
}
Dify Plugin Daemon 的三种运行时模式各有特点:
运行时 | 核心机制 | 最佳场景 |
|---|---|---|
「Local」 | 子进程 + STDIN/STDOUT | 生产环境、私有部署 |
「Debug」 | TCP 全双工连接 | 开发调试、快速迭代 |
「Serverless」 | HTTP 调用云函数 | 弹性扩展、成本优化 |
这种多运行时架构设计使 Dify 能够灵活适应不同的部署场景和开发需求,同时保持了统一的插件开发体验。