ruoyi(若依) 整合 Spring AI Alibaba 实际应用(2) vue3 流式显示大模型结果

ruoyi(若依) 整合 Spring AI Alibaba 实际应用(2) vue3 流式显示大模型结果

第一:ruoyi vue3 流式显示大模型结果:直接看效果

屏幕录制2025-04-12 上午10.52.24


注意事项:ruoyi框架 前端vue版本从2 转到vue3了

第二:实现流式显示:需要的前端组件:

组件1:用来实现SSE和后端spring AI 通讯
npm install @microsoft/fetch-event-source --registry=https://registry.npmmirror.com
组件2:以markdown 的形式来显示 文字内容
npm install vue3-markdown-it --registry=https://registry.npmmirror.com

第三:具体实现步骤

1:最快的搞定方式
直接git 下来:(vue3Dev 分支)
前端:git clone https://gitcode.com/zqy216_2008/RuoYi-Cloud-Vue3.git
后端: git clone https://gitee.com/auto_release/RuoYi-Cloud.git
注意分支:springboot3dev
2:前端 需要安装 上面说到的两个组件:microsoft/fetch-event-source --registry,vue3-markdown-it
通过fetch-event-source --registry 和后端通讯
在这里插入图片描述
通过 vue3-markdown-it 流式展示结果:通过组件 MarkdownIt
在这里插入图片描述
3:后端:完善alibaba ai的返回结果,便于前端知道什么时候结束
在这里插入图片描述
4:关键点:SSE为什么选择 microsoft/fetch-event-source 因为可以 进行post 请求,以及增加header 和token 便于验证
//还是csdn 的代码展示比 微信公众号体贴
//这是点击 搜索按钮调用的方法

前端代码
function llmHandleQuery() {
  llmParams.value.platform=queryParams.value.platform;
  const urlFinal=import.meta.env.VITE_APP_BASE_API+'/aiOperationalTools/aiAskAnswer/askQuestion';
  console.log("url:",urlFinal);
  //每点一次查询 就把之前的查询结果清楚掉
  llmAnswers.value="";
   let aboutContainer = new AbortController(); //用于中断请求
  fetchEventSource(urlFinal, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json;charset=utf-8',
      'Authorization': 'Bearer '+ getToken(),
    },
    body: JSON.stringify(llmParams.value),
    openWhenHidden: false, //页面退至后台是否保持连接
    signal: aboutContainer.signal,
    async onopen(response) {
      if (response.ok && response.headers.get('content-type').includes(EventStreamContentType)) {
        console.log('SSE 连接成功');
      } else {
        throw new Error(response.statusText);
      }
    },
    onmessage(ev) {
      console.log("ev:",ev);
      if (ev.data === '[DONE]') {
        console.log('SSE 数据接收完成');
        aboutContainer.abort("回答完成");
      } else {
        const jsonData = ev.data;
        console.log("内容:",jsonData);
        llmAnswers.value+=jsonData;
      }
    },
    onclose() {
      console.log('SSE 连接关闭');
    },
    onerror(err) {
      console.error('SSE 错误:', err);
      if (err.name === 'AbortError') {
        aboutContainer.abort("服务器错误");
      }
    }
  });
}
后端代码
```java
@PostMapping("/askQuestion")
//    @RequiresPermissions("aiOperationalTools:aiAskAnswer:askQuestion")
    public Flux<ServerSentEvent<String>> stream(@RequestBody
                                   QuestionVO questionVO, HttpServletRequest request
            ) {
        String prompt=questionVO.getPrompt();
        String platform=questionVO.getPlatform();
        //深度思考
        String Rflag= request.getParameter("Rflag");
        //联网搜索
        String forInternet= request.getParameter("forInternet");
        System.out.println("type:"+Rflag+" "+forInternet);
        if (!StringUtils.hasText(platform)) {
            return Flux.just(ServerSentEvent.builder("请选择大模型平台").event("message").build())
                    .concatWithValues(ServerSentEvent.builder("[DONE]").build())
                    .onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
        }
        if (Objects.equals("dashscope", platform)) {
            System.out.println("命中 dashscope ......");
            return chatClient.prompt(prompt).stream().content()
                    .map(content -> ServerSentEvent.builder(content).event("message").build())
                    //问题回答结速标识,以便前端消息展示处理
                    .concatWithValues(ServerSentEvent.builder("[DONE]").build())
                    .onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
        }

        if (Objects.equals("ollama", platform)) {
            System.out.println("命中 ollama ......");
            return ChatClient.builder(ollamaChatModel).build().prompt(prompt).stream().content()
                    .map(content -> ServerSentEvent.builder(content).event("message").build())
                    //问题回答结速标识,以便前端消息展示处理
                    .concatWithValues(ServerSentEvent.builder("[DONE]").build())
                    .onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
        }

        if (Objects.equals("openai", platform)) {
            System.out.println("命中 openai ......");
            return ChatClient.builder(openAIChatModel).build().prompt(prompt).stream().content()
                    .map(content -> ServerSentEvent.builder(content).event("message").build())
                    //问题回答结速标识,以便前端消息展示处理
                    .concatWithValues(ServerSentEvent.builder("[DONE]").build())
                    .onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
        }

           return Flux.just(ServerSentEvent.builder("不存在该模型平台,请管理员新增大模型").event("message").build())
                .concatWithValues(ServerSentEvent.builder("[DONE]").build())
                .onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
    }

5:用到的表
ai_ask_answer 保存用户提问信息
sys_dict_type 新增大模型平台
在这里插入图片描述

最后:自己在测试sse和后端连接的时候,注意不要让前端频繁的发连接请求。很可能会造成不停地调用大模型 而产生费用

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI懒虫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值