Spring Boot3批式访问Dify聊天助手接口

发布于:2025-06-13 ⋅ 阅读:(17) ⋅ 点赞:(0)

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

在这里插入图片描述