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/v1Authorization: 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.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverdruid:url: jdbc:mysql://127.0.0.1:3306/library_1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=trueusername: rootpassword: 123456initial-size: 5min-idle: 15max-active: 30remove-abandoned-timeout: 180max-wait: 300000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000max-evictable-idle-time-millis: 900000stat-view-servlet:enabled: trueloginUsername: adminloginPassword: 123456allow:web-stat-filter:enabled: truesession-stat-enable: truesession-stat-max-count: 1000url-pattern: /*filters: stat,wall,slf4jfilter:stat:enabled: truedb-type: mysqllog-slow-sql: trueslow-sql-millis: 2000Dify:token: app-TNwXhtAMGxq3HfYPUCINeImgurl: http://localhost/v1/chat-messagesuser: 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 {@AutowiredDifyLlmService 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;@Autowiredprivate 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;}@Overridepublic 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