本文适用对象:高级水平 目标:能学会让Dify在使用第三方库时不随系统重启而重复下载。
往期回顾
今天我们来解决一个粉丝的需求。
01. 问题由来
应用实战(3) 有涉及到代码节点的使用,但是当时基本上用的是比较简单的,也不涉及第三方Python库。但我们在实际项目中,不可能只用内置的库。所以 网友提问(3) 中又介绍了如何让代码节点能够使用第三方的Python库。如果还不知道如何用的,可以再点链接去学习一下。
接下来,除了用上第三方库,又有更高的要求了。因为我们改的是sandbox的依赖配置,但是发现每次启动sandbox容器,都会重新按python-requirements.txt定义的库的版本重新下载安装一遍。
如果是轻度使用,库不多的情况下,那还可以接受。但是如果重度使用,且库多到几十个,那重新启动容器时,还要重新下载这些依赖库,那这个等待时间就无法忍受了。更何况仍有朋友运行在内网无法连接公网的情况,网都没有,这个麻烦就尤为突出了。
02. 应对方案
当群里粉丝朋友提出这个需求时,麦金叔就给出了指导建议:学一下,Python查找库的顺序,定义一个新的第三方库的安装路径在启动参数里面。然后再在docker compose里面将这个路径映射为sandbox的那个路径。这样安装之后就不会再重新安装了。
这位朋友马上就领会了,而且动手能力很强。他的做法是,查找容器中安装的依赖的路径,并全部复制到host机器上。然后修改docker-compose.yaml的sandbox配置项,将主机的文件夹路径作为加载卷,配给容器。
实际上到这里,应该就可以用起来了,有新的依赖加入时,会安装到容器默认的库的位置,而这个位置已经被volumes映射为主机路径了,一切看起来都很不错。
但这个方法可行,却比较繁琐,也不利于后期升级维护。假设sandbox镜像升级,里面的Python版本或者基础库升级了。整个过程就要重新来一遍,那就不优雅了。
哪里有不便,那就有改进的空间。麦金叔的本意是,Python自带的库和后续安装的第三方库,实际上可以分开来,系统库跟着镜像走。而Python运行期,除了会从系统库查找,还可以从自定义库的路径查找。这就解决了安装和使用上的分离。
03. 大刀阔斧
研究了一下,现有的代码要实现上述讲的分离很难做到。没关系,知道怎么做,那做起来就容易了。
首先,需要在配置里面给出一个环境变量参数PTYHONUSERBASE,这样当sandbox运行时,Python就能获得一个用户安装第三方库的目录。
其次,PTYHONUSERBASE的路径,是容器内的路径。需要通过volumes来映射,从而达成主机路径和容器内路径的链接。
最后,也是最关键的一步,就是sandbox在安装第三方库的时候,能将安装路径从缺省系统库路径变为用户定义的路径。这里就必须要对sandbox的源码动手了,其实改动真不大。
获取上面配置的环境变量,然后如果有值,则在setup的InstallDependencies方法内,添加参数"--user"。
上面就是所有改动的地方,然后本地编译一下go文件。再重新打包一下镜像,导出到Dify的机器上,将docker-compose.yaml文件中sandbox的镜像改为自己编译的tag。
04. 测试验证
重新执行docker compose up -d启动之后,查看主机目录下sandbox/third-lib生成了安装的库。这里自动生成的路径有点长,需要和后面一致。
关闭docker compose down之后。再启动一下,检查sandbox容器的启动日志,已经看到满足依赖,不再下载。
不过,在运行时,还需要在prescript.py添加运行时库依赖,麦金叔这里弄得不优雅,希望官方能帮忙改进一下。
最后,我们看一下实际运行的结果。
如果你也有同样的需求,可以按上述方案进行处理。或者可以等官方的功能更新,麦金叔已经和官方沟通准备提PR,后面如果合并进正式版本,大家就只需要配置里面改一下就能完美解决了。
总结
今天的内容对绝大多数人来说都很枯燥,但如果做好之后,大家在使用Dify,并有依赖库的需求,特别是内网无法直接更新的情况下,就特别有用。
希望麦金叔的一番折腾,能给大家带来价值。
如果你对AI的发展感兴趣,欢迎一键三连。有任何问题可以私信留言,我们共同探讨。