diff --git a/.run/ruoyi-ai.run.xml b/.run/ruoyi-ai.run.xml
new file mode 100644
index 000000000..c62041da4
--- /dev/null
+++ b/.run/ruoyi-ai.run.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.run/ruoyi-snailai-server.run.xml b/.run/ruoyi-snailai-server.run.xml
new file mode 100644
index 000000000..1bf2102e1
--- /dev/null
+++ b/.run/ruoyi-snailai-server.run.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 05f8ea5ef..1340fe27e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,6 +34,7 @@
4.4.0
2.2.7
2.0.0
+ 0.0.3
1.45.0
1.18.42
7.4
@@ -61,7 +62,7 @@
2.3.4
- 2.0.0-M6
+ 2.0.0-M8
3.14.0
@@ -278,6 +279,19 @@
${snailjob.version}
+
+
+ com.aizuda
+ snail-ai-agent-starter
+ ${snailai.version}
+
+
+
+ com.aizuda
+ snail-ai-openapi-starter
+ ${snailai.version}
+
+
org.bouncycastle
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index 9087d2514..bd9a57132 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -40,6 +40,7 @@
ruoyi-common-social
ruoyi-common-nacos
ruoyi-common-bus
+ ruoyi-common-ai
ruoyi-common-mqtt
ruoyi-common-mcp
diff --git a/ruoyi-common/ruoyi-common-ai/pom.xml b/ruoyi-common/ruoyi-common-ai/pom.xml
new file mode 100644
index 000000000..0d3b4852b
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-ai/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ org.dromara
+ ruoyi-common
+ ${revision}
+
+ 4.0.0
+
+ ruoyi-common-ai
+
+
+ ruoyi-common-ai AI公共模块
+
+
+
+
+
+
+ org.dromara
+ ruoyi-common-core
+
+
+
+
+ com.aizuda
+ snail-ai-agent-starter
+
+
+
+
+ com.aizuda
+ snail-ai-openapi-starter
+
+
+
+
diff --git a/ruoyi-common/ruoyi-common-ai/src/main/java/org/dromara/common/ai/config/SnailAiConfig.java b/ruoyi-common/ruoyi-common-ai/src/main/java/org/dromara/common/ai/config/SnailAiConfig.java
new file mode 100644
index 000000000..5ade5cbdd
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-ai/src/main/java/org/dromara/common/ai/config/SnailAiConfig.java
@@ -0,0 +1,16 @@
+package org.dromara.common.ai.config;
+
+import com.aizuda.snail.ai.agent.starter.EnableSnailAiAgent;
+import com.aizuda.snail.ai.openapi.client.starter.EnableSnailAiOpenApi;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+
+/**
+ * Snail AI 自动配置
+ */
+@AutoConfiguration
+@ConditionalOnProperty(prefix = "snail-ai", name = "enabled", havingValue = "true")
+@EnableSnailAiAgent
+@EnableSnailAiOpenApi
+public class SnailAiConfig {
+}
diff --git a/ruoyi-common/ruoyi-common-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..32af79519
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+org.dromara.common.ai.config.SnailAiConfig
diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml
index b4b1deb32..1f1fc1094 100644
--- a/ruoyi-common/ruoyi-common-bom/pom.xml
+++ b/ruoyi-common/ruoyi-common-bom/pom.xml
@@ -223,6 +223,13 @@
${revision}
+
+
+ org.dromara
+ ruoyi-common-ai
+ ${revision}
+
+
org.dromara
ruoyi-common-mqtt
diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml
index c90636271..6533c0357 100644
--- a/ruoyi-modules/pom.xml
+++ b/ruoyi-modules/pom.xml
@@ -14,6 +14,7 @@
ruoyi-job
ruoyi-resource
ruoyi-workflow
+ ruoyi-ai
ruoyi-modules
diff --git a/ruoyi-modules/ruoyi-ai/Dockerfile b/ruoyi-modules/ruoyi-ai/Dockerfile
new file mode 100644
index 000000000..e87df88a0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-ai/Dockerfile
@@ -0,0 +1,26 @@
+# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
+FROM bellsoft/liberica-openjdk-rocky:17.0.16-cds
+#FROM bellsoft/liberica-openjdk-rocky:21.0.8-cds
+#FROM findepi/graalvm:java17-native
+
+LABEL maintainer="Lion Li"
+
+RUN mkdir -p /ruoyi/ai/logs \
+ /ruoyi/skywalking/agent
+
+WORKDIR /ruoyi/ai
+
+ENV SERVER_PORT=9206 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
+
+EXPOSE ${SERVER_PORT}
+EXPOSE 18889
+
+ADD ./target/ruoyi-ai.jar ./app.jar
+
+SHELL ["/bin/bash", "-c"]
+
+ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
+ #-Dskywalking.agent.service_name=ruoyi-ai \
+ #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
+ -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
+ -jar app.jar
diff --git a/ruoyi-modules/ruoyi-ai/pom.xml b/ruoyi-modules/ruoyi-ai/pom.xml
new file mode 100644
index 000000000..ae8636c28
--- /dev/null
+++ b/ruoyi-modules/ruoyi-ai/pom.xml
@@ -0,0 +1,103 @@
+
+
+
+ org.dromara
+ ruoyi-modules
+ ${revision}
+
+ 4.0.0
+ jar
+ ruoyi-ai
+
+
+ ai模块
+
+
+
+
+
+
+ com.google.protobuf
+ protobuf-java
+ 4.35.0
+
+
+
+
+ io.grpc
+ grpc-protobuf
+ 1.81.0
+
+
+ io.grpc
+ grpc-stub
+ 1.81.0
+
+
+ io.grpc
+ grpc-netty-shaded
+ 1.81.0
+
+
+
+ org.dromara
+ ruoyi-common-nacos
+
+
+
+ org.dromara
+ ruoyi-common-ai
+
+
+
+ org.dromara
+ ruoyi-common-doc
+
+
+
+ org.dromara
+ ruoyi-common-satoken
+
+
+
+ org.dromara
+ ruoyi-common-dubbo
+
+
+
+ org.dromara
+ ruoyi-common-web
+
+
+
+ org.dromara
+ ruoyi-common-security
+
+
+
+ org.dromara
+ ruoyi-api-system
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.version}
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/RuoYiAiApplication.java b/ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/RuoYiAiApplication.java
new file mode 100644
index 000000000..00ac65fde
--- /dev/null
+++ b/ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/RuoYiAiApplication.java
@@ -0,0 +1,22 @@
+package org.dromara.ai;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
+
+/**
+ * AI模块
+ *
+ * @author opensnail
+ */
+@SpringBootApplication
+public class RuoYiAiApplication {
+
+ public static void main(String[] args) {
+ SpringApplication application = new SpringApplication(RuoYiAiApplication.class);
+ application.setApplicationStartup(new BufferingApplicationStartup(2048));
+ application.run(args);
+ System.out.println("(♥◠‿◠)ノ゙ AI模块启动成功 ლ(´ڡ`ლ)゙ ");
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/SnailAiController.java b/ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/SnailAiController.java
new file mode 100644
index 000000000..cc50e21ae
--- /dev/null
+++ b/ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/SnailAiController.java
@@ -0,0 +1,364 @@
+package org.dromara.ai.controller;
+
+import com.aizuda.snail.ai.common.execption.SnailAiException;
+import com.aizuda.snail.ai.common.model.Result;
+import com.aizuda.snail.ai.common.openapi.dto.*;
+import com.aizuda.snail.ai.openapi.client.core.api.OpenApiAgentClient;
+import com.aizuda.snail.ai.openapi.client.core.api.OpenApiChatClient;
+import com.aizuda.snail.ai.openapi.client.core.api.OpenApiConversationClient;
+import com.aizuda.snail.ai.openapi.client.core.api.OpenApiUserClient;
+import com.aizuda.snail.ai.openapi.client.core.listener.SseEventListener;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.domain.PageResult;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.system.api.model.LoginUser;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Snail AI OpenAPI 控制器
+ *
+ * @author opensnail
+ * @date 2026-04-25
+ */
+@Slf4j
+@Validated
+@RestController
+@RequestMapping
+@RequiredArgsConstructor
+public class SnailAiController extends BaseController {
+
+ /**
+ * Snail AI 成功状态码。
+ */
+ private static final int SNAIL_AI_SUCCESS = 1;
+ /**
+ * SSE 超时时间。
+ */
+ private static final long SSE_TIMEOUT = 300000L;
+
+ private final OpenApiAgentClient agentClient;
+ private final OpenApiChatClient chatClient;
+ private final OpenApiConversationClient conversationClient;
+ private final OpenApiUserClient userClient;
+ /**
+ * 聊天模式。
+ */
+ @Value("${snail-ai.chat-mode:stream}")
+ private String chatMode;
+
+ /**
+ * 注册当前登录用户并返回 OpenAPI 用户信息。
+ */
+ @PostMapping("/user/register")
+ public R registerCurrentUser() {
+ return R.ok(ensureOpenApiUser());
+ }
+
+ /**
+ * 查询当前登录用户对应的 OpenAPI 用户信息。
+ */
+ @GetMapping("/user")
+ public R getUser() {
+ OpenApiUserQueryRequest request = new OpenApiUserQueryRequest();
+ request.setOpenId(ensureOpenId());
+ return toR(userClient.getUser(request));
+ }
+
+ /**
+ * 查询当前用户可访问的智能体列表。
+ */
+ @GetMapping("/agents")
+ public R> listAgents() {
+ return toR(agentClient.listAgents());
+ }
+
+ /**
+ * 根据智能体 ID 查询智能体详情。
+ */
+ @GetMapping("/agent/{agentId}")
+ public R getAgent(@NotNull(message = "智能体ID不能为空") @PathVariable Long agentId) {
+ OpenApiAgentIdentityRequest request = new OpenApiAgentIdentityRequest();
+ request.setAgentId(agentId);
+ return toR(agentClient.getAgent(request));
+ }
+
+ /**
+ * 为指定智能体创建新会话。
+ */
+ @PostMapping("/agent/{agentId}/conversation")
+ public R createConversation(
+ @NotNull(message = "智能体ID不能为空") @PathVariable Long agentId,
+ @RequestBody OpenApiCreateConversationRequest request) {
+ request.setAgentId(agentId);
+ request.setOpenId(ensureOpenId());
+ return toR(conversationClient.createConversation(request));
+ }
+
+ /**
+ * 分页查询指定智能体下的会话列表。
+ */
+ @GetMapping("/agent/{agentId}/conversations")
+ public R> listConversations(
+ @NotNull(message = "智能体ID不能为空") @PathVariable Long agentId,
+ @Min(value = 1, message = "页码不能小于1") @RequestParam(defaultValue = "1") int page,
+ @Min(value = 1, message = "每页条数不能小于1") @RequestParam(defaultValue = "10") int size) {
+ OpenApiConversationQueryRequest request = new OpenApiConversationQueryRequest();
+ request.setAgentId(agentId);
+ request.setOpenId(ensureOpenId());
+ request.setPage(page);
+ request.setSize(size);
+ return toPageR(conversationClient.listConversations(request));
+ }
+
+ /**
+ * 查询指定会话的消息历史。
+ */
+ @GetMapping("/agent/{agentId}/conversation/{conversationId}/messages")
+ public R> getMessages(
+ @NotNull(message = "智能体ID不能为空") @PathVariable Long agentId,
+ @NotBlank(message = "会话ID不能为空") @PathVariable String conversationId) {
+ OpenApiConversationIdentityRequest request = new OpenApiConversationIdentityRequest();
+ request.setAgentId(agentId);
+ request.setConversationId(conversationId);
+ request.setOpenId(ensureOpenId());
+ return toR(conversationClient.getMessages(request));
+ }
+
+ /**
+ * 删除指定会话。
+ */
+ @DeleteMapping("/agent/{agentId}/conversation/{conversationId}")
+ public R deleteConversation(
+ @NotNull(message = "智能体ID不能为空") @PathVariable Long agentId,
+ @NotBlank(message = "会话ID不能为空") @PathVariable String conversationId) {
+ OpenApiConversationIdentityRequest request = new OpenApiConversationIdentityRequest();
+ request.setAgentId(agentId);
+ request.setConversationId(conversationId);
+ request.setOpenId(ensureOpenId());
+ return toR(conversationClient.deleteConversation(request));
+ }
+
+ /**
+ * 获取当前聊天发送模式。
+ */
+ @GetMapping("/chat/mode")
+ public R