使用 Dify 和 Moonshot API 构建你的 AI 工作流(一):让不 AI 的应用 AI 化
AI魔法学院
2024-10-17
分享海报

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](增加一堆新功能,比较有意思的是 PgVectorWorkflow 支持快捷键操作,Prompt 中引用的变量可以自动改名字,修复了 14 项内容)v0.6.4[9](支持了代码解释器、月之暗面模型的 FuncCallSD3,修复了 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 告别 MySQLDocker 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--requirepassdifyai123456
    
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 中。

1720435615998

设置默认的系统推理模型

配置完毕之后,在这个弹出窗口的顶端选择系统模型设置,将系统推理模型设置为反应最快、成本最低的 8K 模型。

Dify 中配置好的模型

当两个配置都设置完毕后,这个弹出窗中展示的模型在 Dify 中就完全可用啦。

创建一个“AI 文本生成应用

创建一个文本生成应用

关闭上面的弹出窗口,我们创建一个新的文本生成应用,你可以根据你的喜好来填写应用的标题和描述。

编写我们的提示词内容

根据我们的设想,我们的模型应用应该能够根据我们提供的内容,来自动生成一个合适的标题,为了让模型干活符合预期,我们可以在 Dify IDE 中完成 Prompt 的调试和编写工作。

这里建议使用相对有层次的 Markdown 语法来给模型立一些规矩,效果会相对好一些,这里假设模型是机器之心的记者,擅长挖掘内容和编写标题:

你是人工智能领域专业平台媒体机器之心的首席记者,擅长根据用户提供的内容,提炼合适的标题。

## 生成要求

-标题尽量和AI相关
-标题结果不超过20
-仅生成一条标题
-只输出标题内容

## 用户提供的内容

{{
content}}

## 输出标题结果

在上面的提示词中,我们设置了一个名为content的变量,在随后的真实模型调用中,我们可以在 API 的请求参数中动态的调整这个数据内容,来让它解决不同的文章的标题生成任务。

1720435647846设置模型的具体参数

因为我们希望标题生成的相对合理,和内容比较有相关性,并且标题字数比较少,所以我们可以参考上面的方式来进行模型调用参数设置,来让模型的调用时间更短一些。

选择一篇测试内容

既然我们的 Prompt 提示词都选择的人设都用了机器之心,那么验证测试的文章也选择机器之心的报道好啦,比如,这里我选择的是Linus AI 炒作的一篇报道[20]

调试模型输出结果

将测试内容粘贴到调试对话框中,点击运行,我们就能够验证模型在这个 Prompt 和调用参数下的表现了,你乐意的话,可以打开好几家不同的模型进行调试比较。

这里可以看到,在之前的 Prompt 要求下,虽然没有生成出机器之心感觉的标题(模型生成的标题相比有些无聊),但是确实按照要求生成了一条符合字面要求的标题,满足继续往下折腾的要求。如果你有更高的要求,可以耐心调整上面的 Prompt 提示词。

那么,我们开始在 WordPress 中的折腾。

制作 WordPress 标题生成插件

1720435665591访问 Dify 应用 API

Dify 配置的 AI 应用页面中,我们点击发布按钮,在下拉菜单中选择访问 API”,我们就能得到如何通过 API 访问配置好的 AI 应用的文档说明了。

向模型应用发送请求

我们只需要调用/completion-messages接口,将刚刚 Prompt 中设置的content传入接口即可。

获取当前应用的 API Key

在调用 Dify API 的时候,我们需要进行身份验证,在这个页面的右上角,点击“API 密钥按钮,创建一个 API 密钥即可。

// 调用 dify 服务来生成标题
functiongenerate_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");
functionupdate_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]来作为标题生成素材

1720435686852找另外一篇机器之心的文章做素材

打开机器之心的文章,复制一部分用于标题生成的文本内容。当然,你也可以自己写一些内容,替换我们直接从网上找的测试验证内容。

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.envconfig/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
告别 MySQLDocker 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

© THE END

转载请联系本网站获得授权

投稿或版权问题请加微信:skillupvip