SpringBoot 接入阿里云百炼(通入千问) 并实现流式输出内容

一:引入Maven依赖
<dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>broadscope-bailian-sdk-java</artifactId>
            <version>1.2.0</version>
</dependency>
二:编写阿里云百炼Client类
配置文件加入如下配置

相关参数请从阿里云百炼官网获取

yun:
  bailian:
    accessKeyId:  **************************
    accessKeySecret:  **************************
    agentKey:   **************************
    appId: **************************
@Component
public class BaiLianClient {
    @Resource
    private AlbaiLianConfig baiLianConfig;
    private AccessTokenClient accessTokenClient;
    @Resource
    private ChatCustomerInfoMapper chatCustomerInfoMapper;
    @PostConstruct
    public void init(){
        String accessKeyId = baiLianConfig.getAccessKeyId();
        String agentKey = baiLianConfig.getAgentKey();
        String accessKeySecret = baiLianConfig.getAccessKeySecret();
        accessTokenClient = new AccessTokenClient(accessKeyId,accessKeySecret,agentKey);
    }
    public Flux<CompletionsResponse> createStreamCompletion(ChatCustomerInfo chatCustomerInfo) {
        String content = chatCustomerInfo.getInfoContent();
        String token = accessTokenClient.getToken();
        BaiLianConfig config = new BaiLianConfig()
                .setApiKey(token);
        CompletionsRequest request = new CompletionsRequest()
                .setAppId(baiLianConfig.getAppId())
                .setPrompt(content)
                .setStream(true);
        ApplicationClient client = new ApplicationClient(config);
        chatCustomerInfoMapper.sendInfo(chatCustomerInfo.getUserId(),chatCustomerInfo.getInfoContent(),false);
        Flux<CompletionsResponse> flux = client.streamCompletions(request);
        return client.streamCompletions(request);
    }
}
三:编写Controller层

为了更快实现功能,本人只是简单调用回复消息接口,并将历史消息保存到数据库表中,具体复杂API请参考阿里云百炼官网手册,后端流式输入采用FLux实现,

@RestController
@Slf4j
@Api(tags = "智能助手接口")
@RequestMapping("/chatCustomerInfo")
public class ChatCustomerInfoController {
    @Resource
    private ChatCustomerInfoService chatCustomerInfoService;
    @Resource
    private BaiLianClient baiLianClient;
    @Resource
    private ChatCustomerInfoMapper chatCustomerInfoMapper;
    private String responseText;
    private Long userId;
    @ApiOperation(value =  "获取历史信息",notes = "获取历史信息")
    @PostMapping("/getInfoList")
    public Result<List<ChatCustomerInfo>> getInfoList(@RequestBody ChatCustomerInfo chatCustomerInfo) {
        return Result.success(chatCustomerInfoService.getInfoByUserId(chatCustomerInfo.getUserId()));
    }
    @ApiOperation(value =  "发送消息",notes = "发送消息")
    @PostMapping(value = "/completions", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Result<String>> complete(@RequestBody ChatCustomerInfo chatCustomerInfo,HttpServletResponse response) {
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("X-Accel-Buffering", "no");
        userId = chatCustomerInfo.getUserId();
        Flux<CompletionsResponse> flux = baiLianClient.createStreamCompletion(chatCustomerInfo);
        return flux.onBackpressureBuffer()
                .map(this::convertResponse)
                .doOnComplete(this::handleComplete);
    }
 
    private void handleComplete() {
        log.info(responseText);
        chatCustomerInfoMapper.sendInfo(userId,responseText,true);
    }
 
    private Result<String> convertResponse(CompletionsResponse response) {
        this.responseText = response.getData().getText();
        return Result.success(response.getData().getText());
    }
 
}
四:前端调用接口并流式接受响应内容

<template>
  <div class="chat-container">
    <div class="chat-header">
      <h2>智能助手</h2>
    </div>
    <div class="chat-messages">
        <div v-for="message in list" :key="message.id" class="message">
          <div v-if="!message.isSelf" style="margin-left: 560px;color: grey;white-space: pre-wrap">
            {{message.createTime != null ? message.createTime + '\n' : null}}
          </div>
          <div v-else style="white-space: pre-wrap;color: grey">
            {{message.createTime != null ? message.createTime + '\n' : null}}
          </div>
          <div class="user" v-if="!message.isSelf" >
            <div class="customer-message">
              {{ message.infoContent }}
            </div>
            <div class="customer-message-img">
              <img :src="$target +'public/imgs/yonghu.jpg'" alt="" style="width: 100%; height: 100%">
            </div>
          </div>
          <div class="agent" v-else>
            <div class="agent-message-img">
              <img :src="$target +'public/imgs/chat.png'" alt="" style="width: 100%; height: 100%">
            </div>
            <div class="agent-message">{{ message.infoContent}}</div>
          </div>
        </div>
    </div>
    <div class="chat-input">
      <input type="text" v-model="text" @keyup.enter="onSend" placeholder="输入消息..." />
      <button @click="onSend">发送</button>
    </div>
    <van-image width="45px" height="45px"  round src="https://img01.yzcdn.cn/vant/cat.jpeg" />
  </div>
 
</template>
 
<script>
import { fetchEventSource } from "@microsoft/fetch-event-source";
export default {
  name: 'ChatCustomer',
  data() {
    return {
      see: '',
      text: '',
      list: [],
      gettime: ''
    }
  },
  mounted() {
 
  },
  created() {
    this.init()
  },
  activated() {
    // 获取消息数据
    this.$axios
        .post("/api/chatCustomerInfo/getInfoList", {
          userId: this.$store.getters.getUser.id
        })
        .then(res => {
          if (res.data.code === "001") {
            this.list = res.data.data;
          }
          this.getCurrentTime();
          let chuList = {
            isSelf: 1,
            infoContent: '您好,我是悦选市集智能助手小悦,快开始向我提问吧!',
            createTime: this.gettime
          }
          this.list.push(chuList)
        })
        .catch(err => {
          return Promise.reject(err);
        });
  },
  methods: {
    getCurrentTime() {
      //获取当前时间并打印
      let yy = new Date().getFullYear();
      let mm = new Date().getMonth() + 1;
      let dd = new Date().getDate();
      let hh = new Date().getHours();
      let mf = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes();
      let ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date().getSeconds();
      this.gettime = '                                                          ' + yy + '/' + mm + '/' + dd + ' ' + hh + ':' + mf + ':' + ss + '                                                   ' + '\n';
    },
    onSend: function () {
      let infoContent = this.text
      this.text = ''
      let newList = {
        isSelf: 0,
        infoContent: infoContent,
      }
      this.list.push(newList)
      let newList1 = {
        isSelf: 1,
        infoContent: '......'
      }
      this.list.push(newList1)
      let s = {"infoContent": infoContent,"userId": this.$store.getters.getUser.id}
      //
      fetchEventSource("/api/chatCustomerInfo/completions", {
        body: JSON.stringify(s),
        headers: {
          'Content-Type': 'application/json'
        },
        method: "POST",
        onmessage: (event) => {
          let s = JSON.parse(event.data);
          this.list.splice(this.list.length - 1,1,{isSelf: 1,infoContent: s.data})
        },
        onclose() {
        },
      });
    }
  },
}
</script>
 
<style>
.chat-container {
  width: 800px;
  height: 800px;
  margin: 0 auto;
  border: 1px solid #ccc;
  border-radius: 5px;
  overflow: hidden;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
 
}
 
.chat-header {
  background-color: #f2f2f2;
  padding: 10px;
  text-align: center;
  border-bottom: 1px solid #ccc;
}
 
.chat-messages {
  max-height: 650px;
  overflow-y: auto;
  padding: 10px;
}
.message {
  margin-bottom: 10px;
  height: 100%;
  padding: 15px 16px;
  display: flex;
  flex-direction: column;
}
.user {
  align-self: flex-end;
  display: flex;
}
.agent {
  display: flex;
}
.customer-message {
  background-color: #e6f7ff;
  padding: 20px;
  border-radius: 5px;
  width: fit-content;
  max-width: 500px; /* 聊天气泡的最大宽度 */
  word-wrap: break-word; /* 在单词边界处换行 */
  white-space: pre-wrap;
}
.customer-message-img {
  width: 60px;
  height: 60px;
  padding: 0 0 0 10px;
  border-radius: 5px;
}
.agent-message-img {
  width: 60px;
  height: 60px;
  padding: 0 10px 0 0;
  border-radius: 5px;
}
 
.agent-message {
  background-color: #f2f2f2;
  padding: 20px;
  border-radius: 5px;
  width: fit-content;
  max-width: 500px; /* 聊天气泡的最大宽度 */
  word-wrap: break-word; /* 在单词边界处换行 */
  white-space: pre-wrap;
}
.chat-input {
  display: flex;
  align-items: center;
  padding: 10px;
}
input {
  flex: 1;
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin-right: 10px;
}
 
button {
  padding: 8px 15px;
  background-color: #007bff;
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}
</style>
具体功能实现参考基于SpringBoot + Vue的商城购物系统实战-CSDN博客
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/m0_65408235/article/details/139618719

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值