当前位置:   article > 正文

Spring AI教程 实战篇(一)基于Vue3对接流式对话接口

spring ai

基于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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

 对于这个接口的定义,不言而喻是基于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请求处理如上相同
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

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,
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

 介绍完基于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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5

 调用它,只需这样做:

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)=>{
        // 处理响应的数据,该数据是一段一段的
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

 文章介绍到这里其实就差不多了,动手能力强的同学一定可以写出自己使用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();
      }
    },
  });
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

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请求:

在这里插入图片描述

 Get请求:

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/825214
推荐阅读
相关标签
  

闽ICP备14008679号