文章目录

  • 1、背景
  • 2、搭建步骤
    • 2.1 项目结构
    • 2.2 相关代码
      • 2.2.1 ApiConfig
      • 2.2.2 ChatRequest
      • 2.2.3 ChatResponse
      • 2.2.4 Delta
      • 2.2.5 DeepSeekService
      • 2.2.6 config.properties
      • 2.2.7 Main
      • 2.2.8 pom.xml
  • 3、效果展示

1、背景

最近在关注大模型在测开方向的应用,许多博主都是基于python去开发项目,由于公司里面只使用java,所以学习了下如何使用java调用大模型(以deepseek为例)。

本文通过两种方式调用deepseek

  1. 普通调用:大模型收到用户的请求后,会等待答案生成完毕才返回给用户
  2. 流式调用:需要在请求体中设置stream=true,然后大模型就会以流失传输的方式,一旦生成了部分答案就会马上返回给用户,跟大家在网页使用效果一样。

这里分享一下搭建代码的步骤以及过程中遇到的一些问题,所写代码比较粗糙,大家根据需求自行修改。

deepseek官方API接口文档

2、搭建步骤

2.1 项目结构

首先创建一个Maven项目,我的文件结构如下

image-20250408125023629

  • ApiConfig:用于读取配置文件中调用大模型的Key
  • ChatRequest:调用大模型的请求体
  • ChatResponse:大模型回答问题的响应体
  • Delta:大模型以流式方式回答问题需要的类
  • DeepSeekService:调用大模型方法
  • config.properties:存储调用大模型的Key和Url

2.2 相关代码

2.2.1 ApiConfig

package org.example.config;

import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.fluent.Configurations;
import java.io.File;

public class ApiConfig {
    private static final String CONFIG_FILE = "config.properties";

    public static String getApiKey() {
        return getConfig().getString("deepseek.api.key");
    }

    public static String getApiEndpoint() {
        return getConfig().getString("deepseek.api.endpoint");
    }

    private static PropertiesConfiguration getConfig() {
        try {
            return new Configurations().properties(new File(
                    Thread.currentThread()
                            .getContextClassLoader()
                            .getResource(CONFIG_FILE).getPath()));
        } catch (Exception e) {
            throw new RuntimeException("加载配置文件失败", e);
        }
    }
}

2.2.2 ChatRequest

根据官方API定义,文档里面有每个字段的解释,根据想实现的功能自行定义

image-20250408130427102

package org.example.model;

import java.util.List;

public class ChatRequest {
    private String model;
    private List<Message> messages;
    private double temperature;
    private Boolean stream;

    public void setModel(String s) {
        model = s;
    }

    public void setTemperature(double t) {
        temperature = t;
    }

    public void setMessages(List<Message> m) {
        messages = m;
    }

    public void setStream(Boolean stream) {
        this.stream = stream;
    }

    public Boolean getStream() {
        return stream;
    }

    // 构造方法、Getter/Setter

    public static class Message {
        private String role;
        private String content;

        public void setRole(String user) {
            role = user;
        }

        public void setContent(String s) {
            content = s;
        }

        // 构造方法、Getter/Setter
    }
}

2.2.3 ChatResponse

同理官网有全部字段的解释,需要啥拿啥

package org.example.model;

import java.util.List;


public class ChatResponse {
    private List<Choice> choices;

    public List<Choice> getChoices() {
        return choices;
    }

    // Getter/Setter

    public static class Choice {
        private Message message;
        private Delta delta;

        public Delta getDelta() {
            return delta;
        }

        public Message getMessage() {
            return message;
        }

        // Getter/Setter

        public static class Message {
            private String content;

            // Getter/Setter
            public String getContent() {
                return content;
            }
        }
    }
}

2.2.4 Delta

当我们定义了使用流失传输,deepseek响应体结构将会改变,所以需要定义一个新的类去接收

可以看看两种不同方式调用的返回结果:

  1. stream=false:

    image-20250408130716567
  2. stream=true:

    image-20250408130610063
package org.example.model;

public class Delta {
    private String content;

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

2.2.5 DeepSeekService

两种调用方法:

  • 普通调用:chatCompletion
  • 流式调用:streamChatCompletion
package org.example.service;

import com.google.gson.*;
import org.example.config.ApiConfig;
import org.example.model.ChatRequest;
import org.example.model.ChatResponse;
import okhttp3.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;


public class DeepSeekService {
    private static final OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(300000, TimeUnit.SECONDS)  // 连接超时 xx 秒
            .readTimeout(300000, TimeUnit.SECONDS)     // 读取超时 xx 秒
            .writeTimeout(300000, TimeUnit.SECONDS)    // 写入超时 xx 秒
            .build();
    private static final Gson gson = new Gson();

    public String chatCompletion(ChatRequest request) throws Exception {
        String jsonRequest = gson.toJson(request);


        RequestBody body = RequestBody.create(
                jsonRequest,
                MediaType.parse("application/json")
        );



        Request httpRequest = new Request.Builder()
                .url(ApiConfig.getApiEndpoint())
                .addHeader("Authorization", "Bearer " + ApiConfig.getApiKey())
                .post(body)
                .build();
        try (Response response = client.newCall(httpRequest).execute()) {
            if (!response.isSuccessful()) {
                throw new RuntimeException("API请求失败: " + response.code());
            }


            ChatResponse resp = gson.fromJson(
                    response.body().string(),
                    ChatResponse.class
            );

            return resp.getChoices().get(0).getMessage().getContent();
        }
    }

    // 流式回调
    public void streamChatCompletion(ChatRequest request, StreamCallback callback) throws IOException {
        String jsonRequest = gson.toJson(request);
        RequestBody body = RequestBody.create(jsonRequest, MediaType.get("application/json"));

        Request httpRequest = new Request.Builder()
                .url(ApiConfig.getApiEndpoint())
                .addHeader("Authorization", "Bearer " + ApiConfig.getApiKey())
                .post(body)
                .build();

        try (Response response = client.newCall(httpRequest).execute()) {
            if (!response.isSuccessful()) {
                throw new RuntimeException("API请求失败: " + response.code());
            }

            // 使用try-with-resources确保流关闭
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(response.body().byteStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    if (line.startsWith("data: ")) {
                        String jsonData = line.substring(6).trim();
                        if ("[DONE]".equals(jsonData)) {
                            break;
                        }
					  // 流式处理
                        try {
                            // 解析流式数据块
                            ChatResponse chunk = gson.fromJson(jsonData, ChatResponse.class);
                            // 流式模式下应该检查delta而不是message
                            if (chunk != null &&
                                    chunk.getChoices() != null &&
                                    !chunk.getChoices().isEmpty() &&
                                    chunk.getChoices().get(0).getDelta() != null) {

                                String content = chunk.getChoices().get(0).getDelta().getContent();
                                if (content != null) {
                                    callback.onContentReceived(content);
                                }
                            }
                        } catch (Exception e) {
                            System.err.println("解析数据块出错: " + e.getMessage());
                        }
                    }
                }
            }
        }
    }

    // 流式回调接口
    public interface StreamCallback {
        void onContentReceived(String content);
    }
}

2.2.6 config.properties

deepseek.api.key=sk-xx
deepseek.api.endpoint=https://api.deepseek.com/chat/completions

2.2.7 Main

package org.example;

import org.example.model.ChatRequest;
import org.example.service.DeepSeekService;

import java.util.List;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        DeepSeekService service = new DeepSeekService();

        System.out.println("DeepSeek 对话系统 (输入 'bye' 退出)");
        System.out.println("=================================");

        while (true) {
            System.out.print("\n用户: ");
            String userInput = scanner.nextLine().trim();

            // 检查退出条件
            if ("bye".equalsIgnoreCase(userInput)) {
                System.out.println("对话结束,再见!");
                break;
            }

            // 跳过空输入
            if (userInput.isEmpty()) {
                System.out.println("请输入有效内容");
                continue;
            }

            try {
                // 构建请求
                ChatRequest request = new ChatRequest();
                request.setModel("deepseek-chat");
                request.setTemperature(0.7);
                request.setStream(true);

                ChatRequest.Message message = new ChatRequest.Message();
                message.setRole("user");
                message.setContent(userInput);
                request.setMessages(List.of(message));

                // 获取并显示响应

                // 记录开始时间
                long startTime = System.currentTimeMillis();
                System.out.println("\nDeepSeek 思考中...");

                if(request.getStream()) {
                    service.streamChatCompletion(request, newContent -> {  // 执行流式调用
                        printWithTypingEffect(newContent, 20);
                    });
                } else {
                    String response = service.chatCompletion(request); // 普通调用
                    //                System.out.println("\nDeepSeek: " + response); // 直接返回全部答案
                    printWithTypingEffect(response, 20); //打字机效果
                }

                // 记录结束时间
                long endTime = System.currentTimeMillis();
                long duration = endTime - startTime;
                System.out.printf("\n[本次响应耗时: %ds]\n", duration / 1000);

            } catch (Exception e) {
                System.err.println("\n请求出错: " + e.getMessage());
                e.printStackTrace();
            }
        }

        scanner.close();
    }

    private static void printWithTypingEffect(String text, int delay) {
        try {
            for (char c : text.toCharArray()) {
                System.out.print(c);
                System.out.flush(); // 确保立即输出

                // 处理换行符和制表符
                if (c == '\n') {
                    Thread.sleep(delay * 3); // 换行时多停顿一会
                } else if (c == '\t') {
                    Thread.sleep(delay * 2);
                } else {
                    Thread.sleep(delay);
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

2.2.8 pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- HTTP客户端 -->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.12.0</version>
        </dependency>

        <!-- JSON处理 -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.10.1</version>
        </dependency>

        <!-- 配置管理 -->
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.10</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.virtlink.commons/commons-configuration2-jackson -->
        <dependency>
            <groupId>com.virtlink.commons</groupId>
            <artifactId>commons-configuration2-jackson</artifactId>
            <version>1.3.5</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.4</version>
        </dependency>

    </dependencies>

</project>

3、效果展示

这里只展示流式调用,输入问题后程序可以及时把deepseek返回的部分内容展现给用户。整个问题耗时大约58S,如果没有启动流式传输,程序只有等待58S后才打印答案,所以考虑到用户体验还是流式调用更加方便

deepseek测试

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐