当前位置: 首页 > news >正文

在springboot中调用openai Api并实现流式响应

之前在《在springboot项目中调用openai API及我遇到的问题》这篇博客中,我实现了在springboot中调用openai接口,但是在这里的返回的信息是一次性全部返回的,如果返回的文字比较多,我们可能需要等很久。 所以需要考虑将请求接口响应方式改为流式响应。

目录

openai api文档

码代码!!!

配置

properties

pom文件

1.请求体类

请求体中的信息类

2.响应类

1)响应体主体类

2)Delta类

常量池类

客户端类

websocket后端配置

1)websocket配置类

2)websocket类

ai消息工具类

页面

看结果


openai api文档

查阅openai的api文档,文档中说我们只需要在请求体中添加"stream":true就可以实现流式响应了。

openai api文档流式响应参数

 文档中还说当返回值为data: [DONE]时,标识响应结束。

码代码!!!

跟之前一样,为了缩减篇幅,set、get、构造器都省略

配置

properties

openai.key=你的keyopenai.chatgtp.model=gpt-3.5-turbo
openai.gpt4.model=gpt-4-turbo-preview
openai.chatgtp.api.url=/v1/chat/completions

pom文件

我们在项目中引入websocket和webflux 之前使用的RestTemplate并不擅长处理异步流式的请求。所以我们改用web flux。

<!--		websocket依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
<!--		流式异步响应客户端--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>

请求体类

public class ChatRequest {// 使用的模型private String model;// 历史对话记录private List<ChatMessage> messages;private Boolean stream = Boolean.TRUE;@Overridepublic String toString() {try {return ConstValuePool.OBJECT_MAPPER.writeValueAsString(this);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}
}

请求体中的信息类

public class ChatMessage {// 角色private String role;// 消息内容private String content;
}

响应类

响应类先看接口的返回格式的示例吧。下面json中的content就是本次响应数据

{"id": "chatcmpl-8uk7ofAZnSJhsHlsQ9mSYwFInuSFq","object": "chat.completion.chunk","created": 1708534364,"model": "gpt-3.5-turbo-0125","system_fingerprint": "fp_cbdb91ce3f","choices": [{"index": 0,"delta": {"content": "吗"},"logprobs": null,"finish_reason": null}]
}

根据json格式,我们构造响应体类如下

1)响应体主体类

public class ChatResponse {private String id;private String object;private Long created;private String model;private String system_fingerprint;// GPT返回的对话列表private List<Choice> choices;public static class Choice {private int index;private Delta delta;private Object logprobs;private Object finish_reason;}
}

2)Delta类

public class Delta {private String role;private String content;
}

常量池类

public class ConstValuePool {// openai代理客户端public static WebClient PROXY_OPENAI_CLIENT = null;
}

客户端类

客户端一样还是在钩子函数中生成。

@Component
public class ApiCodeLoadAware implements EnvironmentAware, ApplicationContextAware {Environment environment;@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {// chatgpt、gpt4HttpClient httpClient = HttpClient.create().proxy(clientProxy ->clientProxy.type(ProxyProvider.Proxy.HTTP) // 设置代理类型.host("127.0.0.1") // 代理主机.port(7890)); // 代理端口ConstValuePool.PROXY_OPENAI_CLIENT = WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).baseUrl("https://api.openai.com").defaultHeader("Authorization", "Bearer " + environment.getProperty("openai.key")).build();}
}

websocket后端配置

webscoekt具体可以看我之前的博客使用websocket实现服务端主动发送消息到客户端

1)websocket配置类

@Configuration
public class WebsocketConfig {@Beanpublic ServerEndpointExporter getServerEndpointExporter() {return new ServerEndpointExporter();}}

2)websocket类

这里的参数id是为了区分具体是那个websocket需要推送消息,可以通过登录等方式提供给用户

@Component
@ServerEndpoint("/aiWebsocket/{id}")
public class AiWebsocketService {private final Logger logger = LoggerFactory.getLogger(AiWebsocketService.class);private Session session;//存放所有的websocket连接private static Map<String,AiWebsocketService> aiWebSocketServicesMap = new ConcurrentHashMap<>();//建立websocket连接时自动调用@OnOpenpublic void onOpen(Session session,@PathParam("id") String id){this.session = session;aiWebSocketServicesMap.put(id, this);logger.debug("有新的websocket连接进入,当前连接总数为" + aiWebSocketServicesMap.size());}//关闭websocket连接时自动调用@OnClosepublic void onClose(){aiWebSocketServicesMap.remove(this);logger.debug("连接断开,当前连接总数为" + aiWebSocketServicesMap.size());}//websocket接收到消息时自动调用@OnMessagepublic void onMessage(String message){logger.debug("this:" + message);}//通过websocket发送消息public void sendMessage(String message, String id){AiWebsocketService aiWebsocketService = aiWebSocketServicesMap.get(id);if (aiWebsocketService == null) {return;}try {aiWebsocketService.session.getBasicRemote().sendText(message);} catch (IOException e) {logger.debug(this + "发送消息错误:" + e.getClass() + ":" + e.getMessage());}}}

ai消息工具类

@Component
public class ChatGptModelService implements AiModelService{private static final Logger logger = LoggerFactory.getLogger(ChatGptModelService.class);@Value("${openai.chatgtp.api.url}")private String uri;@Value(("${openai.chatgtp.model}"))private String model;@Resourceprivate AiWebsocketService aiWebsocketService;@Overridepublic String answer(String prompt, HttpServletRequest request) throws InterruptedException {HttpSession session = request.getSession();String identity = AiIdentityFlagUtil.getAiIdentity(request);// 获取历史对话列表,chatMessages实现连续对话、chatDialogues便于页面显示List<ChatMessage> chatMessages = (List<ChatMessage>) session.getAttribute(ConstValuePool.CHAT_MESSAGE_DIALOGUES);List<AiDialogue> chatDialogues = (List<AiDialogue>) session.getAttribute(ConstValuePool.CHAT_DIALOGUES);if (chatMessages == null) {chatMessages = new ArrayList<>();chatMessages.add(ChatMessage.createSystemDialogue("You are a helpful assistant."));chatDialogues = new ArrayList<>();session.setAttribute(ConstValuePool.CHAT_DIALOGUES, chatDialogues);session.setAttribute(ConstValuePool.CHAT_MESSAGE_DIALOGUES, chatMessages);}chatMessages.add(new ChatMessage("user", prompt));chatDialogues.add(AiDialogue.createUserDialogue(prompt));ChatRequest chatRequest = new ChatRequest(this.model, chatMessages);logger.debug("发送的请求为:{}",chatRequest);Flux<String> chatResponseFlux = ConstValuePool.PROXY_OPENAI_CLIENT.post().uri(uri).contentType(MediaType.APPLICATION_JSON).bodyValue(chatRequest.toString()).retrieve().bodyToFlux(String.class);// 得到string返回,便于查看结束标志StringBuilder resultBuilder = new StringBuilder();// 设置同步信号量Semaphore semaphore = new Semaphore(0);chatResponseFlux.subscribe(value -> {logger.debug("返回结果:{}", value);if ("[DONE]".equals(value)) {return;}try {ChatResponse chatResponse = ConstValuePool.OBJECT_MAPPER.readValue(value, ChatResponse.class);List<ChatResponse.Choice> choices = chatResponse.getChoices();ChatResponse.Choice choice = choices.get(choices.size() - 1);Delta delta = choice.getDelta();String res = delta.getContent();if (res != null) {resultBuilder.append(res);aiWebsocketService.sendMessage(resultBuilder.toString(), identity);}} catch (JsonProcessingException e) {throw new AiException("chatgpt运行出错",e);}}, // 获得数据,拼接结果,发送给前端error -> {semaphore.release();throw new AiException("chatpgt执行出错",error);}, // 失败释放信号量,并报错semaphore::release// 成功释放信号量);semaphore.acquire();String resString = resultBuilder.toString();logger.debug(resString);chatDialogues.add(AiDialogue.createAssistantDialogue(resString));chatMessages.add(ChatMessage.createAssistantDialogue(resString));// 对话轮数过多删除最早的历史对话,避免大量消耗tokenswhile (chatMessages.size() > ConstValuePool.CHAT_MAX_MESSAGE) {chatMessages.remove(0);}return "";}
}

页面

因为我的前端写的不太好,就不展示前端代码了

看结果

能够实现 

openai api流式调用结果1

openai api流式调用结果2

相关文章:

在springboot中调用openai Api并实现流式响应

之前在《在springboot项目中调用openai API及我遇到的问题》这篇博客中&#xff0c;我实现了在springboot中调用openai接口&#xff0c;但是在这里的返回的信息是一次性全部返回的&#xff0c;如果返回的文字比较多&#xff0c;我们可能需要等很久。 所以需要考虑将请求接口响应…...

C++构造函数重难点解析

一、C构造函数是什么 C的构造函数是一种特殊的成员函数&#xff0c;用于初始化类的对象。它具有与类相同的名称&#xff0c;并且没有返回类型。构造函数在创建对象时自动调用&#xff0c;并且可以执行必要的初始化操作。 二、C构造函数特点 类的构造函数不能被继承&#xff0c…...

QT day3 作业2.22

思维导图&#xff1a; 作业&#xff1a; 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到…...

AR汽车行业解决方案系列之2-远程汽修

在汽车行业中&#xff0c;AR技术的应用正悄然改变着整个产业链的运作方式&#xff0c;应用涵盖培训、汽修、汽车售后、PDI交付、质检以及汽车装配等&#xff0c;AR技术为多个环节都带来了前所未有的便利与效率提升。 安宝特AR将以系列推文的形式为读者逐一介绍在汽车行业中安宝…...

每日五道java面试题之spring篇(五)

目录&#xff1a; 第一题. 使用 Spring 有哪些方式&#xff1f;第二题. 什么是Spring IOC 容器&#xff1f;第三题. 控制反转(IoC)有什么作用?第四题. IOC的优点是什么&#xff1f;第五题. BeanFactory 和 ApplicationContext有什么区别&#xff1f; 第一题. 使用 Spring 有哪…...

挑战杯 基于YOLO实现的口罩佩戴检测 - python opemcv 深度学习

文章目录 0 前言1 课题介绍2 算法原理2.1 算法简介2.2 网络架构 3 关键代码4 数据集4.1 安装4.2 打开4.3 选择yolo标注格式4.4 打标签4.5 保存 5 训练6 实现效果6.1 pyqt实现简单GUI6.3 视频识别效果6.4 摄像头实时识别 7 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xf…...

12. Springboot集成Dubbo3(三)Dubbo-Admin

目录 1、前言 2、安装 2.1、下载Dubbo-admin 2.2、修改配置 2.3、编译前端 2.4、访问 2.5、加载自己的服务 2.6、服务测试 2.7、其他 3、小结 1、前言 Dubbo Admin是用于管理Dubbo服务的基于Web的管理工具。Dubbo Admin提供了一个用户友好的界面&#xff0c;用于在分…...

c语言的数据结构:找环状链表入口处

一起<(&#xffe3;︶&#xffe3;)↗[GO!] 1.如何判断一个链表是否有环 思路:设定两个快慢指针fast和slow,fast每次走两个结点,slow每次走一个节点 如果fast指针遇到了Null,那么这个链表没有环,如果fast和slow可以相遇,则代表这个链表有环 代码如下 N:fast先进环,slow后…...

LabVIEW声速测定实验数据处理

LabVIEW声速测定实验数据处理 介绍了一个基于LabVIEW的声速测定实验数据处理系统的应用。该系统利用LabVIEW的强大数据处理和分析能力&#xff0c;通过设计友好的用户界面和高效的算法&#xff0c;有效提高了声速测定实验的数据处理效率和准确性。通过这个案例&#xff0c;可以…...

深入剖析C语言中的段错误:从内存模型到实战调试全方位解析

引言 在C语言编程的世界里&#xff0c;段错误&#xff08;Segmentation Fault&#xff09;无疑是最常见的运行时错误之一。它源自程序对内存的非法访问&#xff0c;可能由于数组越界、野指针、悬垂指针、栈溢出等各种原因造成。本篇文章旨在带领读者深入探索C语言中的内存管理…...

1.操作Python入门Python安装和使用教程

1. 命令行与环境 为获取各种设置信息&#xff0c;CPython 解析器会扫描命令行与环境。 CPython 实现细节&#xff1a; 其他实现的命令行方案可能会有所不同。 详见 其他实现。 1.1. 命令行 调用 Python 时&#xff0c;可以指定下列任意选项&#xff1a; python [-bBdEhiIO…...

STM32G030C8T6:定时器1ms中断(以64MHz外部晶振为例)

本专栏记录STM32开发各个功能的详细过程&#xff0c;方便自己后续查看&#xff0c;当然也供正在入门STM32单片机的兄弟们参考&#xff1b; 本小节的目标是&#xff0c;系统主频64 MHZ,采用高速外部晶振&#xff0c;通过定时器3 每秒中断控制 PB9 引脚输出高低电平&#xff0c;从…...

人工智能聊天机器人如何帮助您实现工作与生活的平衡

如何用AI聊天机器人实现高效工作生活平衡 工作与生活平衡是管理个人和职业生活需求和责任的能力。 在当今快节奏和竞争激烈的世界中&#xff0c;工作与生活平衡被视为一个理想的目标。然而&#xff0c;对于忙碌的专业人士来说&#xff0c;实现工作与生活的平衡可能具有挑战性&a…...

3分钟看懂设计模式01:策略模式

一、什么是策略模式 定义一些列算法类&#xff0c;将每一个算法封装起来&#xff0c;并让它们可以互相替换。 策略模式让算法独立于使用它的客户而变化&#xff0c;是一种对象行为型模式。 以上是策略模式的一般定义&#xff0c;属于是课本内容。 在没有真正理解策略模式之…...

数据结构与算法:算法详解

1. 引言 1.1 算法在计算机科学中的地位和重要性 算法是计算机科学的基石&#xff0c;它指导着计算机在解决各种问题时的行为。一个好的算法可以使得问题的解决更加高效、精确和可靠&#xff0c;因此在计算机科学中具有至关重要的地位。 1.2 学习算法的意义和目标 学习算法不…...

AOSP10 替换系统launcher

本文实现将原生的launcher 移除&#xff0c;替换成我们自己写的launcher。 分以下几个步骤&#xff1a; 一、新建一个自己的launcher项目。 1.直接使用android studio 新建一个项目。 2.修改AndroidManifest.xml <applicationandroid:persistent"true"androi…...

视频互动游戏如何暴打海王和舔狗

前言 前2篇文章回答了游戏的可取之处以及不可复制的地方还有对于这一类的情景互动游戏在2024年的发展预言。第三篇主要是回答在一篇中一个留言的读者问的问题“如何暴打海王和舔狗”&#xff0c;求同存异&#xff0c;希望能够跟更多的读者交流与互相学习。 海王和舔狗的特征 …...

大学生多媒体课程学习网站thinkphp+vue

开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 运行环境:phpstudy/wamp/xammp等开发背景 &#xff08;一&#xff09; 研究课程的提出 &#xff08;二&#xff09;学习网站的分类与界定…...

信息系统项目管理师论文分享(质量管理)

水一篇文章。我发现身边考高项的朋友很多都是论文没过&#xff0c;我想着那就把我的论文分享出来&#xff0c;希望能有帮助。 质量管理 摘要 2020年5月&#xff0c;我作为项目经理参加了“某市某医联体的互联网诊疗&#xff08;互联网医院和远程医疗&#xff09;平台”的建设…...

Redis实现滑动窗口限流

常见限流算法 固定窗口算法 在固定的时间窗口下进行计数&#xff0c;达到阈值就拒绝请求。固定窗口如果在窗口开始就打满阈值&#xff0c;窗口后半部分进入的请求都会拒绝。 滑动窗口算法 在固定窗口的基础上&#xff0c;窗口会随着时间向前推移&#xff0c;可以在时间内平滑控…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

基于Java+VUE+MariaDB实现(Web)仿小米商城

仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意&#xff1a;运行前…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...