microblog | 微博客
原创
访问
0
获赞
0
评论
相关推荐
暂无数据
最新文章
暂无数据
热门文章
暂无数据

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

写完bug就找女朋友 08月19日 10:30:27 1 57 3
分类专栏: AI Java 文章标签: AI GPT 通义千问 GPT落地

一、前言

     通义,由通义千问更名而来,是阿里云推出的语言模型 ,于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<>(); @Override public 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落地》前端实现。



评论区

登录后参与交流、获取后续更新提醒

wxz
08月20日 15:06:22
下篇啥时候更新
wxz
08月20日 14:54:09
学习了
写完bug就找女朋友
作者
08月19日 10:32:09
催更
目录
暂无数据