SpringBoot 接入讯飞星火大模型实现对话

申请地址

https://xinghuo.xfyun.cn/sparkapi?scr=price
免费申请200万Token

开发文档

https://www.xfyun.cn/doc/spark/Web.html#_1-接口说明

页面最下面有相关demo可以参考

SpringBoot 接入讯飞星火大模型实现对话_第1张图片

介绍

接口是以套接字的形式分段返回,而且非http请求,比较繁琐,官方也只给了比较简单的deom。

依赖项

  
        <dependency>
            <groupId>com.squareup.okhttp3groupId>
            <artifactId>okhttpartifactId>
        dependency>

     
            <dependency>
                <groupId>com.alibaba.fastjson2groupId>
                <artifactId>fastjson2artifactId>
            dependency>

配置文件

xunfei:
    ai:
        hostUrl: https://spark-api.xf-yun.com/v3.5/chat
        appId: xxx
        apiSecret: xxx
        apiKey: xxx

控制台上可以查看 API认证字符串

SpringBoot 接入讯飞星火大模型实现对话_第2张图片

读取配置文件

@Value("${xunfei.ai.hostUrl}")
private  String  hostUrl;

@Value("${xunfei.ai.appId}")
private  String appId;

@Value("${xunfei.ai.apiSecret}")
private  String apiSecret;

@Value("${xunfei.ai.apiKey}")
private  String apiKey;

权限校验

得到的是一个url,需要将http替换成ws

/**
     * 权限校验
     * @return String
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws MalformedURLException
     */
    private String getAuthUrl() throws NoSuchAlgorithmException, InvalidKeyException, MalformedURLException {
        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();
    }

构建请求参数

请求参数是与json格式进行发送,如果需要结合之前的信息继续回答需要携带历史记录。在官方的api文档也可以查看

#参数构造示例如下

{
        "header": {
            "app_id": "12345",
            "uid": "12345"
        },
        "parameter": {
            "chat": {
                "domain": "generalv3.5",
                "temperature": 0.5,
                "max_tokens": 1024, 
            }
        },
        "payload": {
            "message": {
                # 如果想获取结合上下文的回答,需要开发者每次将历史问答信息一起传给服务端,如下示例
                # 注意:text里面的所有content内容加一起的tokens需要控制在8192以内,开发者如有较长对话需求,需要适当裁剪历史信息
                "text": [
                    {"role":"system","content":"你现在扮演李白,你豪情万丈,狂放不羁;接下来请用李白的口吻和用户对话。"} #设置对话背景或者模型角色
                    {"role": "user", "content": "你是谁"} # 用户的历史问题
                    {"role": "assistant", "content": "....."}  # AI的历史回答结果
                    # ....... 省略的历史对话
                    {"role": "user", "content": "你会做什么"}  # 最新的一条问题,如无需上下文,可只传最新一条问题
                ]
        }
    }
}

JAVA构建


    private    String buildBody(String text,String uid){
        JSONObject body =new JSONObject();

       JSONObject header =new JSONObject();
        header.put("app_id",appId);
        header.put("uid",uid);
        body.put("header",header);

        JSONObject parameter =new JSONObject();
        JSONObject chat =new JSONObject();
        chat.put("domain","generalv3.5");
        parameter.put("chat",chat);

        body.put("parameter",parameter);

        JSONObject history =JSONObject.parseObject(text);
        body.put("payload",history);

        JSONObject back =new JSONObject();
        back.put("role","system");
        back.put("content","请回答我关于一些生产安全的内容");
        //定义会话背景
        
        history.getJSONObject("message").getJSONArray("text").add(0,back);
        return body.toJSONString();
    }

回复消息

基于OKHTTP3的请求库,连接websocket

 /**
     * 回复消息
     * @param text
     * @return
     * @throws MalformedURLException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    public String answer(String text,String uid) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, ExecutionException, InterruptedException, TimeoutException {
        String authUrl =getAuthUrl().replace("http://", "ws://").replace("https://", "wss://");
        Request request = new Request.Builder().url(authUrl).build();
        OkHttpClient client = new OkHttpClient.Builder().build();
        StringBuilder sb =new StringBuilder();
        CompletableFuture<String> messageReceived = new CompletableFuture<>();
        String body = buildBody(text,uid);
        WebSocket webSocket =client.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
            webSocket.send(body);
            //发送消息
            }
            @Override
            public void onMessage(WebSocket webSocket, String text) {
                JSONObject obj = JSON.parseObject(text);
                  String str= obj.getJSONObject("payload").getJSONObject("choices").getJSONArray("text").getJSONObject(0).getString("content");
                  sb.append(str);
                  if(obj.getJSONObject("header").getLong("status")==2){
                      webSocket.close(1000, "Closing WebSocket connection");
                      messageReceived.complete(text); // 将收到的消息传递给 CompletableFuture
                  }
            }

        } );
        String result = messageReceived.get(30, TimeUnit.SECONDS);; // 阻塞等待消息返回
        webSocket.close(1000, "Closing WebSocket connection");
        return sb.toString();
    }

Controller

   @PostMapping("/chat")
    public AjaxResult chat(String text,String uid) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, ExecutionException, InterruptedException, TimeoutException {
        return  success( model.answer(text,uid));
    }

运行效果

SpringBoot 接入讯飞星火大模型实现对话_第3张图片

完整代码

Controller层

@RestController
@RequestMapping("/course")
public class QueryController extends BaseController {
    @Autowired
    private CognitiveMode model;
    
    @PostMapping("/chat")
    public AjaxResult chat(String text,String uid) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, ExecutionException, InterruptedException, TimeoutException {
        return  success( model.answer(text,uid));
    }


}
package com.ruoyi.framework.ai;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Component
public class CognitiveMode {
    @Value("${xunfei.ai.hostUrl}")
    private  String  hostUrl;
    @Value("${xunfei.ai.appId}")
    private  String appId;
    @Value("${xunfei.ai.apiSecret}")
    private  String apiSecret;
    @Value("${xunfei.ai.apiKey}")
    private  String apiKey;



    /**
     * 回复消息
     * @param text
     * @return
     * @throws MalformedURLException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    public String answer(String text,String uid) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, ExecutionException, InterruptedException, TimeoutException {
        String authUrl =getAuthUrl().replace("http://", "ws://").replace("https://", "wss://");
        Request request = new Request.Builder().url(authUrl).build();
        OkHttpClient client = new OkHttpClient.Builder().build();
        StringBuilder sb =new StringBuilder();
        CompletableFuture<String> messageReceived = new CompletableFuture<>();
        String body = buildBody(text,uid);
        WebSocket webSocket =client.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
            webSocket.send(body);
            }
            @Override
            public void onMessage(WebSocket webSocket, String text) {
                JSONObject obj = JSON.parseObject(text);
                  String str= obj.getJSONObject("payload").getJSONObject("choices").getJSONArray("text").getJSONObject(0).getString("content");
                  sb.append(str);
                  if(obj.getJSONObject("header").getLong("status")==2){
                      webSocket.close(1000, "Closing WebSocket connection");
                      messageReceived.complete(text); // 将收到的消息传递给 CompletableFuture
                  }
            }

        } );
        String result = messageReceived.get(30, TimeUnit.SECONDS);; // 阻塞等待消息返回
        webSocket.close(1000, "Closing WebSocket connection");
        return sb.toString();
    }

    private    String buildBody(String text,String uid){
        JSONObject body =new JSONObject();

       JSONObject header =new JSONObject();
        header.put("app_id",appId);
        header.put("uid",uid);
        body.put("header",header);

        JSONObject parameter =new JSONObject();
        JSONObject chat =new JSONObject();
        chat.put("domain","generalv3.5");
        parameter.put("chat",chat);

        body.put("parameter",parameter);

        JSONObject history =JSONObject.parseObject(text);
        body.put("payload",history);

        JSONObject back =new JSONObject();
        back.put("role","system");
        back.put("content","请回答我关于一些xxx的内容");
        history.getJSONObject("message").getJSONArray("text").add(0,back);



        return body.toJSONString();
    }


    /**
     * 权限校验
     * @return String
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws MalformedURLException
     */
    private String getAuthUrl() throws NoSuchAlgorithmException, InvalidKeyException, MalformedURLException {
        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();
    }

}

你可能感兴趣的:(Spring,Boot,spring,boot,后端,java,spring,maven,servlet,tomcat)