java Springboot使用扣子Coze實現實時音頻對話智能客服

一、背景

? ? ? ? 因公司業務需求,需要使用智能客服實時接聽顧客電話。

? ? ? ? 現在已經完成的操作是,智能體已接入系統進行對練,所以本文章不寫對聯相關的功能。只有coze對接~

????????扣子提供了試用Realtime WebSocket,點擊右上角setting配置好智能體token之后就可以試用了

注意:只有扣子專業版支持實時音視頻,所以需要開通專業版,開發測試階段可以先充值1元買1000資源點對接測試, 注意超額會單獨收費哦,

二、準備工作

1、發布智能體為AI服務

? ? ? ? a.登陸扣子平臺注冊賬號

扣子扣子是新一代 AI 大模型智能體開發平臺。整合了插件、長短期記憶、工作流、卡片等豐富能力,扣子能幫你低門檻、快速搭建個性化或具備商業價值的智能體,并發布到豆包、飛書等各個平臺。https://www.coze.cn/home? ? ? ? b. 在左側導航欄中選擇工作空間,并在頁面頂部空間列表中選擇個人空間或團隊空間

? ? ? ? c. 在項目開發頁面,新建智能體

? ? ? ?d.創建智能體完成之后,點擊右上角的發布,在發布頁面,選擇API選項,然后點擊發布

? ? c.獲取智能體ID,后續開發要用

? ? ? ? 點開 工作空間->項目開發->你的智能體,點進新建的智能體,鏈接地址后的數字則為智能體ID

2、獲取訪問令牌

因公司業務需要經過對比我們選用了JWT方式,開發測試階段也可以選擇個人訪問令牌? ? ? ?

????????a.在扣子API頁面,進入授權-> Oauth應用頁面->創建新應用,注意客戶端類型為服務端應用

? ? ? ?

? ? ? ? b.保存后進行下一步授權,將自動生成的公鑰復制保存好,自動下載的私鑰也要存儲好,后續接口認證會用到!

????????

3、安裝Java SDK,參考扣子官網

扣子扣子是新一代 AI 大模型智能體開發平臺。整合了插件、長短期記憶、工作流、卡片等豐富能力,扣子能幫你低門檻、快速搭建個性化或具備商業價值的智能體,并發布到豆包、飛書等各個平臺。https://www.coze.cn/open/docs/developer_guides/java_installation

三、實踐開發

1、添加maven依賴

 <dependency><groupId>com.coze</groupId><artifactId>coze-api</artifactId><version>0.3.0</version>
</dependency><!-- 以下非必須!!!!我把私鑰文件放到resources下了,所以打包需要加上這個類型->
<build><resources><resource><directory>src/main/resources</directory><includes><include>**/*.pem</include></includes><filtering>false</filtering></resource></resources>
</build>

2、獲取token

@Slf4j
@Component
public class CozeOAuth {/*** JWT鑒權token*/public String getJWTToken() {String token = "";try {// 獲取私鑰文件String jwtOauthPrivateKeyFilePath = "這里是你的私鑰文件地址";ClassLoader classLoader = this.getClass().getClassLoader();java.net.URL resourceUrl = classLoader.getResource(jwtOauthPrivateKeyFilePath);if (resourceUrl == null) {log.info("私鑰資源文件未找到,{}", jwtOauthPrivateKeyFilePath);return token;}String jwtOauthPrivateKey = new String(Files.readAllBytes(Paths.get(resourceUrl.toURI())), StandardCharsets.UTF_8);JWTOAuthClient oauth = new JWTOAuthClient.JWTOAuthBuilder().clientID("這里是你之前創建的OAuth應用Id").privateKey(jwtOauthPrivateKey).publicKey("這里是你的公鑰").baseURL(com.coze.openapi.service.config.Consts.COZE_CN_BASE_URL).build();// 獲取tokenOAuthToken resp = oauth.getAccessToken();System.out.println(resp);if (Objects.nonNull(resp)) {token = resp.getAccessToken();}} catch (Exception e) {log.error("獲取coze JWT token異常!", e);}log.info("獲取coze JWT token:{}", token);return token;}}

3、創建新類繼承WebsocketsChatCallbackHandler,接收扣子服務端返回消息并做業務處理

@Slf4j
public class MyWebsocketsChatCallbackHandler extends WebsocketsChatCallbackHandler {public void onChatCreated(WebsocketsChatClient client, ChatCreatedEvent event) {log.info("扣子服務端返回,對話連接成功,{}", JSON.toJSONString(event));}public void onChatUpdated(WebsocketsChatClient client, ChatUpdatedEvent event) {log.info("扣子服務端返回,對話配置成功,{}", JSON.toJSONString(event));}public void onConversationChatCreated(WebsocketsChatClient client, ConversationChatCreatedEvent event) {log.info("扣子服務端返回,對話開始,{}", JSON.toJSONString(event));}public void onConversationChatInProgress(WebsocketsChatClient client, ConversationChatInProgressEvent event) {log.info("扣子服務端返回,對話正在處理,{}", JSON.toJSONString(event));}public void onConversationMessageDelta(WebsocketsChatClient client, ConversationMessageDeltaEvent event) {log.info("扣子服務端返回,增量消息,{}", JSON.toJSONString(event));}public void onConversationAudioDelta(WebsocketsChatClient client, ConversationAudioDeltaEvent event) {log.info("扣子服務端返回,增量語音,{}", JSON.toJSONString(event));// TODO 處理實際業務,比如返回給用戶的語音}public void onConversationMessageCompleted(WebsocketsChatClient client, ConversationMessageCompletedEvent event) {log.info("扣子服務端返回,消息完成,{}", JSON.toJSONString(event));}public void onConversationAudioCompleted(WebsocketsChatClient client, ConversationAudioCompletedEvent event) {log.info("扣子服務端返回,語音回復完成,{}", JSON.toJSONString(event));}public void onConversationChatCompleted(WebsocketsChatClient client, ConversationChatCompletedEvent event) {log.info("扣子服務端返回,對話完成,{}", JSON.toJSONString(event));}public void onConversationChatFailed(WebsocketsChatClient client, ConversationChatFailedEvent event) {log.info("扣子服務端返回,對話失敗,{}", JSON.toJSONString(event));}public void onInputAudioBufferCompleted(WebsocketsChatClient client, InputAudioBufferCompletedEvent event) {log.info("扣子服務端返回,流式提交的音頻完成,{}", JSON.toJSONString(event));}public void onInputAudioBufferCleared(WebsocketsChatClient client, InputAudioBufferClearedEvent event) {log.info("扣子服務端返回,清除緩沖區音頻成功,{}", JSON.toJSONString(event));}public void onConversationCleared(WebsocketsChatClient client, ConversationClearedEvent event) {log.info("扣子服務端返回,上下文清除完成,{}", JSON.toJSONString(event));}public void onConversationChatCanceled(WebsocketsChatClient client, ConversationChatCanceledEvent event) {log.info("扣子服務端返回,智能體輸出中斷,{}", JSON.toJSONString(event));}public void onConversationAudioTranscriptUpdate(WebsocketsChatClient client, ConversationAudioTranscriptUpdateEvent event) {log.info("扣子服務端返回,用戶語音識別字幕,{}", JSON.toJSONString(event));}public void onConversationAudioTranscriptCompleted(WebsocketsChatClient client, ConversationAudioTranscriptCompletedEvent event) {log.info("扣子服務端返回,用戶語音識別完成,{}", JSON.toJSONString(event));}public void onConversationChatRequiresAction(WebsocketsChatClient client, ConversationChatRequiresActionEvent event) {log.info("扣子服務端返回,端插件請求,{}", JSON.toJSONString(event));}public void onInputAudioBufferSpeechStarted(WebsocketsChatClient client, InputAudioBufferSpeechStartedEvent event) {log.info("扣子服務端返回,用戶開始說話,{}", JSON.toJSONString(event));}public void onInputAudioBufferSpeechStopped(WebsocketsChatClient client, InputAudioBufferSpeechStoppedEvent event) {log.info("扣子服務端返回,用戶結束說話,{}", JSON.toJSONString(event));}public void onClosing(WebsocketsChatClient client, int code, String reason) {log.info("扣子服務端返回,onClosing,code:{},reason:{}",code, reason);}public void onClosed(WebsocketsChatClient client, int code, String reason) {log.info("扣子服務端返回,onClosed,code:{},reason:{}", code,reason);}public void onError(WebsocketsChatClient client, ErrorEvent event) {log.info("扣子服務端返回,onError,event:{}", JSON.toJSONString(event));}public void onFailure(WebsocketsChatClient client, Throwable t) {log.info("扣子服務端返回,onFailure,event:{}", JSON.toJSONString(t));}public void onClientException(WebsocketsChatClient client, Throwable t) {log.info("扣子服務端返回,onFailure,event:{}", JSON.toJSONString(t.getMessage()));}
}

4、創建工具類

抽取跟業務無關的代碼到該類中

@Slf4j
@Component
public class WebSocketUtils {@Resourceprivate CozeOAuth cozeOAuth;/*** 更新對話配置 請求參數*/public ChatUpdateEventData initChatUpdateEventData() {// 對話配置ChatConfig chatConfig = new ChatConfig();chatConfig.setAutoSaveHistory(true);// 輸入音頻格式InputAudio inputAudio = new InputAudio("pcm", "g711a", 8000, 1, 16);PCMConfig pcmConfig = new PCMConfig(100,8000);// 輸出音頻格式OutputAudio outputAudio = new OutputAudio("pcm", pcmConfig, null, null, null);// 轉檢測配置// server_vad 模式下,VAD 檢測到語音之前要包含的音頻量,單位為 ms。默認為 600ms。// server_vad 模式下,檢測語音停止的靜音持續時間,單位為 ms。默認為 500msTurnDetection turnDetection = new TurnDetection("server_vad", 300, 500);return cChatUpdateEventData.builder().inputAudio(inputAudio).outputAudio(outputAudio).chatConfig(chatConfig).turnDetection(turnDetection).build();}public CozeAPI getCozeApi(){return new CozeAPI.Builder().baseURL(Consts.COZE_CN_BASE_URL).auth(new TokenAuth(cozeOAuth.getJWTToken())).readTimeout(10000).build();}
}

5、使用websocket雙向流式對話

我們用到了第三方的用戶進線傳輸,直接sip協議拿包,將包傳輸給扣子,之后再將扣子的增量語音返回給第三方就行。所以選擇了websocket的方式

        byte[] buffer = new byte[1500];CozeAPI cozeAPI = webSocketUtils.getCozeApi();WebsocketsChatClient websocketsChatClient = cozeAPI.websockets().chat().create(new WebsocketsChatCreateReq("這里是你的智能體ID", new MyWebsocketsChatCallbackHandler()));// 更新對話配置               websocketsChatClient.chatUpdate(webSocketUtils.initChatUpdateEventData());// 此處可以根據實際業務接收語音流byte[] audioData = Files.readAllBytes(Paths.get("/音頻.pcm"));// 流式上傳音頻片段websocketsChatClient.inputAudioBufferAppend(audioData);

四、踩過的的坑

1、SDK版本會落后服務端功能

? ? ? ? 扣子提供的SDK跟接口文檔中描述的功能有部分差異,比如更新對話接口的入參limit_config,在SDK中是沒有的。

? ? ? ? 遇到這種情況則需要自己封裝參數,比如繼承某個SDK的類,然后在子類中寫自己需要但是SDK沒有的參數。

2、自動打斷功能配置

? ? ? ? 想要實現自動打斷功能,需要使用server_vad模式,并且需要配置輸出音頻的限制limit_config,限制每次服務端返回的包,否則會等服務端返回完成之后才能打斷。

3、工作流模式服務端響應較慢

? ? ? ? 實際應用場景中會需要給智能體傳配置好的參數,目前智能通過工作流的方式記住上下文,但是該模式服務端響應在3s左右,具體還在排查問題

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/77055.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/77055.shtml
英文地址,請注明出處:http://en.pswp.cn/web/77055.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

棧和字符串,力扣.43.字符串相乘力扣1047.刪除字符串中的所有相鄰重復項力扣.844比較含退格的字符串力扣227.基本計算器II

目錄 力扣.43.字符串相乘 力扣1047.刪除字符串中的所有相鄰重復項 力扣.844比較含退格的字符串 力扣227.基本計算器II 力扣.43.字符串相乘 我們剩下的落兩個數字即可。 class Solution {public static String multiply(String num1, String num2) {int mnum1.length();int n…

Spring Boot單元測試實戰指南:從零到高效測試

在Spring Boot開發中&#xff0c;單元測試是保障代碼質量的核心環節。本文將基于實際開發場景&#xff0c;手把手教你如何快速實現分層測試、模擬依賴、編寫高效斷言&#xff0c;并分享最佳實踐&#xff01; 一、5分鐘環境搭建 添加依賴 在pom.xml中引入spring-boot-starter-te…

React狀態提升深度解析:原理、實戰與最佳實踐

一、狀態提升的本質認知 React狀態提升&#xff08;State Lifting&#xff09;是組件間通信的核心模式&#xff0c;其本質是通過組件樹層級關系重構實現狀態共享。與傳統父子傳參不同&#xff0c;它通過將狀態提升到最近的共同祖先組件&#xff0c;建立單向數據流高速公路。 …

https nginx 負載均衡配置

我的系統是OpenEuler。 安裝nginx yum install -y nginx 啟動&開機啟動 systemctl start nginx systemctl enable nginx 自定義conf配置文件 cat <<EOF >> /etc/nginx/conf.d/load_balancer.conf upstream backend {ip_hash; # 防止驗證碼驗證失敗server…

各種插值方法的Python實現

插值方法的Python實現 1. 線性插值&#xff08;Linear Interpolation&#xff09; 原理&#xff1a;用直線連接相鄰數據點&#xff0c;計算中間點的值。 實現&#xff1a; import numpy as np from scipy.interpolate import interp1dx np.array([0, 1, 2, 3, 4]) y np.arr…

重新定義戶外防護!基于DeepSeek的智能展開傘棚系統技術深度解析

從“手動操作”到“感知決策”&#xff0c;AI重構城市空間彈性 全球極端天氣事件頻發&#xff0c;傳統傘棚依賴人工展開/收納&#xff0c;存在響應滯后&#xff08;暴雨突襲時展開需3-5分鐘&#xff09;、抗風能力弱&#xff08;8級風損毀率超60%&#xff09;、空間利用率低等痛…

Redis 基礎和高級用法入門

redis 是什么&#xff1f; Redis是一個遠程內存數據庫&#xff0c;它不僅性能強勁&#xff0c;而且還具有復制特性以及為解決問題而生的獨一無二的數據模型。Redis提供了5種不同類型的數據結構&#xff0c;各式各樣的問題都可以很自然地映射到這些數據結構上&#xff1a…

常見數據庫關鍵字示例 SQL 及執行順序分析(帶詳細注釋)

示例 SQL 及執行順序分析&#xff08;帶詳細注釋&#xff09; 示例 1&#xff1a;基礎查詢&#xff08;含多表關聯、過濾、分組、排序&#xff09; SELECT -- 1. 選擇字段&#xff08;包含聚合函數和別名&#xff09;e.department, COUNT(e.employee_id) AS total_employees, …

設計模式--建造者模式詳解

建造者模式 建造者模式也屬于創建型模式&#xff0c;它提供了一種創建對象的最佳方式 定義&#xff1a;將一個復雜對象的構建和它的表示分離&#xff0c;使得同樣的構建過程可以創建不同的表示&#xff08;假設有不同的建造者實現類&#xff0c;可以產生不同的產品&#xff09…

PCB 過孔銅厚的深入指南

***前言&#xff1a;在上一期的文章中介紹了PCB制造的工藝流程&#xff0c;但仍然想在過孔的銅厚和PCB的過孔厚徑比兩個方面再深入介紹。 PCB銅厚的定義 電路中銅的厚度以盎司(oz)**表示。那么&#xff0c;為什么用重量單位來表示厚度呢? 盎司(oz)的定義 將1盎司(28.35 克)的銅…

如何配置 Conda 使用鏡像源加速

如何配置 Conda 使用鏡像源加速 為了提高使用 Anaconda 或 Miniconda 時包管理的速度&#xff0c;特別是在國內網絡環境下&#xff0c;可以通過配置鏡像源來實現更快的下載。以下是詳細的步驟說明&#xff1a; 1. 安裝 Conda&#xff08;如果尚未安裝&#xff09; 如果你還沒…

【k8s】k8s是怎么實現自動擴縮的

Kubernetes 提供了多種自動擴縮容機制&#xff0c;主要包括 Pod 水平自動擴縮&#xff08;HPA&#xff09;、垂直 Pod 自動擴縮&#xff08;VPA&#xff09; 和 集群自動擴縮&#xff08;Cluster Autoscaler&#xff09;。以下是它們的實現原理和配置方法&#xff1a; 1. Pod …

Reflex 完全指南:用 Python 構建現代 Web 應用的終極體驗

“寫 Python&#xff0c;就能構建 Web 前端。”——這不再是夢想&#xff0c;而是由 Reflex 帶來的現實。 過去&#xff0c;構建一個現代 Web 應用意味著你要學會前端&#xff08;React/JS/HTML/CSS&#xff09; 后端&#xff08;Flask/Django&#xff09; API 交互&#xff08…

Vue實戰(08)解決 Vue 項目中路徑別名 `@` 在 IDE 中報錯無法識別的問題

一、引言 ? 在 Vue 項目開發過程中&#xff0c;路徑別名是一個非常實用的特性&#xff0c;它能夠幫助開發者簡化文件引用路徑&#xff0c;提高代碼的可讀性和可維護性。其中&#xff0c; 作為一個常見的路徑別名&#xff0c;通常被用來指向項目的 src 目錄。然而&#xff0c;…

5.學習筆記-SpringMVC(P61-P70)

SpringMVC-SSM整合-接口測試 (1)業務層接口使用junit接口做測試 (2)表現層用postman做接口測試 (3)事務處理— 1&#xff09;在SpringConfig.java&#xff0c;開啟注解&#xff0c;是事務驅動 2&#xff09;配置事務管理器&#xff08;因為事務管理器是要配置數據源對象&…

[論文閱讀]REPLUG: Retrieval-Augmented Black-Box Language Models

REPLUG: Retrieval-Augmented Black-Box Language Models REPLUG: Retrieval-Augmented Black-Box Language Models - ACL Anthology NAACL-HLT 2024 在這項工作中&#xff0c;我們介紹了RePlug&#xff08;Retrieve and Plug&#xff09;&#xff0c;這是一個新的檢索增強型…

Mysql的深度分頁查詢優化

一、深度分頁為什么慢&#xff1f; 當執行 SELECT * FROM orders ORDER BY id LIMIT 1000000, 10 時&#xff1a; MySQL 會掃描前 1,000,010 行&#xff0c;丟棄前 100 萬行&#xff0c;僅返回 10 行。偏移量&#xff08;offset&#xff09;越大&#xff0c;掃描行數越多&…

最新扣子(Coze)案例教程:Excel數據生成統計圖表,自動清洗數據+轉換可視化圖表+零代碼,完全免費教程

大家好&#xff0c;我是斜杠君。 知識星球群有同學和我說每天的工作涉及很多數據表的重復操作&#xff0c;想學習Excel數據表通過大模型自動轉數據圖片的功能。 今天斜杠君就帶大家一起搭建一個智能體&#xff0c;以一個銷售行業數據為例&#xff0c;可以快速實現自動清洗Exc…

Uniapp 中緩存操作指南

在 Uniapp 中,你可以使用三種方式操作緩存:同步方法、異步方法和 Vuex 持久化存儲。以下是詳細的設置、獲取和清除緩存的方法: 1. 同步方法 設置緩存 uni.setStorageSync(key, value); // 示例 uni.setStorageSync(token, abc123); 獲取緩存 const value = uni.getStor…

k8s的yaml文件里的volume跟volumeMount的區別

volume 是 Pod 級別的資源&#xff0c;用于定義存儲卷。它是一個獨立于容器的存儲資源&#xff0c;可以被一個或多個容器共享使用。volume 的定義位于 Pod 的 spec.volumes 部分。 特點 獨立性&#xff1a;volume 是 Pod 的一部分&#xff0c;而不是容器的一部分。它獨立于容…