Compare commits

..

2 Commits

Author SHA1 Message Date
hanserwei
62cf0ed548 feat(ai): 切换默认模型并新增流式对话接口- 将默认模型从 deepseek-chat 切换为 deepseek-reasoner
- 新增 /ai/generateStream 接口支持流式对话输出
- 新增 /v1/ai/generateStream 接口支持 reasoning 内容流式输出
- 引入 commons-lang3 依赖用于字符串处理- 添加 Flux 支持实现异步流式响应
- 完善 DeepSeekAssistantMessage 处理逻辑,区分推理内容与最终回答
2025-10-21 14:04:49 +08:00
hanserwei
5158a9bcb3 feat(ai): 集成 DeepSeek AI 对话功能并配置加密支持
- 添加 DeepSeek Chat 模型依赖及 starter
- 配置 DeepSeek API Key 加密与基础 URL
- 新增 DeepSeekChatController 实现基本对话接口
- 引入 Jasypt 加密库用于敏感信息加密
- 提供 EncryptorUtil 工具类用于生成加密密钥
- 更新 pom.xml 引入 spring-ai 和 jasypt 依赖管理
2025-10-21 13:57:27 +08:00
5 changed files with 152 additions and 15 deletions

45
pom.xml
View File

@@ -6,28 +6,16 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hanserwei</groupId>
<artifactId>ai-robot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>han-ai-robot-springboot</name>
<description>han-ai-robot-springboot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>21</java.version>
<spring-ai.version>1.0.3</spring-ai.version>
<commons-lang3.version>3.19.0</commons-lang3.version>
</properties>
<dependencies>
<dependency>
@@ -35,12 +23,41 @@
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!-- Deepseek 模型 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</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>

View File

@@ -0,0 +1,46 @@
package com.hanserwei.airobot.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/ai")
public class DeepSeekChatController {
@Resource
private DeepSeekChatModel chatModel;
/**
* 普通对话
* @param message 对话输入内容
* @return 对话结果
*/
@GetMapping("/generate")
public String generate(@RequestParam(value = "message", defaultValue = "你是谁?") String message) {
// 一次性返回结果
return chatModel.call(message);
}
/**
* 流式对话
* @param message 对话输入内容
* @return 对话结果
*/
@GetMapping(value = "/generateStream", produces = "text/html;charset=utf-8")
public Flux<String> generateStream(@RequestParam(value = "message", defaultValue = "你是谁?") String message) {
// 构建提示词
Prompt prompt = new Prompt(new UserMessage(message));
// 流式输出
return chatModel.stream(prompt)
.mapNotNull(chatResponse -> chatResponse.getResult().getOutput().getText());
}
}

View File

@@ -0,0 +1,49 @@
package com.hanserwei.airobot.controller;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekAssistantMessage;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/v1/ai")
public class DeepSeekR1ChatController {
@Resource
private DeepSeekChatModel chatModel;
/**
* 流式对话
* @param message 对话输入内容
* @return 对话结果
*/
@GetMapping(value = "/generateStream", produces = "text/html;charset=utf-8")
public Flux<String> generateStream(@RequestParam(value = "message", defaultValue = "你是谁?") String message) {
// 构建提示词
Prompt prompt = new Prompt(new UserMessage(message));
// 流式输出
return chatModel.stream(prompt)
.mapNotNull(chatResponse -> {
// 获取响应内容
DeepSeekAssistantMessage deepSeekAssistantMessage = (DeepSeekAssistantMessage) chatResponse.getResult().getOutput();
// 推理内容
String reasoningContent = deepSeekAssistantMessage.getReasoningContent();
// 推理结束后的正式回答
String text = deepSeekAssistantMessage.getText();
// 若推理内容有值,则响应推理内容,否则,说明推理结束了,响应正式回答
return StringUtils.isNotBlank(reasoningContent) ? reasoningContent : text;
});
}
}

View File

@@ -0,0 +1,11 @@
package com.hanserwei.airobot.utils;
import org.jasypt.util.text.AES256TextEncryptor;
public class EncryptorUtil {
public static void main(String[] args) {
AES256TextEncryptor textEncryptor = new AES256TextEncryptor();
textEncryptor.setPassword("password");
System.out.println(textEncryptor.encrypt("sk-xxxxxxxxxxxxxx"));
}
}

View File

@@ -1,3 +1,17 @@
#file: noinspection SpringBootConfigYamlInspection
spring:
application:
name: han-ai-robot-springboot
name: han-ai-robot-springboot
ai:
deepseek:
api-key: ENC(MROXdiEHmWk08koE63bTzFqW52MaXLpMkM9Cyl40Ubj+Lw1yKeZuHLEcs6jTFY8ditY4gJ1365LMAY8Z9G1uwfYFYaYdb3NyijplX7GuDZA=) # 填写 DeepSeek Api Key, 改成你自己的
base-url: https://api.deepseek.com # DeepSeek 的请求 URL, 可不填,默认值为 api.deepseek.com
chat:
options:
model: deepseek-reasoner # 使用哪个模型
temperature: 0.8 # 温度值
jasypt:
encryptor:
password: ${jasypt.encryptor.password}
algorithm: PBEWithHMACSHA512AndAES_256
iv-generator-classname: org.jasypt.iv.RandomIvGenerator