Spring集成DeepSeek方法2:使用Spring AI OpenAI模块
·
Spring集成DeepSeek方法2:使用Spring AI OpenAI模块
概述
本文介绍如何使用Spring AI的OpenAI模块来集成DeepSeek API。DeepSeek提供了与OpenAI兼容的API接口,因此我们可以直接使用Spring AI的spring-ai-openai模块,只需修改API基础URL即可。这种方法简化了开发,利用了Spring AI的成熟功能。
前置条件
- Java 17+
- Spring Boot 3.2+
- DeepSeek API密钥(从 https://platform.deepseek.com 获取)
- Maven或Gradle构建工具
项目依赖
Maven (pom.xml)
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI OpenAI Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0-M4</version>
</dependency>
<!-- Spring AI BOM (用于管理版本) -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Lombok (可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
Gradle (build.gradle)
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.0'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter:1.0.0-M4'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
配置
application.yml
spring:
ai:
openai:
# DeepSeek API基础URL
base-url: https://api.deepseek.com
# DeepSeek API密钥
api-key: ${DEEPSEEK_API_KEY:your-api-key-here}
# 默认模型
chat:
options:
model: deepseek-chat
temperature: 0.7
max-tokens: 4096
application.properties
# DeepSeek API配置
spring.ai.openai.base-url=https://api.deepseek.com
spring.ai.openai.api-key=${DEEPSEEK_API_KEY:your-api-key-here}
# 聊天模型配置
spring.ai.openai.chat.options.model=deepseek-chat
spring.ai.openai.chat.options.temperature=0.7
spring.ai.openai.chat.options.max-tokens=4096
基础使用
简单聊天示例
package com.example.deepseek.controller;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/chat")
public class SimpleChatController {
private final ChatClient chatClient;
@Autowired
public SimpleChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/simple")
public String simpleChat(@RequestParam String message) {
return chatClient.call(message);
}
@PostMapping("/prompt")
public ChatResponse chatWithPrompt(@RequestBody String message) {
return chatClient.call(new Prompt(new UserMessage(message)));
}
}
进阶使用
使用ChatModel API
package com.example.deepseek.service;
import org.springframework.ai.chat.ChatModel;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.messages.*;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class DeepSeekChatService {
private final ChatModel chatModel;
@Value("classpath:prompts/system-prompt.st")
private Resource systemPromptResource;
@Autowired
public DeepSeekChatService(ChatModel chatModel) {
this.chatModel = chatModel;
}
/**
* 简单聊天
*/
public String chat(String userMessage) {
return chatModel.call(userMessage);
}
/**
* 带系统提示的聊天
*/
public String chatWithSystemPrompt(String userMessage, String systemPrompt) {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
Message systemMessage = systemPromptTemplate.createMessage();
UserMessage userMessageObj = new UserMessage(userMessage);
Prompt prompt = new Prompt(List.of(systemMessage, userMessageObj));
return chatModel.call(prompt).getResult().getOutput().getContent();
}
/**
* 使用模板文件
*/
public String chatWithTemplate(String userMessage, Map<String, Object> model) {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPromptResource);
Message systemMessage = systemPromptTemplate.createMessage(model);
UserMessage userMessageObj = new UserMessage(userMessage);
Prompt prompt = new Prompt(List.of(systemMessage, userMessageObj));
return chatModel.call(prompt).getResult().getOutput().getContent();
}
/**
* 获取完整响应(包含元数据)
*/
public ChatResponse chatWithMetadata(String userMessage) {
Prompt prompt = new Prompt(new UserMessage(userMessage));
return chatModel.call(prompt);
}
/**
* 多轮对话
*/
public String multiTurnChat(List<Message> messages) {
Prompt prompt = new Prompt(messages);
return chatModel.call(prompt).getResult().getOutput().getContent();
}
}
流式响应
package com.example.deepseek.controller;
import org.springframework.ai.chat.ChatModel;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
@RestController
@RequestMapping("/api/stream")
public class StreamingChatController {
private final ChatModel chatModel;
@Autowired
public StreamingChatController(ChatModel chatModel) {
this.chatModel = chatModel;
}
/**
* 流式聊天 - SSE方式
*/
@GetMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter streamChat(@RequestParam String message) {
SseEmitter emitter = new SseEmitter(60000L);
Prompt prompt = new Prompt(new UserMessage(message));
chatModel.stream(prompt)
.subscribe(
chunk -> {
try {
String content = chunk.getResult().getOutput().getContent();
if (content != null && !content.isEmpty()) {
emitter.send(SseEmitter.event().data(content));
}
} catch (IOException e) {
emitter.completeWithError(e);
}
},
error -> emitter.completeWithError(error),
() -> emitter.complete()
);
return emitter;
}
}
自定义选项
package com.example.deepseek.service;
import org.springframework.ai.chat.ChatModel;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DeepSeekOptionsService {
private final ChatModel chatModel;
@Autowired
public DeepSeekOptionsService(ChatModel chatModel) {
this.chatModel = chatModel;
}
/**
* 使用自定义选项调用
*/
public String chatWithOptions(String message, Double temperature, Integer maxTokens) {
OpenAiChatOptions options = OpenAiChatOptions.builder()
.withTemperature(temperature)
.withMaxTokens(maxTokens)
.withModel("deepseek-chat")
.build();
Prompt prompt = new Prompt(new UserMessage(message), options);
return chatModel.call(prompt).getResult().getOutput().getContent();
}
/**
* 使用TopP选项
*/
public String chatWithTopP(String message, Double topP) {
OpenAiChatOptions options = OpenAiChatOptions.builder()
.withTopP(topP)
.withModel("deepseek-chat")
.build();
Prompt prompt = new Prompt(new UserMessage(message), options);
return chatModel.call(prompt).getResult().getOutput().getContent();
}
/**
* 使用频率惩罚和存在惩罚
*/
public String chatWithPenalties(String message, Double frequencyPenalty, Double presencePenalty) {
OpenAiChatOptions options = OpenAiChatOptions.builder()
.withFrequencyPenalty(frequencyPenalty)
.withPresencePenalty(presencePenalty)
.withModel("deepseek-chat")
.build();
Prompt prompt = new Prompt(new UserMessage(message), options);
return chatModel.call(prompt).getResult().getOutput().getContent();
}
}
函数调用(Function Calling)
定义函数
package com.example.deepseek.function;
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import java.util.function.Function;
@JsonClassDescription("获取当前天气信息")
public class WeatherFunction implements Function<WeatherFunction.Request, WeatherFunction.Response> {
@JsonInclude(JsonInclude.Include.NON_NULL)
public record Request(
@JsonProperty(required = true)
@JsonPropertyDescription("城市名称")
String city,
@JsonProperty
@JsonPropertyDescription("国家名称")
String country
) {}
public record Response(
String city,
String country,
Double temperature,
String condition,
String description
) {}
@Override
public Response apply(Request request) {
// 这里可以调用实际的天气API
// 示例返回模拟数据
return new Response(
request.city,
request.country != null ? request.country : "China",
25.5,
"Sunny",
"晴朗,气温适宜"
);
}
}
配置函数调用
package com.example.deepseek.service;
import com.example.deepseek.function.WeatherFunction;
import org.springframework.ai.chat.ChatModel;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;
import java.util.function.Function;
@Service
public class FunctionCallingService {
private final ChatModel chatModel;
@Autowired
public FunctionCallingService(ChatModel chatModel) {
this.chatModel = chatModel;
}
/**
* 使用函数调用
*/
public String chatWithFunction(String message) {
OpenAiChatOptions options = OpenAiChatOptions.builder()
.withFunction("currentWeather")
.withModel("deepseek-chat")
.build();
Prompt prompt = new Prompt(new UserMessage(message), options);
return chatModel.call(prompt).getResult().getOutput().getContent();
}
/**
* 配置函数Bean
*/
@Configuration
static class FunctionConfig {
@Bean
@Description("获取当前天气信息")
public Function<WeatherFunction.Request, WeatherFunction.Response> currentWeather() {
return new WeatherFunction();
}
}
}
提示词模板
创建提示词模板文件
src/main/resources/prompts/system-prompt.st
你是一位专业的{role}。
你的职责是:
{responsibilities}
请用{language}回答用户的问题。
使用提示词模板
package com.example.deepseek.service;
import org.springframework.ai.chat.ChatModel;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class PromptTemplateService {
private final ChatModel chatModel;
@Value("classpath:prompts/system-prompt.st")
private Resource systemPromptResource;
@Autowired
public PromptTemplateService(ChatModel chatModel) {
this.chatModel = chatModel;
}
/**
* 使用模板生成系统提示
*/
public String chatWithTemplate(String userMessage, Map<String, Object> variables) {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPromptResource);
var systemMessage = systemPromptTemplate.createMessage(variables);
Prompt prompt = new Prompt(List.of(systemMessage, new UserMessage(userMessage)));
return chatModel.call(prompt).getResult().getOutput().getContent();
}
/**
* 内联模板
*/
public String chatWithInlineTemplate(String userMessage) {
String template = """
你是一位Java专家。
请详细回答以下关于Java的问题:
问题:{question}
""";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(template);
var systemMessage = systemPromptTemplate.createMessage(Map.of("question", userMessage));
Prompt prompt = new Prompt(List.of(systemMessage, new UserMessage("请回答上述问题")));
return chatModel.call(prompt).getResult().getOutput().getContent();
}
}
对话记忆管理
使用ChatMemory
package com.example.deepseek.service;
import org.springframework.ai.chat.ChatModel;
import org.springframework.ai.chat.messages.*;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class ChatMemoryService {
private final ChatModel chatModel;
private final Map<String, List<Message>> conversationHistory = new ConcurrentHashMap<>();
@Autowired
public ChatMemoryService(ChatModel chatModel) {
this.chatModel = chatModel;
}
/**
* 创建新对话
*/
public String createConversation() {
String conversationId = UUID.randomUUID().toString();
conversationHistory.put(conversationId, new ArrayList<>());
return conversationId;
}
/**
* 发送消息并保持上下文
*/
public String chat(String conversationId, String userMessage) {
List<Message> messages = conversationHistory.get(conversationId);
if (messages == null) {
throw new IllegalArgumentException("Conversation not found: " + conversationId);
}
// 添加用户消息
messages.add(new UserMessage(userMessage));
// 调用模型
Prompt prompt = new Prompt(messages);
String response = chatModel.call(prompt).getResult().getOutput().getContent();
// 添加助手回复
messages.add(new AssistantMessage(response));
return response;
}
/**
* 清空对话历史
*/
public void clearConversation(String conversationId) {
conversationHistory.remove(conversationId);
}
/**
* 获取对话历史
*/
public List<Message> getConversationHistory(String conversationId) {
return conversationHistory.get(conversationId);
}
}
异常处理
package com.example.deepseek.exception;
import org.springframework.ai.chat.ChatMemory;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(WebClientResponseException.class)
public ResponseEntity<Map<String, Object>> handleWebClientException(WebClientResponseException ex) {
Map<String, Object> error = new HashMap<>();
error.put("timestamp", LocalDateTime.now());
error.put("status", ex.getStatusCode().value());
error.put("error", ex.getStatusText());
error.put("message", ex.getResponseBodyAsString());
return ResponseEntity.status(ex.getStatusCode()).body(error);
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Map<String, Object>> handleIllegalArgumentException(IllegalArgumentException ex) {
Map<String, Object> error = new HashMap<>();
error.put("timestamp", LocalDateTime.now());
error.put("status", HttpStatus.BAD_REQUEST.value());
error.put("error", "Bad Request");
error.put("message", ex.getMessage());
return ResponseEntity.badRequest().body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleGenericException(Exception ex) {
Map<String, Object> error = new HashMap<>();
error.put("timestamp", LocalDateTime.now());
error.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
error.put("error", "Internal Server Error");
error.put("message", ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
完整控制器示例
package com.example.deepseek.controller;
import com.example.deepseek.service.*;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.messages.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/deepseek")
public class DeepSeekController {
private final DeepSeekChatService chatService;
private final StreamingChatService streamingService;
private final FunctionCallingService functionService;
private final PromptTemplateService templateService;
private final ChatMemoryService memoryService;
@Autowired
public DeepSeekController(DeepSeekChatService chatService,
StreamingChatService streamingService,
FunctionCallingService functionService,
PromptTemplateService templateService,
ChatMemoryService memoryService) {
this.chatService = chatService;
this.streamingService = streamingService;
this.functionService = functionService;
this.templateService = templateService;
this.memoryService = memoryService;
}
// 基础聊天
@PostMapping("/chat")
public String chat(@RequestBody String message) {
return chatService.chat(message);
}
// 带系统提示的聊天
@PostMapping("/chat/system")
public String chatWithSystem(@RequestBody Map<String, String> request) {
return chatService.chatWithSystemPrompt(
request.get("message"),
request.get("systemPrompt")
);
}
// 获取完整响应
@PostMapping("/chat/full")
public ChatResponse chatFull(@RequestBody String message) {
return chatService.chatWithMetadata(message);
}
// 流式聊天
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter stream(@RequestParam String message) {
return streamingService.streamChat(message);
}
// 函数调用
@PostMapping("/function")
public String chatWithFunction(@RequestBody String message) {
return functionService.chatWithFunction(message);
}
// 模板聊天
@PostMapping("/template")
public String chatWithTemplate(@RequestBody Map<String, Object> request) {
return templateService.chatWithTemplate(
(String) request.get("message"),
(Map<String, Object>) request.get("variables")
);
}
// 创建对话
@PostMapping("/conversation")
public ResponseEntity<String> createConversation() {
String conversationId = memoryService.createConversation();
return ResponseEntity.ok(conversationId);
}
// 对话消息
@PostMapping("/conversation/{id}/message")
public String chatInConversation(@PathVariable String id, @RequestBody String message) {
return memoryService.chat(id, message);
}
// 获取对话历史
@GetMapping("/conversation/{id}/history")
public List<Message> getHistory(@PathVariable String id) {
return memoryService.getConversationHistory(id);
}
}
测试示例
package com.example.deepseek;
import com.example.deepseek.service.DeepSeekChatService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class DeepSeekServiceTest {
@Autowired
private DeepSeekChatService chatService;
@Test
void testSimpleChat() {
String response = chatService.chat("你好");
assertNotNull(response);
assertFalse(response.isEmpty());
}
@Test
void testChatWithSystemPrompt() {
String response = chatService.chatWithSystemPrompt(
"解释什么是量子计算",
"你是一位物理学教授,请用通俗易懂的语言"
);
assertNotNull(response);
assertTrue(response.length() > 50);
}
}
优点与缺点
优点
- 开发效率高:利用Spring AI的成熟功能,减少代码量
- 功能丰富:内置函数调用、流式输出、提示词模板等功能
- 易于维护:Spring AI团队负责维护API兼容性
- 社区支持:活跃的社区和完善的文档
- 可扩展性:易于与其他Spring AI模块集成
缺点
- 依赖版本:需要关注Spring AI的版本兼容性
- 定制限制:某些深度定制可能受限
- 学习曲线:需要学习Spring AI的API和概念
- 性能开销:抽象层可能带来一定的性能开销
适用场景
- 快速开发原型和MVP
- 需要使用Spring AI高级功能(如函数调用、RAG等)
- 团队熟悉Spring生态系统
- 需要与Spring AI其他模块集成
- 对开发效率要求高于极致性能
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)