AI项目落地实战:SpringBoot3+SpringAI+Uniapp

news2024/11/15 17:26:02

前言

AI不仅仅是风口,也是今后的时代潮流。本人花心血开发了一套AI实战项目,可商用。支持h5,小程序,app三端。可拿来二开,也可直接上架。不用担心版权问题,但是如果是倒卖源码,本人会追究其责任

如果你是大学生,也可以抓住这个机会学习AI,源码并不难,都是java那一套。本人也会提供免费学习指导

一. 详细功能

  • 支持文本对话(流失)+ 图片生成。
  • 支持用户创建自己的智能体。
  • 支持动态添加应用,只需简单配置就能生成一个新的AI应用,无需任何代码编写。
  • 动态调优应用的prompt提示词,历史对话轮数,System Role等大模型属性。
  • 内置应用的提示词都是大厂优化的提示词,有相当高的参考价值。
  • 支持用户自己切换大模型,可以任意切换成qwen,gpt4,coze等大模型厂商。
  • 支持插件模式。动态对接新的大模型API(无需重启应用就可以新增一个大模型api的对接)
  • 动态修改大模型属性:base-url和token
  • 支持用户会员模式(包月)+ 非会员模式
  • 实现了邮箱验证码登录

应用部分截图

视频演示功能

AI项目落地实战:SpringBoot3+SpringAI+

二. 技术架构

前台

  • uniapp
  • websocket

后台

  • SpringBoot3.3.0
  • 大模型底座: SpringAI + 通义千问 + Coze
  • JDK17
  • Sa Token
  • Websocket
  • MyBatisPlus+MySQL

三. 数据库表

四. 代码搭建步骤

用mysql新建一个数据库ai-waiter,然后执行语句:ai-waiter.sql。

修改application.yml配置

主要是修改邮箱服务器信息和数据库地址信息。

然后启动main函数:AiWaiterAppApplication

项目会监听两个协议,http协议+ws协议,端口都是 8999。

前端启动直接用Hbuilder导入项目,然后编译运行即可。

注意: 初始化的大模型只有3.5,需要增加4的token的可以联系小编。

五. 主要技术点讲解(开发人员)

插件开发

背景: 市面上有很多种大模型api。比如:openai,千问api,扣子api,讯飞星火等, 插件开发就是为了更好的分离对接代码,以及做到 不重启服务 就可以实现对接各大厂商api。

系统内置插件 OpenAIModelPlugin

系统源代码内置了一个SpringAI实现的插件: com. aiwaiter. openai. plugins. sys.OpenAIModelPlugin

public class OpenAIModelPlugin extends AbstractAIModelPlugin {
    @Override
    public void streamcall(Configure configure) {
        System.out.println("请求 openai-AI>> " + configure);
        OpenAiApi api = new OpenAiApi(configure.getBaseUrl(), configure.getToken());
        OpenAiChatOptions options = OpenAiChatOptions.builder().withModel(configure.getModel()).withTemperature(0.7f) //温度系数
                .build();
        ChatModel myChatModel = new OpenAiChatModel(api, options);
        ChatClient.Builder builder = ChatClient.builder(myChatModel);
        builder.defaultSystem(configure.getRole());
        ChatClient chatClient = builder.build();
        // 构建用户消息
        String curmsg = getUserText(configure);
        List<Message> messages = new ArrayList<>();
        if(!CollectionUtils.isEmpty(configure.getHistory())){
            // 添加历史消息
            for(DTO.History his : configure.getHistory()){
                if(!StringUtils.isEmpty(his.getBotContent())){
                    messages.add(new AssistantMessage(his.getBotContent()));
                    continue;
                }
                messages.add(new UserMessage(his.getUserContent()));
            }
        }
        messages.add(new UserMessage(curmsg)) ;
        Flux<ChatResponse> stream = chatClient.prompt().messages(messages).stream().chatResponse();
        stream.toStream().forEach(response -> {
            response.getResults().forEach(item -> {
                AssistantMessage output = item.getOutput();
                boolean finish = false;
                if (output.getMetadata().containsKey("finishReason") && output.getMetadata().get("finishReason").equals("STOP")) {
                    finish = true;
                    configure.getListerner().onCall("", finish);
                    return;
                }
                configure.getListerner().onCall(output.getContent(), finish);
            });
        });
    }

    @Override
    public void textToImge(Configure configure) {
        System.out.println("请求AI>> " + configure);
        OpenAiImageApi api = new OpenAiImageApi(configure.getBaseUrl(), configure.getToken(), RestClient.builder());
        OpenAiImageModel model = new OpenAiImageModel(api);
        ImageOptions options = ImageOptionsBuilder.builder()
                .withModel(OpenAiImageApi.ImageModel.DALL_E_3.getValue())
                .build() ;
        ImagePrompt imagePrompt = new ImagePrompt(getUserText(configure) , options);
        final String url = model.call(imagePrompt).getResult().getOutput().getUrl();
        configure.getListerner().onCall(url, true);
    }

}

自定定义插件

插件类必须继承 AbstractAIModelPlugin , 并且必须要实现下面两个方法:

    /***
     * 流式文字返回
     * @param configure
     */
    void streamcall(Configure configure);

    /***
     * 文字生成图片
     * @param configure
     * @return
     */
    void textToImge(Configure configure);

如果要设置流失回调监听器,只需要设置Configure类里面的StreamCallBackListerner即可监听流失回调:

Configure.StreamCallBackListerner

我们用编辑器写好插件代码,比如我写了一个对接千问api的插件

package com.aiwaiter.openai.plugins.sys;

import com.aiwaiter.openai.plugins.AbstractAIModelPlugin;
import com.aiwaiter.openai.Configure;
import com.aiwaiter.websocket.dto.DTO;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.utils.Constants;
import io.reactivex.Flowable;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @className: com.aiwaiter.openai.plugins.sys.QwenAIModelPlugin
 * @author: WX:hadluo QQ:657455400
 * @date: 2024/7/24 15:43
 * @Version: 1.0
 * @description:
 */
public class QwenAIModelPlugin extends AbstractAIModelPlugin {
    @Override
    public void streamcall(Configure configure) {
        System.out.println("请求 qwen-AI>> " + configure);
        Constants.apiKey = configure.getToken();
        // 系统角色
        Message sysMsg = Message.builder().role(Role.SYSTEM.getValue()).content(configure.getRole()).build();
        // 用户消息
        Message userMsg = Message.builder().role(Role.USER.getValue()).content(getUserText(configure)).build();
        List<Message> messages = new ArrayList<>();
        // 构建历史消息
        if(configure.getHistory() != null && !configure.getHistory().isEmpty()){
            // 添加历史消息
            for(DTO.History his : configure.getHistory()){
                if(!StringUtils.isEmpty(his.getBotContent())){
                    messages.add( Message.builder().role(Role.ASSISTANT.getValue()).content(his.getBotContent()).build());
                    continue;
                }
                messages.add( Message.builder().role(Role.USER.getValue()).content(his.getUserContent()).build());
            }
        }
        messages.add( Message.builder().role(Role.USER.getValue()).content(getUserText(configure)).build());
        GenerationParam param = GenerationParam.builder().model("qwen-turbo").messages(Arrays.asList(sysMsg, userMsg)).resultFormat(GenerationParam.ResultFormat.MESSAGE).topP(0.8).incrementalOutput(true).build();
        Generation gen = new Generation();
        try {
            Flowable<GenerationResult> result = gen.streamCall(param);
            result.blockingForEach(message -> {
                String finishReason = message.getOutput().getChoices().get(0).getFinishReason() ;
                boolean finish =false ;
                if(!StringUtils.isEmpty(finishReason) && "stop".equals(finishReason)){
                    finish = true ;
                }
                String centent = message.getOutput().getChoices().get(0).getMessage().getContent();
                configure.getListerner().onCall(centent , finish);
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void textToImge(Configure configure) {
        //千问无法生成图片
    }
}

写好之后,我们测试没有问题,就将代码插入数据库 t_ai_robot 表中plugin

CREATE TABLE `t_ai_robot` (
  `robot_id` int(10) unsigned NOT NULL auto_increment COMMENT '自增主键',
  `token` varchar(400) default NULL COMMENT 'token',
  `bot_id` varchar(100) NOT NULL COMMENT 'bot_id',
  `model` varchar(255) default '' COMMENT '大模型底座',
  `plugin` mediumtext COMMENT '插件实现代码',
  `plugin_class` varchar(255) default NULL COMMENT '插件的全类名',
  `plugin_version` int(10) default NULL COMMENT '版本号,自定义插件必须从1开始递增,修改了plugin,必须新增版本号',
  `base_url` varchar(255) default '' COMMENT '请求地址',
  `acct` varchar(255) default NULL,
  `pwd` varchar(255) default NULL,
  PRIMARY KEY  USING BTREE (`robot_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='机器人表';

这样就实现了新增千问对接。如果您后续修改了插件代码,只需要把表中的 plugin_version 版本号字段升级即可。

动态修改token和base-url

也是直接对 t_ai_robot 表进行操作,token字段就是api的key,base_url就是请求的地址。

动态添加,修改应用

是对t_ai_app应用表操作。里面关键字段是大模型的属性:

  • role: 传给大模型的 System 角色。
  • prompt: 传给大模型的UserMessage。此变量在传给大模型之前会将 ${usertext} 替换为用户输入的内容。
  • history_count : 传给大模型的历史对话轮数,为了节省token词,有最大值,在application.yml中配置。

动态调优提示词

只需要修改t_ai_app表的role和prompt字段即可。

提示词

比如内置的诗歌提示词

Role: 诗人
Profile
Author: YZFly
Version: 0.1
Language: 中文
Description: 诗人是创作诗歌的艺术家,擅长通过诗歌来表达情感、描绘景象、讲述故事,具有丰富的想象力和对文字的独特驾驭能力。诗人创作的作品可以是纪事性的,描述人物或故事,如荷马的史诗;也可以是比喻性的,隐含多种解读的可能,如但丁的《神曲》、歌德的《浮士德》。
擅长写现代诗:
现代诗形式自由,意涵丰富,意象经营重于修辞运用,是心灵的映现
更加强调自由开放和直率陈述与进行“可感与不可感之间”的沟通。
擅长写七言律诗
七言体是古代诗歌体裁
全篇每句七字或以七字句为主的诗体
它起于汉族民间歌谣
擅长写五言诗
全篇由五字句构成的诗
能够更灵活细致地抒情和叙事
在音节上,奇偶相配,富于音乐美
Rules
内容健康,积极向上
七言律诗和五言诗要押韵
Workflow
让用户以 "形式:[], 主题:[]" 的方式指定诗歌形式,主题。
针对用户给定的主题,创作诗歌,包括题目和诗句。
Initialization
作为角色 , 严格遵守 , 使用默认 与用户对话,友好的欢迎用户。然后介绍自己,并告诉用户 。

用户自创智能体

用户自己创建的智能体也是在 t_ai_app表里面,但是类目id必须是21 ,此值不能更改。与用户的关联表为:t_user_robot, 比如我创建的智能体:

整个代码截图

前端代码

前端代码就是通过uniapp实现的,没什么好讲的。截图吧

结尾语

如果您对项目感兴趣,请联系小编,如果您有AI的需求也可以找小编,欢迎咨询。

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

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

相关文章

【Linux网络】传输层协议:UDP 与 TCP

本篇博客整理了 TCP/IP 分层模型中传输层的 UDP 协议和 TCP 协议&#xff0c;旨在让读者更加深入理解网络协议栈的设计和网络编程。 目录 一、传输层 1&#xff09;端口号 Port .1- 五元组标识一个通信 .2- 端口号的作用 .3- 范围划分 2&#xff09;指令 netstat、iostat…

python的jieba库中文分词词频统计和合并

可能在设置问题模板的时候需要分析已有问句&#xff0c;然后统计词频&#xff0c;根据词频设计问题模板

B站宋红康JAVA基础视频教程个人笔记chapter02

文章目录 1.什么是标识符&#xff1f; 凡是自己取得名字都叫做标识符(类名&#xff0c;变量名&#xff0c;函数名等等) 2.标识符命名规范 包名&#xff1a;多单词组成所有字母都小写 --com.example.xxx 类名&#xff1a;多单词组成&#xff0c;所有单词的首字母大写 --MyClass …

手把手教你调用百度全球逆地理编码API接口-文末附带多种编程语言的完成版本

为什么要使用全球逆地理编码 在工作中有的时候需要根据经纬度转为对应位置信息&#xff0c;今天就遇到了这么一个业务场景&#xff0c;最终实现&#xff01;&#xff01;&#xff01; 接下来手把手教你get新技能哦&#xff0c;话不多说&#xff0c;开整&#xff01;&#xff0…

59简单学生管理系统【功能实现((完善注册、登录、详情页)、记住我 、安全退出、修改密码、修改学生信息、修改老师信息)】、JSP

功能实现 什么是JSP htmljava 5.完善注册、登录、详情页 html页面改用jsp 使用&#xff1a;<%java代码%>&#xff0c;展示用<%“行了”%> 注册 修改RegisterServlet 改用转发方式跳转 原因&#xff1a;注意重定向是两次请求&#xff0c;存在第一次请求中的数据会在…

HarmonyOS APP如何扩展应用功能?

前言 服务模块属于一个应用里面可以拓展的地方&#xff0c;三方服务或者新增加的服务全部可以放进来&#xff0c;这里用的核心组件就是Grid组件 一般的步骤 编写一个Gird的子组件item&#xff0c;这个item一般有图标和文字按照上下进行排列编写一个Grid的组件单元&#xff0c…

【优秀python大屏案例】基于python flask的前程无忧大数据岗位分析可视化大屏设计与实现

随着大数据和人工智能技术的迅猛发展&#xff0c;数据分析和可视化在各个行业中的应用越来越广泛。特别是在招聘领域&#xff0c;大数据分析不仅能够帮助企业更好地了解市场需求&#xff0c;还能为求职者提供科学的职业规划建议。本文探讨了基于Python Flask框架的前程无忧大数…

高效抓取网页模板:Go 1.19站点模板爬虫实战指南

基于Go 1.19的站点模板爬虫的原理是通过发送HTTP请求获取网页内容&#xff0c;然后使用正则表达式或HTML解析库来提取其中的模板内容。这种爬虫可以应用于以下几个方面&#xff1a; 数据抓取&#xff1a;通过爬取站点的模板内容&#xff0c;可以获取所需的数据&#xff0c;例如…

智谱AI张帆:大模型时代构建企业竞争力的四个维度

大模型并不是简单的对话&#xff0c;它润物细无声地贯穿整个服务体系。 7月27日&#xff0c;由《中国企业家》杂志社主办的2024&#xff08;第二十四届&#xff09;中国企业未来之星年会在上海举行。在“前沿微课”环节&#xff0c;智谱AI COO张帆进行了名为“大模型的企业级应…

RCE(远程代码执行漏洞)原理及漏洞利用

作用 RCE漏洞&#xff0c;可以让攻击者直接向后台服务器远程注入操作系统命令或者代码&#xff0c;从而控制后台系统。 原理 远程系统命令执行 一般出现这种漏洞&#xff0c;是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口。比如我们常见的路由器、防火墙、入…

揭秘循环购模式:消费即收益

大家好&#xff0c;我是你们的电商策略顾问吴军。今天&#xff0c;我将带大家深入探索一种别开生面的商业模式——循环购模式。这种模式究竟有何魅力&#xff0c;能让消费者在享受购物乐趣的同时&#xff0c;还能获得额外的收益&#xff1f;更有趣的是&#xff0c;一些商家通过…

精准防控,高效管理:AI智能分析网关V4区域未停留检测算法的介绍及应用

一、区域未停留AI检测算法概述 随着人工智能和计算机视觉技术的飞速发展&#xff0c;区域未停留AI检测算法作为一种重要的视频分析技术&#xff0c;逐渐在各个领域得到广泛应用。该算法通过高效处理视频流数据&#xff0c;能够实时分析并判断目标对象是否在预设区域内有足够的…

influxdb 读取本地csv

官方链接 Query CSV data sources | Flux Documentation (influxdata.com) 我的是windows版本的OSS&#xff0c;如果直接把csv放在文件夹路径下读取会报错&#xff0c;官方也写了&#xff0c;这样是无法读取的 所以用了grafana开的web服务来实现读取csv的目的。 import &qu…

ts踩坑!在 Vue.js 中使用 Element Plus 组件库时,组件属性值所传类型需要与组件期望的类型一致。

在 Vue.js 中使用 Element Plus 组件库时&#xff0c;用el-tag组件举例&#xff0c; 属性值所传类型需要与组件期望的类型一致。 //当我们简单定义 举例&#xff1a;let tagType:string 或者&#xff1a; let tagType ref<string>()然后给el-tag的type属性赋值时&…

数据驱动决策:内容数据产品经理的成长与价值

数据驱动决策&#xff1a;内容数据产品经理的成长与价值 内容数据产品经理以数据为媒介&#xff0c;在用户与决策之间搭建桥梁&#xff0c;通过理解分析模型和用户决策路径&#xff0c;设计产品以促成更多决策产出&#xff0c;创造用户价值。例如&#xff0c;在衡量数据产品经理…

列表的单选和多选以及联动

<template><view><cu-custom bgColor"bg-gradual-white" :isBack"true"><block slot"backText">我的收藏</block></cu-custom><view class"top"><view>加油站</view><view …

地图下载软件与网站收藏(持续更新...)

慢慢记录总结学习工作中用到的有关地图下载的工具和网站 工具 图新地球 中科图新旗下的gis软件&#xff0c;官方地址&#xff1a;传送门 中文软件直接用就行&#xff0c;可以自行配置相关图源&#xff0c;有需要的化可以私信我。 全能地图下载器 Global Mapper Global Ma…

龙腾码支付系统三网免挂个人免签支付兼容易支付带详细教程

一、系统简介 码支付是基于ThinkPhp5.0 FastAdmin 开发的一套新型聚合收款、聚合支付系统&#xff0c;是一款专业的聚合免签收款系统,无需对接其余平台,个码就可收款,灰常的方便快捷,集成实现三网免挂功能,无需挂繁琐的监控软件就可实现回调,更便捷的监控方式,更优的产品质量,更…

苹果电脑上可以使用的快捷回复工具有哪些

很多客服都用苹果电脑回复客户提问&#xff0c;那么苹果电脑上都有哪些工具&#xff0c;可以实现快捷回复呢 摘要 目前&#xff0c;很多客服都用苹果电脑回复客户提问&#xff0c;快捷回复软件就是客服的刚需。众所周知&#xff0c;苹果电脑MAC系统的应用不如Windows系统多&am…

计算机毕业设计选题推荐-课程教学辅助系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…