手把手教你实现类ChatGPT的AI流式输出效果

手把手教你实现类ChatGPT的AI流式输出效果

作为一名技术博主,我一直关注AI领域的最新发展。今天,我将分享如何实现类似ChatGPT那样的AI流式输出效果,让你的AI应用更加生动有趣!

效果展示

前言

大家好,和大家分享一个非常酷的技术实现 —— AI的流式输出。相信大家在使用ChatGPT时都被它那种一个字一个字"打出来"的效果所吸引,这种流式输出不仅提升了用户体验,还让AI回答看起来更加自然。

本文将从前后端两个方面详细讲解如何实现这一效果,并提供完整的代码示例。本教程参考了阿里云文档"阿里云ModelStudio DeepSeek API",并结合实际项目进行了优化。

什么是流式输出?

流式输出(Streaming Output)是指服务器不等待完整的响应生成完毕,而是在生成过程中逐步将数据发送给客户端的技术。这种方式有以下优点:

  1. 提升用户体验:用户无需等待完整响应,可以立即看到部分结果
  2. 降低感知延迟:即使完整响应需要较长时间,用户也能感受到系统在持续工作
  3. 模拟人类打字效果:让AI回答看起来更加自然,增强拟人感

技术实现

后端实现(Spring Boot + 阿里云百炼API)

在后端,我们使用Spring WebFlux结合阿里云百炼API来实现真实的AI流式响应。Spring WebFlux是Spring Framework的响应式Web框架,它基于Reactor库,支持非阻塞式的响应式编程。

以下是完整的核心代码:

package com.aso.aistreamoutput;

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import io.reactivex.Flowable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.adapter.rxjava.RxJava2Adapter;
import reactor.core.publisher.Flux;

import java.util.Arrays;

@RestController
@RequestMapping("/callStream")
public class Controller {
    public GenerationParam buildGenerationParam(Message userMsg) {
        return GenerationParam.builder()
                // 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:.apiKey("sk-xxx")
                .apiKey("sk-xxx")
                .model("deepseek-r1")
                .messages(Arrays.asList(userMsg))
                // 不可以设置为"text"
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .incrementalOutput(true)
                .build();
    }
    public  Flowable<GenerationResult> streamCallWithMessage(Generation gen, Message userMsg)
            throws NoApiKeyException, ApiException, InputRequiredException {
        GenerationParam param = buildGenerationParam(userMsg);
        Flowable<GenerationResult> result = gen.streamCall(param);
        return result;
    }

    @PostMapping
    public Flux<String> getReply(@RequestBody String prompt) throws NoApiKeyException, InputRequiredException {
        Generation gen = new Generation();
        Message userMsg = Message.builder().role(Role.USER.getValue()).content(prompt).build();
        Flowable<GenerationResult> message = streamCallWithMessage(gen, userMsg);


        // 将 RxJava Flowable 转换为 Reactor Flux
        return RxJava2Adapter.flowableToFlux(message)
                .map(generationResult -> {
                    // 提取 content 内容
                    return generationResult.getOutput()
                            .getChoices()
                            .get(0)
                            .getMessage()
                            .getContent();
                })
                .filter(content -> !content.isEmpty()); // 过滤空内容
    }
}

这段代码的关键点:

  1. 阿里云百炼API集成:使用阿里云的百炼API(DashScope)进行AI文本生成
  2. 参数配置:通过buildGenerationParam方法配置API调用参数,包括API密钥、模型选择和增量输出设置
  3. 流式调用:使用streamCall方法实现流式API调用,返回RxJava的Flowable对象
  4. 响应式转换:将RxJava的Flowable转换为Spring WebFlux的Flux,实现框架兼容
  5. 内容提取:从API响应中提取实际的文本内容
  6. 过滤处理:过滤掉空内容,确保前端接收到有意义的数据

这种实现方式的优势在于:

  • 使用真实的AI大模型(deepseek-r1)生成高质量回复
  • 通过incrementalOutput(true)启用增量输出,实现真正的流式响应
  • 结合Spring WebFlux的响应式编程模型,高效处理并发请求

配置阿里云百炼API

在使用上述代码之前,你需要进行以下准备工作:

  1. 获取API密钥

    • 访问阿里云百炼
    • 注册/登录阿里云账号
    • 在控制台中创建API密钥(API Key)
    • 将获得的API密钥替换代码中的sk-xxx
  2. 添加Maven依赖
    pom.xml中添加以下依赖:

    <!-- 阿里云百炼SDK -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>dashscope-sdk-java</artifactId>
        <version>2.8.5</version>
    </dependency>
    
    <!-- Spring WebFlux -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    
    <!-- RxJava2 适配器 -->
    <dependency>
        <groupId>io.projectreactor.addons</groupId>
        <artifactId>reactor-adapter</artifactId>
    </dependency>
    

项目源码

https://gitee.com/etilic/AiStreamOutput.git

常见问题解决

  1. API密钥配置

    • 推荐使用环境变量配置API密钥
    • 在系统环境变量中添加DASHSCOPE_API_KEY
    • 或者在代码中直接设置:.apiKey("你的API密钥")
  2. 跨域问题
    如果遇到跨域问题,可以添加以下配置类:

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("http://localhost:5173")
                    .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                    .allowedHeaders("*")
                    .allowCredentials(true);
        }
    }
    
  3. 流式响应超时
    如果遇到响应超时问题,可以在application.properties中配置:

    spring.webflux.base-path=/
    spring.webflux.static-path-pattern=/**
    spring.webflux.timeout=30000
    

前端实现(Vue 3)

前端使用Vue 3和Axios来处理流式响应。以下是核心代码:

// ChatDialog.vue
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';

const inputMessage = ref('');
const messages = ref([]);
const loading = ref(false);

const sendMessage = async () => {
  if (!inputMessage.value.trim()) return;
  
  // 添加用户消息
  const userMessage = inputMessage.value;
  messages.value.push({
    content: userMessage,
    isUser: true
  });
  
  // 添加AI消息占位
  const aiMessageIndex = messages.value.length;
  messages.value.push({
    content: '',
    isUser: false
  });
  
  inputMessage.value = '';
  loading.value = true;
  
  try {
    // 发送请求并处理流式响应
    const response = await axios.post('/callStream', {
      message: userMessage
    }, {
      responseType: 'text',
      onDownloadProgress: (progressEvent) => {
        // 获取已接收的数据
        const responseText = progressEvent.currentTarget.response;
        // 更新AI消息内容
        messages.value[aiMessageIndex].content = responseText;
      }
    });
    
    // 确保最终内容已更新
    messages.value[aiMessageIndex].content = response.data;
  } catch (error) {
    console.error('Error:', error);
    messages.value[aiMessageIndex].content = '抱歉,发生了错误,请稍后再试。';
  } finally {
    loading.value = false;
  }
};
</script>

这段代码的关键点:

  1. 使用ref()创建响应式变量存储消息和状态
  2. 发送消息时,先添加用户消息和AI消息占位
  3. 使用Axios发送POST请求,设置responseType: 'text'
  4. 关键是onDownloadProgress回调函数,它会在接收到新数据时触发
  5. 在回调中更新AI消息内容,实现流式显示效果

前端模板部分

<template>
  <div class="chat-container">
    <div class="messages-container" ref="messagesContainer">
      <div v-for="(msg, index) in messages" :key="index" 
           :class="['message', msg.isUser ? 'user-message' : 'ai-message']">
        <div class="message-content">{{ msg.content }}</div>
      </div>
    </div>
    
    <div class="input-container">
      <el-input 
        v-model="inputMessage" 
        placeholder="请输入您的问题..." 
        :disabled="loading"
        @keyup.enter="sendMessage"
      />
      <el-button 
        type="primary" 
        @click="sendMessage" 
        :loading="loading"
      >
        发送
      </el-button>
    </div>
  </div>
</template>

配置代理解决跨域问题

在前端开发中,我们通常会遇到跨域问题。使用Vite作为构建工具时,可以通过配置代理来解决:

// vite.config.js
export default defineConfig({
  // ...其他配置
  server: {
    proxy: {
      '/callStream': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        secure: false
      }
    }
  }
})

进阶优化

  1. 错误处理:添加更完善的错误处理机制,包括网络错误、服务器错误等
  2. 打字效果:可以添加打字机效果,控制文字显示速度
  3. Markdown渲染:如果AI回复包含Markdown,可以使用库如marked进行渲染
  4. 代码高亮:对代码块进行语法高亮处理
  5. 重试机制:添加请求失败后的重试逻辑

实际应用场景

这种流式输出技术适用于多种场景:

  • AI聊天机器人:提供类似ChatGPT的交互体验
  • 实时翻译工具:边输入边翻译
  • 代码生成工具:实时生成并展示代码
  • 内容创作助手:帮助用户撰写文章、邮件等

总结

通过本文的介绍,我们学习了如何使用Spring Boot和Vue 3实现AI的流式输出效果。这种技术不仅可以提升用户体验,还能让你的AI应用更加生动有趣。希望这篇教程对你有所帮助!

如果你想了解更多关于AI应用开发的内容,欢迎关注我的博客,我会持续分享更多实用技术和经验。

效果展示

参考资料

  1. 阿里云ModelStudio DeepSeek API
  2. Spring WebFlux 官方文档
  3. Vue 3 官方文档
  4. Axios 文档

如果你觉得这篇文章有帮助,别忘了点赞、收藏和关注哦!有任何问题也欢迎在评论区留言,我会及时回复。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值