|
@@ -36,6 +36,9 @@ public class AIService {
|
|
|
@Resource
|
|
@Resource
|
|
|
private AiGateway aiGateway;
|
|
private AiGateway aiGateway;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private PiisService piisService;
|
|
|
|
|
+
|
|
|
@Value("${dify.base-url}")
|
|
@Value("${dify.base-url}")
|
|
|
private String difyBaseUrl;
|
|
private String difyBaseUrl;
|
|
|
|
|
|
|
@@ -100,16 +103,21 @@ public class AIService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private void mirrorConversation(Long userId, String difyConversationId,
|
|
private void mirrorConversation(Long userId, String difyConversationId,
|
|
|
- String assistantType, String userQuery,
|
|
|
|
|
- String assistantAnswer, Map<String, Object> inputs) {
|
|
|
|
|
|
|
+ String assistantType, String userQuery,
|
|
|
|
|
+ String assistantAnswer, Map<String, Object> inputs) {
|
|
|
try {
|
|
try {
|
|
|
|
|
+ // PII脱敏
|
|
|
|
|
+ userQuery = piisService.sanitizeText(userQuery);
|
|
|
|
|
+ assistantAnswer = piisService.sanitizeText(assistantAnswer);
|
|
|
|
|
+ Map<String, Object> sanitizedInputs = piisService.sanitizeContext(inputs, userId);
|
|
|
|
|
+
|
|
|
Long convId = chatMirrorService.upsertConversation(userId, difyConversationId, assistantType, null);
|
|
Long convId = chatMirrorService.upsertConversation(userId, difyConversationId, assistantType, null);
|
|
|
List<Map<String, Object>> messages = new ArrayList<>();
|
|
List<Map<String, Object>> messages = new ArrayList<>();
|
|
|
Map<String, Object> userMsg = new LinkedHashMap<>();
|
|
Map<String, Object> userMsg = new LinkedHashMap<>();
|
|
|
userMsg.put("role", "user");
|
|
userMsg.put("role", "user");
|
|
|
userMsg.put("content", userQuery);
|
|
userMsg.put("content", userQuery);
|
|
|
- if (inputs != null && !inputs.isEmpty()) {
|
|
|
|
|
- userMsg.put("inputs", inputs);
|
|
|
|
|
|
|
+ if (sanitizedInputs != null && !sanitizedInputs.isEmpty()) {
|
|
|
|
|
+ userMsg.put("inputs", sanitizedInputs);
|
|
|
}
|
|
}
|
|
|
messages.add(userMsg);
|
|
messages.add(userMsg);
|
|
|
Map<String, Object> assistantMsg = new LinkedHashMap<>();
|
|
Map<String, Object> assistantMsg = new LinkedHashMap<>();
|
|
@@ -126,8 +134,12 @@ public class AIService {
|
|
|
* 发送聊天消息到精准营养助手(使用专用 API Key)
|
|
* 发送聊天消息到精准营养助手(使用专用 API Key)
|
|
|
*/
|
|
*/
|
|
|
public Map<String, Object> sendNutritionMessage(String query, String userId,
|
|
public Map<String, Object> sendNutritionMessage(String query, String userId,
|
|
|
- String conversationId,
|
|
|
|
|
- Map<String, Object> inputs) {
|
|
|
|
|
|
|
+ String conversationId,
|
|
|
|
|
+ Map<String, Object> inputs) {
|
|
|
|
|
+ // PII脱敏
|
|
|
|
|
+ query = piisService.sanitizeText(query);
|
|
|
|
|
+ Map<String, Object> sanitizedInputs = piisService.sanitizeContext(inputs, Long.valueOf(userId));
|
|
|
|
|
+
|
|
|
String url = difyBaseUrl + "/chat-messages";
|
|
String url = difyBaseUrl + "/chat-messages";
|
|
|
|
|
|
|
|
Map<String, Object> body = new LinkedHashMap<>();
|
|
Map<String, Object> body = new LinkedHashMap<>();
|
|
@@ -135,7 +147,7 @@ public class AIService {
|
|
|
body.put("user", userId);
|
|
body.put("user", userId);
|
|
|
body.put("response_mode", "blocking");
|
|
body.put("response_mode", "blocking");
|
|
|
body.put("conversation_id", conversationId != null ? conversationId : "");
|
|
body.put("conversation_id", conversationId != null ? conversationId : "");
|
|
|
- body.put("inputs", inputs != null ? inputs : Collections.emptyMap());
|
|
|
|
|
|
|
+ body.put("inputs", sanitizedInputs != null ? sanitizedInputs : Collections.emptyMap());
|
|
|
body.put("auto_generate_name", true);
|
|
body.put("auto_generate_name", true);
|
|
|
|
|
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
HttpHeaders headers = new HttpHeaders();
|
|
@@ -206,9 +218,14 @@ public class AIService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public Map<String, Object> runWorkflow(String workflowId, Map<String, Object> inputs, String userId) {
|
|
public Map<String, Object> runWorkflow(String workflowId, Map<String, Object> inputs, String userId) {
|
|
|
|
|
+ // PII脱敏
|
|
|
|
|
+ Long uid = null;
|
|
|
|
|
+ try { uid = Long.valueOf(userId); } catch (Exception ignored) {}
|
|
|
|
|
+ Map<String, Object> sanitizedInputs = piisService.sanitizeContext(inputs, uid);
|
|
|
|
|
+
|
|
|
String url = difyBaseUrl + "/workflows/" + workflowId + "/run";
|
|
String url = difyBaseUrl + "/workflows/" + workflowId + "/run";
|
|
|
Map<String, Object> body = new LinkedHashMap<>();
|
|
Map<String, Object> body = new LinkedHashMap<>();
|
|
|
- body.put("inputs", inputs != null ? inputs : Collections.emptyMap());
|
|
|
|
|
|
|
+ body.put("inputs", sanitizedInputs != null ? sanitizedInputs : Collections.emptyMap());
|
|
|
body.put("response_mode", "blocking");
|
|
body.put("response_mode", "blocking");
|
|
|
body.put("user", userId);
|
|
body.put("user", userId);
|
|
|
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, authHeaders());
|
|
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, authHeaders());
|
|
@@ -235,17 +252,20 @@ public class AIService {
|
|
|
*/
|
|
*/
|
|
|
@SuppressWarnings("unchecked")
|
|
@SuppressWarnings("unchecked")
|
|
|
public Map<String, Object> sendTongueDiagnosis(String imageUrl, String userId, Map<String, Object> inputs) {
|
|
public Map<String, Object> sendTongueDiagnosis(String imageUrl, String userId, Map<String, Object> inputs) {
|
|
|
- // 如果未配置 tongue-api-key,返回模拟数据以便开发
|
|
|
|
|
if (tongueApiKey == null || tongueApiKey.isEmpty()) {
|
|
if (tongueApiKey == null || tongueApiKey.isEmpty()) {
|
|
|
return mockTongueResult();
|
|
return mockTongueResult();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 调用 Dify Workflow API 分析舌象
|
|
|
|
|
|
|
+ // PII脱敏
|
|
|
|
|
+ Long uid = null;
|
|
|
|
|
+ try { uid = Long.valueOf(userId); } catch (Exception ignored) {}
|
|
|
|
|
+ Map<String, Object> sanitizedInputs = piisService.sanitizeContext(inputs, uid);
|
|
|
|
|
+
|
|
|
String url = difyBaseUrl + "/workflows/run";
|
|
String url = difyBaseUrl + "/workflows/run";
|
|
|
HttpHeaders headers = tongueAuthHeaders();
|
|
HttpHeaders headers = tongueAuthHeaders();
|
|
|
|
|
|
|
|
Map<String, Object> body = new LinkedHashMap<>();
|
|
Map<String, Object> body = new LinkedHashMap<>();
|
|
|
- body.put("inputs", inputs != null ? inputs : Collections.emptyMap());
|
|
|
|
|
|
|
+ body.put("inputs", sanitizedInputs != null ? sanitizedInputs : Collections.emptyMap());
|
|
|
body.put("user", userId);
|
|
body.put("user", userId);
|
|
|
body.put("response_mode", "blocking");
|
|
body.put("response_mode", "blocking");
|
|
|
|
|
|
|
@@ -253,7 +273,7 @@ public class AIService {
|
|
|
Map<String, Object> fileInput = new HashMap<>();
|
|
Map<String, Object> fileInput = new HashMap<>();
|
|
|
fileInput.put("type", "image");
|
|
fileInput.put("type", "image");
|
|
|
fileInput.put("url", imageUrl);
|
|
fileInput.put("url", imageUrl);
|
|
|
- inputs.put("tongue_image", fileInput);
|
|
|
|
|
|
|
+ sanitizedInputs.put("tongue_image", fileInput);
|
|
|
|
|
|
|
|
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);
|
|
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);
|
|
|
try {
|
|
try {
|
|
@@ -361,7 +381,7 @@ public class AIService {
|
|
|
SortUtil.applySort(summaryQ);
|
|
SortUtil.applySort(summaryQ);
|
|
|
AiConversationSummary currentSummary = aiConversationSummaryMapper.selectOne(summaryQ);
|
|
AiConversationSummary currentSummary = aiConversationSummaryMapper.selectOne(summaryQ);
|
|
|
if (currentSummary != null) {
|
|
if (currentSummary != null) {
|
|
|
- ctx.put("conversation_summary", currentSummary.getSummaryText());
|
|
|
|
|
|
|
+ ctx.put("conversation_summary", piisService.sanitizeText(currentSummary.getSummaryText()));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 用户近期摘要(最近5条)
|
|
// 用户近期摘要(最近5条)
|
|
@@ -374,6 +394,7 @@ public class AIService {
|
|
|
List<String> summaries = recentList.stream()
|
|
List<String> summaries = recentList.stream()
|
|
|
.map(AiConversationSummary::getSummaryText)
|
|
.map(AiConversationSummary::getSummaryText)
|
|
|
.filter(Objects::nonNull)
|
|
.filter(Objects::nonNull)
|
|
|
|
|
+ .map(s -> piisService.sanitizeText(s))
|
|
|
.collect(Collectors.toList());
|
|
.collect(Collectors.toList());
|
|
|
if (!summaries.isEmpty()) {
|
|
if (!summaries.isEmpty()) {
|
|
|
ctx.put("recent_summaries", summaries);
|
|
ctx.put("recent_summaries", summaries);
|
|
@@ -387,7 +408,7 @@ public class AIService {
|
|
|
if (!facts.isEmpty()) {
|
|
if (!facts.isEmpty()) {
|
|
|
Map<String, String> factMap = new LinkedHashMap<>();
|
|
Map<String, String> factMap = new LinkedHashMap<>();
|
|
|
for (AiUserFact f : facts) {
|
|
for (AiUserFact f : facts) {
|
|
|
- factMap.put(f.getFactKey(), f.getFactValue());
|
|
|
|
|
|
|
+ factMap.put(piisService.sanitizeText(f.getFactKey()), piisService.sanitizeText(f.getFactValue()));
|
|
|
}
|
|
}
|
|
|
ctx.put("user_facts", factMap);
|
|
ctx.put("user_facts", factMap);
|
|
|
}
|
|
}
|
|
@@ -401,9 +422,12 @@ public class AIService {
|
|
|
@Async
|
|
@Async
|
|
|
public void requestSummaryAsync(Long userId, String conversationId, String recentMessages) {
|
|
public void requestSummaryAsync(Long userId, String conversationId, String recentMessages) {
|
|
|
try {
|
|
try {
|
|
|
|
|
+ // PII脱敏
|
|
|
|
|
+ String sanitizedMessages = piisService.sanitizeText(recentMessages);
|
|
|
|
|
+
|
|
|
Map<String, Object> inputs = new LinkedHashMap<>();
|
|
Map<String, Object> inputs = new LinkedHashMap<>();
|
|
|
inputs.put("conversation_id", conversationId);
|
|
inputs.put("conversation_id", conversationId);
|
|
|
- inputs.put("messages_text", recentMessages);
|
|
|
|
|
|
|
+ inputs.put("messages_text", sanitizedMessages);
|
|
|
|
|
|
|
|
String summaryUrl = difyBaseUrl + "/workflows/run";
|
|
String summaryUrl = difyBaseUrl + "/workflows/run";
|
|
|
Map<String, Object> body = new LinkedHashMap<>();
|
|
Map<String, Object> body = new LinkedHashMap<>();
|
|
@@ -441,13 +465,13 @@ public class AIService {
|
|
|
* 在 sendMessage 之前,用记忆数据丰富 inputs
|
|
* 在 sendMessage 之前,用记忆数据丰富 inputs
|
|
|
*/
|
|
*/
|
|
|
public Map<String, Object> enrichInputsWithMemory(Long userId, String conversationId,
|
|
public Map<String, Object> enrichInputsWithMemory(Long userId, String conversationId,
|
|
|
- Map<String, Object> originalInputs) {
|
|
|
|
|
|
|
+ Map<String, Object> originalInputs) {
|
|
|
Map<String, Object> enriched = new LinkedHashMap<>();
|
|
Map<String, Object> enriched = new LinkedHashMap<>();
|
|
|
if (originalInputs != null) {
|
|
if (originalInputs != null) {
|
|
|
enriched.putAll(originalInputs);
|
|
enriched.putAll(originalInputs);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 最近摘要
|
|
|
|
|
|
|
+ // 最近摘要(脱敏后注入)
|
|
|
LambdaQueryWrapper<AiConversationSummary> recentQ = new LambdaQueryWrapper<>();
|
|
LambdaQueryWrapper<AiConversationSummary> recentQ = new LambdaQueryWrapper<>();
|
|
|
recentQ.eq(AiConversationSummary::getUserId, userId);
|
|
recentQ.eq(AiConversationSummary::getUserId, userId);
|
|
|
recentQ.orderByDesc(AiConversationSummary::getCreatedAt);
|
|
recentQ.orderByDesc(AiConversationSummary::getCreatedAt);
|
|
@@ -457,12 +481,13 @@ public class AIService {
|
|
|
List<String> summaries = recentList.stream()
|
|
List<String> summaries = recentList.stream()
|
|
|
.map(AiConversationSummary::getSummaryText)
|
|
.map(AiConversationSummary::getSummaryText)
|
|
|
.filter(Objects::nonNull)
|
|
.filter(Objects::nonNull)
|
|
|
|
|
+ .map(s -> piisService.sanitizeText(s))
|
|
|
.collect(Collectors.toList());
|
|
.collect(Collectors.toList());
|
|
|
if (!summaries.isEmpty()) {
|
|
if (!summaries.isEmpty()) {
|
|
|
enriched.put("conversation_summary", String.join("\n---\n", summaries));
|
|
enriched.put("conversation_summary", String.join("\n---\n", summaries));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 用户事实
|
|
|
|
|
|
|
+ // 用户事实(脱敏后注入)
|
|
|
LambdaQueryWrapper<AiUserFact> factQ = new LambdaQueryWrapper<>();
|
|
LambdaQueryWrapper<AiUserFact> factQ = new LambdaQueryWrapper<>();
|
|
|
factQ.eq(AiUserFact::getUserId, userId);
|
|
factQ.eq(AiUserFact::getUserId, userId);
|
|
|
SortUtil.applySort(factQ);
|
|
SortUtil.applySort(factQ);
|
|
@@ -470,7 +495,11 @@ public class AIService {
|
|
|
if (!facts.isEmpty()) {
|
|
if (!facts.isEmpty()) {
|
|
|
StringBuilder factSb = new StringBuilder();
|
|
StringBuilder factSb = new StringBuilder();
|
|
|
for (AiUserFact f : facts) {
|
|
for (AiUserFact f : facts) {
|
|
|
- factSb.append(f.getFactKey()).append(": ").append(f.getFactValue()).append("\n");
|
|
|
|
|
|
|
+ String key = piisService.sanitizeText(f.getFactKey());
|
|
|
|
|
+ String value = piisService.sanitizeText(f.getFactValue());
|
|
|
|
|
+ if (key != null && !key.isEmpty()) {
|
|
|
|
|
+ factSb.append(key).append(": ").append(value != null ? value : "").append("\n");
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
enriched.put("user_facts", factSb.toString());
|
|
enriched.put("user_facts", factSb.toString());
|
|
|
}
|
|
}
|