cc 3 달 전
부모
커밋
15a6a23dd9

+ 7 - 1
src/main/java/com/qingjian/module/ai/controller/AiController.java

@@ -7,6 +7,8 @@ import org.noear.solon.annotation.*;
 import org.noear.solon.core.util.MimeType;
 import reactor.core.publisher.Flux;
 
+import java.util.List;
+
 @Controller
 @Mapping("/ai")
 public class AiController {
@@ -52,5 +54,9 @@ public class AiController {
         chatRequest.setSessionId(sessionId);
         return aiService.chat(chatRequest);
     }
-
+    @Get
+    @Mapping("/qs")
+    public List<String> qs() {
+        return aiService.qs();
+    }
 }

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

@@ -1,5 +1,6 @@
 package com.qingjian.module.ai.service;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Singleton;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -12,6 +13,7 @@ import com.qingjian.common.model.ai.query.ChatRequest;
 import com.qingjian.common.utils.StateManager;
 import com.qingjian.module.ai.agent.AgentRuntime;
 import com.qingjian.module.ai.agent.skill.Tools;
+import com.qingjian.module.person.service.PersonBasicInfoService;
 import lombok.extern.slf4j.Slf4j;
 import org.noear.snack4.ONode;
 import org.noear.solon.Solon;
@@ -55,6 +57,22 @@ public class AiService {
     private KnowledgeInfoService knowledgeInfoService;
     @Inject
     private VectorStoreService vectorStoreService;
+    //1、***。
+    //2、帮我分析***的定期存款有哪些,统计并列出前几笔交易明细。
+    //3、现有数据对象之间都有哪些交易、通信、碰面的关系?
+    //4、***对象的常交易对象和常联系对象有哪些?
+    //5、调取的数据还缺失哪些?
+    //6、***对象常出现的地点统计。
+    private final List<String> qs = List.of("请分析对象{} 大额的交易记录有哪些,并统计大额交易的对端卡号?",
+            "请分析对象{} 定期存款有哪些,统计并列出前几笔交易明细。",
+            "请分析对象{} 的常交易对象和常联系对象有哪些?",
+            "请分析对象{} 常出现的地点统计。",
+            "现有数据对象之间都有哪些交易、通信、碰面的关系?",
+            "调取的数据还缺失哪些?"
+    );
+    @Inject
+    private PersonBasicInfoService personBasicInfoService;
+
 
     private final Map<String, AgentSession> sessionMap = new ConcurrentHashMap<>();
     private final AgentSessionProvider sessionProvider = (sessionId) -> sessionMap.computeIfAbsent(sessionId, key ->
@@ -110,7 +128,6 @@ public class AiService {
         return agentProperties;
     }
 
-
     /**
      * 处理AI聊天请求
      *
@@ -121,7 +138,7 @@ public class AiService {
         if (!Singleton.exists(AgentRuntime.class)) {
             log.error("Ai 服务初始化失败!");
             String message = new ONode().set("type", "error")
-                    .set("text", "Ai 服务初始化失败!")
+                    .set("text", "Ai 服务初始化失败,请检查Ai 配置!")
                     .toJson();
             return Flux.just(message);
         }
@@ -242,4 +259,14 @@ public class AiService {
         queryVectorBo.setMaxResults(knowledgeInfo.getRetrieveLimit());
         return queryVectorBo;
     }
+
+    public List<String> qs() {
+        List<String> personNames = personBasicInfoService.getPersonNames();
+        if (CollUtil.isEmpty(personNames)) {
+            return List.of("现有数据对象之间都有哪些交易、通信、碰面的关系?",
+                    "调取的数据还缺失哪些?");
+        }
+        String personName = personNames.stream().findAny().get();
+        return qs.stream().map(q -> StrUtil.format(q, personName)).toList();
+    }
 }

+ 77 - 4
src/main/resources/templates/chat.html

@@ -522,7 +522,38 @@
         .welcome-sub {
             color: var(--text-secondary);
             font-size: 15px;
-            margin-bottom: 36px;
+            margin-bottom: 24px;
+        }
+
+        /* Quick Questions */
+        .quick-questions {
+            width: 100%;
+            max-width: 680px;
+            margin-bottom: 16px;
+            display: flex;
+            flex-wrap: wrap;
+            gap: 8px;
+        }
+
+        .quick-q-chip {
+            padding: 6px 14px;
+            border: 1px solid var(--border-color);
+            border-radius: 20px;
+            background: var(--bg-input-box);
+            color: var(--text-secondary);
+            font-size: 13px;
+            cursor: pointer;
+            transition: all 0.2s;
+            white-space: nowrap;
+            max-width: 320px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+        }
+
+        .quick-q-chip:hover {
+            border-color: var(--accent);
+            color: var(--accent);
+            background: var(--accent-light);
         }
 
         .welcome-input-box {
@@ -1364,6 +1395,7 @@
         </div>
         <div class="welcome-title" th:text="'你好,我是 ' + ${nickname} + '!'">你好,我是 清鉴 !</div>
         <div class="welcome-sub">有什么我可以帮你的吗?</div>
+        <div class="quick-questions" id="welcomeQuickQs"></div>
         <div class="welcome-input-box">
             <div class="img-preview-wrap" id="welcomeImgWrap">
                 <div class="img-preview"><img id="welcomeImgPreview" src="" alt=""/><span class="img-preview-remove"
@@ -1385,6 +1417,7 @@
             <!-- Per-session containers are added here dynamically -->
         </div>
         <div class="input-wrap">
+            <div class="quick-questions" id="chatQuickQs" style="max-width:820px;margin:0 auto 10px;"></div>
             <div class="input-box">
                 <div class="img-preview-wrap" id="chatImgWrap">
                     <div class="img-preview"><img id="chatImgPreview" src="" alt=""/><span class="img-preview-remove"
@@ -1468,7 +1501,7 @@
     var welcomeSendBtn = document.getElementById('welcomeSendBtn');
     var chatInput = document.getElementById('chatInput');
     var chatSendBtn = document.getElementById('chatSendBtn');
-    var themeBtn = document.getElementById('themeBtn');
+    // var themeBtn = document.getElementById('themeBtn');
     var themeIcon = document.getElementById('themeIcon');
     var newChatBtn = document.getElementById('newChatBtn');
     var historyList = document.getElementById('historyList');
@@ -1783,6 +1816,45 @@
     welcomeImgRemove.addEventListener('click', clearImagePreview);
     chatImgRemove.addEventListener('click', clearImagePreview);
 
+    /* ===== Quick Questions ===== */
+    var welcomeQuickQs = document.getElementById('welcomeQuickQs');
+    var chatQuickQs = document.getElementById('chatQuickQs');
+
+    function loadQuickQuestions() {
+        httpGetJson('/js/a/ai/qs', function (data) {
+            var questions = unwrapResponse(data);
+            if (!Array.isArray(questions) || questions.length === 0) return;
+            renderQuickQChips(welcomeQuickQs, questions);
+            renderQuickQChips(chatQuickQs, questions);
+        });
+    }
+
+    function renderQuickQChips(container, questions) {
+        container.innerHTML = '';
+        for (var i = 0; i < questions.length; i++) {
+            var chip = document.createElement('span');
+            chip.className = 'quick-q-chip';
+            chip.textContent = questions[i];
+            chip.title = questions[i];
+            chip.addEventListener('click', (function(q) {
+                return function() {
+                    if (inChatMode) {
+                        chatInput.value = q;
+                        chatInput.focus();
+                        autoResize(chatInput);
+                    } else {
+                        welcomeInput.value = q;
+                        welcomeInput.focus();
+                        autoResize(welcomeInput);
+                    }
+                };
+            })(questions[i]));
+            container.appendChild(chip);
+        }
+    }
+
+    loadQuickQuestions();
+
     /* ===== Marked ===== */
     if (typeof marked !== 'undefined') { marked.setOptions({ breaks: true, gfm: true }); }
     function renderMd(text) {
@@ -1793,7 +1865,7 @@
     /* ===== Theme ===== */
     var currentTheme = localStorage.getItem('chat-theme') || 'light';
     document.body.setAttribute('data-theme', currentTheme);
-    updateThemeIcon();
+/*    updateThemeIcon();
     themeBtn.addEventListener('click', function() {
         currentTheme = currentTheme === 'light' ? 'dark' : 'light';
         document.body.setAttribute('data-theme', currentTheme);
@@ -1803,7 +1875,7 @@
     function updateThemeIcon() {
         themeIcon.innerHTML = currentTheme === 'light' ? '&#xe6c2;' : '&#xe748;';
         themeBtn.title = currentTheme === 'light' ? '切换至暗色' : '切换至浅色';
-    }
+    }*/
 
     /* ===== View Switch ===== */
     function switchToChatMode() {
@@ -1843,6 +1915,7 @@
     chatInput.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } });
     welcomeSendBtn.addEventListener('click', function() { sendMessage(); });
     chatSendBtn.addEventListener('click', function() {
+        debugger
         if (isStreaming && activeSessionId && sessionMap[activeSessionId]) {
             finishStream(sessionMap[activeSessionId]);
         } else {