SpringBoot接入星火认知大模型

news2024/11/24 13:55:47

文章目录

  • 准备工作
  • 整体思路
  • 接入大模型
    • 服务端和大模型连接
    • 客户端和服务端的连接
    • 测试

准备工作

  1. 到讯飞星火大模型上根据官方的提示申请tokens
    申请成功后可以获得对应的secret,key还有之前创建的应用的appId,这些就是我们要用到的信息

    在这里插入图片描述

  2. 搭建项目

整体思路

考虑到敏感信息等安全性问题,这里和大模型的交互都放到后端去做。
在这里插入图片描述

客户端,服务端,星火大模型均通过Websocket的方式建立连接,用户询问问题时向SpringBoot服务端发送消息,服务端接收到后,创建与星火大模型的连接,并访问大模型,获取到请求结果后发送给客户端

如果想实现根据上下文问答,就要把历史问题和历史回答结果全部传回大模型服务端

请求参数的构建和响应参数解析参照官方文档Web API文档

接入大模型

服务端和大模型连接

/**
 * 与大模型建立Socket连接
 *
 * @author gwj
 */
@Slf4j
public class BigModelNew extends WebSocketListener {

    public static final String appid = "appid";

    // 对话历史存储集合
    public static Map<Long,List<RoleContent>> hisMap = new ConcurrentHashMap<>();

    public static String totalAnswer = ""; // 大模型的答案汇总

    private static String newAsk = "";

    public static synchronized void ask(String question) {
        newAsk = question;
    }

    public static final Gson gson = new Gson();

    // 项目中需要用到的参数
    private Long userId;
    private Boolean wsCloseFlag;


    // 构造函数
    public BigModelNew(Long userId, Boolean wsCloseFlag) {
        this.userId = userId;
        this.wsCloseFlag = wsCloseFlag;
    }

    // 由于历史记录最大上线1.2W左右,需要判断是能能加入历史
    public boolean canAddHistory() {
        int len = 0;
        List<RoleContent> list = hisMap.get(userId);
        for (RoleContent temp : list) {
            len = len + temp.getContent().length();
        }
        if (len > 12000) {
            list.remove(0);
            list.remove(1);
            list.remove(2);
            list.remove(3);
            list.remove(4);
            return false;
        } else {
            return true;
        }
    }

    // 线程来发送参数
    class ModelThread extends Thread {
        private WebSocket webSocket;
        private Long userId;

        public ModelThread(WebSocket webSocket, Long userId) {
            this.webSocket = webSocket;
            this.userId = userId;
        }

        public void run() {
            try {
                JSONObject requestJson = new JSONObject();

                JSONObject header = new JSONObject();  // header参数
                header.put("app_id", appid);
                header.put("uid", userId+UUID.randomUUID().toString().substring(0,16));

                JSONObject parameter = new JSONObject(); // parameter参数
                JSONObject chat = new JSONObject();
                chat.put("domain", "4.0Ultra");
                chat.put("temperature", 0.5);
                chat.put("max_tokens", 4096);
                parameter.put("chat", chat);

                JSONObject payload = new JSONObject(); // payload参数
                JSONObject message = new JSONObject();
                JSONArray text = new JSONArray();

                // 历史问题获取
                List<RoleContent> list = hisMap.get(userId);
                if (list != null && !list.isEmpty()) {
                    //log.info("his:{}",list);
                    for (RoleContent tempRoleContent : list) {
                        text.add(JSON.toJSON(tempRoleContent));
                    }
                }

                // 最新问题
                RoleContent roleContent = new RoleContent();
                roleContent.setRole("user");
                roleContent.setContent(newAsk);
                text.add(JSON.toJSON(roleContent));
                hisMap.computeIfAbsent(userId, k -> new ArrayList<>());
                hisMap.get(userId).add(roleContent);

                message.put("text", text);
                payload.put("message", message);

                requestJson.put("header", header);
                requestJson.put("parameter", parameter);
                requestJson.put("payload", payload);
                // System.out.println(requestJson);

                webSocket.send(requestJson.toString());
                // 等待服务端返回完毕后关闭
                while (true) {
                    // System.err.println(wsCloseFlag + "---");
                    Thread.sleep(200);
                    if (wsCloseFlag) {
                        break;
                    }
                }
                webSocket.close(1000, "");
            } catch (Exception e) {
                log.error("【大模型】发送消息错误,{}",e.getMessage());
            }
        }
    }

    @Override
    public void onOpen(WebSocket webSocket, Response response) {
        super.onOpen(webSocket, response);
        log.info("上线");
        ModelThread modelThread = new ModelThread(webSocket,userId);
        modelThread.start();
    }

    @Override
    public void onMessage(WebSocket webSocket, String text) {
        JsonParse json = gson.fromJson(text, JsonParse.class);
        if (json.getHeader().getCode() != 0) {
            log.error("发生错误,错误码为:{} sid为:{}", json.getHeader().getCode(),json.getHeader().getSid());
            //System.out.println(json);
            webSocket.close(1000, "");
        }
        List<Text> textList = json.getPayload().getChoices().getText();
        for (Text temp : textList) {
            // 向客户端发送回答信息,如有存储问答需求,在此处存储
            ModelChatEndpoint.sendMsgByUserId(userId,temp.getContent());

            totalAnswer = totalAnswer + temp.getContent();
        }
        if (json.getHeader().getStatus() == 2) {
            // 可以关闭连接,释放资源
            if (canAddHistory()) {
                RoleContent roleContent = new RoleContent();
                roleContent.setRole("assistant");
                roleContent.setContent(totalAnswer);
                hisMap.get(userId).add(roleContent);
            } else {
                hisMap.get(userId).remove(0);
                RoleContent roleContent = new RoleContent();
                roleContent.setRole("assistant");
                roleContent.setContent(totalAnswer);
                hisMap.get(userId).add(roleContent);
            }
            //收到响应后让等待的线程停止等待
            wsCloseFlag = true;
        }
    }

    @Override
    public void onFailure(WebSocket webSocket, Throwable t, Response response) {
        super.onFailure(webSocket, t, response);
        try {
            if (null != response) {
                int code = response.code();
                System.out.println("onFailure code:" + code);
                System.out.println("onFailure body:" + response.body().string());
                if (101 != code) {
                    System.out.println("connection failed");
                    System.exit(0);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    // 鉴权方法
    public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
        URL url = new URL(hostUrl);
        // 时间
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());
        // 拼接
        String preStr = "host: " + url.getHost() + "\n" + "date: " + date + "\n" + "GET " + url.getPath() + " HTTP/1.1";
        // System.err.println(preStr);
        // SHA256加密
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
        mac.init(spec);

        byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
        // Base64加密
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        // System.err.println(sha);
        // 拼接
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
        // 拼接地址
        HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().//
                addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).//
                addQueryParameter("date", date).//
                addQueryParameter("host", url.getHost()).//
                build();

        return httpUrl.toString();
    }
}

其中用来接收响应参数相关实体类

@Data
public class JsonParse {
    Header header;
    Payload payload;
}
@Data
public class Header {
    int code;
    int status;
    String sid;
}
@Data
public class Payload {
    Choices choices;
}
@Data
public class Choices {
    List<Text> text;
}
@Data
public class Text {
    String role;
    String content;
}
@Data
public class RoleContent {
    String role;
    String content;
}

客户端和服务端的连接

/**
 * 接收客户端请求
 *
 * @author gwj
 * @date 2024/10/29 16:51
 */
@ServerEndpoint(value = "/ws/model", configurator = GetUserConfigurator.class)
@Component
@Slf4j
public class ModelChatEndpoint {
    private static AtomicInteger online = new AtomicInteger(0);
    private static final ConcurrentHashMap<Long,ModelChatEndpoint> wsMap = new ConcurrentHashMap<>();

    private static BigModelConfig config;
    @Resource
    private BigModelConfig modelConfig;

    @PostConstruct
    public void init() {
        config = modelConfig;
    }

    private Session session;
    private Long userId;

    @OnOpen
    public void onOpen(EndpointConfig config, Session session) {
        String s = config.getUserProperties().get("id").toString();
        userId = Long.parseLong(s);
        this.session = session;
        wsMap.put(userId,this);
        online.incrementAndGet();
        log.info("用户{},连接成功,在线人数:{}",userId,online);
    }

    @OnClose
    public void onClose() {
        wsMap.remove(userId);
        online.incrementAndGet();
        log.info("{},退出,在线人数:{}",userId,online);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("连接出错,{}", error.getMessage());
    }

    @OnMessage
    public void onMessage(String message,Session session) throws Exception {
        BigModelNew.ask(message);
        //构建鉴权url
        String authUrl = BigModelNew.getAuthUrl(config.getHostUrl(), config.getApiKey(), config.getApiSecret());
        OkHttpClient client = new OkHttpClient.Builder().build();
        String url = authUrl.replace("http://", "ws://").replace("https://", "wss://");
        Request request = new Request.Builder().url(url).build();
        WebSocket webSocket = client.newWebSocket(request,
                new BigModelNew(this.userId, false));
        log.info("收到客户端{}的消息:{}", userId, message);
    }


    private void sendMsg(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("客户端{}发送{}失败",userId,message);
        }
    }


    /**
     * 根据userId向用户发送消息
     *
     * @param userId 用户id
     * @param message 消息
     */
    public static void sendMsgByUserId(Long userId,String message) {
        if (userId != null && wsMap.containsKey(userId)) {
            wsMap.get(userId).sendMsg(message);
        }
    }

    
}

测试

在这里插入图片描述
在这里插入图片描述

这样就简单实现了一个ai问答功能

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

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

相关文章

使用OpenAI控制大模型的输出(免费)——response_format

免费第三方api-key(硅基流动)使用OpenAI格式&#xff0c;还能控制大模型的输出格式&#xff0c;不能说真香&#xff0c;只能说 真香Plus&#xff01; API-Key领取方法看这篇教程 【1024送福利】硅基流动送2000万token啦&#xff01;撒花✿✿ 附使用教程 支持十几个免费的大模…

Databend 产品月报(2024年10月)

很高兴为您带来 Databend 2024 年 10 月的最新更新、新功能和改进&#xff01;我们希望这些增强功能对您有所帮助&#xff0c;并期待您的反馈。 Databend Cloud&#xff1a;多集群的计算集群 多集群的计算集群会根据工作负载需求自动调整计算资源&#xff0c;添加或移除集群。…

多线程编程与并发控制缓存策略负载均衡数据库优化

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…

硅谷甄选(8)spu

Spu模块 SPU(Standard Product Unit)&#xff1a;标准化产品单元。是商品信息聚合的最小单位&#xff0c;是一组可复用、易检索的标准化信息的集合&#xff0c;该集合描述了一个产品的特性。通俗点讲&#xff0c;属性值、特性相同的商品就可以称为一个SPU。 7.1 Spu模块的静态…

【Three.js】SpriteMaterial 加载图片泛白,和原图片不一致

解决方法 如上图所示&#xff0c;整体泛白了&#xff0c;解决方法如下&#xff0c;添加 material.map.colorSpace srgb const imgTexture new THREE.TextureLoader().load(imgSrc)const material new THREE.SpriteMaterial({ map: imgTexture, transparent: true, opacity:…

【高阶数据结构】红黑树的插入

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《高阶数据结构》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多《高阶数据结构》点击专栏链接查看&a…

CCNA对学历有要求吗?看看你是否有资格报考

思科认证网络助理工程师CCNA作为网络工程领域的权威认证之一&#xff0c;备受年轻人的青睐。然而&#xff0c;对于部分文化水平较低的年轻人来说&#xff0c;他们可能会有一个疑问&#xff1a;CCNA认证对学历有要求吗? 一、CCNA对学历有要求吗? 没有! 针对这一问题&#…

别再盲目选购随身WiFi了!一文教你精准挑选最适合自己的随身WiFi!随身wifi哪个牌子的最好用?

市面上随身WiFi种类繁多&#xff0c;4G/5G&#xff0c;单网设备/三网设备&#xff0c;电池款/USB款/充电宝款等难以抉择。本文旨在为你提供一份详尽的随身WiFi选购指南&#xff01; 首先&#xff0c;明确需求&#xff1a;4G还是5G&#xff1f; 日常用网&#xff0c;如浏览视频…

天锐绿盾加密软件与Ping32:信息安全领域的双子星,谁将引领加密新风尚?

在信息安全这片广袤的星空中&#xff0c;有两颗璀璨的明星格外引人注目&#xff0c;它们就是天锐绿盾加密软件和Ping32。这两款加密软件各自以其卓越的性能、全面的功能和深度的安全保障&#xff0c;赢得了众多企业的青睐。那么&#xff0c;在它们之间&#xff0c;谁将引领加密…

《探索 HarmonyOS NEXT(5.0):开启构建模块化项目架构奇幻之旅 —— 构建公共能力层》

上一篇大概说了 《探索 HarmonyOS NEXT(5.0)&#xff1a;开启构建模块化项目架构奇幻之旅 —— 构建基础特性层》&#xff0c;这一篇继续开发 构建公共能力层。 公共能力层 主要针对公共能力层的各子目录将被编译成HAR包&#xff0c;而他们只能被产品定制层和基础特性层所依赖…

开源 AI 智能名片 2+1 链动模式 S2B2C 商城小程序与私域流量圈层

摘要&#xff1a;本文探讨了私域流量圈层的特点以及其在当今时代的重要性&#xff0c;分析了开源 AI 智能名片 21 链动模式 S2B2C 商城小程序源码在私域流量圈层构建中的作用&#xff0c;阐述了产品在圈层时代被标签化的现象&#xff0c;并以实例展示了如何利用该小程序源码打造…

【网络】传输层协议TCP(中)

目录 四次挥手状态变化 流量控制 PSH标记位 URG标记位 四次挥手状态变化 之前我们讲了四次挥手的具体过程以及为什么要进行四次挥手&#xff0c;下面是四次挥手的状态变化 那么我们下面可以来验证一下CLOSE_WAIT这个状态&#xff0c;这个状态出现的条件是后调用close的一方…

11款PDF阅读器深度体验分享!你选哪一款?

不管是哪个行业还是哪个职位&#xff0c;每天处理的文件中&#xff0c;PDF格式的文档占据了相当大的比例。为了提高工作效率&#xff0c;我尝试了市面上几款流行的PDF阅读器&#xff0c;下面来和大家分享我用过的11款PDF阅读软件怎么样吧。 一、福昕PDF编辑器 直达通道&#…

Gradle篇(入门到精通)

目录 一、前言 1. 项目构建历史 1.1. 传统方式 1.2. 构建工具 1.3. Gradle 2. 初始 groovy 2.1. 什么是 Groovy 2.2. Groovy 安装环境 2.3. groovy 与 Java 对比 3. groovy 特性 3.1. 基础语法 3.2. 闭包 二、gradle 实战 1. Gradle 环境搭建 1.1. 安装 gradle …

8.FreeRTOS之软件定时器

什么是定时器&#xff1f; 简单可以理解为闹钟&#xff0c;到达指定一段时间后&#xff0c;就会响铃。 STM32 芯片自带硬件定时器&#xff0c;精度较高&#xff0c;达到定时时间后会触发中断&#xff0c;也可以生成 PWM 、输入 捕获、输出比较&#xff0c;等等&#xff0c;功…

MySQL 9从入门到性能优化-系统信息函数

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

人脸识别算法 - 专利1分析

专利CN117576758A中说明了一种人脸识别算法的整体识别流程&#xff0c;本文将对这篇文章进行详细讲解&#xff0c;并说明有创造性的算法部分。 前置知识&#xff1a;人脸识别 人脸识别技术是一种通过计算机技术和模式识别算法来识别和验证人脸的技术。它能够用于识别人脸的身份…

Linux之nfs服务器和dns服务器

NFS服务器 NFS&#xff08;Network File System&#xff0c;网络文件系统)&#xff0c;NFS服务器可以让PC将网络中的NFS服务器共享的目录挂载到本地端的文件系统中&#xff0c;而在本地端的系统 中看来&#xff0c;那个远程主机的目录就好像是自己的一个磁盘分区一样。 注&am…

第十八章 用于大型程序的工具

18.1 异常处理 异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并做出相应的处理。异常使得问题的检测与解决过程分离开来。 18.1.1 抛出异常 当执行一个throw时&#xff0c;跟在throw后面的语句将不再被执行。相反&#xff0c;程序的控制权从throw转…

红米K80入网,骁龙8至尊版加持主打极致性价比

小米年度旗舰小米 15、15Pro 已经于本周二正式发布&#xff0c;不过 4499 元与 5299 元起售价想必劝退了不少追求极致性价比的小伙伴儿。 于是有同学表示&#xff1a;接下来人民的希望就全看旗舰捍门员红米了。 好消息是&#xff0c;目前新款红米 K80 系已获入网许可&#xff0…