对话bug修复
This commit is contained in:
@@ -901,6 +901,7 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
// 发送请求并处理流式响应
|
||||
StringBuilder responseBuilder = new StringBuilder();
|
||||
StringBuilder fullStreamData = new StringBuilder();
|
||||
final String[] currentEvent = {null}; // 使用数组来解决lambda中的final问题
|
||||
|
||||
java.net.http.HttpResponse<java.util.stream.Stream<String>> response = client.send(request,
|
||||
java.net.http.HttpResponse.BodyHandlers.ofLines());
|
||||
@@ -913,72 +914,143 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
return "流式请求失败,状态码: " + response.statusCode();
|
||||
}
|
||||
|
||||
// 处理流式数据
|
||||
// 处理流式数据 - 正确处理SSE格式
|
||||
response.body().forEach(line -> {
|
||||
fullStreamData.append(line).append("\n");
|
||||
log.debug("收到流式数据行: {}", line);
|
||||
|
||||
if (line.startsWith("data: ")) {
|
||||
// 处理SSE事件格式
|
||||
if (line.startsWith("event:")) {
|
||||
currentEvent[0] = line.substring(6).trim();
|
||||
log.debug("当前事件类型: {}", currentEvent[0]);
|
||||
} else if (line.startsWith("data: ")) {
|
||||
String data = line.substring(6).trim();
|
||||
log.debug("解析流式数据: {}", data);
|
||||
|
||||
// 检查是否为结束标记
|
||||
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);
|
||||
}
|
||||
}
|
||||
// 只处理消息增量事件
|
||||
if ("conversation.message.delta".equals(currentEvent[0])) {
|
||||
try {
|
||||
JSONObject jsonData = JSON.parseObject(data);
|
||||
log.debug("解析后的JSON数据: {}", jsonData);
|
||||
|
||||
// 提取content字段
|
||||
if (jsonData.containsKey("content")) {
|
||||
String content = jsonData.getString("content");
|
||||
if (content != null && !content.trim().isEmpty()) {
|
||||
log.debug("提取增量内容: {}", content);
|
||||
responseBuilder.append(content);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("解析流式数据失败: {}, 数据: {}", e.getMessage(), data);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
} else if ("conversation.message.completed".equals(currentEvent[0])) {
|
||||
// 处理完整消息事件,可以用作备用方案
|
||||
try {
|
||||
JSONObject jsonData = JSON.parseObject(data);
|
||||
|
||||
// 检查是否为answer类型的消息
|
||||
if (jsonData.containsKey("type") && "answer".equals(jsonData.getString("type"))) {
|
||||
String content = jsonData.getString("content");
|
||||
if (content != null && !content.trim().isEmpty() && responseBuilder.length() == 0) {
|
||||
log.debug("使用完整消息内容作为备用: {}", content);
|
||||
responseBuilder.append(content);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("解析完整消息数据失败: {}, 数据: {}", e.getMessage(), data);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("解析流式数据失败: {}, 数据: {}", e.getMessage(), data);
|
||||
}
|
||||
|
||||
// 重置当前事件
|
||||
currentEvent[0] = null;
|
||||
} else if (line.trim().isEmpty()) {
|
||||
// 空行表示事件结束
|
||||
currentEvent[0] = null;
|
||||
}
|
||||
});
|
||||
|
||||
// 记录完整的流式数据用于调试
|
||||
updateApiCallStreamData(apiCall, fullStreamData.toString());
|
||||
log.info("流式响应处理完成,提取内容长度: {}", responseBuilder.length());
|
||||
|
||||
String finalResponse = responseBuilder.toString();
|
||||
if (finalResponse.isEmpty()) {
|
||||
log.warn("流式响应为空,返回默认消息");
|
||||
return "收到了流式响应,但内容为空。";
|
||||
log.warn("流式响应为空,尝试降级到非流式处理");
|
||||
// 降级到非流式处理
|
||||
return handleNonStreamFallback(url, headers, requestBody, apiCall);
|
||||
}
|
||||
|
||||
return finalResponse;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理流式响应失败", e);
|
||||
throw new RuntimeException("处理流式响应失败: " + e.getMessage(), e);
|
||||
// 降级到非流式处理
|
||||
return handleNonStreamFallback(url, headers, requestBody, apiCall);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 非流式处理降级方案
|
||||
*/
|
||||
private String handleNonStreamFallback(String url, HttpHeaders headers, Map<String, Object> requestBody, CozeApiCall apiCall) {
|
||||
try {
|
||||
log.info("执行非流式降级处理");
|
||||
|
||||
// 移除stream参数,改为非流式请求
|
||||
Map<String, Object> nonStreamBody = new HashMap<>(requestBody);
|
||||
nonStreamBody.put("stream", false);
|
||||
|
||||
// 构建请求头(移除流式相关头)
|
||||
HttpHeaders nonStreamHeaders = new HttpHeaders();
|
||||
headers.forEach((key, values) -> {
|
||||
if (!"Accept".equals(key) || !"text/event-stream".equals(values.get(0))) {
|
||||
nonStreamHeaders.put(key, values);
|
||||
}
|
||||
});
|
||||
nonStreamHeaders.set("Content-Type", "application/json");
|
||||
|
||||
HttpEntity<Map<String, Object>> request = new HttpEntity<>(nonStreamBody, nonStreamHeaders);
|
||||
log.info("发送非流式降级请求到: {}", url);
|
||||
|
||||
// 发送请求
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
request,
|
||||
String.class);
|
||||
|
||||
log.info("收到非流式降级响应: {}", response.getBody());
|
||||
|
||||
// 解析响应获取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("非流式降级处理成功: reply={}", aiReply);
|
||||
return aiReply;
|
||||
} else {
|
||||
log.error("非流式降级处理失败:无法从响应中获取chat_id或conversation_id");
|
||||
return "抱歉,AI服务暂时不可用,请稍后再试。";
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("非流式降级处理失败", e);
|
||||
return "抱歉,AI服务暂时不可用,请稍后再试。";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -987,8 +1059,14 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
*/
|
||||
private void updateApiCallStreamData(CozeApiCall apiCall, String streamData) {
|
||||
try {
|
||||
// 可以将流式数据存储到响应体字段中,用于调试和分析
|
||||
apiCall.setResponseBody(streamData);
|
||||
// 将流式数据包装成有效的JSON格式存储
|
||||
Map<String, Object> streamDataWrapper = new HashMap<>();
|
||||
streamDataWrapper.put("type", "stream");
|
||||
streamDataWrapper.put("data", streamData);
|
||||
streamDataWrapper.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
String jsonStreamData = JSON.toJSONString(streamDataWrapper);
|
||||
apiCall.setResponseBody(jsonStreamData);
|
||||
cozeApiCallService.updateById(apiCall);
|
||||
} catch (Exception e) {
|
||||
log.error("更新API调用记录流式数据失败: {}", e.getMessage(), e);
|
||||
@@ -1096,6 +1174,13 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
// 根据配置决定是否使用流式输出
|
||||
boolean useStream = config.getSupportStream() != null && config.getSupportStream() == 1;
|
||||
cozeRequest.put("stream", useStream);
|
||||
|
||||
// 添加auto_save_history参数,确保对话历史被保存
|
||||
cozeRequest.put("auto_save_history", true);
|
||||
|
||||
// 如果有conversationId,添加到请求中(但不是必需的,Coze会自动创建)
|
||||
// 注意:这里的conversationId是我们系统内部的ID,不是Coze的conversation_id
|
||||
// Coze会自动创建新的conversation_id
|
||||
|
||||
// 构建消息列表 - 按照 Coze API 标准格式
|
||||
java.util.List<Map<String, Object>> messages = new java.util.ArrayList<>();
|
||||
@@ -1105,11 +1190,14 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
currentMsg.put(ROLE_KEY, USER_ROLE);
|
||||
currentMsg.put(CONTENT_KEY, userMessage);
|
||||
currentMsg.put(CONTENT_TYPE_KEY, TEXT_TYPE);
|
||||
currentMsg.put("type", "question");
|
||||
// 移除type字段,可能不是必需的
|
||||
messages.add(currentMsg);
|
||||
|
||||
cozeRequest.put("additional_messages", messages);
|
||||
cozeRequest.put("parameters", new HashMap<>());
|
||||
|
||||
// 确保parameters不为空,添加一些默认参数
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
cozeRequest.put("parameters", parameters);
|
||||
|
||||
return cozeRequest;
|
||||
}
|
||||
@@ -1435,7 +1523,27 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
private void updateApiCallResponse(CozeApiCall apiCall, ResponseEntity<String> response) {
|
||||
try {
|
||||
apiCall.setResponseStatus(response.getStatusCodeValue());
|
||||
apiCall.setResponseBody(response.getBody());
|
||||
|
||||
// 确保响应体是有效的JSON格式
|
||||
String responseBody = response.getBody();
|
||||
if (responseBody != null && !responseBody.trim().isEmpty()) {
|
||||
try {
|
||||
// 尝试解析为JSON,如果失败则包装成JSON
|
||||
JSON.parseObject(responseBody);
|
||||
apiCall.setResponseBody(responseBody);
|
||||
} catch (Exception jsonException) {
|
||||
// 如果不是有效JSON,则包装成JSON格式
|
||||
Map<String, Object> wrapper = new HashMap<>();
|
||||
wrapper.put("type", "raw_response");
|
||||
wrapper.put("data", responseBody);
|
||||
wrapper.put("timestamp", System.currentTimeMillis());
|
||||
apiCall.setResponseBody(JSON.toJSONString(wrapper));
|
||||
}
|
||||
} else {
|
||||
// 空响应体,设置为空JSON对象
|
||||
apiCall.setResponseBody("{}");
|
||||
}
|
||||
|
||||
apiCall.setResponseHeaders(JSON.toJSONString(response.getHeaders().toSingleValueMap()));
|
||||
cozeApiCallService.updateById(apiCall);
|
||||
} catch (Exception e) {
|
||||
|
||||
Reference in New Issue
Block a user