备注:本文章主要实现Java调用本地DeepSeek整合WebSocket

DeepSeek本地部署

只需要安装Ollama和下载模型

可自行查看之前文章:DeepSeek-r1模型本地部署整合java案例-CSDN博客

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket 整合 DeepSeek</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        #messages {
            width: 100%;
            height: 200px;
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
            overflow-y: scroll;
        }
        #messageInput {
            width: calc(100% - 110px);
            padding: 10px;
        }
        #sendButton {
            padding: 10px 20px;
        }
    </style>
</head>
<body>
<h1>WebSocket 整合 DeepSeek</h1>
<div>
    <textarea id="messages" readonly></textarea>
</div>
<div>
    <input type="text" id="messageInput" placeholder="请输入消息">
    <button id="sendButton">发送</button>
</div>

<script>
    /*注意自身websocket地址信息*/
    const wsUri = "ws://localhost:8888/websocket";
    const websocket = new WebSocket(wsUri);

    websocket.onopen = function(event) {
        console.log("Connected to WebSocket server.");
        document.getElementById("messages").value += "已连接到WebSocket服务器。\n";
    };

    websocket.onmessage = function(event) {
        const messages = document.getElementById("messages");
        messages.value += "DeepSeek: " + event.data + "\n";
        messages.scrollTop = messages.scrollHeight;
    };

    websocket.onclose = function(event) {
        console.log("Disconnected from WebSocket server.");
        document.getElementById("messages").value += "已断开与WebSocket服务器的连接。\n";
    };

    websocket.onerror = function(error) {
        console.error("WebSocket error: ", error);
        document.getElementById("messages").value += "WebSocket错误: " + error.message + "\n";
    };

    document.getElementById("sendButton").addEventListener("click", function() {
        const messageInput = document.getElementById("messageInput");
        const message = messageInput.value;
        if (message) {
            if (websocket.readyState === WebSocket.OPEN) {
                websocket.send(message);
                messageInput.value = "";
                const messages = document.getElementById("messages");
                messages.value += "我: " + message + "\n";
                messages.scrollTop = messages.scrollHeight;
            } else {
                document.getElementById("messages").value += "WebSocket连接未打开,无法发送消息。\n";
            }
        }
    });
</script>
</body>
</html>

Java代码

WebSocket配置

package com.example.deepseekwebsocket.config;

import com.example.deepseekwebsocket.websocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * @Author: 菜鸟Lm
 * @Version:1.0
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    private final WebSocketServer webSocketServer;

    @Autowired
    public WebSocketConfig(WebSocketServer webSocketServer) {
        this.webSocketServer = webSocketServer;
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketServer, "/websocket").setAllowedOrigins("*");
    }
}

WebSocket实现

package com.example.deepseekwebsocket.websocket;

import com.example.deepseekwebsocket.service.DeepSeekService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.Deque;
import java.util.LinkedList;


/**
 * @Description: WebSocketServer
 * @Author: 菜鸟Lm
 */
@Component
public class WebSocketServer extends TextWebSocketHandler {

    @Autowired
    private DeepSeekService deepSeekService;


    /**
     * 连接建立成功调用
     *
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("连接建立成功" + session.getId());
    }

    // 用于DeepSeek存储消息历史记录 TODO 非实际场景
    private final Deque<String> messageHistory = new LinkedList<>();

    /**
     * 接收消息调用
     *
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        // 获得客户端传来的消息
        String payload = (String) message.getPayload();
        String systemMessage = null;
        // 判断DeepSeek是否有记录
        if (messageHistory.size() > 0) {
             // 获得DeepSeek最后一条消息
             systemMessage = deepSeekService.onMessage(payload, messageHistory.getLast());
        } else {
             systemMessage = deepSeekService.onMessage(payload, "");
        }
        messageHistory.add(systemMessage);
        session.sendMessage(new TextMessage(systemMessage));
    }

    /**
     * socket 断开连接时
     *
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

    }


}

Java调用本地DeepSeek

package com.example.deepseekwebsocket.service.impl;

import com.example.deepseekwebsocket.service.DeepSeekService;
import com.example.deepseekwebsocket.vo.request.DeepSeekRequest;
import com.example.deepseekwebsocket.vo.response.DeepSeekResponse;
import com.google.gson.Gson;
import okhttp3.*;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: 菜鸟Lm
 * @Version:1.0
 */
@Service
public class DeepSeekServiceImpl implements DeepSeekService {
    @Override
    public String onMessage(String message, String systemMessage) throws IOException {
        /**
         * DeepSeeK本地调用地址
         * 1.怎么自动端口号 使用 ollama serve 命令可以看到控制台打印默认端口号:11434
         * 2.怎么自动调用的地址 打开俩个终端分别执行: ollama run deepseek-r1:7b (运行deepseek-r1:7b模型) ;ollama stop (停止ollma)。在运行了
         * 运行deepseek-r1:7b模型终端随便提问题后会报错: 调用http://127.0.0.1:11434/api/chat失败等信息 
         * 
         */
        String url = "http://127.0.0.1:11434/api/chat";

        /**
         * 创建DeepSeekRequest对象 属性注解均在实体类中
         * 官方案例文档地址:https://api-docs.deepseek.com/zh-cn/api/create-chat-completion
         */
        DeepSeekRequest requestObject = new DeepSeekRequest();
        List<DeepSeekRequest.Message> messages = new ArrayList<>();
        //用户消息
        messages.add(new DeepSeekRequest.Message(message, "user"));
        //上一条deepseek消息
        messages.add(new DeepSeekRequest.Message(systemMessage, "system"));
        requestObject.setMessages(messages);
        // 模型 根据自身情况调整
        requestObject.setModel("deepseek-r1:7b");
        requestObject.setFrequency_penalty(0);
        requestObject.setMax_tokens(2048);
        requestObject.setPresence_penalty(0);
        requestObject.setResponse_format(new DeepSeekRequest.ResponseFormat("text"));
        requestObject.setStop(null);
        requestObject.setStream(false);
        requestObject.setStream_options(null);
        requestObject.setTemperature(1);
        requestObject.setTop_p(1);
        requestObject.setTools(null);
        requestObject.setTool_choice("none");
        requestObject.setLogprobs(false);
        requestObject.setTop_logprobs(null);


        // 使用Gson将请求对象转换为JSON字符串
        Gson gson = new Gson();
        String jsonBody = gson.toJson(requestObject);

        // 创建OkHttpClient实例,并设置超时时间
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(300, java.util.concurrent.TimeUnit.SECONDS)
                .readTimeout(300, java.util.concurrent.TimeUnit.SECONDS)
                .writeTimeout(300, java.util.concurrent.TimeUnit.SECONDS)
                .build();
        // 设置请求体的媒体类型为JSON
        MediaType mediaType = MediaType.parse("application/json");
        // 创建请求体,包含JSON字符串
        RequestBody body = RequestBody.create(mediaType, jsonBody);
        // 创建HTTP POST请求
        Request request = new Request.Builder()
                .url(url)
                .method("POST", body)
                .addHeader("Content-Type", "application/json")
                .addHeader("Accept", "application/json")
                .build();
        // 发送请求并获取响应
        Response response = client.newCall(request).execute();
        // 将响应体内容转换为DeepSeekResponse对象
        DeepSeekResponse deepSeekResponse = gson.fromJson(response.body().string(), DeepSeekResponse.class);
        return deepSeekResponse.getMessage().getContent();
    }
}
DeepSeek传参
package com.example.deepseekwebsocket.vo.request;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * DeepSeek请求对象
 * @Author: 菜鸟Lm
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DeepSeekRequest {
    /**
     * 消息列表,包含对话中的消息对象
     */
    private List<Message> messages;

    /**
     * 模型名称,指定要使用的模型
     */
    private String model;

    /**
     * 频率惩罚,用于减少重复内容的概率
     * 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性
     */
    private double frequency_penalty;

    /**
     * 最大生成的令牌数
     * 介于 1 到 8192 间的整数,限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。
     * 如未指定 max_tokens参数,默认使用 4096
     */
    private int max_tokens;

    /**
     * 存在惩罚,用于增加新话题的概率
     */
    private double presence_penalty;

    /**
     * 响应格式,指定返回的响应格式
     */
    private ResponseFormat response_format;

    /**
     * 停止序列,指定生成文本时的停止条件
     */
    private Object stop;

    /**
     * 是否流式返回结果
     */
    private boolean stream;

    /**
     * 流式选项,指定流式返回的选项
     * stream 为 true 时,才可设置此参数
     */
    private Object stream_options;

    /**
     * 温度,控制生成文本的随机性
     * 介于 0 和 2 之间的数字,值越低,更加准确
     */
    private double temperature;

    /**
     * 核采样参数,控制生成文本的多样性
     */
    private double top_p;

    /**
     * 工具列表,指定可用的工具
     */
    private Object tools;

    /**
     * 工具选择,指定使用的工具
     */
    private String tool_choice;

    /**
     * 是否返回对数概率
     */
    private boolean logprobs;

    /**
     * 对数概率选项,指定返回的对数概率选项
     */
    private Object top_logprobs;

    /**
     * 消息对象,包含单个消息的内容和角色
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Message {
        /**
         * 消息内容
         */
        private String content;

        /**
         * 消息角色,例如 "system" 或 "user"
         */
        private String role;
    }

    /**
     * 响应格式对象,指定返回的响应格式类型
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class ResponseFormat {
        /**
         * 响应格式类型,例如 "text"
         */
        private String type;
    }
}
DeepSeek响应
package com.example.deepseekwebsocket.vo.response;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 响应对象,用于封装 API 响应数据。
 * @Author: 菜鸟Lm
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeepSeekResponse {

    /**
     * API 模型名称,标识使用的模型版本。
     */
    private String model;

    /**
     * 响应创建时间,格式为 ISO 8601 标准的时间戳。
     */
    private String createdAt;

    /**
     * 消息内容,包含角色和具体内容。
     */
    private Message message;

    /**
     * 完成原因,表示请求完成的具体原因。
     */
    private String doneReason;

    /**
     * 是否完成,表示请求是否成功完成。
     */
    private boolean done;

    /**
     * 总持续时间,单位为纳秒,表示整个请求处理的总时间。
     */
    private long totalDuration;

    /**
     * 加载持续时间,单位为纳秒,表示加载阶段的耗时。
     */
    private long loadDuration;

    /**
     * 提示评估次数,表示提示评估的次数。
     */
    private int promptEvalCount;

    /**
     * 提示评估持续时间,单位为纳秒,表示提示评估的总耗时。
     */
    private long promptEvalDuration;

    /**
     * 评估次数,表示总的评估次数。
     */
    private int evalCount;

    /**
     * 评估持续时间,单位为纳秒,表示总的评估耗时。
     */
    private long evalDuration;

    /**
     * 内部静态类,用于封装消息内容。
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Message {
        /**
         * 消息的角色,例如 "system" 或 "user"。
         */
        private String role;

        /**
         * 消息的具体内容。
         */
        private String content;
    }
}

完整代码Gitee地址

DeepSeekWebsocket: DeepSeek整合Websocket

Logo

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

更多推荐