java springBoot 整合 扣子cozeAI 智能体 对话_springboot集成coze
文档列表
官方API在线调试,帮助理解https://www.coze.cn/open/playground/list_conversations官方API文档,帮助理解
https://www.coze.cn/open/docs/developer_guides/create_conversationCoze_JavaSDK 文档
https://www.coze.cn/open/docs/developer_guides/java_overviewcoze_java_sdk_demo 示例
https://github.com/coze-dev/coze-java
demo中各工具类的Example有助于理解后按自己的需求编码,建议pull下来看一遍
引入依赖
父pom.xml 0.3.0 com.coze coze-api ${coze.version}
子pom.xml com.coze coze-api
参数配置
clientId、publicKeyId、privateKey请参考教程OAuth应用的创建与授权
我的配置方式只是演示,并非最优唯一解,官方文档中都是建议把这些参数写入系统环境变量中
application.ymlcoze: 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 */@Componentpublic 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 授权
为什么需要会话隔离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;}
结果
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有提供获取这些会话内消息列表的接口/工具类}
结语
前期会花一些时间在官方文档里,吸收概念,理解透彻了才好动手,大家有不理解的,其它想法或经验欢迎留言提出、分享,后续研究更多使用方法会逐一分享出来 。
高阶版实现方式
流式传输接口 打字机效果的完整实现https://blog.csdn.net/weixin_44548582/article/details/148171608