一、前言:
Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,OceanBase 从 4.3.3 版本开始支持了向量数据类型的存储和检索,在 Dify 0.11.0 中开始支持使用 OceanBase 作为其向量数据库,接下来将通过实际搭建使用。
实验环境
Git
Docker 和 Docker Compose
MySQL 客户端(可选,如果使用 Docker 部署 OceanBase 则必须)
以上安装环境mysql客户端可采取开通 OBCloud 实例,还需一台安装部署doker(compose)服务器
当前采取虚拟机方式:
- 系统版本:Centos 7.6
- 内存:8G
- 存储:30G
- docker版本:26.1.4
- 本地还需安装ob4.3版本单实例数据库
当前部署参考官网提供网址https://gitee.com/oceanbase-devhub/dify/blob/master/docs/dify@oceanbase-workshop.md
二、平台搭建步骤
1. 获取 OceanBase 数据库
进行实验之前,我们需要先获取 OceanBase 数据库,目前可行的方式有两种:使用 OBCloud 实例或者使用 Docker 本地部署单机版 OceanBase 数据库。我们在此推荐 OBCloud 实例,因为它部署和管理都更加简单,且不需要本地环境支持。
##### 1.1 注册并开通 OBCloud 实例
进入[OB Cloud 云数据库 365 天免费试用](https://gitee.com/link?target=https%3A%2F%2Fwww.oceanbase.com%2Ffree-trial)页面,点击“立即试用”按钮,注册并登录账号,填写相关信息,开通实例,等待创建完成。
##### 1.2 获取数据库实例连接串
进入实例详情页的“实例工作台”,点击“连接”-“获取连接串”按钮来获取数据库连接串,将其中的连接信息填入后续步骤中创建的 .env 文件内。
![image](https://ask.oceanbase.com/uploads/default/original/3X/1/7/17961488c3ef27af6e2559c0ce53eb3f04681449.jpeg)
##### 1.3 创建多个数据库
为了分别存放结构化数据(满足 alembic 的数据库结构迁移方案要求)和向量数据,我们需要至少创建两个数据库。可在实例详情页面中的“数据库管理”功能中创建数据库。
![image](https://ask.oceanbase.com/uploads/default/original/3X/f/2/f293aea84401f2d09795eea6e3d9473072404432.jpeg)
2. 克隆项目
我们针对 Dify 的 0.12.1 版本进行了 MySQL 协议兼容的修改,并且上传到了我们 fork 的代码仓库中。大家网络条件好的话推荐克隆 Github 上的版本,否则克隆 Gitee 上的版本。
git命令如果没有可以 yum -y install git 安装
git clone https://github.com/oceanbase-devhub/dify.git
# 如果网络条件差
git clone https://gitee.com/oceanbase-devhub/dify.git
为保证 dify 目录的代码是最新的状态,需要执行 git pull 命令,拉取最新的代码
cd dify
git pull
3.安装docker到虚拟机
##### 3.1安装docker
这里我采取的是离线安装的方法
[root@localhost local]# tar -xvf docker-26.1.4.tgz
docker/
docker/docker-init
docker/runc
docker/docker
docker/docker-proxy
docker/containerd
docker/ctr
docker/dockerd
docker/containerd-shim
[root@localhost local]# ls -l docker
total 195504
-rwxr-xr-x. 1 lr lr 32751272 May 14 17:29 containerd
-rwxr-xr-x. 1 lr lr 6012928 May 14 17:29 containerd-shim
-rwxr-xr-x. 1 lr lr 18194536 May 14 17:29 ctr
-rwxr-xr-x. 1 lr lr 61113382 May 14 17:29 docker
-rwxr-xr-x. 1 lr lr 68874208 May 14 17:29 dockerd
-rwxr-xr-x. 1 lr lr 708616 May 14 17:29 docker-init
-rwxr-xr-x. 1 lr lr 2928514 May 14 17:29 docker-proxy
-rwxr-xr-x. 1 lr lr 9600696 May 14 17:29 runc
[root@localhost local]# mv docker/* /usr/bin/
vim /etc/systemd/system/docker.service
添加文件内容:
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
[Service]
Type=notify
ExecStart=/usr/bin/dockerd --selinux-enabled=false
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TimeoutStartSec=0
Delegate=yes
KillMode=process
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s
[Install]
WantedBy=multi-user.target
赋执行权限
chmod +x /etc/systemd/system/docker.service
systemctl daemon-reload
#开机启动
systemctl enable docker.service
启动docker
systemctl start docker
root@zhz11g_dg docker]# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/etc/systemd/system/docker.service; enabled; vendor preset: disabled)
Active: active (running) since Wed 2024-04-10 15:57:42 CST; 4s ago
Docs: https://docs.docker.com
Main PID: 11471 (dockerd)
Tasks: 17
Memory: 39.4M
CGroup: /system.slice/docker.service
├─11471 /usr/bin/dockerd --selinux-enabled=false
└─11478 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
##### 3.2安装docker Compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
0 0 0 0 0 0 0 0 --:--:-- 0:00:01 --:--:-- 0
100 61.2M 100 61.2M 0 0 34070 0 0:31:24 0:31:24 --:--:-- 34053
下载完成后,确保文件具有执行权限。
sudo chmod +x /usr/local/bin/docker-compose
验证安装:最后,验证docker-compose是否正确安装。
docker-compose --version
Docker Compose version v2.31.0
4. 拉取 Docker 镜像
进入到 dify 的工作目录中的docker目录下,执行docker compose --profile workshop pull,拉取所需要的镜像
/usr/local/bin/docker-compose --profile workshop pull
WARN[0000] The "CERTBOT_EMAIL" variable is not set. Defaulting to a blank string.
WARN[0000] The "CERTBOT_DOMAIN" variable is not set. Defaulting to a blank string.
[+] Pulling 60/47
✔ worker Skipped - Image is already being pulled by api 0.0s
✔ web Pulled 162.6s
✔ api Pulled 177.6s
✔ redis Pulled 104.1s
✔ sandbox Pulled 100.9s
✔ nginx Pulled 124.1s
✔ ssrf_proxy Pulled 145.4s
5. ### 修改环境变量
在docker目录下存放着一个.env.example文件,其中包含了若干 Dify 运行所需的环境变量,我们需要把其中几个重要的配置项填写上
我们在 docker/scripts 目录下提供了一个脚本 setup-env.sh 用来交互式地获取数据库连接信息,填入 .env 文件中并且完成数据库连接校验。你只需要执行:
bash ./scripts/setup-env.sh
[root@postgres docker]# bash ./scripts/setup-env.sh
已从 .env.example 创建新的 .env 文件
请输入数据库配置信息:
数据库主机地址 [当前值: db]: 这里填写OBCloud的连接地址即可
数据库端口 [当前值: 5432]: 3306
数据库用户名 [当前值: postgres]: zhz
数据库密码 [当前值: difyai123456]: 密码
数据库名称 [当前值: dify]: zhz1
向量数据库名称 [当前值: test]: zhz2
数据库配置已更新到 .env 文件中
检测数据库连接:
mysql: [Warning] Using a password on the command line interface can be insecure.
zhz1 数据库连接成功~
mysql: [Warning] Using a password on the command line interface can be insecure.
zhz2 数据库连接成功~
6. 启动 Dify 容器组
启动之前先看看第 2 步开始拉取的镜像是否就绪,如果已经完成,可以使用下列命令启动 Dify 的容器组
/usr/local/bin/docker-compose --profile workshop up -d
[+] Running 4/6
[+] Running 6/7cker-ssrf_proxy-1 Running 0.0s
[+] Running 7/7cker-ssrf_proxy-1 Running 0.0s
✔ Container docker-ssrf_proxy-1 Running 0.0s
✔ Container docker-redis-1 Running 0.0s
✔ Container docker-worker-1 Started 19.2s
✔ Container docker-web-1 Running 0.0s
✔ Container docker-sandbox-1 Running 0.0s
✔ Container docker-nginx-1 Started 19.7s
✔ Container docker-api-1 Started 9.0s
7. 查看 Dify 后端服务日志
docker logs -f docker-api-1
docker logs -f docker-worker-1
7.1日志信息报错情况
docker logs -f docker-api-1
Running migrations
None of PyTorch, TensorFlow >= 2.0, or Flax have been found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.
/app/api/.venv/lib/python3.12/site-packages/tencentcloud/hunyuan/v20230901/models.py:5585: SyntaxWarning: invalid escape sequence '\_'
"""function名称,只能包含a-z,A-Z,0-9,\_或-
/app/api/.venv/lib/python3.12/site-packages/jieba/__init__.py:44: SyntaxWarning: invalid escape sequence '\.'
re_han_default = re.compile("([\u4E00-\u9FD5a-zA-Z0-9+#&\._%\-]+)", re.U)
/app/api/.venv/lib/python3.12/site-packages/jieba/__init__.py:46: SyntaxWarning: invalid escape sequence '\s'
re_skip_default = re.compile("(\r\n|\s)", re.U)
/app/api/.venv/lib/python3.12/site-packages/jieba/finalseg/__init__.py:78: SyntaxWarning: invalid escape sequence '\.'
re_skip = re.compile("([a-zA-Z0-9]+(?:\.\d+)?%?)")
/app/api/.venv/lib/python3.12/site-packages/jieba/posseg/__init__.py:16: SyntaxWarning: invalid escape sequence '\.'
re_skip_detail = re.compile("([\.0-9]+|[a-zA-Z0-9]+)")
/app/api/.venv/lib/python3.12/site-packages/jieba/posseg/__init__.py:17: SyntaxWarning: invalid escape sequence '\.'
re_han_internal = re.compile("([\u4E00-\u9FD5a-zA-Z0-9+#&\._]+)")
/app/api/.venv/lib/python3.12/site-packages/jieba/posseg/__init__.py:18: SyntaxWarning: invalid escape sequence '\s'
re_skip_internal = re.compile("(\r\n|\s)")
/app/api/.venv/lib/python3.12/site-packages/jieba/posseg/__init__.py:21: SyntaxWarning: invalid escape sequence '\.'
re_num = re.compile("[\.0-9]+")
2024-12-11 02:34:40.394 INFO [pre_load_builtin_providers_cache] [font_manager.py:1578] - generated new fontManager
Preparing database migration...
Starting database migration.
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
ERROR [root] Failed to execute database migration
这里报错ERROR [root] Failed to execute database migration,我这里采取的方法是切换非root用户,重新执行第5步,修改环境变量后启动就正常了。
7.2日志信息
执行成功后,显示Database migration successful! 即可完成操作
docker logs -f docker-api-1
docker logs -f docker-api-1
Running migrations
.................................................................
fontManager
Preparing database migration...
Starting database migration.
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 01d6889832f7, snapshot
Database migration successful!
Traceback (most recent call last):
File "/app/api/.venv/bin/flask", line 8, in <module>
sys.exit(main())
File "/app/api/commands.py", line 595, in upgrade_db
lock.release()
File "/app/api/.venv/lib/python3.12/site-packages/redis/lock.py", line 256, in release
self.do_release(expected_token)
File "/app/api/.venv/lib/python3.12/site-packages/redis/lock.py", line 262, in do_release
raise LockNotOwnedError(
redis.exceptions.LockNotOwnedError: Cannot release a lock that's no longer owned
如果在其中任意一个容器的日志中看到了Database migration successful!
这一条信息,则说明数据库结构升级完成(另一个容器中可能会有Database migration skipped
表示在该容器中跳过了数据库结构迁移),如果没有其他ERROR
信息,则说明可以正常打开 Dify 界面了。
7.3OBCloud上mysql中有 72 张表
mysql> show tables;
+-----------------------------------+
| Tables_in_test1 |
+-----------------------------------+
| account_integrates |
| accounts |
| alembic_version |
| api_based_extensions |
| api_requests |
| api_tokens |
| app_annotation_hit_histories |
| app_annotation_settings |
| app_dataset_joins |
| app_model_configs |
| apps |
| celery_taskmeta |
| celery_tasksetmeta |
| conversations |
| data_source_api_key_auth_bindings |
| data_source_oauth_bindings |
| dataset_collection_bindings |
| dataset_keyword_tables |
| dataset_permissions |
| dataset_process_rules |
| dataset_queries |
| dataset_retriever_resources |
| datasets |
| dify_setups |
| document_segments |
| documents |
| embeddings |
| end_users |
| external_knowledge_apis |
| external_knowledge_bindings |
| installed_apps |
| invitation_codes |
| load_balancing_model_configs |
| message_agent_thoughts |
| message_annotations |
| message_chains |
| message_feedbacks |
| message_files |
| messages |
| operation_logs |
| pinned_conversations |
| provider_model_settings |
| provider_models |
| provider_orders |
| providers |
| recommended_apps |
| saved_messages |
| sites |
| tag_bindings |
| tags |
| tenant_account_joins |
| tenant_default_models |
| tenant_preferred_model_providers |
| tenants |
| tidb_auth_bindings |
| tool_api_providers |
| tool_builtin_providers |
| tool_conversation_variables |
| tool_files |
| tool_label_bindings |
| tool_model_invokes |
| tool_providers |
| tool_published_apps |
| tool_workflow_providers |
| trace_app_config |
| upload_files |
| whitelists |
| workflow_app_logs |
| workflow_conversation_variables |
| workflow_node_executions |
| workflow_runs |
| workflows |
+-----------------------------------+
72 rows in set (0.03 sec)
8. 访问 Dify 应用
默认情况下,Dify 的前端页面会启动在本机的80
端口上,也就是说可以通过访问当前机器的 IP 来访问 Dify 的界面。也就是说如果我在笔记本上运行的话,我在浏览器上访问localhost
即可(或者是内网 IP);如果在服务器上部署 Dify,则需要访问服务器的公网 IP。初次访问 Dify 应用会进入“设置管理员账户”的页面,设置完成后即可使用该账号登录。
三、应用开发步骤
在该步骤当中我们将使用阿里云百炼的模型服务,通过 Dify 搭建一个文档 RAG 问答助手。
1. 开通阿里云百炼模型调用服务并获取 API KEY
首先,我们需要注册阿里云百炼账号,开通模型调用服务并获取 API Key
参考文档:docs/dify@oceanbase-workshop.md · oceanbase-devhub/dify - Gitee.com
2. 设置模型供应商和系统模型
设置完成模型供应商之后刷新网页,再向通义千问模型供应商中添加模型qwen-turbo-2024-11-01
完成系统模型设置,将系统推理模型设置为qwen-turbo-2024-11-01
,Embedding 模型设置为text-embedding-v3
3. 创建知识库并上传文档
3.1 克隆文档仓库
我们将 OceanBase 数据库的开源文档仓库克隆下来,作为数据来源。
git clone --single-branch --branch V4.3.4 https://github.com/oceanbase/oceanbase-doc.git ~/oceanbase-doc
# 如果您访问 Github 仓库速度较慢,可以使用以下命令克隆 Gitee 的镜像版本
git clone --single-branch --branch V4.3.4 https://gitee.com/oceanbase-devhub/oceanbase-doc.git ~/oceanbase-doc
Cloning into '/root/oceanbase-doc'...
remote: Enumerating objects: 126520, done.
remote: Counting objects: 100% (126520/126520), done.
remote: Compressing objects: 100% (70851/70851), done.
remote: Total 126520 (delta 55338), reused 126520 (delta 55338), pack-reused 0
Receiving objects: 100% (126520/126520), 69.60 MiB | 6.89 MiB/s, done.
Resolving deltas: 100% (55338/55338), done.
3.2 将指定文档上传到知识库中
回到首页,顶端中部的“知识库”标签页,进入知识库管理界面,点击创建知识库。
![[Pasted image 20241211120126.png]]
为了节省时间和模型服务调用量,我们仅处理 OceanBase 向量检索有关的几篇文档,这些文档相对于oceanbase-doc
目录的相对路径是zh-CN/640.ob-vector-search
,我们需要将这个目录下面所有的文档都上传。
索引方式选择“高质量”,点击“保存并处理”。
Dify 会提示知识库“已创建”,后续可能会看到某些文档已经在此处理完成。点击“前往文档”。
点击“前往文档”后会看到该知识库中的文档列表。
4. 创建对话应用并选中知识库
点击“工作室”标签页,进入应用管理界面,点击“创建空白应用”。
保持默认选项“聊天助手”和“基础编排”即可,应用名称可以自行填写,例如 “OB 向量文档助手”。输入完成后点击“创建”按钮。
创建完成后会进入应用编排界面,首先关注右上角的默认模型设置,如果不是qwen-turbo-2024-11-01
则修改为qwen-turbo-2024-11-01
点击“上下文”卡片中的“添加”按钮,选中刚才我们创建的知识库并点击“添加”按钮。
随后,在提示词的输入框中填写如下的提示词:
你是一个专注于回答 OceanBase 社区版问题的机器人。
你的目标是利用可能存在的历史对话和检索到的文档片段,回答用户的问题。
任务描述:根据可能存在的历史对话、用户问题和检索到的文档片段,尝试回答用户问题。如果用户的问题与 OceanBase 无关,则抱歉说明无法回答。如果所有文档都无法解决用户问题,首先考虑用户问题的合理性。如果用户问题不合理,需要进行纠正。如果用户问题合理但找不到相关信息,则表示抱歉并给出基于内在知识的可能解答。如果文档中的信息可以解答用户问题,则根据文档信息严格回答问题。
回答要求:
- 如果所有文档都无法解决用户问题,首先考虑用户问题的合理性。如果用户问题不合理,请回答:“您的问题可能存在误解,实际上据我所知……(提供正确的信息)”。如果用户问题合理但找不到相关信息,请回答:“抱歉,无法从检索到的文档中找到解决此问题的信息。请联系OceanBase的人工答疑以获取更多帮助。基于我的内在知识,可能的解答是……(根据内在知识给出可能解答)”。
- 如果文档中的信息可以解答用户问题,请回答:“根据文档库中的信息,……(严格依据文档信息回答用户问题)”。如果答案可以在某一篇文档中找到,请在回答时直接指出依据的文档名称及段落的标题(不要指出片段标号)。
- 如果某个文档片段中包含代码,请务必引起重视,给用户的回答中尽可能包含代码。请完全参考文档信息回答用户问题,不要编造事实,尤其是数据表名、SQL 语句等关键信息。
- 如果需要综合多个文档中的片段信息,请全面地总结理解后尝试给出全面专业的回答。
- 尽可能分点并且详细地解答用户的问题,回答不宜过短。
可以在右侧聊天框里进行应用调试,例如询问“请介绍一下 OceanBase 的向量功能”
5. 发布应用,开始对话!
点击应用详情右上角的“发布”下面的“运行”按钮,会打开该应用的专属页面。
点击 Start Chat 按钮即可开始聊天。
如果你是在服务器上部署的 Dify,也可以将该应用的链接分享给身边的朋友,让他们也一起来试用一下吧!
自此,你已经通过 Dify + OceanBase 搭建了你自己的 LLM 应用平台和智能体应用,恭喜你!
四、参考文档:
docs/dify@oceanbase-workshop.md · oceanbase-devhub/dify - Gitee.com
#AI 实战营–上海站# 基于 OBCloud 构建 Dify x OceanBase 构建 “应用级” 的 AI 助手 - 社区问答- OceanBase社区-分布式数据库