Bootstrap

API接口模仿AI大模型进行流式返回

背景
   今天接到个很有意思的需求,本来是前端直接对接AI大模型的接口并流式返回,并没有后端什么事。但是需要对请求做一些额外的组装,所以变成了前端去请求后端API接口,后端对接AI大模型,并且流式返回数据给前端。相当于套了一层娃。如下图所示

变成了


技术方案
   一开始折腾了好久,后面终于找到了这个神器。
.SseEmitter
  SseEmitter是 Spring Framework 提供的一个类,用于实现服务器发送事件(Server-Sent Events,简称 SSE)。SSE 是一种基于 HTTP 协议的单向通信机制,允许服务器向客户端推送实时数据,而不需要客户端频繁地轮询服务器
核心特点
   .SSE 是单向的,仅支持服务器向客户端推送数据。 
   .基于 HTTP:使用标准的 HTTP 协议,兼容性好,无需额外协议支持。
   .自动重连:客户端会在连接断开后自动尝试重新连接。
   .轻量级:相比 WebSocket,SSE 更简单,适合单向推送场景。
使用场景
   .实时通知(如消息提醒)。
   .实时数据更新(如股票价格、新闻推送)。
   .流式数据传输(如日志流、聊天应用)

上代码
附上okhttp相关pom.xml,SseEmitter由于是Spring Framework 的,我的是springboot项目,所以不需要再导包了

<dependency>
   <groupId>com.squareup.okhttp3</groupId>
   <artifactId>okhttp</artifactId>
   <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp-sse</artifactId>
    <version>4.2.0</version>
</dependency>
@Data
public class GptRequestDTO {


    private String content;

    private String role;

}
 

private SseEmitter callGPT() {

        SseEmitter sseEmitter = new SseEmitter();

        String prompt="你好";

        

        String gptUrl = "你的ai大模型请求地址";

        String token = "你的ai大模型请求秘钥";

		
		//开始组装大模型的请求参数------可按实际情况修改
        HashMap<String,Object> params = new HashMap<>();
        
        params.put("chatId","xnkj");
        params.put("stream",true);
        params.put("detail",false);

        HashMap<String,Object> variables = new HashMap<>();
        variables.put("uid","asdfadsfasfd2323");
        variables.put("detail","张三");
        params.put("variables",variables);
        
        // 这里我使用了实体类,使用其他形式封装都可以,看个人
        List<GptRequestDTO> messages = new ArrayList<>();
        GptRequestDTO message = new GptRequestDTO();
        message.setRole("user");
        message.setContent(prompt);
        messages.add(message);

        params.put("messages",messages);

        // 转换为 JSON 字符串----我使用的是hutool工具
        String jsonParams = JSONUtil.toJsonStr(params);

        // 使用 OkHttpClient 发送请求
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(50, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.MINUTES)
                .build();

        Request request = new Request.Builder()
                .url(gptUrl)
                .addHeader("Authorization", "Bearer " + token)
                .addHeader("Content-Type", "application/json")
                .post(okhttp3.RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),
                        jsonParams))
                .build();

        // 发送请求并处理响应
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                log.error("请求失败", e);
                sseEmitter.completeWithError(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) {
                    log.error("Unexpected response code: " + response.code());
                    sseEmitter.completeWithError(new IOException("Unexpected response code: " + response.code()));
                    return;
                }

                try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        if (line.trim().isEmpty()) {
                            continue; // 跳过空行
                        }
                        sseEmitter.send(line); // 将每行数据发送到前端
                    }
                } catch (Exception e) {
                    log.error("推送数据失败", e);
                    sseEmitter.completeWithError(e);
                } finally {
                    sseEmitter.complete(); // 完成 SseEmitter
                }
            }
        });

        return sseEmitter; // 返回 SseEmitter 实例
    }

postman请求接口后返回如下图

前端接受数据的时候,也需要流式接收。

至此,收工,有问题欢迎留言沟通交流。

 

;