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

news2024/12/27 12:02:49

我这里集成后,做成了一个工具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> {
    @Override
    public Generation create() throws Exception {
        return new Generation();
    }

    @Override
    public 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");
    }

    @Override
    public 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());
        }

        // 获取token
        if (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;

    @PostConstruct
    public 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();
    }

    @PostConstruct
    public void shutdown() {
        if (dashListener != null) {
            dashListener.shutdown();
        }
    }
}

4、测试 

5、踩坑

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2229029.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

今年双11,拼多多吹“新”风

文 | 螳螂观察 作者 | 陈小江 这届双11真变了。 以前提到双11&#xff0c;不管平台、商家全都盯着价格。但今年不一样。这届双11给出了新解法——平台不再把“我的价格比你低”挂在嘴边&#xff0c;转而更关心消费者体验和为商家减负。 双11这艘大船&#xff0c;在航行到第…

005 IP地址的分类

拓扑结构如下 两台主机处于同一个网关下&#xff0c;通过ping命令检测&#xff0c;可以连通 &nbps; 拓扑结构如下 使用ping 检查两台电脑是否相通, 因为网络号不一样&#xff0c;表示两台电脑不在同一个网络&#xff0c;因此无法连通 拓扑结构如下 不在同一网络的PC要相…

记本地第一次运行seatunnel示例项目

前置 静态源码编译通过&#xff1a;https://blog.csdn.net/u011924665/article/details/143372464 参考 seatunnel官方的开发环境搭建文档&#xff1a;https://seatunnel.incubator.apache.org/zh-CN/docs/2.3.5/contribution/setup 安装scala 下载scala 去官网下载&…

《暗河传》 顺利杀青,苏棋演绎“千面鬼”慕婴引期待

近日&#xff0c;由龚俊、彭小苒、常华森、杨雨潼等一众优秀演员出演的古装武侠剧《暗河传》顺利杀青&#xff0c;00后小花苏棋饰演的“千面鬼”慕婴一角也收获了许多关注的目光。 《暗河传》凭借其精彩的剧情和庞大的粉丝基础&#xff0c;自开拍起便备受关注。在剧中&#xff…

推荐一个没有广告,可以白嫖的产品宣传册转换翻页电子书的网站

​随着数字化时代的到来&#xff0c;传统的纸质宣传册逐渐被电子书所取代。为了满足企业和个人对高效、便捷的电子宣传册制作需求&#xff0c;许多在线平台应运而生。今天&#xff0c;就让我为您推荐一个无需广告干扰、完全免费使用的在线宣传册转换翻页电子书网站——【FLBOOK…

QT 从ttf文件中读取图标

最近在做项目时&#xff0c;遇到需要显示一些特殊字符的需求&#xff0c;这些特殊字符无法从键盘敲出来&#xff0c;于是乎&#xff0c;发现可以从字体库文件ttf中读取显示。 参考博客&#xff1a;QT 图标字体类IconHelper封装支持Font Awesome 5-CSDN博客 该博客封装的很不错…

[Linux关键词]unmask,mv,dev/pts,stdin stdout stderr,echo

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;大大会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

人工智能与伦理:我们应该如何平衡科技与人性?

内容概要 在这个瞬息万变的时代&#xff0c;人工智能的迅猛发展让我们面对前所未有的伦理困境。科技进步带来了便利&#xff0c;但同时也亟需我们反思如何对待人性。尤其是在实现算法透明性时&#xff0c;我们要确保每一个决策背后都能被理解与追溯&#xff0c;这不仅是对技术…

聚水潭数据集成到MySQL的技术实操与解决方案

聚水潭数据集成到MySQL的技术案例分享 在现代企业的数据管理过程中&#xff0c;如何高效、可靠地实现不同系统之间的数据对接是一个关键问题。本案例将聚焦于“聚水谭-仓库查询单-->BI邦盈-仓库表”的数据集成方案&#xff0c;详细探讨如何通过轻易云数据集成平台&#xff…

Mybatis-03.入门-配置SQL提示

一.配置SQL提示 目前的Springboot框架在mybatis程序中编写sql语句并没有给到任何的提示信息&#xff0c;这对于开发者而言是很不友好的。因此我们需要配置SQL提示。 配置SQL提示 这样再去写SQL语句就会有提示了。 但是会发现指定表名时并没有给出提示。这是因为&#xff1a…

计算机视觉中的点算子:从零开始构建

Hey小伙伴们&#xff01;今天我们要聊的是一个非常基础但极其重要的计算机视觉技术——点算子&#xff08;Point Operators&#xff09;。点算子主要用于对图像的每个像素进行独立的处理&#xff0c;比如亮度调整、对比度增强、灰度化等。通过这些简单的操作&#xff0c;我们可…

计算机网络:网络层 —— IPv4 协议的表示方法及其编址方法

文章目录 IPv4IPv4的表示方法IPv4的编址方法分类编址A类地址B类地址C类地址可指派的地址数量一般不使用的特殊IPv4地址 划分子网编址子网掩码默认子网掩码 无分类编址方法地址掩码斜线记法无分类域间路由选择 CIDR IPv4 IPv4&#xff08;Internet Protocol version 4&#xff…

excel自定义导出实现(使用反射)

前言 项目中接到需求&#xff0c;需要对导出的字段进行自定义导出 &#xff0c;用户可在前端选择自定义导出的字段&#xff08;如图&#xff09;&#xff0c;实现过程做以下记录&#xff0c;仅供参考&#xff1b; 思路 跟前端约定好所有要导出的字段名称(headName)跟对应的…

练习LabVIEW第十七题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第十七题&#xff1a; 编写一个程序,用labview的信号生成函数产生一个三角波并显示在chart上,在编写例外一个程序读出数据…

【论文阅读】PGAN

1. WHY 问题 图像超分辨率一直是一个热门研究课题&#xff0c;具有重要的应用价值。基于生成对抗网络GAN的单幅图像超分辨率方法显示重建图像与人类视觉特征更一致。因此&#xff0c;基于 GAN 的网络优化已成为图像超分辨率的主流。然而&#xff0c;一些最新研究表明&#xf…

江协科技STM32学习- P28 USART串口数据包

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

C# Retry库

比如网络访问或硬件参数设置需要重试&#xff0c;可引用gunet上的Polly库。 同步方式&#xff08;每次重试有不同的时间间隔&#xff09; var polly Policy.Handle<Exception>().WaitAndRetry(new[] { new TimeSpan(0, 0, 1), new TimeSpan(0, 0, 2), new TimeSpan(0, …

Java避坑案例 - 线程池使用中的风险识别与应对

文章目录 线程池的基本概念创建线程池的注意事项实例1&#xff1a; newFixedThreadPool 使用无界队列&#xff0c;可能因任务积压导致 OOM实例2&#xff1a; newCachedThreadPool 会创建大量线程&#xff0c;可能因线程数量过多导致无法创建新线程。 线程池参数设置的最佳实践线…

基于SSM+微信小程序的社区垃圾回收管理系统(垃圾1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于ssm微信小程序的社区垃圾回收管理系统&#xff0c;有管理员&#xff0c;回收员&#xff0c;用户三个角色。 1、管理员功能有个人中心&#xff0c;用户管理&#xff0c;回收员管理&am…

确保组织决策权清晰的有效策略

在组织中&#xff0c;明确的决策权、有效的沟通机制、权责明确的结构是确定和维护清晰决策权的关键要素。明确的决策权确保了每个成员知道自己的职责和权限&#xff0c;有效的沟通机制促进了信息的流通和理解&#xff0c;权责明确的结构则为组织的运作提供了清晰的框架。明确的…