如何实现大模型 “边生成边显示“

今天要实现大模型 "边生成边显示" 的效果。并介绍如何Java接入DeepSeek接口。

Java 可以通过SSE(Server-Sent Events,服务器发送事件) 技术实现。SSE 是一种基于 HTTP 的单向通信协议,适合服务器持续向客户端推送数据流(如大模型的流式输出),非常符合 "边吐内容边显示" 的场景。

准备工作

进入deepseek官网 https://www.deepseek.com/

创建key,API key 仅在创建时可见可复制,请妥善保存。

创建一个Java springboot工程。

导入依赖

必要依赖

okhttp

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.9.3</version>
</dependency>

控制层

java 复制代码
@Slf4j
@RestController
@RequestMapping("/model/v2")
public class SSEv2Controller {

    // 线程池:用于异步处理大模型生成(避免阻塞主线程)
    private final ExecutorService executor = Executors.newCachedThreadPool();

    // DeepSeek客户端实例
    private final ModelSseClient modelSseClient = new ModelSseClient();


    /**
     * 处理前端的SSE请求,返回模型流式响应
     *
     * @param prompt 用户输入的提示词
     * @return SseEmitter(用于向前端推送流式数据)
     */
    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamResponse(@RequestParam String prompt) {
        // 创建SSE发射器(30分钟超时)
        SseEmitter emitter = new SseEmitter(30 * 60 * 1000L);

        // 异步调用DeepSeek模型(避免阻塞控制器线程)
        executor.submit(() -> {
            // 通过回调处理模型返回的流式数据
            modelSseClient.streamChat(prompt, new StreamCallback() {
                @Override
                public void onChunk(String chunk) {
                    try {
                        // 将模型返回的内容块推送给前端
                        emitter.send(SseEmitter.event().data(chunk));
                    } catch (Exception e) {
                        // 推送失败时关闭连接
                        emitter.completeWithError(e);
                    }
                }

                @Override
                public void onComplete() {
                    try {
                        // 模型响应结束,发送结束信号并关闭连接
                        emitter.send(SseEmitter.event().data("[DONE]"));
                        emitter.complete();
                    } catch (Exception e) {
                        emitter.completeWithError(e);
                    }
                }

                @Override
                public void onError(Throwable throwable) {
                    // 模型调用出错时,通知前端并关闭连接
                    emitter.completeWithError(throwable);
                }
            });
        });


        // 当连接关闭时清理资源
        emitter.onCompletion(() -> System.out.println("SSE连接已完成"));
        emitter.onError(e -> System.err.println("SSE连接错误: " + e.getMessage()));
        emitter.onTimeout(() -> emitter.completeWithError(new RuntimeException("SSE连接超时")));

        return emitter;
    }
}

模型流式数据回调接口

java 复制代码
// 模型流式数据回调接口
public interface StreamCallback {
    // 接收模型返回的内容块
    void onChunk(String chunk);
    // 流式响应结束
    void onComplete();
    // 发生错误时调用
    void onError(Throwable throwable);
}

模型客户端

java 复制代码
public class ModelSseClient {
    // DeepSeek API地址(请确认官方最新地址)
    private static final String DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions";
    // 替换为你的DeepSeek API Key
    private static final String API_KEY = "key";
    // 模型名称(根据DeepSeek官方文档填写)
    private static final String MODEL = "deepseek-chat";

    private OkHttpClient client;
    private ObjectMapper objectMapper;

    public ModelSseClient() {
        // 初始化OkHttp客户端(设置超时时间)
        this.client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS) // 流式响应需较长读取超时
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();
        // 初始化JSON解析器(用于构建请求体和解析响应)
        this.objectMapper = new ObjectMapper();
    }

    /**
     * 调用DeepSeek流式接口,通过回调返回实时内容
     *
     * @param prompt   用户输入的提示词
     * @param callback 回调接口(用于传递内容块、结束信号、错误信息)
     */
    public void streamChat(String prompt, StreamCallback callback) {
        try {
            // 构建请求体(使用Jackson生成JSON,避免字符串拼接导致的格式错误)
            // 1. 构建messages参数
            List<Message> messages = new ArrayList<>();
            messages.add(new Message("user", prompt));
            // 2. 构建完整请求对象
            ChatRequest request = new ChatRequest(MODEL, messages, true);
            // 3. 转换为JSON字符串
            String requestBody = objectMapper.writeValueAsString(request);

            // 构建请求
            Request httpRequest = new Request.Builder()
                    .url(DEEPSEEK_API_URL)
                    .addHeader("Content-Type", "application/json")
                    .addHeader("Authorization", "Bearer " + API_KEY)
                    .post(RequestBody.create(requestBody, MediaType.parse("application/json")))
                    .build();


            // 发送异步请求
            client.newCall(httpRequest).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    // 调用失败时通知回调
                    callback.onError(e);
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (!response.isSuccessful()) {
                        // 非200状态码时通知错误(如401认证失败、400参数错误)
                        callback.onError(new RuntimeException(
                                "DeepSeek请求失败: 状态码=" + response.code() + ", 信息=" + response.message()
                        ));
                        response.close();
                        return;
                    }

                    ResponseBody body = response.body();
                    if (body == null) {
                        callback.onError(new RuntimeException("DeepSeek响应体为空"));
                        response.close();
                        return;
                    }

                    try (BufferedSource source = body.source()) {
                        // 循环读取流式响应
                        while (!source.exhausted()) {
                            String line = source.readUtf8Line();
                            if (line == null) break;
                            if (line.trim().isEmpty()) continue;

                            // 解析SSE格式数据
                            if (line.startsWith("data: ")) {
                                String data = line.substring("data: ".length()).trim();
                                if (data.equals("[DONE]")) {
                                    // 流式结束,通知回调
                                    callback.onComplete();
                                    break;
                                }

                                // 解析JSON获取内容
                                JsonNode jsonNode = objectMapper.readTree(data);
                                String content = jsonNode.at("/choices/0/delta/content").asText(null);
                                if (content != null && !content.isEmpty()) {
                                    // 将内容块通过回调传递给SSE控制器
                                    callback.onChunk(content);
                                }
                            }
                        }
                    } finally {
                        response.close();
                    }
                }
            });

        } catch (Exception e) {
            // 构建请求体失败时通知错误
            callback.onError(e);
        }
    }
}

创建两个类,用来构建请求体

java 复制代码
class ChatRequest {
	private String model;
    private List<Message> messages;
    private boolean stream;
}

class Message { 
    private String role;
    private String content;
}
相关推荐
一介书生-0074 小时前
2025-10-27 Java AI学习路线
java·人工智能·学习
py有趣4 小时前
LeetCode算法学习之移除元素
java·数据结构·算法
旺仔小拳头..6 小时前
Maven相关
java·maven
要一起看日出6 小时前
数据结构---------红黑树
java·数据结构·红黑树
程序定小飞6 小时前
基于springboot的民宿在线预定平台开发与设计
java·开发语言·spring boot·后端·spring
FREE技术6 小时前
山区农产品售卖系统
java·spring boot
星光一影7 小时前
Java医院管理系统HIS源码带小程序和安装教程
java·开发语言·小程序
YA3337 小时前
java设计模式七、代理模式
java·设计模式·代理模式