Dify

官网:Dify.AI · The Innovation Engine for Generative AI Applications

Github官网:GitHub - langgenius/dify

官网介绍:Dify 是一个开源的LLM应用开发平台。其直观的界面结合了AI工作流程、RAG管道、代理功能、模型管理、可观察性功能等,让您可以快速从原型到生产。

官方文档:欢迎使用 Dify | 中文 | Dify

运行环境:Ubuntu 22.04 + Intel® Core™ i5-6600K CPU @ 3.50GHz × 4 + RAM(16GB)

依赖安装:PostgresSQL / Redis / Weaviate 均是本地安装

部署方式:本地源码启动

前期准备

自身依赖

dify关系真够复杂的,为了弄他一个,还得额外搞三个……如何部署,您可以参考下面的链接

Xorbits Inference

dify支持很多模型提供商,这里选用Xorbits Inference,他支持多种类型的模型:大语言模型、Embedding 模型、Rerank 模型等。如何部署,您可以参考:Xorbits Inference - xiaodu114.github.io

Xorbits Inference 项目之 Embedding 模型

部署进行中

参考:本地源码启动 | 中文 | Dify

下载项目

2024-04-19下载的版本是dify-0.6.3.zip

langgenius/dify 项目目录结构

api 目录

这里包括准备环境变量、秘钥、创建并激活虚拟环境、安装依赖等

#   进入 api 目录
cd api

#   获取 .env 文件
cp .env.example .env

#   利用下面的命令获取 key,得到的字符串赋值给 .env 文件中的 SECRET_KEY
openssl rand -base64 42

#   创建虚拟环境并激活
python3 -m venv venv
source ./venv/bin/activate	

#   安装依赖
pip install -r requirements.txt
pip install torch

#   数据库迁移
#       说明:api/migrations 这个目录应该就是和PostgreSQL数据库相关,会创建表等工作
flask db upgrade
            

得到.env文件之后,您需要根据自身安装PostgreSQLRedisWeaviate的实际情况修改对应的配置

langgenius/dify 项目部署过程 准备环境变量、秘钥、创建并激活虚拟环境、安装依赖

langgenius/dify 项目部署过程 安装 torch

langgenius/dify 项目部署过程 数据库迁移

web 目录

这里是npm的主场

#   进入 web 目录
cd web

#   获取 .env 文件
cp .env.example .env

#   安装依赖包
npm install

#   构建代码
npm run build
            

langgenius/dify 项目部署过程 web 目录,环境变量、安装依赖

langgenius/dify 项目部署过程 web 目录,构建代码

启动项目

启动项目,这里介绍两种方式:localhost、IP地址

localhost

他的优势就是需要修改的地方不多,api和web两个目录的.env中基本上都是localhost或者127.0.0.1

#   进入 api 目录,并激活虚拟环境
flask run --host 0.0.0.0 --port=5001 --debug

#   再打开一个新的终端,进入 api 目录,并激活虚拟环境
celery -A app.celery worker -P gevent -c 1 -Q dataset,generation,mail --loglevel INFO

#   再打开一个新的终端,进入 web 目录
npm run start --host=0.0.0.0 --port=3000
            

langgenius/dify 项目启动 api 目录,flask

langgenius/dify 项目启动 api 目录,celery

langgenius/dify 项目启动 web 目录,npm run start

初次访问

终于弄完了,赶紧试试吧!初次进入和再次进入界面有所不同,初次会强制要求设置管理员账户,如下图:

langgenius/dify 项目部署成功,浏览器初次访问

登录成功

设置管理员账户之后便会跳转到登录页面,登录之后如下图:

langgenius/dify 项目部署成功,登录成功之后截图

模型供应商

页面右上角点击“头像用户名称”-》下拉菜单中点击“设置”-》弹窗中点击“模型供应商”-》选择并添加你的“模型供应商”

langgenius/dify 项目部署成功,添加 Xorbits Inference

langgenius/dify 项目部署成功,设置 Jina

jina 官网:Jina AI - Your Search Foundation, Supercharged.,进去看看,说不定有惊喜呢

添加模型供应商之后,你就可以设置系统模型了,例如,Embedding 模型和Rerank 模型这里选择的是Xorbits Inference提供的

langgenius/dify 项目部署成功,已添加模型供应商列表,设置系统模型

知识库

在测试知识库的时候,“文本分段与清洗”环节发现一个问题:针对同一个文档,在分别使用 Xorbits Inference 或者 Jina 的 相同 Embedding 模型时(选用的是 jina-embeddings-v2-base-zh),得到的结果不一样,看一下截图:

下图是 Xorbits Inference 的 Embedding 模型

langgenius/dify 项目部署成功,知识库,Xorbits Inference 的 Embedding 模型,文档拆分

下图是 Jina 的 Embedding 模型

langgenius/dify 项目部署成功,知识库,Jina 的 Embedding 模型,文档拆分

下面在看一下文档的截图:

langgenius/dify 项目部署成功,知识库,被拆分的文档

查了一下出现这种现象的原因,这里记录一下。先梳理一下API(前端请求地址:http://192.168.xxx.xxx:5001/console/api/datasets/indexing-estimate)的调用逻辑

  1. api\controllers\console\datasets\datasets.py > api: /datasets/indexing-estimate > class DatasetIndexingEstimateApi(Resource) > def post(self) > response = indexing_runner.indexing_estimate ->
  2. api\core\indexing_runner.py > class IndexingRunner > def indexing_estimate ->
    • 获取 Embedding 模型实例:embedding_model_instance = self.model_manager.get_default_model_instance(tenant_id=tenant_id,model_type=ModelType.TEXT_EMBEDDING,)
    • 根据文档类型处理文档:text_docs = index_processor.extract(extract_setting, process_rule_mode=tmp_processing_rule["mode"])
    • 获取拆分器:splitter = self._get_splitter(processing_rule, embedding_model_instance),这里获取的拆分器是class EnhanceRecursiveCharacterTextSplitter(RecursiveCharacterTextSplitter)
    • 对文档进行拆分:documents = self._split_to_documents_for_estimate(text_docs=text_docs,splitter=splitter, processing_rule=processing_rule) > documents = splitter.split_documents([text_doc])
  3. api\core\splitter\text_splitter.py > def split_documents(self, documents: Iterable[Document] ) > def create_documents(self, texts: list[str], metadatas: Optional[list[dict]] = None) > self.split_text(text)(这里便会调用拆分器的 split_text 方法) ->
  4. api\core\splitter\text_splitter.py > class RecursiveCharacterTextSplitter(TextSplitter) > def split_text(self, text: str) > def _split_text(self, text: str, separators: list[str])

    问题就出在该方法中的if self._length_function(s) < self._chunk_size:处,这里会利用_length_function方法来计算文本块的token数,之后和self._chunk_size比较,上面采用的两个供应商的模型计算token的方式不同

langgenius/dify 项目代码,文本拆分

再看一下获取拆分器截图,下图中可以看到模型实例调用get_num_tokens方法来计算token数

langgenius/dify 项目代码,拆分器

下图是 Xorbits Inference 中 Embedding 模型的计算方法

langgenius/dify 项目代码,Xorbits Inference 中 Embedding 模型的计算方法

下图是 Jina 中 Embedding 模型的计算方法

langgenius/dify 项目代码,Jina 中 Embedding 模型的计算方法

最后在模拟一下他们是如何计算的

import sys
sys.path.append(".")

texts = ["","",""]
print("---------------------------------- 下面是 Jina/jina-embeddings-v2-base-zh 的计算结果")
from transformers import AutoTokenizer
gpt2_tokenizer_path = "E:\\llm\\dify-0.x.x\\api\\core\\model_runtime\\model_providers\\jina\\text_embedding\\tokenizer"
tokenizer = AutoTokenizer.from_pretrained(gpt2_tokenizer_path)
for text in texts:
    tokens = tokenizer.encode(text)
    num_tokens = len(tokens)
    print(num_tokens)

print("---------------------------------- 下面是 Xorbits Inference/jina-embeddings-v2-base-zh 的计算结果")
from core.model_runtime.model_providers.__base.tokenizers.gpt2_tokenzier import GPT2Tokenizer
for text in texts:
    print(GPT2Tokenizer.get_num_tokens(text))
            

IP地址

正常情况下应该都需要其他电脑访问,这里才是重点

踩坑经历

开始只是修改了web/.env该文件,修改之后,在重新npm run build一下,如下图:

langgenius/dify 项目,IP地址访问,修改 web/.env 文件

构建结束之后,就可以启动了,命令是npm run start --host=192.168.13.175 --port=3000,如下图:

langgenius/dify 项目,IP地址访问,IP地址启动

换另外一台电脑访问试试,可以访问没有问题,但是还是发现了点小问题,改的不够彻底啊,如下图:

langgenius/dify 项目,IP地址访问,浏览器IP地址访问,模型提供商图片显示异常

要想IP地址访问,需要修改api/.envweb/.env这两个环境变量文件,之后在重新编译启动

api/.env

langgenius/dify 项目,IP地址访问,修改 api/.env 文件

修改完之后重新启动一下,如下:

#   为了方便使用
source ./venv/bin/activate	

#   进入 api 目录,并激活虚拟环境
flask run --host 0.0.0.0 --port=5001 --debug

#   【视情况而定】再打开一个新的终端,进入 api 目录,并激活虚拟环境
celery -A app.celery worker -P gevent -c 1 -Q dataset,generation,mail --loglevel INFO
            

web/.env

langgenius/dify 项目,IP地址访问,修改 web/.env 文件

修改完之后

#   进入 web 目录
#   构建代码
npm run build

#   IP地址启动
npm run start --host=192.168.13.175 --port=3000
            

之后在访问就没有问题了,如下图:

langgenius/dify 项目,IP地址访问,浏览器IP地址访问,模型提供商图片显示正常

dify-sandbox

2024-04-29我们老大发现的问题,如下图:

langgenius/dify 项目,代码执行 测试

langgenius/dify 项目,代码执行 异常

顺着给出的错误信息一路搜索,发现是dify-sandbox的问题,看名字像是一个沙箱。线索如下:

langgenius/dify 项目,代码执行 异常,问题追踪一

langgenius/dify 项目,代码执行 异常,问题追踪二

想着再部署一下就行了呗,结果发现这个项目没有开源,也不知道啥时候开源( Will dify-sandbox be open source? )。废了半天劲儿,终于在没有使用 docker 的情况下部署成功了。结果在这儿等着我呢,还是得用 docker 啊!!!

复制一下docker/docker-compose.middleware.yaml这个文件,这里命名为:docker-compose.middleware-ddz.yaml,文件内容只保留dify-sandbox相关的(其他的都已经弄好了),如下:

version: '3'
services:
  # The DifySandbox
  sandbox:
    image: langgenius/dify-sandbox:latest
    restart: always
    cap_add:
      - SYS_ADMIN
    environment:
      # The DifySandbox configurations
      API_KEY: dify-sandbox
      GIN_MODE: 'release'
      WORKER_TIMEOUT: 15
    ports:
      - "8194:8194"
            

之后在该文件所在的目录打开终端,执行docker compose -f docker-compose.middleware-ddz.yaml -p dify-sandbox up -d,执行成功之后你可以使用docker ps查看正在运行的容器

langgenius/dify 项目,docker 启动 dify-sandbox

langgenius/dify 项目,代码执行,执行正常

请忽略截图中 dify 版本的问题

一键脚本启动

为了能快速启动 dify,弄了一个 dify 脚本,名称为:dify一键启动.sh,内容如下:

#!/bin/bash

# 打开一个新的终端,并指定终端窗口标题
gnome-terminal --title 'dify 项目之  dify-sandbox' --working-directory=/home/ddz/llm/2-code/dify-0.6.0-fix1 -- /bin/bash -c 'cd docker; \
docker compose -f docker-compose.middleware-ddz.yaml -p dify-sandbox up -d'

gnome-terminal --title 'dify 项目之 flask' --working-directory=/home/ddz/llm/2-code/dify-0.6.0-fix1 -- /bin/bash -c 'cd api; \
source ./venv/bin/activate; \
flask run --host 0.0.0.0 --port=5001 --debug'

gnome-terminal --title 'dify 项目之 celery' --working-directory=/home/ddz/llm/2-code/dify-0.6.0-fix1 -- /bin/bash -c 'cd api; \
source ./venv/bin/activate; \
celery -A app.celery worker -P gevent -c 1 -Q dataset,generation,mail --loglevel INFO'

gnome-terminal --title 'dify 项目之 Web' --working-directory=/home/ddz/llm/2-code/dify-0.6.0-fix1 -- /bin/bash -c 'cd web; \
npm run start --host=192.168.13.175 --port=3000'

exit