📖 目录


一、为什么选择 DeepSeek?

┌─────────────────────────────────────────────────────┐
│              主流 AI 大模型对比(2026)                 │
├──────────┬──────────┬──────────┬─────────────────────┤
│   模型    │  价格     │  中文能力  │     特点             │
├──────────┼──────────┼──────────┤─────────────────────┤
│ DeepSeek │  ¥1/百万token │ ⭐⭐⭐⭐⭐ │ 性价比之王,Java友好  │
│ GPT-4o   │  ¥30/百万token│ ⭐⭐⭐⭐ │ 生态最好            │
│ Claude   │  ¥24/百万token│ ⭐⭐⭐⭐ │ 代码能力强           │
│ 文心一言  │  ¥12/百万token│ ⭐⭐⭐⭐ │ 国内合规             │
└──────────┴──────────┴──────────┴─────────────────────┘

DeepSeek 对 Java 开发者的优势:

  • API 兼容 OpenAI 格式,直接用 HTTP 调用,无需额外 SDK
  • 中文能力强,适合国内业务场景
  • 价格低,开发和测试成本几乎可以忽略
  • 响应快,普通对话 1-3 秒

二、准备工作

2.1 获取 API Key

  1. 访问 DeepSeek 开放平台
  2. 注册/登录账号
  3. 进入「API Keys」页面,创建新的 Key
  4. 复制保存(只显示一次!)

2.2 技术栈

Spring Boot 3.2.x + Java 17 + OkHttp + Jackson

不引入额外的 AI SDK,纯 HTTP 调用,方便理解和定制。


三、创建 Spring Boot 项目

3.1 项目结构

springboot-deepseek/
├── src/main/java/com/example/deepseek/
│   ├── DeepseekApplication.java        # 启动类
│   ├── config/
│   │   └── DeepseekConfig.java         # 配置类
│   ├── model/
│   │   ├── ChatRequest.java            # 请求模型
│   │   ├── ChatResponse.java           # 响应模型
│   │   └── ChatMessage.java            # 消息模型
│   ├── service/
│   │   └── DeepseekService.java        # AI 服务
│   └── controller/
│       └── AiController.java           # 接口层
├── src/main/resources/
│   └── application.yml                 # 配置文件
└── pom.xml

3.2 依赖配置

<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- OkHttp(HTTP 客户端,比 RestTemplate 更现代) -->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>

    <!-- Jackson(JSON 处理) -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

    <!-- Lombok(简化代码) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

3.3 配置文件

# application.yml
deepseek:
  api-key: ${DEEPSEEK_API_KEY:sk-your-api-key}
  base-url: https://api.deepseek.com
  model: deepseek-chat
  timeout: 30  # 超时时间(秒)

server:
  port: 8080

3.4 配置类

package com.example.deepseek.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "deepseek")
public class DeepseekConfig {
    /** API Key */
    private String apiKey;

    /** API 基础URL */
    private String baseUrl = "https://api.deepseek.com";

    /** 模型名称 */
    private String model = "deepseek-chat";

    /** 请求超时时间(秒) */
    private int timeout = 30;
}

3.5 数据模型

package com.example.deepseek.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * 对话消息
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatMessage {
    /** 角色:system(系统)、user(用户)、assistant(助手) */
    private String role;
    /** 消息内容 */
    private String content;
}

/**
 * 请求体
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatRequest {
    private String model;
    private List<ChatMessage> messages;
    /** 温度:0-2,越低越确定,越高越随机 */
    @Builder.Default
    private double temperature = 0.7;
    /** 最大生成 token 数 */
    @Builder.Default
    private int maxTokens = 1024;

    public static ChatRequest of(String model, List<ChatMessage> messages) {
        return ChatRequest.builder()
                .model(model)
                .messages(messages)
                .build();
    }
}

/**
 * 响应体
 */
@Data
public class ChatResponse {
    private String id;
    private String object;
    private long created;
    private List<Choice> choices;
    private Usage usage;

    @Data
    public static class Choice {
        private int index;
        private ChatMessage message;
        @JsonProperty("finish_reason")
        private String finishReason;
    }

    @Data
    public static class Usage {
        @JsonProperty("prompt_tokens")
        private int promptTokens;
        @JsonProperty("completion_tokens")
        private int completionTokens;
        @JsonProperty("total_tokens")
        private int totalTokens;
    }

    /** 提取回复内容 */
    public String getContent() {
        if (choices != null && !choices.isEmpty()) {
            return choices.get(0).getMessage().getContent();
        }
        return "";
    }
}

四、场景一:智能对话接口

4.1 Service 层

package com.example.deepseek.service;

import com.example.deepseek.config.DeepseekConfig;
import com.example.deepseek.model.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class DeepseekService {

    private final DeepseekConfig config;
    private final ObjectMapper objectMapper;

    /** OkHttp 客户端(单例复用) */
    private final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .build();

    /**
     * 普通对话(同步返回完整结果)
     */
    public String chat(String userMessage, String systemPrompt) {
        // 1. 构建消息列表
        ChatMessage systemMsg = ChatMessage.builder()
                .role("system")
                .content(systemPrompt)
                .build();
        ChatMessage userMsg = ChatMessage.builder()
                .role("user")
                .content(userMessage)
                .build();

        ChatRequest request = ChatRequest.of(config.getModel(), List.of(systemMsg, userMsg));

        // 2. 发送 HTTP 请求
        try {
            String jsonBody = objectMapper.writeValueAsString(request);

            Request httpRequest = new Request.Builder()
                    .url(config.getBaseUrl() + "/v1/chat/completions")
                    .addHeader("Authorization", "Bearer " + config.getApiKey())
                    .addHeader("Content-Type", "application/json")
                    .post(RequestBody.create(jsonBody, MediaType.parse("application/json")))
                    .build();

            try (Response response = httpClient.newCall(httpRequest).execute()) {
                String responseBody = response.body().string();

                if (!response.isSuccessful()) {
                    log.error("DeepSeek API 调用失败: status={}, body={}", response.code(), responseBody);
                    throw new RuntimeException("AI 服务暂不可用,请稍后重试");
                }

                // 3. 解析响应
                ChatResponse chatResponse = objectMapper.readValue(responseBody, ChatResponse.class);
                log.info("Token 使用: prompt={}, completion={}",
                        chatResponse.getUsage().getPromptTokens(),
                        chatResponse.getUsage().getCompletionTokens());

                return chatResponse.getContent();
            }
        } catch (IOException e) {
            log.error("调用 DeepSeek 异常", e);
            throw new RuntimeException("AI 服务异常: " + e.getMessage());
        }
    }
}

4.2 Controller 层

package com.example.deepseek.controller;

import com.example.deepseek.model.ChatMessage;
import com.example.deepseek.service.DeepseekService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@RequestMapping("/api/ai")
@RequiredArgsConstructor
@CrossOrigin(origins = "*")
public class AiController {

    private final DeepseekService deepseekService;

    /**
     * 场景一:智能对话
     * POST /api/ai/chat
     */
    @PostMapping("/chat")
    public Map<String, String> chat(@RequestBody Map<String, String> request) {
        String userMessage = request.get("message");
        if (userMessage == null || userMessage.isBlank()) {
            return Map.of("error", "消息不能为空");
        }

        String systemPrompt = "你是一个有帮助的AI助手,用简洁的中文回答问题。";

        String reply = deepseekService.chat(userMessage, systemPrompt);
        return Map.of("reply", reply);
    }
}

4.3 测试

curl -X POST http://localhost:8080/api/ai/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "用Java怎么实现单例模式?"}'

# 响应
{"reply": "Java实现单例模式有以下几种方式:\n\n1. 饿汉式(线程安全,推荐)..."}

五、场景二:文本翻译接口

// 在 AiController 中添加

/**
 * 场景二:文本翻译
 * POST /api/ai/translate
 */
@PostMapping("/translate")
public Map<String, String> translate(@RequestBody Map<String, String> request) {
    String text = request.get("text");
    String targetLang = request.getOrDefault("targetLang", "英文");

    if (text == null || text.isBlank()) {
        return Map.of("error", "文本不能为空");
    }

    String systemPrompt = String.format(
            "你是一个专业翻译。将用户输入的文本翻译为%s。"
            + "只输出翻译结果,不要加任何解释。", targetLang
    );

    String translation = deepseekService.chat(text, systemPrompt);
    return Map.of(
            "original", text,
            "translation", translation,
            "targetLang", targetLang
    );
}
# 测试翻译
curl -X POST http://localhost:8080/api/ai/translate \
  -H "Content-Type: application/json" \
  -d '{"text": "人工智能正在改变世界", "targetLang": "英文"}'

# 响应
{"original":"人工智能正在改变世界","translation":"Artificial Intelligence is changing the world.","targetLang":"英文"}

六、场景三:文章摘要接口

// 在 AiController 中添加

/**
 * 场景三:文章摘要
 * POST /api/ai/summarize
 */
@PostMapping("/summarize")
public Map<String, String> summarize(@RequestBody Map<String, String> request) {
    String article = request.get("article");
    String style = request.getOrDefault("style", "简洁");

    if (article == null || article.isBlank()) {
        return Map.of("error", "文章内容不能为空");
    }

    String systemPrompt = String.format(
            "你是一个文章摘要助手。请用%s的风格,对用户提供的文章生成摘要。"
            + "摘要应包含:核心观点、关键数据和结论。"
            + "控制在200字以内。", style
    );

    String summary = deepseekService.chat(article, systemPrompt);
    return Map.of(
            "summary", summary,
            "style", style,
            "originalLength", String.valueOf(article.length())
    );
}

七、流式输出(SSE)实现

对于长文本生成(如写文章、写代码),等待完整结果太慢了。SSE(Server-Sent Events)可以实现逐字输出,用户体验好得多。

7.1 Service 层添加流式方法

// 在 DeepseekService 中添加

/**
 * 流式对话(SSE,逐 token 返回)
 */
public void chatStream(String userMessage, String systemPrompt,
                        Consumer<String> onToken) {
    ChatMessage systemMsg = ChatMessage.builder()
            .role("system").content(systemPrompt).build();
    ChatMessage userMsg = ChatMessage.builder()
            .role("user").content(userMessage).build();

    ChatRequest request = ChatRequest.of(config.getModel(), List.of(systemMsg, userMsg));

    try {
        String jsonBody = objectMapper.writeValueAsString(request);

        // 流式请求需要 stream=true
        var requestNode = objectMapper.readTree(jsonBody);
        ((com.fasterxml.jackson.databind.node.ObjectNode) requestNode).put("stream", true);

        Request httpRequest = new Request.Builder()
                .url(config.getBaseUrl() + "/v1/chat/completions")
                .addHeader("Authorization", "Bearer " + config.getApiKey())
                .addHeader("Content-Type", "application/json")
                .post(RequestBody.create(
                        objectMapper.writeValueAsString(requestNode),
                        MediaType.parse("application/json")))
                .build();

        httpClient.newCall(httpRequest).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                log.error("流式请求失败", e);
                onToken.accept("[错误: " + e.getMessage() + "]");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try (ResponseBody body = response.body()) {
                    if (!response.isSuccessful()) {
                        onToken.accept("[错误: HTTP " + response.code() + "]");
                        return;
                    }

                    // 逐行读取 SSE 数据
                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(body.byteStream()));

                    String line;
                    while ((line = reader.readLine()) != null) {
                        if (line.startsWith("data: ")) {
                            String data = line.substring(6).trim();

                            // 流结束标记
                            if ("[DONE]".equals(data)) break;

                            // 解析每个 token
                            var node = objectMapper.readTree(data);
                            String content = node.at("/choices/0/delta/content").asText("");
                            if (!content.isEmpty()) {
                                onToken.accept(content);
                            }
                        }
                    }
                }
            }
        });

    } catch (IOException e) {
        log.error("构建流式请求失败", e);
        onToken.accept("[错误: " + e.getMessage() + "]");
    }
}

7.2 Controller 层添加 SSE 接口

// 在 AiController 中添加
import org.springframework.http.MediaType;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

/**
 * 流式对话接口(SSE)
 * GET /api/ai/chat/stream?message=你好
 */
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter chatStream(@RequestParam String message) {

    SseEmitter emitter = new SseEmitter(60_000L); // 60秒超时

    deepseekService.chatStream(message, "你是一个有帮助的AI助手。", token -> {
        try {
            emitter.send(SseEmitter.event().data(token));
        } catch (IOException e) {
            emitter.completeWithError(e);
        }
    });

    emitter.onCompletion(() -> log.info("SSE 连接关闭"));
    emitter.onTimeout(() -> log.warn("SSE 连接超时"));

    return emitter;
}

八、异常处理与超时策略

8.1 全局异常处理

package com.example.deepseek.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /** 处理所有未捕获异常 */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, String>> handleException(Exception e) {
        log.error("系统异常", e);
        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Map.of("error", "系统异常,请稍后重试"));
    }

    /** 处理超时异常 */
    @ExceptionHandler(java.net.SocketTimeoutException.class)
    public ResponseEntity<Map<String, String>> handleTimeout(Exception e) {
        log.warn("请求超时: {}", e.getMessage());
        return ResponseEntity
                .status(HttpStatus.REQUEST_TIMEOUT)
                .body(Map.of("error", "AI 响应超时,请稍后重试"));
    }

    /** 处理参数异常 */
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<Map<String, String>> handleIllegalArgument(Exception e) {
        return ResponseEntity
                .status(HttpStatus.BAD_REQUEST)
                .body(Map.of("error", e.getMessage()));
    }
}

8.2 超时策略总结

┌─────────────────────────────────────────────────────┐
│              三层超时防护                              │
├──────────────┬──────────┬───────────────────────────┤
│     层级      │   超时时间  │         说明              │
├──────────────┼──────────┼───────────────────────────┤
│ OkHttp 连接   │   10秒    │ 建立TCP连接超时            │
│ OkHttp 读取   │   30秒    │ 等待API响应超时            │
│ SSE Emitter   │   60秒    │ 流式连接保活超时           │
└──────────────┴──────────┴───────────────────────────┘

九、总结与扩展

9.1 完整接口一览

接口 方法 路径 用途
智能对话 POST /api/ai/chat 通用对话
文本翻译 POST /api/ai/translate 多语言翻译
文章摘要 POST /api/ai/summarize 文本摘要
流式对话 GET /api/ai/chat/stream SSE逐字输出

9.2 扩展方向

  1. 接入数据库:存储对话历史,实现多轮对话
  2. 接入缓存:相同问题直接返回缓存结果,减少 API 调用
  3. 接入消息队列:异步处理高并发请求
  4. 接入 RAG:结合向量数据库,让 AI 回答业务相关问题
  5. 接入 Function Calling:让 AI 调用你的业务接口

🎉 总结:整个项目核心代码不到 400 行,5 分钟跑通。DeepSeek 的 OpenAI 兼容格式让 Java 接入变得极其简单,不需要学习任何新框架。

如果觉得有帮助,点赞 + 收藏支持一下!有问题欢迎评论区讨论 💬

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐