文章目录
问题
最近在研究利用多智能体进行实体关系抽取,所以想到了用Dify的工作流来做,工作流是搭建好了,但是发现这些生成的内容好像无法导出到本地或者线上导出。
原因分析
1、沙箱环境的限制
Dify 的代码执行环境是基于沙箱(Sandbox)的,这种环境是为了确保代码执行的安全性和隔离性。沙箱环境限制了代码对本地文件系统和外部网络的直接访问,因此无法直接将生成的内容导出到本地或线上存储。沙箱环境中代码的执行和资源访问受到严格控制,例如临时文件的创建和删除都在沙箱内部完成。
2、功能设计的侧重点
Dify 的工作流界面主要是为了方便用户直接查看和使用生成的内容,而不是进行复杂的数据处理。这种设计使得用户可以快速获得所需信息,但同时也限制了数据导出的灵活性。
思路
那么在查了一些博主的方案后,发现是代码在docker部署下的sandbox容器目录中临时运行的,那么有了下面的思路:
- 在工作流中添加代码节点,将生成内容写入到sandbox的临时环境下
- 在宿主机建立与镜像位置的映射,将沙箱文件映射到宿主机,免于进入docker镜像查看
步骤
操作系统:ubuntu 18.04
dify版本:1.1.3
部署方式:docker compose 部署
1、在sandbox容器和本地中创建存放文件的目录
1.1 sandbox
在启动dify时候可以看到有一个Container 名字为 docker-sandbox-1 这个就是我们的代码执行节点的运行目录。
如果容器正在运行,可以使用 docker exec 命令进入容器的 shell 环境。可以运行以下命令:
docker exec -it docker-sandbox-1 /bin/bash
进入容器后找到如下目录,并执行以下操作:
#进入目录
cd /var/sandbox/sandbox-python/tmp/
# 创建一个file的目录 可以自定义
mkdir file
# 给予此文件夹可写入权限 个人建议给于整个目录权限
cd /var
chmod -R 777 *
1.2 本地
#进入本地目录
cd /dify/docker/volumes/sandbox/
# 创建一个file的目录 可以自定义
mkdir file
# 给予此文件夹可写入权限 个人建议给于整个目录权限
chmod -R 777 *
2、建立宿主机映射
在dify的docker目录下,打开docker-compose.yaml文件,找到以下内容,在volumes下加一行
# ./volumes/sandbox/file 是宿主机目录下的地址
# /var/sandbox/sandbox-python/tmp/file:rw 是容器中的地址 赋予读写权限
- ./volumes/sandbox/file:/var/sandbox/sandbox-python/tmp/file:rw
# The DifySandbox
sandbox:
image: langgenius/dify-sandbox:0.2.11
restart: always
environment:
# The DifySandbox configurations
# Make sure you are changing this key for your deployment with a strong key.
# You can generate a strong key using `openssl rand -base64 42`.
API_KEY: ${SANDBOX_API_KEY:-dify-sandbox}
GIN_MODE: ${SANDBOX_GIN_MODE:-release}
WORKER_TIMEOUT: ${SANDBOX_WORKER_TIMEOUT:-15}
ENABLE_NETWORK: ${SANDBOX_ENABLE_NETWORK:-true}
HTTP_PROXY: ${SANDBOX_HTTP_PROXY:-http://ssrf_proxy:3128}
HTTPS_PROXY: ${SANDBOX_HTTPS_PROXY:-http://ssrf_proxy:3128}
SANDBOX_PORT: ${SANDBOX_PORT:-8194}
volumes:
- ./volumes/sandbox/dependencies:/dependencies
- ./volumes/sandbox/conf:/conf
- ./volumes/sandbox/file:/var/sandbox/sandbox-python/tmp/file:rw
healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:8194/health' ]
networks:
- ssrf_proxy_network
至此,准备工作已完成,接下来就是在Dify进行操作
3、 在dify中创建工作流
工作室-创建空白应用-选择工作流
在这里创建了四个节点,开始、结束、大模型回答节点以及代码执行节点
4、 各节点设计
开始、结束节点就不介绍了,简单介绍中间两个节点
LLM节点
此节点的作用主要是调用大模型生成回答。
文件导出代码节点
在这里传入了两个参数,一个是上个节点大模型生成的text回答,一个是方便导出文件辨别加入了user_id(系统自动生成)。
代码如下:
文件路径为临时代码执行位置,在sandbox的docker容器中,后面展示如何进入该位置
import os
import json
def main(arg1: list, user_id: str) -> dict:
# 定义文件路径
file_path = f'/tmp/file/{user_id}_response.json'
# 获取目录路径
directory = os.path.dirname(file_path)
# 如果目录不存在,则创建目录
if not os.path.exists(directory):
os.makedirs(directory)
# 将 JSON 对象序列化为字符串
json_str = json.dumps(arg1, ensure_ascii=False, indent=4)
# 打开文件并写入内容
with open(file_path, 'w', encoding='utf-8') as f:
f.write(json_str)
# 返回结果
return {
"result": f'文件生成完毕:{file_path}'
}
结果
运行
在Dify运行工作流,在input_text中输入你好,工作流会自动执行,最后生成结果如下
查看文件生成
当然咱们的目标不是看大模型生成什么,而是看是否能导出本地
Docker容器:
打开刚才的容器,进入目录发现文件已经以规定格式生成
宿主机
在dify/docker/volumes/sandbox/file 文件夹中发现同样生成了一份文件
打开看一下
至此,问题解决
那么这只是一个初步探索,目前发现导出还是以字符串格式,那么我的目标是json或者其他格式,可以通过格式转换或其他方法进行;另外,本地导出解决了,后面打算通过线上导出方式,通过http、钉钉、邮件等方式尝试