引入依賴

父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 授權

為什麼需要會話隔離

一款Java 開源的 Spring Boot 即時通訊 IM 聊天系統_#springboot

https://www.coze.cn/open/docs/developer_guides/session_isolation

引入上面編寫的工具類和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;
}

結果

一款Java 開源的 Spring Boot 即時通訊 IM 聊天系統_#大模型_02

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有提供獲取這些會話內消息列表的接口/工具類
}

結語

前期會花一些時間在官方文檔裏,吸收概念,理解透徹了才好動手,大家有不理解的,其它想法或經驗歡迎留言提出、分享,後續研究更多使用方法會逐一分享出來 。