《通义千问AI落地—上》:后端接口

embedded/2024/12/21 22:23:25/

一、前言

本文源于微博客且已获授权,请尊重版权.

     通义,由通义千问更名而来,是阿里云推出的语言模型 ,于2023年9月13日正式向公众开放。 属于(AI Generated Content,AIGC)领域, 是一个MaaS(模型即服务)的底座。为多模态大模型(Multimodal Models)。

     通义意为“通情,达义”,具备全副AI能力,致力于成为人们的工作、学习、生活助手。 功能包括多轮对话、文案创作、逻辑推理、多模态理解、多语言支持,能够跟人类进行多轮的交互,也融入了多模态的知识理解,且有文案创作能力,能够续写小说,编写邮件等。

     虽然通义千问已经开源,但是连最基础的 Qwen2-0.5B ,也需要起码16GB的显存以上才能流程云运行(本人4060 8GB显存的显卡跑起来,等待一个问答结果,需要好几分钟),所以,私有化部署大模型所需要承受的硬件代价,不是一般的玩家所能承受的。此路不通,只得另寻他法了,这里所指的他法,就是购买通义千问的Token接口服务,价格也相当实惠:

image.png

其他信息本文不再赘述,请移步 通义千问官网 进行查看和购买。

二、成果展示

     最终的效果如下所示:

image.png

三、接口正文

3.1、maven依赖

      通义千问maven依赖主要有一下两个:

  <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-ai</artifactId></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java --><dependency><groupId>com.alibaba</groupId><artifactId>dashscope-sdk-java</artifactId><version>2.15.2</version></dependency>

其中,dashscope-sdk-java的版本需要根据你的jdk版本调整。

3.2、接口

  1. interface
public interface IChatGPTService {void chat(ChatMessageRequest content, Principal principal) throws NoApiKeyException, InputRequiredException;
}
  1. impl
/*** 历史对话记录;  sessionId---> 历史记录*/private static final ConcurrentHashMap<String, List<Message>> history = new ConcurrentHashMap<>();@Overridepublic void chat(ChatMessageRequest msg, Principal principal) throws NoApiKeyException, InputRequiredException {String sessionId = msg.getSessionId();//用户发送的消息入库CompletableFuture.runAsync(() -> {saveMsg(msg.getContent(), sessionId, Role.USER, getLocalDate());});Message message = Message.builder().role(Role.USER.getValue()).content(msg.getContent()).build();// 创建QwenParam对象,设置参数GenerationParam param = GenerationParam.builder().model(module) // 模型版本 qwen-max.messages(getHistory(sessionId)) // 消息内容,如果需要启用多伦连续对话的话,就把用户历史消息以及GPT回复的消息一起放进去.resultFormat(GenerationParam.ResultFormat.MESSAGE).topP(0.8).enableSearch(true).apiKey(apiKey)  // 你的apiKey,需要到阿里云百炼官网申请.incrementalOutput(true).build();// 调用生成接口,获取Flowable对象Flux<GenerationResult> result = Flux.from(gen.streamCall(param));StringBuffer builder = new StringBuffer();DateTime finalLocalTime = getLocalDate();Flux.from(result)// 控制发送频率.delayElements(Duration.ofMillis(200)).doOnNext(res -> {String output = res.getOutput().getChoices().get(0).getMessage().getContent();if (output == null || "".equals(output)) {return;}// 将生成的消息通过websocket发送给前端,websocket内容将在下篇文章介绍sendMsg(output, sessionId, principal);builder.append(output);}).doFinally(signalType -> {//消息发送结束,告诉前端sendMsg("!$$---END---$$!", sessionId, principal);//消息入库CompletableFuture.runAsync(() -> {saveMsg(builder.toString(), sessionId, Role.ASSISTANT, finalLocalTime);buildHistory(sessionId,Message.builder().role(Role.ASSISTANT.getValue()).content(builder.toString()));});}).onErrorResume(str -> {if (str instanceof ApiException) {ApiException exception = (ApiException) str;log.error("接口调用出现错误:{}", exception.getMessage());}sendMsg("GPT接口调用出现错误,该功能暂时无法使用,敬请期待.", sessionId, principal);return Mono.empty();}).subscribeOn(Schedulers.boundedElastic()) // 在弹性线程池中执行.subscribe();}/*** 每日凌晨自动清理历史对话缓存,防止缓存过大*/@Scheduled(cron = "0 59 23 * * ?")private void autoCleanHistory() {history.clear();}/*** 构建历史消息*/private void buildHistory(String sessionId, MessageBuilder<?, ?> message) {List<Message> historyMessages = history.computeIfAbsent(sessionId, k -> {List<ChatMessageVO> list = sessionService.getById(sessionId).getMessages();List<Message> getMsgList = new ArrayList<>();if (list.isEmpty()) return getMsgList;MessageBuilder<?, ?> msg = Message.builder();//只取后面60条,历史消息太多,一是过快消耗token,二是压力太大list.subList(Math.max(0, list.size() - 60), list.size()).forEach(item -> {if (!"".equals(item.getContent())) {msg.content(item.getContent()).role(item.getRole()).build();getMsgList.add(msg.build());}});return getMsgList;});// 添加消息到列表historyMessages.add(message.build());history.remove(sessionId);history.put(sessionId, historyMessages);}private List<Message> getHistory(String sessionId) {List<Message> list = history.get(sessionId);if (list == null || list.isEmpty()) {return new ArrayList<>();}list.removeIf(item -> ("".equals(item.getContent())));List<Message> hist = list.subList(Math.max(0, list.size() - 80), list.size());history.remove(sessionId);history.put(sessionId, hist);return hist;}
  1. 接口调用
@Controller
@AllArgsConstructor
@Slf4j
@CrossOrigin
public class WebSocketController {private final IChatGPTService service;private final SocketServiceImpl socketService;// 前端通过websocket发送消息给GPT,调用相关接口生成内容@MessageMapping("/chat/send")public void chat(@Payload ChatMessageRequest message, Principal principal) throws NoApiKeyException,InputRequiredException {service.chat(message, principal);}
}

其中涉及到的一些Java实体如下:

@Data
@Accessors(chain = true)
@RequiredArgsConstructor
public class ChatMessageRequest extends BaseEntity {private String content;private String role;private String sessionId;
}
    //上文提到的getLocalDate函数内容public static DateTime getLocalDate() {ZoneId zoneId = ZoneId.of("Asia/Shanghai");ZonedDateTime now = ZonedDateTime.now(zoneId);DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(zoneId);String formattedDate = now.format(formatter);try {ZonedDateTime parsedDate = ZonedDateTime.parse(formattedDate, formatter);return new DateTime(parsedDate.toInstant().toEpochMilli());}catch (DateTimeParseException e) {e.printStackTrace();return cn.hutool.core.date.DateUtil.parse(cn.hutool.core.date.DateUtil.now());}}

     通义千问主要的接口调用如上所述。在中篇,我将介绍《通义千问AI落地》前端实现。


http://www.ppmy.cn/embedded/100490.html

相关文章

无需多部备用机,云手机方便又便宜!

云手机&#xff0c;是云计算技术的又一创新应用&#xff0c;它通过在云服务器上虚拟出带有原生安卓操作系统的手机实例&#xff0c;为用户提供了一种全新的手机使用体验。无需携带多部手机&#xff0c;只需通过云手机&#xff0c;便可轻松实现多账号管理、应用运行及数据存储等…

SQL Server 2017上服务端设置强制加密启用SSL

在数据库服务端设置&#xff0c;强制所有客户端使用 SSL&#xff0c;设置完后&#xff0c;后续客户端所有连接&#xff0c;都将以密文传送&#xff0c;不论客户端是否指定安全连接&#xff08;即EncryptTrue/False&#xff09; 一、服务端强制加密使用 SSL 1.在数据库服务器上…

Base64编码的原理与应用场景

Base64编码的原理与应用场景 大家好&#xff0c;我是微赚淘客返利系统3.0的小编&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; Base64是一种编码方法&#xff0c;可以将二进制数据转换成64个可打印的ASCII字符&#xff0c;常用于在文本格式中表…

WRF-LES与PALM微尺度气象大涡模拟实践技术应用

针对微尺度气象的复杂性&#xff0c;大涡模拟&#xff08;LES&#xff09;提供了一种无可比拟的解决方案。微尺度气象学涉及对小范围内的大气过程进行精确模拟&#xff0c;这些过程往往与天气模式、地形影响和人为因素如城市布局紧密相关。在这种规模上&#xff0c;传统的气象模…

MySQL数据存储引擎:InnoDB与MyISAM的优缺点及常见业务场景

引言&#xff1a; MySQL作为广泛使用的开源关系型数据库管理系统&#xff0c;支持多种数据存储引擎以满足不同场景下的需求。本文将深入探讨MySQL中的InnoDB和MyISAM两种主流存储引擎的优缺点、常见使用业务场景&#xff0c;并详细讲解它们的锁机制和表压缩原理。 1. InnoDB存…

【Orb-Slam3学习】 特征匹配函数的目的与分类

特征匹配函数的目的 特征匹配的目的大致有3个&#xff1a; 1、特征点最朴素的匹配方式的暴力匹配&#xff0c;但是暴力匹配过于耗时。从而在Orb-Slam3中为快速进行特征点匹配创建了多个特征匹配函数。 此目的采用缩小特征点搜索范围达到&#xff0c;具体由以下三种方式达到&…

音视频封装格式之FLV

FLV&#xff08;Flash Video&#xff09;是一种常见的视频文件格式&#xff0c;FLV 格式最初是由 Adobe 公司开发的&#xff0c;旨在为网络视频提供一种高效、可扩展且易于流式传输的解决方案。随着在线视频的迅速发展&#xff0c;FLV 因其良好的兼容性和流式传输性能&#xff…

Ps:首选项 - 常规

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“常规” General选项卡主要用于调整 Photoshop 的整体工作行为和用户体验。这些设置让用户可以根据个人习惯和工作流程定制软件的响应方式和界面布局&#xff0c;从而提高工作…