引入依賴
父pom.xml
<properties>
<coze.version>0.3.0</coze.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.coze</groupId>
<artifactId>coze-api</artifactId>
<version>${coze.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
子pom.xml
<dependencies>
<dependency>
<groupId>com.coze</groupId>
<artifactId>coze-api</artifactId>
</dependency>
</dependencies>
參數配置
clientId、publicKeyId、privateKey請參考教程OAuth應用的創建與授權
我的配置方式只是演示,並非最優唯一解,官方文檔中都是建議把這些參數寫入系統環境變量中
application.yml
coze:
clientId: 123456789
publicKeyId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
privateKeyFilePath: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#privateKey: test
wwwBase: https://www.coze.cn
apiBase: https://api.coze.cn
botId: 987654321
新建配置類
package com.coze.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Author: Tenk
* 釦子參數配置類
*/
@Component
@ConfigurationProperties(prefix = "coze")
public class CozeConfig {
//OAuth應用ID
private String clientId;
//公鑰
private String publicKeyId;
//私鑰
private String privateKey;
//私鑰證書路徑
private String privateKeyFilePath;
//coze官網
private String wwwBase;
//cozeApi請求地址
private String apiBase;
//智能體botId
private String botId;
編寫工具類
package com.coze.util;
import com.coze.openapi.service.auth.JWTOAuthClient;
import com.coze.openapi.service.auth.TokenAuth;
import com.coze.openapi.service.service.CozeAPI;
import com.coze.config.CozeConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* 初始化CozeJWTOAuth授權工具
*
* @url https://github.com/coze-dev/coze-java/blob/main/example/src/main/java/example/auth/JWTOAuthExample.java
* @Author: Tenk
*/
@Component
public class CozeJWTOAuthUtil {
private static final Logger log = LoggerFactory.getLogger(CozeJWTOAuthUtil.class);
//配置類
@Autowired
private CozeConfig cozeConfig;
//OAuth授權工具
private JWTOAuthClient oauth;
//get
public CozeConfig getCozeConfig() {
return cozeConfig;
}
//get
public JWTOAuthClient getJWTOAuthClient() {
return oauth;
}
//單例
@PostConstruct
public void init() {
this.oauth = createJWTOAuthClient();
}
/**
* 初始化CozeJWTOAuth
*
* @return
*/
public JWTOAuthClient createJWTOAuthClient() {
try {
//讀取coze_private_key_pem
String jwtOAuthPrivateKey = new String(Files.readAllBytes(Paths.get(cozeConfig.getPrivateKeyFilePath())), StandardCharsets.UTF_8);
oauth = new JWTOAuthClient.JWTOAuthBuilder()
.clientID(cozeConfig.getClientId())
.privateKey(jwtOAuthPrivateKey)
.publicKey(cozeConfig.getPublicKeyId())
.baseURL(cozeConfig.getApiBase())
.build();
} catch (Exception e) {
log.error("初始化CozeJWTOAuth失敗", e);
return null;
}
return oauth;
}
}
service方法
/**
* 常量
*
* @Author: Tenk
*/
public class CozeConstants {
public static final String JWT_TOKEN = "coze:jwt_token:";
public static final String CONVERSATION = "coze:conversation:";
}
1.OAuth JWT 授權
為什麼需要會話隔離
引入上面編寫的工具類和redis工具類
@Autowired
private CozeJWTOAuthUtil cozeJWTOAuthUtil;
@Autowired
private RedisService redisService;
//根據系統用户id,生成access_token,以達到會話隔離的效果
public OAuthToken getAccessToken(String userId) {
OAuthToken accessToken;
if (redisService.hasKey(CozeConstants.JWT_TOKEN + userId)) {
accessToken = JSONObject.parseObject(redisService.getCacheObject(CozeConstants.JWT_TOKEN + userId).toString(), OAuthToken.class);
} else {
//token默認15分鐘有效期,redis存14分鐘
accessToken = cozeJWTOAuthUtil.getJWTOAuthClient().getAccessToken(userId);
redisService.setCacheObject(CozeConstants.JWT_TOKEN + userId, accessToken, 14L, TimeUnit.MINUTES);
}
return accessToken;
}
結果
2.向智能體發送消息
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Null;
/**
* @Author: Tenk
* AI聊天業務對象
*/
public class ChatBo {
@NotBlank(message = "請輸入內容")
private String content;
private String userId;
}
public String createChatMessage(ChatBo bo) {
//獲取用户jwtToken
OAuthToken accessToken = getAccessToken(bo.getUserId());
//獲取釦子Api工具
CozeAPI cozeAPI = cozeJWTOAuthUtil.createCozeAPIByUser(accessToken.getAccessToken());
//獲取會話id,演示所設計的是每人是唯一的會話,可以單獨new個表做管理
String conversationId = null;
if (redisService.hasKey(CozeConstants.CONVERSATION + bo.getUserId())) {
conversationId = redisService.getCacheObject(CozeConstants.CONVERSATION + bo.getUserId()).toString();
} else {
//反之創建新會話
CreateConversationResp resp = cozeAPI.conversations()
.create(new CreateConversationReq(null, null, cozeJWTOAuthUtil.getCozeConfig().getTripBotId()));
conversationId = resp.getConversation().getId();
redisService.setCacheObject(CozeConstants.CONVERSATION + bo.getUserId(), conversationId);
}
//先手動創建message,否則拿不到messageId等信息以存入數據庫
CreateMessageReq.CreateMessageReqBuilder<?, ?> msgReq = CreateMessageReq.builder()
.conversationID(conversationId)
.role(MessageRole.USER)
.content(bo.getContent())
.contentType(MessageContentType.TEXT);
CreateMessageResp msgResp = cozeAPI.conversations().messages().create(msgReq.build());
//通過響應拿到user question的message信息
Message userMsg = msgResp.getMessage();
//創建對話請求,再將創建的message附加到對話中
//.messages()中也可以直接傳文字而不封裝Message
CreateChatReq chatReq = CreateChatReq.builder()
.stream(false)
.autoSaveHistory(true)
.botID(cozeJWTOAuthUtil.getCozeConfig().getTripBotId())
.conversationID(conversationId)
.userID(bo.getUserId())
.messages(Collections.singletonList(userMsg))
//.messages(Collections.singletonList(Message.buildUserQuestionText(bo.getContent()))
.build();
ChatPoll chatPoll = null;
try {
//發送對話請求並自動輪詢
chatPoll = cozeAPI.chat().createAndPoll(chatReq);
} catch (Exception e) {
log.error("發送對話失敗:{}", e.getMessage());
return null;
}
//解析AI消息
String content = null;
for (Message message : chatPoll.getMessages()) {
if (message.getType().getValue().equals(MessageType.ANSWER.getValue())) {
// AI完成後的完整答覆
content = message.getContent();
}
}
return content;
//Tips
//封裝的userMessage和遍歷chatPoll得到的botMessage可以自己整理寫入數據庫以記錄。
//無特殊需求,其實不用,因為coze有提供獲取這些會話內消息列表的接口/工具類
}
結語
前期會花一些時間在官方文檔裏,吸收概念,理解透徹了才好動手,大家有不理解的,其它想法或經驗歡迎留言提出、分享,後續研究更多使用方法會逐一分享出來 。
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。