当前位置: 首页 > news >正文

Springboot集成阿里云通义千问(灵积模型)

我这里集成后,做成了一个工具jar包,如果有不同方式的,欢迎大家讨论,共同进步。

集成限制:

1、灵积模型有QPM(QPS)限制,每个模型不一样,需要根据每个模型适配

集成开发思路:

因有QPS限制,无法支持多任务并发执行,所以使用任务池操作,定时监听任务池中任务状态;

因系统中执行不能等待QPS释放后执行,故使用异步调用;

开发思路:

1、创建任务,提交到任务池中

2、任务监听器每10秒检查任务池中的任务执行情况:

        1)任务未执行:获任务token,获取到执行任务,否则不执行

        2)任务执行中:判断任务执行是否超时,如果超时,重置任务状态,重试计数加1

        3)任务执行失败:执行失败回调。从任务池中清除

        4)任务执行成功:从任务池中清除

3、任务执行:

        1)获取任务token,如果获取到就执行,否则不执行

        2)利用工具类请求灵积模型

        3)判断任务执行状态:成功:执行成功回调;失败:重试计数加1,重置任务状态

        4)归还token

集成编码

1、前置操作

详见阿里云灵积模型服务开发参开icon-default.png?t=O83Ahttps://help.aliyun.com/zh/dashscope/developer-reference/acquisition-and-configuration-of-api-key?spm=a2c4g.11186623.0.0.1403193eLiHQfl

开发参考中获取到的API-KEY需要写到项目的配置文件中

2、创建灵积服务jar(aliyun-dashscope)

按照灵积模型Java jdk最佳实践的方式实现集成模型灵积模型Java jdk最佳实践icon-default.png?t=O83Ahttps://help.aliyun.com/zh/dashscope/java-sdk-best-practices?spm=a2c4g.11186623.0.0.4da417d9T9NKfMpom文件中引入jar

<dependency><groupId>com.alibaba</groupId><artifactId>dashscope-sdk-java</artifactId><version>2.15.0</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>com.aa.bb</groupId><artifactId>common-redis</artifactId><version>1.0.0</version></dependency>

dashscope-sdk-java : 灵积服务模型jar

commons-pool2 : 对象池工具jar

common-redis :个人项目中redis工具包(可以自己封装一个)

3、编码

1)创建config

@Data
@Configuration
@ConfigurationProperties(prefix = "aliyun.dashscope")
public class DashScopeConfig {/*** api密钥*/@Value("${aliyun.dashscope.apiKey}")private String apiKey;/*** 最大tokens数*/private int maxTokens = 800;/*** 模型*/private String model = "qwen-plus";/*** QPS*/private int qps = 15;/*** qps缓存密钥*/private String qpsRedisKey = "aliyun:dashscope:token";/*** 尝试计数*/private int tryCount = 3;/*** task间隔时间*/private int time = 10000;}

2)创建对象池工厂

public class DashScopePoolFactory extends BasePooledObjectFactory<Generation> {@Overridepublic Generation create() throws Exception {return new Generation();}@Overridepublic PooledObject<Generation> wrap(Generation generation) {return new DefaultPooledObject<>(generation);}
}

 3)创建task

DashTask:任务类

@Data
@Slf4j
public class DashTask {/*** qps令牌*/private Long qpsToken;/*** 正在执行*/private boolean execute = false;/*** 成功*/private boolean success = false;/*** 尝试计数*/private int tryCount = 0;/*** 生成参数*/private GenerationParam generationParam;/*** 结果*/private Message result;/*** 成功回调*/private Consumer<DashTask> successCallback;/*** 失败回调*/private Consumer<DashTask> failCallback;public void setSuccess(boolean success) {if (success) {this.onSuccess();} else {this.onFail();}}/*** 论成功*/public void onSuccess() {this.success = true;try {if (this.successCallback != null) {this.successCallback.accept(this);}} catch (Exception ex) {log.error("dash task onSuccess error:" + ex.getMessage());}}/*** 失败*/public void onFail() {this.success = false;try {if (this.failCallback != null) {this.failCallback.accept(this);}} catch (Exception ex) {log.error("spark task onFail error:" + ex.getMessage());}}}

DashListener:任务监听类 

@Slf4j
public class DashListener extends Listener {public DashListener(long interval) {super(interval, "dash-listener");}@Overridepublic void run() {log.info("灵积服务(通义千问)任务监听 start");setExecute(true);while (isExecute()) {try {DashScopeUtils.asyncTaskStart();Thread.sleep(getInterval());} catch (Exception e) {log.error("灵积服务(通义千问)任务监听 error", e);}}}
}

4)创建工具类

DashScopeUtils:灵积模型基础工具类

@Slf4j
public class DashScopeUtils {private static volatile DashScopeConfig config;private static volatile RedisService redisService;/*** 获取令牌*/public static final int GET_TOKEN_STATUS = 0;/*** 归还令牌*/public static final int BACK_TOKEN_STATUS = 1;private static CopyOnWriteArraySet<DashTask> taskList = new CopyOnWriteArraySet<DashTask>();/*** 通用池*/private static volatile GenericObjectPool<Generation> pool;/*** 创建消息** @param role    角色* @param content 所容纳之物* @return {@link Message }*/public static Message createMessage(Role role, String content) {return Message.builder().role(role.getValue()).content(content).build();}/*** 调用服务** @param param param* @return {@link GenerationResult }*/public static GenerationResult call(GenerationParam param) {try {if (param.getMaxTokens() == null) {param.setMaxTokens(getConfig().getMaxTokens());}Generation gen = getPool().borrowObject();GenerationResult call = gen.call(param);getPool().returnObject(gen);return call;} catch (Exception e) {log.error(e.getMessage(), e);throw new RuntimeException(e.getMessage());}}/*** 获取对象池** @return {@link GenericObjectPool }<{@link Generation }>*/public static GenericObjectPool<Generation> getPool() {if (pool == null) {synchronized (DashScopeUtils.class) {if (pool == null) {DashScopePoolFactory poolFactory = new DashScopePoolFactory();GenericObjectPoolConfig<Generation> config = new GenericObjectPoolConfig<>();config.setMaxTotal(64);config.setMaxIdle(64);config.setMinIdle(64);Constants.apiKey = getConfig().getApiKey();pool = new GenericObjectPool<>(poolFactory, config);}}}return pool;}/*** 获取配置** @return {@link DashScopeConfig }*/public static DashScopeConfig getConfig() {if (config == null) {synchronized (DashScopeConfig.class) {if (config == null) {config = SpringUtils.getBean(DashScopeConfig.class);}}}return config;}/*** 异步任务启动*/public static void asyncTaskStart() {instanceRedis();getConfig();// 令牌数量int current = 0;if (redisService.hasKey(config.getQpsRedisKey())) {current = Integer.parseInt(redisService.get(config.getQpsRedisKey()).toString());}if (current > 0) {String all = config.getQpsRedisKey() + ":*";int size = redisService.keys(all).size();if (size < current) {redisService.decr(config.getQpsRedisKey(), current - size);}}if (!taskList.isEmpty()) {Iterator<DashTask> iterator = taskList.iterator();while (iterator.hasNext()) {DashTask dashTask = iterator.next();if (dashTask.isExecute()) {if (!redisService.hasKey(config.getQpsRedisKey() + ":" + dashTask.getQpsToken())) {dashTask.setExecute(false);dashTask.setTryCount(dashTask.getTryCount()+1);}continue;} else if (dashTask.isSuccess()) {taskList.remove(dashTask);} else if (dashTask.getTryCount() > config.getTryCount()) {dashTask.setSuccess(false);taskList.remove(dashTask);} else if (!asyncTaskStart(dashTask)) {break;}}}}/*** 提交任务** @param dashTask 短跑任务*/public static void submitTask(DashTask dashTask) {taskList.add(dashTask);}/*** 异步任务启动** @param task 任务* @return boolean*/private static boolean asyncTaskStart(DashTask task) {if (qpsToken(GET_TOKEN_STATUS, task)) {AsyncManager.me().execute(() -> {try {task.setExecute(true);GenerationResult call = call(task.getGenerationParam());task.setResult(call.getOutput().getChoices().get(0).getMessage());task.setSuccess(true);} catch (Exception e) {task.setTryCount(task.getTryCount() + 1);}task.setExecute(false);qpsToken(BACK_TOKEN_STATUS, task);});return true;}return false;}/*** qps令牌** @param status 地位* @param task   任务* @return boolean*/private static synchronized boolean qpsToken(int status, DashTask task) {instanceRedis();getConfig();int current = 0;if (redisService.hasKey(config.getQpsRedisKey())) {current = Integer.parseInt(redisService.get(config.getQpsRedisKey()).toString());}// 获取tokenif (status == GET_TOKEN_STATUS) {if (current < config.getQps()) {Long incr = redisService.incr(config.getQpsRedisKey());task.setQpsToken(incr);redisService.set(config.getQpsRedisKey() + ":" + incr, "1", 1, TimeUnit.MINUTES);return true;} else {return false;}} else {if (current > 0) {redisService.decr(config.getQpsRedisKey());}redisService.del(config.getQpsRedisKey() + ":" + task.getQpsToken());return true;}}/*** 实例redis** @return {@link RedisService}*/private static RedisService instanceRedis() {if (redisService == null) {synchronized (DashScopeUtils.class) {if (redisService == null) {redisService = SpringUtils.getBean(RedisService.class);}if (redisService == null) {throw new RuntimeException("redisService is null");}}}return redisService;}
}

 QiamwenUtils:通义千问工具类


public class QianWenUtils {/*** 单轮对话** @param content 内容* @param success 成功*/public static void call(String content, Consumer<Message> success) {Message message = DashScopeUtils.createMessage(Role.USER, content);call(Collections.singletonList(message), success);}/*** 多轮对话** @param messages 对话列表* @return {@link Message }*/public static void call(List<Message> messages, Consumer<Message> success) {try {GenerationParam param = GenerationParam.builder().model(DashScopeUtils.getConfig().getModel()).messages(messages).resultFormat(GenerationParam.ResultFormat.MESSAGE).topP(0.8).maxTokens(600).build();DashTask dashTask = new DashTask();dashTask.setGenerationParam(param);dashTask.setSuccessCallback(dash -> success.accept(dash.getResult()));DashScopeUtils.submitTask(dashTask);} catch (Exception e) {throw new RuntimeException("通义千问失败:" + e.getMessage());}}
}

5)创建runner

runner主要作用:

(1)检查配置文件是否正确配置;

(2)启动任务监听器

@Slf4j
@Component
public class DashScopeRunner {private DashListener dashListener;@PostConstructpublic void run() {DashScopeConfig config = DashScopeUtils.getConfig();if (config == null || ObjectUtil.isEmpty(config.getApiKey())) {throw new RuntimeException("灵积服务(通义千问)启动失败,请检查配置文件");} else {log.info("灵积服务(通义千问)启动");}dashListener = new DashListener(config.getTime());dashListener.start();}@PostConstructpublic void shutdown() {if (dashListener != null) {dashListener.shutdown();}}
}

4、测试 

5、踩坑

1)token数量验证:每次开始执行任务池中任务状态检查时,要先检查任务token是否和实际一致,避免实际可用token数不足,导致进入死循环

2)任务池中的数据不能使用缓存(redis)

3)成功和失败回调必须是public

4)使用对象池(GenericObjectPool),借出对象,使用完成后必须归还,否则会出现无法借出的情况

5)config中QPS最好小于15,否则会出现限流情况

相关文章:

Springboot集成阿里云通义千问(灵积模型)

我这里集成后&#xff0c;做成了一个工具jar包&#xff0c;如果有不同方式的&#xff0c;欢迎大家讨论&#xff0c;共同进步。 集成限制&#xff1a; 1、灵积模型有QPM(QPS)限制&#xff0c;每个模型不一样&#xff0c;需要根据每个模型适配 集成开发思路&#xff1a; 因有…...

微信公众号(或微信浏览器)获取openId(网页授权)

下单支付需要openId 首先授权去拿到code --然后调用后太换取openId 1.去拿取code 下图中执行到window.location.href &#xff08; redirect_uri 传入当前路径-&#xff09;–执行后重新跳转到当前页面–但是路径上会带上code参数 //然后调用后台方法–将code传给后台得到 o…...

C++算法第五天

本篇文章继续和大家一起刷算法题 第一题 题目链接 . - 力扣&#xff08;LeetCode&#xff09; 题目解析 题目要求&#xff1a; 这是一个连续的子数组 计算子数组内元素的和&#xff0c;若数组内元素的和符合 > target的值并且该子数组的长度是最短的&#xff0c;则返回…...

牛客网剑指Offer-树篇-JZ26 树的子结构

题目 来源&#xff1a;JZ26 树的子结构 描述 输入两棵二叉树A&#xff0c;B&#xff0c;判断B是不是A的子结构。&#xff08;我们约定空树不是任意一个树的子结构&#xff09; 假如给定A为{8,8,7,9,2,#,#,#,#,4,7}&#xff0c;B为{8,9,2}&#xff0c;2个树的结构如下&#xff…...

FFmpeg 4.3 音视频-多路H265监控录放C++开发六,使用SDLVSQT显示yuv文件

使用QT 显示YUV 文件 在最后一帧的时候会不停的显示最后一帧图片。 Vsqtshowyuv.h #pragma once#include <QtWidgets/QWidget> #include "ui_vsqtshowyuv.h" #include <sdl/SDL.h> #include <iostream> #include <fstream> #include <Q…...

Spring 设计模式之适配器模式

Spring 设计模式之适配器模式 适配器模式用到的场景java举例 适配器模式 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许接口不兼容的类一起工作。 其核心思想是通过一个适配器类将不兼容的接口转换成客户端期望的另一个接口&…...

多传感器数字化分析系统

在工业飞速发展的今天&#xff0c;设备的安全稳定运行成为企业高效生产的关键因素。然而&#xff0c;传统的人工巡检方式面临着诸多挑战&#xff0c;如效率低下、漏检误检以及难以精准掌握设备运行状态等。旗晟凭借深厚的技术积累和创新精神&#xff0c;推出了多传感器数字化分…...

Java 基础教学:面向对象编程基础-封装、继承与多态

面向对象编程&#xff08;OOP&#xff09;是现代编程的重要范式&#xff0c;Java 语言提供了丰富的 OOP 特性&#xff0c;主要包括封装、继承和多态。本文将详细讲解这三个概念及其实现方式&#xff0c;并提供相应的代码示例。 1. 封装 1.1 概念 封装是将对象的状态&#xf…...

Ubuntu环境本地部署DbGate数据库管理工具并实现无公网IP远程访问

文章目录 前言1. 安装Docker2. 使用Docker拉取DbGate镜像3. 创建并启动DbGate容器4. 本地连接测试5. 公网远程访问本地DbGate容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 前言 本文主要介绍如何在Linux Ubuntu系统中使用Docker部署DbGate数…...

【AI抠图整合包及教程】Meta SAM 2:视觉分割的革命性飞跃

在人工智能的浪潮中&#xff0c;每一次技术的革新都如同一场视觉盛宴&#xff0c;让我们见证着数字时代的变迁。Meta再次以Segment Anything Model 2&#xff08;SAM 2&#xff09;引领了图像和视频分割技术的新纪元。作为首个用于实时、可提示的图像和视频对象分割的统一模型&…...

使用语言模型进行文本摘要的五个级别(llm)

视频链接&#xff1a;5 Levels Of LLM Summarizing: Novice to Expert...

ubuntu交叉编译libffi库给arm平台使用

1.下载并解压&#xff1a; 2.生成makefile 编译&#xff1a; make 编译成功&#xff1a; 安装&#xff1a; make install 安装成功 查看安装后的libffi库...

【jvm】空间分配担保策略

目录 1. 说明2. 工作原理2.1 估算新生代存活对象大小2.2 判断老年代的剩余空间2.3 触发Full GC的条件 3. 相关参数与配置3.1 -XX:HandlePromotionFailure3.2 -XX:PretenureSizeThreshold3.3 -XX:MaxTenuringThreshold3.4 -XX:TargetSurvivorRatio 4.作用与意义 1. 说明 1.在Ja…...

iQOO手机怎样将屏幕投射到MacBook?可以同步音频吗?

众所周知&#xff0c;苹果品牌的设备自己有AirPlay的投屏功能&#xff0c;iPhone要投屏到MacBook只要连接同一网络&#xff0c;然后开启AirPlay就可以投屏。但其他品牌的手机没有AirPlay&#xff0c;怎么将手机屏幕投射到MacBook呢&#xff1f; 安卓系统的手机可以使用无线投屏…...

BUU usualCrypt1

查壳&#xff0c;32bit&#xff0c;丢进ida32中进行反编译&#xff0c;简单的不多说&#xff0c;直接进main分析 简单分析&#xff0c;打上注释&#xff0c;没啥好看的&#xff0c;就一个加密函数&#xff0c;加密完后和一个字符串进行比较&#xff0c;由此可以逆推出加密前的字…...

第十七章 标准库特殊设施

17.1 tuple类型 当希望将一些数据合成单一对象&#xff0c;但又不想麻烦地定义一个新数据结构来表示这些数据时&#xff0c;tuple非常有用。tuple是类似pair的模板。 tuple<size_t, size_t, size_t> threeD; //三个成员都设置为0//为每个成员提供初始值 tuple<strin…...

【格言分享】程序员的经典名言解读

上一期文章我们分享了一些程序员的经典名言,每一句都蕴含着深刻的道理。 接下来就给大家一个一个分析一下 这些格言确实捕捉到了编程和软件开发的精髓,每一条都蕴含着丰富的经验和智慧。下面我将逐一解释这些格言,并分享一些我的看法。 C程序员永远不会灭亡。他们只是cast…...

SpringBoot接收LocalDateTime参数

一、通过RequestBody接收 方式1&#xff1a;实体类上加上 JsonFormat&#xff0c;并通过 pattern 属性指定时间格式 public class Time {JsonFormat(pattern "yyyy-MM-dd HH:mm:ss")LocalDateTime localDateTime;JsonFormat(pattern "yyyy-MM-dd")Loca…...

Typora配置GitHub图床--结合PicGo

【当前问题】Typora文档分享时 无法看到本地路径图片 【怎么解决】把文档中的图片设置为 公开链接 【准备工具】 Typora 官网https://typoraio.cn/&#xff08;购买 / 自寻破解法&#xff09;GitHub账号 https://github.com/PicGo https://github.com/Molunerfinn/PicGo/relea…...

【书生.浦语实战营】——入门岛

【书生.浦语实战营】——入门岛_第一关_Linux基础 任务分布1. 本地vscode远程连接并进行端口映射端口映射What——何为端口映射How——怎么进行端口映射 2. Linux基础命令touch &#xff1a;创建文件mkdir &#xff1a;创建目录cd:进入 退出 目录pwd :确定当前所在目录cat:可以…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...