从0到1:如何在dify中自定义工具并进行使用
前言
本章介绍的是如何在Dify中使用自定义工具,前提是已经在本地部署了Dify,还没有部署的可以参考上篇博客。其实Dify提供的服务已经很全面了,大多数情况下是不需要自定义工具的,但是难免有少数已有工具不能满足要求的时候,这里结合自己的实际例子记录一下。
本文以获取给定url对应的网页源码为例(但其实这个功能Dify已经提供了,这里着重讲一下搭建过程)
打开Dify的自定义工具页面发现有两个必填项:名称和Schema。名称随便填,重点就是这个Schema该如何写
1、本地搭建http服务
1.1 搭建SpringBoot项目
我们使用IDEA新建一个SpringBoot项目,工程目录如下:
SendHttpRequest.java
package com.example.difyurl.controller;
import com.example.difyurl.service.GetHtmlContentService;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
@RestController
public class SendHttpRequest {
@Autowired
private GetHtmlContentService getHtmlContentService;
@PostMapping("/send")
public String sendGet(@RequestHeader(value = "Authorization", required = true) String authorization, @RequestParam(value = "url") String url)
{
try {
if (!authorization.equals("Bearer nanfeng")) {
return "authorization 错误";
}
if (Strings.isBlank(url) || Strings.isEmpty(url)) {
return "url 格式错误";
}
return getHtmlContentService.getHtmlContent(url);
} catch (Exception e) {
return "系统内部错误";
}
}
}
GetHtmlContentService.java
package com.example.difyurl.service;
public interface GetHtmlContentService {
String getHtmlContent(String url);
}
GetHtmlContentServiceImpl.java
package com.example.difyurl.service.impl;
import com.example.difyurl.service.GetHtmlContentService;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
@Service
public class GetHtmlContentServiceImpl implements GetHtmlContentService {
@Override
public String getHtmlContent(String url) {
URL url1 = null;
try {
url1 = new URL(url);
} catch (Exception e) {
e.printStackTrace();
return null;
}
StringBuffer contentBuffer = new StringBuffer();
int responseCode = -1;
HttpURLConnection con = null;
try {
con = (HttpURLConnection) url1.openConnection();
/*
* 此处的urlConnection对象实际上是根据URL的
* 请求协议(此处是http)生成的URLConnection类
* 的子类HttpURLConnection,故此处最好将其转化
* 为HttpURLConnection类型的对象,以便用到
* HttpURLConnection更多的API
* */
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36");
con.setConnectTimeout(60000);
con.setReadTimeout(60000);
// 获得网页返回信息码
responseCode = con.getResponseCode();
if (responseCode == -1) {
System.out.println(url.toString() + " : connection is failure...");
con.disconnect();
return null;
}
if (responseCode >= 400) // 请求失败
{
System.out.println("请求失败:get response code: " + responseCode);
con.disconnect();
return null;
}
InputStream inStr = con.getInputStream();
InputStreamReader istreamReader = new InputStreamReader(inStr, "utf-8");
BufferedReader buffStr = new BufferedReader(istreamReader);
String str = null;
while ((str = buffStr.readLine()) != null)
contentBuffer.append(str);
inStr.close();
} catch (IOException e) {
e.printStackTrace();
contentBuffer = null;
System.out.println("error: " + url.toString());
} finally {
con.disconnect();
}
return contentBuffer.toString();
}
}
可以看到项目中只有一个接口,而这个接口就是实现接收url并获取对应网页源码功能的。接下来我们启动服务,并测试接口的功能。
1.2 测试接口
我们使用Postman来发送和接收请求,服务运行在本地,我设置的端口为8081,所以地址为http://localhost:8081/send,根据接口的sendGet(@RequestHeader(value = “Authorization”, required = true) String authorization, @RequestParam(value = “url”) String url)参数可知,除了要给出url之外,还要在请求头中设置Authorization参数(之所以要设置Authorization是后面在Dify中可以设置鉴权)
点击send发送请求可以看到返回的结果
如果想检验返回的结果是否正确,可以自行进行对比
2、生成OpenAPI schema
如果上一步能测试成功,我们进一步生成该接口的curl命令。点击右上角的图标就会生成右侧的命令
接下来需要使用大模型将我们的curl命令转为OpenAPI schema,这里以ChatGpt为例,提示词如下:
请把curl请求命令转成openapi 3.1.0 版本的json schema,不需要包含response信息
<curl>
curl --location 'http://localhost:8081/send' \
--header 'Authorization: Bearer nanfeng' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'url=https://blog.csdn.net/shi_jiaye/article/details/119175407'
</curl>
json schema请参照下面的例子
<json-schema>
{
"openapi": "3.1.0",
"info": {
"title": "Get weather data",
"description": "Retrieves current weather data for a location.",
"version": "v1.0.0"
},
"servers": [
{
"url": ""
}
],
"paths": {},
"components": {
"schemas": {}
}
}
</json-schema>
生成结果如下
{
"openapi": "3.1.0",
"info": {
"title": "Send URL",
"description": "Sends a URL for processing",
"version": "v1.0.0"
},
"servers": [
{
"url": "http://localhost:8081"
}
],
"paths": {
"/send": {
"post": {
"description": "Send a URL for processing",
"operationId": "SendUrl",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/x-www-form-urlencoded": {
"schema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "The URL to be sent for processing"
}
},
"required": ["url"]
}
}
}
},
"responses": {
"200": {
"description": "URL successfully processed",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "success"
},
"message": {
"type": "string",
"example": "URL processed successfully"
}
}
}
}
}
},
"400": {
"description": "Bad Request"
},
"401": {
"description": "Unauthorized"
},
"500": {
"description": "Internal Server Error"
}
},
"security": [
{
"bearerAuth": []
}
]
}
}
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
}
}
但是这里有个坑,我们可以看到上面给出的url地址是http://localhost:8081,我们确实是在本地运行的,但是Dify服务是部署在Docker上的,所以本地是Docker的宿主机,所以要将url改为http://host.docker.internal:8081才能访问本地的服务
3、在Dify中自定义工具
3.1 创建自定义工具
到这里我们就获取到了心心念念的Schema,我们将生成的代码粘贴到对应的区域,“可用工具”这一栏就会识别到服务,还记得最开始我们设置了Authorization这个参数,就是用来鉴权用的
保存之后我们点击“测试”,输入url
可以看到返回了正确的结果,并且查看此时本地的服务日志,接口也是被调用了一次
如果发现返回的是“Reached maximum retries (3) for URL http://localhost:8081/send”,说明前面没将url改为http://host.docker.internal:8081,导致访问不到服务
3.2 使用自定义工具
我们创建一个工作流,并引入自定义的工具
紧接着使用大模型,给的提示词是“将{{#1728877649433.text#}}的内容提取出来,并进行总结概括,100字左右”,运行工作流,可以看到输出了结果
至此,自定义工具并使用完成,定义其他工具也是同理,希望对你们使用有帮助。
作者:南南的sky