科大讯飞星火·Mirai QQ机器人

  • 前言

“AI大战”正在如火如荼地进行,科大讯飞作为国内知名的智能语音和人工智能企业,自然不愿落后。今年5月份,科大讯飞宣布推出了“讯飞星火认知大模型”,引起了广泛关注。近期我也获得了星火大模型的体验资格,今天我将详细介绍科大讯飞星火进入QQ的过程。

  • 成果展示

欢迎大家加入体验(812531736)

  • 官方Web文档

  • 对自己有信心的小伙伴可以先看官方文档熟悉一下流程

星火认知大模型Web文档 | 讯飞开放平台文档中心 (xfyun.cn)icon-default.png?t=N7T8https://www.xfyun.cn/doc/spark/Web.html

  • 申请星火api

  • 1.先到官网申请免费试用。

讯飞星火认知大模型-AI大语言模型-星火大模型-科大讯飞 (xfyun.cn)icon-default.png?t=N7T8https://xinghuo.xfyun.cn/sparkapi?scr=price

  • 2.星火大模型​V2.0需要和具体的应用绑定,我们需要先创建一个新应用。

  • 3.填写完成后,等待后台审核,项目紧急可以在官网联系技术支持,快速审核通过。

  • 前置教程(已出)

  • 有兴趣的小伙伴可以看前置教程更快的熟悉和上手项目
  • 1.

Mirai QQ群机器人零基础教程(1)_mirai机器人_神殊的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63754068/article/details/130995444?spm=1001.2014.3001.5502

  • 2.

Mirai QQ群机器人零基础教程(2)_神殊的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63754068/article/details/132406815?spm=1001.2014.3001.5502

  • 3.

Mirai QQ群机器人特殊篇—针对腾讯风控的教程_神殊的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63754068/article/details/132645178?spm=1001.2014.3001.5502

  • 调用流程概览

  • 1.针对星火大模型的接口对接,为了确保跨平台的兼容性,采用web方式进行接口对接。
  • 2.根据官方文档的指示,我们首先需要调用鉴权接口,以获取接口授权。
  • 3.接下来,我们使用websocket协议与服务端进行握手,并确保握手成功后,在60秒内发送请求。
  • 4.接口采用流式输出模式,因此我们需要根据返回的数据进行判断,并拼接成完整的回复信息。以下是大致的流程:

  • 鉴权URL

  • 1.鉴权说明

需要自行先在控制台创建应用,利用应用中提供的appid,APIKey, APISecret进行鉴权,生成最终请求的鉴权url。鉴权方法见下方

  • 2.鉴权参数

参数类型必须说明示例
hoststring请求的主机aichat.xf-yun.com(使用时需替换为实际使用的接口地址)
datestring当前时间戳,采用RFC1123格式,时间偏差需控制在300s内Fri, 05 May 2023 10:43:39 GMT
authorizationstringbase64编码的签名信息

参考下方生成方式

  • 3.具体鉴权过程及代码

  • 1)到控制台获取APIKey 和APISecret参数
  • 2)利用下方的date动态拼接生成字符串tmp,这里以星火url为例,实际使用需要根据具体的请求url替换host和path。
  • 3)利用hmac-sha256算法结合APISecret对上一步的tmp签名,获得签名后的摘要tmp_sha。
  • 4)将上一步的tmp_sha进行base64编码生成signature
  • 5)利用上一步生成的signature,拼接下方的字符串生成authorization_origin
  • 6)最后再将上一步的authorization_origin进行base64编码,生成最终的authorization
  • 7)将鉴权参数组合成最终的键值对生成最终的握手url。小伙伴可先根据步骤一步步进行参数校验,确保生成的参数无误。
//星火url鉴权
    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";
        // 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);
        // 拼接
        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();
    }
  • WebSocket通信过程

  • 1.首先,你需要准备好一些必要的参数,包括hostUrl、apiKey和apiSecret。

  • 2.然后,你可以调用getAuthUrl方法来生成鉴权URL,该方法使用了之前提到的一系列步骤,包括动态拼接字符串、进行签名和编码等操作。

  • 3.接下来,你需要创建一个OkHttpClient实例,并且将鉴权URL进行一些替换操作,将其转换为WebSocket的URL。

  • 4.构建一个WebSocket的请求对象,并且传入URL。

  • 5.创建一个CountDownLatch对象,并将其初始化为1。

  • 6.实现WebSocketListener的几个回调方法,包括onOpen、onMessage等。在onOpen方法中,你可以发送你的问题到服务器端。

  • 7.在onMessage方法中,你可以解析服务器返回的消息,并将答案添加到answerList中。如果返回的消息中的header.status为2,表示对话结束,此时关闭WebSocket并释放CountDownLatch。

  • 8.调用latch.await()方法来等待CountDownLatch为0,确保WebSocket连接完成。

  • 9.最后,将answerList转换为数组并返回。

    //星火
    public static String[] Spark(String question) throws Exception {
        final List<String> answerList = new ArrayList<>();
        String hostUrl = "https://spark-api.xf-yun.com/v2.1/chat";
        String apiKey = "";
        String apiSecret = "";
        String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
        String appid = "cc36e4de";
        OkHttpClient client = new OkHttpClient.Builder().build();
        String url = authUrl.replace("http://", "ws://").replace("https://", "wss://");
        Request request = new Request.Builder().url(url).build();
        CountDownLatch latch = new CountDownLatch(1);

        WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                // 发送问题
                JsonObject requestJson = new JsonObject();

                JsonObject header = new JsonObject();
                header.addProperty("app_id", appid);
                header.addProperty("uid", UUID.randomUUID().toString().substring(0, 10));

                JsonObject parameter = new JsonObject();
                JsonObject chat = new JsonObject();
                chat.addProperty("domain", "generalv2");
                chat.addProperty("temperature", 0.5);
                chat.addProperty("max_tokens", 4096);
                parameter.add("chat", chat);

                JsonObject payload = new JsonObject();
                JsonObject message = new JsonObject();
                JsonArray text = new JsonArray();
                RoleContent roleContent = new RoleContent();
                roleContent.role = "user";
                roleContent.content = question;
                text.add(new Gson().toJsonTree(roleContent));
                message.add("text", text);
                payload.add("message", message);
                requestJson.add("header", header);
                requestJson.add("parameter", parameter);
                requestJson.add("payload", payload);

                String jsonString = new Gson().toJson(requestJson);
                webSocket.send(jsonString);
            }

            @Override
            public void onMessage(WebSocket webSocket, String text) {
                Gson gson = new Gson();
                JsonParse myJsonParse = gson.fromJson(text, JsonParse.class);
                if (myJsonParse.header.code != 0) {
                    System.out.println("发生错误,错误码为:" + myJsonParse.header.code);
                    System.out.println("本次请求的sid为:" + myJsonParse.header.sid);
                    webSocket.close(1000, "");
                }
                List<Text> textList = myJsonParse.payload.choices.text;
                for (Text temp : textList) {
                    answerList.add(temp.content);
                }
                if (myJsonParse.header.status == 2) {
                    webSocket.close(1000, "");
                    latch.countDown();
                }
            }
        });
        latch.await();
        String[] answers = new String[answerList.size()];
        return answerList.toArray(answers);
    }
  • 辅助方法和类

  • RoleContent:表示对话中的角色和内容,包含rolecontent属性。

  • JsonParse:表示JSON数据的解析结果,包含headerpayload属性。

  • Header:表示JSON数据中的头部信息,包含codestatussid属性。

  • Payload:表示JSON数据中的有效负载信息,包含choices属性。

  • Choices:表示对话中的选项,包含一个text属性,它是一个Text对象的列表。

  • Text:表示对话中的文本内容,包含一个content属性。

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

  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值