对话bug修复
This commit is contained in:
@@ -901,6 +901,7 @@ public class AiChatServiceImpl implements AiChatService {
|
|||||||
// 发送请求并处理流式响应
|
// 发送请求并处理流式响应
|
||||||
StringBuilder responseBuilder = new StringBuilder();
|
StringBuilder responseBuilder = new StringBuilder();
|
||||||
StringBuilder fullStreamData = 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<java.util.stream.Stream<String>> response = client.send(request,
|
||||||
java.net.http.HttpResponse.BodyHandlers.ofLines());
|
java.net.http.HttpResponse.BodyHandlers.ofLines());
|
||||||
@@ -913,72 +914,143 @@ public class AiChatServiceImpl implements AiChatService {
|
|||||||
return "流式请求失败,状态码: " + response.statusCode();
|
return "流式请求失败,状态码: " + response.statusCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理流式数据
|
// 处理流式数据 - 正确处理SSE格式
|
||||||
response.body().forEach(line -> {
|
response.body().forEach(line -> {
|
||||||
fullStreamData.append(line).append("\n");
|
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();
|
String data = line.substring(6).trim();
|
||||||
|
log.debug("解析流式数据: {}", data);
|
||||||
|
|
||||||
|
// 检查是否为结束标记
|
||||||
if ("[DONE]".equals(data)) {
|
if ("[DONE]".equals(data)) {
|
||||||
log.info("流式响应完成");
|
log.info("流式响应完成");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// 只处理消息增量事件
|
||||||
JSONObject jsonData = JSON.parseObject(data);
|
if ("conversation.message.delta".equals(currentEvent[0])) {
|
||||||
|
try {
|
||||||
|
JSONObject jsonData = JSON.parseObject(data);
|
||||||
|
log.debug("解析后的JSON数据: {}", jsonData);
|
||||||
|
|
||||||
// 提取消息内容
|
// 提取content字段
|
||||||
if (jsonData.containsKey("choices")) {
|
if (jsonData.containsKey("content")) {
|
||||||
com.alibaba.fastjson2.JSONArray choices = jsonData.getJSONArray("choices");
|
String content = jsonData.getString("content");
|
||||||
if (choices != null && !choices.isEmpty()) {
|
if (content != null && !content.trim().isEmpty()) {
|
||||||
JSONObject choice = choices.getJSONObject(0);
|
log.debug("提取增量内容: {}", content);
|
||||||
if (choice != null && choice.containsKey("delta")) {
|
responseBuilder.append(content);
|
||||||
JSONObject delta = choice.getJSONObject("delta");
|
|
||||||
if (delta != null && delta.containsKey("content")) {
|
|
||||||
String content = delta.getString("content");
|
|
||||||
if (content != null) {
|
|
||||||
responseBuilder.append(content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Coze特定格式处理
|
} catch (Exception e) {
|
||||||
if (jsonData.containsKey("event")) {
|
log.warn("解析流式数据失败: {}, 数据: {}", e.getMessage(), data);
|
||||||
String event = jsonData.getString("event");
|
}
|
||||||
if ("conversation.message.delta".equals(event) && jsonData.containsKey("data")) {
|
} else if ("conversation.message.completed".equals(currentEvent[0])) {
|
||||||
JSONObject eventData = jsonData.getJSONObject("data");
|
// 处理完整消息事件,可以用作备用方案
|
||||||
if (eventData != null && eventData.containsKey("content")) {
|
try {
|
||||||
String content = eventData.getString("content");
|
JSONObject jsonData = JSON.parseObject(data);
|
||||||
if (content != null) {
|
|
||||||
responseBuilder.append(content);
|
// 检查是否为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) {
|
} catch (Exception e) {
|
||||||
log.warn("解析流式数据失败: {}, 数据: {}", e.getMessage(), data);
|
log.warn("解析完整消息数据失败: {}, 数据: {}", e.getMessage(), data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重置当前事件
|
||||||
|
currentEvent[0] = null;
|
||||||
|
} else if (line.trim().isEmpty()) {
|
||||||
|
// 空行表示事件结束
|
||||||
|
currentEvent[0] = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 记录完整的流式数据用于调试
|
// 记录完整的流式数据用于调试
|
||||||
updateApiCallStreamData(apiCall, fullStreamData.toString());
|
updateApiCallStreamData(apiCall, fullStreamData.toString());
|
||||||
|
log.info("流式响应处理完成,提取内容长度: {}", responseBuilder.length());
|
||||||
|
|
||||||
String finalResponse = responseBuilder.toString();
|
String finalResponse = responseBuilder.toString();
|
||||||
if (finalResponse.isEmpty()) {
|
if (finalResponse.isEmpty()) {
|
||||||
log.warn("流式响应为空,返回默认消息");
|
log.warn("流式响应为空,尝试降级到非流式处理");
|
||||||
return "收到了流式响应,但内容为空。";
|
// 降级到非流式处理
|
||||||
|
return handleNonStreamFallback(url, headers, requestBody, apiCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalResponse;
|
return finalResponse;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("处理流式响应失败", 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) {
|
private void updateApiCallStreamData(CozeApiCall apiCall, String streamData) {
|
||||||
try {
|
try {
|
||||||
// 可以将流式数据存储到响应体字段中,用于调试和分析
|
// 将流式数据包装成有效的JSON格式存储
|
||||||
apiCall.setResponseBody(streamData);
|
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);
|
cozeApiCallService.updateById(apiCall);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("更新API调用记录流式数据失败: {}", e.getMessage(), e);
|
log.error("更新API调用记录流式数据失败: {}", e.getMessage(), e);
|
||||||
@@ -1097,6 +1175,13 @@ public class AiChatServiceImpl implements AiChatService {
|
|||||||
boolean useStream = config.getSupportStream() != null && config.getSupportStream() == 1;
|
boolean useStream = config.getSupportStream() != null && config.getSupportStream() == 1;
|
||||||
cozeRequest.put("stream", useStream);
|
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 标准格式
|
// 构建消息列表 - 按照 Coze API 标准格式
|
||||||
java.util.List<Map<String, Object>> messages = new java.util.ArrayList<>();
|
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(ROLE_KEY, USER_ROLE);
|
||||||
currentMsg.put(CONTENT_KEY, userMessage);
|
currentMsg.put(CONTENT_KEY, userMessage);
|
||||||
currentMsg.put(CONTENT_TYPE_KEY, TEXT_TYPE);
|
currentMsg.put(CONTENT_TYPE_KEY, TEXT_TYPE);
|
||||||
currentMsg.put("type", "question");
|
// 移除type字段,可能不是必需的
|
||||||
messages.add(currentMsg);
|
messages.add(currentMsg);
|
||||||
|
|
||||||
cozeRequest.put("additional_messages", messages);
|
cozeRequest.put("additional_messages", messages);
|
||||||
cozeRequest.put("parameters", new HashMap<>());
|
|
||||||
|
// 确保parameters不为空,添加一些默认参数
|
||||||
|
Map<String, Object> parameters = new HashMap<>();
|
||||||
|
cozeRequest.put("parameters", parameters);
|
||||||
|
|
||||||
return cozeRequest;
|
return cozeRequest;
|
||||||
}
|
}
|
||||||
@@ -1435,7 +1523,27 @@ public class AiChatServiceImpl implements AiChatService {
|
|||||||
private void updateApiCallResponse(CozeApiCall apiCall, ResponseEntity<String> response) {
|
private void updateApiCallResponse(CozeApiCall apiCall, ResponseEntity<String> response) {
|
||||||
try {
|
try {
|
||||||
apiCall.setResponseStatus(response.getStatusCodeValue());
|
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()));
|
apiCall.setResponseHeaders(JSON.toJSONString(response.getHeaders().toSingleValueMap()));
|
||||||
cozeApiCallService.updateById(apiCall);
|
cozeApiCallService.updateById(apiCall);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user