DeepSeek整合WebSocket
只需要安装Ollama和下载模型。
·
备注:本文章主要实现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地址
更多推荐
所有评论(0)