文章目录
- 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
- 普通调用:大模型收到用户的请求后,会等待答案生成完毕才返回给用户
- 流式调用:需要在请求体中设置stream=true,然后大模型就会以流失传输的方式,一旦生成了部分答案就会马上返回给用户,跟大家在网页使用效果一样。
这里分享一下搭建代码的步骤以及过程中遇到的一些问题,所写代码比较粗糙,大家根据需求自行修改。
deepseek官方API接口文档
2、搭建步骤
2.1 项目结构
首先创建一个Maven项目,我的文件结构如下
- 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定义,文档里面有每个字段的解释,根据想实现的功能自行定义
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/Setterpublic 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/Setterpublic static class Choice {private Message message;private Delta delta;public Delta getDelta() {return delta;}public Message getMessage() {return message;}// Getter/Setterpublic static class Message {private String content;// Getter/Setterpublic String getContent() {return content;}}}
}
2.2.4 Delta
当我们定义了使用流失传输,deepseek响应体结构将会改变,所以需要定义一个新的类去接收
可以看看两种不同方式调用的返回结果:
-
stream=false:
-
stream=true:
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而不是messageif (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后才打印答案,所以考虑到用户体验还是流式调用更加方便