一.前往deepseek官网申请api key
二.java端接入
首先展示项目结构
1.pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelversion>4.0.0</modelversion> <groupid>com.chat</groupid> <artifactid>chat_demo</artifactid> <version>0.0.1-snapshot</version> <name>chat_demo</name> <description>chat_demo</description> <properties> <java.version>1.8</java.version> <project.build.sourceencoding>utf-8</project.build.sourceencoding> <project.reporting.outputencoding>utf-8</project.reporting.outputencoding> <spring-boot.version>2.6.13</spring-boot.version> </properties> <dependencies> <!-- 添加thymeleaf依赖 --> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>cn.hutool</groupid> <artifactid>hutool-all</artifactid> <version>5.8.35</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> <version>1.18.30</version> <scope>provided</scope> </dependency> <!-- http客户端 --> <dependency> <groupid>com.squareup.okhttp3</groupid> <artifactid>okhttp</artifactid> <version>4.9.0</version> </dependency> <dependency> <groupid>com.mashape.unirest</groupid> <artifactid>unirest-java</artifactid> <version>1.4.9</version> </dependency> <dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpclient</artifactid> <version>4.3.6</version> </dependency> <dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpasyncclient</artifactid> <version>4.0.2</version> </dependency> <dependency> <groupid>org.apache.httpcomponents</groupid> <artifactid>httpmime</artifactid> <version>4.3.6</version> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> <scope>provided</scope> </dependency> </dependencies> <dependencymanagement> <dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-dependencies</artifactid> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencymanagement> <build> <plugins> <plugin> <groupid>org.apache.maven.plugins</groupid> <artifactid>maven-compiler-plugin</artifactid> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>utf-8</encoding> </configuration> </plugin> <plugin> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-maven-plugin</artifactid> <version>${spring-boot.version}</version> <configuration> <mainclass>com.chat.chat_demo.chatdemoapplication</mainclass> <skip>true</skip> </configuration> </plugin> </plugins> </build> </project>
2.springboot配置文件 application.yaml
ai: config: deepseek: apikey: 填写官网申请的key baseurl: https://api.deepseek.com/chat/completions server: port: 8080 spring: thymeleaf: prefix: classpath:/templates/ suffix: .html
3.编写controller接口
package com.chat.chat_demo.controller; import com.fasterxml.jackson.databind.jsonnode; import com.fasterxml.jackson.databind.objectmapper; import lombok.extern.slf4j.slf4j; import org.apache.http.client.methods.closeablehttpresponse; import org.apache.http.client.methods.httppost; import org.apache.http.entity.stringentity; import org.apache.http.impl.client.closeablehttpclient; import org.apache.http.impl.client.httpclients; import org.springframework.beans.factory.annotation.value; import org.springframework.http.mediatype; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.modelandview; import org.springframework.web.servlet.mvc.method.annotation.sseemitter; import java.io.bufferedreader; import java.io.inputstreamreader; import java.nio.charset.standardcharsets; import java.util.*; import java.util.concurrent.concurrenthashmap; import java.util.concurrent.executorservice; import java.util.concurrent.executors; @restcontroller @requestmapping("deepseek") @slf4j public class openaicontroller { @value("${ai.config.deepseek.apikey}") private string api_key; @value("${ai.config.deepseek.baseurl}") private string api_url; // 用于保存每个用户的对话历史 //https://api.deepseek.com/chat/completions 此接口为无状态接口,需要上下文连贯对话需要将历史聊天记录一并发送至接口中 private final map<string, list<map<string, string>>> sessionhistory = new concurrenthashmap<>(); private final executorservice executorservice = executors.newcachedthreadpool(); private final objectmapper objectmapper = new objectmapper(); @getmapping() public modelandview chat(modelandview modelandview) { modelandview.setviewname("chat"); return modelandview; } @postmapping(value = "/chat", produces = mediatype.text_event_stream_value) public sseemitter chat( // @requestheader("authorization") string token, @requestbody string question) { // 假设 token 是用户的唯一标识 // string userid = token; // 或者从 token 中解析出用户 id string userid = "123"; // 或者从 token 中解析出用户 id sseemitter emitter = new sseemitter(-1l); executorservice.execute(() -> { try { log.info("流式回答开始, 问题: {}", question); // 获取当前用户的对话历史 list<map<string, string>> messages = sessionhistory.getordefault(userid, new arraylist<>()); // 添加用户的新问题到对话历史 map<string, string> usermessage = new hashmap<>(); usermessage.put("role", "user"); usermessage.put("content", question); map<string, string> systemmessage = new hashmap<>(); systemmessage.put("role", "system"); systemmessage.put("content", "聚城网络科技有限公司的物业管理助手"); messages.add(usermessage); messages.add(systemmessage); // 调用 deepseek api try (closeablehttpclient client = httpclients.createdefault()) { httppost request = new httppost(api_url); request.setheader("content-type", "application/json"); request.setheader("authorization", "bearer " + api_key); map<string, object> requestmap = new hashmap<>(); requestmap.put("model", "deepseek-chat"); // requestmap.put("model", "deepseek-reasoner"); requestmap.put("messages", messages); // 包含对话历史 requestmap.put("stream", true); string requestbody = objectmapper.writevalueasstring(requestmap); request.setentity(new stringentity(requestbody, standardcharsets.utf_8)); try (closeablehttpresponse response = client.execute(request); bufferedreader reader = new bufferedreader( new inputstreamreader(response.getentity().getcontent(), standardcharsets.utf_8))) { stringbuilder airesponse = new stringbuilder(); string line; while ((line = reader.readline()) != null) { if (line.startswith("data: ")) { system.err.println(line); string jsondata = line.substring(6); if ("[done]".equals(jsondata)) { break; } jsonnode node = objectmapper.readtree(jsondata); string content = node.path("choices") .path(0) .path("delta") .path("content") .astext(""); if (!content.isempty()) { emitter.send(content); airesponse.append(content); // 收集 ai 的回复 } } } // 将 ai 的回复添加到对话历史 map<string, string> aimessage = new hashmap<>(); aimessage.put("role", "assistant"); aimessage.put("content", airesponse.tostring()); messages.add(aimessage); // 更新会话状态 sessionhistory.put(userid, messages); log.info("流式回答结束, 问题: {}", question); emitter.complete(); } } catch (exception e) { log.error("处理 deepseek 请求时发生错误", e); emitter.completewitherror(e); } } catch (exception e) { log.error("处理 deepseek 请求时发生错误", e); emitter.completewitherror(e); } }); return emitter; } }
4.编写前端界面 chat.html
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>deepseek chat</title> <style> :root { --primary-color: #5b8cff; --user-bg: linear-gradient(135deg, #5b8cff 0%, #3d6ef7 100%); --bot-bg: linear-gradient(135deg, #f0f8ff 0%, #e6f3ff 100%); --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); } body { font-family: 'segoe ui', system-ui, -apple-system, sans-serif; margin: 0; padding: 20px; display: flex; justify-content: center; min-height: 100vh; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); } .chat-container { width: 100%; max-width: 800px; height: 90vh; background: rgba(255, 255, 255, 0.95); border-radius: 20px; box-shadow: var(--shadow); backdrop-filter: blur(10px); display: flex; flex-direction: column; overflow: hidden; } .chat-header { padding: 24px; background: var(--primary-color); color: white; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .chat-header h1 { margin: 0; font-size: 1.8rem; font-weight: 600; letter-spacing: -0.5px; } .chat-messages { flex: 1; padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; } .chat-message { max-width: 75%; padding: 16px 20px; border-radius: 20px; line-height: 1.5; animation: messageappear 0.3s ease-out; position: relative; word-break: break-word; } .chat-message.user { background: var(--user-bg); color: white; align-self: flex-end; border-bottom-right-radius: 4px; box-shadow: var(--shadow); } .chat-message.bot { background: var(--bot-bg); color: #2d3748; align-self: flex-start; border-bottom-left-radius: 4px; box-shadow: var(--shadow); } .chat-input { padding: 20px; background: rgba(255, 255, 255, 0.9); border-top: 1px solid rgba(0, 0, 0, 0.05); display: flex; gap: 12px; } .chat-input input { flex: 1; padding: 14px 20px; border: 2px solid rgba(0, 0, 0, 0.1); border-radius: 16px; font-size: 1rem; transition: all 0.2s ease; background: rgba(255, 255, 255, 0.8); } .chat-input input:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(91, 140, 255, 0.2); } .chat-input button { padding: 12px 24px; border: none; border-radius: 16px; background: var(--primary-color); color: white; font-size: 1rem; font-weight: 500; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; gap: 8px; } .chat-input button:hover { background: #406cff; transform: translatey(-1px); } .chat-input button:disabled { background: #c2d1ff; cursor: not-allowed; transform: none; } .chat-input button svg { width: 18px; height: 18px; fill: currentcolor; } @keyframes messageappear { from { opacity: 0; transform: translatey(10px); } to { opacity: 1; transform: translatey(0); } } .typing-indicator { display: inline-flex; gap: 6px; padding: 12px 20px; background: var(--bot-bg); border-radius: 20px; align-self: flex-start; } .typing-dot { width: 8px; height: 8px; background: rgba(0, 0, 0, 0.3); border-radius: 50%; animation: typing 1.4s infinite ease-in-out; } .typing-dot:nth-child(2) { animation-delay: 0.2s; } .typing-dot:nth-child(3) { animation-delay: 0.4s; } @keyframes typing { 0%, 100% { transform: translatey(0); } 50% { transform: translatey(-4px); } } @media (max-width: 640px) { body { padding: 10px; } .chat-container { height: 95vh; border-radius: 16px; } .chat-message { max-width: 85%; } } </style> </head> <body> <div class="chat-container"> <div class="chat-header"> <h1>聚城网络科技有限公司 deepseek chat</h1> </div> <div class="chat-messages" id="chatmessages"></div> <div class="chat-input"> <input type="text" id="questioninput" placeholder="输入消息..." onkeydown="handlekeydown(event)"> <button id="sendbutton" disabled> <svg viewbox="0 0 24 24"> <path d="m2.01 21l23 12 2.01 3 2 10l15 2-15 2z" /> </svg> 发送 </button> </div> </div> <script> const questioninput = document.getelementbyid('questioninput'); const sendbutton = document.getelementbyid('sendbutton'); const chatmessages = document.getelementbyid('chatmessages'); let isbotresponding = false; // 输入验证 questioninput.addeventlistener('input', () => { sendbutton.disabled = questioninput.value.trim().length === 0; }); // 回车发送 function handlekeydown(e) { if (e.key === 'enter' && !sendbutton.disabled && !isbotresponding) { sendbutton.click(); } } // 修改后的消息处理逻辑 let currentbotmessage = null; // 当前正在更新的ai消息 async function handlebotresponse(response) { const reader = response.body.getreader(); const decoder = new textdecoder(); let buffer = ''; currentbotmessage = displaymessage('bot', ''); try { while (true) { const { done, value } = await reader.read(); if (done) { // 处理最后剩余的数据 if (buffer) processline(buffer); break; } buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); // 保留未完成的行在缓冲区 buffer = lines.pop() || ''; lines.foreach(line => { if (line.startswith('data:')) { const data = line.replace(/^data:\s*/g, '').trim(); if (data === '[done]') return; currentbotmessage.textcontent += data; } }); chatmessages.scrolltop = chatmessages.scrollheight; } } finally { currentbotmessage = null; } } // 发送逻辑 sendbutton.addeventlistener('click', async () => { if (isbotresponding) return; const question = questioninput.value.trim(); if (!question) return; questioninput.value = ''; sendbutton.disabled = true; isbotresponding = true; // 显示用户消息(新消息始终出现在下方) displaymessage('user', question); try { // 显示加载状态 const typingindicator = createtypingindicator(); const response = await fetch('/deepseek/chat', { method: 'post', headers: { 'content-type': 'application/json', 'accept': 'text/event-stream' }, body: json.stringify({ question }), }); typingindicator.remove(); await handlebotresponse(response); // 处理流式响应 } catch (error) { displaymessage('bot', '暂时无法处理您的请求,请稍后再试'); } finally { isbotresponding = false; questioninput.focus(); } }); // 创建消息元素 function displaymessage(sender, content) { const messagediv = document.createelement('div'); messagediv.classname = `chat-message ${sender}`; messagediv.textcontent = content; chatmessages.appendchild(messagediv); chatmessages.scrolltop = chatmessages.scrollheight; return messagediv; } // 创建输入指示器 function createtypingindicator() { const container = document.createelement('div'); container.classname = 'typing-indicator'; container.innerhtml = ` <div class="typing-dot"></div> <div class="typing-dot"></div> <div class="typing-dot"></div> `; chatmessages.appendchild(container); chatmessages.scrolltop = chatmessages.scrollheight; return container; } // 更新 ai 消息 // let currentbotmessage = null; // function updatebotmessage(content) { // if (!currentbotmessage) { // currentbotmessage = displaymessage('bot', content); // } else { // currentbotmessage.textcontent = content; // } // chatmessages.scrolltop = chatmessages.scrollheight; // } </script> </body> </html>
三.测试
- 启动项目,访问 http://localhost:8080/deepseek 即可出现页面,开始对话即可
总结
到此这篇关于springboot快速接入deepseek api(带页面)的文章就介绍到这了,更多相关springboot快速接入deepseek api内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论