From fd8d036cb13b3c6342fefdb34c7db0cf947341d7 Mon Sep 17 00:00:00 2001 From: thinkgem Date: Sun, 19 Oct 2025 13:21:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E5=88=86=20jeesite-ai-tools=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E4=BF=9D=E6=8C=81=E4=BC=9A=E8=AF=9D=E6=8E=A7=E5=88=B6=E6=9D=83?= =?UTF-8?q?=E9=99=90=EF=BC=8C=E5=A6=82=E5=BD=93=E5=89=8D=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=8F=AA=E8=83=BD=E6=9F=A5=E8=AF=A2=E6=9C=89=E6=9D=83=E9=99=90?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/cms/service/AiCmsChatService.java} | 14 ++-- modules/ai/ai-tools/bin/deploy.bat | 22 ++++++ modules/ai/ai-tools/bin/deploy.sh | 18 +++++ modules/ai/ai-tools/bin/package.bat | 22 ++++++ modules/ai/ai-tools/bin/package.sh | 18 +++++ modules/ai/ai-tools/pom.xml | 69 ++++++++++++++++ .../jeesite/modules/ai/tools/TestAiTools.java | 54 +++++++++++++ .../jeesite/modules/ai/tools/UserAITools.java | 78 +++++++++++++++++++ .../ai/tools/aspect/ToolContextAspect.java | 43 ++++++++++ .../modules/cms/ai/tools/CmsAiTools.java | 46 ----------- 10 files changed, 332 insertions(+), 52 deletions(-) rename modules/{cms-ai/src/main/java/com/jeesite/modules/cms/ai/service/CmsAiChatService.java => ai/ai-cms/src/main/java/com/jeesite/modules/ai/cms/service/AiCmsChatService.java} (96%) create mode 100644 modules/ai/ai-tools/bin/deploy.bat create mode 100644 modules/ai/ai-tools/bin/deploy.sh create mode 100644 modules/ai/ai-tools/bin/package.bat create mode 100644 modules/ai/ai-tools/bin/package.sh create mode 100644 modules/ai/ai-tools/pom.xml create mode 100644 modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/TestAiTools.java create mode 100644 modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/UserAITools.java create mode 100644 modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/aspect/ToolContextAspect.java delete mode 100644 modules/cms-ai/src/main/java/com/jeesite/modules/cms/ai/tools/CmsAiTools.java diff --git a/modules/cms-ai/src/main/java/com/jeesite/modules/cms/ai/service/CmsAiChatService.java b/modules/ai/ai-cms/src/main/java/com/jeesite/modules/ai/cms/service/AiCmsChatService.java similarity index 96% rename from modules/cms-ai/src/main/java/com/jeesite/modules/cms/ai/service/CmsAiChatService.java rename to modules/ai/ai-cms/src/main/java/com/jeesite/modules/ai/cms/service/AiCmsChatService.java index 6c836497..45dec1aa 100644 --- a/modules/cms-ai/src/main/java/com/jeesite/modules/cms/ai/service/CmsAiChatService.java +++ b/modules/ai/ai-cms/src/main/java/com/jeesite/modules/ai/cms/service/AiCmsChatService.java @@ -2,7 +2,7 @@ * Copyright (c) 2013-Now http://jeesite.com All rights reserved. * No deletion without permission, or be held responsible to law. */ -package com.jeesite.modules.cms.ai.service; +package com.jeesite.modules.ai.cms.service; import com.jeesite.common.cache.CacheUtils; import com.jeesite.common.collect.ListUtils; @@ -13,11 +13,12 @@ import com.jeesite.common.lang.DateUtils; import com.jeesite.common.lang.StringUtils; import com.jeesite.common.mapper.JsonMapper; import com.jeesite.common.service.BaseService; -import com.jeesite.modules.cms.ai.properties.CmsAiProperties; +import com.jeesite.modules.ai.cms.properties.AiCmsProperties; import com.jeesite.modules.sys.entity.Area; import com.jeesite.modules.sys.utils.AreaUtils; import com.jeesite.modules.sys.utils.UserUtils; import jakarta.servlet.http.HttpServletRequest; +import org.apache.shiro.util.ThreadContext; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor; @@ -51,7 +52,7 @@ import java.util.Map; * @author ThinkGem */ @Service -public class CmsAiChatService extends BaseService { +public class AiCmsChatService extends BaseService { private static final String CMS_CHAT_CACHE = "cmsChatCache"; private static final String[] USER_MESSAGE_SEARCH = new String[]{"{", "}"}; @@ -60,12 +61,12 @@ public class CmsAiChatService extends BaseService { private final ChatClient chatClient; private final ChatMemory chatMemory; private final VectorStore vectorStore; - private final CmsAiProperties properties; + private final AiCmsProperties properties; - public CmsAiChatService(ChatClient chatClient, + public AiCmsChatService(ChatClient chatClient, ChatMemory chatMemory, ObjectProvider vectorStore, - CmsAiProperties properties) { + AiCmsProperties properties) { this.chatClient = chatClient; this.chatMemory = chatMemory; this.vectorStore = vectorStore.getIfAvailable(); @@ -150,6 +151,7 @@ public class CmsAiChatService extends BaseService { .promptTemplate(new PromptTemplate(properties.getDefaultPromptTemplate())) .build()); } + spec.toolContext(Map.of("subject", ThreadContext.getSubject())); return spec.stream() .chatResponse() .doOnNext(response -> { diff --git a/modules/ai/ai-tools/bin/deploy.bat b/modules/ai/ai-tools/bin/deploy.bat new file mode 100644 index 00000000..f4258bf5 --- /dev/null +++ b/modules/ai/ai-tools/bin/deploy.bat @@ -0,0 +1,22 @@ +@echo off +rem /** +rem * Copyright (c) 2013-Now http://jeesite.com All rights reserved. +rem * No deletion without permission, or be held responsible to law. +rem * +rem * Author: ThinkGem@163.com +rem */ +echo. +echo [信息] 部署工程到Maven服务器。 +echo. + +%~d0 +cd %~dp0 + +call mvn -v +echo. + +cd .. +call mvn clean deploy -Dmaven.test.skip=true -Pdeploy + +cd bin +pause \ No newline at end of file diff --git a/modules/ai/ai-tools/bin/deploy.sh b/modules/ai/ai-tools/bin/deploy.sh new file mode 100644 index 00000000..f8696c71 --- /dev/null +++ b/modules/ai/ai-tools/bin/deploy.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# /** +# * Copyright (c) 2013-Now http://jeesite.com All rights reserved. +# * No deletion without permission, or be held responsible to law. +# * +# * Author: ThinkGem@163.com +# */ +echo "" +echo "[淇℃伅] 閮ㄧ讲宸ョ▼鍒癕aven鏈嶅姟鍣ㄣ" +echo "" + +mvn -v +echo "" + +cd .. +mvn clean deploy -Dmaven.test.skip=true -Pdeploy + +cd bin \ No newline at end of file diff --git a/modules/ai/ai-tools/bin/package.bat b/modules/ai/ai-tools/bin/package.bat new file mode 100644 index 00000000..a6b29c2c --- /dev/null +++ b/modules/ai/ai-tools/bin/package.bat @@ -0,0 +1,22 @@ +@echo off +rem /** +rem * Copyright (c) 2013-Now http://jeesite.com All rights reserved. +rem * No deletion without permission, or be held responsible to law. +rem * +rem * Author: ThinkGem@163.com +rem */ +echo. +echo [信息] 打包安装工程,生成jar包文件。 +echo. + +%~d0 +cd %~dp0 + +call mvn -v +echo. + +cd .. +call mvn clean install -Dmaven.test.skip=true -Ppackage + +cd bin +pause \ No newline at end of file diff --git a/modules/ai/ai-tools/bin/package.sh b/modules/ai/ai-tools/bin/package.sh new file mode 100644 index 00000000..8deff508 --- /dev/null +++ b/modules/ai/ai-tools/bin/package.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# /** +# * Copyright (c) 2013-Now http://jeesite.com All rights reserved. +# * No deletion without permission, or be held responsible to law. +# * +# * Author: ThinkGem@163.com +# */ +echo "" +echo "[淇℃伅] 鎵撳寘瀹夎宸ョ▼锛岀敓鎴恓ar鍖呮枃浠躲" +echo "" + +mvn -v +echo "" + +cd .. +mvn clean install -Dmaven.test.skip=true -Ppackage + +cd bin \ No newline at end of file diff --git a/modules/ai/ai-tools/pom.xml b/modules/ai/ai-tools/pom.xml new file mode 100644 index 00000000..6f668f9b --- /dev/null +++ b/modules/ai/ai-tools/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + + com.jeesite + jeesite-parent-ai + 5.14.0.springboot3-SNAPSHOT + ../../../parent/ai/pom.xml + + + jeesite-module-ai-tools + jar + + JeeSite Module AI Tools + http://jeesite.com + 2013-Now + + + + + + + + + + com.jeesite + jeesite-module-core + ${project.parent.version} + + + + + com.jeesite + jeesite-module-cms + ${project.parent.version} + + + + + org.springframework.ai + spring-ai-model + + + + + org.springframework.ai + spring-ai-mcp + + + + + + + thinkgem + WangZhen + thinkgem at 163.com + Project lead + +8 + + + + + JeeSite + http://jeesite.com + + + \ No newline at end of file diff --git a/modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/TestAiTools.java b/modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/TestAiTools.java new file mode 100644 index 00000000..f7e13c18 --- /dev/null +++ b/modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/TestAiTools.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2013-Now http://jeesite.com All rights reserved. + * No deletion without permission, or be held responsible to law. + */ +package com.jeesite.modules.ai.tools; + +import com.jeesite.common.lang.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ai.tool.annotation.Tool; +import org.springframework.ai.tool.annotation.ToolParam; +import org.springframework.stereotype.Component; + +/** + * AI MCP 宸ュ叿璋冪敤 + * @author ThinkGem + */ +@Component +public class TestAiTools { + + private final Logger logger = LoggerFactory.getLogger(TestAiTools.class); + + /** + * 鑾峰彇鏈嶅姟鍣ㄦ椂闂 + */ + @Tool(name="鑾峰彇鏈嶅姟鍣ㄦ椂闂", description = "鑾峰彇褰撳墠鐨勬棩鏈熷拰鏃堕棿锛屾牸寮忎负 yyyy-MM-dd HH:mm:ss銆") + public String getCurrentDateTime() { + String dateTime = "褰撳墠鏃ユ湡鏃堕棿锛" + DateUtils.getDateTime(); + logger.info("褰撳墠鏃ユ湡鏃堕棿 ============== {}", dateTime); + return dateTime; + } + + /** + * 寮鍏虫埧闂寸殑鐏 + */ + @Tool( + name = "鎴块棿鐏厜寮鍏", + description = "鎺у埗鎸囧畾鎴块棿鐨勭伅鍏夊紑鍏炽傞渶瑕佹彁渚涙埧闂村悕绉帮紙濡 '瀹㈠巺'銆'鍗у'锛夊拰鐩爣鐘舵侊紙true 琛ㄧず寮鐏紝false 琛ㄧず鍏崇伅锛夈" + ) + public String roomLightSwitch( + @ToolParam(description = "瑕佹帶鍒剁殑鎴块棿鍚嶇О锛屼緥濡傦細'瀹㈠巺'銆'鍗у'銆'椁愬巺'銆'鍘ㄦ埧'") String roomName, + @ToolParam(description = "鐏厜鐩爣鐘舵侊細true 琛ㄧず鎵撳紑鐏紝false 琛ㄧず鍏抽棴鐏") boolean on) { + String message = roomName + " 鎴块棿閲岀殑鐏 " + (on ? "鎵撳紑" : "鍏抽棴"); + logger.info("鎴块棿鐏厜寮鍏 ============== {}", message); + return String.format(""" + { + "message": "%s", + "roomName": "%s", + "on": %s + } + """, message, roomName, on); + } + +} \ No newline at end of file diff --git a/modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/UserAITools.java b/modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/UserAITools.java new file mode 100644 index 00000000..0e473fbf --- /dev/null +++ b/modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/UserAITools.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2013-Now http://jeesite.com All rights reserved. + * No deletion without permission, or be held responsible to law. + */ +package com.jeesite.modules.ai.tools; + +import com.jeesite.common.lang.StringUtils; +import com.jeesite.common.mapper.JsonMapper; +import com.jeesite.common.mybatis.mapper.query.QueryType; +import com.jeesite.modules.sys.entity.EmpUser; +import com.jeesite.modules.sys.entity.User; +import com.jeesite.modules.sys.service.EmpUserService; +import com.jeesite.modules.sys.utils.UserUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ai.chat.model.ToolContext; +import org.springframework.ai.tool.annotation.Tool; +import org.springframework.ai.tool.annotation.ToolParam; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * AI MCP 宸ュ叿璋冪敤 + * @author ThinkGem + */ +@Component +public class UserAITools { + + private final Logger logger = LoggerFactory.getLogger(UserAITools.class); + + private final EmpUserService empUserService; + + public UserAITools(EmpUserService empUserService) { + this.empUserService = empUserService; + } + + /** + * 鑾峰彇褰撳墠浼氳瘽鐨勭敤鎴蜂俊鎭 + */ + @Tool(name="褰撳墠鐢ㄦ埛淇℃伅", description = "鏃犳潯浠惰幏鍙栧綋鍓嶇敤鎴蜂俊鎭") + public String getCurrentUser(ToolContext toolContext) { + User currentUser = UserUtils.getUser(); + if (StringUtils.isBlank(currentUser.getUserCode())) { + logger.info("褰撳墠鐢ㄦ埛淇℃伅 ============== 褰撳墠鐢ㄦ埛鏈櫥褰曘"); + return "褰撳墠鐢ㄦ埛鏈櫥褰曘"; + } + String result = JsonMapper.toJson(currentUser); + logger.info("褰撳墠鐢ㄦ埛淇℃伅 ============== 鏌ヨ缁撴灉锛歿}", result); + return result; + } + + + /** + * 鏌ヨ鐢ㄦ埛淇℃伅 + */ + @Tool(name="鏌ヨ鐢ㄦ埛淇℃伅", description = "鏍规嵁鐢ㄦ埛鍚嶏紙鐧诲綍璐﹀彿锛夋垨鍛樺伐濮撳悕妯$硦鏌ヨ鐢ㄦ埛淇℃伅銆" + + "缁撴灉浠ヨ〃鏍煎舰寮忓睍绀猴紝鍖呭惈鐢ㄦ埛鍚島serName銆佸鍚峞mpUser銆侀儴闂╫fficeName绛夊熀鏈俊鎭") + public String findEmpUserInfo(ToolContext toolContext, + @ToolParam(description = "鐢ㄦ埛鐨勭櫥褰曞悕鎴栧憳宸ョ殑鐪熷疄濮撳悕锛屾敮鎸佹ā绯婂尮閰") String userName + ) { + EmpUser where = new EmpUser(); + where.sqlMap().getWhere().and(w -> w + .or("a.user_name", QueryType.LIKE, userName) + .or("e.emp_name", QueryType.LIKE, userName)); + // 鏉冮檺鎺у埗锛屽彧鑳芥煡璇㈠綋鍓嶇敤鎴疯兘鏌ヨ鐨勭敤鎴蜂俊鎭 + logger.info("鑾峰彇鐢ㄦ埛淇℃伅 ============== 褰撳墠鐢ㄦ埛: {}", where.currentUser().getUserCode()); + empUserService.addDataScopeFilter(where); + List list = empUserService.findList(where); + String result = JsonMapper.toJson(list); + logger.info("鑾峰彇鐢ㄦ埛淇℃伅 ============== 鏌ヨ缁撴灉锛 {}", result); + if (list.isEmpty()) { + return "鏈壘鍒扮鍚堟潯浠剁殑鐢ㄦ埛淇℃伅銆"; + } + return result; + } + +} \ No newline at end of file diff --git a/modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/aspect/ToolContextAspect.java b/modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/aspect/ToolContextAspect.java new file mode 100644 index 00000000..b2edee4f --- /dev/null +++ b/modules/ai/ai-tools/src/main/java/com/jeesite/modules/ai/tools/aspect/ToolContextAspect.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2013-Now http://jeesite.com All rights reserved. + * No deletion without permission, or be held responsible to law. + */ +package com.jeesite.modules.ai.tools.aspect; + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.ai.chat.model.ToolContext; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * Tool 涓婁笅鏂囩敤鎴蜂俊鎭敞鍏ュ垏闈 + * @author ThinkGem + */ +@Aspect +@Component +public class ToolContextAspect { + + @Around("@annotation(org.springframework.ai.tool.annotation.Tool)") + public Object handleThreadContext(ProceedingJoinPoint joinPoint) throws Throwable { + Object[] args = joinPoint.getArgs(); + ToolContext toolContext = null; + for (Object arg : args) { + if (arg instanceof ToolContext) { + toolContext = (ToolContext) arg; + break; + } + } + if (toolContext != null) { + Map context = toolContext.getContext(); + if (context.containsKey("subject")) { + ThreadContext.bind((Subject) context.get("subject")); + } + } + return joinPoint.proceed(); + } +} \ No newline at end of file diff --git a/modules/cms-ai/src/main/java/com/jeesite/modules/cms/ai/tools/CmsAiTools.java b/modules/cms-ai/src/main/java/com/jeesite/modules/cms/ai/tools/CmsAiTools.java deleted file mode 100644 index 8f53fcfd..00000000 --- a/modules/cms-ai/src/main/java/com/jeesite/modules/cms/ai/tools/CmsAiTools.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2013-Now http://jeesite.com All rights reserved. - * No deletion without permission, or be held responsible to law. - */ -package com.jeesite.modules.cms.ai.tools; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ai.tool.annotation.Tool; -import org.springframework.ai.tool.annotation.ToolParam; - -import java.time.LocalDateTime; - -/** - * AI 宸ュ叿璋冪敤銆乀ool calling锛堥渶閫夋嫨鏀寔 Tools 鐨勬ā鍨嬶級 - * @author ThinkGem - */ -public class CmsAiTools { - - private final Logger logger = LoggerFactory.getLogger(CmsAiTools.class); - - /** - * 鏈仈缃戞悳绱㈢殑鏃跺欙紝鍙幏鍙栧埌鏈嶅姟鍣ㄦ椂闂 - */ - @Tool(description = "褰撳墠鏃堕棿锛屽綋鍓嶆棩鏈燂紝鍑犵偣浜") - public String getCurrentDateTime() { - String dateTime = "褰撳墠鏃ユ湡鏃堕棿锛" + LocalDateTime.now(); - logger.info(dateTime + " ============== "); - return dateTime; - } - - /** - * 浣犲彲浠ヨ闂細鎵撳紑瀹㈠巺鐨勭伅锛屽叧闂崸瀹ょ殑鐏紙闇鍒涘缓鏂板璇濓級 - */ - @Tool(description = "鎴块棿閲岀殑鐏墦寮鎴栧叧闂") - public String turnLight(@ToolParam(description = "鎴块棿") String roomName, @ToolParam(description = "寮鍏") boolean on) { - String message = roomName + " 鎴块棿閲岀殑鐏 " + (on ? "鎵撳紑" : "鍏抽棴"); - logger.info(message + " ============== "); - return String.format(""" - message: %s - roomName: %s - on: %s - """, - roomName, on, message); - } -} \ No newline at end of file