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> {
    @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,否则会出现限流情况

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【CSS】入门详解

你是否曾经浏览网页时&#xff0c;被一些网站精美的布局、炫酷的动画和赏心悦目的色彩所吸引&#xff1f;这背后神奇的力量就是 CSS&#xff08;层叠样式表&#xff09;。CSS 就像网页的化妆师&#xff0c;它负责网页的样式和布局&#xff0c;让原本枯燥的 HTML 结构变得生动有…

【论文分享】HashGAT-VCA:一种结合哈希函数和图注意力网络的矢量元胞自动机模型,用于城市土地利用变化模拟

本文考虑地块内部异质性&#xff0c;提出一个结合哈希函数和图注意力网络&#xff08;GAT&#xff09;的矢量元胞自动机&#xff08;VCA&#xff09;方法&#xff0c;用于研究城市土地利用变化&#xff1b;并将该模型应用于模拟深圳市2009年至2012年的城市土地利用变化&#xf…

二十、Innodb底层原理与Mysql日志机制深入剖析

文章目录 一、MySQL的内部组件结构1、Server层1.1、连接器1.2、查询缓存1.3、分析器1.4、优化器1.5、执行器 2、存储引擎层 二、Innodb底层原理与Mysql日志机制1、redo log重做日志关键参数2、binlog二进制归档日志2.1、binlog日志文件恢复数据 3、undo log回滚日志4、错误日志…

安全芯片 OPTIGA TRUST M 使用介绍与示例(基于STM32裸机)

文章目录 目的资料索引硬件电路软件框架介绍数据存储框架移植框架使用 使用示例示例地址与硬件连接通讯测试功能测试 总结 目的 OPTIGA TRUST M 是英飞凌推出的安全芯片&#xff0c;芯片通提供了很多 slot &#xff0c;用于存放各类安全证书、密钥、用户数据等&#xff0c;内置…

10. NSTableView Table 数据表格

表格是非常重要和复杂的一个控件&#xff0c;本节会用大量篇幅来把表格这东西力求讲清楚。 基本设置 表格结构 表格是 OS X 组件中为数不多采用了MVC设计模式来实现的控件&#xff0c;即tableView–dataSource–Delegate&#xff0c;这种分层架构给处理数据带来了极大的便利…

控制流与循环:掌握程序的基本控制(2/10)

目录 控制流与循环&#xff1a;掌握程序的基本控制&#xff08;2/10&#xff09; 介绍 条件语句 基本用法 示例&#xff1a;判断用户输入的数字 条件语句中的逻辑运算符 示例&#xff1a;判断年龄阶段 循环结构 for 循环 示例 1&#xff1a;遍历列表 示例 2&#xf…

Python酷库之旅-第三方库Pandas(173)

目录 一、用法精讲 796、pandas.Float32Dtype类 796-1、语法 796-2、参数 796-3、功能 796-4、返回值 796-5、说明 796-6、用法 796-6-1、数据准备 796-6-2、代码示例 796-6-3、结果输出 797、pandas.Float64Dtype类 797-1、语法 797-2、参数 797-3、功能 797-…

linux查看系统架构的命令

两种方式&#xff0c;以下以中标麒麟为示例&#xff1a; 1.cat /proc/verison Linux version 3.10.0-862.ns7_4.016.mips64el mips64el即为架构 2.uname -a 输出所有内容 Linux infosec 3.10.0-862.ns7_4.016.mips64el #1 SMP PREEMPT Mon Sep 17 16:06:31 CST 2018 mips64el…

第J8周:Inception v1算法实战与解析

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营]中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊]** &#x1f4cc; 本周任务&#xff1a; 1了解并学习图2中的卷积层运算量的计算过程&#xff08;&#x1f3d0;储备知识->卷积层运算…

内网穿透之网络层ICMP隧道

免责申明 本文仅是用于学习检测自己搭建的靶场环境有关ICMP隧道原理和攻击实验,请勿用在非法途径上,若将其用于非法目的,所造成的一切后果由您自行承担,产生的一切风险和后果与笔者无关;本文开始前请认真详细学习《‌中华人民共和国网络安全法》‌及其所在国家地区相关法规…

提升网站流量和自然排名的SEO基本知识与策略分析

内容概要 在当今数字化时代&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;成为加强网站可见度和提升流量的重要工具。SEO的基础知识包括理解搜索引擎的工作原理&#xff0c;以及如何通过优化网站内容和结构来提高自然排名。白帽SEO和黑帽SEO代表了两种截然不同的策略&a…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-27

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-27 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-27目录1. Large Language Model-based Augmentation for Imbalanced Node Classification on Text-Attributed Graphs摘要研究背…

耳背式助听器与定制式助听器,究竟该如何选?

在面对听力损失问题时&#xff0c;选择一款合适的助听器至关重要。目前&#xff0c;耳背式助听器和定制式助听器是比较常见的两种类型&#xff0c;很多人在二者之间犹豫不决。那么&#xff0c;到底应该怎么选呢&#xff1f; 一、耳背式助听器的特点 耳背式助听器形状类似香蕉&a…

论文阅读 - Pre-trained Online Contrastive Learning for Insurance Fraud Detection

Pre-trained Online Contrastive Learning for Insurance Fraud Detection| Proceedings of the AAAI Conference on Artificial Intelligence 目录 摘要 Introduction Methodology Problem Formulation Pre-trained Model for Enhanced Robustness Detecting Network a…

【STM32】程序建立模板

文章目录 STM32的开发方式建立基于库函数的工程建立工程的具体步骤具体程序举例工程架构 本篇介绍如何建立一个STM32工程 STM32工程结构比较复杂&#xff0c;需要用到的文件很多&#xff0c;并且程序代码也都是建立在工程结构的基础之上&#xff0c;所以学习如何新建一个STM32工…

Oracle视频基础1.1.4练习

1.1.4 dbb,ddabcPMON,SMON,LGWR,CKPT,DBWna5,b4,c2,d3,e1ad,a,c,b,eOracle instance,Oracle databaseSGA,background processcontrol file,data file,online redo file 以下是一篇关于 Oracle 基础习题 1.1.4 的博客&#xff1a; Oracle 基础习题解析&#xff1a;1.1.4 本篇文…

UE5 喷射背包

首选创建一个输入操作 然后在输入映射中添加&#xff0c;shift是向上飞&#xff0c;ctrl是向下飞 进入人物蓝图中编写逻辑&#xff0c;变量HaveJatpack默认true&#xff0c;Thrust为0 最后

linux进程的状态

​​​​​​​linux进程的概念 上篇我们学习了进程的概念&#xff0c;这篇我们将学习进程的状态 目录 前言 一、子进程和父进程 1、pid和ppid 2、通过系统调用创建进程-fork初识 二、进程的状态 1.Linux内核源代码 2.进程状态查看 3、Z(zombie)-僵尸进程 ​编辑 僵尸…

Linux下docker中elasticsearch与kibana的安装

他的脸红不是因为亚热带季风气候&#xff0c;而是因为那天太阳不忠&#xff0c;出卖一九九四年夏末心动。–《太平山顶》 在本篇博客中&#xff0c;我将详细介绍如何在 Linux 系统中安装并配置 Elasticsearch 和 Kibana&#xff0c;这两者是 ELK 堆栈的重要组成部分&#xff0c…

密钥管理方法DUKPT的OpenSSL代码实现Demo

目录 1 DUKPT简介 2 基本概念 2.1 BDK 2.2 KSN 2.3 IPEK 2.4 FK 2.5 TK 3 工作流程 3.1 密钥注入过程 3.2 交易过程 3.3 BDK派生IPEK过程 3.4 IPEK计算FK过程 4 演示Demo 4.1 开发环境 4.2 功能介绍 4.3 下载地址 5 在线工具 6 标准下载 1 DUKPT简介 DUKPT&a…