Explorar o código

java的代码调整优化

liuchengsen hai 1 mes
pai
achega
a643a40858

+ 31 - 12
backend-java/src/main/java/com/pharmacopoeia/controller/ChatController.java

@@ -192,6 +192,7 @@ public class ChatController {
 
     private List<Map<String, Object>> buildSources(List<Map<String, Object>> docs) {
         return docs.stream()
+                .limit(3)  // 最多 3 条
                 .map(d -> {
                     String content = (String) d.getOrDefault("content", "");
                     String storedSection = (String) d.getOrDefault("section", "");
@@ -217,19 +218,25 @@ public class ChatController {
         // 从 query 中提取关注的 section(如"用法用量"→"用法与用量")
         String targetSection = extractQuerySection(query);
 
-        // 过滤:优先匹配药品名 + section
+        // 过滤:用 chunk 头部【药名 - section】精确匹配
         List<Map<String, Object>> filtered = new ArrayList<>();
         List<Map<String, Object>> fallback = new ArrayList<>();
         for (Map<String, Object> d : docs) {
             String content = (String) d.getOrDefault("content", "");
-            String section = (String) d.getOrDefault("section", "");
-            boolean drugMatch = targetDrug.isEmpty() || content.contains(targetDrug);
+            String storedSection = (String) d.getOrDefault("section", "");
+            String section = realSection(content, storedSection);
+            // 精确匹配:【布洛芬 - xxx】不匹配【布洛芬口服溶液 - xxx】
+            String exactHeader = "【" + targetDrug + " - ";
+            boolean drugExactMatch = targetDrug.isEmpty() || content.contains(exactHeader);
+            boolean drugPartialMatch = !drugExactMatch && content.contains(targetDrug);
             boolean sectionMatch = targetSection.isEmpty() || section.contains(targetSection);
 
-            if (drugMatch && sectionMatch) {
-                filtered.add(d);  // 精确匹配
-            } else if (drugMatch) {
-                fallback.add(d);  // 只匹配药品名
+            if (drugExactMatch && sectionMatch) {
+                filtered.add(d);
+            } else if (drugExactMatch) {
+                fallback.add(d);
+            } else if (drugPartialMatch && sectionMatch && filtered.isEmpty()) {
+                fallback.add(d);  // 兜底:无精确匹配时才用模糊匹配
             }
         }
 
@@ -264,11 +271,23 @@ public class ChatController {
 
     /** 从 query 提取药品名:"布洛芬的用法用量" → "布洛芬" */
     private String extractQueryDrugName(String query) {
-        // 去掉常见后缀
-        String cleaned = query.replaceAll("[的之](用法|用量|副作用|禁忌|注意事项|说明书|是什么|多少钱).*$", "");
-        // 去掉纯问句前缀
-        cleaned = cleaned.replaceAll("^(什么是|什么是|怎么|如何|告诉我|请问|查询|搜索|查一下)", "");
-        return cleaned.trim();
+        if (query == null) return "";
+        String cleaned = query.trim();
+        // 去掉常见后缀词(包括拼接形式如"阿莫西林禁忌")
+        String[] suffixes = {
+            "的用法与用量", "的用法用量", "用法与用量", "用法用量", "的用法", "的用量",
+            "的副作用", "不良反应", "的禁忌", "禁忌", "的注意事项", "注意事项",
+            "是什么", "说明书", "怎么用", "怎么吃", "多少钱", "的用量", "用量", "的剂量", "剂量"
+        };
+        for (String s : suffixes) {
+            if (cleaned.endsWith(s)) {
+                cleaned = cleaned.substring(0, cleaned.length() - s.length()).trim();
+                break;
+            }
+        }
+        // 去掉问句前缀
+        cleaned = cleaned.replaceAll("^(什么是|怎么|如何|告诉我|请问|查询|搜索|查一下)", "").trim();
+        return cleaned;
     }
 
     /** 从 query 提取关注的 section */

+ 40 - 9
backend-java/src/main/java/com/pharmacopoeia/service/RetrieverService.java

@@ -56,17 +56,32 @@ public class RetrieverService {
         String sql;
         Object[] params;
         if (!drugName.isEmpty()) {
-            // 药品名+向量混合检索:精确匹配 或 前缀匹配(如"布洛芬"匹配"布洛芬片")
+            // 先精确匹配,没结果再前缀匹配(如"布洛芬"→先查"布洛芬",没有再查"布洛芬片/胶囊等")
             sql = """
                 SELECT c.content, c.source, c.drug_id, c.section,
                        1 - (c.vec <=> ?::vector) AS similarity
                 FROM drug_chunks c
                 JOIN drugs d ON d.drug_id = c.drug_id
-                WHERE c.vec IS NOT NULL AND (d.name = ? OR d.name LIKE ?)
+                WHERE c.vec IS NOT NULL AND d.name = ?
                 ORDER BY c.vec <=> ?::vector
                 LIMIT ?
                 """;
-            params = new Object[]{vecStr, drugName, drugName + "%", vecStr, topK};
+            params = new Object[]{vecStr, drugName, vecStr, topK};
+            List<Map<String, Object>> results = jdbc.queryForList(sql, params);
+            if (results.isEmpty()) {
+                // 精确无结果,用前缀匹配
+                sql = """
+                    SELECT c.content, c.source, c.drug_id, c.section,
+                           1 - (c.vec <=> ?::vector) AS similarity
+                    FROM drug_chunks c
+                    JOIN drugs d ON d.drug_id = c.drug_id
+                    WHERE c.vec IS NOT NULL AND d.name LIKE ?
+                    ORDER BY c.vec <=> ?::vector
+                    LIMIT ?
+                    """;
+                params = new Object[]{vecStr, drugName + "%", vecStr, topK};
+            }
+            return jdbc.queryForList(sql, params);
         } else {
             sql = """
                 SELECT content, source, drug_id, section,
@@ -84,18 +99,34 @@ public class RetrieverService {
 
     /** 从 query 中提取已知药品名:查 drugs 表精确匹配 */
     private String extractDrugName(String query) {
-        // 去掉常见后缀词,尝试匹配药品名
-        String cleaned = query.replaceAll("[的之是](用法|用量|副作用|禁忌|注意|说明书|是什么|怎么|如何).*$", "");
-        cleaned = cleaned.trim();
-        if (cleaned.length() < 2) return "";
-
-        // 查 drugs 表精确匹配
+        String cleaned = query.trim();
+        // 逐步去掉尾部修饰词,尝试匹配药品名
+        String[] suffixes = {
+            "的用法与用量", "的用法用量", "用法与用量", "用法用量", "的用量", "的用法",
+            "的副作用", "不良反应", "的禁忌", "禁忌", "的注意事项", "注意事项",
+            "是什么", "说明书", "怎么用", "怎么吃", "的用量", "用量", "的剂量", "剂量"
+        };
+        // 先尝试原始 query
         try {
             List<String> matches = jdbc.queryForList(
                 "SELECT name FROM drugs WHERE name = ? AND is_active = TRUE LIMIT 1",
                 String.class, cleaned);
             if (!matches.isEmpty()) return matches.get(0);
         } catch (Exception ignored) {}
+
+        // 逐步去掉后缀再试
+        for (String suffix : suffixes) {
+            if (cleaned.endsWith(suffix)) {
+                String candidate = cleaned.substring(0, cleaned.length() - suffix.length()).trim();
+                if (candidate.length() < 2) continue;
+                try {
+                    List<String> matches = jdbc.queryForList(
+                        "SELECT name FROM drugs WHERE name = ? AND is_active = TRUE LIMIT 1",
+                        String.class, candidate);
+                    if (!matches.isEmpty()) return matches.get(0);
+                } catch (Exception ignored) {}
+            }
+        }
         return "";
     }