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
- Windows:
C:\ProgramData\Ollama\models
Windos(我用的是这个)由于ollama是默认存储在C盘, 所以修改一下模型存储位置, 修改步骤如下:
-
在环境变量中新增
OLLAMA_MODELS
变量名, 注意了这个变量名一定不能变, 变量值指向你要存放的文件夹, 如下
-
重启电脑(不重启电脑不生效!), 然后本地下载的模型就自动迁移的到你配置的目录了!
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 日期格式进行转换