?????歡迎光臨????
??我是蘇澤,一位對技術(shù)充滿熱情的探索者和分享者。????
??特別推薦給大家我的最新專欄《Spring 狂野之旅:底層原理高級進(jìn)階》 ??
本專欄純屬為愛發(fā)電永久免費(fèi)?。?!
這是蘇澤的個(gè)人主頁可以看到我其他的內(nèi)容哦????
努力的蘇澤http://suzee.blog.csdn.net/
大家好這里還是蘇澤,關(guān)于我的Spring狂野之旅已經(jīng)出了5期,基本都是從Spring的底層源碼去讀它,不僅能學(xué)會(huì)使用方法又能理解其工作機(jī)制以及原理,我認(rèn)為這是非常美妙的一件事,這幾期反響都還行,
于是這一章專門出一期運(yùn)用前面所講過的知識(shí)? 自己從0開始搭建一個(gè)后臺(tái)程序? 能夠?qū)邮忻嫔辖^大多數(shù)的ai對話api(不同公司的具體的鑒權(quán)方法你們找官網(wǎng)文檔copy就行了)
那么正片開始
目錄
大家好這里還是蘇澤,關(guān)于我的Spring狂野之旅已經(jīng)出了5期,基本都是從Spring的底層源碼去讀它,不僅能學(xué)會(huì)使用方法又能理解其工作機(jī)制以及原理,我認(rèn)為這是非常美妙的一件事,這幾期反響都還行,
于是這一章專門出一期運(yùn)用前面所講過的知識(shí)? 自己從0開始搭建一個(gè)后臺(tái)程序? 能夠?qū)邮忻嫔辖^大多數(shù)的ai對話api(不同公司的具體的鑒權(quán)方法你們找官網(wǎng)文檔copy就行了)
從零開始 搭建一個(gè)Spring boot程序
確保你已經(jīng)安裝了Java開發(fā)工具(JDK)。你可以在命令行中輸入java -version來驗(yàn)證是否已安裝Java,并確保版本符合Spring Boot的要求。?編輯安裝的部分我就不演示了默認(rèn)大家已經(jīng)裝好配置好環(huán)境
創(chuàng)建一個(gè)新的Spring Boot項(xiàng)目。你可以使用?編輯新建一個(gè)Springboot項(xiàng)目?編輯
手動(dòng)添加的依賴:
統(tǒng)一封裝一下返回結(jié)果
開干:
1.構(gòu)建好機(jī)器人需要的常量:
下面的部分就是存放對應(yīng)模型的url和Serverid以及秘鑰 這些是根據(jù)你們要選定的模型去改的
存歷史記錄 以及查詢歷史記錄的方法:
下面是獲取ai對話的回答內(nèi)容的部分 (這里是有線程優(yōu)化的處理的)
這段代碼實(shí)現(xiàn)了一個(gè)基于Spring和WebSocket的異步問答系統(tǒng)。主要的邏輯如下:
在并發(fā)場景下,這樣優(yōu)化的優(yōu)勢在于:
這一段代碼是可以應(yīng)用在其他使用WebSocket連接ai的業(yè)務(wù)上的
是基于WebSocket的與遠(yuǎn)程服務(wù)進(jìn)行問答的功能。主要的邏輯如下:
最后 就是鑒權(quán)方法 這個(gè)內(nèi)容 作為api的調(diào)用者? 我們是不需要理解的(并不影響使用),當(dāng)然如果對網(wǎng)絡(luò)協(xié)議以及加密感興趣的伙伴可以細(xì)看 我就直接放上來了,因?yàn)槊考夜镜蔫b權(quán)方法都不一樣,而且文檔中會(huì)直接給出鑒權(quán)方法 直接復(fù)制到代碼里面就可以的
實(shí)現(xiàn)效果?
如果朋友對定向提示詞工程有興趣的話可以專門出一期這個(gè)模塊的博客
提示詞工程主要的優(yōu)勢在于以下:
從零開始 搭建一個(gè)Spring boot程序
-
確保你已經(jīng)安裝了Java開發(fā)工具(JDK)。你可以在命令行中輸入
java -version
來驗(yàn)證是否已安裝Java,并確保版本符合Spring Boot的要求。
安裝的部分我就不演示了默認(rèn)大家已經(jīng)裝好配置好環(huán)境
? -
創(chuàng)建一個(gè)新的Spring Boot項(xiàng)目。你可以使用
新建一個(gè)Springboot項(xiàng)目
我這里選了Springboot版本2.6.13? 這個(gè)倒是不會(huì)有太大問題根據(jù)自己項(xiàng)目來就行?
旁邊這些依賴自己有需要的就加 我這邊不用這里的東西就不選了?
然后出來? 他就幫我們建立好了 這些依賴跟框架了 可以在pom文件里看到
手動(dòng)添加的依賴:
在Maven里輸入:
<!-- Hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
<version>5.7.10</version>
</dependency>
<!-- Alibaba FastJSON -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<!-- Google Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<!-- OkHttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
<!-- Spring Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.5.4</version>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.4</version>
</dependency>
然后我們新建一個(gè)class文件
在里面加入導(dǎo)包:
?
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
配置了那么多。終于可以開始項(xiàng)目了...
統(tǒng)一封裝一下返回結(jié)果
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/*/**
*@author suze
*@date 2023-10-25
*@time 15:19
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Boolean success;
private String errorMsg;
private Object data;
private Long total;
public static Result ok(){
return new Result(true, null, null, null);
}
public static Result ok(Object data){
return new Result(true, null, data, null);
}
public static Result ok(List<?> data, Long total){
return new Result(true, null, data, total);
}
public static Result fail(String errorMsg){
return new Result(false, errorMsg, null, null);
}
// public static Result fail(int errorCode, String errorMsg){
// return new Result(false, errorMsg, null, null);
// }
}
然后用到的地方就導(dǎo)入Result的包即可
import TopOne.dto.Result;
?
開干:
為了方便演示我把Controller和Server放在一個(gè)文件里 大家根據(jù)自己需要去調(diào)整
1.構(gòu)建好機(jī)器人需要的常量:
public static final Gson gson = new Gson();
// 個(gè)性化參數(shù)
private String userId;
private Boolean wsCloseFlag;
private static Boolean totalFlag=true; // 控制提示用戶是否輸入
public List<RoleContent> historyList=new ArrayList<>(); // 這個(gè)是臨時(shí)的 每次用完在線程中要注銷他
//寫一個(gè)結(jié)構(gòu)專門來存機(jī)器的回答
public static class BotText{
String content=" ";
}
public static BotText botText=new BotText();
//告訴控制器輸出完沒有
public static Boolean outputFlag=true;
private String botContent = "";
//返回的json結(jié)果拆解
class JsonParse {
Header header;
Payload payload;
}
class Header {
int code;
int status;
String sid;
}
class Payload {
Choices choices;
}
class Choices {
List<Text> text;
}
class Text {
String role;
String content;
}
class RoleContent{
String role;
String content;
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
下面的部分就是存放對應(yīng)模型的url和Serverid以及秘鑰 這些是根據(jù)你們要選定的模型去改的
public static final String hostUrl = "";
public static final String appid = "";
public static final String apiSecret = "";
public static final String apiKey = "";
存歷史記錄 以及查詢歷史記錄的方法:
我這里做的緩存是2h? 用的是Redis,一開始構(gòu)建項(xiàng)目的時(shí)候可以用一個(gè)簡單的Map來代替Redis? 自行替換掉Redis部分代碼即可
// 將對話歷史存儲(chǔ)到 Redis
public void saveHistory(String history,String id) {
// 設(shè)置有效期為 2 小時(shí)
stringRedisTemplate.opsForValue().set("id:" + id + ":history", history, 2, TimeUnit.HOURS);
}
@RequestMapping("/SaveHistory")
public Result SaveHistory(@RequestParam("userId") String id, @RequestBody String history) {
saveHistory(history,id);
return Result.ok("保存歷史記錄成功有效時(shí)間:2h");
}
// 從 Redis 中獲取對話歷史
public List<RoleContent> getHistory(String userId) {
String historyStr = stringRedisTemplate.opsForValue().get("id:" + userId + ":history");
if (historyStr==null){
return null;
}
return JSONUtil.toList(JSONUtil.parseArray(historyStr), RoleContent.class);
}
//用于獲取歷史聊天記錄
@RequestMapping("/history")
public Result history(@RequestParam("userId") String id) {
String history = stringRedisTemplate.opsForValue().get("id:" + id + ":history");
if (history == null) {
return Result.fail("沒有找到歷史記錄");
}
JSONArray jsonObject = JSON.parseArray(history);
// String jsonString = JSON.toJSONString(jsonObject);
return Result.ok(jsonObject);
}
下面是獲取ai對話的回答內(nèi)容的部分 (這里是有線程優(yōu)化的處理的)
public static String totalAnswer=""; // 大模型的答案匯總
// 可以寫原始問題
public static String NewQuestion = "";
public WordUtils wordUtils=new WordUtils();
//線程池
public ThreadPoolExecutor pool = new ThreadPoolExecutor(13, 13, 1,
TimeUnit.MINUTES, new ArrayBlockingQueue<>(6),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
@RequestMapping("/get")
public DeferredResult<Result> get(@RequestParam("question") String question, @RequestParam("id") String id) {
//創(chuàng)建了一個(gè)DeferredResult<Result>對象,并將其返回給前端。在異步任務(wù)執(zhí)行完畢后,
// 通過調(diào)用deferredResult.setResult(result)方法將結(jié)果設(shè)置到DeferredResult對象中,從而實(shí)現(xiàn)異步返回結(jié)果給前端。
DeferredResult<Result> deferredResult = new DeferredResult<>();
CompletableFuture.supplyAsync(() -> {
try {
callable callable = new callable(question, id);
String answer = callable.call();
Result result = Result.ok(answer);
return result;
} catch (Exception e) {
Result result = Result.fail(e.getMessage());
return result;
}
}, pool).whenComplete((result, throwable) -> {
if (throwable != null) {
result = Result.fail(throwable.getMessage());
deferredResult.setResult(result);
} else {
deferredResult.setResult(result);
}
});
return deferredResult;
}
public class callable implements Callable<String>{
private String id;
private String question;
public callable(String question,String id) {
//這里處理一下userId的長度 因?yàn)橛嶏w那邊限制了
if (id.length() >= 30) {
id= id.substring(0, 30);
}
this.question = question;
this.id=id;
}
@Override
public String call() throws Exception {
String answer =main(question,id);
//System.out.println(answer);
answer = JSONUtil.toJsonStr(answer);
System.out.println("call");
botText.content="";//清空
//緩存歷史對話 時(shí)效兩小時(shí)
//String historyStr = JSONUtil.toJsonStr();
//stringRedisTemplate.opsForValue().set("id:"+id+":history", historyStr,2,TimeUnit.HOURS);
return answer;
}
}
// 主函數(shù)
public String main(String newQuestion,String userid) throws Exception {
// 個(gè)性化參數(shù)入口,如果是并發(fā)使用,可以在這里模擬
System.out.println(totalFlag);
if(totalFlag){
totalFlag=false;
NewQuestion=newQuestion;
// 構(gòu)建鑒權(quán)url
String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
OkHttpClient client = new OkHttpClient.Builder().build();
String url = authUrl.toString().replace("http://", "ws://").replace("https://", "wss://");
Request request = new Request.Builder().url(url).build();
totalAnswer="";
// WebSocket webSocket = client.newWebSocket(request, new BigModelNew(i + "",false));
//這里創(chuàng)建了大模型的新對象 實(shí)際上那些發(fā)送請求獲取答案的操作都是在這個(gè)線程中做的
BigModelNew bigModelNew = null;
if (getHistory(userid)!=null){
bigModelNew=new BigModelNew(userid, false,getHistory(userid),stringRedisTemplate);
}
else {
bigModelNew=new BigModelNew(userid, false,historyList,stringRedisTemplate);
}
// 等待 WebSocket 的 run() 方法執(zhí)行完畢
int maxWaitTime = 10000; // 最大等待時(shí)間,單位:毫秒
int currentWaitTime = 0; // 當(dāng)前已等待的時(shí)間,單位:毫秒
int waitInterval = 1000;// 每次等待的時(shí)間間隔,單位:毫秒
WebSocket webSocket = client.newWebSocket(request, bigModelNew);
System.out.println(maxWaitTime);
while (currentWaitTime < maxWaitTime) {
if (bigModelNew.getBotContent().equals("")) {
// run() 方法還未執(zhí)行完畢,可以進(jìn)行一些其他操作或等待一段時(shí)間
Thread.sleep(waitInterval);
System.out.println("正在執(zhí)行線程"+Thread.currentThread().getName()+"...等待時(shí)間還剩:"+(maxWaitTime-currentWaitTime));
currentWaitTime += waitInterval;
} else {
// run() 方法已執(zhí)行完畢,獲取 bot.content 值并進(jìn)行后續(xù)操作
//System.out.println("run執(zhí)行完畢");
return bigModelNew.getBotContent();
//System.out.println(botText.content);
// ...
}
}
}
totalFlag=true;
return "網(wǎng)絡(luò)開了點(diǎn)小差 試試重新發(fā)送你的消息吧";
}
// 構(gòu)造函數(shù)
public BigModelNew(@Value("${userId}") String userId
,@Value("${wsCloseFlag}") Boolean wsCloseFlag
,@Value("${HistoryList}")List<RoleContent> HistoryList
,@Value("${stringRedisTemplate}") StringRedisTemplate stringRedisTemplate) {
this.userId = userId;
this.wsCloseFlag = wsCloseFlag;
this.historyList=HistoryList;
this.stringRedisTemplate = stringRedisTemplate;
}
這段代碼實(shí)現(xiàn)了一個(gè)基于Spring和WebSocket的異步問答系統(tǒng)。主要的邏輯如下:
-
在
get()
方法中,接收前端傳遞的問題和id參數(shù),并創(chuàng)建一個(gè)DeferredResult<Result>
對象,用于異步返回結(jié)果給前端。 -
通過
CompletableFuture.supplyAsync()
方法創(chuàng)建一個(gè)異步任務(wù),該任務(wù)會(huì)在一個(gè)線程池中執(zhí)行。 -
異步任務(wù)的具體邏輯在
callable
類的call()
方法中實(shí)現(xiàn)。在該方法中,調(diào)用main()
方法進(jìn)行問題回答,并將結(jié)果轉(zhuǎn)換為JSON格式。 -
異步任務(wù)執(zhí)行完畢后,通過
deferredResult.setResult(result)
方法將結(jié)果設(shè)置到DeferredResult
對象中,實(shí)現(xiàn)異步返回結(jié)果給前端。 -
在
main()
方法中,判斷是否是并發(fā)場景下的第一個(gè)請求。如果是第一個(gè)請求,則創(chuàng)建一個(gè)WebSocket連接,并等待run()
方法執(zhí)行完畢。 -
在
run()
方法中,通過WebSocket與遠(yuǎn)程服務(wù)進(jìn)行通信,獲取問題的回答。 -
在并發(fā)場景下,如果有多個(gè)請求同時(shí)到達(dá),只有第一個(gè)請求會(huì)創(chuàng)建WebSocket連接,后續(xù)的請求會(huì)等待第一個(gè)請求的回答結(jié)果,并共享同一個(gè)
totalAnswer
。
在并發(fā)場景下,這樣優(yōu)化的優(yōu)勢在于:
-
使用線程池和異步任務(wù)可以提高并發(fā)處理能力,減少請求的等待時(shí)間。通過異步任務(wù),可以將耗時(shí)的操作(如遠(yuǎn)程服務(wù)調(diào)用)放在后臺(tái)線程中執(zhí)行,而不會(huì)阻塞主線程。
-
使用
DeferredResult
對象可以實(shí)現(xiàn)異步返回結(jié)果給前端。每個(gè)請求都會(huì)得到一個(gè)獨(dú)立的DeferredResult
對象,通過設(shè)置結(jié)果到該對象中,可以實(shí)現(xiàn)異步返回給前端。 -
在并發(fā)場景下,只有第一個(gè)請求會(huì)創(chuàng)建WebSocket連接,后續(xù)的請求會(huì)等待第一個(gè)請求的回答結(jié)果。這樣可以減少對遠(yuǎn)程服務(wù)的重復(fù)請求,節(jié)省資源和提高性能。
然后我們把外面的調(diào)用的功能構(gòu)建好了 接下來就是WebSocket內(nèi)部當(dāng)中 重寫的具體方法 下面是重寫部分的方法:
?
//一個(gè)很關(guān)鍵的函數(shù) 用于得到botContent
public String getBotContent() {
return botContent;
}
public boolean canAddHistory(){ // 由于歷史記錄最大上線1.2W左右,需要判斷是能能加入歷史
int history_length=0;
for(RoleContent temp:historyList){
history_length=history_length+temp.content.length();
}
if(history_length>12000){
historyList=new ArrayList<>();
return false;
}else{
return true;
}
}
// 線程來發(fā)送音頻與參數(shù)
class MyThread extends Thread {
private WebSocket webSocket;
private String newAnswer;
public MyThread(WebSocket webSocket) {
this.webSocket = webSocket;
}
public void run() {
try {
JSONObject requestJson=new JSONObject();
JSONObject header=new JSONObject(); // header參數(shù)
header.put("app_id",appid);
header.put("uid",userId);//這里放userId
JSONObject parameter=new JSONObject(); // parameter參數(shù)
JSONObject chat=new JSONObject();
chat.put("domain","generalv3");
chat.put("temperature",0.6);
chat.put("max_tokens",8192);
parameter.put("chat",chat);
JSONObject payload=new JSONObject(); // payload參數(shù)
JSONObject message=new JSONObject();
JSONArray text=new JSONArray();
// 歷史問題獲取
if(historyList.size()>0){
for(RoleContent tempRoleContent:historyList){
text.add(JSON.toJSON(tempRoleContent));
}
}
// 最新問題
RoleContent roleContent=new RoleContent();
roleContent.role="user";
roleContent.content=NewQuestion;
text.add(JSON.toJSON(roleContent));
// System.err.println("text:");
historyList.add(roleContent);
// historyList.forEach(System.out::println);
//saveHistory(historyList);//在這里就把歷史記錄存到Redis了 就可以清空該線程的歷史記錄List了
//historyList.clear();
message.put("text",text);
payload.put("message",message);
requestJson.put("header",header);
requestJson.put("parameter",parameter);
requestJson.put("payload",payload);
// System.err.println(requestJson); // 可以打印看每次的傳參明細(xì)
webSocket.send(requestJson.toString());
// 等待服務(wù)端返回完畢后關(guān)閉
while (true) {
// System.err.println(wsCloseFlag + "---");
Thread.sleep(200);
if (wsCloseFlag) {
break;
}
}
webSocket.close(1000, "");
// System.out.println("answer"+botText.content);
} catch (Exception e) {
e.printStackTrace();
}finally {
botContent=botText.content;
}
}
}
@Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
//System.out.print("AI:");
MyThread myThread = new MyThread(webSocket);
myThread.start();
}
@Override
public void onMessage(WebSocket webSocket, String text) {
// System.out.println(userId + "用來區(qū)分那個(gè)用戶的結(jié)果" + text);
JsonParse myJsonParse = gson.fromJson(text, JsonParse.class);
//System.out.println("AI:" + gson.toJson(myJsonParse.payload));
if (myJsonParse.header.code != 0) {
System.out.println("發(fā)生錯(cuò)誤,錯(cuò)誤碼為:" + myJsonParse.header.code);
System.out.println("本次請求的sid為:" + myJsonParse.header.sid);
webSocket.close(1000, "");
}
List<Text> textList = myJsonParse.payload.choices.text;
for (Text temp : textList) {
//System.out.println("這里存了機(jī)器的話"+temp.content);
botText.content=botText.content+temp.content;//這里存機(jī)器的話
totalAnswer=totalAnswer+temp.content;
}
//System.out.println("answeraaaa:"+botText.content);//這里能夠打印 但是打印很多次說明他調(diào)用了很多次
// botText.content=totalAnswer;
if (myJsonParse.header.status == 2) {
// 可以關(guān)閉連接,釋放資源
// System.out.println();
// System.out.println("*************************************************************************************");
if(canAddHistory()){
RoleContent roleContent=new RoleContent();
roleContent.setRole("assistant");
roleContent.setContent(totalAnswer);
historyList.add(roleContent);
//System.out.println("OnMessage:"+historyList.toString());
//String jsonString = JSON.toJSONString(historyList, SerializerFeature.PrettyFormat);
//history.text=jsonString;//這里已經(jīng)把歷史記錄給保存了
//在這里答案已經(jīng)輸出完了,就應(yīng)該outputFinished = true;
// output=botText.content;
// myHistory=history.text;
outputFlag=true;
System.out.println("AI:answer"+botText.content);
}else{
RoleContent roleContent=new RoleContent();
roleContent.setRole("assistant");
roleContent.setContent(totalAnswer);
historyList.add(roleContent);
}
//saveHistory(historyList);//在這里就把歷史記錄存到Redis了 就可以清空該線程的歷史記錄List了
historyList.clear();
wsCloseFlag = true;//只有等消息傳過來了 才能夠結(jié)束
totalFlag=true;
}
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
if (null != response) {
int code = response.code();
System.out.println("onFailure code:" + code);
try {
System.out.println("onFailure body:" + response.body().string());
} catch (IOException e) {
throw new RuntimeException(e);
}
if (101 != code) {
System.out.println("connection failed");
System.exit(0);
}
}
}
這一段代碼是可以應(yīng)用在其他使用WebSocket連接ai的業(yè)務(wù)上的
是基于WebSocket的與遠(yuǎn)程服務(wù)進(jìn)行問答的功能。主要的邏輯如下:
MyThread
類是一個(gè)繼承自Thread
的線程類,用于發(fā)送問答請求和接收回答。在run()
方法中,首先構(gòu)建請求的JSON對象,包括頭部參數(shù)、參數(shù)和載荷參數(shù)。然后通過WebSocket發(fā)送該JSON對象。接著,在一個(gè)循環(huán)中等待服務(wù)端返回結(jié)果,并將返回的結(jié)果拼接到botText.content
和totalAnswer
中。最后,關(guān)閉WebSocket連接,并將botText.content
賦值給botContent
。
onOpen()
方法在WebSocket連接建立時(shí)被調(diào)用。在該方法中,創(chuàng)建一個(gè)MyThread
對象并啟動(dòng)線程。
onMessage()
方法在接收到WebSocket消息時(shí)被調(diào)用。該方法首先解析收到的消息,并判斷是否存在錯(cuò)誤。如果沒有錯(cuò)誤,則將回答文本拼接到botText.content
和totalAnswer
中,并根據(jù)返回的狀態(tài)碼進(jìn)行相應(yīng)的處理。如果狀態(tài)碼為2,表示回答已經(jīng)完整返回,此時(shí)可以關(guān)閉連接并進(jìn)行一些后續(xù)處理,如將回答文本添加到歷史記錄中。
onFailure()
方法在WebSocket連接失敗時(shí)被調(diào)用。在該方法中,可以根據(jù)失敗的原因進(jìn)行相應(yīng)的處理。
最后 就是鑒權(quán)方法 這個(gè)內(nèi)容 作為api的調(diào)用者? 我們是不需要理解的(并不影響使用),當(dāng)然如果對網(wǎng)絡(luò)協(xié)議以及加密感興趣的伙伴可以細(xì)看 我就直接放上來了,因?yàn)槊考夜镜蔫b權(quán)方法都不一樣,而且文檔中會(huì)直接給出鑒權(quán)方法 直接復(fù)制到代碼里面就可以的
// 鑒權(quán)方法
public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
URL url = new URL(hostUrl);
// 時(shí)間
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();
// System.err.println(httpUrl.toString());
return httpUrl.toString();
}
實(shí)現(xiàn)效果?
跟一個(gè)搞小程序的朋友借了個(gè)模板 套了一下前端 目前在小程序端實(shí)現(xiàn)了
響應(yīng)的內(nèi)容:
這是獲取聊天記錄的響應(yīng):
具體的業(yè)務(wù)我還做了提示詞工程,讓ai具備一點(diǎn)定向場景的傾向了,就像如下
具體的業(yè)務(wù)截圖回應(yīng)效果
如果朋友對定向提示詞工程有興趣的話可以專門出一期這個(gè)模塊的博客
提示詞工程主要的優(yōu)勢在于以下:
提高問答準(zhǔn)確性:通過給用戶提供定向場景的提示詞,可以引導(dǎo)用戶在特定領(lǐng)域或場景下提問。這樣做可以減少模糊或不相關(guān)的問題,從而提高問答的準(zhǔn)確性。提示詞可以限制用戶的問題范圍,使得AI能夠更好地理解用戶的意圖并給出相關(guān)的回答。
加速問題解決:定向場景的提示詞工程可以幫助用戶快速定位到他們感興趣的領(lǐng)域或問題類型,并提供相關(guān)的問題模板或關(guān)鍵詞。這樣可以節(jié)省用戶在描述問題上的時(shí)間和精力,使得問題能夠更快地得到解答,提高問題解決的效率。
提升用戶體驗(yàn):通過為用戶提供定向場景的提示詞,可以使用戶感到更加舒適和自信。用戶知道他們在與AI進(jìn)行交互時(shí)所處的場景,并且可以根據(jù)提示詞的指引進(jìn)行提問。這種引導(dǎo)性的交互方式可以減少用戶的迷茫和猶豫,提升用戶與AI的交互體驗(yàn)。
簡化系統(tǒng)配置:定向場景的提示詞工程可以幫助系統(tǒng)管理員或開發(fā)人員更好地配置和管理問答系統(tǒng)。通過定義和組織不同的場景和相關(guān)的提示詞,可以使系統(tǒng)的配置更加直觀和可控。管理員可以根據(jù)實(shí)際需求進(jìn)行提示詞的調(diào)整和更新,以適應(yīng)不同的應(yīng)用場景。文章來源:http://www.zghlxwxcb.cn/news/detail-826757.html
有興趣的小伙伴可以先關(guān)注一下我? 下一期出的話我會(huì)發(fā)粉絲通告哦文章來源地址http://www.zghlxwxcb.cn/news/detail-826757.html
到了這里,關(guān)于【Spring boot實(shí)戰(zhàn)】Springboot+對話ai模型整體框架+高并發(fā)線程機(jī)制處理優(yōu)化+提示詞工程效果展示(按照框架自己修改可對接市面上百分之99的模型)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!