Java集成openai,ollama,千帆,通义千问实现文本聊天

news2024/11/19 18:30:08

Java集成openai,ollama,千帆,通义千问实现文本聊天

本文所使用的环境如下

Win10 + JDK17 + SpringBoot3.2.4

集成Open AI

pom文件

<?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.2.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.whiteBrocade</groupId>
    <artifactId>ai-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ai-demo</name>
    <description>ai-demo</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
        <spring-ai.version>1.0.0-M1</spring-ai.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--热部署插件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.26</version>
        </dependency>
        <!-- Open AI-->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        </dependency>
        <!-- ollama AI-->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <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>
    <repositories>
        <!-- 这里指定了Srping的仓库, 因为现在市面上的Mvaven仓库还没有Spring-ai模块, 例如阿里云, 腾讯云等等都是下载不到的-->
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

yaml文件

spring:
  # 代码中的配置会覆盖yaml中相关AI参数配置
  ai:
    openai:
      # Api-Key, 没有的可以去淘宝搜索购买, 替换成你自己的key
      api-key: sk-xxxxxxxxx
      chat:
        options:
          # 模型, 默认为gpt-3.5-turbo,目前这个代理api支持以下模型:
          # gpt-3.5-turbo, gpt-3.5-turbo-0301, gpt-3.5-turbo-0613, gpt-3.5-turbo-0125, gpt-3.5-turbo-16k
          # gpt-3.5-turbo-1706, gpt-3.5-turbo-16k-0613, text-embedding-3-small, text-embedding-3-large
          model: gpt-3.5-turbo
          # 温度
          temperature: 0.4
      # 直连地址/代理地址
      # 切换成你自己的地址
      base-url: https://xxxx.com

AiMessage

import lombok.Data;
import org.springframework.ai.chat.messages.Message;

import java.util.Date;

/**
 * @author whiteBrocade
 * @description: 通用的Message抽象对象
 */
@Data
public class AiMessage {
    /**
     * id
     */
    private Long id;

    /**
     * 用户id
     */
    private Long userId;

    /**
     * 角色: user/assistant
     */
    private String role;

    /**
     * AI类型: ollama, openai
     */
    private String type;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * message内容
     */
    private Message message;
}

Controller

import cn.hutool.core.collection.BoundedPriorityQueue;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.RandomUtil;
import com.whitebrocade.aidemo2.domain.AiMessage;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.Date;
import java.util.List;
 
@Slf4j
@RestController
@RequestMapping("/openai")
public class OpenaiController {
    /**
     * Open AI 聊天模型
     */
    @Resource
    private OpenAiChatModel openAiChatModel;

    /**
     * 同步方式实现聊天功能
     *
     * @param prompt 提示词
     * @return 聊天结果
     */
    @PostMapping("/chat")
    public String chat(String prompt) {
        String ret = openAiChatModel.call(prompt);
        log.info(ret);
        return ret;
    }

    /**
     * 同步方式实现聊天功能
     *
     * @param prompt 提示词
     * @return 聊天结果
     */
    @PostMapping("/chat2")
    public String chat2(String prompt) {
        ChatResponse chatResponse = openAiChatModel.call(new Prompt(prompt));
        String ret = chatResponse.getResult().getOutput().getContent();
        log.info(ret);
        return ret;
    }

    /**
     * OpenAiChatOptions.builder() 传入的一个参数, 可以控制大模型的设置
     * 参数可以再代码中配置, 也可以在文件中配置, 如果代码中写了关于gpt的参数, 配置文件中也配置了参数, 那么以代码中为主
     * @param prompt 提示词
     * @return 聊天结果
     */

    @PostMapping("/chat3")
    public Object chat3(String prompt){
        ChatResponse chatResponse = openAiChatModel.call(new Prompt(prompt, OpenAiChatOptions.builder()
                //.withModel("gpt-4-32k")  // gpt的版本 , 32K是参数, 参数越高, 回答问题越准确
                .withTemperature(0.4F)  // 温度值, 温度越高, 回答的准确率越低, 温度越低, 回答的准确率越高
                .build()));
        String ret = chatResponse.getResult().getOutput().getContent();
        log.info(ret);
        return ret;
    }


    /***
     * 流式方式实现聊天功能
     * @param prompt 提示词
     * @return 聊天结果流
     */
    @PostMapping(value = "/stream1", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream1(String prompt) {
        return openAiChatModel.stream(prompt);
    }


    /**
     * 对话上下文轮数
     */
    private final int limit = 2;

    /**
     * 有界队列, 这里限2条信息, 并且删除最先发送的那条消息
     */
    private final BoundedPriorityQueue<AiMessage> messageQueue = new BoundedPriorityQueue<>(limit * 2,
            (o1, o2) -> -(DateUtil.compare(o1.getCreateTime(), o2.getCreateTime())));

    // 初始化java角色
    {
        // 系统前置消息
        Message systemMessage = new SystemMessage("你是一个Java行业的专家, 现在根据我的提问进行相关解答");

        AiMessage aiMessage = new AiMessage();
        aiMessage.setId(RandomUtil.randomLong());
        aiMessage.setUserId(1L);
        aiMessage.setRole(OpenAiApi.ChatCompletionMessage.Role.SYSTEM.name());
        aiMessage.setType("openai");
        aiMessage.setCreateTime(new Date());
        aiMessage.setMessage(systemMessage);
        messageQueue.offer(aiMessage);
    }

    /**
     * 流式方式实现聊天功能
     * @param prompt 提示词
     * @return 聊天结果流
     */
    @PostMapping(value = "/stream2", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream2(String prompt){
        // 构造用户回答
        UserMessage userMessage = new UserMessage(prompt);
        AiMessage userAiMessage = new AiMessage();
        userAiMessage.setId(RandomUtil.randomLong());
        userAiMessage.setUserId(1L);
        userAiMessage.setRole("user");
        userAiMessage.setType("openai");
        userAiMessage.setCreateTime(new Date());
        userAiMessage.setMessage(userMessage);
        messageQueue.offer(userAiMessage);
        List<Message> messageList = messageQueue.stream()
                .map(AiMessage::getMessage)
                .toList();

        // 模型填参
        Flux<ChatResponse> flux = openAiChatModel.stream(new Prompt(messageList, OpenAiChatOptions.builder()
                //.withModel("gpt-4-32k")  //gpt的版本 ,32K是参数,参数越高,回答问题越准确
                .withTemperature(0.4F)  //温度值,温度越高,回答的准确率越低,温度越低,回答的准确率越高
                .build()));

        // strBuilder用于追加AI的回答
        StrBuilder strBuilder = StrBuilder.create();
        return flux.map(response -> {
            String content = response.getResult().getOutput().getContent();
            if (content != null) {
                strBuilder.append(content);
                return content;
            } else { // null表示结束, 此时我们返回"", 前端判断返回结果是否为""
                return "";
            }
        }).doOnComplete(() -> {
            AssistantMessage assistantMessage = new AssistantMessage(strBuilder.toString());
            AiMessage assistantAiMessage = new AiMessage();
            assistantAiMessage.setId(RandomUtil.randomLong());
            assistantAiMessage.setUserId(1L);
            assistantAiMessage.setRole("system");
            assistantAiMessage.setType("openai");
            assistantAiMessage.setCreateTime(new Date());
            assistantAiMessage.setMessage(assistantMessage);
            messageQueue.offer(assistantAiMessage);
            log.info("Open AI模型响应结果: {}", strBuilder);
            log.info("执行完了");
        });
    }
}

集成Ollama

Ollama使用

如果本地没下载和或者没有使用Ollama, 那么这里你得看看, 如果已经使用过了, 那么跳过过这里, 去直接看代码集成

Ollama的下载, 安装以及使用

去Ollama官网下载, 并按照, 需要说明的是Ollama并非是一个大模型, 而是一个大模型的管理, 类似于Docker一样的角色, 而大模型类似于镜像的角色

进入PowerShell命令行中输入以下命令

ollama -v

如果显示了版本, 那么就说明安装成功

在这里插入图片描述

Ollama模型存储位置修改

此时不着急下载模型, Ollama默认会将模型存储如下位置

  • Linux/var/lib/ollama/models
  • WindowsC:\ProgramData\Ollama\models

Windos(我用的是这个)由于ollama是默认存储在C盘, 所以修改一下模型存储位置, 修改步骤如下:

  1. 在环境变量中新增OLLAMA_MODELS变量名, 注意了这个变量名一定不能变, 变量值指向你要存放的文件夹, 如下
    在这里插入图片描述

  2. 重启电脑(不重启电脑不生效!), 然后本地下载的模型就自动迁移的到你配置的目录了!

Ollama基本命令
  • ollama serve: 启动

  • ollama list: 查看所有可用模型

  • ollama run 模型名称: 运行选定模型

    • 如果本地没有这个模型, 那么就会下载
  • ollama show 模型名称: 来获取更详细的模型信息, 包括模型的描述、版本、大小等

  • ollama download 模型名称: 下载模型

  • ollama update 模型名称: 来更新已下载的模型

  • ollama rm 模型名称

  • ollama ps: 查看正在运行的模型

这里先简单过一眼, 后续使用到会介绍

模型下载

点击Models进入模型下载页面
在这里插入图片描述

搜索阿里的千问2的模型
在这里插入图片描述

选择0.5b的参数的模型

这里的b是指模型的训练的参数量, 1b=1亿参数, 参数越多, AI模型越智能, 对应的硬盘越大, 所需要的GPU和内存要求越高, 所以此处选择0.5的, 不懵逼, 不伤脑

在这里插入图片描述

复制此命令到, 命令行中执行, 然后ollama就会下载该模型, 耐心等待即可, 下载完成会运行该模型

在这里插入图片描述

模型的启动和使用

直接输入你的问题即可, Ctrl + D退出模型对话
在这里插入图片描述

代码集成Ollama本地模型

pom文件
<?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.2.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.whiteBrocade</groupId>
    <artifactId>ai-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ai-demo</name>
    <description>ai-demo2</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
        <spring-ai.version>1.0.0-M1</spring-ai.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--热部署插件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.26</version>
        </dependency>
        <!-- Open AI-->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        </dependency>
        <!-- ollama AI-->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <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>
    <repositories>
        <!-- 这里指定了Srping的仓库, 因为现在市面上的Mvaven仓库还没有Spring-ai模块, 例如阿里云, 腾讯云等等都是下载不到的-->
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

yaml文件
spring:
  # 代码中的配置会覆盖yaml中相关AI参数配置
  ai:
    ollama:
      chat:
        options:
          # 注意了, ollama中要有这个模型
          model: qwen:0.5b
          temperature: 0.4
      # 直连地址/代理地址
      # 这里填的是我本地的ollama的启动地址,
      base-url: http://localhost:11434
Controller
import cn.hutool.core.text.StrBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.ArrayList;
import java.util.List;

/**
 * @author whiteBrocade
 * @description: OllamaController
 */
@Slf4j
@RestController
@RequestMapping("/ollama")
public class OllamaController {
    @Resource
    private OllamaChatModel ollamaChatModel;

    /**
     * 一次性聊天, 不会记录上下文
     * @param prompt 提示词
     * @return
     */
    @PostMapping(value = "/chat1")
    public Object chat1(String prompt) {
        String ret = ollamaChatModel.call(prompt);
        log.info(ret);
        return ret;
    }


    /**
     * 这里可以将消息存入MySQL
     */
    private final List<Message> messageList = new ArrayList<>();
    {
        // 系统前置消息
        Message systemMessage = new SystemMessage("你是一个Java行业的专家, 现在根据我的提问进行相关解答");
        messageList.add(systemMessage);
    }

    /**
     * 一次性聊天, 不会记录上下文
     * @param prompt 提示词
     * @return
     */
    @RequestMapping(value = "/chat2")
    public Object chat2(String prompt) {
        Message userMessage = new UserMessage(prompt);
        messageList.add(userMessage);

        ChatResponse chatResponse = ollamaChatModel.call(new Prompt(messageList));
        String content = chatResponse.getResult().getOutput().getContent();
        log.info(content);
        return content;
    }

    /**
     * 流式方式实现聊天功能
     * @param prompt 提示词
     * @return 聊天结果流
     */
    @PostMapping(value = "/stream1", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream1(String prompt) {
        return ollamaChatModel.stream(prompt);
    }

    /**
     * 流式方式实现聊天功能
     * @param prompt 提示词
     * @return 聊天结果流
     */
    @PostMapping(value = "/stream2", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream2(String prompt) {
        UserMessage userMessage = new UserMessage(prompt);
        messageList.add(userMessage);

        Flux<ChatResponse> flux = ollamaChatModel.stream(new Prompt(messageList, OllamaOptions.create()
                .withTemperature(0.4F)  //温度值,温度越高,回答的准确率越低,温度越低,回答的准确率越高
        ));
        // sb用于追加AI的回答
        StrBuilder strBuilder = StrBuilder.create();
        return flux.map(response -> {
            String content = response.getResult().getOutput().getContent();
            if (content != null) {
                strBuilder.append(content);
                return content;
            } else { // null表示结束, 此时我们返回"", 前端判断返回结果是否为""
                return "";
            }
        }).doOnComplete(() -> {
            AssistantMessage assistantMessage = new AssistantMessage(strBuilder.toString());
            messageList.add(assistantMessage);
            log.info("ollama AI模型响应结果: {}", strBuilder);
            log.info("执行完了");
        });
    }
}

通义千问对接

SDK

pom文件
<?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>
    <groupId>com.whiteBrocade</groupId>
    <artifactId>ai-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ai-demo</name>
    <description>ai-demo</description>
    <properties>
        <java.version>17</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.4.2</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--阿里AI的SDK-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dashscope-sdk-java</artifactId>
            <version>2.15.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-simple</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Reactor Core -->
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
            <version>3.4.6</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.26</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.whitebrocade.aidemo.AiDemoApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
yaml文件
spring:
  ai:
    qwen:
      # 义通千问的api-key, 没有的去阿里云申请
      api-key: sk-xxxxxx
      # 模型
      model: qwen-plus
      # 随机数种
      seed: 1234
      # 消耗的token数目
      # 对于中文文本来说, 千问模型的1个token平均对应1.5-1.8个汉字;对于英文文本来说, 1个token通常对应一个单词或词根
      maxTokens: 10
      # 温度
      temperature: 0.4
      # 是否允许联网查询
      enableSearch: true
      # 返回内容是否不需要追加, true不追加, false追加
      incrementalOutput: true
Controller
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.aigc.generation.GenerationUsage;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import io.reactivex.Flowable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/qwen")
public class QwenController {

    /**
     * 随机数种
     */
    @Value("${spring.ai.qwen.seed}")
    private Integer seed;

    /**
     * AI模型
     */
    @Value("${spring.ai.qwen.model}")
    private String model;

    /**
     * AI密钥
     */
    @Value("${spring.ai.qwen.api-key}")
    private String apiKey;

    /**
     * 生成最大的token
     */
    @Value("${spring.ai.qwen.maxTokens}")
    private Integer maxTokens;

    /**
     * 温度
     */
    @Value("${spring.ai.qwen.temperature}")
    private Float temperature;

    /**
     * 是否联网
     */
    @Value("${spring.ai.qwen.enableSearch}")
    private boolean enableSearch;

    /**
     * 返回内容是否不需要追加
     */
    @Value("${spring.ai.qwen.incrementalOutput}")
    private boolean incrementalOutput;

    /**
     * AI生成对象
     */
    private final Generation generation = new Generation();

    // -------------------------- 方法 --------------------------

    /**
     * 单轮对话
     *
     * @param prompt 提示词
     * @return 返回结果
     */
    @PostMapping(value = "/chat")
    public String chat(String prompt) {
        try {
            // 用户与模型的对话历史。list中的每个元素形式为{“role”:角色, “content”: 内容}。
            Message userMessage = this.buildMessage(Role.USER, prompt);

            // 填充参数
            GenerationParam param = this.getBaseGenerationParam()
                    .messages(Collections.singletonList(userMessage))
                    .build();

            GenerationResult generationResult = generation.call(param);
            log.info("千问输出结果:{}", JSONUtil.toJsonStr(generationResult));
            return generationResult.getOutput().getChoices().get(0).getMessage().getContent();
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * message存储, 这里模拟数据查询出来的Message集合
     */
    private final List<Message> messageList = new ArrayList<>();

    {
        // 初始化系统角色
        Message systemMsg = Message.builder()
                .role(Role.ASSISTANT.getValue())
                .content("你是Java高级工程师, 现在根据我的提问进行回答") // 初始化系统角色
                .build();
        messageList.add(systemMsg);
    }


    /**
     * 流式单论对话
     *
     * @param prompt 提示词
     * @return 返回结果
     */
    @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flowable<String> stream(String prompt) throws NoApiKeyException, InputRequiredException {
        // 用户与模型的对话历史。list中的每个元素形式为{"role":角色, "content": 内容}。
        Message userMessage = this.buildMessage(Role.USER, prompt);
        messageList.add(userMessage);

        // 填充参数
        GenerationParam param = this.getBaseGenerationParam()
                .messages(messageList)
                .build();

        Flowable<GenerationResult> flux = generation.streamCall(param);
        return flux.map(generationResult -> {
            Message repMessage = generationResult.getOutput().getChoices().get(0).getMessage();
            log.info("千问模型响应结果, 角色:{}, 内容: {}", repMessage.getRole(), repMessage.getContent());
            String content = repMessage.getContent();
            return content;
        }).doOnComplete(() -> {
            log.info("结果响应完了");
        }).doOnError(e -> {
            log.error("结果响应发生异常");
            throw new RuntimeException(e);
        });
    }

    /**
     * 流式单论对话, 当相应内容完成后, 返回""
     *
     * @param prompt 多轮对话提示词
     * @return 返回结果
     */
    @PostMapping(value = "/stream2", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flowable<String> stream2(String prompt) throws NoApiKeyException, InputRequiredException {
        // 初始化用户信息
        Message userMessage = this.buildMessage(Role.USER, prompt);

        // 支持对话轮数
        if (messageList.size() == 7) {
            messageList.remove(0);
        }
        messageList.add(userMessage);

        // 填充参数
        GenerationParam param = this.getBaseGenerationParam()
                .messages(messageList)
                .build();

        // 流式返回
        Flowable<GenerationResult> flux = generation.streamCall(param);
        // 响应的消息
        StrBuilder strBuilder = new StrBuilder();
        return flux.map(generationResult -> {
            // token消耗情况
            GenerationUsage usage = generationResult.getUsage();
            Integer inputTokens = usage.getInputTokens();
            Integer outputTokens = usage.getOutputTokens();
            Integer totalTokens = usage.getTotalTokens();
            log.info("输入token: {}, 输出token: {}, 总token: {}", inputTokens, outputTokens, totalTokens);

            Message assistantMessage = generationResult.getOutput().getChoices().get(0).getMessage();
            String role = assistantMessage.getRole();
            String content = assistantMessage.getContent();
            if (StrUtil.isNotEmpty(content)) {
                strBuilder.append(content);
                log.info("千问模型响应结果, 角色: {}, 内容: {}", role, content);
            }
            return content;
        }).doOnComplete(() -> {
            // 添加assistant返回到messages列表,user/assistant消息必须交替出现
            if (messageList.size() == 7) {
                messageList.remove(0);
            }
            // 将响应消息添加到集合中
            Message assistantMessage = Message.builder()
                    .role(Role.ASSISTANT.getValue())
                    .content(strBuilder.toString())
                    .build();
            messageList.add(assistantMessage);
            log.info("结果响应完了");
        }).doOnError(e -> {
            log.error("结果响应发生异常");
            throw new RuntimeException(e);
        });
    }

    /**
     * 构建Message
     * @param role 角色
     * @param content 内容
     * @return 构建好的Message
     */
    private Message buildMessage(Role role, String content) {
        return Message.builder()
                .role(role.getValue())
                .content(content)
                .build();
    };

    /**
     * 构建公共param
     *
     * @return GenerationParam
     */
    private GenerationParam.GenerationParamBuilder<?, ?> getBaseGenerationParam() {
        // 模型参数
        return GenerationParam.builder()
                .seed(seed) // 随机数种
                .model(model) // AI模型
                .apiKey(apiKey) // Api Key
                .maxTokens(maxTokens) // 生成结果消耗的最大token值
                .temperature(temperature) // 温度
                .enableSearch(enableSearch)
                .incrementalOutput(incrementalOutput) // 后续输出不追加之前输出的内容
                .resultFormat(GenerationParam.ResultFormat.MESSAGE); // 返回值信息
    }
}

HTTP

api文档

pom文件
<?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>
    <groupId>com.whiteBrocade</groupId>
    <artifactId>ai-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ai-demo</name>
    <description>ai-demo</description>
    <properties>
        <java.version>17</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.4.2</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Reactor Core -->
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
            <version>3.4.6</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.26</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.whitebrocade.aidemo.AiDemoApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
yaml文件
spring:
  ai:
    qwen:
      # 义通千问的api-key, 没有的去阿里云申请
      api-key: sk-xxxxxxx
      # 模型
      model: qwen-plus
      # 随机数种
      seed: 1234
      # 消耗的token数目
      maxTokens: 1000
      # 温度
      temperature: 0.4
      # 是否允许联网查询
      enableSearch: true
      # 返回内容是否不需要追加, true不追加, false追加
      incrementalOutput: true
      # http请求地址
      url: https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation



Request
QwenRequest
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.util.List;

/**
 * 千问请求参数
 */
@Data
public class QwenRequest implements Serializable {
    /**
     * AI模型
     */
    private String model;

    /**
     * message输入
     */
    private Input input;

    /**
     * 其它参数
     */
    private Parameters parameters;

    @Data
    public static class Input implements Serializable {
        private List<Message> messages;
    }

    @Data
    public static class Message implements Serializable {
        /**
         * 角色
         */
        private String role;

        /**
         * 内容
         */
        private String content;
    }

    /**
     * 参数
     */
    public static class Parameters implements Serializable {
        /**
         * 随机数种
         */
        @Getter
        @Setter
        @JsonProperty("seed")
        private Integer seed;

        /**
         * 限制模型生成token的数量
         */
        @Getter
        @Setter
        @JsonProperty("max_tokens")
        private Integer maxTokens;

        /**
         * 温度
         */
        @Getter
        @Setter
        @JsonProperty("temperature")
        private Float temperature;

        /**
         * 是否互联网搜索
         */
        @Getter
        @Setter
        @JsonProperty("enable_search")
        private boolean enableSearch;

        /**
         * 响应结果格式: text
         */
        @Getter
        @JsonProperty("result_format")
        private String resultFormat = GenerationParam.ResultFormat.TEXT;
    }
}
QwenStreamRequest
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;

/**
 * @author whiteBrocade
 * @description: 流式请求
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class QwenStreamRequest extends QwenRequest implements Serializable {

    public static class Parameters extends QwenRequest.Parameters implements Serializable {
        /**
         * 是否增量输出
         */
        @Getter
        @Setter
        @JsonProperty("incremental_output")
        private boolean incrementalOutput;

        /**
         * 响应结果格式: message
         */
        @Getter
        @JsonProperty("result_format")
        private String resultFormat = GenerationParam.ResultFormat.MESSAGE;
    }
}

Response
QwenBaseResponse
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * @author whiteBrocade
 * @description: 千问普通请求响应结果
 */
@Data
public class QwenBaseResponse implements Serializable {
    /**
     * 本次调用使用的token信息。
     */
    private Usage usage;

    /**
     * 本次请求的系统唯一码
     */
    @JsonProperty("request_id")
    private String requestId;

    @Data
    public static class Usage implements Serializable {
        /**
         * 本次对话消耗的token
         */
        @JsonProperty("total_tokens")
        private int totalTokens;

        /**
         * 响应消耗的token
         */
        @JsonProperty("output_tokens")
        private int outputTokens;

        /**
         * 请求消耗的token
         */
        @JsonProperty("input_tokens")
        private int inputTokens;
    }
}
QwenResponse
package com.whitebrocade.aidemo.response;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;

/**
 * @author whiteBrocade
 * @description: 千问普通请求响应结果
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class QwenResponse extends QwenBaseResponse implements Serializable {
    /**
     * 响应输出结果
     */
    private QwenResponse.Output output;

    /**
     * 响应输出结果
     */
    @Data
    public static class Output implements Serializable {
        /**
         * 结束原因
         * 有三种情况:
         *   1. 正在生成时为null
         *   2. 生成结束时如果由于停止token导致则为stop
         *   3. 生成结束时如果因为生成长度过长导致则为length。当result_format设置为text时返回该字段
         */
        @JsonProperty("finish_reason")
        private String finishReason;

        /**
         * 响应结果文本
         */
        private String text;
    }
}

QwenStreamResponse
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;

/**
 * @author whiteBrocade
 * @description: 流式响应结果
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class QwenStreamResponse extends QwenBaseResponse implements Serializable {
    /**
     * 响应输出结果
     */
    private QwenStreamResponse.Output output;

    /**
     * 响应输出结果
     */
    @Data
    public static class Output implements Serializable {
        /**
         * 响应结果
         */

        @JsonProperty("choices")
        private Choices[] choices;
    }

    /**
     * 响应结果
     */
    @Data
    public static class Choices implements Serializable {
        /**
         * 停止原因
         * 1. null: 生成过程中
         * 2. stop: stop token导致结束
         * 3. length: 生成长度导致结束
         */
        @JsonProperty("finish_reason")
        private String finish_reason;

        /**
         * 信息
         */
        private Message message;
    }

    /**
     * message每个元素形式为{"role":角色, "content": 内容}。角色可选值:system、user、assistant。content为模型输出的内容
     */
    @Data
    public static class Message implements Serializable {
        /**
         * 角色
         */
        private String role;

        /**
         * 内容
         */
        private String content;
    }
}
Controller
package com.whitebrocade.aidemo.web;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.dashscope.common.Role;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.whitebrocade.aidemo.request.QwenRequest;
import com.whitebrocade.aidemo.request.QwenStreamRequest;
import com.whitebrocade.aidemo.response.QwenResponse;
import com.whitebrocade.aidemo.response.QwenStreamResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;

/**
 * @author whiteBrocade
 * @version 1.0
 * @date 2024/7/4 下午1:02
 * @description: TODO
 */
@Slf4j
@RestController
@RequestMapping("/qwen/http")
public class QwenHttpController {
    /**
     * 请求地址
     */
    @Value("${spring.ai.qwen.url}")
    private String url;

    /**
     * 随机数种
     */
    @Value("${spring.ai.qwen.seed}")
    private Integer seed;

    /**
     * AI模型
     */
    @Value("${spring.ai.qwen.model}")
    private String model;

    /**
     * AI密钥
     */
    @Value("${spring.ai.qwen.api-key}")
    private String apiKey;

    /**
     * 生成最大的token
     */
    @Value("${spring.ai.qwen.maxTokens}")
    private Integer maxTokens;

    /**
     * 温度
     */
    @Value("${spring.ai.qwen.temperature}")
    private Float temperature;

    /**
     * 是否联网
     */
    @Value("${spring.ai.qwen.enableSearch}")
    private boolean enableSearch;

    /**
     * 返回内容是否不需要追加
     */
    @Value("${spring.ai.qwen.incrementalOutput}")
    private boolean incrementalOutput;

    // --------------------- 方法 ---------------------


    @PostMapping(value = "/chat")
    public String chat(String prompt) throws Exception {
        // 填充message
        // system message
        QwenRequest.Message systemMessage = new QwenRequest.Message();
        systemMessage.setRole(Role.SYSTEM.getValue());
        systemMessage.setContent("你是一个Java高级工程师");
        // user message
        QwenRequest.Message userMessage = new QwenRequest.Message();
        userMessage.setRole(Role.USER.getValue());
        userMessage.setContent(prompt);
        List<QwenRequest.Message> messages = List.of(systemMessage, userMessage);

        QwenRequest qwenRequest = this.getQwenRequest(messages);
        // 转换成json
        ObjectMapper objectMapper = new ObjectMapper();
        String reqJsonStr = objectMapper.writeValueAsString(qwenRequest);
        // 请求的str
        log.info("请求参数: {}", reqJsonStr);

        HttpRequest httpRequest = this.getHttpRequest()
                .body(reqJsonStr); // 请求体

        String text; // 响应文本
        try {
            String responseJsonStr = httpRequest.execute() // 发送请求
                    .body();
            log.info("响应结果: {}", responseJsonStr);

            QwenResponse qwenResponse = objectMapper.readValue(responseJsonStr, QwenResponse.class);

            text = qwenResponse.getOutput().getText();
        } catch (Exception e) {
            log.error("请求异常: {}", e.getMessage());
            throw new RuntimeException();
        }
        return text;
    }

    /**
     * 流式对话
     * @param prompt 提示词
     * @return 响应结果
     */
    @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream(String prompt) throws JsonProcessingException {
        // 填充message
        // system message
        QwenRequest.Message systemMessage = new QwenRequest.Message();
        systemMessage.setRole(Role.SYSTEM.getValue());
        systemMessage.setContent("你是一个Java高级工程师");
        // user message
        QwenRequest.Message userMessage = new QwenRequest.Message();
        userMessage.setRole(Role.USER.getValue());
        userMessage.setContent(prompt);
        List<QwenRequest.Message> messages = List.of(systemMessage, userMessage);


        QwenStreamRequest qwenStreamRequest = this.getQwenStreamRequest(messages);
        // 转换成json
        ObjectMapper objectMapper = new ObjectMapper();
        String reqJsonStr = objectMapper.writeValueAsString(qwenStreamRequest);
        // 请求的str
        log.info("请求参数: {}", reqJsonStr);

        HttpRequest streamHttpRequest = this.getStreamHttpRequest()
                .body(reqJsonStr); // 请求体

        // try {
        //
        //     // text = qwenResponse.getOutput().getText();
        // } catch (Exception e) {
        //     log.error("请求异常: {}", e.getMessage());
        //     throw new RuntimeException();
        // }


        return Flux.create(sink -> {
            InputStream inputStream = streamHttpRequest.execute(true) // 异步调用
                    .bodyStream();
            BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
            try (inputStream) {
                StrBuilder strBuilder = StrBuilder.create();
                String line;
                while ((line = in.readLine()) != null) {
                    // 数据前缀
                    String prefix = "data:";
                    if (line.startsWith(prefix)) {
                        // 截取数据
                        String responseJsonStr = StrUtil.subAfter(line.trim(), prefix, false);
                        QwenStreamResponse qwenStreamResponse = objectMapper.readValue(responseJsonStr, QwenStreamResponse.class);

                        // 内容
                        String content = qwenStreamResponse
                                .getOutput()
                                .getChoices()[0]
                                .getMessage()
                                .getContent();
                        
                        strBuilder.append(content);

                        // 发送每个响应片段
                        // 当为""的时候, 那么就就表示响应结束
                        sink.next(content);
                    }
                }

                log.info("响应内容为: {}", strBuilder.toString());
                // todo 这里可以讲响应内容保存到数据库中

                // 完成流式传输
                sink.complete();
            } catch (Exception e) {
                // 发送错误信号
                log.error("传输错误", e);
                sink.error(e);
            }
        });
    }



    /**
     * 获取基本的QwenRequest
     * @return HttpRequest
     */
    private HttpRequest getHttpRequest() {
        return HttpUtil.createPost(url)
                .contentType(ContentType.JSON.getValue()) // Content-Type
                .auth("Bearer " + apiKey); // Authorization
    }

    /**
     * 流式请求
     * @return HttpRequest
     */
    private HttpRequest getStreamHttpRequest() {
        return this.getHttpRequest()
                .header("X-DashScope-SSE", "enable");
    }


    /**
     * 获取基本的QwenRequest
     * @param messages 消息
     * @return QwenRequest
     */
    private QwenRequest getQwenRequest(List<QwenRequest.Message> messages) {
        QwenRequest qwenRequest = new QwenRequest();
        qwenRequest.setModel(model); // 模型

        QwenRequest.Input input = new QwenRequest.Input();
        input.setMessages(messages);
        qwenRequest.setInput(input);

        QwenRequest.Parameters parameters = new QwenRequest.Parameters();
        parameters.setSeed(seed);
        parameters.setMaxTokens(maxTokens);
        parameters.setTemperature(temperature);
        parameters.setEnableSearch(enableSearch);
        qwenRequest.setParameters(parameters);

        return qwenRequest;
    }

    /**
     * 获取 QwenStreamRequest
     * @param messages 消息
     * @return QwenStreamRequest
     */
    private QwenStreamRequest getQwenStreamRequest(List<QwenRequest.Message> messages) {
        QwenRequest qwenRequest = this.getQwenRequest(messages);
        QwenStreamRequest qwenStreamRequest = new QwenStreamRequest();
        qwenStreamRequest.setModel(qwenRequest.getModel());
        qwenStreamRequest.setInput(qwenRequest.getInput());

        QwenRequest.Parameters parameters = qwenRequest.getParameters();
        QwenStreamRequest.Parameters tempparameters = new QwenStreamRequest.Parameters();
        BeanUtil.copyProperties(parameters, tempparameters);

        // 设置增量输出
        tempparameters.setIncrementalOutput(incrementalOutput);
        qwenStreamRequest.setParameters(tempparameters);

        return qwenStreamRequest;
    }
}

百度千帆大模型对接

SDK

pom
<?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>
    <groupId>com.whiteBrocade</groupId>
    <artifactId>ai-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ai-demo</name>
    <description>ai-demo</description>
    <properties>
        <java.version>17</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.4.2</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <!--千帆-文心一言-->
        <dependency>
            <groupId>com.baidubce</groupId>
            <artifactId>qianfan</artifactId>
            <version>0.0.9</version>
        </dependency>

        <!-- Reactor Core -->
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
            <version>3.4.6</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.26</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.whitebrocade.aidemo.AiDemoApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
yaml
spring:
  ai:
    # 千帆
    qianfan:
      access-key: xxxxx
      secret-key: xxxxxx
      # 使用免费的模型, 免费的模型如下ERNIE-Speed-128K, ERNIE-Speed-8K, ERNIE Speed-AppBuilder, ERNIE-Lite-8K, ERNIE-Lite-8K-0922, ERNIE-Tiny-8K, Yi-34B-Chat, Fuyu-8B, Prompt模板
      model: ERNIE-Speed-128K
      # 输出结果影响因子
      topP: 0.7
      # 温度
      temperature: 0.7
      # 输出最大的token值
      maxOutputTokens: 50
Controller
import com.baidubce.qianfan.Qianfan;
import com.baidubce.qianfan.core.StreamIterator;
import com.baidubce.qianfan.core.auth.Auth;
import com.baidubce.qianfan.core.builder.ChatBuilder;
import com.baidubce.qianfan.model.chat.ChatResponse;
import com.baidubce.qianfan.model.constant.ChatRole;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

/**
 * @author whiteBrocade
 * @version 1.0
 * @date 2024/7/3 下午7:02
 * @description: TODO
 */

@Slf4j
@RestController
@RequestMapping("/qianfan")
public class QianFanController {

    /**
     * 输出文本的多样性因子, 影响输出文本的多样性,  取值越大,  生成文本的多样性越强
     */
    @Value("${spring.ai.qianfan.topP}")
    private Double topP;

    /**
     * AI模型
     */
    @Value("${spring.ai.qianfan.model}")
    private String model;

    /**
     * accessKey
     */
    @Value("${spring.ai.qianfan.access-key}")
    private String accessKey;

    /**
     * secretKey
     */
    @Value("${spring.ai.qianfan.secret-key}")
    private String secretKey;


    /**
     * 温度
     */
    @Value("${spring.ai.qianfan.temperature}")
    private Double temperature;

    /**
     * 指定模型最大输出token数, 说明:
     * (1)如果设置此参数, 范围[2, 2048]
     * (2)如果不设置此参数, 最大输出token数为1024
     */
    @Value("${spring.ai.qianfan.maxOutputTokens}")
    private Integer maxOutputTokens;

    /**
     * 单论对话
     *
     * @param prompt 提示词
     * @return 响应结果
     */
    @PostMapping(value = "/chat")
    public String chat(String prompt) {
        // 创建
        ChatResponse response = this.getBaseChatBuilder()
                .addMessage(ChatRole.USER, prompt) // com.baidubce.qianfan.model.constant.ChatRole 这个类有相关的角色 添加用户消息 (此方法可以调用多次, 以实现多轮对话的消息传递)
                // .addUserMessage("") // 添加用户信息
                // .addAssistantMessage("") // 添加助理信息
                .execute(); // 发起请求
        String result = response.getResult();
        log.info(result);
        return result;
    }

    /**
     * 多轮对话
     *
     * @param prompt 提示词
     * @return 响应结果
     */
    @PostMapping(value = "/chat2")
    public String chat2(String prompt) {
        // 创建
        ChatResponse response = this.getBaseChatBuilder()
                .addUserMessage("你是谁?") // 添加用户信息
                .addAssistantMessage("你好,  我是一个人工智能语言模型,  可以协助你完成范围广泛的任务并提供有关各种主题的信息。作为一个计算机程序,  我没有具体的身份和个人特征,  我的目的是通过回答问题、提供信息和执行任务来帮助用户。请问你有任何问题都可以询问我。") // 添加助理信息
                .addUserMessage(prompt) // 添加用户信息
                .execute(); // 发起请求
        String result = response.getResult();
        log.info(result);
        return result;
    }

    /**
     * 流式对话
     * @param prompt 提示词
     * @return 响应结果
     */
    @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream(String prompt) {
        return Flux.create(sink -> {
            try(StreamIterator<ChatResponse> flux = this.getBaseChatBuilder()
                    .addUserMessage(prompt)
                    .executeStream()) {
                // 当有数据的时候, 将数据返回
                while (flux.hasNext()) {
                    ChatResponse chunk = flux.next();
                    String result = chunk.getResult();
                    // 发送每个响应片段
                    // 当为""的时候, 那么就就表示响应结束
                    sink.next(result);
                }
                
                // 完成流式传输
                sink.complete();
            } catch (Exception e) {
                // 发送错误信号
                log.error("传输错误", e);
                sink.error(e);
            }
        });
    }

    /**
     * 获取初始化ChatBuilder
     *
     * @return ChatBuilder
     */
    private ChatBuilder getBaseChatBuilder() {
        Qianfan qianfan = new Qianfan(Auth.TYPE_OAUTH, accessKey, secretKey);
        return qianfan.chatCompletion()
                .topP(topP) // 输出文本的多样性因子
                .maxOutputTokens(maxOutputTokens) // 指定模型最大输出token数
                .model(model) // 使用model指定预置模型
                .temperature(temperature); // 温度
    }
}

参考资料

Spring接入阿里云通义千问Demo

java对接阿里云通义千问API多轮对话承接上下文

SpringBoot + 通义千问 + 自定义React组件, 支持EventStream数据解析!

通义千问最新api java调用

Spring AI 接入OpenAI大模型实现同步和流式对话

Spring Boot 整合 Spring AI 实现项目接入ChatGPT

Ollama全面指南:安装、使用与高级定制

Ollama 常用命令_ollama 命令

【ollama】linux、window系统更改模型存放位置, 全网首发2024!_ollama 设置模型位置

更改ollama模型存储路径

ollama如何把gemma模型部署到D盘

五个优秀的免费 Ollama WebUI 客户端推荐

[Ollama深度探索:AI大模型本地部署的全面教程_ollama训练大模型](https://blog.csdn.net/xiaobing259/article/details/139853520#:~:text=二、安装与配置 1 1、系统要求 在开始安装Ollama之前, 确保您的系统满足以下基本要求: 操作系统:macOS、Windows 10及以上版本、Linux(包括但不限于Ubuntu、Fedora) 内存:至少4GB RAM,推荐8GB或以上, 具体取决于所运行模型的大小,… 4 4、启动Ollama服务 Ollama服务可以通过命令行界面(CLI)启动。 使用以下命令启动Ollama服务: ollama serve )

open-webui

零基础入门AI:一键本地运行各种开源大语言模型 - Ollama

RAG 实践-Ollama+AnythingLLM 搭建本地知识库

超级详细Spring AI运用Ollama大模型

Kimi API 还没用起来?请看这篇无门槛快速入门指南

Moonshot AI - 开放平台

ERNIE-4.0-Turbo-8K - 千帆大模型平台 | 百度智能云文档

bce-qianfan-sdk/java at main · baidubce/bce-qianfan-sdk

使用Python调用百度千帆免费大模型接口

java ISO 8601 日期格式进行转换

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1896335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Emacs有什么优点,用Emacs写程序真的比IDE更方便吗?

Emacs 是一个功能强大的文本编辑器&#xff0c;它在开发者和程序员中非常受欢迎&#xff0c;主要优点包括&#xff1a; 可定制性&#xff1a;Emacs 允许用户通过 Lisp 编程语言来自定义编辑器的行为和界面&#xff0c;几乎可以修改任何方面。扩展性&#xff1a;拥有大量的扩展…

【博士每天一篇文献-综述】Threats, Attacks, and Defenses in Machine Unlearning A Survey

1 介绍 年份&#xff1a;2024 作者&#xff1a;刘子耀&#xff0c;陈晨&#xff0c;南洋理工大学 期刊&#xff1a; 未发表 引用量&#xff1a;6 Liu Z, Ye H, Chen C, et al. Threats, attacks, and defenses in machine unlearning: A survey[J]. arXiv preprint arXiv:2403…

idea导入Maven项目

导入Maven项目 方式1&#xff1a;使用Maven面板&#xff0c;快速导入项目 打开IDEA&#xff0c;选择右侧Maven面板&#xff0c;点击 号&#xff0c;选中对应项目的pom.xml文件&#xff0c;双击即可 说明&#xff1a;如果没有Maven面板&#xff0c;选择 View > Appearance…

马拉松报名小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;赛事信息管理&#xff0c;赛事报名管理&#xff0c;活动商城管理&#xff0c;留言板管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;赛事信息&…

AI Agent框架(LLM Agent):LLM驱动的智能体如何引领行业变革,应用探索与未来展望

AI Agent框架&#xff08;LLM Agent&#xff09;&#xff1a;LLM驱动的智能实体如何引领行业变革&#xff0c;应用探索与未来展望 1. AI Agent&#xff08;LLM Agent&#xff09;介绍 1.1. 术语 Agent&#xff1a;“代理” 通常是指有意行动的表现。在哲学领域&#xff0c;Ag…

通证经济重塑经济格局

在数字化转型的全球浪潮中&#xff0c;通证经济模式犹如一股新兴力量&#xff0c;以其独特的价值传递与共享机制&#xff0c;重塑着经济格局&#xff0c;引领我们步入数字经济的新纪元。 通证&#xff0c;作为这一模式的核心&#xff0c;不仅是权利与权益的数字化凭证&#xf…

ETCD 基本介绍与常见命令的使用

转载请标明出处&#xff1a;https://blog.csdn.net/donkor_/article/details/140171610 文章目录 一、基本介绍1.1 参考1.2 什么是ETCD1.3 ETCD的特点1.4 ETCD的主要功能1.5 ETCD的整体架构1.6 什么时候用ETCD&#xff0c;什么时候用redis 二、安装三、使用3.1 etcdctl3.2 常用…

【动态规划】动态规划一

动态规划一 1.第 N 个泰波那契数2.面试题 08.01. 三步问题3.使用最小花费爬楼梯4.解码方法 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.…

快手矩阵管理系统:开启短视频营销的智能时代

在短视频内容营销的浪潮中&#xff0c;快手矩阵管理系统以其独特的优势和功能&#xff0c;成为品牌和个人创作者不可或缺的工具。本文将详细解析快手矩阵管理系统的核心功能&#xff0c;探讨它如何帮助用户高效管理多平台、多账号的内容发布和互动。 快手矩阵管理系统概述 快…

14. Revit API: Selection(选择器)

前言 这篇写选择器&#xff0c;经过前面好些篇的讲解&#xff0c;总算把前置内容都写完了。 我们来回忆下都在哪里提到过… 算了&#xff0c;直接进入正文。 一、Selection 命名空间 选择器位于Autodesk.Revit.UI.Selection命名空间下&#xff0c;关系到交互嘛&#xff0c;所…

PostMan Error:Maximum response size reached

一、问题描述 用postman本地测试&#xff0c;restful api接口导出文件&#xff0c;文件大小为190M&#xff0c;服务没问题&#xff0c;总是在导出时&#xff0c;抛出&#xff1a;Error:Maximum response size reached。开始以为是服务相应文件过大或者相应时间超时导致的。其实…

数字流的秩

题目链接 数字流的秩 题目描述 注意点 x < 50000 解答思路 可以使用二叉搜索树存储出现的次数以及数字的出现次数&#xff0c;方便后续统计数字x的秩关键在于构建树的过程&#xff0c;如果树中已经有值为x的节点&#xff0c;需要将该节点对应的数字出现次数加1&#xf…

14-8 小型语言模型的兴起

过去几年&#xff0c;我们看到人工智能能力呈爆炸式增长&#xff0c;其中很大一部分是由大型语言模型 (LLM) 的进步推动的。GPT-3 等模型包含 1750 亿个参数&#xff0c;已经展示了生成类似人类的文本、回答问题、总结文档等能力。然而&#xff0c;虽然 LLM 的能力令人印象深刻…

Redis深度解析:核心数据类型与键操作全攻略

文章目录 前言redis数据类型string1. 设置单个字符串数据2.设置多个字符串类型的数据3.字符串拼接值4.根据键获取字符串的值5.根据多个键获取多个值6.自增自减7.获取字符串的长度8.比特流操作key操作a.查找键b.设置键值的过期时间c.查看键的有效期d.设置key的有效期e.判断键是否…

免杀笔记 ---> PE

本来是想先把Shellcode Loader给更新了的&#xff0c;但是涉及到一些PE相关的知识&#xff0c;所以就先把PE给更了&#xff0c;后面再把Shellcode Loader 给补上。 声明&#xff1a;本文章内容来自于B站小甲鱼 1.PE的结构 首先我们要讲一个PE文件&#xff0c;就得知道它的结构…

Appium+python自动化(四十二)- 寿终正寝完结篇 - 结尾有惊喜,过时不候(超详解)

1.简介 按照上一篇的计划&#xff0c;今天给小伙伴们分享执行测试用例&#xff0c;生成测试报告&#xff0c;以及自动化平台。今天这篇分享讲解完。Appium自动化测试框架就要告一段落了。 2.执行测试用例&报告生成 测试报告&#xff0c;宏哥已经讲解了testng、HTMLTestRun…

springboot整合Camunda实现业务

1.bean实现 业务 1.画流程图 系统任务&#xff0c;实现方式 2.定义bean package com.jmj.camunda7test.process.config;import lombok.extern.slf4j.Slf4j; import org.camunda.bpm.engine.TaskService; import org.camunda.bpm.engine.delegate.JavaDelegate; import org.…

开源大模型和闭源大模型,打法有何区别?

现阶段&#xff0c;各个公司都有自己的大模型产品&#xff0c;有的甚至不止一个。除了小部分开源外&#xff0c;大部分都选择了闭源。那么&#xff0c;头部开源模型厂商选择开源是出于怎样的初衷和考虑&#xff1f;未来大模型将如何发展&#xff1f;我们来看看本文的分享。 在对…

Hi3861 OpenHarmony嵌入式应用入门--SNTP

sntp&#xff08;Simple Network Time Protocol&#xff09;是一种网络时间协议&#xff0c;它是NTP&#xff08;Network Time Protocol&#xff09;的一个简化版本。 本项目是从LwIP中抽取的SNTP代码&#xff1b; Hi3861 SDK中已经包含了一份预编译的lwip&#xff0c;但没有…

基于布雷格曼偏差校正技术的全变分一维时间序列信号降噪方法(MATLAB R2018A)

信号降噪是信号处理的重要步骤之一&#xff0c;目的是提高所获得信号数据的质量&#xff0c;以达到更高的定性和定量分析精度。信号降噪能提升信号处理其他环节的性能和人们对信息识别的准确率&#xff0c;给信号处理工作提供更可靠的保证。信号降噪的难点是降低噪声的同时也会…