Spring Boot3批式访问Dify聊天助手接口
前言
之前已经配置好Dify1.4.1及LM Studio集成:
https://lizhiyong.blog.csdn.net/article/details/148607462
现在就可以借助Spring Boot3去访问Dify的后端接口,让前端展示大模型的返回内容。这是我等大数据资深学徒工们久经验证,在实际生产环境的正确使用方法,较直接调用大模型接口,使用聊天助手、工作流等方式做一层中间件包装后更安全、可控,可观测性也很好便于大数据平台开发攻城狮们查找到不良租户。
Dify聊天助手配置
为了防止超时,笔者配置的是DeepSeek14b小模型:
配置聊天助手时需要开启Think模式:
此时验证下:
可以正确调用LLM了!!!更新、发布、生成Token素质三联!!!
Dify接口文档
访问API即可看到接口文档,主要内容:
接口地址:
http://localhost/v1
Authorization: Bearer {API_KEY}
批式访问的请求报文:
curl -X POST 'http://localhost/v1/chat-messages' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"inputs": {},
"query": "What are the specs of the iPhone 13 Pro Max?",
"response_mode": "streaming",
"conversation_id": "",
"user": "abc-123",
"files": [
{
"type": "image",
"transfer_method": "remote_url",
"url": "https://cloud.dify.ai/logo/logo-site.png"
}
]
}'
批式访问的响应报文:
{
"event": "message",
"task_id": "c3800678-a077-43df-a102-53f23ed20b88",
"id": "9da23599-e713-473b-982c-4328d4f5c78a",
"message_id": "9da23599-e713-473b-982c-4328d4f5c78a",
"conversation_id": "45701982-8118-4bc5-8e9b-64562b4555f2",
"mode": "chat",
"answer": "iPhone 13 Pro Max specs are listed here:...",
"metadata": {
"usage": {
"prompt_tokens": 1033,
"prompt_unit_price": "0.001",
"prompt_price_unit": "0.001",
"prompt_price": "0.0010330",
"completion_tokens": 128,
"completion_unit_price": "0.002",
"completion_price_unit": "0.001",
"completion_price": "0.0002560",
"total_tokens": 1161,
"total_price": "0.0012890",
"currency": "USD",
"latency": 0.7682376249867957
},
"retriever_resources": [
{
"position": 1,
"dataset_id": "101b4c97-fc2e-463c-90b1-5261a4cdcafb",
"dataset_name": "iPhone",
"document_id": "8dd1ad74-0b5f-4175-b735-7d98bbbb4e00",
"document_name": "iPhone List",
"segment_id": "ed599c7f-2766-4294-9d1d-e5235a61270a",
"score": 0.98457545,
"content": "\"Model\",\"Release Date\",\"Display Size\",\"Resolution\",\"Processor\",\"RAM\",\"Storage\",\"Camera\",\"Battery\",\"Operating System\"\n\"iPhone 13 Pro Max\",\"September 24, 2021\",\"6.7 inch\",\"1284 x 2778\",\"Hexa-core (2x3.23 GHz Avalanche + 4x1.82 GHz Blizzard)\",\"6 GB\",\"128, 256, 512 GB, 1TB\",\"12 MP\",\"4352 mAh\",\"iOS 15\""
}
]
},
"created_at": 1705407629
}
就是常规的Post+Json模式!!!
Spring Boot3编码实现
依赖
简单配置一些依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zhiyong</groupId>
<artifactId>spring.study</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring.study</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.test.skip>true</maven.test.skip>
<fastjson.version>1.2.83</fastjson.version>
<log4j2.version>2.20.0</log4j2.version>
<disruptor.version>3.4.2</disruptor.version>
<mybatis-plus.version>3.5.12</mybatis-plus.version>
<druid.version>1.2.23</druid.version>
<mysql-connector.version>8.0.33</mysql-connector.version>
<hutool.version>5.8.11</hutool.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<xxl.job.version>2.4.0</xxl.job.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置文件
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
url: jdbc:mysql://127.0.0.1:3306/library_1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username: root
password: 123456
initial-size: 5
min-idle: 15
max-active: 30
remove-abandoned-timeout: 180
max-wait: 300000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
max-evictable-idle-time-millis: 900000
stat-view-servlet:
enabled: true
loginUsername: admin
loginPassword: 123456
allow:
web-stat-filter:
enabled: true
session-stat-enable: true
session-stat-max-count: 1000
url-pattern: /*
filters: stat,wall,slf4j
filter:
stat:
enabled: true
db-type: mysql
log-slow-sql: true
slow-sql-millis: 2000
Dify:
token: app-TNwXhtAMGxq3HfYPUCINeImg
url: http://localhost/v1/chat-messages
user: CSDN@HBaseIsNotFish
Controller
package com.zhiyong.spring.study.controller;
import com.zhiyong.spring.study.service.DifyLlmService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.Map;
@RestController
@RequestMapping("/Dify")
@Slf4j
public class DifyController {
@Autowired
DifyLlmService difyLlmService;
@PostMapping("/get/llm/solution")
public Map<String,Object> getAnswerByQueryBatch(@RequestHeader("UserId") String UserId, @RequestParam("query") String query) {
log.info("入参query={}", query);
Map<String,Object> result= difyLlmService.getAnswerByQueryBatch(query);
// result.put("llmSolution", query);
// result.put("thinkProcess", "思考中"+System.currentTimeMillis());
return result;
}
}
后续可以PostMan通过调用该接口验证可行性
Service
package com.zhiyong.spring.study.service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.Map;
public interface DifyLlmService {
Map<String, Object> getAnswerByQueryBatch(String query);
}
用于封装接口
Impl
package com.zhiyong.spring.study.service.Impl;
import com.alibaba.fastjson.JSONObject;
import com.zhiyong.spring.study.Obj.Entity.DifyBatchReqMessage;
import com.zhiyong.spring.study.Obj.Entity.DifyBatchResMessage;
import com.zhiyong.spring.study.Obj.Entity.DifyStreamReqMessage;
import com.zhiyong.spring.study.service.DifyLlmService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Service
@Slf4j
public class DifyLlmServiceImpl implements DifyLlmService {
@Value("${Dify.token}")
private String difyToken;
@Value("${Dify.url}")
private String difyUrl;
@Value("${Dify.user}")
private String difyUser;
@Autowired
private RestTemplate restTemplate;
private String getQuery(String queryInput) {
return "有个问题," + queryInput + "?";
}
private String getReqMessageBatch(String queryInput) {
String query = getQuery(queryInput);
String reqMessage = "";
DifyBatchReqMessage batchReqMessage = new DifyBatchReqMessage();
batchReqMessage.setUser(difyUser);
batchReqMessage.setQuery(query);
reqMessage = JSONObject.toJSONString(batchReqMessage);
return reqMessage;
}
@Override
public Map<String, Object> getAnswerByQueryBatch(String query) {
String answer = "";
String reqMessage = getReqMessageBatch(query);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setBearerAuth(difyToken);
HttpEntity<String> batchResMessageHttpEntity = new HttpEntity<>(reqMessage, httpHeaders);
try {
ResponseEntity<DifyBatchResMessage> entity = restTemplate.postForEntity(difyUrl, batchResMessageHttpEntity, DifyBatchResMessage.class);
if (!entity.getStatusCode().is2xxSuccessful()) {
log.error("调用Dify接口失败{}", entity.getStatusCode().value());
}
answer = entity.getBody().getAnswer();
log.info("answer={}", answer);
} catch (Exception e) {
log.error("调用Dify接口出现异常,{}", e.getMessage());
}
Map<String, Object> result = new HashMap<>();
String[] split = answer.split("</think>");
String[] split1 = split[0].split("<think>");
log.info("llmSolution,{}", split[1]);
log.info("thinkProcess,{}", split1);
result.put("llmSolution", split[1]);
result.put("thinkProcess", split1);
return result;
}
}
Entity
package com.zhiyong.spring.study.Obj.Entity;
import lombok.Data;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@Data
public class DifyBatchReqMessage {
Map<String, Object> inputs = new HashMap<>();
String query = "";
String mode = "chat";
String conversation_id = "";
String user = "";
List<Map<String, String>> files = new LinkedList<>();
}
批式请求的实体主要是这种格式
package com.zhiyong.spring.study.Obj.Entity;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class DifyBatchResMessage {
String event = "";
String task_id = "";
String id = "";
String message_id = "";
String conversation_id = "";
String mode = "";
String answer = "";
Map<String, Object> metadata = new HashMap<>();
String created_at = "";
}
批式请求的响应实体主要是这种格式
接口验证
http://127.0.0.1:9999/Dify/get/llm/solution
填写必要的请求头和请求参数后,接口在90s以后返回了内容:
{
"llmSolution": "虎鲸是鱼吗?\n\n**结论:虎鲸不是鱼,而是哺乳动物。**\n\n解释:\n\n1. **分类基础**:\n - 鱼类和哺乳动物都属于脊椎动物,但它们有不同的特征。\n\n2. **鱼类的特征**:\n - 用鳃呼吸。\n - 大多为卵生或卵胎生。\n\n3. **哺乳动物的特征**:\n - 用肺呼吸。\n - 胎生,通过哺乳喂养幼崽。\n\n4. **虎鲸的分类**:\n - 属于鲸鱼,是哺乳动物。\n - 尽管生活在海洋中,但它们符合哺乳动物的定义,因此不是鱼类。\n\n总结:虎鲸虽然是水生生物,但属于哺乳动物,而不是鱼。",
"thinkProcess": [
"",
"\n嗯,今天老师布置了一个问题,问虎鲸是不是鱼。这个问题听起来简单,但我觉得可能有些同学会有一些不同的看法。让我仔细想想。\n\n首先,我知道虎鲸是一种海洋生物,它生活在海里,应该和鱼一样在水里游动。那它们到底是不是鱼呢?我记得以前学过鱼类的定义,可能有帮助。鱼类通常有什么特征呢?\n\n我记得鱼是脊椎动物,有鳃,没有皮肤覆盖全身,身体两侧有一对鳍。比如,鲤鱼、鲨鱼这些都是鱼。那么虎鲸呢?它属于鲸鱼,鲸鱼是不是和鱼一样?\n\n然后,我想到鲸鱼其实不是鱼,而是哺乳动物。它们在哺乳动物的分类里,对吧?那为什么它们被称为鲸鱼,而没有叫鲸兽或者其他什么名字?\n\n再想想,鲸鱼的特点。它们是生活在海洋中的哺乳动物,用肺呼吸,生 baby是通过胎生的,对吗?比如,虎鲸就是其中一种鲸鱼,所以它应该属于哺乳动物,而不是鱼。\n\n那为什么有时候人们会说鲸鱼呢?可能是因为它们在水里生活,和鱼一样,但其实分类上它们有不同的特征。所以,虽然虎鲸生活在海里,但它还是哺乳动物,不是鱼。\n\n总结一下,鱼类和哺乳动物都是脊椎动物,但哺乳动物有胎生、用肺呼吸等特征,而鱼类则用鳃呼吸,卵生或卵胎生。因此,虎鲸作为鲸鱼,属于哺乳动物,所以它不是鱼。\n"
]
}
至此,获取到后端返回的内容来展示就是前端攻城狮的事情啦!!!
Dify后台观测
可以看到耗时93s花销了498个Token!!!
7840HS的780M核显跑14b小模型能到11个Token/s还不错!!!
转载请注明出处:https://lizhiyong.blog.csdn.net/article/details/148607578