https://xinghuo.xfyun.cn/sparkapi?scr=price
免费申请200万Token
https://www.xfyun.cn/doc/spark/Web.html#_1-接口说明
页面最下面有相关demo可以参考
接口是以套接字的形式分段返回,而且非http请求,比较繁琐,官方也只给了比较简单的deom。
com.squareup.okhttp3 okhttp com.alibaba.fastjson2 fastjson2
xunfei: ai: hostUrl: https://spark-api.xf-yun.com/v3.5/chat appId: xxx apiSecret: xxx apiKey: xxx
控制台上可以查看 API认证字符串
@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(); CompletableFuturemessageReceived = 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(); }
@PostMapping("/chat") public AjaxResult chat(String text,String uid) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, ExecutionException, InterruptedException, TimeoutException { return success( model.answer(text,uid)); }
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(); CompletableFuturemessageReceived = 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(); } }