基于deepseek的智能语音客服【第二讲】后端异步接口调用封装

本篇内容主要讲前端请求(不包含)访问后端服务接口,接口通过检索知识库,封装提示词,调用deepseek的,并返回给前端的全过程,非完整代码,不可直接运行。

1.基于servlet封装异步请求

为什么要进行异步分装?因为前段需要流式输出,以减少用户长时间等待造成的不良体验

集成HttpServlet 实现POST方法,get方式多伦对话有数据了限制

@WebServlet(urlPatterns = "/ds",asyncSupported = true // 启用异步支持
)
public class DeepseekApi extends HttpServlet 

2.设置跨域(如果没有前后端分离可以忽略此步骤)

		response.setHeader("Access-Control-Allow-Origin", "*");response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");response.setHeader("Access-Control-Max-Age", "3600");response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type");response.setHeader("Access-Control-Allow-Credentials", "true");// 设置SSE头response.setContentType("text/event-stream");response.setCharacterEncoding("UTF-8");response.setHeader("Cache-Control", "no-cache");response.setHeader("Connection", "keep-alive");// 处理OPTIONS预检请求if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {response.setStatus(HttpServletResponse.SC_OK);asyncContext.complete();return;}

3.获取参数

我这里前段直接封装好多轮对话参数和当前问题,当前问题为什么要分开,应为问题需要在知识库做增强检索,这样好取参数

 // 获取问题参数String question = request.getParameter("question");String his = request.getParameter("his");

4.封装异步任务

asyncContext

 // 获取异步上下文final AsyncContext asyncContext = request.startAsync();asyncContext.setTimeout(3*60*1000); // 超时 60 秒writer = response.getWriter();processRequest(asyncContext, writer, question,his);

5.设置异步事件监听

 asyncContext.addListener(new AsyncListener() {@Overridepublic void onComplete(AsyncEvent event) {// 确保资源释放}@Overridepublic void onTimeout(AsyncEvent event) {writer.write("event:error\ndata:请求超时\n\n");writer.flush();asyncContext.complete();}@Overridepublic void onError(AsyncEvent event) {asyncContext.complete();}@Overridepublic void onStartAsync(AsyncEvent event) {}});

6.检索向量库

   // 检索向量库List<Map<String, Object>> kl = KnowledgeBaseService.searchKnowledge(question, 40);

7.构建提示词

 private static String buildPrompt(String question, List<Map<String, Object>> knowledge) {System.out.println("提示词封装!");String txtString="";for (Map<String, Object> map : knowledge) {txtString+=map.get("title")+"\n"+map.get("text")+"\n";}return "问题:\n" + question + "\n\n参考知识:" +txtString+ "\n\n请以参考知识为主,给出简明扼要的回复,如果参考知识与问题没有相关性或不存在请拒绝答复:";}

8.构建请求体

  // 新的对话内容JSONObject newMessage = new JSONObject();newMessage.put("role", "user");newMessage.put("content", prompt);// 插入新的对话messages.add(newMessage);System.out.println(messages.toJSONString());// 构建请求体Map<String, String> headers = new HashMap<>();headers.put("Authorization", "Bearer " + Consist.DEEPSEEK_API_KEY);headers.put("Content-Type", "application/json");JSONObject requestBody = new JSONObject();requestBody.put("model", Consist.MODEL_NAME);requestBody.put("messages", messages);requestBody.put("stream", true);requestBody.put("max_tokens", Consist.MAX_TOKENS);

9.进行异步调用

sendAsyncRequestWithCallback(Consist.DEEPSEEK_API_URL,headers,requestBody.toJSONString(),new StreamCallback() {@Overridepublic void onDataReceived(String content) {
//                    	System.out.print(content);writer.write("data:" + content+"\n\n");writer.flush();}@Overridepublic void onComplete() {System.out.println("调用完成!");writer.write("event:done\ndata:\n\n");writer.flush();asyncContext.complete();}@Overridepublic void onError(Exception ex) {
//                    	ex.printStackTrace();System.err.println("报错!");writer.write("event:error\ndata:发生错误\n\n");writer.flush();asyncContext.complete();}});

10.监听调用状态,防止客户端掉线造成的异常

  if (asyncClient == null || !asyncClient.isRunning()) {synchronized (DeepseekR1WebApiPost.class) {if (asyncClient == null || !asyncClient.isRunning()) {try {if (asyncClient != null) {asyncClient.close();}asyncClient = HttpAsyncClients.createDefault();asyncClient.start();} catch (IOException e) {e.printStackTrace();}}}}

11.封装客户端参数,调用deepseek官网接口

 HttpPost request = new HttpPost(new URI(url));request.setEntity(new StringEntity(requestBody, "UTF-8"));headers.forEach(request::setHeader);HttpHost target = new HttpHost(new URI(url).getHost(),new URI(url).getPort(),new URI(url).getScheme());

12.执行异步请求,并处理返回数据

   @Overrideprotected void onContentReceived(ContentDecoder decoder, IOControl ioctrl) {try {ByteBuffer bb = ByteBuffer.allocate(Consist.MAX_TOKENS);int read;while ((read = decoder.read(bb)) > 0) {bb.flip();byte[] bytes = new byte[bb.remaining()];bb.get(bytes);buffer.write(bytes);processBuffer(callback);bb.clear();}} catch (Exception e) {e.printStackTrace();System.out.println("报错: " + e.getMessage().toString());callback.onError(e);}}

13.解析流式数据返回给前端

 private void processBuffer(StreamCallback callback) throws Exception {String chunk = buffer.toString("UTF-8");buffer.reset();// 按行分割并过滤空行String[] lines = chunk.split("\\r?\\n");for (String line : lines) {if (line.isEmpty()) continue;if (line.startsWith("data: ")) {String jsonStr = line.substring(6).trim();if ("[DONE]".equals(jsonStr)) {callback.onComplete();return; // 提前返回避免后续处理}try {if(isJsonComplete(jsonStr)) {JsonObject responseJson = JsonParser.parseString(jsonStr).getAsJsonObject();JsonArray choices = responseJson.getAsJsonArray("choices");callback.onDataReceived(choices.toString());};
//                             // callback.onDataReceived(jsonStr);} catch (Exception e) {callback.onError(new RuntimeException("解析 JSON 失败: " + jsonStr, e));continue;}}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/74123.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

归并排序的思路与实现

归并排序主要是两大模块 分治 和 合并 即将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有序&#xff0c;再使子序列段间有序。若将两个有序表合并成一个有序表&#xff0c;称为二路归并 由于使用了新的数组 那么空间复杂度就为O(n) 但这…

Word中公式自动标号带章节编号

&#xff08;1&#xff09;插入一行三列的表格&#xff0c;设置宽度分别为0.5&#xff0c;13.39和1.5&#xff0c;设置纵向居中&#xff0c;中间列居中对齐&#xff0c;最右侧列靠右对齐&#xff0c;设置段落如下 &#xff08;2&#xff09;插入域代码 【Word】利用域代码快速实…

阿里云服务器环境部署 四 MySQL主从配置

安装MySQL 导入mysql镜像 docker load -i /opt/dockerinstall/mysql/mysql-8.1.0.tar docker run --privilegedtrue --name mysql8 --restartunless-stopped -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -v /usr/local/mysql/logs:/var/log/mysql -v /usr/local/mysql/d…

[RH342]iscsi配置与排错

[RH342]iscsi配置与排错 1. 服务端配置1.1 安装targetcli1.2 准备磁盘1.3 服务端配置1.4 防火墙配置 2. 客户端配置2.1 安装客户端软件2.2 配置客户端2.3 连接登录服务端2.4 挂载使用 3. 安全验证扩展3.1 服务端3.2 客户端 4. 常见的排错点4.1 服务端常见错误4.2 客户端常见错误…

服装零售行业数字化时代的业务与IT转型规划P111(111页PPT)(文末有下载方式)

服装零售行业数字化时代的业务与IT转型规划P111 详细资料请看本解读文章的最后内容。 随着数字化技术的迅猛发展&#xff0c;服装零售行业正经历着前所未有的变革。本文将对《服装零售行业数字化时代的业务与IT转型规划P111》进行详细解读&#xff0c;探讨未来几年内该行业的…

基于javaweb的SSM+Maven宠物领养宠物商城流浪动物管理系统与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

PostgreSQL 数据库中导入大量数据

在 PostgreSQL 数据库中导入大量数据,可根据数据来源和格式选择不同的方法。以下为你详细介绍几种常见的方式: 1. 使用 COPY 命令(适用于本地数据文件) COPY 命令是 PostgreSQL 内置的高效数据导入工具,适合处理本地的数据文件。 步骤 准备数据文件 确保你的数据文件格…

C++语法之命名空间二

A.h头文件中代码&#xff1a; namespace a {void 输出(); }; A.cpp源文件中代码&#xff1a; #include <iostream> #include "A.h" void a::输出() {std::cout << "A.h里的输出函数" << std::endl; } B.h头文件中代码&#xff1a; …

基于FPGA的DDS连续FFT 仿真验证

基于FPGA的 DDS连续FFT 仿真验证 1 摘要 本文聚焦 AMD LogiCORE IP Fast Fourier Transform (FFT) 核心,深入剖析其在 FPGA 设计中的应用。该 FFT 核心基于 Cooley - Tukey 算法,具备丰富特性,如支持多种数据精度、算术类型及灵活的运行时配置。文中详细介绍了其架构选项、…

美团Leaf分布式ID生成器使用教程:号段模式与Snowflake模式详解

引言 在分布式系统中&#xff0c;生成全局唯一ID是核心需求之一。美团开源的Leaf提供了两种分布式ID生成方案&#xff1a;号段模式&#xff08;高可用、依赖数据库&#xff09;和Snowflake模式&#xff08;高性能、去中心化&#xff09;。本文将手把手教你如何配置和使用这两种…

Swift 并发任务的协作式取消

在 Swift 并发&#xff08;Swift Concurrency&#xff09;中&#xff0c;任务&#xff08;Task&#xff09;不会被强行终止&#xff0c;而是采用**协作式取消&#xff08;cooperative cancellation&#xff09;**机制。这意味着任务会被标记为“已取消”&#xff0c;但是否真正…

大数据(1.1)纽约出租车大数据分析实战:从Hadoop到Azkaban的全链路解析与优化

目录 一、背景与数据价值‌ ‌二、技术选型与组件分工‌ ‌三、数据准备与预处理‌ 四、实战步骤详解‌ ‌1. 数据上传至HDFS ‌2. Hive数据建模与清洗‌ 4‌.2.1 建表语句&#xff08;分区表按年份&#xff09;‌&#xff1a; ‌4‌.2.2 数据清洗&#xff08;剔除无效…

代码随想录刷题day50|(回溯算法篇)131.分割回文串▲

目录 一、回溯算法基础知识 二、分割回文串思路 2.1 如何切割 2.2 判断回文 2.3 回溯三部曲 2.4 其他问题 三、相关算法题目 四、总结 一、回溯算法基础知识 详见&#xff1a;代码随想录刷题day46|&#xff08;回溯算法篇&#xff09;77.组合-CSDN博客 二、分割回文…

VS Code PowerShell、Windows PowerShell、CMD 的区别与联系

VS Code PowerShell、Windows PowerShell、CMD 的区别与联系? VS Code PowerShell、Windows PowerShell、CMD 的区别与联系&#xff1a; 一、核心概念对比 名称 全称 类型 定位 VS Code PowerShell Visual Studio Code PowerShell 代码编辑器集成终端 开发/脚本编写…

关于Unity的CanvasRenderer报错

MissingReferenceException: The object of type ‘CanvasRenderer’ has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object. UnityEngine.UI.GraphicRaycaster.Raycast (UnityEng…

C++编译流程

编译器其实就是一个翻译器&#xff0c;把我们的文件内容翻译成机器能够看懂的指令&#xff0c;但如何合理翻译是核心。 C语言编译 需要经过以下几步&#xff1a; 词法分析&#xff1a;扫描代码&#xff0c;确定单词类型&#xff0c;比如是变量还是函数&#xff0c;是标识符还…

python学智能算法(八)|决策树

【1】引言 前序学习进程中&#xff0c;已经对KNN邻近算法有了探索&#xff0c;相关文章链接为&#xff1a; python学智能算法&#xff08;七&#xff09;|KNN邻近算法-CSDN博客 但KNN邻近算法有一个特点是&#xff1a;它在分类的时候&#xff0c;不能知晓每个类别内事物的具…

使用 OpenCV 拼接进行图像处理对比:以形态学操作为例

图像处理在计算机视觉中起着至关重要的作用&#xff0c;而 OpenCV 作为一个强大的图像处理库&#xff0c;提供了丰富的函数来实现各类图像处理任务。形态学操作&#xff08;Morphological Operations&#xff09;是其中常用的技术&#xff0c;尤其适用于二值图像的处理。常见的…

版本控制器Git ,Gitee如何连接Linux Gitee和Github区别

&#x1f4d6; 示例场景 假设你和朋友在开发一个「在线笔记网站」&#xff0c;代码需要频繁修改和协作&#xff1a; 只用本地文件管理 每次修改后手动复制文件&#xff0c;命名为 v1.html、v2.html 问题&#xff1a;无法追踪具体改动内容&#xff1b;多人修改易冲突&#xff1…

使用DeepSeek翻译英文科技论文,以MarkDown格式输出,使用Writage 3.3.1插件转换为Word文件

一、使用DeepSeek翻译英文科技论文&#xff0c;以MarkDown格式输出 以科技论文“Electrical Power System Sizing within the Numerical Propulsion System Simulation”为例。 关于Writage 3.3.1的进一步了解&#xff0c;可发送邮件至邮箱pyengine163.com. 首先&#xff0c;打…