AI接口支持流式调用
This commit is contained in:
@@ -13,6 +13,8 @@ import com.emotion.service.ConversationService;
|
||||
import com.emotion.service.CozeApiCallService;
|
||||
import com.emotion.service.EmotionRecordService;
|
||||
import com.emotion.service.EmotionAnalysisService;
|
||||
import com.emotion.service.AiConfigService;
|
||||
import com.emotion.entity.AiConfig;
|
||||
import com.emotion.dto.request.*;
|
||||
import com.emotion.dto.response.*;
|
||||
import com.emotion.util.SnowflakeIdGenerator;
|
||||
@@ -41,6 +43,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* AI聊天服务实现类
|
||||
@@ -73,37 +76,15 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
@Value("${emotion.coze.api.token:}")
|
||||
private String cozeApiToken;
|
||||
|
||||
@Value("${emotion.coze.api.base-url:https://api.coze.cn}")
|
||||
private String cozeBaseUrl;
|
||||
|
||||
@Value("${emotion.coze.api.chat.path:/v3/chat}")
|
||||
private String chatPath;
|
||||
|
||||
@Value("${emotion.coze.api.chat.talk.bot-id:}")
|
||||
private String chatBotId;
|
||||
|
||||
@Value("${emotion.coze.api.chat.talk.workflow-id:}")
|
||||
private String chatWorkflowId;
|
||||
|
||||
@Value("${emotion.coze.api.chat.summary.bot-id:}")
|
||||
private String summaryBotId;
|
||||
|
||||
@Value("${emotion.coze.api.chat.summary.workflow-id:}")
|
||||
private String summaryWorkflowId;
|
||||
|
||||
@Value("${emotion.coze.api.timeout:30000}")
|
||||
private int timeout;
|
||||
|
||||
@Value("${emotion.coze.api.retry-count:3}")
|
||||
private int retryCount;
|
||||
|
||||
@Value("${emotion.coze.api.retry-delay:1000}")
|
||||
private int retryDelay;
|
||||
@Autowired
|
||||
private AiConfigService aiConfigService;
|
||||
|
||||
private static final String DEFAULT_USER_ID = "emotion-museum-user";
|
||||
|
||||
// 使用场景常量
|
||||
private static final String USAGE_SCENARIO_CHAT = "chat";
|
||||
private static final String USAGE_SCENARIO_SUMMARY = "summary";
|
||||
private static final String DEFAULT_ENVIRONMENT = "production";
|
||||
|
||||
// API 相关常量
|
||||
private static final String CONTENT_KEY = "content";
|
||||
@@ -494,13 +475,20 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
@Override
|
||||
public boolean healthCheck() {
|
||||
try {
|
||||
// 简化健康检查 - 检查必要配置是否存在
|
||||
boolean configValid = cozeApiToken != null && !cozeApiToken.trim().isEmpty() &&
|
||||
chatBotId != null && !chatBotId.trim().isEmpty() &&
|
||||
cozeBaseUrl != null && !cozeBaseUrl.trim().isEmpty();
|
||||
// 检查聊天场景的AI配置是否存在
|
||||
AiConfig chatConfig = aiConfigService.getBestConfig(USAGE_SCENARIO_CHAT, DEFAULT_ENVIRONMENT);
|
||||
if (chatConfig == null) {
|
||||
log.warn("未找到聊天场景的AI配置");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查必要配置是否完整
|
||||
boolean configValid = chatConfig.getApiToken() != null && !chatConfig.getApiToken().trim().isEmpty() &&
|
||||
chatConfig.getBotId() != null && !chatConfig.getBotId().trim().isEmpty() &&
|
||||
chatConfig.getApiBaseUrl() != null && !chatConfig.getApiBaseUrl().trim().isEmpty();
|
||||
|
||||
if (!configValid) {
|
||||
log.warn("Coze API 配置不完整");
|
||||
log.warn("AI配置不完整: configId={}", chatConfig.getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -587,65 +575,89 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
CozeApiCall apiCall = createSummaryApiCallRecord(conversationId, null, userMessage, userId, "summary");
|
||||
|
||||
try {
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + cozeApiToken);
|
||||
headers.set("Content-Type", "application/json");
|
||||
|
||||
// 构建请求体 - 使用总结专用的bot和workflow
|
||||
Map<String, Object> requestBody = buildSummaryRequest(conversationId, userMessage, userId);
|
||||
|
||||
// 更新API调用记录的请求信息
|
||||
updateApiCallRequest(apiCall, cozeBaseUrl + chatPath, requestBody, headers);
|
||||
|
||||
HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
|
||||
|
||||
// 构建完整的API URL
|
||||
String cozeApiUrl = cozeBaseUrl + chatPath;
|
||||
log.info("发送Coze总结请求到: {}, 请求体: {}", cozeApiUrl, requestBody);
|
||||
|
||||
// 发送请求
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
cozeApiUrl,
|
||||
HttpMethod.POST,
|
||||
request,
|
||||
String.class);
|
||||
|
||||
log.info("收到Coze总结初始响应: {}", response.getBody());
|
||||
|
||||
// 更新API调用记录的响应信息
|
||||
updateApiCallResponse(apiCall, response);
|
||||
|
||||
// 解析响应获取chat_id和conversation_id
|
||||
JSONObject responseJson = JSON.parseObject(response.getBody());
|
||||
String chatId = extractChatIdFromResponse(responseJson);
|
||||
String cozeConversationId = extractConversationIdFromResponse(responseJson);
|
||||
|
||||
if (chatId != null && cozeConversationId != null) {
|
||||
// 更新API调用记录的Coze ID信息
|
||||
updateApiCallCozeIds(apiCall, chatId, cozeConversationId);
|
||||
|
||||
// 轮询聊天状态直到完成并获取回复内容
|
||||
String aiReply = waitForChatCompletionWithTracking(chatId, cozeConversationId, apiCall);
|
||||
log.info("Coze AI总结响应成功: reply={}", aiReply);
|
||||
|
||||
// 更新API调用记录的最终结果
|
||||
updateApiCallSuccess(apiCall, aiReply);
|
||||
|
||||
return aiReply;
|
||||
} else {
|
||||
log.error("无法从Coze总结响应中获取chat_id或conversation_id");
|
||||
updateApiCallError(apiCall, "INVALID_RESPONSE", "无法从Coze总结响应中获取chat_id或conversation_id");
|
||||
return "抱歉,AI总结服务响应异常,请稍后再试。";
|
||||
}
|
||||
|
||||
return executeSummaryCozeApiCall(apiCall, conversationId, userMessage, userId);
|
||||
} catch (Exception e) {
|
||||
log.error("发送总结消息到Coze AI失败", e);
|
||||
updateApiCallError(apiCall, "REQUEST_FAILED", e.getMessage());
|
||||
log.error("发送总结消息失败", e);
|
||||
updateApiCallFailure(apiCall, e.getMessage());
|
||||
return "抱歉,AI总结服务暂时不可用,请稍后再试。";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行总结Coze API调用的逻辑
|
||||
*/
|
||||
private String executeSummaryCozeApiCall(CozeApiCall apiCall, String conversationId, String userMessage, String userId) {
|
||||
// 获取总结场景的AI配置
|
||||
AiConfig config = getSummaryAiConfig();
|
||||
|
||||
// 构建请求体 - 使用总结专用的bot和workflow
|
||||
Map<String, Object> requestBody = buildSummaryRequest(conversationId, userMessage, userId);
|
||||
|
||||
// 检查是否使用流式输出
|
||||
boolean useStream = (Boolean) requestBody.get("stream");
|
||||
|
||||
if (useStream) {
|
||||
return executeStreamCozeApiCall(apiCall, config, requestBody, conversationId, userMessage, userId);
|
||||
} else {
|
||||
return executeSummaryNormalCozeApiCall(apiCall, config, requestBody, conversationId, userMessage, userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行普通(非流式)总结Coze API调用
|
||||
*/
|
||||
private String executeSummaryNormalCozeApiCall(CozeApiCall apiCall, AiConfig config, Map<String, Object> requestBody,
|
||||
String conversationId, String userMessage, String userId) {
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + config.getApiToken());
|
||||
headers.set("Content-Type", "application/json");
|
||||
|
||||
// 构建完整的API URL
|
||||
String cozeApiUrl = config.getApiBaseUrl() + getApiPath(config);
|
||||
|
||||
// 更新API调用记录的请求信息
|
||||
updateApiCallRequest(apiCall, cozeApiUrl, requestBody, headers);
|
||||
|
||||
HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
|
||||
log.info("发送Coze总结请求到: {}, 请求体: {}", cozeApiUrl, requestBody);
|
||||
|
||||
// 发送请求
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
cozeApiUrl,
|
||||
HttpMethod.POST,
|
||||
request,
|
||||
String.class);
|
||||
|
||||
log.info("收到Coze总结初始响应: {}", response.getBody());
|
||||
|
||||
// 更新API调用记录的响应信息
|
||||
updateApiCallResponse(apiCall, response);
|
||||
|
||||
// 解析响应获取chat_id和conversation_id
|
||||
JSONObject responseJson = JSON.parseObject(response.getBody());
|
||||
String chatId = extractChatIdFromResponse(responseJson);
|
||||
String cozeConversationId = extractConversationIdFromResponse(responseJson);
|
||||
|
||||
if (chatId != null && cozeConversationId != null) {
|
||||
// 更新API调用记录的Coze ID信息
|
||||
updateApiCallCozeIds(apiCall, chatId, cozeConversationId);
|
||||
|
||||
// 轮询聊天状态直到完成并获取回复内容
|
||||
String aiReply = waitForChatCompletionWithTracking(chatId, cozeConversationId, apiCall);
|
||||
log.info("Coze AI总结响应成功: reply={}", aiReply);
|
||||
|
||||
// 更新API调用记录的最终结果
|
||||
updateApiCallSuccess(apiCall, aiReply);
|
||||
|
||||
return aiReply;
|
||||
} else {
|
||||
log.error("无法从Coze总结响应中获取chat_id或conversation_id");
|
||||
updateApiCallError(apiCall, "INVALID_RESPONSE", "无法从Coze总结响应中获取chat_id或conversation_id");
|
||||
return "抱歉,AI总结服务响应异常,请稍后再试。";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmotionSummaryGenerateResponse generateEmotionSummaryWithResponse(String userId) {
|
||||
log.info("生成用户情绪记录总结: userId={}", userId);
|
||||
@@ -719,9 +731,11 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
|
||||
public boolean isServiceAvailable() {
|
||||
try {
|
||||
// 简单的健康检查
|
||||
return cozeApiToken != null && !cozeApiToken.isEmpty() &&
|
||||
chatBotId != null && !chatBotId.isEmpty();
|
||||
// 检查聊天场景的AI配置是否可用
|
||||
AiConfig chatConfig = aiConfigService.getBestConfig(USAGE_SCENARIO_CHAT, DEFAULT_ENVIRONMENT);
|
||||
return chatConfig != null &&
|
||||
chatConfig.getApiToken() != null && !chatConfig.getApiToken().isEmpty() &&
|
||||
chatConfig.getBotId() != null && !chatConfig.getBotId().isEmpty();
|
||||
} catch (Exception e) {
|
||||
log.error("检查AI服务可用性失败", e);
|
||||
return false;
|
||||
@@ -751,22 +765,40 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
* 执行Coze API调用的公共逻辑
|
||||
*/
|
||||
private String executeCozeApiCall(CozeApiCall apiCall, String conversationId, String userMessage, String userId) {
|
||||
// 获取聊天场景的AI配置
|
||||
AiConfig config = getChatAiConfig();
|
||||
|
||||
// 构建请求体 - 使用正确的Coze API格式
|
||||
Map<String, Object> requestBody = buildCozeRequestWithConfig(conversationId, userMessage, userId, config);
|
||||
|
||||
// 检查是否使用流式输出
|
||||
boolean useStream = (Boolean) requestBody.get("stream");
|
||||
|
||||
if (useStream) {
|
||||
return executeStreamCozeApiCall(apiCall, config, requestBody, conversationId, userMessage, userId);
|
||||
} else {
|
||||
return executeNormalCozeApiCall(apiCall, config, requestBody, conversationId, userMessage, userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行普通(非流式)Coze API调用
|
||||
*/
|
||||
private String executeNormalCozeApiCall(CozeApiCall apiCall, AiConfig config, Map<String, Object> requestBody,
|
||||
String conversationId, String userMessage, String userId) {
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + cozeApiToken);
|
||||
headers.set("Authorization", "Bearer " + config.getApiToken());
|
||||
headers.set("Content-Type", "application/json");
|
||||
|
||||
// 构建请求体 - 使用正确的Coze API格式
|
||||
Map<String, Object> requestBody = buildCozeRequest(conversationId, userMessage, userId);
|
||||
|
||||
// 构建完整的API URL
|
||||
String cozeApiUrl = config.getApiBaseUrl() + getApiPath(config);
|
||||
|
||||
// 更新API调用记录的请求信息
|
||||
updateApiCallRequest(apiCall, cozeBaseUrl + chatPath, requestBody, headers);
|
||||
updateApiCallRequest(apiCall, cozeApiUrl, requestBody, headers);
|
||||
|
||||
HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
|
||||
|
||||
// 构建完整的API URL
|
||||
String cozeApiUrl = cozeBaseUrl + chatPath;
|
||||
log.info("发送Coze请求到: {}, 请求体: {}", cozeApiUrl, requestBody);
|
||||
log.info("发送Coze普通请求到: {}, 请求体: {}", cozeApiUrl, requestBody);
|
||||
|
||||
// 发送请求
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
@@ -791,7 +823,7 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
|
||||
// 轮询聊天状态直到完成并获取回复内容
|
||||
String aiReply = waitForChatCompletionWithTracking(chatId, cozeConversationId, apiCall);
|
||||
log.info("Coze AI响应成功: reply={}", aiReply);
|
||||
log.info("Coze AI普通响应成功: reply={}", aiReply);
|
||||
|
||||
// 更新API调用记录的最终结果
|
||||
updateApiCallSuccess(apiCall, aiReply);
|
||||
@@ -804,6 +836,165 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行流式Coze API调用
|
||||
*/
|
||||
private String executeStreamCozeApiCall(CozeApiCall apiCall, AiConfig config, Map<String, Object> requestBody,
|
||||
String conversationId, String userMessage, String userId) {
|
||||
try {
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + config.getApiToken());
|
||||
headers.set("Content-Type", "application/json");
|
||||
headers.set("Accept", "text/event-stream");
|
||||
|
||||
// 构建完整的API URL
|
||||
String cozeApiUrl = config.getApiBaseUrl() + getApiPath(config);
|
||||
|
||||
// 更新API调用记录的请求信息
|
||||
updateApiCallRequest(apiCall, cozeApiUrl, requestBody, headers);
|
||||
|
||||
log.info("发送Coze流式请求到: {}, 请求体: {}", cozeApiUrl, requestBody);
|
||||
|
||||
// 使用RestTemplate处理流式响应
|
||||
String streamResponse = handleStreamResponse(cozeApiUrl, headers, requestBody, apiCall);
|
||||
|
||||
log.info("Coze AI流式响应完成: length={}", streamResponse != null ? streamResponse.length() : 0);
|
||||
|
||||
// 更新API调用记录的最终结果
|
||||
updateApiCallSuccess(apiCall, streamResponse);
|
||||
|
||||
return streamResponse;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("流式API调用失败", e);
|
||||
updateApiCallError(apiCall, "STREAM_ERROR", e.getMessage());
|
||||
return "抱歉,AI流式服务暂时不可用,请稍后再试。";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理流式响应
|
||||
*/
|
||||
private String handleStreamResponse(String url, HttpHeaders headers, Map<String, Object> requestBody, CozeApiCall apiCall) {
|
||||
try {
|
||||
// 创建HTTP客户端
|
||||
java.net.http.HttpClient client = java.net.http.HttpClient.newBuilder()
|
||||
.connectTimeout(java.time.Duration.ofSeconds(30))
|
||||
.build();
|
||||
|
||||
// 构建请求
|
||||
java.net.http.HttpRequest.Builder requestBuilder = java.net.http.HttpRequest.newBuilder()
|
||||
.uri(java.net.URI.create(url))
|
||||
.timeout(java.time.Duration.ofMinutes(5))
|
||||
.POST(java.net.http.HttpRequest.BodyPublishers.ofString(JSON.toJSONString(requestBody)));
|
||||
|
||||
// 添加请求头
|
||||
headers.forEach((key, values) -> {
|
||||
if (values != null && !values.isEmpty()) {
|
||||
requestBuilder.header(key, values.get(0));
|
||||
}
|
||||
});
|
||||
|
||||
java.net.http.HttpRequest request = requestBuilder.build();
|
||||
|
||||
// 发送请求并处理流式响应
|
||||
StringBuilder responseBuilder = new StringBuilder();
|
||||
StringBuilder fullStreamData = new StringBuilder();
|
||||
|
||||
java.net.http.HttpResponse<java.util.stream.Stream<String>> response = client.send(request,
|
||||
java.net.http.HttpResponse.BodyHandlers.ofLines());
|
||||
|
||||
log.info("流式响应状态码: {}", response.statusCode());
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
String errorBody = response.body().collect(java.util.stream.Collectors.joining("\n"));
|
||||
log.error("流式请求失败,状态码: {}, 响应: {}", response.statusCode(), errorBody);
|
||||
return "流式请求失败,状态码: " + response.statusCode();
|
||||
}
|
||||
|
||||
// 处理流式数据
|
||||
response.body().forEach(line -> {
|
||||
fullStreamData.append(line).append("\n");
|
||||
|
||||
if (line.startsWith("data: ")) {
|
||||
String data = line.substring(6).trim();
|
||||
|
||||
if ("[DONE]".equals(data)) {
|
||||
log.info("流式响应完成");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject jsonData = JSON.parseObject(data);
|
||||
|
||||
// 提取消息内容
|
||||
if (jsonData.containsKey("choices")) {
|
||||
com.alibaba.fastjson2.JSONArray choices = jsonData.getJSONArray("choices");
|
||||
if (choices != null && !choices.isEmpty()) {
|
||||
JSONObject choice = choices.getJSONObject(0);
|
||||
if (choice != null && choice.containsKey("delta")) {
|
||||
JSONObject delta = choice.getJSONObject("delta");
|
||||
if (delta != null && delta.containsKey("content")) {
|
||||
String content = delta.getString("content");
|
||||
if (content != null) {
|
||||
responseBuilder.append(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coze特定格式处理
|
||||
if (jsonData.containsKey("event")) {
|
||||
String event = jsonData.getString("event");
|
||||
if ("conversation.message.delta".equals(event) && jsonData.containsKey("data")) {
|
||||
JSONObject eventData = jsonData.getJSONObject("data");
|
||||
if (eventData != null && eventData.containsKey("content")) {
|
||||
String content = eventData.getString("content");
|
||||
if (content != null) {
|
||||
responseBuilder.append(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("解析流式数据失败: {}, 数据: {}", e.getMessage(), data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 记录完整的流式数据用于调试
|
||||
updateApiCallStreamData(apiCall, fullStreamData.toString());
|
||||
|
||||
String finalResponse = responseBuilder.toString();
|
||||
if (finalResponse.isEmpty()) {
|
||||
log.warn("流式响应为空,返回默认消息");
|
||||
return "收到了流式响应,但内容为空。";
|
||||
}
|
||||
|
||||
return finalResponse;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理流式响应失败", e);
|
||||
throw new RuntimeException("处理流式响应失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新API调用记录的流式数据
|
||||
*/
|
||||
private void updateApiCallStreamData(CozeApiCall apiCall, String streamData) {
|
||||
try {
|
||||
// 可以将流式数据存储到响应体字段中,用于调试和分析
|
||||
apiCall.setResponseBody(streamData);
|
||||
cozeApiCallService.updateById(apiCall);
|
||||
} catch (Exception e) {
|
||||
log.error("更新API调用记录流式数据失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> guestChat(String message, String clientIp) {
|
||||
log.info("访客聊天: message={}, clientIp={}", message, clientIp);
|
||||
|
||||
@@ -883,16 +1074,28 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
* 构建Coze API请求 - 根据官方文档修正格式
|
||||
*/
|
||||
private Map<String, Object> buildCozeRequest(String conversationId, String userMessage, String userId) {
|
||||
// 获取聊天场景的AI配置
|
||||
AiConfig config = getChatAiConfig();
|
||||
return buildCozeRequestWithConfig(conversationId, userMessage, userId, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定配置构建Coze API请求
|
||||
*/
|
||||
private Map<String, Object> buildCozeRequestWithConfig(String conversationId, String userMessage, String userId, AiConfig config) {
|
||||
Map<String, Object> cozeRequest = new HashMap<>();
|
||||
cozeRequest.put("bot_id", chatBotId);
|
||||
cozeRequest.put("bot_id", config.getBotId());
|
||||
|
||||
// 如果有workflow_id,则添加
|
||||
if (chatWorkflowId != null && !chatWorkflowId.trim().isEmpty()) {
|
||||
cozeRequest.put("workflow_id", chatWorkflowId);
|
||||
if (config.getWorkflowId() != null && !config.getWorkflowId().trim().isEmpty()) {
|
||||
cozeRequest.put("workflow_id", config.getWorkflowId());
|
||||
}
|
||||
|
||||
cozeRequest.put("user_id", userId != null ? userId : DEFAULT_USER_ID);
|
||||
cozeRequest.put("stream", false);
|
||||
|
||||
// 根据配置决定是否使用流式输出
|
||||
boolean useStream = config.getSupportStream() != null && config.getSupportStream() == 1;
|
||||
cozeRequest.put("stream", useStream);
|
||||
|
||||
// 构建消息列表 - 按照 Coze API 标准格式
|
||||
java.util.List<Map<String, Object>> messages = new java.util.ArrayList<>();
|
||||
@@ -944,6 +1147,9 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
*/
|
||||
private String waitForChatCompletion(String chatId, String conversationId) {
|
||||
try {
|
||||
// 获取聊天场景的AI配置
|
||||
AiConfig config = getChatAiConfig();
|
||||
|
||||
// 最多等待30秒,每2秒轮询一次
|
||||
int maxAttempts = 15;
|
||||
int attempt = 0;
|
||||
@@ -952,12 +1158,12 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
log.info("轮询聊天状态,第{}次尝试: chatId={}, conversationId={}", attempt + 1, chatId, conversationId);
|
||||
|
||||
// 构建状态查询URL
|
||||
String statusUrl = cozeBaseUrl + "/v3/chat/retrieve?chat_id=" + chatId + "&conversation_id="
|
||||
String statusUrl = config.getApiBaseUrl() + "/v3/chat/retrieve?chat_id=" + chatId + "&conversation_id="
|
||||
+ conversationId;
|
||||
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + cozeApiToken);
|
||||
headers.set("Authorization", "Bearer " + config.getApiToken());
|
||||
headers.set("Content-Type", "application/json");
|
||||
|
||||
HttpEntity<String> request = new HttpEntity<>(headers);
|
||||
@@ -1009,15 +1215,18 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
*/
|
||||
private String getChatMessages(String chatId, String conversationId) {
|
||||
try {
|
||||
// 获取聊天场景的AI配置
|
||||
AiConfig config = getChatAiConfig();
|
||||
|
||||
log.info("获取聊天消息: chatId={}, conversationId={}", chatId, conversationId);
|
||||
|
||||
// 构建消息查询URL
|
||||
String messagesUrl = cozeBaseUrl + "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id="
|
||||
String messagesUrl = config.getApiBaseUrl() + "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id="
|
||||
+ conversationId;
|
||||
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + cozeApiToken);
|
||||
headers.set("Authorization", "Bearer " + config.getApiToken());
|
||||
headers.set("Content-Type", "application/json");
|
||||
|
||||
HttpEntity<String> request = new HttpEntity<>(headers);
|
||||
@@ -1067,16 +1276,22 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
* 构建总结请求 - 使用专门的总结bot和workflow
|
||||
*/
|
||||
private Map<String, Object> buildSummaryRequest(String conversationId, String userMessage, String userId) {
|
||||
// 获取总结场景的AI配置
|
||||
AiConfig config = getSummaryAiConfig();
|
||||
|
||||
Map<String, Object> cozeRequest = new HashMap<>();
|
||||
cozeRequest.put("bot_id", summaryBotId != null && !summaryBotId.trim().isEmpty() ? summaryBotId : chatBotId);
|
||||
cozeRequest.put("bot_id", config.getBotId());
|
||||
|
||||
// 如果有总结workflow_id,则添加
|
||||
if (summaryWorkflowId != null && !summaryWorkflowId.trim().isEmpty()) {
|
||||
cozeRequest.put("workflow_id", summaryWorkflowId);
|
||||
// 如果有workflow_id,则添加
|
||||
if (config.getWorkflowId() != null && !config.getWorkflowId().trim().isEmpty()) {
|
||||
cozeRequest.put("workflow_id", config.getWorkflowId());
|
||||
}
|
||||
|
||||
cozeRequest.put("user_id", userId != null ? userId : DEFAULT_USER_ID);
|
||||
cozeRequest.put("stream", false);
|
||||
|
||||
// 根据配置决定是否使用流式输出
|
||||
boolean useStream = config.getSupportStream() != null && config.getSupportStream() == 1;
|
||||
cozeRequest.put("stream", useStream);
|
||||
cozeRequest.put("auto_save_history", true);
|
||||
|
||||
// 如果有会话ID,则添加
|
||||
@@ -1118,6 +1333,9 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
*/
|
||||
private CozeApiCall createApiCallRecord(String conversationId, String messageId, String userMessage, String userId,
|
||||
String requestType) {
|
||||
// 获取聊天场景的AI配置
|
||||
AiConfig config = getChatAiConfig();
|
||||
|
||||
CozeApiCall apiCall = CozeApiCall.builder()
|
||||
.conversationId(conversationId)
|
||||
.messageId(messageId) // 设置messageId
|
||||
@@ -1125,8 +1343,8 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
.requestType(requestType)
|
||||
.userMessage(userMessage)
|
||||
.userMessageType("text")
|
||||
.botId(chatBotId)
|
||||
.workflowId(chatWorkflowId)
|
||||
.botId(config.getBotId())
|
||||
.workflowId(config.getWorkflowId())
|
||||
.status("pending")
|
||||
.startTime(LocalDateTime.now())
|
||||
.traceId(UUID.randomUUID().toString())
|
||||
@@ -1158,6 +1376,9 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
*/
|
||||
private CozeApiCall createSummaryApiCallRecord(String conversationId, String messageId, String userMessage,
|
||||
String userId, String requestType) {
|
||||
// 获取总结场景的AI配置
|
||||
AiConfig config = getSummaryAiConfig();
|
||||
|
||||
CozeApiCall apiCall = CozeApiCall.builder()
|
||||
.conversationId(conversationId)
|
||||
.messageId(messageId) // 设置messageId
|
||||
@@ -1165,8 +1386,8 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
.requestType(requestType)
|
||||
.userMessage(userMessage)
|
||||
.userMessageType("text")
|
||||
.botId(summaryBotId)
|
||||
.workflowId(summaryWorkflowId)
|
||||
.botId(config.getBotId())
|
||||
.workflowId(config.getWorkflowId())
|
||||
.status("pending")
|
||||
.startTime(LocalDateTime.now())
|
||||
.traceId(UUID.randomUUID().toString())
|
||||
@@ -1505,4 +1726,36 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天场景的AI配置
|
||||
*/
|
||||
private AiConfig getChatAiConfig() {
|
||||
AiConfig config = aiConfigService.getBestConfig(USAGE_SCENARIO_CHAT, DEFAULT_ENVIRONMENT);
|
||||
if (config == null) {
|
||||
log.error("未找到聊天场景的AI配置");
|
||||
throw new RuntimeException("未找到聊天场景的AI配置,请先在管理后台配置");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取总结场景的AI配置
|
||||
*/
|
||||
private AiConfig getSummaryAiConfig() {
|
||||
AiConfig config = aiConfigService.getBestConfig(USAGE_SCENARIO_SUMMARY, DEFAULT_ENVIRONMENT);
|
||||
if (config == null) {
|
||||
log.warn("未找到总结场景的AI配置,使用聊天配置");
|
||||
return getChatAiConfig();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置的API路径
|
||||
*/
|
||||
private String getApiPath(AiConfig config) {
|
||||
// 默认使用 /v3/chat 路径
|
||||
return "/v3/chat";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user