Spring AI 实战 基于Vue3实现前后端分离的流式输出

301次阅读
没有评论

共计 3847 个字符,预计需要花费 10 分钟才能阅读完成。

内容目录

基于Vue3实现流式输出

 应网友的需要,本篇将介绍下如何使用Vue3去调用Spring AI实现的流式输出接口。整个流程演示基于前后端分离。

流式接口回顾

 目前大模型的流式响应接口所采用的技术主要是两种:WebSocketSSE。这两种方式都支持服务端主动向客户端发送内容,其中,Websocket是一种双向通信协议,可以在一个连接上进行双向通信,即:客户端可以向服务端发送信息,服务端也可以主动向客户端发送信息。而SSE是基于标准的Http协议实现的,是一种单向信道,即:只支持服务端向客户端发送数据。一般我们主要是通过EventSource事件源来监听并获取服务端的消息。

 回顾流式对话的博客文章,我们使用Spring AI实现的流式接口如下:

    // 流式调用 将produces声明为文本事件流
    @GetMapping(value = "/stream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream(String prompt){
        // 将流中的内容按顺序返回
        return streamingChatClient.stream(prompt).flatMapSequential(Flux::just);
    }

 对于这个接口的定义,不言而喻是基于Http协议的,而我们使用produces将响应内容声明为事件流,其实也就说明我们的流式接口是一个SSE接口。

一般的,Websocket比较占用资源,因为是双向通信,一旦建立连接不断开,就会一直占用资源,所以不推荐使用Websocket。并且,Websocket的主要应用场景是在线聊天、实时游戏等,与我们的请求AI,AI响应我们有所不同。

前端发起SSE

 这里我就直接跳过对SSE请求的原理介绍,感兴趣的同学可以自行谷歌学习。前端发起请求无非就两种技术:axiosfetch,相信大部分人了解axios胜过fetch。但不幸的是,axios并不支持流事件,而fetch支持。因此对接SSE接口需要使用fetch或基于fetch实现的第三方请求库。

 这里我推荐一个微软开源的事件流请求库:fetch-event-source

fetch-event-source是基于fetch实现的、用于快速处理事件流请求的第三方库,它简化了事件流请求的操作,便于我们更好地处理这类请求。以发起SSE请求为例:

POST请求

const ctrl = new AbortController();
fetchEventSource('/api/sse', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        foo: 'bar'
    }),
    signal: ctrl.signal,
    onmessage: (message)=>{
        // 处理监听到的消息
    },
    onclose: ()=>{
        // 连接关闭后处理逻辑
    },
    onerror: (err)=>{
        // 发生错误后调用
    }
    // Get请求处理如上相同
});

Get请求

const url = new URL(baseUrl);
  Object.keys(params).forEach(key => url.searchParams.append(key, params[key].toString()));
const requestUrl = url.toString();
fetchEventSource(requestUrl, {
    method: 'GET',
    headers: {
        'Content-Type': 'application/json',
    },
    body:null,
    signal: ctrl.signal,
});

 介绍完基于Post和Get的SSE请求操作后,我们再回过来看最开始的流式接口:

    @GetMapping(value = "/stream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream(String prompt){
        // 将流中的内容按顺序返回
        return streamingChatClient.stream(prompt).flatMapSequential(Flux::just);
    }

 调用它,只需这样做:

const BaseUrl = "http://localhost:[port]/stream";
const prompt = "你的问题";
fetchEventSource(BaseUrl + "?prompt=" + prompt, {
    method: "GET",
    headers: {
        "Content-Type": "application/json",
    },
    body: null,
    signal: ctrl.signal,
    onmessage: (message)=>{
        // 处理响应的数据,该数据是一段一段的
    }
});

 文章介绍到这里其实就差不多了,动手能力强的同学一定可以写出自己使用Spring AI实现的各种需要传参数的流式接口。在写这篇博客时,我已经实现了一个基于Vue3实现了一个前后端分离的Demo。请求的核心代码摘至于下:

 详见可查看仓库代码:https://github.com/NingNing0111/spring-ai-zh-tutorial

import { fetchEventSource } from "@microsoft/fetch-event-source";
class FatalError extends Error {}
class RetriableError extends Error {}

type ResultCallBack = (e: any | null) => void;

const BaseUrl = "http://localhost:8898";
export const postStreamChat = (
  author: string,
  onMessage: ResultCallBack,
  onError: ResultCallBack,
  onClose: ResultCallBack
) => {
  const ctrl = new AbortController();
  fetchEventSource(BaseUrl + "/post-chat", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      author: author,
    }),
    signal: ctrl.signal,
    onmessage: onMessage,
    onerror: (err: any) => {
      onError(err);
    },
    onclose: () => {
      onClose(null);
    },
    onopen: async (response: any) => {
      if (response.ok) {
        return;
      } else if (
        response.status >= 400 &&
        response.status < 500 &&
        response.status !== 429
      ) {
        throw new FatalError();
      } else {
        throw new RetriableError();
      }
    },
  });
};

export const getStreamChat = (
  author: string,
  onMessage: ResultCallBack,
  onError: ResultCallBack,
  onClose: ResultCallBack
) => {
  const ctrl = new AbortController();
  fetchEventSource(BaseUrl + "/get-chat?author=" + author, {
    method: "GET",
    headers: {
      "Content-Type": "application/json",
    },
    body: null,
    signal: ctrl.signal,
    onmessage: onMessage,
    onerror: (err: any) => {
      onError(err);
    },
    onclose: () => {
      onClose(null);
    },
    onopen: async (response: any) => {
      if (response.ok) {
        return;
      } else if (
        response.status >= 400 &&
        response.status < 500 &&
        response.status !== 429
      ) {
        throw new FatalError();
      } else {
        throw new RetriableError();
      }
    },
  });
};

Demo注意事项

  • 基于Vue3实现,node版本:v18.16.0。
  • 项目已build,启动spring-ai-v1-stream-chat-demo项目后,可直接通过http://localhost:8898/index.html 访问网页。
  • Spring AI版本采用的是最新的1.0,与之前的0.8相比,对话接口类有所改变,需要注意。

效果图

 Post请求:

Spring AI 实战 基于Vue3实现前后端分离的流式输出

 Get请求:

Spring AI 实战 基于Vue3实现前后端分离的流式输出

正文完
 
PG Thinker
版权声明:本站原创文章,由 PG Thinker 2024-07-31发表,共计3847字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
热评文章
Rust中所有权与借用规则概述

Rust中所有权与借用规则概述

在GC与手动管理内存之间,Rust选择了第三种:所有权机制...