使用 Dify 和 Moonshot API 构建你的 AI 工作流(一):让不 AI 的应用 AI 化
Dify 团队推出了包含 AI Workflow 新功能的 v0.6.0[2],这个功能虽然从去年年底就在做了,但是因为功能复杂,代码变更量巨大,直至今天 v0.6.4[3] 正式发布,才算进入一个相对稳定期,所以适合写一篇新的文章来聊聊啦。
感受下 Dify 团队在过去两周的发布动作:
•v0.6.0[4] (带有 AI Workflow 功能和一大堆更新,1241 个文件变动)•v0.6.0-fix[5] (紧急修正:Agent
应用的添加功能)•v0.6.1[6] (修复了 23 项内容)•v0.6.2[7](支持了新的模型、新的图片 Agent,并且修复了 17 项内容)•v0.6.3[8](增加一堆新功能,比较有意思的是 PgVector,Workflow 支持快捷键操作,Prompt 中引用的变量可以自动改名字,修复了 14 项内容)•v0.6.4[9](支持了代码解释器、月之暗面模型的 FuncCall、SD3,修复了 20
项内容)
小步快跑,干的漂亮。
而获取 API Key 难度很低的 MoonShot,则在最近悄悄上线了 “Tool Use[10]” 功能。在开源社区里,我们一般称这个功能为 Function Call,借助特殊构造的请求结构和提示词,来让模型自动的调用用户预定义的远程函数,实现智能的 RPA 调用。
能够调用“外部工具”的模型功能
我计划将工作流相关的事情拆分为两篇来聊,过程中不太想切换模型,所以就选择了支持 “Function Call” 的它。下篇相关的文章,我们展开聊聊,如何利用这个功能,结合“Workflow”来做一些有趣的自动化。本篇文章是基础篇,我们就先用它的基础功能就好,难度大概是 “有手就行”。
准备工作
我将本文用到的 Dify 和 WordPress 的
Docker “一键启动”配置相关文件开源在了 soulteary/dify-with-wordpress[11],如果你感兴趣一些使用和配置上不同于官方的小的优化、维护技巧,可以翻阅下文的“维护优化”小节。
我们先从基础实战开始。
Docker 运行环境
想顺滑的完成实践,我推荐你安装 Docker,不论你的设备是否有显卡,都可以根据自己的操作系统喜好,参考这两篇来完成基础环境的配置《基于 Docker 的深度学习环境:Windows 篇[12]》、《基于 Docker 的深度学习环境:入门篇[13]》。当然,使用 Docker 之后,你还可以做很多事情,比如:之前几十篇有关 Docker 的实践[14],在此就不赘述啦。
快速初始化 WordPress
在之前的两三篇文章《把 WordPress 变成 BaaS 服务:API 调用指南[15]》、《WordPress 告别 MySQL:Docker
SQLite WordPress[16]》、《WordPress
SQLite Docker 镜像封装细节[17]》中,我介绍过轻量化的、能够在本地快速运行的 WordPress,以及能够提供 API 交互的 WordPress 方案。感兴趣可以自行翻阅,这里就不展开具体细节啦。
为了能够更简单的折腾本文的内容,我封装了一个开箱即用的、轻量化的、能够提供 API 交互的 WordPress Docker 镜像,项目开源在了 soulteary/docker-wp-api[18],使用方法非常简单:
docker pull soulteary/wp-api:6.5.2-sqlite
使用上面的命令完成 Docker 镜像的下载,然后使用类似下面的配置,可以快速启动这个镜像中的
WordPress:
version:
'3'
services:
wordpress:
image:
soulteary/wp-api:6.5.2-sqlite
restart:
always
ports:
-
8080
:80
volumes:
-
./wordpress:/var/www/html
完整的验证环境
当然,为了更简单一些,我将文章相关的代码和配置都开源到了 soulteary/dify-with-wordpress[19],你可以在项目中获取所有的代码。项目中的配置将 Dify 和它相关的依赖、WordPress 都打包到了一起:
version:
'3'
services:
# API service
api:
image:
langgenius/dify-api:0.6.4
restart:
always
env_file:
-
./config/api.env
-
./config/middleware.env
depends_on:
-
db
-
redis
volumes:
-
./volumes/app/storage:/app/api/storage
# worker service
worker:
image:
langgenius/dify-api:0.6.4
restart:
always
env_file:
-
./config/worker.env
-
./config/middleware.env
depends_on:
-
db
-
redis
volumes:
-
./volumes/app/storage:/app/api/storage
# Frontend web application.
web:
image:
langgenius/dify-web:0.6.4
restart:
always
environment:
EDITION:
SELF_HOSTED
CONSOLE_API_URL:
''
APP_API_URL:
''
SENTRY_DSN:
''
# The postgres database.
db:
image:
postgres:15-alpine
restart:
always
environment:
PGUSER:
postgres
POSTGRES_PASSWORD:
difyai123456
POSTGRES_DB:
dify
PGDATA:
/var/lib/postgresql/data/pgdata
volumes:
-
./volumes/db/data:/var/lib/postgresql/data
healthcheck:
test:
[
"CMD"
,
"pg_isready"
]
interval:
1s
timeout:
3s
retries:
30
# The redis cache.
redis:
image:
redis:6-alpine
restart:
always
volumes:
-
./volumes/redis/data:/data
command:
redis-server
--requirepass
difyai123456
healthcheck:
test:
[
"CMD"
,
"redis-cli"
,
"ping"
]
# The Weaviate vector store.
weaviate:
image:
semitechnologies/weaviate:1.19.0
restart:
always
volumes:
-
./volumes/weaviate:/var/lib/weaviate
environment:
QUERY_DEFAULTS_LIMIT:
25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED:
'false'
PERSISTENCE_DATA_PATH:
'/var/lib/weaviate'
DEFAULT_VECTORIZER_MODULE:
'none'
CLUSTER_HOSTNAME:
'node1'
AUTHENTICATION_APIKEY_ENABLED:
'true'
AUTHENTICATION_APIKEY_ALLOWED_KEYS:
'WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih'
AUTHENTICATION_APIKEY_USERS:
'hello@dify.ai'
AUTHORIZATION_ADMINLIST_ENABLED:
'true'
AUTHORIZATION_ADMINLIST_USERS:
'hello@dify.ai'
# The DifySandbox
sandbox:
image:
langgenius/dify-sandbox:latest
restart:
always
cap_add:
-
SYS_ADMIN
environment:
API_KEY:
dify-sandbox
GIN_MODE:
release
WORKER_TIMEOUT:
15
nginx:
image:
nginx:latest
restart:
always
volumes:
-
./nginx.conf:/etc/nginx/nginx.conf
depends_on:
-
api
-
web
ports:
-
"8082:80"
wordpress:
image:
soulteary/wp-api:6.5.2-sqlite
restart:
always
ports:
-
8083
:80
volumes:
-
./wordpress:/var/www/html
当我们获取项目代码后,执行 docker-compose up
-d
之后,稍等片刻,我们就可以在浏览器中分别访问:
•http://localhost:8082
来初始化和访问 Dify•http://localhost:8083
来初始化和访问 WordPress
一路 “Next” 快速初始化 Dify一路 “Next” 快速初始化 WordPress
当两个应用都初始化完毕后,我们就完成了所有的准备工作。
让
WordPress 拥有一个 AI 功能
如果你也经常写文章或者文字材料,那么我相信你或许和我一样,在给写好的内容起合适标题的时候,可能会发愁、挠头。
那么,我们就先来实现一个简单的功能,让 WordPress 能够在我们写好内容的时候,根据内容自动生成一个合适的标题。你可以举一反三,来让其他的“内容生成、优化”也都 AI 化。
初始化 Dify 中的模型配置
点击界面右上角的用户头像,在下拉菜单中点击“设置”,在弹出窗口中选择左侧的“模型供应商”菜单,能够看到 Dify 支持配置使用的所有模型类型。
在 Dify 模型配置中设置模型的 API Token
在列表中往下拉,找到“月之暗面”,然后把我们的模型 API
Token 配置到 Dify 中。
设置默认的系统推理模型
配置完毕之后,在这个弹出窗口的顶端选择“系统模型设置”,将“系统推理模型”设置为反应最快、成本最低的 8K 模型。
在 Dify 中配置好的模型
当两个配置都设置完毕后,这个弹出窗中展示的模型在 Dify 中就完全可用啦。
创建一个“AI 文本生成”应用
创建一个文本生成应用
关闭上面的弹出窗口,我们创建一个新的文本生成应用,你可以根据你的喜好来填写应用的标题和描述。
编写我们的提示词内容
根据我们的设想,我们的模型应用应该能够根据我们提供的内容,来自动生成一个合适的标题,为了让模型干活符合预期,我们可以在 Dify 的 IDE 中完成
Prompt 的调试和编写工作。
这里建议使用相对有层次的 Markdown 语法来给模型“立一些规矩”,效果会相对好一些,这里假设模型是“机器之心”的记者,擅长挖掘内容和编写标题:
你是人工智能领域专业平台媒体机器之心的首席记者,擅长根据用户提供的内容,提炼合适的标题。
##
生成要求
-
标题尽量和
AI
相关
-
标题结果不超过
20
字
-
仅生成一条标题
-
只输出标题内容
##
用户提供的内容
{{
content
}}
##
输出标题结果
在上面的提示词中,我们设置了一个名为 “content
” 的变量,在随后的真实模型调用中,我们可以在 API 的请求参数中动态的调整这个数据内容,来让它解决不同的文章的标题生成任务。
设置模型的具体参数
因为我们希望标题生成的相对合理,和内容比较有相关性,并且标题字数比较少,所以我们可以参考上面的方式来进行模型调用参数设置,来让模型的调用时间更短一些。
选择一篇测试内容
既然我们的 Prompt 提示词都选择的人设都用了“机器之心”,那么验证测试的文章也选择机器之心的报道好啦,比如,这里我选择的是 “Linus
喷 AI 炒作的一篇报道[20]”。
调试模型输出结果
将测试内容粘贴到调试对话框中,点击“运行”,我们就能够验证模型在这个
Prompt 和调用参数下的表现了,你乐意的话,可以打开好几家不同的模型进行调试比较。
这里可以看到,在之前的 Prompt 要求下,虽然没有生成出“机器之心”感觉的标题(模型生成的标题相比有些无聊),但是确实按照要求生成了一条符合字面要求的标题,满足继续往下折腾的要求。如果你有更高的要求,可以耐心调整上面的 Prompt 提示词。
那么,我们开始在 WordPress 中的折腾。
制作 WordPress 标题生成插件
访问 Dify 应用 API
在
Dify 配置的 AI 应用页面中,我们点击“发布”按钮,在下拉菜单中选择“访问 API”,我们就能得到如何通过 API 访问配置好的 AI 应用的文档说明了。
向模型应用发送请求
我们只需要调用 /completion-messages
接口,将刚刚 Prompt 中设置的 content
传入接口即可。
获取当前应用的 API Key
在调用 Dify API 的时候,我们需要进行身份验证,在这个页面的右上角,点击“API 密钥” 按钮,创建一个 API 密钥即可。
//
调用 dify 服务来生成标题
function
generate_title_by_content
(
$content
)
{
$ch
=
curl_init
();
curl_setopt
(
$ch
,
CURLOPT_URL,
"http://10.11.12.90:8082/v1/completion-messages"
);
curl_setopt
(
$ch
, CURLOPT_RETURNTRANSFER,
true
);
curl_setopt
(
$ch
, CURLOPT_CUSTOMREQUEST,
"POST"
);
curl_setopt
(
$ch
, CURLOPT_HTTPHEADER, [
"Authorization: Bearer app-YChjQYVOeEgiMR6tsmrXfVZM"
,
"Content-Type: application/json"
,
]);
$payload
= [
"inputs"
=> [
"content"
=>
$content
,
],
"response_mode"
=>
"blocking"
,
"user"
=>
"soulteary"
,
];
curl_setopt
(
$ch
,
CURLOPT_POSTFIELDS,
json_encode
(
$payload
, JSON_UNESCAPED_UNICODE)
);
$response
=
curl_exec
(
$ch
);
curl_close
(
$ch
);
$data
=
json_decode
(
$response
,
true
);
if
(
empty
(
$data
[
"answer"
])) {
return
"AI
生成标题失败"
;
}
$title
=
$data
[
"answer"
];
$title
=
str_replace
(
'"'
,
""
,
$title
);
return
$title
;
}
创建好 Dify AI 应用的 API Key 后,我们可以将上面文档中的调用写成一个简单的 PHP 模型调用函数。这个函数接收一个参数(文章内容),并将文章内容传入 Dify 的调用结构体中,当 Dify 调用 Moonshot 模型后,我们解析调用结果,取出返回内容中的 answer
字段,就得到了模型生成的标题内容。
而让
WordPress 能够在我们的文章有内容,没有标题的时候,调用上面的函数,就更简单了(借助
WordPress 定制能力中的 hooks/the_post[21]):
//
当文章发布或更新时,如果标题为空,自动生成一个标题
add_action
(
"the_post"
,
"update_post_title"
);
function
update_post_title
(
$post
)
{
//
当标题存在,就不再生成
if
(!
empty
(
$post
->post_title)) {
return
;
}
//
生成标题
$post_title
=
generate_title_by_content
(
$post
->post_content);
//
更新数据库中标题
wp_update_post
([
"ID"
=>
$post
->ID,
"post_title"
=>
$post_title
]);
//
更新当前文章对象
$post
->post_title =
$post_title
;
}
完整的插件程序实现,可以在 soulteary/dify-with-wordpress/title-generate.php[22] 找到,你可以将这个文件放置到你启动 WordPress 程序目录的 wordpress/wp-content/plugins/title-generate.php
位置,然后在你的 WordPress 后台的插件管理中启用这个插件。(注意替换 http://10.11.12.90:8082/v1/completion-messages
为你真正部署 Dify 的 IP 地址或域名)
启用 WordPress AI 插件
体验插件能力
这里,我们还是偷懒,暂且用机器之心的一篇文章[23]来作为“标题生成素材”。
找另外一篇机器之心的文章做素材
打开机器之心的文章,复制一部分用于标题生成的文本内容。当然,你也可以自己写一些内容,替换我们直接从网上找的测试验证内容。
在 WordPress 中创建新的内容
接着,打开 WordPress 后台,创建一篇新文章,然后在内容中输入一些内容,我这里偷懒,选择了粘贴刚刚找到的机器之心的文章内容。既然要 AI 来生成标题,标题区域我们留空就好。
点击发布,AI 将迅速的生成标题
当我们点击“发布”按钮后,WordPress 会调用上文中我们配置好的 Dify AI 应用,将我们的文章内容发送给 Dify,构建出一个新的(完整的)提示词,然后向 Moonshot 的模型进行请求,并将模型生成结果填充到标题区域。
当然,因为我们上文中的模型参数设置的相对合理,这个时间应该在 1 秒到 2 秒之间。
因为我们的 Prompt 提示词和模型调用参数是维护在 Dify 中的,所以我们如果想完善模型的生成规则、风格、生成数量,我们只需要更新上文中 Dify IDE 中的 Prompt 提示词内容,而不需要修改程序,这是不是非常方便呢?
优化
Dify 项目配置
Dify 项目的默认配置目前有比较大的优化空间,可以让配置更简单、更易于长期使用的维护管理。
优化 Docker 配置文件
官方很贴心的在项目中提供了一键启动的配置文件[24],不过如果你认真浏览,你会发现官方尽可能给出了丰富的选项。
•你能够调整前端服务、后端 API 服务、实际干活的 Worker、代码执行沙盒环境的一些配置。后端服务和实际干活的 Worker 还是一个镜像,省却了一些下载的功夫。•你能够设置或替换 Postgres 数据库、Redis 缓存、Weaviate(默认使用)和 Qdrant (支持全文索引)向量数据库,甚至还有网关程序 Nginx 的细节。
但是,Dify 相关服务的配置目前其实稍显复杂,API 和 Worker 虽然是同一份镜像,但是在不同的工作模式下,他们的配置是有一些不同的。
所以,我们可以通过 Compose File 的 env file[25] 功能,来对官方的配置文件进行抽象和整理,让骨干配置文件更清晰和简洁,比如我们可以将原本 230 多行的配置简化为下面更简洁漂亮的格式:
version:
'3'
services:
# API service
api:
image:
langgenius/dify-api:0.6.4
restart:
always
env_file:
-
./config/api.env
-
./config/middleware.env
depends_on:
-
db
-
redis
volumes:
-
./volumes/app/storage:/app/api/storage
# Worker service
worker:
image:
langgenius/dify-api:0.6.4
restart:
always
env_file:
-
./config/worker.env
-
./config/middleware.env
depends_on:
-
db
-
redis
volumes:
-
./volumes/app/storage:/app/api/storage
根据服务需要的环境变量,我们分别将两个服务需要的环境变量(配置)保存在 config/api.env
和 config/worker.env
两个文件中,而两个服务共享的数据库相关配置,我们可以保存在 config/middleware.env
中,做到“共享环境配置”,改一处文件两处服务都受益。
优化 Nginx 配置文件
官方的 Nginx 配置文件应该借鉴了 Nginx Docker 容器中被模块化处理过的配置示例,相关的配置文件一共使用了三个,使用了传统的嵌套配置,并且包含了冗余的反向代理配置,尽管已经努力的抽象了一个名为 proxy.conf
的配置。
我们可以用一个简洁的表达方式来完成相同的诉求,甚至让配置变的更加适合 “扩容”,抽象需要分发流量的 “前端” 和 “后端”,在必要的时候,只需要扩展 “服务数量即可”:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main
'$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'
;
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
client_max_body_size 15M;
server {
listen 80;
server_name _;
proxy_set_header Host
$host
;
proxy_set_header X-Forwarded-For
$proxy_add_x_forwarded_for
;
proxy_set_header X-Forwarded-Proto
$scheme
;
proxy_http_version 1.1;
proxy_set_header Connection
""
;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
location @backend {
proxy_pass http://api:5001;
}
location @frontend {
proxy_pass http://web:3000;
}
location /console/api {
try_files
$uri
$uri
/ @backend;
}
location /api {
try_files
$uri
$uri
/ @backend;
}
location /v1 {
try_files
$uri
$uri
/ @backend;
}
location /files {
try_files
$uri
$uri
/ @backend;
}
location / {
try_files
$uri
$uri
/ @frontend;
}
}
}
关于其他的配置优化、应用配置细节,我们在后面的文章中陆续展开,这里就先聊到这里。
最后
好啦,这篇文章就先聊到这里,后面的文章里,我们继续聊聊如何构建 “AI 工作流”,让你的不 AI 的应用,能够 AI 化。
关于本文中埋的一些未展开的伏笔,其实有很多有趣的玩法,比如可以将 WordPress 变成一个低成本的、简单的 RAG 知识库、带有版本管理的 CMS、搭配模型使用的带版本管理的图床。
我们下篇文章再见。
--EOF
引用链接
[1]
使用 Dify 和 AWS Bedrock 玩转 Anthropic Claude 3: https://soulteary.com/2024/03/18/play-with-anthropopic-claude-3-using-dify-and-aws-bedrock.html
[2]
v0.6.0: https://github.com/langgenius/dify/releases/tag/0.6.0
[3]
v0.6.4: https://github.com/langgenius/dify/releases/tag/0.6.4
[4]
v0.6.0: https://github.com/langgenius/dify/compare/0.5.11-fix1...0.6.0
[5]
v0.6.0-fix: https://github.com/langgenius/dify/compare/0.6.0...0.6.0-fix1
[6]
v0.6.1: https://github.com/langgenius/dify/compare/0.6.0-fix1...0.6.1
[7]
v0.6.2: https://github.com/langgenius/dify/compare/0.6.1...0.6.2
[8]
v0.6.3: https://github.com/langgenius/dify/compare/0.6.2...0.6.3
[9]
v0.6.4: https://github.com/langgenius/dify/compare/0.6.3...0.6.4
[10]
Tool
Use: https://platform.moonshot.cn/docs/api-reference#tool-use
[11]
soulteary/dify-with-wordpress: https://github.com/soulteary/dify-with-wordpress
[12]
基于 Docker 的深度学习环境:Windows 篇: https://soulteary.com/2023/07/29/docker-based-deep-learning-environment-under-windows.html
[13]
基于 Docker 的深度学习环境:入门篇: https://soulteary.com/2023/03/22/docker-based-deep-learning-environment-getting-started.html
[14]
几十篇有关 Docker 的实践: https://soulteary.com/tags/docker.html
[15]
把 WordPress 变成 BaaS 服务:API 调用指南: https://soulteary.com/2024/04/22/turn-wordpress-into-a-baas-service-a-guide-to-wp-api-calls.html
[16]
WordPress
告别 MySQL:Docker SQLite
WordPress: https://soulteary.com/2024/04/17/say-goodbye-to-mysql-docker-sqlite-wordpress.html
[17]
WordPress
SQLite Docker 镜像封装细节: https://soulteary.com/2024/04/21/wordpress-sqlite-docker-image-packaging-details.html
[18]
soulteary/docker-wp-api: https://github.com/soulteary/docker-wp-api
[19]
soulteary/dify-with-wordpress: https://github.com/soulteary/dify-with-wordpress
[20]
Linus 喷 AI 炒作的一篇报道: https://www.jiqizhixin.com/articles/2024-04-22-11
[21]
hooks/the_post: https://developer.wordpress.org/reference/hooks/the_post/
[22]
soulteary/dify-with-wordpress/title-generate.php: https://github.com/soulteary/dify-with-wordpress/blob/main/title-generate.php
[23]
一篇文章: https://www.jiqizhixin.com/articles/2024-04-23-6
[24]
配置文件: https://github.com/soulteary/dify-with-wordpress/commit/63cccd75d7f698ab4aae587ca3e231da231b2e58
[25]
env
file: https://docs.docker.com/compose/compose-file/05-services/#env_file
出自:https://mp.weixin.qq.com/s/9Bmg-VFI94dB2o4tGw9msQ