cc 3 mesi fa
parent
commit
27cd70e921

+ 20 - 0
src/main/java/com/qingjian/common/model/ai/dto/PblDTO.java

@@ -0,0 +1,20 @@
+package com.qingjian.common.model.ai.dto;
+
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+public class PblDTO {
+    private String personName;
+
+    private String personNo;
+
+    public static Map<String, String> mapping() {
+        Map<String, String> map = new HashMap<>();
+        map.put("personName", "嫌疑人姓名");
+        map.put("personNo", "手机号/银行卡号");
+        return map;
+    }
+}

+ 17 - 0
src/main/java/com/qingjian/common/model/ai/dto/SelectPersonNamesDTO.java

@@ -0,0 +1,17 @@
+package com.qingjian.common.model.ai.dto;
+
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+public class SelectPersonNamesDTO {
+    private String personName;
+
+    public static Map<String, String> mapping() {
+        Map<String, String> map = new HashMap<>();
+        map.put("personName", "嫌疑人姓名");
+        return map;
+    }
+}

+ 25 - 0
src/main/java/com/qingjian/module/ai/agent/skill/Tools.java

@@ -32,6 +32,8 @@ public class Tools {
         mapping.put("selectSameMacOrIp", "查询不同名下账户同MAC或同IP登录。");
         mapping.put("selectBankBindPhone", "查询银行卡绑定手机号不是本人的。");
         mapping.put("callInfoStat", "通信数据统计。");
+        mapping.put("selectPersonNames", "系统中所有的嫌疑对象列表。");
+        mapping.put("selectCardsAndPhone", "系统中所有的嫌疑对象的手机号、银行卡列表。");
     }
 
     private final SkillService skillService = Solon.context().getBean(SkillService.class);
@@ -168,4 +170,27 @@ public class Tools {
         }
         return str;
     }
+
+    @ToolMapping(name = "selectPersonNames", description = "系统中所有的嫌疑对象姓名列表。")
+    public String selectPersonNames() {
+        var result = skillService.selectPersonNames();
+        String str = Json.toStr(result);
+        try {
+            str = JsonToMarkdownUtil.convertToMarkdown(str, AiStatCallFrequencyTop10.mapping());
+        } catch (Exception e) {
+            log.error("转换失败", e);
+        }
+        return str;
+    }
+    @ToolMapping(name = "selectCardsAndPhone", description = "系统中所有的嫌疑对象的手机号、银行卡列表")
+    public String selectCardsAndPhone() {
+        var result = skillService.selectCardsAndPhone();
+        String str = Json.toStr(result);
+        try {
+            str = JsonToMarkdownUtil.convertToMarkdown(str, AiStatCallFrequencyTop10.mapping());
+        } catch (Exception e) {
+            log.error("转换失败", e);
+        }
+        return str;
+    }
 }

+ 14 - 17
src/main/java/com/qingjian/module/ai/agent/skill/text2sql/Text2SqlSkill.java

@@ -17,6 +17,7 @@ package com.qingjian.module.ai.agent.skill.text2sql;
 
 import com.google.common.collect.Lists;
 import com.qingjian.module.ai.agent.skill.text2sql.dialect.PostgreDialect;
+import lombok.extern.slf4j.Slf4j;
 import org.noear.snack4.ONode;
 import org.noear.solon.Solon;
 import org.noear.solon.Utils;
@@ -56,6 +57,7 @@ import java.util.stream.Collectors;
  * @author noear
  * @since 3.9.1
  */
+@Slf4j
 @Preview("3.9.1")
 public class Text2SqlSkill extends AbsSkill {
     protected final static Logger LOG = LoggerFactory.getLogger(Text2SqlSkill.class);
@@ -74,7 +76,6 @@ public class Text2SqlSkill extends AbsSkill {
     protected int maxContextLength = 8000;
     protected SchemaMode schemaMode = SchemaMode.FULL;
     protected boolean readOnly = true;
-    private SqlUtils sqlUtils;
 
     public record ColumnInfo(String name, String en, String type) {
     }
@@ -82,13 +83,6 @@ public class Text2SqlSkill extends AbsSkill {
     public Text2SqlSkill() {
         super();
         this.dialect = new PostgreDialect();
-        try {
-            DynamicDataSource dds = Solon.context().getBean("db1");
-            DataSource ds = dds.getDefaultTargetDataSource();
-            this.sqlUtils = SqlUtils.of(ds);
-        } catch (Exception e) {
-            LOG.error("Failed to initialize Text2SqlSkill", e);
-        }
     }
 
     private void init() {
@@ -120,8 +114,8 @@ public class Text2SqlSkill extends AbsSkill {
      * 初始化:识别数据库方言并预加载表元数据
      */
     private void initDialectAndMetadata() {
-        tableColumnsMap.put("call_record", getCallColumns());
-        tableColumnsMap.put("trans_record", getColumnInfos());
+        tableColumnsMap.put("call_record_all", getCallColumns());
+        tableColumnsMap.put("trans_record_all", getColumnInfos());
         tableColumnsMap.put("express_info", getExpressColumns());
         tableColumnsMap.put("together_live_info", getTogetherLiveInfoColumns());
         tableColumnsMap.put("together_flight_info", getTogetherFlightInfoColumns());
@@ -130,8 +124,8 @@ public class Text2SqlSkill extends AbsSkill {
         tableColumnsMap.put("flight_ticket_info", getFlightTicketInfoColumns());
         tableColumnsMap.put("hotel_stay_info", getHotelStayInfoColumns());
 
-        tableRemarksMap.put("call_record", "通信记录表");
-        tableRemarksMap.put("trans_record", "交易记录表");
+        tableRemarksMap.put("call_record_all", "通信记录表");
+        tableRemarksMap.put("trans_record_all", "交易记录表");
         tableRemarksMap.put("express_info", "快递收发记录表");
         tableRemarksMap.put("together_live_info", "同住记录表");
         tableRemarksMap.put("together_flight_info", "同航班记录表");
@@ -140,8 +134,8 @@ public class Text2SqlSkill extends AbsSkill {
         tableRemarksMap.put("flight_ticket_info", "飞机票购买记录表");
         tableRemarksMap.put("hotel_stay_info", "酒店住宿记录表");
 
-        tableNames.add("call_record");
-        tableNames.add("trans_record");
+        tableNames.add("call_record_all");
+        tableNames.add("trans_record_all");
         tableNames.add("express_info");
         tableNames.add("together_live_info");
         tableNames.add("together_flight_info");
@@ -185,7 +179,7 @@ public class Text2SqlSkill extends AbsSkill {
 
     @Override
     public String description() {
-        return "数据库专家:具备深厚的 DuckDB 方言知识,擅长多表分析。";
+        return "数据库专家:具备深厚的 DuckDB 方言知识,擅长多表分析。\n 找不到其他可用工具函数时,才使用该技能。";
     }
 
     @Override
@@ -226,7 +220,7 @@ public class Text2SqlSkill extends AbsSkill {
         return sb.toString();
     }
 
-    @ToolMapping(name = "execute_sql", description = "执行单条 SELECT 查询语句。")
+    @ToolMapping(name = "execute_sql", description = "无其他可用工具函数时,可使用execute_sql执行单条 SELECT 查询语句进行分析。")
     public String executeSql(@Param("sql") String sql) {
         if (Assert.isBlank(sql)) return "Error: SQL is empty.";
 
@@ -243,12 +237,15 @@ public class Text2SqlSkill extends AbsSkill {
             cleanSql = dialect.applyPagination(cleanSql, maxRows);
         }
         try {
-
+            DynamicDataSource dds = Solon.context().getBean("db1");
+            DataSource ds = dds.getDefaultTargetDataSource();
+            SqlUtils sqlUtils = SqlUtils.of(ds);
             List<Map> rows = sqlUtils.sql(cleanSql).queryRowList(Map.class);
             if (rows == null || rows.isEmpty()) return "Query OK. No data found.";
             String json = ONode.serialize(rows);
             return json.length() > maxContextLength ? json.substring(0, maxContextLength) + "... [Truncated]" : json;
         } catch (SQLException e) {
+            log.error("SQL Error: ", e);
             return "SQL Error: " + e.getMessage() + "\nHint: " + dialect.getErrorHint(e);
         }
     }

+ 6 - 0
src/main/java/com/qingjian/module/ai/mapper/SkillMapper.java

@@ -1,6 +1,7 @@
 package com.qingjian.module.ai.mapper;
 
 import com.qingjian.common.model.ai.dto.*;
+import com.qingjian.common.model.person.entity.PersonLibNo;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -81,4 +82,9 @@ public interface SkillMapper {
      * @return 银行卡绑定手机号信息列表
      */
     List<AiStatBankBindPhoneDTO> statBankBindPhone(String personName);
+
+    List<String> selectPersonNames();
+
+    List<PersonLibNo> selectCardsAndPhone(List<String> personNames);
+
 }

+ 3 - 2
src/main/java/com/qingjian/module/ai/service/AiService.java

@@ -109,7 +109,7 @@ public class AiService {
             chatConfig.setModel(model.getModelName());
             chatConfig.setProvider(model.getProviderCode());
         }
-        if (!StrUtil.isAllNotEmpty(chatConfig.getApiKey(), chatConfig.getApiUrl(), chatConfig.getModel())) {
+        if (!StrUtil.isAllNotEmpty(chatConfig.getApiUrl(), chatConfig.getModel())) {
             log.info("未配置模型ApiKey、ApiUrl、Model!");
             return null;
         }
@@ -269,7 +269,8 @@ public class AiService {
             return List.of("现有数据对象之间都有哪些交易、通信、碰面的关系?",
                     "调取的数据还缺失哪些?");
         }
-        String personName = personNames.stream().findAny().get();
+        Collections.shuffle(personNames);
+        String personName = personNames.getFirst();
         List<String> list = qs.stream().map(q -> StrUtil.format(q, personName)).collect(Collectors.toList());
         Collections.shuffle(list);
         return list.subList(0,3);

+ 19 - 0
src/main/java/com/qingjian/module/ai/service/SkillService.java

@@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.google.common.collect.Lists;
 import com.qingjian.common.model.ai.dto.*;
+import com.qingjian.common.model.person.entity.PersonLibNo;
 import com.qingjian.common.model.track.dto.ExpressInfoDTO;
 import com.qingjian.common.model.track.dto.TrackMeetSummeryDTO;
 import com.qingjian.common.model.track.dto.TravelTimelineDTO;
@@ -208,5 +209,23 @@ public class SkillService {
         return skillMapper.statBankBindPhone(personName);
     }
 
+    public List<SelectPersonNamesDTO> selectPersonNames() {
+        List<String> personNames = skillMapper.selectPersonNames();
+        return personNames.stream().map(personName -> {
+            SelectPersonNamesDTO selectPersonNamesDTO = new SelectPersonNamesDTO();
+            selectPersonNamesDTO.setPersonName(personName);
+            return selectPersonNamesDTO;
+        }).toList();
+    }
 
+    public List<PblDTO> selectCardsAndPhone() {
+        List<String> personNames = skillMapper.selectPersonNames();
+        List<PersonLibNo> plb = skillMapper.selectCardsAndPhone(personNames);
+        return plb.stream().map(personLibNo -> {
+            PblDTO pblDTO = new PblDTO();
+            pblDTO.setPersonName(personLibNo.getPersonName());
+            pblDTO.setPersonNo(personLibNo.getPersonNo());
+            return pblDTO;
+        }).toList();
+    }
 }

+ 13 - 0
src/main/resources/mappers/ai/SkillMapper.xml

@@ -148,4 +148,17 @@
              filtered_call_opening_info u ON b.personName = u.personName
         WHERE b.phone != u.personPhone
     </select>
+    <select id="selectPersonNames" resultType="java.lang.String">
+
+        select personName from person_basic_info;
+    </select>
+    <select id="selectCardsAndPhone" resultType="com.qingjian.common.model.person.entity.PersonLibNo">
+        select personName, personNo, libType from person_lib_no where personName
+        in
+        <foreach collection="personNames" item="p" open="(" separator="," close=")">
+            #{p}
+        </foreach>
+    </select>
+
+
 </mapper>

+ 1 - 1
src/main/resources/prompt/AGENTS.md

@@ -11,7 +11,7 @@
 ## 核心规则 (Action Logic)
 
 1. **先思后行**:你采用“先思后行”的决策机制。
-2. **严禁伪造**:禁止在回复中模拟或伪造工具的执行过程;当你没找到可用工具时,请直接使用sql_expert进行sql查询。
+2. **严禁伪造**:禁止在回复中模拟或伪造工具的执行过程;当你没找到可用工具或函数时,请直接使用sql_expert进行sql查询。
 3. **自然回复**:任务完成后,请以自然语言直接回复(不要有表情符),无需输出 `Final Answer:` 等标签。
 
 ## Core Capabilities