From 41499fd3c28216c1526a72b10fa98eb8ffee78cb Mon Sep 17 00:00:00 2001
From: houzhongjian <houzhongyi@126.com>
Date: 星期四, 29 五月 2025 14:39:32 +0800
Subject: [PATCH] AI大模型服务初始化

---
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/music/AiMusicStatusEnum.java                                               |   39 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingPageReqVO.java      |   28 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/package-info.java                                                            |    4 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateEnergyReqVO.java   |   13 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyActionReqVO.java                |   20 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java                            |   20 
 iailab-module-ai/pom.xml                                                                                                                                  |  409 +
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/write/AiWriteServiceImpl.java                                            |  181 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiToolServiceImpl.java                                             |  100 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/TongYiImagesModelTest.java                                     |   38 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java                            |   34 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeServiceImpl.java                                    |   93 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeSegmentService.java                                 |  150 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/LlamaChatModelTests.java                                        |   65 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/YiYanChatModelTests.java                                        |   62 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java                      |   63 
 iailab-module-ai/iailab-module-ai-biz/pom.xml                                                                                                             |  123 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java                              |   28 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicRespVO.java                                     |   70 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java      |   30 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/write/AiWriteDO.java                                              |  104 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/chat/AiChatMessageMapper.java                                          |   57 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java                     |   43 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiModelDO.java                                              |   88 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/package-info.java                                                      |    6 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/security/config/SecurityConfiguration.java                             |   41 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/image/AiImageStatusEnum.java                                               |   37 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/xinghuo/XingHuoChatModel.java                             |   45 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java                             |   28 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/package-info.java                                                     |    6 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiToolMapper.java                                                |   35 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/AiChatRoleEnum.java                                                        |   50 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/ppt/wdd/WenDuoDuoPptApiTests.java                                    |  314 +
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/mcp/DouBaoMcpTests.java                                              |  122 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiModelServiceImpl.java                                            |  192 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/AiWorkflowController.java                              |   77 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java                                  |   40 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/midjourney/api/MidjourneyApi.java                         |  351 +
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java                          |   15 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/mindmap/AiMindMapServiceImpl.java                                        |  161 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/StabilityAiImageModelTests.java                                |   65 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java           |   23 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java                          |   25 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java                              |   69 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java                            |  214 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/music/AiMusicGenerateModeEnum.java                                         |   37 
 iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml                                                                                                    |  149 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/music/AiMusicDO.java                                              |  119 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/MidjourneyApiTests.java                                        |   62 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateServiceImpl.java                        |   71 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/chat/AiChatMessageDO.java                                         |  104 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/chat/AiChatConversationDO.java                                    |  100 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java           |   26 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplateRespVO.java                 |   38 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/AiServerApplication.java                                                         |   23 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java                               |   72 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/image/AiImageDO.java                                              |  126 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/factory/AiModelFactoryImpl.java                                 |  752 ++
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiToolController.java                                     |   84 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeService.java                                        |   63 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/MoonshotChatModelTests.java                                     |   62 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java              |   40 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java                              |   18 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java                              |   39 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/chat/AiChatConversationMapper.java                                     |   44 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/questionparamsetting/QuestionParamSettingMapper.java                   |   28 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java         |   27 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRolePageReqVO.java                      |   20 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiChatRoleServiceImpl.java                                         |  200 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java                                       |   32 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/config/IailabAiAutoConfiguration.java                                |  253 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java       |   16 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/ZhiPuAiChatModelTests.java                                      |   64 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/music/AiMusicMapper.java                                               |   44 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java                              |   34 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java            |   45 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java                  |   34 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java |   22 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelRespVO.java                               |   48 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java                    |   40 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java                  |  105 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImagePageReqVO.java                                  |   35 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiChatMapper.java                                                |   47 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java                                 |   27 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java                          |   34 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/image/AiMidjourneySyncJob.java                                               |   27 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateService.java                            |   53 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/AiMusicController.http                                    |   26 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java         |   17 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java                        |   75 
 iailab-module-ai/iailab-module-ai-biz/Dockerfile                                                                                                          |   19 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java                    |   43 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java                    |   90 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageRespVO.java                                     |   60 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/QianFanImageTests.java                                         |   43 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/xinghuo/api/XunFeiPptApi.java                             |  522 +
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/package-info.java                                                    |   13 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java                         |   45 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/util/AiUtils.java                                               |   87 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/baichuan/BaiChuanChatModel.java                           |   45 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java                               |   39 
 iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-test.yaml                                                                            |  122 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java       |   39 
 iailab-module-ai/iailab-module-ai-biz/src/main/resources/logback-spring.xml                                                                               |   76 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/ErrorCodeConstants.java                                                    |   73 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/OpenAiImageModelTests.java                                     |   40 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java                         |   64 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/music/AiMusicServiceImpl.java                                            |  218 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/AiImageController.java                                    |  139 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/suno/api/SunoApi.java                                     |  200 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java           |   21 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingSaveReqVO.java      |   30 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java                     |   29 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questionparamsetting/QuestionParamSettingServiceImpl.java                |   71 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/image/AiImageService.java                                                |  129 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiChatRoleMapper.java                                            |   54 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java                              |   27 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/mindmap/AiMindMapDO.java                                          |   66 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/image/AiImageMapper.java                                               |   57 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationService.java                                      |  106 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java                      |  115 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports        |    1 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiToolDO.java                                               |   48 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/write/AiWriteTypeEnum.java                                                 |   42 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/AiWriteController.java                                    |   57 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatMessageController.http                               |   29 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/AiMusicController.java                                    |   98 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/BaiChuanChatModelTests.java                                     |   68 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/music/AiMusicService.java                                                |   90 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java                            |   14 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/workflow/AiWorkflowMapper.java                                         |   30 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/factory/AiModelFactory.java                                     |  113 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageUpdateReqVO.java                                |   18 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatConversationController.java                          |  124 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiModelController.java                                    |   89 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/rpc/package-info.java                                                  |    4 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/workflow/AiWorkflowService.java                                          |   62 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/write/AiWriteService.java                                                |   41 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java                                  |  201 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http                    |   35 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java                          |   39 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DouBaoChatModelTests.java                                       |   69 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java                    |  159 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiApiKeyServiceImpl.java                                           |   99 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java                     |  131 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java                               |   33 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplateSaveReqVO.java              |   38 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/OpenAIChatModelTests.java                                       |   68 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java                                      |   64 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiChatRoleService.java                                             |  129 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiChatRoleController.java                                 |  124 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/image/AiImageServiceImpl.java                                            |  375 +
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java       |   21 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/music/AiSunoSyncJob.java                                                     |   28 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java   |   22 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java                |   41 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiModelService.java                                                |  142 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/QuestionParamSettingController.java        |   72 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/AiImageController.http                                    |   42 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/FastGPTChatModelTests.java                                      |   63 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/music/SunoApiTests.java                                              |   84 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeController.java                            |   75 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiChatRoleDO.java                                           |  103 
 iailab-module-ai/iailab-module-ai-api/pom.xml                                                                                                             |   44 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/write/AiWriteMapper.java                                               |   27 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/tool/DirectoryListToolFunction.java                                |   99 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java                            |   32 
 iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-dev.yaml                                                                             |  122 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/AzureOpenAIChatModelTests.java                                  |   69 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeController.http                            |   35 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatMessageController.java                               |  183 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/deepseek/DeepSeekChatModel.java                           |   45 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java                            |   20 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/wenduoduo/api/WenDuoDuoPptApi.java                        |  381 +
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/XingHuoChatModelTests.java                                      |   67 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java                            |   59 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiApiKeyDO.java                                             |   54 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java                             |  357 +
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/config/IailabAiProperties.java                                       |  164 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/enums/AiPlatformEnum.java                                       |   70 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageService.java                                           |   87 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/questiontemplate/QuestionTemplateDO.java                          |   59 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java       |   19 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/OllamaChatModelTests.java                                       |   65 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java                                 |   36 
 iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-local.yaml                                                                           |  121 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/questionparamsetting/QuestionParamSettingDO.java                  |   49 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/doubao/DouBaoChatModel.java                               |   45 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/workflow/AiWorkflowDO.java                                        |   51 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DeepSeekChatModelTests.java                                     |   67 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWritePageReqVO.java                                  |   31 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiApiKeyMapper.java                                              |   35 
 iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml                                                                                 |  224 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/hunyuan/HunYuanChatModel.java                             |   52 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http                     |   17 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/AiMindMapController.java                                |   57 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWriteRespVO.java                                     |   54 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/mindmap/AiMindMapService.java                                            |   41 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/rpc/config/RpcConfiguration.java                                       |   11 
 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/DictTypeConstants.java                                                     |   16 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/ZhiPuAiImageModelTests.java                                    |   35 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/app/package-info.java                                                 |    4 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/mindmap/AiMindMapMapper.java                                           |   26 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplatePageReqVO.java              |   42 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageServiceImpl.java                                       |  364 +
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/security/core/package-info.java                                        |    4 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/workflow/AiWorkflowServiceImpl.java                                      |  150 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingRespVO.java         |   28 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/tool/WeatherQueryToolFunction.java                                 |  118 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/SiliconFlowImageModelTests.java                                |   35 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java                                |   67 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questionparamsetting/QuestionParamSettingService.java                    |   53 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java              |   71 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/enums/AiModelTypeEnum.java                                      |   41 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java                                |   18 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/MiniMaxChatModelTests.java                                      |   62 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/QuestionTemplateController.java                |   73 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java                               |   57 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DifyChatModelTests.java                                         |   63 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/SiliconFlowChatModelTests.java                                  |   70 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiToolService.java                                                 |   80 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java                |   29 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/HunYuanChatModelTests.java                                      |  110 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java                     |   23 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/questiontemplate/QuestionTemplateMapper.java                           |   30 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiApiKeyService.java                                               |   80 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java   |   42 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java                   |   39 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java               |   35 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiApiKeyController.java                                   |   83 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/package-info.java                                                                |   10 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java        |   16 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java                                  |   49 
 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeDocumentService.java                                |  118 
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/ppt/xunfei/XunFeiPptApiTests.java                                    |  319 +
 iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/TongYiChatModelTests.java                                       |   66 
 236 files changed, 18,502 insertions(+), 0 deletions(-)

diff --git a/iailab-module-ai/iailab-module-ai-api/pom.xml b/iailab-module-ai/iailab-module-ai-api/pom.xml
new file mode 100644
index 0000000..5799a96
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.iailab</groupId>
+        <artifactId>iailab-module-ai</artifactId>
+        <version>${ai.revision}</version>
+    </parent>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>17</source>
+                    <target>17</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>iailab-module-ai-api</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>
+        ai 模块 API,暴露给其它模块调用
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common</artifactId>
+        </dependency>
+
+        <!-- 参数校验 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/package-info.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/package-info.java
new file mode 100644
index 0000000..4a3c94b
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 占位,没有特别的作用
+ */
+package com.iailab.module.ai.api;
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/AiChatRoleEnum.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/AiChatRoleEnum.java
new file mode 100644
index 0000000..a7addf8
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/AiChatRoleEnum.java
@@ -0,0 +1,50 @@
+package com.iailab.module.ai.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * AI 内置聊天角色的枚举
+ *
+ * @author xiaoxin
+ */
+@AllArgsConstructor
+@Getter
+public enum AiChatRoleEnum {
+
+    AI_WRITE_ROLE("写作助手", """
+            你是一位出色的写作助手,能够帮助用户生成创意和灵感,并在用户提供场景和提示词时生成对应的回复。你的任务包括:
+            1.	撰写建议:根据用户提供的主题或问题,提供详细的写作建议、情节发展方向、角色设定以及背景描写,确保内容结构清晰、有逻辑。
+            2.	回复生成:根据用户提供的场景和提示词,生成合适的对话或文字回复,确保语气和风格符合场景需求。
+            除此之外不需要除了正文内容外的其他回复,如标题、开头、任何解释性语句或道歉。
+            """),
+
+    AI_MIND_MAP_ROLE("导图助手", """
+             你是一位非常优秀的思维导图助手,你会把用户的所有提问都总结成思维导图,然后以 Markdown 格式输出。markdown 只需要输出一级标题,二级标题,三级标题,四级标题,最多输出四级,除此之外不要输出任何其他 markdown 标记。下面是一个合格的例子:
+             # Geek-AI 助手
+             ## 完整的开源系统
+             ### 前端开源
+             ### 后端开源
+             ## 支持各种大模型
+             ### OpenAI
+             ### Azure
+             ### 文心一言
+             ### 通义千问
+             ## 集成多种收费方式
+             ### 支付宝
+             ### 微信
+            除此之外不要任何解释性语句。
+            """),
+    ;
+
+    /**
+     * 角色名
+     */
+    private final String name;
+
+    /**
+     * 角色设定
+     */
+    private final String systemMessage;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/DictTypeConstants.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/DictTypeConstants.java
new file mode 100644
index 0000000..03d4c88
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/DictTypeConstants.java
@@ -0,0 +1,16 @@
+package com.iailab.module.ai.enums;
+
+/**
+ * AI 字典类型的枚举类
+ *
+ * @author xiaoxin
+ */
+public interface DictTypeConstants {
+
+    // ========== AI Write ==========
+    String AI_WRITE_FORMAT = "ai_write_format"; // 写作格式
+    String AI_WRITE_LENGTH = "ai_write_length"; // 写作长度
+    String AI_WRITE_LANGUAGE = "ai_write_language"; // 写作语言
+    String AI_WRITE_TONE = "ai_write_tone"; // 写作语气
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/ErrorCodeConstants.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/ErrorCodeConstants.java
new file mode 100644
index 0000000..d863744
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/ErrorCodeConstants.java
@@ -0,0 +1,73 @@
+package com.iailab.module.ai.enums;
+
+import com.iailab.framework.common.exception.ErrorCode;
+
+/**
+ * AI 错误码枚举类
+ * <p>
+ * ai 系统,使用 1-040-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+    // ========== API 密钥 1-040-000-000 ==========
+    ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在");
+    ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!");
+
+    // ========== API 模型 1-040-001-000 ==========
+    ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
+    ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!");
+    ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认模型");
+    ErrorCode MODEL_USE_TYPE_ERROR = new ErrorCode(1_040_001_003, "操作失败,该模型的模型类型不正确");
+    ErrorCode MODEL_NAME_EXISTS = new ErrorCode(1_040_001_004, "模型名称不能重复!");
+
+    // ========== API 聊天角色 1-040-002-000 ==========
+    ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在");
+    ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!");
+
+    // ========== API 聊天会话 1-040-003-000 ==========
+    ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!");
+    ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整");
+
+    // ========== API 聊天消息 1-040-004-000 ==========
+    ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!");
+    ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!");
+
+    // ========== API 绘画 1-040-005-000 ==========
+    ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_040_005_000, "图片不存在!");
+    ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_040_005_001, "Midjourney 提交失败!原因:{}");
+    ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_040_005_002, "Midjourney 按钮 customId 不存在! {}");
+
+    // ========== API 音乐 1-040-006-000 ==========
+    ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_040_006_000, "音乐不存在!");
+
+    // ========== API 写作 1-040-007-000 ==========
+    ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_040_007_000, "作文不存在!");
+    ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_040_07_001, "写作生成异常!");
+
+    // ========== API 思维导图 1-040-008-000 ==========
+    ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!");
+
+    // ========== API 知识库 1-040-009-000 ==========
+    ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_040_009_000, "知识库不存在!");
+
+    ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_040_009_101, "文档不存在!");
+    ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_040_009_102, "文档内容为空!");
+    ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_040_009_102, "文件下载失败!");
+    ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_040_009_102, "文档加载失败!");
+
+    ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_040_009_202, "段落不存在!");
+    ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_040_009_203, "内容 Token 数为 {},超过最大限制 {}");
+
+    // ========== AI 工具 1-040-010-000 ==========
+    ErrorCode TOOL_NOT_EXISTS = new ErrorCode(1_040_010_000, "工具不存在");
+    ErrorCode TOOL_NAME_NOT_EXISTS = new ErrorCode(1_040_010_001, "工具({})找不到 Bean");
+
+    // ========== AI 工作流 1-040-011-000 ==========
+    ErrorCode WORKFLOW_NOT_EXISTS = new ErrorCode(1_040_011_000, "工作流不存在");
+    ErrorCode WORKFLOW_CODE_EXISTS = new ErrorCode(1_040_011_001, "工作流标识已存在");
+
+    // ========== AI 模型模板 ==========
+    ErrorCode QUESTION_TEMPLATE_NOT_EXISTS = new ErrorCode(1_040_012_001, "大模型问题模板不存在");
+    ErrorCode QUESTION_PARAM_SETTING_NOT_EXISTS = new ErrorCode(1_040_012_002, "大模型问题设置参数不存在");
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/image/AiImageStatusEnum.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/image/AiImageStatusEnum.java
new file mode 100644
index 0000000..7a09d1f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/image/AiImageStatusEnum.java
@@ -0,0 +1,37 @@
+package com.iailab.module.ai.enums.image;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * AI 绘画状态的枚举
+ *
+ * @author fansili
+ */
+@AllArgsConstructor
+@Getter
+public enum AiImageStatusEnum {
+
+    IN_PROGRESS(10, "进行中"),
+    SUCCESS(20, "已完成"),
+    FAIL(30, "已失败");
+
+    /**
+     * 状态
+     */
+    private final Integer status;
+    /**
+     * 状态名
+     */
+    private final String name;
+
+    public static AiImageStatusEnum valueOfStatus(Integer status) {
+        for (AiImageStatusEnum statusEnum : AiImageStatusEnum.values()) {
+            if (statusEnum.getStatus().equals(status)) {
+                return statusEnum;
+            }
+        }
+        throw new IllegalArgumentException("未知会话状态: " + status);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/music/AiMusicGenerateModeEnum.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/music/AiMusicGenerateModeEnum.java
new file mode 100644
index 0000000..153f4c4
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/music/AiMusicGenerateModeEnum.java
@@ -0,0 +1,37 @@
+package com.iailab.module.ai.enums.music;
+
+import com.iailab.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * AI 音乐生成模式的枚举
+ *
+ * @author xiaoxin
+ */
+@AllArgsConstructor
+@Getter
+public enum AiMusicGenerateModeEnum implements ArrayValuable<Integer> {
+
+    DESCRIPTION(1, "描述模式"),
+    LYRIC(2, "歌词模式");
+
+    /**
+     * 模式
+     */
+    private final Integer mode;
+    /**
+     * 模式名
+     */
+    private final String name;
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicGenerateModeEnum::getMode).toArray(Integer[]::new);
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/music/AiMusicStatusEnum.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/music/AiMusicStatusEnum.java
new file mode 100644
index 0000000..1d979fb
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/music/AiMusicStatusEnum.java
@@ -0,0 +1,39 @@
+package com.iailab.module.ai.enums.music;
+
+import com.iailab.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * AI 音乐状态的枚举
+ *
+ * @author xiaoxin
+ */
+@AllArgsConstructor
+@Getter
+public enum AiMusicStatusEnum implements ArrayValuable<Integer> {
+
+    IN_PROGRESS(10, "进行中"),
+    SUCCESS(20, "已完成"),
+    FAIL(30, "已失败");
+
+    /**
+     * 状态
+     */
+    private final Integer status;
+
+    /**
+     * 状态名
+     */
+    private final String name;
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicStatusEnum::getStatus).toArray(Integer[]::new);
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/write/AiWriteTypeEnum.java b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/write/AiWriteTypeEnum.java
new file mode 100644
index 0000000..37c9a2b
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/write/AiWriteTypeEnum.java
@@ -0,0 +1,42 @@
+package com.iailab.module.ai.enums.write;
+
+import com.iailab.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * AI 写作类型的枚举
+ *
+ * @author xiaoxin
+ */
+@AllArgsConstructor
+@Getter
+public enum AiWriteTypeEnum implements ArrayValuable<Integer> {
+
+    WRITING(1, "撰写", "请撰写一篇关于 [{}] 的文章。文章的内容格式:{},语气:{},语言:{},长度:{}。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"),
+    REPLY(2, "回复", "请针对如下内容:[{}] 做个回复。回复内容参考:[{}], 回复格式:{},语气:{},语言:{},长度:{}。不需要除了正文内容外的其他回复,如标题、开头、额外的解释或道歉。");
+
+    /**
+     * 类型
+     */
+    private final Integer type;
+    /**
+     * 类型名
+     */
+    private final String name;
+
+    /**
+     * 模版
+     */
+    private final String prompt;
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiWriteTypeEnum::getType).toArray(Integer[]::new);
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/Dockerfile b/iailab-module-ai/iailab-module-ai-biz/Dockerfile
new file mode 100644
index 0000000..e9af566
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/Dockerfile
@@ -0,0 +1,19 @@
+## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
+## 感谢复旦核博士的建议!灰子哥,牛皮!
+FROM eclipse-temurin:21-jre
+
+## 创建目录,并使用它作为工作目录
+RUN mkdir -p /iailab-module-ai-biz
+WORKDIR /iailab-module-ai-biz
+## 将后端项目的 Jar 文件,复制到镜像中
+COPY ./target/iailab-module-ai-biz.jar app.jar
+
+## 设置 TZ 时区
+## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
+ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
+
+## 暴露后端项目的 48080 端口
+EXPOSE 48090
+
+## 启动后端项目
+CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
diff --git a/iailab-module-ai/iailab-module-ai-biz/pom.xml b/iailab-module-ai/iailab-module-ai-biz/pom.xml
new file mode 100644
index 0000000..a7958b1
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/pom.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.iailab</groupId>
+        <artifactId>iailab-module-ai</artifactId>
+        <version>${ai.revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>iailab-module-ai-biz</artifactId>
+
+    <name>${project.artifactId}</name>
+    <description>
+        ai 模块下,接入 LLM 大模型,支持聊天、绘图、音乐、写作、思维导图等功能。
+        目前已接入各种模型,不限于:
+        国内:通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek
+        国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno
+    </description>
+
+    <dependencies>
+        <!-- Spring Cloud 基础 -->
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common-env</artifactId>
+        </dependency>
+
+        <!-- 依赖服务 -->
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-module-ai-api</artifactId>
+            <version>${ai.revision}</version>
+        </dependency>
+
+        <!-- 业务组件 -->
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-spring-boot-starter-ai</artifactId>
+            <version>${ai.revision}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common-biz-tenant</artifactId>
+        </dependency>
+
+        <!-- Web 相关 -->
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common-security</artifactId>
+        </dependency>
+
+        <!-- DB 相关 -->
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common-mybatis</artifactId>
+        </dependency>
+
+        <!-- RPC 远程调用相关 -->
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common-rpc</artifactId>
+        </dependency>
+
+        <!-- Registry 注册中心相关 -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <!-- Config 配置中心相关 -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <!-- Job 相关 -->
+<!--        <dependency>-->
+<!--            <groupId>com.iailab</groupId>-->
+<!--            <artifactId>iailab-common-job</artifactId>-->
+<!--        </dependency>-->
+
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common-test</artifactId>
+        </dependency>
+
+        <!-- 监控相关 -->
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common-monitor</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <!-- 设置构建的 jar 包名 -->
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <!-- 打包 -->
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring.boot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>17</source>
+                    <target>17</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/AiServerApplication.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/AiServerApplication.java
new file mode 100644
index 0000000..c4bad6c
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/AiServerApplication.java
@@ -0,0 +1,23 @@
+package com.iailab.module.ai;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * 项目的启动类
+ * <p>
+ *
+ * @author Iailab
+ */
+@SpringBootApplication(exclude = {
+        org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration.class,
+        org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration.class,
+}) // 解决 application-${profile}.yaml 配置文件下,通过 spring.autoconfigure.exclude 无法排除的问题
+public class AiServerApplication {
+
+    public static void main(String[] args) {
+
+        SpringApplication.run(AiServerApplication.class, args);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatConversationController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatConversationController.java
new file mode 100644
index 0000000..2f26bab
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatConversationController.java
@@ -0,0 +1,124 @@
+package com.iailab.module.ai.controller.admin.chat;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.*;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO;
+import com.iailab.module.ai.service.chat.AiChatConversationService;
+import com.iailab.module.ai.service.chat.AiChatMessageService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - AI 聊天对话")
+@RestController
+@RequestMapping("/ai/chat/conversation")
+@Validated
+public class AiChatConversationController {
+
+    @Resource
+    private AiChatConversationService chatConversationService;
+    @Resource
+    private AiChatMessageService chatMessageService;
+
+    @PostMapping("/create-my")
+    @Operation(summary = "创建【我的】聊天对话")
+    public CommonResult<Long> createChatConversationMy(@RequestBody @Valid AiChatConversationCreateMyReqVO createReqVO) {
+        return success(chatConversationService.createChatConversationMy(createReqVO, getLoginUserId()));
+    }
+
+    @PostMapping("/create-energy")
+    @Operation(summary = "创建【我的】聊天对话")
+    public CommonResult<Long> createChatConversationEnergy(@RequestBody @Valid AiChatConversationCreateEnergyReqVO createReqVO) {
+        return success(chatConversationService.createChatConversationEnergy(createReqVO));
+    }
+
+    @PutMapping("/update-my")
+    @Operation(summary = "更新【我的】聊天对话")
+    public CommonResult<Boolean> updateChatConversationMy(@RequestBody @Valid AiChatConversationUpdateMyReqVO updateReqVO) {
+        chatConversationService.updateChatConversationMy(updateReqVO, getLoginUserId());
+        return success(true);
+    }
+
+    @GetMapping("/my-list")
+    @Operation(summary = "获得【我的】聊天对话列表")
+    public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() {
+        List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId());
+        return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));
+    }
+
+    @GetMapping("/energy-list")
+    @Operation(summary = "获得【工业大模型】聊天对话列表")
+    public CommonResult<List<AiChatConversationRespVO>> getChatConversationEnergyList(@RequestParam("modelName") String modelName) {
+        List<AiChatConversationDO> list = chatConversationService.getChatConversationList(getLoginUserId(), modelName);
+        return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));
+    }
+
+    @GetMapping("/get-my")
+    @Operation(summary = "获得【我的】聊天对话")
+    @Parameter(name = "id", required = true, description = "对话编号", example = "1024")
+    public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) {
+        AiChatConversationDO conversation = chatConversationService.getChatConversation(id);
+        if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
+            conversation = null;
+        }
+        return success(BeanUtils.toBean(conversation, AiChatConversationRespVO.class));
+    }
+
+    @DeleteMapping("/delete-my")
+    @Operation(summary = "删除聊天对话")
+    @Parameter(name = "id", required = true, description = "对话编号", example = "1024")
+    public CommonResult<Boolean> deleteChatConversationMy(@RequestParam("id") Long id) {
+        chatConversationService.deleteChatConversationMy(id, getLoginUserId());
+        return success(true);
+    }
+
+    @DeleteMapping("/delete-by-unpinned")
+    @Operation(summary = "删除未置顶的聊天对话")
+    public CommonResult<Boolean> deleteChatConversationMyByUnpinned() {
+        chatConversationService.deleteChatConversationMyByUnpinned(getLoginUserId());
+        return success(true);
+    }
+
+    // ========== 对话管理 ==========
+
+    @GetMapping("/page")
+    @Operation(summary = "获得对话分页", description = "用于【对话管理】菜单")
+    @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')")
+    public CommonResult<PageResult<AiChatConversationRespVO>> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
+        PageResult<AiChatConversationDO> pageResult = chatConversationService.getChatConversationPage(pageReqVO);
+        if (CollUtil.isEmpty(pageResult.getList())) {
+            return success(PageResult.empty());
+        }
+        // 拼接关联数据
+        Map<Long, Integer> messageCountMap = chatMessageService.getChatMessageCountMap(
+                convertList(pageResult.getList(), AiChatConversationDO::getId));
+        return success(BeanUtils.toBean(pageResult, AiChatConversationRespVO.class,
+                conversation -> conversation.setMessageCount(messageCountMap.getOrDefault(conversation.getId(), 0))));
+    }
+
+    @Operation(summary = "管理员删除对话")
+    @DeleteMapping("/delete-by-admin")
+    @Parameter(name = "id", required = true, description = "对话编号", example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:chat-conversation:delete')")
+    public CommonResult<Boolean> deleteChatConversationByAdmin(@RequestParam("id") Long id) {
+        chatConversationService.deleteChatConversationByAdmin(id);
+        return success(true);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatMessageController.http b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatMessageController.http
new file mode 100644
index 0000000..4c4c8c0
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatMessageController.http
@@ -0,0 +1,29 @@
+### 发送消息(段式)
+POST {{baseUrl}}/ai/chat/message/send
+Content-Type: application/json
+Authorization: {{token}}
+tenant-id: {{adminTenantId}}
+
+{
+  "conversationId": "1781604279872581724",
+  "content": "你是 OpenAI 么?"
+}
+
+### 发送消息(流式)
+POST {{baseUrl}}/ai/chat/message/send-stream
+Content-Type: application/json
+Authorization: {{token}}
+tenant-id: {{adminTenantId}}
+
+{
+  "conversationId": "1781604279872581724",
+  "content": "1+1=?"
+}
+
+### 获得指定对话的消息列表
+GET {{baseUrl}}/ai/chat/message/list-by-conversation-id?conversationId=1781604279872581649
+Authorization: {{token}}
+
+### 删除消息
+DELETE {{baseUrl}}/ai/chat/message/delete?id=50
+Authorization: {{token}}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatMessageController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatMessageController.java
new file mode 100644
index 0000000..ee38df8
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatMessageController.java
@@ -0,0 +1,183 @@
+package com.iailab.module.ai.controller.admin.chat;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import com.aliyun.tea.utils.StringUtils;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.collection.MapUtils;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatMessageDO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.iailab.module.ai.service.chat.AiChatConversationService;
+import com.iailab.module.ai.service.chat.AiChatMessageService;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeDocumentService;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeSegmentService;
+import com.iailab.module.ai.service.model.AiChatRoleService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.annotation.security.PermitAll;
+import jakarta.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Flux;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.common.util.collection.CollectionUtils.*;
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - 聊天消息")
+@RestController
+@RequestMapping("/ai/chat/message")
+@Slf4j
+public class AiChatMessageController {
+
+    @Resource
+    private AiChatMessageService chatMessageService;
+    @Resource
+    private AiChatConversationService chatConversationService;
+    @Resource
+    private AiChatRoleService chatRoleService;
+    @Resource
+    private AiKnowledgeSegmentService knowledgeSegmentService;
+    @Resource
+    private AiKnowledgeDocumentService knowledgeDocumentService;
+
+    @Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢")
+    @PostMapping("/send")
+    public CommonResult<AiChatMessageSendRespVO> sendMessage(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
+        return success(chatMessageService.sendMessage(sendReqVO, getLoginUserId()));
+    }
+
+    @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
+    @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
+        return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());
+    }
+
+    @Operation(summary = "获得指定对话的消息列表")
+    @GetMapping("/list-by-conversation-id")
+    @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024")
+    public CommonResult<List<AiChatMessageRespVO>> getChatMessageListByConversationId(
+            @RequestParam("conversationId") Long conversationId) {
+        AiChatConversationDO conversation = chatConversationService.getChatConversation(conversationId);
+        if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
+            return success(Collections.emptyList());
+        }
+        // 1. 获取消息列表
+        List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);
+        if (CollUtil.isEmpty(messageList)) {
+            return success(Collections.emptyList());
+        }
+
+        // 2. 拼接数据,主要是知识库段落信息
+        Map<Long, AiKnowledgeSegmentDO> segmentMap = knowledgeSegmentService.getKnowledgeSegmentMap(convertListByFlatMap(messageList,
+                message -> CollUtil.isEmpty(message.getSegmentIds()) ? null : message.getSegmentIds().stream()));
+        Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(
+                convertList(segmentMap.values(), AiKnowledgeSegmentDO::getDocumentId));
+        List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class);
+        for (int i = 0; i < messageList.size(); i++) {
+            AiChatMessageDO message = messageList.get(i);
+            if (CollUtil.isEmpty(message.getSegmentIds())) {
+                continue;
+            }
+            // 设置知识库段落信息
+            messageVOList.get(i).setSegments(convertList(message.getSegmentIds(), segmentId -> {
+                AiKnowledgeSegmentDO segment = segmentMap.get(segmentId);
+                if (segment == null) {
+                    return null;
+                }
+                AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
+                if (document == null) {
+                    return null;
+                }
+                return new AiChatMessageRespVO.KnowledgeSegment().setId(segment.getId()).setContent(segment.getContent())
+                        .setDocumentId(segment.getDocumentId()).setDocumentName(document.getName());
+            }));
+        }
+        return success(messageVOList);
+    }
+
+    @Operation(summary = "获得指定对话的消息列表【工业大模型专用】")
+    @GetMapping("/energy-list-by-conversation-id")
+    @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024")
+    public CommonResult<List<AiChatMessageRespVO>> getEnergyChatMessageListByConversationId(
+            @RequestParam("conversationId") Long conversationId) {
+        // 1. 获取消息列表
+        List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);
+        if (CollUtil.isEmpty(messageList)) {
+            return success(Collections.emptyList());
+        }
+        // 查询最近的一次对话
+        int size = messageList.size();
+        if(size >= 2) {
+            AiChatMessageDO aiChatMessageDO = messageList.get(size - 1);
+            if(size >= 4 && StringUtils.isEmpty(aiChatMessageDO.getContent())) {
+                messageList = messageList.subList(size - 4, size - 2);
+            } else {
+                messageList = messageList.subList(size - 2, size);
+            }
+        }
+        List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class);
+        return success(messageVOList);
+    }
+
+    @Operation(summary = "删除消息")
+    @DeleteMapping("/delete")
+    @Parameter(name = "id", required = true, description = "消息编号", example = "1024")
+    public CommonResult<Boolean> deleteChatMessage(@RequestParam("id") Long id) {
+        chatMessageService.deleteChatMessage(id, getLoginUserId());
+        return success(true);
+    }
+
+    @Operation(summary = "删除指定对话的消息")
+    @DeleteMapping("/delete-by-conversation-id")
+    @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024")
+    public CommonResult<Boolean> deleteChatMessageByConversationId(@RequestParam("conversationId") Long conversationId) {
+        chatMessageService.deleteChatMessageByConversationId(conversationId, getLoginUserId());
+        return success(true);
+    }
+
+    // ========== 对话管理 ==========
+
+    @GetMapping("/page")
+    @Operation(summary = "获得消息分页", description = "用于【对话管理】菜单")
+    @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')")
+    public CommonResult<PageResult<AiChatMessageRespVO>> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {
+        PageResult<AiChatMessageDO> pageResult = chatMessageService.getChatMessagePage(pageReqVO);
+        if (CollUtil.isEmpty(pageResult.getList())) {
+            return success(PageResult.empty());
+        }
+        // 拼接数据
+        Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap(
+                convertSet(pageResult.getList(), AiChatMessageDO::getRoleId));
+        return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class,
+                respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(),
+                        role -> respVO.setRoleName(role.getName()))));
+    }
+
+    @Operation(summary = "管理员删除消息")
+    @DeleteMapping("/delete-by-admin")
+    @Parameter(name = "id", required = true, description = "消息编号", example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:chat-message:delete')")
+    public CommonResult<Boolean> deleteChatMessageByAdmin(@RequestParam("id") Long id) {
+        chatMessageService.deleteChatMessageByAdmin(id);
+        return success(true);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateEnergyReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateEnergyReqVO.java
new file mode 100644
index 0000000..befdf17
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateEnergyReqVO.java
@@ -0,0 +1,13 @@
+package com.iailab.module.ai.controller.admin.chat.vo.conversation;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 聊天对话创建【我的】 Request VO")
+@Data
+public class AiChatConversationCreateEnergyReqVO {
+
+    @Schema(description = "模型名称", example = "zhuanlu")
+    private String modelName;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java
new file mode 100644
index 0000000..a4a5beb
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java
@@ -0,0 +1,16 @@
+package com.iailab.module.ai.controller.admin.chat.vo.conversation;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 聊天对话创建【我的】 Request VO")
+@Data
+public class AiChatConversationCreateMyReqVO {
+
+    @Schema(description = "聊天角色编号", example = "666")
+    private Long roleId;
+
+    @Schema(description = "知识库编号", example = "1204")
+    private Long knowledgeId;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java
new file mode 100644
index 0000000..f52b054
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java
@@ -0,0 +1,26 @@
+package com.iailab.module.ai.controller.admin.chat.vo.conversation;
+
+import com.iailab.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI 聊天对话的分页 Request VO")
+@Data
+public class AiChatConversationPageReqVO extends PageParam {
+
+    @Schema(description = "用户编号", example = "1024")
+    private Long userId;
+
+    @Schema(description = "对话标题", example = "你好")
+    private String title;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
new file mode 100644
index 0000000..cfbec26
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
@@ -0,0 +1,71 @@
+package com.iailab.module.ai.controller.admin.chat.vo.conversation;
+
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.fhs.core.trans.anno.Trans;
+import com.fhs.core.trans.constant.TransType;
+import com.fhs.core.trans.vo.VO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - AI 聊天对话 Response VO")
+@Data
+public class AiChatConversationRespVO implements VO {
+
+    @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+    private Long userId;
+
+    @Schema(description = "对话标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是一个标题")
+    private String title;
+
+    @Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean pinned;
+
+    @Schema(description = "角色编号", example = "1")
+    @Trans(type = TransType.SIMPLE, target = AiChatRoleDO.class, fields = {"name", "avatar"}, refs = {"roleName", "roleAvatar"})
+    private Long roleId;
+
+    @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = "name", ref = "modelName")
+    private Long modelId;
+
+    @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
+    private String model;
+
+    @Schema(description = "模型名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+    private String modelName;
+
+    @Schema(description = "角色设定", example = "一个快乐的程序员")
+    private String systemMessage;
+
+    @Schema(description = "温度参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8")
+    private Double temperature;
+
+    @Schema(description = "单条回复的最大 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
+    private Integer maxTokens;
+
+    @Schema(description = "上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Integer maxContexts;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+    // ========== 关联 role 信息 ==========
+
+    @Schema(description = "角色头像", example = "https://www.Iailab.cn/1.png")
+    private String roleAvatar;
+
+    @Schema(description = "角色名字", example = "小黄")
+    private String roleName;
+
+    // ========== 仅在【对话管理】时加载 ==========
+
+    @Schema(description = "消息数量", example = "20")
+    private Integer messageCount;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
new file mode 100644
index 0000000..d11084e
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
@@ -0,0 +1,39 @@
+package com.iailab.module.ai.controller.admin.chat.vo.conversation;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 聊天对话更新【我的】 Request VO")
+@Data
+public class AiChatConversationUpdateMyReqVO {
+
+    @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "对话编号不能为空")
+    private Long id;
+
+    @Schema(description = "对话标题", example = "我是一个标题")
+    private String title;
+
+    @Schema(description = "是否置顶", example = "true")
+    private Boolean pinned;
+
+    @Schema(description = "模型编号", example = "1")
+    private Long modelId;
+
+    @Schema(description = "知识库编号", example = "1")
+    private Long knowledgeId;
+
+    @Schema(description = "角色设定", example = "一个快乐的程序员")
+    private String systemMessage;
+
+    @Schema(description = "温度参数", example = "0.8")
+    private Double temperature;
+
+    @Schema(description = "单条回复的最大 Token 数量", example = "4096")
+    private Integer maxTokens;
+
+    @Schema(description = "上下文的最大 Message 数量", example = "10")
+    private Integer maxContexts;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java
new file mode 100644
index 0000000..8ef693e
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java
@@ -0,0 +1,29 @@
+package com.iailab.module.ai.controller.admin.chat.vo.message;
+
+import com.iailab.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI 聊天消息的分页 Request VO")
+@Data
+public class AiChatMessagePageReqVO extends PageParam {
+
+    @Schema(description = "对话编号", example = "2048")
+    private Long conversationId;
+
+    @Schema(description = "用户编号", example = "1024")
+    private Long userId;
+
+    @Schema(description = "消息内容", example = "你好")
+    private String content;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
new file mode 100644
index 0000000..c95da12
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
@@ -0,0 +1,75 @@
+package com.iailab.module.ai.controller.admin.chat.vo.message;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - AI 聊天消息 Response VO")
+@Data
+public class AiChatMessageRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+    private Long conversationId;
+
+    @Schema(description = "回复消息编号", example = "1024")
+    private Long replyId;
+
+    @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role")
+    private String type; // 参见 MessageType 枚举类
+
+    @Schema(description = "用户编号", example = "4096")
+    private Long userId;
+
+    @Schema(description = "角色编号", example = "888")
+    private Long roleId;
+
+    @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo")
+    private String model;
+
+    @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123")
+    private Long modelId;
+
+    @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
+    private String content;
+
+    @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean useContext;
+
+    @Schema(description = "知识库段落编号数组", example = "[1,2,3]")
+    private List<Long> segmentIds;
+
+    @Schema(description = "知识库段落数组")
+    private List<KnowledgeSegment> segments;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
+    private LocalDateTime createTime;
+
+    // ========== 仅在【对话管理】时加载 ==========
+
+    @Schema(description = "角色名字", example = "小黄")
+    private String roleName;
+
+    @Schema(description = "知识库段落", example = "Java 开发手册")
+    @Data
+    public static class KnowledgeSegment {
+
+        @Schema(description = "段落编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+        private Long id;
+
+        @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
+        private String content;
+
+        @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+        private Long documentId;
+
+        @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册")
+        private String documentName;
+
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java
new file mode 100644
index 0000000..e2c8241
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java
@@ -0,0 +1,23 @@
+package com.iailab.module.ai.controller.admin.chat.vo.message;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 聊天消息发送 Request VO")
+@Data
+public class AiChatMessageSendReqVO {
+
+    @Schema(description = "聊天对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "聊天对话编号不能为空")
+    private Long conversationId;
+
+    @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "帮我写个 Java 算法")
+    @NotEmpty(message = "聊天内容不能为空")
+    private String content;
+
+    @Schema(description = "是否携带上下文", example = "true")
+    private Boolean useContext;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java
new file mode 100644
index 0000000..d06cb7d
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java
@@ -0,0 +1,43 @@
+package com.iailab.module.ai.controller.admin.chat.vo.message;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - AI 聊天消息发送 Response VO")
+@Data
+public class AiChatMessageSendRespVO {
+
+    @Schema(description = "发送消息", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Message send;
+
+    @Schema(description = "接收消息", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Message receive;
+
+    @Schema(description = "消息")
+    @Data
+    public static class Message {
+
+        @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+        private Long id;
+
+        @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role")
+        private String type; // 参见 MessageType 枚举类
+
+        @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
+        private String content;
+
+        @Schema(description = "知识库段落编号数组", example = "[1,2,3]")
+        private List<Long> segmentIds;
+
+        @Schema(description = "知识库段落数组")
+        private List<AiChatMessageRespVO.KnowledgeSegment> segments;
+
+        @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+        private LocalDateTime createTime;
+
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/AiImageController.http b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/AiImageController.http
new file mode 100644
index 0000000..9047610
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/AiImageController.http
@@ -0,0 +1,42 @@
+### 生成图片:OpenAI(DALL)
+POST {{baseUrl}}/ai/image/draw
+Content-Type: application/json
+Authorization: {{token}}
+
+{
+  "platform": "OpenAI",
+  "prompt": "可爱的小喵星人",
+  "model": "dall-e-3",
+  "height": "1024",
+  "width": "1024",
+  "options": {
+    "style": "vivid"
+  }
+}
+
+### 生成图片:StableDiffusion
+POST {{baseUrl}}/ai/image/draw
+Content-Type: application/json
+Authorization: {{token}}
+
+{
+  "platform": "StableDiffusion",
+  "prompt": "中国长城",
+  "model": "stable-diffusion-v1-6",
+  "height": "1024",
+  "width": "1024",
+  "style": "vivid"
+}
+
+### 生成图片:生成图片(Midjourney)
+POST {{baseUrl}}/ai/image/midjourney/imagine
+Content-Type: application/json
+Authorization: {{token}}
+
+{
+  "prompt": "中国旗袍",
+  "model": "midjourney",
+  "width": "1",
+  "height": "1",
+  "version": "6.0"
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/AiImageController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/AiImageController.java
new file mode 100644
index 0000000..00410e4
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/AiImageController.java
@@ -0,0 +1,139 @@
+package com.iailab.module.ai.controller.admin.image;
+
+import cn.hutool.core.util.ObjUtil;
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.tenant.core.aop.TenantIgnore;
+import com.iailab.module.ai.controller.admin.image.vo.*;
+import com.iailab.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
+import com.iailab.module.ai.dal.dataobject.image.AiImageDO;
+import com.iailab.module.ai.service.image.AiImageService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.annotation.security.PermitAll;
+import jakarta.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - AI 绘画")
+@RestController
+@RequestMapping("/ai/image")
+@Slf4j
+public class AiImageController {
+
+    @Resource
+    private AiImageService imageService;
+
+    @GetMapping("/my-page")
+    @Operation(summary = "获取【我的】绘图分页")
+    public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated AiImagePageReqVO pageReqVO) {
+        PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
+    }
+
+    @GetMapping("/public-page")
+    @Operation(summary = "获取公开的绘图分页")
+    public CommonResult<PageResult<AiImageRespVO>> getImagePagePublic(AiImagePublicPageReqVO pageReqVO) {
+        PageResult<AiImageDO> pageResult = imageService.getImagePagePublic(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
+    }
+
+    @GetMapping("/get-my")
+    @Operation(summary = "获取【我的】绘图记录")
+    @Parameter(name = "id", required = true, description = "绘画编号", example = "1024")
+    public CommonResult<AiImageRespVO> getImageMy(@RequestParam("id") Long id) {
+        AiImageDO image = imageService.getImage(id);
+        if (image == null || ObjUtil.notEqual(getLoginUserId(), image.getUserId())) {
+            return success(null);
+        }
+        return success(BeanUtils.toBean(image, AiImageRespVO.class));
+    }
+
+    @GetMapping("/my-list-by-ids")
+    @Operation(summary = "获取【我的】绘图记录列表")
+    @Parameter(name = "ids", required = true, description = "绘画编号数组", example = "1024,2048")
+    public CommonResult<List<AiImageRespVO>> getImageListMyByIds(@RequestParam("ids") List<Long> ids) {
+        List<AiImageDO> imageList = imageService.getImageList(ids);
+        imageList.removeIf(item -> !ObjUtil.equal(getLoginUserId(), item.getUserId()));
+        return success(BeanUtils.toBean(imageList, AiImageRespVO.class));
+    }
+
+    @Operation(summary = "生成图片")
+    @PostMapping("/draw")
+    public CommonResult<Long> drawImage(@Valid @RequestBody AiImageDrawReqVO drawReqVO) {
+        return success(imageService.drawImage(getLoginUserId(), drawReqVO));
+    }
+
+    @Operation(summary = "删除【我的】绘画记录")
+    @DeleteMapping("/delete-my")
+    @Parameter(name = "id", required = true, description = "绘画编号", example = "1024")
+    public CommonResult<Boolean> deleteImageMy(@RequestParam("id") Long id) {
+        imageService.deleteImageMy(id, getLoginUserId());
+        return success(true);
+    }
+
+    // ================ midjourney 专属 ================
+
+    @Operation(summary = "【Midjourney】生成图片")
+    @PostMapping("/midjourney/imagine")
+    public CommonResult<Long> midjourneyImagine(@Valid @RequestBody AiMidjourneyImagineReqVO reqVO) {
+        Long imageId = imageService.midjourneyImagine(getLoginUserId(), reqVO);
+        return success(imageId);
+    }
+
+    @Operation(summary = "【Midjourney】通知图片进展", description = "由 Midjourney Proxy 回调")
+    @PostMapping("/midjourney/notify") // 必须是 POST 方法,否则会报错
+    @PermitAll
+    @TenantIgnore
+    public CommonResult<Boolean> midjourneyNotify(@Valid @RequestBody MidjourneyApi.Notify notify) {
+        imageService.midjourneyNotify(notify);
+        return success(true);
+    }
+
+    @Operation(summary = "【Midjourney】Action 操作(二次生成图片)", description = "例如说:放大、缩小、U1、U2 等")
+    @PostMapping("/midjourney/action")
+    public CommonResult<Long> midjourneyAction(@Valid @RequestBody AiMidjourneyActionReqVO reqVO) {
+        Long imageId = imageService.midjourneyAction(getLoginUserId(), reqVO);
+        return success(imageId);
+    }
+
+    // ================ 绘图管理 ================
+
+    @GetMapping("/page")
+    @Operation(summary = "获得绘画分页")
+    @PreAuthorize("@ss.hasPermission('ai:image:query')")
+    public CommonResult<PageResult<AiImageRespVO>> getImagePage(@Valid AiImagePageReqVO pageReqVO) {
+        PageResult<AiImageDO> pageResult = imageService.getImagePage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新绘画")
+    @PreAuthorize("@ss.hasPermission('ai:image:update')")
+    public CommonResult<Boolean> updateImage(@Valid @RequestBody AiImageUpdateReqVO updateReqVO) {
+        imageService.updateImage(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除绘画")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:image:delete')")
+    public CommonResult<Boolean> deleteImage(@RequestParam("id") Long id) {
+        imageService.deleteImage(id);
+        return success(true);
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java
new file mode 100644
index 0000000..311a9e0
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java
@@ -0,0 +1,49 @@
+package com.iailab.module.ai.controller.admin.image.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import org.springframework.ai.openai.OpenAiImageOptions;
+import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
+
+import java.util.Map;
+
+@Schema(description = "管理后台 - AI 绘画 Request VO")
+@Data
+public class AiImageDrawReqVO {
+
+    @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "模型编号不能为空")
+    private Long modelId;
+
+    @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城")
+    @NotEmpty(message = "提示词不能为空")
+    @Size(max = 1200, message = "提示词最大 1200")
+    private String prompt;
+
+    /**
+     * 1. dall-e-2 模型:256x256、512x512、1024x1024
+     * 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792
+     */
+    @Schema(description = "图片高度")
+    @NotNull(message = "图片高度不能为空")
+    private Integer height;
+
+    @Schema(description = "图片宽度")
+    @NotNull(message = "图片宽度不能为空")
+    private Integer width;
+
+    // ========== 各平台绘画的拓展参数 ==========
+
+    /**
+     * 绘制参数,不同 platform 的不同参数
+     *
+     * 1. {@link OpenAiImageOptions}
+     * 2. {@link StabilityAiImageOptions}
+     */
+    @Schema(description = "绘制参数")
+    private Map<String, String> options;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImagePageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImagePageReqVO.java
new file mode 100644
index 0000000..3f3129f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImagePageReqVO.java
@@ -0,0 +1,35 @@
+package com.iailab.module.ai.controller.admin.image.vo;
+
+import com.iailab.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI 绘画分页 Request VO")
+@Data
+public class AiImagePageReqVO extends PageParam {
+
+    @Schema(description = "用户编号", example = "28987")
+    private Long userId;
+
+    @Schema(description = "平台", example = "OpenAI")
+    private String platform;
+
+    @Schema(description = "提示词", example = "1")
+    private String prompt;
+
+    @Schema(description = "绘画状态", example = "1")
+    private Integer status;
+
+    @Schema(description = "是否发布", example = "1")
+    private Boolean publicStatus;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java
new file mode 100644
index 0000000..2eeee00
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java
@@ -0,0 +1,14 @@
+package com.iailab.module.ai.controller.admin.image.vo;
+
+import com.iailab.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 绘画公开的分页 Request VO")
+@Data
+public class AiImagePublicPageReqVO extends PageParam {
+
+    @Schema(description = "提示词")
+    private String prompt;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageRespVO.java
new file mode 100644
index 0000000..a5c1023
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageRespVO.java
@@ -0,0 +1,60 @@
+package com.iailab.module.ai.controller.admin.image.vo;
+
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+@Schema(description = "管理后台 - AI 绘画 Response VO")
+@Data
+public class AiImageRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long id;
+
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long userId;
+
+    @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+    private String platform;  // 参见 AiPlatformEnum 枚举
+
+    @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
+    private String model;
+
+    @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "南极的小企鹅")
+    private String prompt;
+
+    @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Integer width;
+
+    @Schema(description = "图片高度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Integer height;
+
+    @Schema(description = "绘画状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Integer status;
+
+    @Schema(description = "是否发布", requiredMode = Schema.RequiredMode.REQUIRED, example = "public")
+    private Boolean publicStatus;
+
+    @Schema(description = "图片地址", example = "https://www.Iailab.cn/1.png")
+    private String picUrl;
+
+    @Schema(description = "绘画错误信息", example = "图片错误信息")
+    private String errorMessage;
+
+    @Schema(description = "绘制参数")
+    private Map<String, String> options;
+
+    @Schema(description = "mj buttons 按钮")
+    private List<MidjourneyApi.Button> buttons;
+
+    @Schema(description = "完成时间")
+    private LocalDateTime finishTime;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageUpdateReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageUpdateReqVO.java
new file mode 100644
index 0000000..1661352
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/AiImageUpdateReqVO.java
@@ -0,0 +1,18 @@
+package com.iailab.module.ai.controller.admin.image.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 绘画修改 Request VO")
+@Data
+public class AiImageUpdateReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+    @Schema(description = "是否发布", example = "true")
+    private Boolean publicStatus;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyActionReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyActionReqVO.java
new file mode 100644
index 0000000..6c67794
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyActionReqVO.java
@@ -0,0 +1,20 @@
+package com.iailab.module.ai.controller.admin.image.vo.midjourney;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 绘图操作(Midjourney) Request VO")
+@Data
+public class AiMidjourneyActionReqVO {
+
+    @Schema(description = "图片编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "图片编号不能为空")
+    private Long id;
+
+    @Schema(description = "操作按钮编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "MJ::JOB::variation::4::06aa3e66-0e97-49cc-8201-e0295d883de4")
+    @NotEmpty(message = "操作按钮编号不能为空")
+    private String customId;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java
new file mode 100644
index 0000000..e77803b
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java
@@ -0,0 +1,35 @@
+package com.iailab.module.ai.controller.admin.image.vo.midjourney;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 绘画生成(Midjourney) Request VO")
+@Data
+public class AiMidjourneyImagineReqVO {
+
+    @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "中国神龙")
+    @NotEmpty(message = "提示词不能为空!")
+    private String prompt;
+
+    @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "模型编号不能为空")
+    private Long modelId;
+
+    @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "图片宽度不能为空")
+    private Integer width;
+
+    @Schema(description = "图片高度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "图片高度不能为空")
+    private Integer height;
+
+    @Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6.0")
+    @NotEmpty(message = "版本号不能为空")
+    private String version;
+
+    @Schema(description = "参考图", example = "https://www.Iailab.cn/x.png")
+    private String referImageUrl;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeController.http b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeController.http
new file mode 100644
index 0000000..a0f1278
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeController.http
@@ -0,0 +1,35 @@
+### 创建知识库
+POST {{baseUrl}}/ai/knowledge/create
+Content-Type: application/json
+Authorization: {{token}}
+tenant-id: {{adminTenantId}}
+
+{
+  "name": "测试标题",
+  "description": "测试描述",
+  "embeddingModelId": 30,
+  "topK": 3,
+  "similarityThreshold": 0.5,
+  "status": 0
+}
+
+### 更新知识库
+PUT {{baseUrl}}/ai/knowledge/update
+Content-Type: application/json
+Authorization: {{token}}
+tenant-id: {{adminTenantId}}
+
+{
+  "id": 1,
+  "name": "测试标题(更新)",
+  "description": "测试描述",
+  "embeddingModelId": 30,
+  "topK": 5,
+  "similarityThreshold": 0.6,
+  "status": 0
+}
+
+### 获取知识库分页
+GET {{baseUrl}}/ai/knowledge/page?pageNo=1&pageSize=10
+Authorization: {{token}}
+tenant-id: {{adminTenantId}}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeController.java
new file mode 100644
index 0000000..ee0aae8
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeController.java
@@ -0,0 +1,75 @@
+package com.iailab.module.ai.controller.admin.knowledge;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+
+@Tag(name = "管理后台 - AI 知识库")
+@RestController
+@RequestMapping("/ai/knowledge")
+@Validated
+public class AiKnowledgeController {
+
+    @Resource
+    private AiKnowledgeService knowledgeService;
+
+    @GetMapping("/page")
+    @Operation(summary = "获取知识库分页")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+    public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) {
+        PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得知识库")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+    public CommonResult<AiKnowledgeRespVO> getKnowledge(@RequestParam("id") Long id) {
+        AiKnowledgeDO knowledge = knowledgeService.getKnowledge(id);
+        return success(BeanUtils.toBean(knowledge, AiKnowledgeRespVO.class));
+    }
+
+    @PostMapping("/create")
+    @Operation(summary = "创建知识库")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
+    public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) {
+        return success(knowledgeService.createKnowledge(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新知识库")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
+    public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO updateReqVO) {
+        knowledgeService.updateKnowledge(updateReqVO);
+        return success(true);
+    }
+
+    @GetMapping("/simple-list")
+    @Operation(summary = "获得知识库的精简列表")
+    public CommonResult<List<AiKnowledgeRespVO>> getKnowledgeSimpleList() {
+        List<AiKnowledgeDO> list = knowledgeService.getKnowledgeSimpleListByStatus(CommonStatusEnum.ENABLE.getStatus());
+        return success(convertList(list, knowledge -> new AiKnowledgeRespVO()
+                .setId(knowledge.getId()).setName(knowledge.getName())));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http
new file mode 100644
index 0000000..aae39c9
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http
@@ -0,0 +1,35 @@
+### 创建知识文档
+POST {{baseUrl}}/ai/knowledge/document/create
+Content-Type: application/json
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenantId}}
+
+{
+  "knowledgeId": 2,
+  "name": "测试文档",
+  "url": "https://static.Iailab.cn/README.md",
+  "segmentMaxTokens": 800
+}
+
+### 批量创建知识文档
+POST {{baseUrl}}/ai/knowledge/document/create-list
+Content-Type: application/json
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenantId}}
+
+{
+  "knowledgeId": 1,
+  "list": [
+    {
+      "name": "测试文档1",
+      "url": "https://static.Iailab.cn/README.md",
+      "segmentMaxTokens": 800
+    },
+    {
+      "name": "测试文档2",
+      "url": "https://static.Iailab.cn/README_iailab.md",
+      "segmentMaxTokens": 400
+    }
+  ]
+}
+
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
new file mode 100644
index 0000000..9a9656e
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
@@ -0,0 +1,90 @@
+package com.iailab.module.ai.controller.admin.knowledge;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.*;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeDocumentService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - AI 知识库文档")
+@RestController
+@RequestMapping("/ai/knowledge/document")
+@Validated
+public class AiKnowledgeDocumentController {
+
+    @Resource
+    private AiKnowledgeDocumentService documentService;
+
+    @GetMapping("/page")
+    @Operation(summary = "获取文档分页")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+    public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(
+            @Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
+        PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获取文档详情")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+    public CommonResult<AiKnowledgeDocumentRespVO> getKnowledgeDocument(@RequestParam("id") Long id) {
+        AiKnowledgeDocumentDO document = documentService.getKnowledgeDocument(id);
+        return success(BeanUtils.toBean(document, AiKnowledgeDocumentRespVO.class));
+    }
+
+    @PostMapping("/create")
+    @Operation(summary = "新建文档(单个)")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
+    public CommonResult<Long> createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) {
+        Long id = documentService.createKnowledgeDocument(reqVO);
+        return success(id);
+    }
+
+    @PostMapping("/create-list")
+    @Operation(summary = "新建文档(多个)")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
+    public CommonResult<List<Long>> createKnowledgeDocumentList(
+            @RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) {
+        List<Long> ids = documentService.createKnowledgeDocumentList(reqVO);
+        return success(ids);
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新文档")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
+    public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {
+        documentService.updateKnowledgeDocument(reqVO);
+        return success(true);
+    }
+
+    @PutMapping("/update-status")
+    @Operation(summary = "更新文档状态")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
+    public CommonResult<Boolean> updateKnowledgeDocumentStatus(
+            @Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
+        documentService.updateKnowledgeDocumentStatus(reqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除文档")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
+    public CommonResult<Boolean> deleteKnowledgeDocument(@RequestParam("id") Long id) {
+        documentService.deleteKnowledgeDocument(id);
+        return success(true);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http
new file mode 100644
index 0000000..7407362
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http
@@ -0,0 +1,17 @@
+### 切片内容
+GET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.Iailab.cn/README_iailab.md&segmentMaxTokens=800
+Content-Type: application/json
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenantId}}
+
+### 搜索段落内容
+GET {{baseUrl}}/ai/knowledge/segment/search?knowledgeId=2&content=如何使用这个产品&topK=5&similarityThreshold=0.1
+Content-Type: application/json
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenantId}}
+
+### 获取文档处理列表
+GET {{baseUrl}}/ai/knowledge/segment/get-process-list?documentIds=1,2,3
+Content-Type: application/json
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenantId}}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
new file mode 100644
index 0000000..1605694
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
@@ -0,0 +1,131 @@
+package com.iailab.module.ai.controller.admin.knowledge;
+
+import cn.hutool.core.collection.CollUtil;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.collection.MapUtils;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.*;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeDocumentService;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeSegmentService;
+import com.iailab.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
+import com.iailab.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.hibernate.validator.constraints.URL;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertSet;
+
+@Tag(name = "管理后台 - AI 知识库段落")
+@RestController
+@RequestMapping("/ai/knowledge/segment")
+@Validated
+public class AiKnowledgeSegmentController {
+
+    @Resource
+    private AiKnowledgeSegmentService segmentService;
+    @Resource
+    private AiKnowledgeDocumentService documentService;
+
+    @GetMapping("/get")
+    @Operation(summary = "获取段落详情")
+    @Parameter(name = "id", description = "段落编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+    public CommonResult<AiKnowledgeSegmentRespVO> getKnowledgeSegment(@RequestParam("id") Long id) {
+        AiKnowledgeSegmentDO segment = segmentService.getKnowledgeSegment(id);
+        return success(BeanUtils.toBean(segment, AiKnowledgeSegmentRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获取段落分页")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+    public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(
+            @Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
+        PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
+    }
+
+    @PostMapping("/create")
+    @Operation(summary = "创建段落")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
+    public CommonResult<Long> createKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO createReqVO) {
+        return success(segmentService.createKnowledgeSegment(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新段落内容")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
+    public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO reqVO) {
+        segmentService.updateKnowledgeSegment(reqVO);
+        return success(true);
+    }
+
+    @PutMapping("/update-status")
+    @Operation(summary = "启禁用段落内容")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
+    public CommonResult<Boolean> updateKnowledgeSegmentStatus(
+            @Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
+        segmentService.updateKnowledgeSegmentStatus(reqVO);
+        return success(true);
+    }
+
+    @GetMapping("/split")
+    @Operation(summary = "切片内容")
+    @Parameters({
+            @Parameter(name = "url", description = "文档 URL", required = true),
+            @Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true)
+    })
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+    public CommonResult<List<AiKnowledgeSegmentRespVO>> splitContent(
+            @RequestParam("url") @URL String url,
+            @RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) {
+        List<AiKnowledgeSegmentDO> segments = segmentService.splitContent(url, segmentMaxTokens);
+        return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class));
+    }
+
+    @GetMapping("/get-process-list")
+    @Operation(summary = "获取文档处理列表")
+    @Parameter(name = "documentIds", description = "文档编号列表", required = true, example = "1,2,3")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+    public CommonResult<List<AiKnowledgeSegmentProcessRespVO>> getKnowledgeSegmentProcessList(
+            @RequestParam("documentIds") List<Long> documentIds) {
+        List<AiKnowledgeSegmentProcessRespVO> list = segmentService.getKnowledgeSegmentProcessList(documentIds);
+        return success(list);
+    }
+
+    @GetMapping("/search")
+    @Operation(summary = "搜索段落内容")
+    @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+    public CommonResult<List<AiKnowledgeSegmentSearchRespVO>> searchKnowledgeSegment(
+            @Valid AiKnowledgeSegmentSearchReqVO reqVO) {
+        // 1. 搜索段落
+        List<AiKnowledgeSegmentSearchRespBO> segments = segmentService
+                .searchKnowledgeSegment(BeanUtils.toBean(reqVO, AiKnowledgeSegmentSearchReqBO.class));
+        if (CollUtil.isEmpty(segments)) {
+            return success(Collections.emptyList());
+        }
+
+        // 2. 拼接 VO
+        Map<Long, AiKnowledgeDocumentDO> documentMap = documentService.getKnowledgeDocumentMap(convertSet(
+                segments, AiKnowledgeSegmentSearchRespBO::getDocumentId));
+        return success(BeanUtils.toBean(segments, AiKnowledgeSegmentSearchRespVO.class,
+                segment -> MapUtils.findAndThen(documentMap, segment.getDocumentId(),
+                        document -> segment.setDocumentName(document.getName()))));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java
new file mode 100644
index 0000000..6c6be03
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java
@@ -0,0 +1,42 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.document;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.hibernate.validator.constraints.URL;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - AI 知识库文档批量创建 Request VO")
+@Data
+public class AiKnowledgeDocumentCreateListReqVO {
+
+    @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
+    @NotNull(message = "知识库编号不能为空")
+    private Long knowledgeId;
+
+    @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
+    @NotNull(message = "分段的最大 Token 数不能为空")
+    private Integer segmentMaxTokens;
+
+    @Schema(description = "文档列表", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "文档列表不能为空")
+    private List<Document> list;
+
+    @Schema(description = "文档")
+    @Data
+    public static class Document {
+
+        @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "三方登陆")
+        @NotBlank(message = "文档名称不能为空")
+        private String name;
+
+        @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.Iailab.cn")
+        @URL(message = "文档 URL 格式不正确")
+        private String url;
+
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java
new file mode 100644
index 0000000..272e5ed
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java
@@ -0,0 +1,17 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.document;
+
+import com.iailab.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 知识库文档的分页 Request VO")
+@Data
+public class AiKnowledgeDocumentPageReqVO extends PageParam {
+
+    @Schema(description = "知识库编号", example = "1")
+    private Long knowledgeId;
+
+    @Schema(description = "文档名称", example = "Java 开发手册")
+    private String name;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
new file mode 100644
index 0000000..71b7d23
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
@@ -0,0 +1,45 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.document;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - AI 知识库文档 Response VO")
+@Data
+public class AiKnowledgeDocumentRespVO {
+
+    @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+    private Long id;
+
+    @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+    private Long knowledgeId;
+
+    @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
+    private String name;
+
+    @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.Iailab.cn")
+    private String url;
+
+    @Schema(description = "文档内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....")
+    private String content;
+
+    @Schema(description = "文档内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+    private Integer contentLength;
+
+    @Schema(description = "文档 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Integer tokens;
+
+    @Schema(description = "分片最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
+    private Integer segmentMaxTokens;
+
+    @Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Integer retrievalCount;
+
+    @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+    private Integer status;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java
new file mode 100644
index 0000000..34d38ef
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java
@@ -0,0 +1,21 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.document;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 知识库文档更新 Request VO")
+@Data
+public class AiKnowledgeDocumentUpdateReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+    @Schema(description = "名称", example = "Java 开发手册")
+    private String name;
+
+    @Schema(description = "分片最大 Token 数", example = "1000")
+    private Integer segmentMaxTokens;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java
new file mode 100644
index 0000000..ec47013
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java
@@ -0,0 +1,22 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.document;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 知识库文档更新状态 Request VO")
+@Data
+public class AiKnowledgeDocumentUpdateStatusReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+    @NotNull(message = "状态不能为空")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
new file mode 100644
index 0000000..6feaf68
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
@@ -0,0 +1,30 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.knowledge;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.hibernate.validator.constraints.URL;
+
+
+@Schema(description = "管理后台 - AI 知识库文档的创建 Request VO")
+@Data
+public class AiKnowledgeDocumentCreateReqVO {
+
+    @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
+    @NotNull(message = "知识库编号不能为空")
+    private Long knowledgeId;
+
+    @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "三方登陆")
+    @NotBlank(message = "文档名称不能为空")
+    private String name;
+
+    @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.Iailab.cn")
+    @URL(message = "文档 URL 格式不正确")
+    private String url;
+
+    @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
+    @NotNull(message = "分段的最大 Token 数不能为空")
+    private Integer segmentMaxTokens;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
new file mode 100644
index 0000000..d368d5a
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
@@ -0,0 +1,29 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.knowledge;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageParam;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI 知识库的分页 Request VO")
+@Data
+public class AiKnowledgePageReqVO extends PageParam {
+
+    @Schema(description = "知识库名称", example = "Iailab")
+    private String name;
+
+    @Schema(description = "是否启用", example = "1")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java
new file mode 100644
index 0000000..5ced30b
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java
@@ -0,0 +1,39 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.knowledge;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - AI 知识库 Response VO")
+@Data
+public class AiKnowledgeRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+    private Long id;
+
+    @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
+    private String name;
+
+    @Schema(description = "知识库描述", example = "帮助你快速构建系统")
+    private String description;
+
+    @Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
+    private Long embeddingModelId;
+
+    @Schema(description = "向量模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat")
+    private String embeddingModel;
+
+    @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
+    private Integer topK;
+
+    @Schema(description = "相似度阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.7")
+    private Double similarityThreshold;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer status;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java
new file mode 100644
index 0000000..29503f7
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java
@@ -0,0 +1,41 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.knowledge;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 知识库新增/修改 Request VO")
+@Data
+public class AiKnowledgeSaveReqVO {
+
+    @Schema(description = "对话编号", example = "1204")
+    private Long id;
+
+    @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
+    @NotBlank(message = "知识库名称不能为空")
+    private String name;
+
+    @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档")
+    private String description;
+
+    @Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "向量模型不能为空")
+    private Long embeddingModelId;
+
+    @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
+    @NotNull(message = "topK 不能为空")
+    private Integer topK;
+
+    @Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5")
+    @NotNull(message = "相似性阈值不能为空")
+    private Double similarityThreshold;
+
+    @Schema(description = "是否启用",  requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "是否启用不能为空")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java
new file mode 100644
index 0000000..d49e4ab
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java
@@ -0,0 +1,23 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.segment;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageParam;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 知识库分段的分页 Request VO")
+@Data
+public class AiKnowledgeSegmentPageReqVO extends PageParam {
+
+    @Schema(description = "文档编号", example = "1")
+    private Integer documentId;
+
+    @Schema(description = "分段内容关键字", example = "Java 开发")
+    private String content;
+
+    @Schema(description = "分段状态", example = "1")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java
new file mode 100644
index 0000000..04aae29
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java
@@ -0,0 +1,19 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.segment;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 知识库段落向量进度 Response VO")
+@Data
+public class AiKnowledgeSegmentProcessRespVO {
+
+    @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long documentId;
+
+    @Schema(description = "总段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Long count;
+
+    @Schema(description = "已向量化段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+    private Long embeddingCount;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java
new file mode 100644
index 0000000..8c0c8ba
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java
@@ -0,0 +1,40 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.segment;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 知识库文档分片 Response VO")
+@Data
+public class AiKnowledgeSegmentRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+    private Long id;
+
+    @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+    private Long documentId;
+
+    @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+    private Long knowledgeId;
+
+    @Schema(description = "向量库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1858496a-1dde-4edf-a43e-0aed08f37f8c")
+    private String vectorId;
+
+    @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
+    private String content;
+
+    @Schema(description = "切片内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Integer contentLength;
+
+    @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Integer tokens;
+
+    @Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Integer retrievalCount;
+
+    @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer status;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Long createTime;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java
new file mode 100644
index 0000000..48eadcc
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java
@@ -0,0 +1,21 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.segment;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 新增/修改知识库段落 request VO")
+@Data
+public class AiKnowledgeSegmentSaveReqVO {
+
+    @Schema(description = "编号", example = "24790")
+    private Long id;
+
+    @Schema(description = "知识库文档编号", example = "1024")
+    private Long documentId;
+
+    @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
+    @NotEmpty(message = "切片内容不能为空")
+    private String content;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
new file mode 100644
index 0000000..177b4b7
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
@@ -0,0 +1,27 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.segment;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - AI 知识库段落搜索 Request VO")
+@Data
+public class AiKnowledgeSegmentSearchReqVO {
+
+    @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "知识库编号不能为空")
+    private Long knowledgeId;
+
+    @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "如何使用这个产品")
+    @NotEmpty(message = "内容不能为空")
+    private String content;
+
+    @Schema(description = "最大返回数量", example = "5")
+    private Integer topK;
+
+    @Schema(description = "相似度阈值", example = "0.7")
+    private Double similarityThreshold;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java
new file mode 100644
index 0000000..679f035
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java
@@ -0,0 +1,16 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.segment;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 知识库段落搜索 Response VO")
+@Data
+public class AiKnowledgeSegmentSearchRespVO extends AiKnowledgeSegmentRespVO {
+
+    @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册")
+    private String documentName;
+
+    @Schema(description = "相似度分数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.95")
+    private Double score;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java
new file mode 100644
index 0000000..1d3fef5
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java
@@ -0,0 +1,22 @@
+package com.iailab.module.ai.controller.admin.knowledge.vo.segment;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+
+@Schema(description = "管理后台 - AI 知识库段落的更新状态 Request VO")
+@Data
+public class AiKnowledgeSegmentUpdateStatusReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+    private Long id;
+
+    @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "是否启用不能为空")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/AiMindMapController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/AiMindMapController.java
new file mode 100644
index 0000000..154d660
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/AiMindMapController.java
@@ -0,0 +1,57 @@
+package com.iailab.module.ai.controller.admin.mindmap;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
+import com.iailab.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
+import com.iailab.module.ai.controller.admin.mindmap.vo.AiMindMapRespVO;
+import com.iailab.module.ai.dal.dataobject.mindmap.AiMindMapDO;
+import com.iailab.module.ai.service.mindmap.AiMindMapService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.http.MediaType;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Flux;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - AI 思维导图")
+@RestController
+@RequestMapping("/ai/mind-map")
+public class AiMindMapController {
+
+    @Resource
+    private AiMindMapService mindMapService;
+
+    @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    @Operation(summary = "导图生成(流式)", description = "流式返回,响应较快")
+    public Flux<CommonResult<String>> generateMindMap(@RequestBody @Valid AiMindMapGenerateReqVO generateReqVO) {
+        return mindMapService.generateMindMap(generateReqVO, getLoginUserId());
+    }
+
+    // ================ 导图管理 ================
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除思维导图")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:mind-map:delete')")
+    public CommonResult<Boolean> deleteMindMap(@RequestParam("id") Long id) {
+        mindMapService.deleteMindMap(id);
+        return success(true);
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得思维导图分页")
+    @PreAuthorize("@ss.hasPermission('ai:mind-map:query')")
+    public CommonResult<PageResult<AiMindMapRespVO>> getMindMapPage(@Valid AiMindMapPageReqVO pageReqVO) {
+        PageResult<AiMindMapDO> pageResult = mindMapService.getMindMapPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiMindMapRespVO.class));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java
new file mode 100644
index 0000000..43901f3
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java
@@ -0,0 +1,15 @@
+package com.iailab.module.ai.controller.admin.mindmap.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 思维导图生成 Request VO")
+@Data
+public class AiMindMapGenerateReqVO {
+
+    @Schema(description = "思维导图内容提示", example = "Java 学习路线")
+    @NotBlank(message = "思维导图内容提示不能为空")
+    private String prompt;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java
new file mode 100644
index 0000000..93916ff
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java
@@ -0,0 +1,28 @@
+package com.iailab.module.ai.controller.admin.mindmap.vo;
+
+import com.iailab.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI 思维导图分页 Request VO")
+@Data
+public class AiMindMapPageReqVO extends PageParam {
+
+    @Schema(description = "用户编号", example = "4325")
+    private Long userId;
+
+    @Schema(description = "生成内容提示", example = "Java 学习路线")
+    private String prompt;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java
new file mode 100644
index 0000000..7e5b914
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java
@@ -0,0 +1,36 @@
+package com.iailab.module.ai.controller.admin.mindmap.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - AI 思维导图 Response VO")
+@Data
+public class AiMindMapRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3373")
+    private Long id;
+
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4325")
+    private Long userId;
+
+    @Schema(description = "生成内容提示", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 学习路线")
+    private String prompt;
+
+    @Schema(description = "生成的思维导图内容")
+    private String generatedContent;
+
+    @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+    private String platform;
+
+    @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo-0125")
+    private String model;
+
+    @Schema(description = "错误信息")
+    private String errorMessage;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiApiKeyController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiApiKeyController.java
new file mode 100644
index 0000000..9d0c355
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiApiKeyController.java
@@ -0,0 +1,83 @@
+package com.iailab.module.ai.controller.admin.model;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;
+import com.iailab.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.model.AiModelRespVO;
+import com.iailab.module.ai.dal.dataobject.model.AiApiKeyDO;
+import com.iailab.module.ai.service.model.AiApiKeyService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+
+@Tag(name = "管理后台 - AI API 密钥")
+@RestController
+@RequestMapping("/ai/api-key")
+@Validated
+public class AiApiKeyController {
+
+    @Resource
+    private AiApiKeyService apiKeyService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建 API 密钥")
+    @PreAuthorize("@ss.hasPermission('ai:api-key:create')")
+    public CommonResult<Long> createApiKey(@Valid @RequestBody AiApiKeySaveReqVO createReqVO) {
+        return success(apiKeyService.createApiKey(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新 API 密钥")
+    @PreAuthorize("@ss.hasPermission('ai:api-key:update')")
+    public CommonResult<Boolean> updateApiKey(@Valid @RequestBody AiApiKeySaveReqVO updateReqVO) {
+        apiKeyService.updateApiKey(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除 API 密钥")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:api-key:delete')")
+    public CommonResult<Boolean> deleteApiKey(@RequestParam("id") Long id) {
+        apiKeyService.deleteApiKey(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得 API 密钥")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:api-key:query')")
+    public CommonResult<AiApiKeyRespVO> getApiKey(@RequestParam("id") Long id) {
+        AiApiKeyDO apiKey = apiKeyService.getApiKey(id);
+        return success(BeanUtils.toBean(apiKey, AiApiKeyRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得 API 密钥分页")
+    @PreAuthorize("@ss.hasPermission('ai:api-key:query')")
+    public CommonResult<PageResult<AiApiKeyRespVO>> getApiKeyPage(@Valid AiApiKeyPageReqVO pageReqVO) {
+        PageResult<AiApiKeyDO> pageResult = apiKeyService.getApiKeyPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiApiKeyRespVO.class));
+    }
+
+    @GetMapping("/simple-list")
+    @Operation(summary = "获得 API 密钥分页列表")
+    public CommonResult<List<AiModelRespVO>> getApiKeySimpleList() {
+        List<AiApiKeyDO> list = apiKeyService.getApiKeyList();
+        return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName())));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiChatRoleController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiChatRoleController.java
new file mode 100644
index 0000000..2c110cd
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiChatRoleController.java
@@ -0,0 +1,124 @@
+package com.iailab.module.ai.controller.admin.model;
+
+import cn.hutool.core.util.ObjUtil;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRoleRespVO;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.iailab.module.ai.service.model.AiChatRoleService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - AI 聊天角色")
+@RestController
+@RequestMapping("/ai/chat-role")
+@Validated
+public class AiChatRoleController {
+
+    @Resource
+    private AiChatRoleService chatRoleService;
+
+    @GetMapping("/my-page")
+    @Operation(summary = "获得【我的】聊天角色分页")
+    public CommonResult<PageResult<AiChatRoleRespVO>> getChatRoleMyPage(@Valid AiChatRolePageReqVO pageReqVO) {
+        PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRoleMyPage(pageReqVO, getLoginUserId());
+        return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class));
+    }
+
+    @GetMapping("/get-my")
+    @Operation(summary = "获得【我的】聊天角色")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    public CommonResult<AiChatRoleRespVO> getChatRoleMy(@RequestParam("id") Long id) {
+        AiChatRoleDO chatRole = chatRoleService.getChatRole(id);
+        if (ObjUtil.notEqual(chatRole.getUserId(), getLoginUserId())) {
+            return success(null);
+        }
+        return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class));
+    }
+
+    @PostMapping("/create-my")
+    @Operation(summary = "创建【我的】聊天角色")
+    public CommonResult<Long> createChatRoleMy(@Valid @RequestBody AiChatRoleSaveMyReqVO createReqVO) {
+        return success(chatRoleService.createChatRoleMy(createReqVO, getLoginUserId()));
+    }
+
+    @PutMapping("/update-my")
+    @Operation(summary = "更新【我的】聊天角色")
+    public CommonResult<Boolean> updateChatRoleMy(@Valid @RequestBody AiChatRoleSaveMyReqVO updateReqVO) {
+        chatRoleService.updateChatRoleMy(updateReqVO, getLoginUserId());
+        return success(true);
+    }
+
+    @DeleteMapping("/delete-my")
+    @Operation(summary = "删除【我的】聊天角色")
+    @Parameter(name = "id", description = "编号", required = true)
+    public CommonResult<Boolean> deleteChatRoleMy(@RequestParam("id") Long id) {
+        chatRoleService.deleteChatRoleMy(id, getLoginUserId());
+        return success(true);
+    }
+
+    @GetMapping("/category-list")
+    @Operation(summary = "获得聊天角色的分类列表")
+    public CommonResult<List<String>> getChatRoleCategoryList() {
+        return success(chatRoleService.getChatRoleCategoryList());
+    }
+
+    // ========== 角色管理 ==========
+
+    @PostMapping("/create")
+    @Operation(summary = "创建聊天角色")
+    @PreAuthorize("@ss.hasPermission('ai:chat-role:create')")
+    public CommonResult<Long> createChatRole(@Valid @RequestBody AiChatRoleSaveReqVO createReqVO) {
+        return success(chatRoleService.createChatRole(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新聊天角色")
+    @PreAuthorize("@ss.hasPermission('ai:chat-role:update')")
+    public CommonResult<Boolean> updateChatRole(@Valid @RequestBody AiChatRoleSaveReqVO updateReqVO) {
+        chatRoleService.updateChatRole(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除聊天角色")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:chat-role:delete')")
+    public CommonResult<Boolean> deleteChatRole(@RequestParam("id") Long id) {
+        chatRoleService.deleteChatRole(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得聊天角色")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:chat-role:query')")
+    public CommonResult<AiChatRoleRespVO> getChatRole(@RequestParam("id") Long id) {
+        AiChatRoleDO chatRole = chatRoleService.getChatRole(id);
+        return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得聊天角色分页")
+    @PreAuthorize("@ss.hasPermission('ai:chat-role:query')")
+    public CommonResult<PageResult<AiChatRoleRespVO>> getChatRolePage(@Valid AiChatRolePageReqVO pageReqVO) {
+        PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRolePage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiModelController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiModelController.java
new file mode 100644
index 0000000..6e40809
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiModelController.java
@@ -0,0 +1,89 @@
+package com.iailab.module.ai.controller.admin.model;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.model.AiModelRespVO;
+import com.iailab.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.service.model.AiModelService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+
+@Tag(name = "管理后台 - AI 模型")
+@RestController
+@RequestMapping("/ai/model")
+@Validated
+public class AiModelController {
+
+    @Resource
+    private AiModelService modelService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建模型")
+    @PreAuthorize("@ss.hasPermission('ai:model:create')")
+    public CommonResult<Long> createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) {
+        return success(modelService.createModel(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新模型")
+    @PreAuthorize("@ss.hasPermission('ai:model:update')")
+    public CommonResult<Boolean> updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) {
+        modelService.updateModel(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除模型")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:model:delete')")
+    public CommonResult<Boolean> deleteModel(@RequestParam("id") Long id) {
+        modelService.deleteModel(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得模型")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:model:query')")
+    public CommonResult<AiModelRespVO> getModel(@RequestParam("id") Long id) {
+        AiModelDO model = modelService.getModel(id);
+        return success(BeanUtils.toBean(model, AiModelRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得模型分页")
+    @PreAuthorize("@ss.hasPermission('ai:model:query')")
+    public CommonResult<PageResult<AiModelRespVO>> getModelPage(@Valid AiModelPageReqVO pageReqVO) {
+        PageResult<AiModelDO> pageResult = modelService.getModelPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiModelRespVO.class));
+    }
+
+    @GetMapping("/simple-list")
+    @Operation(summary = "获得模型列表")
+    @Parameter(name = "type", description = "类型", required = true, example = "1")
+    @Parameter(name = "platform", description = "平台", example = "midjourney")
+    public CommonResult<List<AiModelRespVO>> getModelSimpleList(
+            @RequestParam("type") Integer type,
+            @RequestParam(value = "platform", required = false) String platform) {
+        List<AiModelDO> list = modelService.getModelListByStatusAndType(
+                CommonStatusEnum.ENABLE.getStatus(), type, platform);
+        return success(convertList(list, model -> new AiModelRespVO().setId(model.getId())
+                .setName(model.getName()).setModel(model.getModel()).setPlatform(model.getPlatform())));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiToolController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiToolController.java
new file mode 100644
index 0000000..e8d386f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/AiToolController.java
@@ -0,0 +1,84 @@
+package com.iailab.module.ai.controller.admin.model;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.tool.AiToolRespVO;
+import com.iailab.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiToolDO;
+import com.iailab.module.ai.service.model.AiToolService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+
+@Tag(name = "管理后台 - AI 工具")
+@RestController
+@RequestMapping("/ai/tool")
+@Validated
+public class AiToolController {
+
+    @Resource
+    private AiToolService toolService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建工具")
+    @PreAuthorize("@ss.hasPermission('ai:tool:create')")
+    public CommonResult<Long> createTool(@Valid @RequestBody AiToolSaveReqVO createReqVO) {
+        return success(toolService.createTool(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新工具")
+    @PreAuthorize("@ss.hasPermission('ai:tool:update')")
+    public CommonResult<Boolean> updateTool(@Valid @RequestBody AiToolSaveReqVO updateReqVO) {
+        toolService.updateTool(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除工具")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:tool:delete')")
+    public CommonResult<Boolean> deleteTool(@RequestParam("id") Long id) {
+        toolService.deleteTool(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得工具")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:tool:query')")
+    public CommonResult<AiToolRespVO> getTool(@RequestParam("id") Long id) {
+        AiToolDO tool = toolService.getTool(id);
+        return success(BeanUtils.toBean(tool, AiToolRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得工具分页")
+    @PreAuthorize("@ss.hasPermission('ai:tool:query')")
+    public CommonResult<PageResult<AiToolRespVO>> getToolPage(@Valid AiToolPageReqVO pageReqVO) {
+        PageResult<AiToolDO> pageResult = toolService.getToolPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiToolRespVO.class));
+    }
+
+    @GetMapping("/simple-list")
+    @Operation(summary = "获得工具列表")
+    public CommonResult<List<AiToolRespVO>> getToolSimpleList() {
+        List<AiToolDO> list = toolService.getToolListByStatus(CommonStatusEnum.ENABLE.getStatus());
+        return success(convertList(list, tool -> new AiToolRespVO()
+                .setId(tool.getId()).setName(tool.getName())));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java
new file mode 100644
index 0000000..3ff9e05
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java
@@ -0,0 +1,25 @@
+package com.iailab.module.ai.controller.admin.model.vo.apikey;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import com.iailab.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI API 密钥分页 Request VO")
+@Data
+public class AiApiKeyPageReqVO extends PageParam {
+
+    @Schema(description = "名称", example = "文心一言")
+    private String name;
+
+    @Schema(description = "平台", example = "OpenAI")
+    private String platform;
+
+    @Schema(description = "状态", example = "1")
+    private Integer status;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java
new file mode 100644
index 0000000..434f9ea
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java
@@ -0,0 +1,28 @@
+package com.iailab.module.ai.controller.admin.model.vo.apikey;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+@Schema(description = "管理后台 - AI API 密钥 Response VO")
+@Data
+public class AiApiKeyRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
+    private Long id;
+
+    @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言")
+    private String name;
+
+    @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
+    private String apiKey;
+
+    @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+    private String platform;
+
+    @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com")
+    private String url;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer status;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java
new file mode 100644
index 0000000..172b41b
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java
@@ -0,0 +1,34 @@
+package com.iailab.module.ai.controller.admin.model.vo.apikey;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import jakarta.validation.constraints.*;
+
+@Schema(description = "管理后台 - AI API 密钥新增/修改 Request VO")
+@Data
+public class AiApiKeySaveReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
+    private Long id;
+
+    @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言")
+    @NotEmpty(message = "名称不能为空")
+    private String name;
+
+    @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
+    @NotEmpty(message = "密钥不能为空")
+    private String apiKey;
+
+    @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+    @NotEmpty(message = "平台不能为空")
+    private String platform;
+
+    @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com")
+    private String url;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRolePageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRolePageReqVO.java
new file mode 100644
index 0000000..d40303d
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRolePageReqVO.java
@@ -0,0 +1,20 @@
+package com.iailab.module.ai.controller.admin.model.vo.chatRole;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import com.iailab.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - AI 聊天角色分页 Request VO")
+@Data
+public class AiChatRolePageReqVO extends PageParam {
+
+    @Schema(description = "角色名称", example = "李四")
+    private String name;
+
+    @Schema(description = "角色类别", example = "创作")
+    private String category;
+
+    @Schema(description = "是否公开", example = "1")
+    private Boolean publicStatus;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
new file mode 100644
index 0000000..f3d21f7
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
@@ -0,0 +1,64 @@
+package com.iailab.module.ai.controller.admin.model.vo.chatRole;
+
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.fhs.core.trans.anno.Trans;
+import com.fhs.core.trans.constant.TransType;
+import com.fhs.core.trans.vo.VO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - AI 聊天角色 Response VO")
+@Data
+public class AiChatRoleRespVO implements VO {
+
+    @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32746")
+    private Long id;
+
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9442")
+    private Long userId;
+
+    @Schema(description = "模型编号", example = "17640")
+    @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = { "name", "model" }, refs = { "modelName", "model" })
+    private Long modelId;
+    @Schema(description = "模型名字", example = "张三")
+    private String modelName;
+    @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125")
+    private String model;
+
+    @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
+    private String name;
+
+    @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.Iailab.cn/1.png")
+    private String avatar;
+
+    @Schema(description = "角色类别", requiredMode = Schema.RequiredMode.REQUIRED, example = "创作")
+    private String category;
+
+    @Schema(description = "角色排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer sort;
+
+    @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
+    private String description;
+
+    @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String systemMessage;
+
+    @Schema(description = "引用的知识库编号列表", example = "1,2,3")
+    private List<Long> knowledgeIds;
+
+    @Schema(description = "引用的工具编号列表", example = "1,2,3")
+    private List<Long> toolIds;
+
+    @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Boolean publicStatus;
+
+    @Schema(description = "状态", example = "1")
+    private Integer status;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java
new file mode 100644
index 0000000..afb26a8
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java
@@ -0,0 +1,40 @@
+package com.iailab.module.ai.controller.admin.model.vo.chatRole;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+import org.hibernate.validator.constraints.URL;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - AI 聊天角色新增/修改【我的】 Request VO")
+@Data
+public class AiChatRoleSaveMyReqVO {
+
+    @Schema(description = "角色编号", example = "32746")
+    private Long id;
+
+    @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
+    @NotEmpty(message = "角色名称不能为空")
+    private String name;
+
+    @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.Iailab.cn/1.png")
+    @NotEmpty(message = "角色头像不能为空")
+    @URL(message = "角色头像必须是 URL 格式")
+    private String avatar;
+
+    @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
+    @NotEmpty(message = "角色描述不能为空")
+    private String description;
+
+    @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
+    @NotEmpty(message = "角色设定不能为空")
+    private String systemMessage;
+
+    @Schema(description = "引用的知识库编号列表", example = "1,2,3")
+    private List<Long> knowledgeIds;
+
+    @Schema(description = "引用的工具编号列表", example = "1,2,3")
+    private List<Long> toolIds;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java
new file mode 100644
index 0000000..f5db047
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java
@@ -0,0 +1,63 @@
+package com.iailab.module.ai.controller.admin.model.vo.chatRole;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import jakarta.validation.constraints.*;
+import org.hibernate.validator.constraints.URL;
+
+
+import java.util.List;
+
+@Schema(description = "管理后台 - AI 聊天角色新增/修改 Request VO")
+@Data
+public class AiChatRoleSaveReqVO {
+
+    @Schema(description = "角色编号", example = "32746")
+    private Long id;
+
+    @Schema(description = "模型编号", example = "17640")
+    private Long modelId;
+
+    @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
+    @NotEmpty(message = "角色名称不能为空")
+    private String name;
+
+    @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.Iailab.cn/1.png")
+    @NotEmpty(message = "角色头像不能为空")
+    @URL(message = "角色头像必须是 URL 格式")
+    private String avatar;
+
+    @Schema(description = "角色类别", requiredMode = Schema.RequiredMode.REQUIRED, example = "创作")
+    @NotEmpty(message = "角色类别不能为空")
+    private String category;
+
+    @Schema(description = "角色排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "角色排序不能为空")
+    private Integer sort;
+
+    @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
+    @NotEmpty(message = "角色描述不能为空")
+    private String description;
+
+    @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
+    @NotEmpty(message = "角色设定不能为空")
+    private String systemMessage;
+
+    @Schema(description = "引用的知识库编号列表", example = "1,2,3")
+    private List<Long> knowledgeIds;
+
+    @Schema(description = "引用的工具编号列表", example = "1,2,3")
+    private List<Long> toolIds;
+
+    @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "是否公开不能为空")
+    private Boolean publicStatus;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "状态不能为空")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java
new file mode 100644
index 0000000..5e210d5
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java
@@ -0,0 +1,20 @@
+package com.iailab.module.ai.controller.admin.model.vo.model;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import com.iailab.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - API 模型分页 Request VO")
+@Data
+public class AiModelPageReqVO extends PageParam {
+
+    @Schema(description = "模型名字", example = "张三")
+    private String name;
+
+    @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125")
+    private String model;
+
+    @Schema(description = "模型平台", example = "OpenAI")
+    private String platform;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelRespVO.java
new file mode 100644
index 0000000..7bb0047
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelRespVO.java
@@ -0,0 +1,48 @@
+package com.iailab.module.ai.controller.admin.model.vo.model;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - AI 模型 Response VO")
+@Data
+public class AiModelRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630")
+    private Long id;
+
+    @Schema(description = "API 秘钥编号", example = "22042")
+    private Long keyId;
+
+    @Schema(description = "模型名字", example = "张三")
+    private String name;
+
+    @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125")
+    private String model;
+
+    @Schema(description = "模型平台", example = "OpenAI")
+    private String platform;
+
+    @Schema(description = "模型类型", example = "1")
+    private Integer type;
+
+    @Schema(description = "排序", example = "1")
+    private Integer sort;
+
+    @Schema(description = "状态", example = "2")
+    private Integer status;
+
+    @Schema(description = "温度参数", example = "1")
+    private Double temperature;
+
+    @Schema(description = "单条回复的最大 Token 数量", example = "4096")
+    private Integer maxTokens;
+
+    @Schema(description = "上下文的最大 Message 数量", example = "8192")
+    private Integer maxContexts;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java
new file mode 100644
index 0000000..075fc20
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java
@@ -0,0 +1,59 @@
+package com.iailab.module.ai.controller.admin.model.vo.model;
+
+import com.iailab.framework.ai.core.enums.AiModelTypeEnum;
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - API 模型新增/修改 Request VO")
+@Data
+public class AiModelSaveReqVO {
+
+    @Schema(description = "编号", example = "2630")
+    private Long id;
+
+    @Schema(description = "API 秘钥编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22042")
+    @NotNull(message = "API 秘钥编号不能为空")
+    private Long keyId;
+
+    @Schema(description = "模型名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+    @NotEmpty(message = "模型名字不能为空")
+    private String name;
+
+    @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo-0125")
+    @NotEmpty(message = "模型标识不能为空")
+    private String model;
+
+    @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+    @NotEmpty(message = "模型平台不能为空")
+    @InEnum(AiPlatformEnum.class)
+    private String platform;
+
+    @Schema(description = "模型类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "模型类型不能为空")
+    @InEnum(AiModelTypeEnum.class)
+    private Integer type;
+
+    @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "排序不能为空")
+    private Integer sort;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @InEnum(CommonStatusEnum.class)
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+    @Schema(description = "温度参数", example = "1")
+    private Double temperature;
+
+    @Schema(description = "单条回复的最大 Token 数量", example = "4096")
+    private Integer maxTokens;
+
+    @Schema(description = "上下文的最大 Message 数量", example = "8192")
+    private Integer maxContexts;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java
new file mode 100644
index 0000000..3d67bfd
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java
@@ -0,0 +1,34 @@
+package com.iailab.module.ai.controller.admin.model.vo.tool;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageParam;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI 工具分页 Request VO")
+@Data
+public class AiToolPageReqVO extends PageParam {
+
+    @Schema(description = "工具名称", example = "王五")
+    private String name;
+
+    @Schema(description = "工具描述", example = "你猜")
+    private String description;
+
+    @Schema(description = "状态", example = "1")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java
new file mode 100644
index 0000000..520256d
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java
@@ -0,0 +1,27 @@
+package com.iailab.module.ai.controller.admin.model.vo.tool;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - AI 工具 Response VO")
+@Data
+public class AiToolRespVO {
+
+    @Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661")
+    private Long id;
+
+    @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+    private String name;
+
+    @Schema(description = "工具描述", example = "你猜")
+    private String description;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer status;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java
new file mode 100644
index 0000000..cd0fe3e
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java
@@ -0,0 +1,27 @@
+package com.iailab.module.ai.controller.admin.model.vo.tool;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 工具新增/修改 Request VO")
+@Data
+public class AiToolSaveReqVO {
+
+    @Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661")
+    private Long id;
+
+    @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+    @NotEmpty(message = "工具名称不能为空")
+    private String name;
+
+    @Schema(description = "工具描述", example = "你猜")
+    private String description;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/AiMusicController.http b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/AiMusicController.http
new file mode 100644
index 0000000..ae68c82
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/AiMusicController.http
@@ -0,0 +1,26 @@
+### 生成音乐:Suno + 歌词模式
+POST {{baseUrl}}/ai/music/generate
+Content-Type: application/json
+Authorization: {{token}}
+
+{
+  "platform": "Suno",
+  "generateMode": 2,
+  "prompt": "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。",
+  "model": "chirp-v3.5",
+  "tags": ["Happy"],
+  "title": "Happy Song"
+}
+
+### 生成音乐:Suno + 描述模式
+POST {{baseUrl}}/ai/music/generate
+Content-Type: application/json
+Authorization: {{token}}
+
+{
+  "platform": "Suno",
+  "generateMode": 1,
+  "model": "chirp-v3.5",
+  "prompt": "happy music",
+  "makeInstrumental": false
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/AiMusicController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/AiMusicController.java
new file mode 100644
index 0000000..6f5189f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/AiMusicController.java
@@ -0,0 +1,98 @@
+package com.iailab.module.ai.controller.admin.music;
+
+import cn.hutool.core.util.ObjUtil;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.music.vo.*;
+import com.iailab.module.ai.dal.dataobject.music.AiMusicDO;
+import com.iailab.module.ai.service.music.AiMusicService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - AI 音乐")
+@RestController
+@RequestMapping("/ai/music")
+public class AiMusicController {
+
+    @Resource
+    private AiMusicService musicService;
+
+    @GetMapping("/my-page")
+    @Operation(summary = "获得【我的】音乐分页")
+    public CommonResult<PageResult<AiMusicRespVO>> getMusicMyPage(@Valid AiMusicPageReqVO pageReqVO) {
+        PageResult<AiMusicDO> pageResult = musicService.getMusicMyPage(pageReqVO, getLoginUserId());
+        return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));
+    }
+
+    @PostMapping("/generate")
+    @Operation(summary = "音乐生成")
+    public CommonResult<List<Long>> generateMusic(@RequestBody @Valid AiSunoGenerateReqVO reqVO) {
+        return success(musicService.generateMusic(getLoginUserId(), reqVO));
+    }
+
+    @Operation(summary = "删除【我的】音乐记录")
+    @DeleteMapping("/delete-my")
+    @Parameter(name = "id", required = true, description = "音乐编号", example = "1024")
+    public CommonResult<Boolean> deleteMusicMy(@RequestParam("id") Long id) {
+        musicService.deleteMusicMy(id, getLoginUserId());
+        return success(true);
+    }
+
+    @GetMapping("/get-my")
+    @Operation(summary = "获取【我的】音乐")
+    @Parameter(name = "id", required = true, description = "音乐编号", example = "1024")
+    public CommonResult<AiMusicRespVO> getMusicMy(@RequestParam("id") Long id) {
+        AiMusicDO music = musicService.getMusic(id);
+        if (music == null || ObjUtil.notEqual(getLoginUserId(), music.getUserId())) {
+            return success(null);
+        }
+        return success(BeanUtils.toBean(music, AiMusicRespVO.class));
+    }
+
+    @PostMapping("/update-my")
+    @Operation(summary = "修改【我的】音乐 目前只支持修改标题")
+    @Parameter(name = "title", required = true, description = "音乐名称", example = "夜空中最亮的星")
+    public CommonResult<Boolean> updateMy(AiMusicUpdateMyReqVO updateReqVO) {
+        musicService.updateMyMusic(updateReqVO, getLoginUserId());
+        return success(true);
+    }
+
+    // ================ 音乐管理 ================
+
+    @GetMapping("/page")
+    @Operation(summary = "获得音乐分页")
+    @PreAuthorize("@ss.hasPermission('ai:music:query')")
+    public CommonResult<PageResult<AiMusicRespVO>> getMusicPage(@Valid AiMusicPageReqVO pageReqVO) {
+        PageResult<AiMusicDO> pageResult = musicService.getMusicPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除音乐")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:music:delete')")
+    public CommonResult<Boolean> deleteMusic(@RequestParam("id") Long id) {
+        musicService.deleteMusic(id);
+        return success(true);
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新音乐")
+    @PreAuthorize("@ss.hasPermission('ai:music:update')")
+    public CommonResult<Boolean> updateMusic(@Valid @RequestBody AiMusicUpdateReqVO updateReqVO) {
+        musicService.updateMusic(updateReqVO);
+        return success(true);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java
new file mode 100644
index 0000000..cd17a6f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java
@@ -0,0 +1,40 @@
+package com.iailab.module.ai.controller.admin.music.vo;
+
+import com.iailab.framework.common.pojo.PageParam;
+import com.iailab.framework.common.validation.InEnum;
+import com.iailab.module.ai.enums.music.AiMusicGenerateModeEnum;
+import com.iailab.module.ai.enums.music.AiMusicStatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI 音乐分页 Request VO")
+@Data
+public class AiMusicPageReqVO extends PageParam {
+
+    @Schema(description = "用户编号", example = "12212")
+    private Long userId;
+
+    @Schema(description = "音乐名称", example = "夜空中最亮的星")
+    private String title;
+
+    @Schema(description = "音乐状态", example = "20")
+    @InEnum(AiMusicStatusEnum.class)
+    private Integer status;
+
+    @Schema(description = "生成模式", example = "1")
+    @InEnum(AiMusicGenerateModeEnum.class)
+    private Integer generateMode;
+
+    @Schema(description = "是否发布", example = "true")
+    private Boolean publicStatus;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicRespVO.java
new file mode 100644
index 0000000..e47ec8e
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicRespVO.java
@@ -0,0 +1,70 @@
+package com.iailab.module.ai.controller.admin.music.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - AI 音乐 Response VO")
+@Data
+public class AiMusicRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+    private Long id;
+
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12212")
+    private Long userId;
+
+    @Schema(description = "音乐名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "夜空中最亮的星")
+    private String title;
+
+    @Schema(description = "歌词", example = "oh~卖糕的")
+    private String lyric;
+
+    @Schema(description = "图片地址", example = "https://www.Iailab.cn")
+    private String imageUrl;
+
+    @Schema(description = "音频地址", example = "https://www.Iailab.cn")
+    private String audioUrl;
+
+    @Schema(description = "视频地址", example = "https://www.Iailab.cn")
+    private String videoUrl;
+
+    @Schema(description = "音乐状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
+    private Integer status;
+
+    @Schema(description = "描述词", example = "一首轻快的歌曲")
+    private String gptDescriptionPrompt;
+
+    @Schema(description = "提示词", example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
+    private String prompt;
+
+    @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "Suno")
+    private String platform;
+
+    @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
+    private String model;
+
+    @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer generateMode;
+
+    @Schema(description = "音乐风格标签")
+    private List<String> tags;
+
+    @Schema(description = "音乐时长", example = "[\"pop\",\"jazz\",\"punk\"]")
+    private Double duration;
+
+    @Schema(description = "是否发布", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean publicStatus;
+
+    @Schema(description = "任务编号", example = "11369")
+    private String taskId;
+
+    @Schema(description = "错误信息")
+    private String errorMessage;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java
new file mode 100644
index 0000000..02791d0
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java
@@ -0,0 +1,18 @@
+package com.iailab.module.ai.controller.admin.music.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 修改我的音乐 Request VO")
+@Data
+public class AiMusicUpdateMyReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+    @Schema(description = "音乐名称", example = "夜空中最亮的星")
+    private String title;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java
new file mode 100644
index 0000000..9582eea
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java
@@ -0,0 +1,18 @@
+package com.iailab.module.ai.controller.admin.music.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 音乐修改 Request VO")
+@Data
+public class AiMusicUpdateReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+    @Schema(description = "是否发布", example = "true")
+    private Boolean publicStatus;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java
new file mode 100644
index 0000000..b637bef
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java
@@ -0,0 +1,57 @@
+package com.iailab.module.ai.controller.admin.music.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - AI 音乐生成 Request VO")
+@Data
+public class AiSunoGenerateReqVO {
+
+    @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "Suno")
+    @NotBlank(message = "平台不能为空")
+    private String platform; // 参见 AiPlatformEnum 枚举
+
+    /**
+     * 1. 描述模式:描述词 + 是否纯音乐 + 模型
+     * 2. 歌词模式:歌词 + 音乐风格 + 标题 + 模型
+     */
+    @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @NotNull(message = "生成模式不能为空")
+    private Integer generateMode; // 参见 AiMusicGenerateModeEnum 枚举
+
+    @Schema(description = "用于生成音乐音频的歌词提示",
+            example = """
+                    1.描述模式:创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。
+                    2.歌词模式:
+                    [Verse]
+                    阳光下奔跑 多么欢快
+                    假期就要来 心都飞起来
+                    朋友在一旁 笑声又灿烂
+                    无忧无虑的 每一天甜蜜
+                    [Chorus]
+                    马上放假了 快来庆祝
+                    一起去旅行 快去冒险
+                    日子太短暂 别再等待
+                    马上放假了 梦想起飞
+                    """)
+    private String prompt;
+
+    @Schema(description = "是否纯音乐", example = "true")
+    private Boolean makeInstrumental;
+
+    @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
+    @NotEmpty(message = "模型不能为空")
+    private String model;
+
+    @Schema(description = "音乐风格", example = "[\"pop\",\"jazz\",\"punk\"]")
+    private List<String> tags;
+
+    @Schema(description = "音乐/歌曲名称", example = "夜空中最亮的星")
+    private String title;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/QuestionParamSettingController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/QuestionParamSettingController.java
new file mode 100644
index 0000000..482973b
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/QuestionParamSettingController.java
@@ -0,0 +1,72 @@
+package com.iailab.module.ai.controller.admin.questionparamsetting;
+
+import jakarta.annotation.Resource;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.*;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import static com.iailab.framework.common.pojo.CommonResult.success;
+
+import com.iailab.module.ai.controller.admin.questionparamsetting.vo.*;
+import com.iailab.module.ai.dal.dataobject.questionparamsetting.QuestionParamSettingDO;
+import com.iailab.module.ai.service.questionparamsetting.QuestionParamSettingService;
+
+@Tag(name = "管理后台 - 大模型问题设置参数")
+@RestController
+@RequestMapping("/ai/question-param-setting")
+@Validated
+public class QuestionParamSettingController {
+
+    @Resource
+    private QuestionParamSettingService questionParamSettingService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建大模型问题设置参数")
+    @PreAuthorize("@ss.hasPermission('ai:question-param-setting:create')")
+    public CommonResult<String> createQuestionParamSetting(@Valid @RequestBody QuestionParamSettingSaveReqVO createReqVO) {
+        return success(questionParamSettingService.createQuestionParamSetting(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新大模型问题设置参数")
+    @PreAuthorize("@ss.hasPermission('ai:question-param-setting:update')")
+    public CommonResult<Boolean> updateQuestionParamSetting(@Valid @RequestBody QuestionParamSettingSaveReqVO updateReqVO) {
+        questionParamSettingService.updateQuestionParamSetting(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除大模型问题设置参数")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:question-param-setting:delete')")
+    public CommonResult<Boolean> deleteQuestionParamSetting(@RequestParam("id") String id) {
+        questionParamSettingService.deleteQuestionParamSetting(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得大模型问题设置参数")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:question-param-setting:query')")
+    public CommonResult<QuestionParamSettingRespVO> getQuestionParamSetting(@RequestParam("id") String id) {
+        QuestionParamSettingDO questionParamSetting = questionParamSettingService.getQuestionParamSetting(id);
+        return success(BeanUtils.toBean(questionParamSetting, QuestionParamSettingRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得大模型问题设置参数分页")
+    @PreAuthorize("@ss.hasPermission('ai:question-param-setting:query')")
+    public CommonResult<PageResult<QuestionParamSettingRespVO>> getQuestionParamSettingPage(@Valid QuestionParamSettingPageReqVO pageReqVO) {
+        PageResult<QuestionParamSettingDO> pageResult = questionParamSettingService.getQuestionParamSettingPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, QuestionParamSettingRespVO.class));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingPageReqVO.java
new file mode 100644
index 0000000..d0f8d7e
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingPageReqVO.java
@@ -0,0 +1,28 @@
+package com.iailab.module.ai.controller.admin.questionparamsetting.vo;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import com.iailab.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - 大模型问题设置参数分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class QuestionParamSettingPageReqVO extends PageParam {
+
+    @Schema(description = "问题模板id", example = "2283")
+    private String templateId;
+
+    @Schema(description = "key")
+    private String settingKey;
+
+    @Schema(description = "参数名称", example = "iailab")
+    private String settingName;
+
+    @Schema(description = "参数默认值")
+    private String settingValue;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingRespVO.java
new file mode 100644
index 0000000..bc95e04
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingRespVO.java
@@ -0,0 +1,28 @@
+package com.iailab.module.ai.controller.admin.questionparamsetting.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+@Schema(description = "管理后台 - 大模型问题设置参数 Response VO")
+@Data
+public class QuestionParamSettingRespVO {
+
+    @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23197")
+    private String id;
+
+    @Schema(description = "问题模板id", requiredMode = Schema.RequiredMode.REQUIRED, example = "2283")
+    private String templateId;
+
+    @Schema(description = "key")
+    private String settingKey;
+
+    @Schema(description = "参数名称", example = "iailab")
+    private String settingName;
+
+    @Schema(description = "参数默认值")
+    private String settingValue;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingSaveReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingSaveReqVO.java
new file mode 100644
index 0000000..532fdc2
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questionparamsetting/vo/QuestionParamSettingSaveReqVO.java
@@ -0,0 +1,30 @@
+package com.iailab.module.ai.controller.admin.questionparamsetting.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 大模型问题设置参数新增/修改 Request VO")
+@Data
+public class QuestionParamSettingSaveReqVO {
+
+    @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23197")
+    private String id;
+
+    @Schema(description = "问题模板id", requiredMode = Schema.RequiredMode.REQUIRED, example = "2283")
+    @NotEmpty(message = "问题模板id不能为空")
+    private String templateId;
+
+    @Schema(description = "key")
+    private String settingKey;
+
+    @Schema(description = "参数名称", example = "iailab")
+    private String settingName;
+
+    @Schema(description = "参数默认值")
+    private String settingValue;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/QuestionTemplateController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/QuestionTemplateController.java
new file mode 100644
index 0000000..d673385
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/QuestionTemplateController.java
@@ -0,0 +1,73 @@
+package com.iailab.module.ai.controller.admin.questiontemplate;
+
+import jakarta.annotation.Resource;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.*;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import static com.iailab.framework.common.pojo.CommonResult.success;
+
+
+import com.iailab.module.ai.controller.admin.questiontemplate.vo.*;
+import com.iailab.module.ai.dal.dataobject.questiontemplate.QuestionTemplateDO;
+import com.iailab.module.ai.service.questiontemplate.QuestionTemplateService;
+
+@Tag(name = "管理后台 - 大模型问题模板")
+@RestController
+@RequestMapping("/ai/question-template")
+@Validated
+public class QuestionTemplateController {
+
+    @Resource
+    private QuestionTemplateService questionTemplateService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建大模型问题模板")
+    @PreAuthorize("@ss.hasPermission('ai:question-template:create')")
+    public CommonResult<String> createQuestionTemplate(@Valid @RequestBody QuestionTemplateSaveReqVO createReqVO) {
+        return success(questionTemplateService.createQuestionTemplate(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新大模型问题模板")
+    @PreAuthorize("@ss.hasPermission('ai:question-template:update')")
+    public CommonResult<Boolean> updateQuestionTemplate(@Valid @RequestBody QuestionTemplateSaveReqVO updateReqVO) {
+        questionTemplateService.updateQuestionTemplate(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除大模型问题模板")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:question-template:delete')")
+    public CommonResult<Boolean> deleteQuestionTemplate(@RequestParam("id") String id) {
+        questionTemplateService.deleteQuestionTemplate(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得大模型问题模板")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:question-template:query')")
+    public CommonResult<QuestionTemplateRespVO> getQuestionTemplate(@RequestParam("id") String id) {
+        QuestionTemplateDO questionTemplate = questionTemplateService.getQuestionTemplate(id);
+        return success(BeanUtils.toBean(questionTemplate, QuestionTemplateRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得大模型问题模板分页")
+    @PreAuthorize("@ss.hasPermission('ai:question-template:query')")
+    public CommonResult<PageResult<QuestionTemplateRespVO>> getQuestionTemplatePage(@Valid QuestionTemplatePageReqVO pageReqVO) {
+        PageResult<QuestionTemplateDO> pageResult = questionTemplateService.getQuestionTemplatePage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, QuestionTemplateRespVO.class));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplatePageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplatePageReqVO.java
new file mode 100644
index 0000000..ae5ee7f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplatePageReqVO.java
@@ -0,0 +1,42 @@
+package com.iailab.module.ai.controller.admin.questiontemplate.vo;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import com.iailab.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 大模型问题模板分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class QuestionTemplatePageReqVO extends PageParam {
+
+    @Schema(description = "模型id", example = "11609")
+    private String modelId;
+
+    @Schema(description = "问题编号")
+    private String questionCode;
+
+    @Schema(description = "问题名称", example = "赵六")
+    private String questionName;
+
+    @Schema(description = "问题内容")
+    private String questionContent;
+
+    @Schema(description = "输入个数")
+    private Integer dataLength;
+
+    @Schema(description = "是否启用(0禁用 1启用)")
+    private Integer isEnable;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplateRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplateRespVO.java
new file mode 100644
index 0000000..471d4ff
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplateRespVO.java
@@ -0,0 +1,38 @@
+package com.iailab.module.ai.controller.admin.questiontemplate.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 大模型问题模板 Response VO")
+@Data
+public class QuestionTemplateRespVO {
+
+    @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31480")
+    private String id;
+
+    @Schema(description = "模型id", requiredMode = Schema.RequiredMode.REQUIRED, example = "11609")
+    private String modelId;
+
+    @Schema(description = "问题编号", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String questionCode;
+
+    @Schema(description = "问题名称", example = "赵六")
+    private String questionName;
+
+    @Schema(description = "问题内容")
+    private String questionContent;
+
+    @Schema(description = "输入个数")
+    private Integer dataLength;
+
+    @Schema(description = "是否启用(0禁用 1启用)")
+    private Integer isEnable;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplateSaveReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplateSaveReqVO.java
new file mode 100644
index 0000000..788ffeb
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplateSaveReqVO.java
@@ -0,0 +1,38 @@
+package com.iailab.module.ai.controller.admin.questiontemplate.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import javax.validation.constraints.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 大模型问题模板新增/修改 Request VO")
+@Data
+public class QuestionTemplateSaveReqVO {
+
+    @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31480")
+    private String id;
+
+    @Schema(description = "模型id", requiredMode = Schema.RequiredMode.REQUIRED, example = "11609")
+    @NotEmpty(message = "模型id不能为空")
+    private String modelId;
+
+    @Schema(description = "问题编号", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "问题编号不能为空")
+    private String questionCode;
+
+    @Schema(description = "问题名称", example = "赵六")
+    private String questionName;
+
+    @Schema(description = "问题内容")
+    private String questionContent;
+
+    @Schema(description = "输入个数")
+    private Integer dataLength;
+
+    @Schema(description = "是否启用(0禁用 1启用)")
+    private Integer isEnable;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/AiWorkflowController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/AiWorkflowController.java
new file mode 100644
index 0000000..b66f8c3
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/AiWorkflowController.java
@@ -0,0 +1,77 @@
+package com.iailab.module.ai.controller.admin.workflow;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.workflow.vo.*;
+import com.iailab.module.ai.dal.dataobject.workflow.AiWorkflowDO;
+import com.iailab.module.ai.service.workflow.AiWorkflowService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - AI 工作流")
+@RestController
+@RequestMapping("/ai/workflow")
+@Slf4j
+public class AiWorkflowController {
+
+    @Resource
+    private AiWorkflowService workflowService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建 AI 工作流")
+    @PreAuthorize("@ss.hasPermission('ai:workflow:create')")
+    public CommonResult<Long> createWorkflow(@Valid @RequestBody AiWorkflowSaveReqVO createReqVO) {
+        return success(workflowService.createWorkflow(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新 AI 工作流")
+    @PreAuthorize("@ss.hasPermission('ai:workflow:update')")
+    public CommonResult<Boolean> updateWorkflow(@Valid @RequestBody AiWorkflowSaveReqVO updateReqVO) {
+        workflowService.updateWorkflow(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除 AI 工作流")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:workflow:delete')")
+    public CommonResult<Boolean> deleteWorkflow(@RequestParam("id") Long id) {
+        workflowService.deleteWorkflow(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得 AI 工作流")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('ai:workflow:query')")
+    public CommonResult<AiWorkflowRespVO> getWorkflow(@RequestParam("id") Long id) {
+        AiWorkflowDO workflow = workflowService.getWorkflow(id);
+        return success(BeanUtils.toBean(workflow, AiWorkflowRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得 AI 工作流分页")
+    @PreAuthorize("@ss.hasPermission('ai:workflow:query')")
+    public CommonResult<PageResult<AiWorkflowRespVO>> getWorkflowPage(@Valid AiWorkflowPageReqVO pageReqVO) {
+        PageResult<AiWorkflowDO> pageResult = workflowService.getWorkflowPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiWorkflowRespVO.class));
+    }
+
+    @PostMapping("/test")
+    @Operation(summary = "测试 AI 工作流")
+    @PreAuthorize("@ss.hasPermission('ai:workflow:test')")
+    public CommonResult<Object> testWorkflow(@Valid @RequestBody AiWorkflowTestReqVO testReqVO) {
+        return success(workflowService.testWorkflow(testReqVO));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java
new file mode 100644
index 0000000..b3d5180
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java
@@ -0,0 +1,32 @@
+package com.iailab.module.ai.controller.admin.workflow.vo;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageParam;
+import com.iailab.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI 工作流分页 Request VO")
+@Data
+public class AiWorkflowPageReqVO extends PageParam {
+
+    @Schema(description = "名称", example = "工作流")
+    private String name;
+
+    @Schema(description = "标识", example = "FLOW")
+    private String code;
+
+    @Schema(description = "状态", example = "1")
+    @InEnum(CommonStatusEnum.class)
+    private Integer status;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java
new file mode 100644
index 0000000..fd7827f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java
@@ -0,0 +1,33 @@
+package com.iailab.module.ai.controller.admin.workflow.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - AI 工作流 Response VO")
+@Data
+public class AiWorkflowRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long id;
+
+    @Schema(description = "工作流标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW")
+    private String code;
+
+    @Schema(description = "工作流名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作流")
+    private String name;
+
+    @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作流")
+    private String remark;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer status;
+
+    @Schema(description = "工作流模型 JSON", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
+    private String graph;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
+    private LocalDateTime createTime;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java
new file mode 100644
index 0000000..63e9451
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java
@@ -0,0 +1,34 @@
+package com.iailab.module.ai.controller.admin.workflow.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 工作流新增/修改 Request VO")
+@Data
+public class AiWorkflowSaveReqVO {
+
+    @Schema(description = "编号", example = "1")
+    private Long id;
+
+    @Schema(description = "工作流标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW")
+    @NotEmpty(message = "工作流标识不能为空")
+    private String code;
+
+    @Schema(description = "工作流名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作流")
+    @NotEmpty(message = "工作流名称不能为空")
+    private String name;
+
+    @Schema(description = "备注", example = "FLOW")
+    private String remark;
+
+    @Schema(description = "工作流模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
+    @NotEmpty(message = "工作流模型不能为空")
+    private String graph;
+
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW")
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java
new file mode 100644
index 0000000..2ed1547
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java
@@ -0,0 +1,20 @@
+package com.iailab.module.ai.controller.admin.workflow.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+import java.util.Map;
+
+@Schema(description = "管理后台 - AI 工作流测试 Request VO")
+@Data
+public class AiWorkflowTestReqVO {
+
+    @Schema(description = "工作流模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
+    @NotEmpty(message = "工作流模型不能为空")
+    private String graph;
+
+    @Schema(description = "参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
+    private Map<String, Object> params;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/AiWriteController.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/AiWriteController.java
new file mode 100644
index 0000000..141e2d5
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/AiWriteController.java
@@ -0,0 +1,57 @@
+package com.iailab.module.ai.controller.admin.write;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
+import com.iailab.module.ai.controller.admin.write.vo.AiWritePageReqVO;
+import com.iailab.module.ai.controller.admin.write.vo.AiWriteRespVO;
+import com.iailab.module.ai.dal.dataobject.write.AiWriteDO;
+import com.iailab.module.ai.service.write.AiWriteService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.http.MediaType;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Flux;
+
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - AI 写作")
+@RestController
+@RequestMapping("/ai/write")
+public class AiWriteController {
+
+    @Resource
+    private AiWriteService writeService;
+
+    @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    @Operation(summary = "写作生成(流式)", description = "流式返回,响应较快")
+    public Flux<CommonResult<String>> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) {
+        return writeService.generateWriteContent(generateReqVO, getLoginUserId());
+    }
+
+    // ================ 写作管理 ================
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除写作")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('ai:write:delete')")
+    public CommonResult<Boolean> deleteWrite(@RequestParam("id") Long id) {
+        writeService.deleteWrite(id);
+        return success(true);
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得写作分页")
+    @PreAuthorize("@ss.hasPermission('ai:write:query')")
+    public CommonResult<PageResult<AiWriteRespVO>> getWritePage(@Valid AiWritePageReqVO pageReqVO) {
+        PageResult<AiWriteDO> pageResult = writeService.getWritePage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, AiWriteRespVO.class));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java
new file mode 100644
index 0000000..7c1e94d
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java
@@ -0,0 +1,39 @@
+package com.iailab.module.ai.controller.admin.write.vo;
+
+import com.iailab.framework.common.validation.InEnum;
+import com.iailab.module.ai.enums.write.AiWriteTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 写作生成 Request VO")
+@Data
+public class AiWriteGenerateReqVO {
+
+    @Schema(description = "写作类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @InEnum(value = AiWriteTypeEnum.class, message = "写作类型必须是 {value}")
+    private Integer type;
+
+    @Schema(description = "写作内容提示", example = "1.撰写:田忌赛马;2.回复:不批")
+    private String prompt;
+
+    @Schema(description = "原文", example = "领导我要辞职")
+    private String originalContent;
+
+    @Schema(description = "长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "长度不能为空")
+    private Integer length;
+
+    @Schema(description = "格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "格式不能为空")
+    private Integer format;
+
+    @Schema(description = "语气", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "语气不能为空")
+    private Integer tone;
+
+    @Schema(description = "语言", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "语言不能为空")
+    private Integer language;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWritePageReqVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWritePageReqVO.java
new file mode 100644
index 0000000..1384f2e
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWritePageReqVO.java
@@ -0,0 +1,31 @@
+package com.iailab.module.ai.controller.admin.write.vo;
+
+import com.iailab.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - AI 写作分页 Request VO")
+@Data
+public class AiWritePageReqVO extends PageParam {
+
+    @Schema(description = "用户编号", example = "28404")
+    private Long userId;
+
+    @Schema(description = "写作类型", example = "1")
+    private Integer type;
+
+    @Schema(description = "平台", example = "TongYi")
+    private String platform;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWriteRespVO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWriteRespVO.java
new file mode 100644
index 0000000..e3bc103
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/write/vo/AiWriteRespVO.java
@@ -0,0 +1,54 @@
+package com.iailab.module.ai.controller.admin.write.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - AI 写作 Response VO")
+@Data
+public class AiWriteRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5311")
+    private Long id;
+
+    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28404")
+    private Long userId;
+
+    @Schema(description = "写作类型", example = "1")
+    private Integer type;
+
+    @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "TongYi")
+    private String platform;
+
+    @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen")
+    private String model;
+
+    @Schema(description = "生成内容提示", requiredMode = Schema.RequiredMode.REQUIRED, example = "撰写:田忌赛马")
+    private String prompt;
+
+    @Schema(description = "生成的内容", example = "你非常不错")
+    private String generatedContent;
+
+    @Schema(description = "原文", example = "真的么?")
+    private String originalContent;
+
+    @Schema(description = "长度提示词", example = "1")
+    private Integer length;
+
+    @Schema(description = "格式提示词", example = "2")
+    private Integer format;
+
+    @Schema(description = "语气提示词", example = "3")
+    private Integer tone;
+
+    @Schema(description = "语言提示词", example = "4")
+    private Integer language;
+
+    @Schema(description = "错误信息")
+    private String errorMessage;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/app/package-info.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/app/package-info.java
new file mode 100644
index 0000000..1a020f7
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/app/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * TODO Iailab:站位,无特殊作用
+ */
+package com.iailab.module.ai.controller.app;
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/package-info.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/package-info.java
new file mode 100644
index 0000000..cfeca28
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 提供 RESTful API 给前端:
+ * 1. admin 包:提供给管理后台 iailab-ui-admin 前端项目
+ * 2. app 包:提供给用户 APP iailab-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
+ */
+package com.iailab.module.ai.controller;
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/chat/AiChatConversationDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/chat/AiChatConversationDO.java
new file mode 100644
index 0000000..40bfd32
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/chat/AiChatConversationDO.java
@@ -0,0 +1,100 @@
+package com.iailab.module.ai.dal.dataobject.chat;
+
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+/**
+ * AI Chat 对话 DO
+ *
+ * 用户每次发起 Chat 聊天时,会创建一个 {@link AiChatConversationDO} 对象,将它的消息关联在一起
+ *
+ * @author fansili
+ * @since 2024/4/14 17:35
+ */
+@TableName("ai_chat_conversation")
+@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AiChatConversationDO extends BaseDO {
+
+    public static final String TITLE_DEFAULT = "新对话";
+
+    /**
+     * ID 编号,自增
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 用户编号
+     *
+     * 关联 AdminUserDO 的 userId 字段
+     */
+    private Long userId;
+
+    /**
+     * 对话标题
+     *
+     * 默认由系统自动生成,可用户手动修改
+     */
+    private String title;
+    /**
+     * 是否置顶
+     */
+    private Boolean pinned;
+    /**
+     * 置顶时间
+     */
+    private LocalDateTime pinnedTime;
+
+    /**
+     * 角色编号
+     *
+     * 关联 {@link AiChatRoleDO#getId()}
+     */
+    private Long roleId;
+
+    /**
+     * 模型编号
+     *
+     * 关联 {@link AiModelDO#getId()} 字段
+     */
+    private Long modelId;
+    /**
+     * 模型标志
+     *
+     * 冗余 {@link AiModelDO#getModel()} 字段
+     */
+    private String model;
+
+    // ========== 对话配置 ==========
+
+    /**
+     * 角色设定
+     */
+    private String systemMessage;
+    /**
+     * 温度参数
+     *
+     * 用于调整生成回复的随机性和多样性程度:较低的温度值会使输出更收敛于高频词汇,较高的则增加多样性
+     */
+    private Double temperature;
+    /**
+     * 单条回复的最大 Token 数量
+     */
+    private Integer maxTokens;
+    /**
+     * 上下文的最大 Message 数量
+     */
+    private Integer maxContexts;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/chat/AiChatMessageDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/chat/AiChatMessageDO.java
new file mode 100644
index 0000000..0848b0a
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/chat/AiChatMessageDO.java
@@ -0,0 +1,104 @@
+package com.iailab.module.ai.dal.dataobject.chat;
+
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.iailab.framework.mybatis.core.type.LongListTypeHandler;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+import org.springframework.ai.chat.messages.MessageType;
+
+import java.util.List;
+
+/**
+ * AI Chat 消息 DO
+ *
+ * @since 2024/4/14 17:35
+ * @since 2024/4/14 17:35
+ */
+@TableName(value = "ai_chat_message", autoResultMap = true)
+@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AiChatMessageDO extends BaseDO {
+
+    /**
+     * 编号,作为每条聊天记录的唯一标识符
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 对话编号
+     *
+     * 关联 {@link AiChatConversationDO#getId()} 字段
+     */
+    private Long conversationId;
+    /**
+     * 回复消息编号
+     *
+     * 关联 {@link #id} 字段
+     *
+     * 大模型回复的消息编号,用于“问答”的关联
+     */
+    private Long replyId;
+
+    /**
+     * 消息类型
+     *
+     * 也等价于 OpenAPI 的 role 字段
+     *
+     * 枚举 {@link MessageType}
+     */
+    private String type;
+    /**
+     * 用户编号
+     *
+     * 关联 AdminUserDO 的 userId 字段
+     */
+    private Long userId;
+    /**
+     * 角色编号
+     *
+     * 关联 {@link AiChatRoleDO#getId()} 字段
+     */
+    private Long roleId;
+
+    /**
+     * 模型标志
+     *
+     * 冗余 {@link AiModelDO#getModel()}
+     */
+    private String model;
+    /**
+     * 模型编号
+     *
+     * 关联 {@link AiModelDO#getId()} 字段
+     */
+    private Long modelId;
+
+    /**
+     * 聊天内容
+     */
+    private String content;
+
+    /**
+     * 是否携带上下文
+     */
+    private Boolean useContext;
+
+    /**
+     * 知识库段落编号数组
+     *
+     * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段
+     */
+    @TableField(typeHandler = LongListTypeHandler.class)
+    private List<Long> segmentIds;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/image/AiImageDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/image/AiImageDO.java
new file mode 100644
index 0000000..54e3369
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/image/AiImageDO.java
@@ -0,0 +1,126 @@
+package com.iailab.module.ai.dal.dataobject.image;
+
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.enums.image.AiImageStatusEnum;
+import com.iailab.module.system.api.user.dto.AdminUserRespDTO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import lombok.Data;
+import org.springframework.ai.openai.OpenAiImageOptions;
+import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * AI 绘画 DO
+ *
+ * @author fansili
+ */
+@TableName(value = "ai_image", autoResultMap = true)
+@KeySequence("ai_image_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class AiImageDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 用户编号
+     *
+     * 关联 {@link AdminUserRespDTO#getId()}
+     */
+    private Long userId;
+
+    /**
+     * 提示词
+     */
+    private String prompt;
+
+    /**
+     * 平台
+     *
+     * 枚举 {@link com.iailab.framework.ai.core.enums.AiPlatformEnum}
+     */
+    private String platform;
+    /**
+     * 模型编号
+     *
+     * 关联 {@link AiModelDO#getId()}
+     */
+    private Long modelId;
+    /**
+     * 模型标识
+     *
+     * 冗余 {@link AiModelDO#getModel()}
+     */
+    private String model;
+
+    /**
+     * 图片宽度
+     */
+    private Integer width;
+    /**
+     * 图片高度
+     */
+    private Integer height;
+
+    /**
+     * 生成状态
+     *
+     * 枚举 {@link AiImageStatusEnum}
+     */
+    private Integer status;
+
+    /**
+     * 完成时间
+     */
+    private LocalDateTime finishTime;
+
+    /**
+     * 绘画错误信息
+     */
+    private String errorMessage;
+
+    /**
+     * 图片地址
+     */
+    private String picUrl;
+    /**
+     * 是否公开
+     */
+    private Boolean publicStatus;
+
+    /**
+     * 绘制参数,不同 platform 的不同参数
+     *
+     * 1. {@link OpenAiImageOptions}
+     * 2. {@link StabilityAiImageOptions}
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, Object> options;
+
+    /**
+     * mj buttons 按钮
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private List<MidjourneyApi.Button> buttons;
+
+    /**
+     * 任务编号
+     *
+     * 1. midjourney proxy:关联的 task id
+     */
+    private String taskId;
+
+}
+
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
new file mode 100644
index 0000000..607071a
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
@@ -0,0 +1,64 @@
+package com.iailab.module.ai.dal.dataobject.knowledge;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * AI 知识库 DO
+ *
+ * @author xiaoxin
+ */
+@TableName(value = "ai_knowledge", autoResultMap = true)
+@KeySequence("ai_knowledge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class AiKnowledgeDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 知识库名称
+     */
+    private String name;
+    /**
+     * 知识库描述
+     */
+    private String description;
+
+    /**
+     * 向量模型编号
+     *
+     * 关联 {@link AiModelDO#getId()}
+     */
+    private Long embeddingModelId;
+    /**
+     * 模型标识
+     *
+     * 冗余 {@link AiModelDO#getModel()}
+     */
+    private String embeddingModel;
+
+    /**
+     * topK
+     */
+    private Integer topK;
+    /**
+     * 相似度阈值
+     */
+    private Double similarityThreshold;
+
+    /**
+     * 状态
+     * <p>
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
new file mode 100644
index 0000000..b5ad159
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
@@ -0,0 +1,69 @@
+package com.iailab.module.ai.dal.dataobject.knowledge;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * AI 知识库-文档 DO
+ *
+ * @author xiaoxin
+ */
+@TableName(value = "ai_knowledge_document")
+@KeySequence("ai_knowledge_document_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class AiKnowledgeDocumentDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 知识库编号
+     * <p>
+     * 关联 {@link AiKnowledgeDO#getId()}
+     */
+    private Long knowledgeId;
+    /**
+     * 文档名称
+     */
+    private String name;
+    /**
+     * 文件 URL
+     */
+    private String url;
+    /**
+     * 内容
+     */
+    private String content;
+    /**
+     * 文档长度
+     */
+    private Integer contentLength;
+
+    /**
+     * 文档 token 数量
+     */
+    private Integer tokens;
+    /**
+     * 分片最大 Token 数
+     */
+    private Integer segmentMaxTokens;
+
+    /**
+     * 召回次数
+     */
+    private Integer retrievalCount;
+
+    /**
+     * 状态
+     * <p>
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
new file mode 100644
index 0000000..4dc57dc
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
@@ -0,0 +1,72 @@
+package com.iailab.module.ai.dal.dataobject.knowledge;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * AI 知识库-文档分段 DO
+ *
+ * @author xiaoxin
+ */
+@TableName(value = "ai_knowledge_segment")
+@KeySequence("ai_knowledge_segment_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class AiKnowledgeSegmentDO extends BaseDO {
+
+    /**
+     * 向量库的编号 - 空值
+     */
+    public static final String VECTOR_ID_EMPTY = "";
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 知识库编号
+     * <p>
+     * 关联 {@link AiKnowledgeDO#getId()}
+     */
+    private Long knowledgeId;
+    /**
+     * 文档编号
+     * <p>
+     * 关联 {@link AiKnowledgeDocumentDO#getId()}
+     */
+    private Long documentId;
+    /**
+     * 切片内容
+     */
+    private String content;
+    /**
+     * 切片内容长度
+     */
+    private Integer contentLength;
+
+    /**
+     * 向量库的编号
+     */
+    private String vectorId;
+    /**
+     * token 数量
+     */
+    private Integer tokens;
+
+    /**
+     * 召回次数
+     */
+    private Integer retrievalCount;
+
+    /**
+     * 状态
+     * <p>
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/mindmap/AiMindMapDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/mindmap/AiMindMapDO.java
new file mode 100644
index 0000000..bb7c12a
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/mindmap/AiMindMapDO.java
@@ -0,0 +1,66 @@
+package com.iailab.module.ai.dal.dataobject.mindmap;
+
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * AI 思维导图 DO
+ *
+ * @author xiaoxin
+ */
+@TableName(value = "ai_mind_map")
+@KeySequence("ai_mind_map_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class AiMindMapDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 用户编号
+     * <p>
+     * 关联 AdminUserDO 的 userId 字段
+     */
+    private Long userId;
+
+    /**
+     * 平台
+     * <p>
+     * 枚举 {@link AiPlatformEnum}
+     */
+    private String platform;
+    /**
+     * 模型编号
+     *
+     * 关联 {@link AiModelDO#getId()}
+     */
+    private Long modelId;
+    /**
+     * 模型
+     */
+    private String model;
+
+    /**
+     * 生成内容提示
+     */
+    private String prompt;
+
+    /**
+     * 生成的内容
+     */
+    private String generatedContent;
+
+    /**
+     * 错误信息
+     */
+    private String errorMessage;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiApiKeyDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiApiKeyDO.java
new file mode 100644
index 0000000..a458f13
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiApiKeyDO.java
@@ -0,0 +1,54 @@
+package com.iailab.module.ai.dal.dataobject.model;
+
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * AI API 秘钥 DO
+ *
+ * @author Iailab
+ */
+@TableName("ai_api_key")
+@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AiApiKeyDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 名称
+     */
+    private String name;
+    /**
+     * 密钥
+     */
+    private String apiKey;
+    /**
+     * 平台
+     *
+     * 枚举 {@link AiPlatformEnum}
+     */
+    private String platform;
+    /**
+     * API 地址
+     */
+    private String url;
+    /**
+     * 状态
+     *
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiChatRoleDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiChatRoleDO.java
new file mode 100644
index 0000000..08099e3
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiChatRoleDO.java
@@ -0,0 +1,103 @@
+package com.iailab.module.ai.dal.dataobject.model;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.iailab.framework.mybatis.core.type.LongListTypeHandler;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.util.List;
+
+/**
+ * AI 聊天角色 DO
+ *
+ * @author fansili
+ * @since 2024/4/24 19:39
+ */
+@TableName(value = "ai_chat_role", autoResultMap = true)
+@KeySequence("ai_chat_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AiChatRoleDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 角色名称
+     */
+    private String name;
+    /**
+     * 角色头像
+     */
+    private String avatar;
+    /**
+     * 角色分类
+     */
+    private String category;
+    /**
+     * 角色描述
+     */
+    private String description;
+    /**
+     * 角色设定
+     */
+    private String systemMessage;
+
+    /**
+     * 用户编号
+     *
+     * 关联 AdminUserDO 的 userId 字段
+     */
+    private Long userId;
+
+    /**
+     * 模型编号
+     *
+     * 关联 {@link AiModelDO#getId()} 字段
+     */
+    private Long modelId;
+
+    /**
+     * 引用的知识库编号列表
+     *
+     * 关联 {@link AiKnowledgeDO#getId()} 字段
+     */
+    @TableField(typeHandler = LongListTypeHandler.class)
+    private List<Long> knowledgeIds;
+    /**
+     * 引用的工具编号列表
+     *
+     * 关联 {@link AiToolDO#getId()} 字段
+     */
+    @TableField(typeHandler = LongListTypeHandler.class)
+    private List<Long> toolIds;
+
+    /**
+     * 是否公开
+     *
+     * 1. true - 公开;由管理员在【角色管理】所创建
+     * 2. false - 私有;由个人在【我的角色】所创建
+     */
+    private Boolean publicStatus;
+
+    /**
+     * 排序值
+     */
+    private Integer sort;
+    /**
+     * 状态
+     *
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiModelDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiModelDO.java
new file mode 100644
index 0000000..a569b09
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiModelDO.java
@@ -0,0 +1,88 @@
+package com.iailab.module.ai.dal.dataobject.model;
+
+import com.iailab.framework.ai.core.enums.AiModelTypeEnum;
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * AI 模型 DO
+ *
+ * 默认模型:{@link #status} 为开启,并且 {@link #sort} 排序第一
+ *
+ * @author fansili
+ * @since 2024/4/24 19:39
+ */
+@TableName("ai_model")
+@KeySequence("ai_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AiModelDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * API 秘钥编号
+     *
+     * 关联 {@link AiApiKeyDO#getId()}
+     */
+    private Long keyId;
+    /**
+     * 模型名称
+     */
+    private String name;
+    /**
+     * 模型标志
+     */
+    private String model;
+    /**
+     * 平台
+     *
+     * 枚举 {@link AiPlatformEnum}
+     */
+    private String platform;
+    /**
+     * 类型
+     *
+     * 枚举 {@link AiModelTypeEnum}
+     */
+    private Integer type;
+
+    /**
+     * 排序值
+     */
+    private Integer sort;
+    /**
+     * 状态
+     *
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+
+    // ========== 对话配置 ==========
+
+    /**
+     * 温度参数
+     *
+     * 用于调整生成回复的随机性和多样性程度:较低的温度值会使输出更收敛于高频词汇,较高的则增加多样性
+     */
+    private Double temperature;
+    /**
+     * 单条回复的最大 Token 数量
+     */
+    private Integer maxTokens;
+    /**
+     * 上下文的最大 Message 数量
+     */
+    private Integer maxContexts;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiToolDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiToolDO.java
new file mode 100644
index 0000000..61860e6
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/model/AiToolDO.java
@@ -0,0 +1,48 @@
+package com.iailab.module.ai.dal.dataobject.model;
+
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.iailab.module.ai.service.model.tool.DirectoryListToolFunction;
+import com.iailab.module.ai.service.model.tool.WeatherQueryToolFunction;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * AI 工具 DO
+ *
+ * @author Iailab
+ */
+@TableName("ai_tool")
+@KeySequence("ai_tool_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AiToolDO extends BaseDO {
+
+    /**
+     * 工具编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 工具名称
+     *
+     * 对应 Bean 的名字,例如说:
+     * 1. {@link DirectoryListToolFunction} 的 Bean 名字是 directory_list
+     * 2. {@link WeatherQueryToolFunction} 的 Bean 名字是 weather_query
+     */
+    private String name;
+    /**
+     * 工具描述
+     */
+    private String description;
+    /**
+     * 状态
+     *
+     * 枚举 {@link com.iailab.framework.common.enums.CommonStatusEnum}
+     */
+    private Integer status;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/music/AiMusicDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/music/AiMusicDO.java
new file mode 100644
index 0000000..0caac5b
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/music/AiMusicDO.java
@@ -0,0 +1,119 @@
+package com.iailab.module.ai.dal.dataobject.music;
+
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.iailab.module.ai.enums.music.AiMusicGenerateModeEnum;
+import com.iailab.module.ai.enums.music.AiMusicStatusEnum;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * AI 音乐 DO
+ *
+ * @author xiaoxin
+ */
+@TableName(value = "ai_music", autoResultMap = true)
+@KeySequence("ai_music_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class AiMusicDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 用户编号
+     * <p>
+     * 关联 AdminUserDO 的 userId 字段
+     */
+    private Long userId;
+
+    /**
+     * 音乐名称
+     */
+    private String title;
+
+    /**
+     * 歌词
+     */
+    private String lyric;
+
+    /**
+     * 图片地址
+     */
+    private String imageUrl;
+    /**
+     * 音频地址
+     */
+    private String audioUrl;
+    /**
+     * 视频地址
+     */
+    private String videoUrl;
+
+    /**
+     * 音乐状态
+     * <p>
+     * 枚举 {@link AiMusicStatusEnum}
+     */
+    private Integer status;
+
+    /**
+     * 生成模式
+     * <p>
+     * 枚举 {@link AiMusicGenerateModeEnum}
+     */
+    private Integer generateMode;
+
+    /**
+     * 描述词
+     */
+    private String description;
+
+    /**
+     * 平台
+     * <p>
+     * 枚举 {@link AiPlatformEnum}
+     */
+    private String platform;
+    // TODO @Iailab:modelId?
+    /**
+     * 模型
+     */
+    private String model;
+
+    /**
+     * 音乐风格标签
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private List<String> tags;
+
+    /**
+     * 音乐时长
+     */
+    private Double duration;
+
+    /**
+     * 是否公开
+     */
+    private Boolean publicStatus;
+
+    /**
+     * 任务编号
+     */
+    private String taskId;
+
+    /**
+     * 错误信息
+     */
+    private String errorMessage;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/questionparamsetting/QuestionParamSettingDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/questionparamsetting/QuestionParamSettingDO.java
new file mode 100644
index 0000000..a9bb0be
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/questionparamsetting/QuestionParamSettingDO.java
@@ -0,0 +1,49 @@
+package com.iailab.module.ai.dal.dataobject.questionparamsetting;
+
+import lombok.*;
+import java.util.*;
+import com.baomidou.mybatisplus.annotation.*;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 大模型问题设置参数 DO
+ *
+ * @author 超级管理员
+ */
+@TableName("ai_question_param_setting")
+@KeySequence("ai_question_param_setting_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class QuestionParamSettingDO extends BaseDO {
+
+    /**
+     * id
+     */
+    @TableId(type = IdType.INPUT)
+    private String id;
+    /**
+     * 问题模板id
+     */
+    private String templateId;
+    /**
+     * key
+     */
+    private String settingKey;
+    /**
+     * 参数名称
+     */
+    private String settingName;
+    /**
+     * 参数默认值
+     */
+    private String settingValue;
+    /**
+     * 排序
+     */
+    private Integer sort;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/questiontemplate/QuestionTemplateDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/questiontemplate/QuestionTemplateDO.java
new file mode 100644
index 0000000..6300371
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/questiontemplate/QuestionTemplateDO.java
@@ -0,0 +1,59 @@
+package com.iailab.module.ai.dal.dataobject.questiontemplate;
+
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 大模型问题模板 DO
+ *
+ * @author 超级管理员
+ */
+@TableName("ai_question_template")
+@KeySequence("ai_question_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class QuestionTemplateDO extends BaseDO {
+
+    /**
+     * id
+     */
+    @TableId(type = IdType.INPUT)
+    private String id;
+    /**
+     * 模型id
+     */
+    private String modelId;
+    /**
+     * 问题编号
+     */
+    private String questionCode;
+    /**
+     * 问题名称
+     */
+    private String questionName;
+    /**
+     * 问题内容
+     */
+    private String questionContent;
+    /**
+     * 输入个数
+     */
+    private Integer dataLength;
+    /**
+     * 是否启用(0禁用 1启用)
+     */
+    private Integer isEnable;
+    /**
+     * 备注
+     */
+    private String remark;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/workflow/AiWorkflowDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/workflow/AiWorkflowDO.java
new file mode 100644
index 0000000..1be5619
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/workflow/AiWorkflowDO.java
@@ -0,0 +1,51 @@
+package com.iailab.module.ai.dal.dataobject.workflow;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * AI 工作流 DO
+ *
+ * @author lesan
+ */
+@TableName(value = "ai_workflow", autoResultMap = true)
+@KeySequence("ai_workflow") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class AiWorkflowDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 工作流名称
+     */
+    private String name;
+    /**
+     * 工作流标识
+     */
+    private String code;
+
+    /**
+     * 工作流模型 JSON 数据
+     */
+    private String graph;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 状态
+     *
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/write/AiWriteDO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/write/AiWriteDO.java
new file mode 100644
index 0000000..d773c05
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/dataobject/write/AiWriteDO.java
@@ -0,0 +1,104 @@
+package com.iailab.module.ai.dal.dataobject.write;
+
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.mybatis.core.dataobject.BaseDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.enums.DictTypeConstants;
+import com.iailab.module.ai.enums.write.AiWriteTypeEnum;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * AI 写作 DO
+ *
+ * @author xiaoxin
+ */
+@TableName("ai_write")
+@KeySequence("ai_write_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class AiWriteDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 用户编号
+     *
+     * 关联 AdminUserDO 的 userId 字段
+     */
+    private Long userId;
+
+    /**
+     * 写作类型
+     * <p>
+     * 枚举 {@link AiWriteTypeEnum}
+     */
+    private Integer type;
+
+    /**
+     * 平台
+     *
+     * 枚举 {@link AiPlatformEnum}
+     */
+    private String platform;
+    /**
+     * 模型编号
+     *
+     * 关联 {@link AiModelDO#getId()}
+     */
+    private Long modelId;
+    /**
+     * 模型
+     */
+    private String model;
+
+    /**
+     * 生成内容提示
+     */
+    private String prompt;
+
+    /**
+     * 生成的内容
+     */
+    private String generatedContent;
+    /**
+     * 原文
+     */
+    private String originalContent;
+
+    /**
+     * 长度提示词
+     *
+     * 字典:{@link DictTypeConstants#AI_WRITE_LENGTH}
+     */
+    private Integer length;
+    /**
+     * 格式提示词
+     *
+     * 字典:{@link DictTypeConstants#AI_WRITE_FORMAT}
+     */
+    private Integer format;
+    /**
+     * 语气提示词
+     *
+     * 字典:{@link DictTypeConstants#AI_WRITE_TONE}
+     */
+    private Integer tone;
+    /**
+     * 语言提示词
+     *
+     * 字典:{@link DictTypeConstants#AI_WRITE_LANGUAGE}
+     */
+    private Integer language;
+
+    /**
+     * 错误信息
+     */
+    private String errorMessage;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/chat/AiChatConversationMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/chat/AiChatConversationMapper.java
new file mode 100644
index 0000000..951e16d
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/chat/AiChatConversationMapper.java
@@ -0,0 +1,44 @@
+package com.iailab.module.ai.dal.mysql.chat;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * AI 聊天对话 Mapper
+ *
+ * @author Iailab
+ */
+@Mapper
+public interface AiChatConversationMapper extends BaseMapperX<AiChatConversationDO> {
+
+    default List<AiChatConversationDO> selectListByUserId(Long userId) {
+        return selectList(AiChatConversationDO::getUserId, userId);
+    }
+
+    default List<AiChatConversationDO> selectListByModel(Long userId, Long modelId) {
+        return selectList(new LambdaQueryWrapperX<AiChatConversationDO>()
+                .eq(AiChatConversationDO::getUserId, userId)
+                .eq(AiChatConversationDO::getModelId, modelId));
+    }
+
+    default List<AiChatConversationDO> selectListByUserIdAndPinned(Long userId, boolean pinned) {
+        return selectList(new LambdaQueryWrapperX<AiChatConversationDO>()
+                .eq(AiChatConversationDO::getUserId, userId)
+                .eq(AiChatConversationDO::getPinned, pinned));
+    }
+
+    default PageResult<AiChatConversationDO> selectChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
+        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatConversationDO>()
+                .eqIfPresent(AiChatConversationDO::getUserId, pageReqVO.getUserId())
+                .likeIfPresent(AiChatConversationDO::getTitle, pageReqVO.getTitle())
+                .betweenIfPresent(AiChatConversationDO::getCreateTime, pageReqVO.getCreateTime())
+                .orderByDesc(AiChatConversationDO::getId));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/chat/AiChatMessageMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/chat/AiChatMessageMapper.java
new file mode 100644
index 0000000..61ff79c
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/chat/AiChatMessageMapper.java
@@ -0,0 +1,57 @@
+package com.iailab.module.ai.dal.mysql.chat;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.collection.CollectionUtils;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatMessageDO;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * AI 聊天对话 Mapper
+ *
+ * @author fansili
+ */
+@Mapper
+public interface AiChatMessageMapper extends BaseMapperX<AiChatMessageDO> {
+
+    default List<AiChatMessageDO> selectListByConversationId(Long conversationId) {
+        return selectList(new LambdaQueryWrapperX<AiChatMessageDO>()
+                .eq(AiChatMessageDO::getConversationId, conversationId)
+                .orderByAsc(AiChatMessageDO::getId));
+    }
+
+    default Map<Long, Integer> selectCountMapByConversationId(Collection<Long> conversationIds) {
+        // SQL count 查询
+        List<Map<String, Object>> result = selectMaps(new QueryWrapper<AiChatMessageDO>()
+                .select("COUNT(id) AS count, conversation_id AS conversationId")
+                .in("conversation_id", conversationIds)
+                .groupBy("conversation_id"));
+        if (CollUtil.isEmpty(result)) {
+            return Collections.emptyMap();
+        }
+        // 转换数据
+        return CollectionUtils.convertMap(result,
+                record -> MapUtil.getLong(record, "conversationId"),
+                record -> MapUtil.getInt(record, "count" ));
+    }
+
+    default PageResult<AiChatMessageDO> selectPage(AiChatMessagePageReqVO pageReqVO) {
+        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatMessageDO>()
+                .eqIfPresent(AiChatMessageDO::getConversationId, pageReqVO.getConversationId())
+                .eqIfPresent(AiChatMessageDO::getUserId, pageReqVO.getUserId())
+                .likeIfPresent(AiChatMessageDO::getContent, pageReqVO.getContent())
+                .betweenIfPresent(AiChatMessageDO::getCreateTime, pageReqVO.getCreateTime())
+                .orderByDesc(AiChatMessageDO::getId));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/image/AiImageMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/image/AiImageMapper.java
new file mode 100644
index 0000000..3132b52
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/image/AiImageMapper.java
@@ -0,0 +1,57 @@
+package com.iailab.module.ai.dal.mysql.image;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.image.vo.AiImagePageReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;
+import com.iailab.module.ai.dal.dataobject.image.AiImageDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * AI 绘图 Mapper
+ *
+ * @author fansili
+ */
+@Mapper
+public interface AiImageMapper extends BaseMapperX<AiImageDO> {
+
+    default AiImageDO selectByTaskId(String taskId) {
+        return selectOne(AiImageDO::getTaskId, taskId);
+    }
+
+    default PageResult<AiImageDO> selectPage(AiImagePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiImageDO>()
+                .eqIfPresent(AiImageDO::getUserId, reqVO.getUserId())
+                .eqIfPresent(AiImageDO::getPlatform, reqVO.getPlatform())
+                .eqIfPresent(AiImageDO::getStatus, reqVO.getStatus())
+                .eqIfPresent(AiImageDO::getPublicStatus, reqVO.getPublicStatus())
+                .betweenIfPresent(AiImageDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(AiImageDO::getId));
+    }
+
+    default PageResult<AiImageDO> selectPageMy(Long userId, AiImagePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiImageDO>()
+                .likeIfPresent(AiImageDO::getPrompt, reqVO.getPrompt())
+                // 情况一:公开
+                .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiImageDO::getPublicStatus, reqVO.getPublicStatus())
+                // 情况二:私有
+                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiImageDO::getUserId, userId)
+                .orderByDesc(AiImageDO::getId));
+    }
+
+    default PageResult<AiImageDO> selectPage(AiImagePublicPageReqVO pageReqVO) {
+        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiImageDO>()
+                .eqIfPresent(AiImageDO::getPublicStatus, Boolean.TRUE)
+                .likeIfPresent(AiImageDO::getPrompt, pageReqVO.getPrompt())
+                .orderByDesc(AiImageDO::getId));
+    }
+
+    default List<AiImageDO> selectListByStatusAndPlatform(Integer status, String platform) {
+        return selectList(AiImageDO::getStatus, status,
+                AiImageDO::getPlatform, platform);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java
new file mode 100644
index 0000000..730be7c
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java
@@ -0,0 +1,39 @@
+package com.iailab.module.ai.dal.mysql.knowledge;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * AI 知识库文档 Mapper
+ *
+ * @author xiaoxin
+ */
+@Mapper
+public interface AiKnowledgeDocumentMapper extends BaseMapperX<AiKnowledgeDocumentDO> {
+
+    default PageResult<AiKnowledgeDocumentDO> selectPage(AiKnowledgeDocumentPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeDocumentDO>()
+                .eqIfPresent(AiKnowledgeDocumentDO::getKnowledgeId, reqVO.getKnowledgeId())
+                .likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName())
+                .orderByDesc(AiKnowledgeDocumentDO::getId));
+    }
+
+    default void updateRetrievalCountIncr(Collection<Long> ids) {
+        update(new LambdaUpdateWrapper<AiKnowledgeDocumentDO>()
+                .setSql(" retrieval_count = retrieval_count + 1")
+                .in(AiKnowledgeDocumentDO::getId, ids));
+    }
+
+    default List<AiKnowledgeDocumentDO> selectListByStatus(Integer status) {
+        return selectList(AiKnowledgeDocumentDO::getStatus, status);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
new file mode 100644
index 0000000..12c77d0
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
@@ -0,0 +1,32 @@
+package com.iailab.module.ai.dal.mysql.knowledge;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * AI 知识库 Mapper
+ *
+ * @author xiaoxin
+ */
+@Mapper
+public interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> {
+
+    default PageResult<AiKnowledgeDO> selectPage(AiKnowledgePageReqVO pageReqVO) {
+        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiKnowledgeDO>()
+                .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName())
+                .eqIfPresent(AiKnowledgeDO::getStatus, pageReqVO.getStatus())
+                .betweenIfPresent(AiKnowledgeDO::getCreateTime, pageReqVO.getCreateTime())
+                .orderByDesc(AiKnowledgeDO::getId));
+    }
+
+    default List<AiKnowledgeDO> selectListByStatus(Integer status) {
+        return selectList(AiKnowledgeDO::getStatus, status);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
new file mode 100644
index 0000000..a05d082
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
@@ -0,0 +1,67 @@
+package com.iailab.module.ai.dal.mysql.knowledge;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.framework.mybatis.core.query.MPJLambdaWrapperX;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * AI 知识库分片 Mapper
+ *
+ * @author xiaoxin
+ */
+@Mapper
+public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegmentDO> {
+
+    default PageResult<AiKnowledgeSegmentDO> selectPage(AiKnowledgeSegmentPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
+                .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId())
+                .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getContent())
+                .eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus())
+                .orderByDesc(AiKnowledgeSegmentDO::getId));
+    }
+
+    default List<AiKnowledgeSegmentDO> selectListByVectorIds(List<String> vectorIdList) {
+        return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
+                .in(AiKnowledgeSegmentDO::getVectorId, vectorIdList)
+                .orderByDesc(AiKnowledgeSegmentDO::getId));
+    }
+
+    default List<AiKnowledgeSegmentDO> selectListByDocumentId(Long documentId) {
+        return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
+                .eq(AiKnowledgeSegmentDO::getDocumentId, documentId)
+                .orderByDesc(AiKnowledgeSegmentDO::getId));
+    }
+
+    default List<AiKnowledgeSegmentDO> selectListByKnowledgeIdAndStatus(Long knowledgeId, Integer status) {
+        return selectList(AiKnowledgeSegmentDO::getKnowledgeId, knowledgeId,
+                AiKnowledgeSegmentDO::getStatus, status);
+    }
+
+    default List<AiKnowledgeSegmentProcessRespVO> selectProcessList(Collection<Long> documentIds) {
+        MPJLambdaWrapper<AiKnowledgeSegmentDO> wrapper = new MPJLambdaWrapperX<AiKnowledgeSegmentDO>()
+                .selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId)
+                .selectCount(AiKnowledgeSegmentDO::getId, "count")
+                .select("COUNT(CASE WHEN vector_id > '" + AiKnowledgeSegmentDO.VECTOR_ID_EMPTY
+                        + "' THEN 1 ELSE NULL END) AS embeddingCount")
+                .in(AiKnowledgeSegmentDO::getDocumentId, documentIds)
+                .groupBy(AiKnowledgeSegmentDO::getDocumentId);
+        return selectJoinList(AiKnowledgeSegmentProcessRespVO.class, wrapper);
+    }
+
+    default void updateRetrievalCountIncrByIds(List<Long> ids) {
+        update(new LambdaUpdateWrapper<AiKnowledgeSegmentDO>()
+                .setSql(" retrieval_count = retrieval_count + 1")
+                .in(AiKnowledgeSegmentDO::getId, ids));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/mindmap/AiMindMapMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/mindmap/AiMindMapMapper.java
new file mode 100644
index 0000000..ea69aaf
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/mindmap/AiMindMapMapper.java
@@ -0,0 +1,26 @@
+package com.iailab.module.ai.dal.mysql.mindmap;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
+import com.iailab.module.ai.dal.dataobject.mindmap.AiMindMapDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * AI 思维导图 Mapper
+ *
+ * @author xiaoxin
+ */
+@Mapper
+public interface AiMindMapMapper extends BaseMapperX<AiMindMapDO> {
+
+    default PageResult<AiMindMapDO> selectPage(AiMindMapPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiMindMapDO>()
+                .eqIfPresent(AiMindMapDO::getUserId, reqVO.getUserId())
+                .eqIfPresent(AiMindMapDO::getPrompt, reqVO.getPrompt())
+                .betweenIfPresent(AiMindMapDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(AiMindMapDO::getId));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiApiKeyMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiApiKeyMapper.java
new file mode 100644
index 0000000..9bd8a1f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiApiKeyMapper.java
@@ -0,0 +1,35 @@
+package com.iailab.module.ai.dal.mysql.model;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.framework.mybatis.core.query.QueryWrapperX;
+import com.iailab.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiApiKeyDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * AI API 密钥 Mapper
+ *
+ * @author Iailab
+ */
+@Mapper
+public interface AiApiKeyMapper extends BaseMapperX<AiApiKeyDO> {
+
+    default PageResult<AiApiKeyDO> selectPage(AiApiKeyPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiApiKeyDO>()
+                .likeIfPresent(AiApiKeyDO::getName, reqVO.getName())
+                .eqIfPresent(AiApiKeyDO::getPlatform, reqVO.getPlatform())
+                .eqIfPresent(AiApiKeyDO::getStatus, reqVO.getStatus())
+                .orderByDesc(AiApiKeyDO::getId));
+    }
+
+    default AiApiKeyDO selectFirstByPlatformAndStatus(String platform, Integer status) {
+        return selectOne(new QueryWrapperX<AiApiKeyDO>()
+                .eq("platform", platform)
+                .eq("status", status)
+                .limitN(1)
+                .orderByAsc("id"));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiChatMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiChatMapper.java
new file mode 100644
index 0000000..da8a51f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiChatMapper.java
@@ -0,0 +1,47 @@
+package com.iailab.module.ai.dal.mysql.model;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.framework.mybatis.core.query.QueryWrapperX;
+import com.iailab.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+/**
+ * API 模型 Mapper
+ *
+ * @author fansili
+ */
+@Mapper
+public interface AiChatMapper extends BaseMapperX<AiModelDO> {
+
+    default AiModelDO selectFirstByStatus(Integer type, Integer status) {
+        return selectOne(new QueryWrapperX<AiModelDO>()
+                .eq("type", type)
+                .eq("status", status)
+                .limitN(1)
+                .orderByAsc("sort"));
+    }
+
+    default PageResult<AiModelDO> selectPage(AiModelPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiModelDO>()
+                .likeIfPresent(AiModelDO::getName, reqVO.getName())
+                .eqIfPresent(AiModelDO::getModel, reqVO.getModel())
+                .eqIfPresent(AiModelDO::getPlatform, reqVO.getPlatform())
+                .orderByAsc(AiModelDO::getSort));
+    }
+
+    default List<AiModelDO> selectListByStatusAndType(Integer status, Integer type,
+                                                      @Nullable String platform) {
+        return selectList(new LambdaQueryWrapperX<AiModelDO>()
+                .eq(AiModelDO::getStatus, status)
+                .eq(AiModelDO::getType, type)
+                .eqIfPresent(AiModelDO::getPlatform, platform)
+                .orderByAsc(AiModelDO::getSort));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiChatRoleMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiChatRoleMapper.java
new file mode 100644
index 0000000..9950b17
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiChatRoleMapper.java
@@ -0,0 +1,54 @@
+package com.iailab.module.ai.dal.mysql.model;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * AI 聊天角色 Mapper
+ *
+ * @author fansili
+ */
+@Mapper
+public interface AiChatRoleMapper extends BaseMapperX<AiChatRoleDO> {
+
+    default PageResult<AiChatRoleDO> selectPage(AiChatRolePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiChatRoleDO>()
+                .likeIfPresent(AiChatRoleDO::getName, reqVO.getName())
+                .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory())
+                .eqIfPresent(AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus())
+                .orderByAsc(AiChatRoleDO::getSort));
+    }
+
+    default PageResult<AiChatRoleDO> selectPageByMy(AiChatRolePageReqVO reqVO, Long userId) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiChatRoleDO>()
+                .likeIfPresent(AiChatRoleDO::getName, reqVO.getName())
+                .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory())
+                // 情况一:公开
+                .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus())
+                // 情况二:私有
+                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getUserId, userId)
+                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
+                .orderByAsc(AiChatRoleDO::getSort));
+    }
+
+    default List<AiChatRoleDO> selectListGroupByCategory(Integer status) {
+        return selectList(new LambdaQueryWrapperX<AiChatRoleDO>()
+                .select(AiChatRoleDO::getCategory)
+                .eq(AiChatRoleDO::getStatus, status)
+                .groupBy(AiChatRoleDO::getCategory));
+    }
+
+    default List<AiChatRoleDO> selectListByName(String name) {
+        return selectList(new LambdaQueryWrapperX<AiChatRoleDO>()
+                .likeIfPresent(AiChatRoleDO::getName, name)
+                .orderByAsc(AiChatRoleDO::getSort));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiToolMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiToolMapper.java
new file mode 100644
index 0000000..1846979
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/model/AiToolMapper.java
@@ -0,0 +1,35 @@
+package com.iailab.module.ai.dal.mysql.model;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiToolDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * AI 工具 Mapper
+ *
+ * @author Iailab
+ */
+@Mapper
+public interface AiToolMapper extends BaseMapperX<AiToolDO> {
+
+    default PageResult<AiToolDO> selectPage(AiToolPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiToolDO>()
+                .likeIfPresent(AiToolDO::getName, reqVO.getName())
+                .eqIfPresent(AiToolDO::getDescription, reqVO.getDescription())
+                .eqIfPresent(AiToolDO::getStatus, reqVO.getStatus())
+                .betweenIfPresent(AiToolDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(AiToolDO::getId));
+    }
+
+    default List<AiToolDO> selectListByStatus(Integer status) {
+        return selectList(new LambdaQueryWrapperX<AiToolDO>()
+                .eq(AiToolDO::getStatus, status)
+                .orderByDesc(AiToolDO::getId));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/music/AiMusicMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/music/AiMusicMapper.java
new file mode 100644
index 0000000..0ad3662
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/music/AiMusicMapper.java
@@ -0,0 +1,44 @@
+package com.iailab.module.ai.dal.mysql.music;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
+import com.iailab.module.ai.dal.dataobject.music.AiMusicDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * AI 音乐 Mapper
+ *
+ * @author xiaoxin
+ */
+@Mapper
+public interface AiMusicMapper extends BaseMapperX<AiMusicDO> {
+
+    default List<AiMusicDO> selectListByStatus(Integer status) {
+        return selectList(AiMusicDO::getStatus, status);
+    }
+
+    default PageResult<AiMusicDO> selectPage(AiMusicPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()
+                .eqIfPresent(AiMusicDO::getUserId, reqVO.getUserId())
+                .eqIfPresent(AiMusicDO::getTitle, reqVO.getTitle())
+                .eqIfPresent(AiMusicDO::getStatus, reqVO.getStatus())
+                .eqIfPresent(AiMusicDO::getGenerateMode, reqVO.getGenerateMode())
+                .betweenIfPresent(AiMusicDO::getCreateTime, reqVO.getCreateTime())
+                .eqIfPresent(AiMusicDO::getPublicStatus, reqVO.getPublicStatus())
+                .orderByDesc(AiMusicDO::getId));
+    }
+
+    default PageResult<AiMusicDO> selectPageByMy(AiMusicPageReqVO reqVO, Long userId) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()
+                // 情况一:公开
+                .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiMusicDO::getPublicStatus, reqVO.getPublicStatus())
+                // 情况二:私有
+                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiMusicDO::getUserId, userId)
+                .orderByAsc(AiMusicDO::getId));
+    }
+    
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/questionparamsetting/QuestionParamSettingMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/questionparamsetting/QuestionParamSettingMapper.java
new file mode 100644
index 0000000..07e7473
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/questionparamsetting/QuestionParamSettingMapper.java
@@ -0,0 +1,28 @@
+package com.iailab.module.ai.dal.mysql.questionparamsetting;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.module.ai.dal.dataobject.questionparamsetting.QuestionParamSettingDO;
+import org.apache.ibatis.annotations.Mapper;
+import com.iailab.module.ai.controller.admin.questionparamsetting.vo.*;
+
+/**
+ * 大模型问题设置参数 Mapper
+ *
+ * @author 超级管理员
+ */
+@Mapper
+public interface QuestionParamSettingMapper extends BaseMapperX<QuestionParamSettingDO> {
+
+    default PageResult<QuestionParamSettingDO> selectPage(QuestionParamSettingPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<QuestionParamSettingDO>()
+                .eqIfPresent(QuestionParamSettingDO::getTemplateId, reqVO.getTemplateId())
+                .eqIfPresent(QuestionParamSettingDO::getSettingKey, reqVO.getSettingKey())
+                .likeIfPresent(QuestionParamSettingDO::getSettingName, reqVO.getSettingName())
+                .eqIfPresent(QuestionParamSettingDO::getSettingValue, reqVO.getSettingValue())
+                .eqIfPresent(QuestionParamSettingDO::getSort, reqVO.getSort())
+                .orderByDesc(QuestionParamSettingDO::getId));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/questiontemplate/QuestionTemplateMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/questiontemplate/QuestionTemplateMapper.java
new file mode 100644
index 0000000..eefa331
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/questiontemplate/QuestionTemplateMapper.java
@@ -0,0 +1,30 @@
+package com.iailab.module.ai.dal.mysql.questiontemplate;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.module.ai.dal.dataobject.questiontemplate.QuestionTemplateDO;
+import org.apache.ibatis.annotations.Mapper;
+import com.iailab.module.ai.controller.admin.questiontemplate.vo.*;
+
+/**
+ * 大模型问题模板 Mapper
+ *
+ * @author 超级管理员
+ */
+@Mapper
+public interface QuestionTemplateMapper extends BaseMapperX<QuestionTemplateDO> {
+
+    default PageResult<QuestionTemplateDO> selectPage(QuestionTemplatePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<QuestionTemplateDO>()
+                .eqIfPresent(QuestionTemplateDO::getModelId, reqVO.getModelId())
+                .eqIfPresent(QuestionTemplateDO::getQuestionCode, reqVO.getQuestionCode())
+                .likeIfPresent(QuestionTemplateDO::getQuestionName, reqVO.getQuestionName())
+                .eqIfPresent(QuestionTemplateDO::getQuestionContent, reqVO.getQuestionContent())
+                .eqIfPresent(QuestionTemplateDO::getDataLength, reqVO.getDataLength())
+                .eqIfPresent(QuestionTemplateDO::getIsEnable, reqVO.getIsEnable())
+                .eqIfPresent(QuestionTemplateDO::getRemark, reqVO.getRemark())
+                .orderByDesc(QuestionTemplateDO::getId));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/workflow/AiWorkflowMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/workflow/AiWorkflowMapper.java
new file mode 100644
index 0000000..49ac104
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/workflow/AiWorkflowMapper.java
@@ -0,0 +1,30 @@
+package com.iailab.module.ai.dal.mysql.workflow;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO;
+import com.iailab.module.ai.dal.dataobject.workflow.AiWorkflowDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * AI 工作流 Mapper
+ *
+ * @author lesan
+ */
+@Mapper
+public interface AiWorkflowMapper extends BaseMapperX<AiWorkflowDO> {
+
+    default AiWorkflowDO selectByCode(String code) {
+        return selectOne(AiWorkflowDO::getCode, code);
+    }
+
+    default PageResult<AiWorkflowDO> selectPage(AiWorkflowPageReqVO pageReqVO) {
+        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiWorkflowDO>()
+                .eqIfPresent(AiWorkflowDO::getStatus, pageReqVO.getStatus())
+                .likeIfPresent(AiWorkflowDO::getName, pageReqVO.getName())
+                .likeIfPresent(AiWorkflowDO::getCode, pageReqVO.getCode())
+                .betweenIfPresent(AiWorkflowDO::getCreateTime, pageReqVO.getCreateTime()));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/write/AiWriteMapper.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/write/AiWriteMapper.java
new file mode 100644
index 0000000..4a2e1c2
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/write/AiWriteMapper.java
@@ -0,0 +1,27 @@
+package com.iailab.module.ai.dal.mysql.write;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.mybatis.core.mapper.BaseMapperX;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.write.vo.AiWritePageReqVO;
+import com.iailab.module.ai.dal.dataobject.write.AiWriteDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * AI 写作 Mapper
+ *
+ * @author xiaoxin
+ */
+@Mapper
+public interface AiWriteMapper extends BaseMapperX<AiWriteDO> {
+
+    default PageResult<AiWriteDO> selectPage(AiWritePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiWriteDO>()
+                .eqIfPresent(AiWriteDO::getUserId, reqVO.getUserId())
+                .eqIfPresent(AiWriteDO::getType, reqVO.getType())
+                .eqIfPresent(AiWriteDO::getPlatform, reqVO.getPlatform())
+                .betweenIfPresent(AiWriteDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(AiWriteDO::getId));
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/package-info.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/package-info.java
new file mode 100644
index 0000000..3e6bb61
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 属于 ai 模块的 framework 封装
+ *
+ * @author Iailab
+ */
+package com.iailab.module.ai.framework;
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/rpc/config/RpcConfiguration.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/rpc/config/RpcConfiguration.java
new file mode 100644
index 0000000..b6a10a8
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/rpc/config/RpcConfiguration.java
@@ -0,0 +1,11 @@
+package com.iailab.module.ai.framework.rpc.config;
+
+import com.iailab.module.infra.api.file.FileApi;
+import com.iailab.module.system.api.dict.DictDataApi;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration(proxyBeanMethods = false)
+@EnableFeignClients(clients = {DictDataApi.class, FileApi.class})
+public class RpcConfiguration {
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/rpc/package-info.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/rpc/package-info.java
new file mode 100644
index 0000000..ed9c251
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/rpc/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 占位
+ */
+package com.iailab.module.ai.framework.rpc;
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/security/config/SecurityConfiguration.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/security/config/SecurityConfiguration.java
new file mode 100644
index 0000000..c061d89
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/security/config/SecurityConfiguration.java
@@ -0,0 +1,41 @@
+package com.iailab.module.ai.framework.security.config;
+
+import com.iailab.framework.security.config.AuthorizeRequestsCustomizer;
+import com.iailab.module.infra.enums.ApiConstants;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
+
+/**
+ * AI 模块的 Security 配置
+ */
+@Configuration(proxyBeanMethods = false, value = "aiSecurityConfiguration")
+public class SecurityConfiguration {
+
+    @Bean("aiAuthorizeRequestsCustomizer")
+    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
+        return new AuthorizeRequestsCustomizer() {
+
+            @Override
+            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
+                // Swagger 接口文档
+                registry.requestMatchers("/v3/api-docs/**").permitAll()
+                        .requestMatchers("/webjars/**").permitAll()
+                        .requestMatchers("/swagger-ui").permitAll()
+                        .requestMatchers("/swagger-ui/**").permitAll();
+                // Spring Boot Actuator 的安全配置
+                registry.requestMatchers("/actuator").permitAll()
+                        .requestMatchers("/actuator/**").permitAll();
+                // Druid 监控
+                registry.requestMatchers("/druid/**").permitAll();
+
+                // TODO Iailab:这个每个项目都需要重复配置,得捉摸有没通用的方案
+                // RPC 服务的安全配置
+                registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
+            }
+
+        };
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/security/core/package-info.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/security/core/package-info.java
new file mode 100644
index 0000000..62ca02f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/framework/security/core/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 占位
+ */
+package com.iailab.module.ai.framework.security.core;
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/image/AiMidjourneySyncJob.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/image/AiMidjourneySyncJob.java
new file mode 100644
index 0000000..3bcb221
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/image/AiMidjourneySyncJob.java
@@ -0,0 +1,27 @@
+//package com.iailab.module.ai.job.image;
+//
+//import com.iailab.module.ai.service.image.AiImageService;
+//import com.xxl.job.core.handler.annotation.XxlJob;
+//import jakarta.annotation.Resource;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.stereotype.Component;
+//
+///**
+// * Midjourney 同步 Job:定时拉去 midjourney 绘制状态
+// *
+// * @author fansili
+// */
+//@Component
+//@Slf4j
+//public class AiMidjourneySyncJob {
+//
+//    @Resource
+//    private AiImageService imageService;
+//
+//    @XxlJob("aiMidjourneySyncJob")
+//    public void execute(String param) {
+//        Integer count = imageService.midjourneySync();
+//        log.info("[execute][同步 Midjourney ({}) 个]", count);
+//    }
+//
+//}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/music/AiSunoSyncJob.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/music/AiSunoSyncJob.java
new file mode 100644
index 0000000..e75afe2
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/music/AiSunoSyncJob.java
@@ -0,0 +1,28 @@
+//package com.iailab.module.ai.job.music;
+//
+//import com.iailab.module.ai.service.music.AiMusicService;
+//import com.xxl.job.core.handler.annotation.XxlJob;
+//import jakarta.annotation.Resource;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.stereotype.Component;
+//
+//
+///**
+// * 同步 Suno 任务状态以及回写对应的音乐信息 Job
+// *
+// * @author xiaoxin
+// */
+//@Component
+//@Slf4j
+//public class AiSunoSyncJob {
+//
+//    @Resource
+//    private AiMusicService musicService;
+//
+//    @XxlJob("aiSunoSyncJob")
+//    public void execute(String param) {
+//        Integer count = musicService.syncMusic();
+//        log.info("[execute][同步 Suno ({}) 个]", count);
+//    }
+//
+//}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/package-info.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/package-info.java
new file mode 100644
index 0000000..604aac8
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * ai 模块下,接入 LLM 大模型,支持聊天、绘图、音乐、写作、思维导图等功能。
+ * 目前已接入各种模型,不限于:
+ * 国内:通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek
+ * 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno
+ *
+ * 1. Controller URL:以 /ai/ 开头,避免和其它 Module 冲突
+ * 2. DataObject 表名:以 ai_ 开头,方便在数据库中区分
+ */
+package com.iailab.module.ai;
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationService.java
new file mode 100644
index 0000000..3fae905
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationService.java
@@ -0,0 +1,106 @@
+package com.iailab.module.ai.service.chat;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateEnergyReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO;
+
+import java.util.List;
+
+/**
+ * AI 聊天对话 Service 接口
+ *
+ * @author fansili
+ */
+public interface AiChatConversationService {
+
+    /**
+     * 创建【我的】聊天对话
+     *
+     * @param createReqVO 创建信息
+     * @param userId 用户编号
+     * @return 编号
+     */
+    Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId);
+
+    /**
+     * 创建【工业大模型】聊天对话
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createChatConversationEnergy(AiChatConversationCreateEnergyReqVO createReqVO);
+
+    /**
+     * 更新【我的】聊天对话
+     *
+     * @param updateReqVO 更新信息
+     * @param userId 用户编号
+     */
+    void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId);
+
+    /**
+     * 获得【我的】聊天对话列表
+     *
+     * @param userId 用户编号
+     * @return 聊天对话列表
+     */
+    List<AiChatConversationDO> getChatConversationListByUserId(Long userId);
+
+    /**
+     * 获得【工业大模型】聊天对话列表
+     *
+     * @param userId 用户编号
+     * @return 聊天对话列表
+     */
+    List<AiChatConversationDO> getChatConversationList(Long userId, String modelName);
+
+    /**
+     * 获得聊天对话
+     *
+     * @param id 编号
+     * @return 聊天对话
+     */
+    AiChatConversationDO getChatConversation(Long id);
+
+    /**
+     * 删除【我的】聊天对话
+     *
+     * @param id 编号
+     * @param userId 用户编号
+     */
+    void deleteChatConversationMy(Long id, Long userId);
+
+    /**
+     * 【管理员】删除聊天对话
+     *
+     * @param id 编号
+     */
+    void deleteChatConversationByAdmin(Long id);
+
+    /**
+     * 校验聊天对话是否存在
+     *
+     * @param id 编号
+     * @return 聊天对话
+     */
+    AiChatConversationDO validateChatConversationExists(Long id);
+
+    /**
+     * 删除【我的】 + 非置顶的聊天对话
+     *
+     * @param userId 用户编号
+     */
+    void deleteChatConversationMyByUnpinned(Long userId);
+
+    /**
+     * 获得聊天对话的分页列表
+     *
+     * @param pageReqVO 分页查询
+     * @return 聊天对话的分页列表
+     */
+    PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java
new file mode 100644
index 0000000..f2a15e7
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java
@@ -0,0 +1,201 @@
+package com.iailab.module.ai.service.chat;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.iailab.framework.ai.core.enums.AiModelTypeEnum;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateEnergyReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.iailab.module.ai.dal.mysql.chat.AiChatConversationMapper;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeService;
+import com.iailab.module.ai.service.model.AiModelService;
+import com.iailab.module.ai.service.model.AiChatRoleService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
+
+/**
+ * AI 聊天对话 Service 实现类
+ *
+ * @author fansili
+ */
+@Service
+@Validated
+@Slf4j
+public class AiChatConversationServiceImpl implements AiChatConversationService {
+
+    @Resource
+    private AiChatConversationMapper chatConversationMapper;
+
+    @Resource
+    private AiModelService modalService;
+    @Resource
+    private AiChatRoleService chatRoleService;
+    @Resource
+    private AiKnowledgeService knowledgeService;
+
+    @Override
+    public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {
+        // 1.1 获得 AiChatRoleDO 聊天角色
+        AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null;
+        // 1.2 获得 AiModelDO 聊天模型
+        AiModelDO model = role != null && role.getModelId() != null ? modalService.validateModel(role.getModelId())
+                : modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
+        Assert.notNull(model, "必须找到默认模型");
+        validateChatModel(model);
+
+        // 1.3 校验知识库
+        if (Objects.nonNull(createReqVO.getKnowledgeId())) {
+            knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
+        }
+
+        // 2. 创建 AiChatConversationDO 聊天对话
+        AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false)
+                .setModelId(model.getId()).setModel(model.getModel())
+                .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
+        if (role != null) {
+            conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage());
+        } else {
+            conversation.setTitle(AiChatConversationDO.TITLE_DEFAULT);
+        }
+        chatConversationMapper.insert(conversation);
+        return conversation.getId();
+    }
+
+    @Override
+    public Long createChatConversationEnergy(AiChatConversationCreateEnergyReqVO createReqVO) {
+        // 1 获得 AiModelDO 聊天模型
+        AiModelDO model = modalService.getModelByName(createReqVO.getModelName());
+        Assert.notNull(model, "必须找到默认模型");
+        validateChatModel(model);
+
+        // 2. 创建 AiChatConversationDO 聊天对话
+        AiChatConversationDO conversation = new AiChatConversationDO().setUserId(getLoginUserId()).setPinned(false)
+                .setModelId(model.getId()).setModel(model.getModel())
+                .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
+        conversation.setTitle("新对话");
+        chatConversationMapper.insert(conversation);
+        return conversation.getId();
+    }
+
+    @Override
+    public void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId) {
+        // 1.1 校验对话是否存在
+        AiChatConversationDO conversation = validateChatConversationExists(updateReqVO.getId());
+        if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
+            throw exception(CHAT_CONVERSATION_NOT_EXISTS);
+        }
+        // 1.2 校验模型是否存在(修改模型的情况)
+        AiModelDO model = null;
+        if (updateReqVO.getModelId() != null) {
+            model = modalService.validateModel(updateReqVO.getModelId());
+        }
+
+        // 1.3 校验知识库是否存在
+        if (updateReqVO.getKnowledgeId() != null) {
+            knowledgeService.validateKnowledgeExists(updateReqVO.getKnowledgeId());
+        }
+
+        // 2. 更新对话信息
+        AiChatConversationDO updateObj = BeanUtils.toBean(updateReqVO, AiChatConversationDO.class);
+        if (Boolean.TRUE.equals(updateReqVO.getPinned())) {
+            updateObj.setPinnedTime(LocalDateTime.now());
+        }
+        if (model != null) {
+            updateObj.setModel(model.getModel());
+        }
+        chatConversationMapper.updateById(updateObj);
+    }
+
+    @Override
+    public List<AiChatConversationDO> getChatConversationListByUserId(Long userId) {
+        return chatConversationMapper.selectListByUserId(userId);
+    }
+
+    @Override
+    public List<AiChatConversationDO> getChatConversationList(Long userId, String modelName) {
+        AiModelDO model = modalService.getModelByName(modelName);
+        if(model != null) {
+            return chatConversationMapper.selectListByModel(userId, model.getId());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public AiChatConversationDO getChatConversation(Long id) {
+        return chatConversationMapper.selectById(id);
+    }
+
+    @Override
+    public void deleteChatConversationMy(Long id, Long userId) {
+        // 1. 校验对话是否存在
+        AiChatConversationDO conversation = validateChatConversationExists(id);
+        if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), userId)) {
+            throw exception(CHAT_CONVERSATION_NOT_EXISTS);
+        }
+        // 2. 执行删除
+        chatConversationMapper.deleteById(id);
+    }
+
+    @Override
+    public void deleteChatConversationByAdmin(Long id) {
+        // 1. 校验对话是否存在
+        AiChatConversationDO conversation = validateChatConversationExists(id);
+        if (conversation == null) {
+            throw exception(CHAT_CONVERSATION_NOT_EXISTS);
+        }
+        // 2. 执行删除
+        chatConversationMapper.deleteById(id);
+    }
+
+    private void validateChatModel(AiModelDO model) {
+        if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
+            return;
+        }
+        Assert.equals(model.getType(), AiModelTypeEnum.CHAT.getType(), "模型类型不正确:" + model);
+        throw exception(CHAT_CONVERSATION_MODEL_ERROR);
+    }
+
+    public AiChatConversationDO validateChatConversationExists(Long id) {
+        AiChatConversationDO conversation = chatConversationMapper.selectById(id);
+        if (conversation == null) {
+            throw exception(CHAT_CONVERSATION_NOT_EXISTS);
+        }
+        return conversation;
+    }
+
+    @Override
+    public void deleteChatConversationMyByUnpinned(Long userId) {
+        List<AiChatConversationDO> list = chatConversationMapper.selectListByUserIdAndPinned(userId, false);
+        if (CollUtil.isEmpty(list)) {
+            return;
+        }
+        chatConversationMapper.deleteBatchIds(convertList(list, AiChatConversationDO::getId));
+    }
+
+    @Override
+    public PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
+        return chatConversationMapper.selectChatConversationPage(pageReqVO);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageService.java
new file mode 100644
index 0000000..4b755bd
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageService.java
@@ -0,0 +1,87 @@
+package com.iailab.module.ai.service.chat;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatMessageDO;
+import reactor.core.publisher.Flux;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * AI 聊天消息 Service 接口
+ *
+ * @author fansili
+ */
+public interface AiChatMessageService {
+
+    /**
+     * 发送消息
+     *
+     * @param sendReqVO 发送信息
+     * @param userId 用户编号
+     * @return 发送结果
+     */
+    AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId);
+
+    /**
+     * 发送消息
+     *
+     * @param sendReqVO 发送信息
+     * @param userId 用户编号
+     * @return 发送结果
+     */
+    Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId);
+
+    /**
+     * 获得指定对话的消息列表
+     *
+     * @param conversationId 对话编号
+     * @return 消息列表
+     */
+    List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId);
+
+    /**
+     * 删除消息
+     *
+     * @param id 消息编号
+     * @param userId 用户编号
+     */
+    void deleteChatMessage(Long id, Long userId);
+
+    /**
+     * 删除指定对话的消息
+     *
+     * @param conversationId 对话编号
+     * @param userId 用户编号
+     */
+    void deleteChatMessageByConversationId(Long conversationId, Long userId);
+
+    /**
+     * 【管理员】删除消息
+     *
+     * @param id 消息编号
+     */
+    void deleteChatMessageByAdmin(Long id);
+
+    /**
+     * 获得聊天对话的消息数量 Map
+     *
+     * @param conversationIds 对话编号数组
+     * @return 消息数量 Map
+     */
+    Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds);
+
+    /**
+     * 获得聊天消息的分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 聊天消息的分页
+     */
+    PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageServiceImpl.java
new file mode 100644
index 0000000..0083759
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageServiceImpl.java
@@ -0,0 +1,364 @@
+package com.iailab.module.ai.service.chat;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.ai.core.util.AiUtils;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.tenant.core.util.TenantUtils;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
+import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO;
+import com.iailab.module.ai.dal.dataobject.chat.AiChatMessageDO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.dal.dataobject.model.AiToolDO;
+import com.iailab.module.ai.dal.mysql.chat.AiChatMessageMapper;
+import com.iailab.module.ai.enums.ErrorCodeConstants;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeDocumentService;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeSegmentService;
+import com.iailab.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
+import com.iailab.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
+import com.iailab.module.ai.service.model.AiChatRoleService;
+import com.iailab.module.ai.service.model.AiModelService;
+import com.iailab.module.ai.service.model.AiToolService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.MessageType;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.model.StreamingChatModel;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import reactor.core.publisher.Flux;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.framework.common.pojo.CommonResult.error;
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertSet;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST;
+
+/**
+ * AI 聊天消息 Service 实现类
+ *
+ * @author fansili
+ */
+@Service
+@Slf4j
+public class AiChatMessageServiceImpl implements AiChatMessageService {
+
+    /**
+     * 知识库转 {@link UserMessage} 的内容模版
+     */
+    private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = "使用 <Reference></Reference> 标记中的内容作为本次对话的参考:\n\n" +
+            "%s\n\n" + // 多个 <Reference></Reference> 的拼接
+            "回答要求:\n- 避免提及你是从 <Reference></Reference> 获取的知识。";
+
+    @Resource
+    private AiChatMessageMapper chatMessageMapper;
+
+    @Resource
+    private AiChatConversationService chatConversationService;
+    @Resource
+    private AiChatRoleService chatRoleService;
+    @Resource
+    private AiModelService modalService;
+    @Resource
+    private AiKnowledgeSegmentService knowledgeSegmentService;
+    @Resource
+    private AiKnowledgeDocumentService knowledgeDocumentService;
+    @Resource
+    private AiToolService toolService;
+
+    @Transactional(rollbackFor = Exception.class)
+    public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
+        // 1.1 校验对话存在
+        AiChatConversationDO conversation = chatConversationService
+                .validateChatConversationExists(sendReqVO.getConversationId());
+        if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
+            throw exception(CHAT_CONVERSATION_NOT_EXISTS);
+        }
+        List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
+        // 1.2 校验模型
+        AiModelDO model = modalService.validateModel(conversation.getModelId());
+        ChatModel chatModel = modalService.getChatModel(model.getId());
+
+        // 2. 知识库找回
+        List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),
+                conversation);
+
+        // 3. 插入 user 发送消息
+        AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
+                userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
+                null);
+
+        // 3.1 插入 assistant 接收消息
+        AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
+                userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
+                knowledgeSegments);
+
+        // 3.2 创建 chat 需要的 Prompt
+        Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
+        ChatResponse chatResponse = chatModel.call(prompt);
+
+        // 3.3 更新响应内容
+        String newContent = chatResponse.getResult().getOutput().getText();
+        chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));
+        // 3.4 响应结果
+        List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments,
+                AiChatMessageRespVO.KnowledgeSegment.class,
+                segment -> {
+                    AiKnowledgeDocumentDO document = knowledgeDocumentService
+                            .getKnowledgeDocument(segment.getDocumentId());
+                    segment.setDocumentName(document != null ? document.getName() : null);
+                });
+        return new AiChatMessageSendRespVO()
+                .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
+                .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
+                        .setContent(newContent).setSegments(segments));
+    }
+
+    @Override
+    public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO,
+            Long userId) {
+        // 1.1 校验对话存在
+        AiChatConversationDO conversation = chatConversationService
+                .validateChatConversationExists(sendReqVO.getConversationId());
+        if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
+            throw exception(CHAT_CONVERSATION_NOT_EXISTS);
+        }
+        List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
+        // 1.2 校验模型
+        AiModelDO model = modalService.validateModel(conversation.getModelId());
+        StreamingChatModel chatModel = modalService.getChatModel(model.getId());
+
+        // 2. 知识库找回
+        List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),
+                conversation);
+
+        // 3. 插入 user 发送消息
+        AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
+                userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
+                null);
+
+        // 4.1 插入 assistant 接收消息
+        AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
+                userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
+                knowledgeSegments);
+
+        // 4.2 构建 Prompt,并进行调用
+        Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
+        Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
+
+        // 4.3 流式返回
+        StringBuffer contentBuffer = new StringBuffer();
+        return streamResponse.map(chunk -> {
+            // 处理知识库的返回,只有首次才有
+            List<AiChatMessageRespVO.KnowledgeSegment> segments = null;
+            if (StrUtil.isEmpty(contentBuffer)) {
+                segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class,
+                        segment -> TenantUtils.executeIgnore(() -> {
+                            AiKnowledgeDocumentDO document = knowledgeDocumentService
+                                    .getKnowledgeDocument(segment.getDocumentId());
+                            segment.setDocumentName(document != null ? document.getName() : null);
+                        }));
+            }
+            // 响应结果
+            String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
+            newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
+            contentBuffer.append(newContent);
+            return success(new AiChatMessageSendRespVO()
+                    .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
+                    .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
+                            .setContent(newContent).setSegments(segments)));
+        }).doOnComplete(() -> {
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
+                    new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())));
+        }).doOnError(throwable -> {
+            log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
+                    new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())));
+        }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
+    }
+
+    private List<AiKnowledgeSegmentSearchRespBO> recallKnowledgeSegment(String content,
+            AiChatConversationDO conversation) {
+        // 1. 查询聊天角色
+        if (conversation == null || conversation.getRoleId() == null) {
+            return Collections.emptyList();
+        }
+        AiChatRoleDO role = chatRoleService.getChatRole(conversation.getRoleId());
+        if (role == null || CollUtil.isEmpty(role.getKnowledgeIds())) {
+            return Collections.emptyList();
+        }
+
+        // 2. 遍历找回
+        List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = new ArrayList<>();
+        for (Long knowledgeId : role.getKnowledgeIds()) {
+            knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO()
+                    .setKnowledgeId(knowledgeId).setContent(content)));
+        }
+        return knowledgeSegments;
+    }
+
+    private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,
+            List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
+            AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
+        List<Message> chatMessages = new ArrayList<>();
+        // 1.1 System Context 角色设定
+        if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
+            chatMessages.add(new SystemMessage(conversation.getSystemMessage()));
+        }
+
+        // 1.2 历史 history message 历史消息
+        List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO);
+        contextMessages
+                .forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent())));
+
+        // 1.3 当前 user message 新发送消息
+        chatMessages.add(new UserMessage(sendReqVO.getContent()));
+
+        // 1.4 知识库,通过 UserMessage 实现
+        if (CollUtil.isNotEmpty(knowledgeSegments)) {
+            String reference = knowledgeSegments.stream()
+                    .map(segment -> "<Reference>" + segment.getContent() + "</Reference>")
+                    .collect(Collectors.joining("\n\n"));
+            chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference)));
+        }
+
+        // 2.1 查询 tool 工具
+        Set<String> toolNames = null;
+        if (conversation.getRoleId() != null) {
+            AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId());
+            if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) {
+                toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);
+            }
+        }
+        // 2.2 构建 ChatOptions 对象
+        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
+        ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(),
+                conversation.getTemperature(), conversation.getMaxTokens(), toolNames);
+        return new Prompt(chatMessages, chatOptions);
+    }
+
+    /**
+     * 从历史消息中,获得倒序的 n 组消息作为消息上下文
+     * <p>
+     * n 组:指的是 user + assistant 形成一组
+     *
+     * @param messages     消息列表
+     * @param conversation 对话
+     * @param sendReqVO    发送请求
+     * @return 消息上下文
+     */
+    private List<AiChatMessageDO> filterContextMessages(List<AiChatMessageDO> messages,
+            AiChatConversationDO conversation,
+            AiChatMessageSendReqVO sendReqVO) {
+        if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) {
+            return Collections.emptyList();
+        }
+        List<AiChatMessageDO> contextMessages = new ArrayList<>(conversation.getMaxContexts() * 2);
+        for (int i = messages.size() - 1; i >= 0; i--) {
+            AiChatMessageDO assistantMessage = CollUtil.get(messages, i);
+            if (assistantMessage == null || assistantMessage.getReplyId() == null) {
+                continue;
+            }
+            AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);
+            if (userMessage == null
+                    || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
+                    || StrUtil.isEmpty(assistantMessage.getContent())) {
+                continue;
+            }
+            // 由于后续要 reverse 反转,所以先添加 assistantMessage
+            contextMessages.add(assistantMessage);
+            contextMessages.add(userMessage);
+            // 超过最大上下文,结束
+            if (contextMessages.size() >= conversation.getMaxContexts() * 2) {
+                break;
+            }
+        }
+        Collections.reverse(contextMessages);
+        return contextMessages;
+    }
+
+    private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
+            AiModelDO model, Long userId, Long roleId,
+            MessageType messageType, String content, Boolean useContext,
+            List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments) {
+        AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)
+                .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)
+                .setType(messageType.getValue()).setContent(content).setUseContext(useContext)
+                .setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId));
+        message.setCreateTime(LocalDateTime.now());
+        chatMessageMapper.insert(message);
+        return message;
+    }
+
+    @Override
+    public List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId) {
+        return chatMessageMapper.selectListByConversationId(conversationId);
+    }
+
+    @Override
+    public void deleteChatMessage(Long id, Long userId) {
+        // 1. 校验消息存在
+        AiChatMessageDO message = chatMessageMapper.selectById(id);
+        if (message == null || ObjUtil.notEqual(message.getUserId(), userId)) {
+            throw exception(CHAT_MESSAGE_NOT_EXIST);
+        }
+        // 2. 执行删除
+        chatMessageMapper.deleteById(id);
+    }
+
+    @Override
+    public void deleteChatMessageByConversationId(Long conversationId, Long userId) {
+        // 1. 校验消息存在
+        List<AiChatMessageDO> messages = chatMessageMapper.selectListByConversationId(conversationId);
+        if (CollUtil.isEmpty(messages) || ObjUtil.notEqual(messages.get(0).getUserId(), userId)) {
+            throw exception(CHAT_MESSAGE_NOT_EXIST);
+        }
+        // 2. 执行删除
+        chatMessageMapper.deleteBatchIds(convertList(messages, AiChatMessageDO::getId));
+    }
+
+    @Override
+    public void deleteChatMessageByAdmin(Long id) {
+        // 1. 校验消息存在
+        AiChatMessageDO message = chatMessageMapper.selectById(id);
+        if (message == null) {
+            throw exception(CHAT_MESSAGE_NOT_EXIST);
+        }
+        // 2. 执行删除
+        chatMessageMapper.deleteById(id);
+    }
+
+    @Override
+    public Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds) {
+        return chatMessageMapper.selectCountMapByConversationId(conversationIds);
+    }
+
+    @Override
+    public PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {
+        return chatMessageMapper.selectPage(pageReqVO);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/image/AiImageService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/image/AiImageService.java
new file mode 100644
index 0000000..b2f7390
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/image/AiImageService.java
@@ -0,0 +1,129 @@
+package com.iailab.module.ai.service.image;
+
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.AiImagePageReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.AiImageUpdateReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
+import com.iailab.module.ai.dal.dataobject.image.AiImageDO;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/**
+ * AI 绘图 Service 接口
+ *
+ * @author fansili
+ */
+public interface AiImageService {
+
+    /**
+     * 获取【我的】绘图分页
+     *
+     * @param userId 用户编号
+     * @param pageReqVO 分页条件
+     * @return 绘图分页
+     */
+    PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO);
+
+    /**
+     * 获取公开的绘图分页
+     *
+     * @param pageReqVO 分页条件
+     * @return 绘图分页
+     */
+    PageResult<AiImageDO> getImagePagePublic(AiImagePublicPageReqVO pageReqVO);
+
+    /**
+     * 获得绘图记录
+     *
+     * @param id 绘图编号
+     * @return 绘图记录
+     */
+    AiImageDO getImage(Long id);
+
+    /**
+     * 获得绘图列表
+     *
+     * @param ids 绘图编号数组
+     * @return 绘图记录列表
+     */
+    List<AiImageDO> getImageList(List<Long> ids);
+
+    /**
+     * 绘制图片
+     *
+     * @param userId 用户编号
+     * @param drawReqVO 绘制请求
+     * @return 绘画编号
+     */
+    Long drawImage(Long userId, AiImageDrawReqVO drawReqVO);
+
+    /**
+     * 删除【我的】绘画记录
+     *
+     * @param id 绘画编号
+     * @param userId 用户编号
+     */
+    void deleteImageMy(Long id, Long userId);
+
+    /**
+     * 获得绘画分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 绘画分页
+     */
+    PageResult<AiImageDO> getImagePage(AiImagePageReqVO pageReqVO);
+
+    /**
+     * 更新绘画
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateImage(@Valid AiImageUpdateReqVO updateReqVO);
+
+    /**
+     * 删除绘画
+     *
+     * @param id 编号
+     */
+    void deleteImage(Long id);
+
+    // ================ midjourney 专属 ================
+
+    /**
+     * 【Midjourney】生成图片
+     *
+     * @param userId 用户编号
+     * @param reqVO 绘制请求
+     * @return 绘画编号
+     */
+    Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO);
+
+    /**
+     * 【Midjourney】同步图片进展
+     *
+     * @return 同步成功数量
+     */
+    Integer midjourneySync();
+
+    /**
+     * 【Midjourney】通知图片进展
+     *
+     * @param notify 通知
+     */
+    void midjourneyNotify(MidjourneyApi.Notify notify);
+
+    /**
+     * 【Midjourney】Action 操作(放大、缩小、U1、U2...)
+     *
+     * @param userId 用户编号
+     * @param reqVO 绘制请求
+     * @return 绘画编号
+     */
+    Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/image/AiImageServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/image/AiImageServiceImpl.java
new file mode 100644
index 0000000..a2efae8
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/image/AiImageServiceImpl.java
@@ -0,0 +1,375 @@
+package com.iailab.module.ai.service.image;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.hutool.http.HttpUtil;
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowImageOptions;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.AiImagePageReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.AiImageUpdateReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
+import com.iailab.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
+import com.iailab.module.ai.dal.dataobject.image.AiImageDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.dal.mysql.image.AiImageMapper;
+import com.iailab.module.ai.enums.image.AiImageStatusEnum;
+import com.iailab.module.ai.service.model.AiModelService;
+import com.iailab.module.infra.api.file.FileApi;
+import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.image.ImageModel;
+import org.springframework.ai.image.ImageOptions;
+import org.springframework.ai.image.ImagePrompt;
+import org.springframework.ai.image.ImageResponse;
+import org.springframework.ai.openai.OpenAiImageOptions;
+import org.springframework.ai.qianfan.QianFanImageOptions;
+import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
+import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertMap;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertSet;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.*;
+
+/**
+ * AI 绘画 Service 实现类
+ *
+ * @author fansili
+ */
+@Service
+@Slf4j
+public class AiImageServiceImpl implements AiImageService {
+
+    @Resource
+    private AiModelService modelService;
+
+    @Resource
+    private AiImageMapper imageMapper;
+
+    @Resource
+    private FileApi fileApi;
+
+    @Override
+    public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) {
+        return imageMapper.selectPageMy(userId, pageReqVO);
+    }
+
+    @Override
+    public PageResult<AiImageDO> getImagePagePublic(AiImagePublicPageReqVO pageReqVO) {
+        return imageMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public AiImageDO getImage(Long id) {
+        return imageMapper.selectById(id);
+    }
+
+    @Override
+    public List<AiImageDO> getImageList(List<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        return imageMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) {
+        // 1. 校验模型
+        AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
+
+        // 2. 保存数据库
+        AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId)
+                .setPlatform(model.getPlatform()).setModelId(model.getId()).setModel(model.getModel())
+                .setPublicStatus(false).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
+        imageMapper.insert(image);
+
+        // 3. 异步绘制,后续前端通过返回的 id 进行轮询结果
+        getSelf().executeDrawImage(image, drawReqVO, model);
+        return image.getId();
+    }
+
+    @Async
+    public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) {
+        try {
+            // 1.1 构建请求
+            ImageOptions request = buildImageOptions(reqVO, model);
+            // 1.2 执行请求
+            ImageModel imageModel = modelService.getImageModel(model.getId());
+            ImageResponse response = imageModel.call(new ImagePrompt(reqVO.getPrompt(), request));
+            if (response.getResult() == null) {
+                throw new IllegalArgumentException("生成结果为空");
+            }
+
+            // 2. 上传到文件服务
+            String b64Json = response.getResult().getOutput().getB64Json();
+            byte[] fileContent = StrUtil.isNotEmpty(b64Json) ? Base64.decode(b64Json)
+                    : HttpUtil.downloadBytes(response.getResult().getOutput().getUrl());
+            String filePath = fileApi.createFile(fileContent);
+
+            // 3. 更新数据库
+            imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus())
+                    .setPicUrl(filePath).setFinishTime(LocalDateTime.now()));
+        } catch (Exception ex) {
+            log.error("[executeDrawImage][image({}) 生成异常]", image, ex);
+            imageMapper.updateById(new AiImageDO().setId(image.getId())
+                    .setStatus(AiImageStatusEnum.FAIL.getStatus())
+                    .setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now()));
+        }
+    }
+
+    private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) {
+        if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
+            // https://platform.openai.com/docs/api-reference/images/create
+            return OpenAiImageOptions.builder().withModel(model.getModel())
+                    .withHeight(draw.getHeight()).withWidth(draw.getWidth())
+                    .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格
+                    .withResponseFormat("b64_json")
+                    .build();
+        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) {
+            // https://docs.siliconflow.cn/cn/api-reference/images/images-generations
+            return SiliconFlowImageOptions.builder().model(model.getModel())
+                    .height(draw.getHeight()).width(draw.getWidth())
+                    .build();
+        }  else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
+            // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage
+            // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage
+            return StabilityAiImageOptions.builder().model(model.getModel())
+                    .height(draw.getHeight()).width(draw.getWidth())
+                    .seed(Long.valueOf(draw.getOptions().get("seed")))
+                    .cfgScale(Float.valueOf(draw.getOptions().get("scale")))
+                    .steps(Integer.valueOf(draw.getOptions().get("steps")))
+                    .sampler(String.valueOf(draw.getOptions().get("sampler")))
+                    .stylePreset(String.valueOf(draw.getOptions().get("stylePreset")))
+                    .clipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset")))
+                    .build();
+        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
+            return DashScopeImageOptions.builder()
+                    .withModel(model.getModel()).withN(1)
+                    .withHeight(draw.getHeight()).withWidth(draw.getWidth())
+                    .build();
+        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
+            return QianFanImageOptions.builder()
+                    .model(model.getModel()).N(1)
+                    .height(draw.getHeight()).width(draw.getWidth())
+                    .build();
+        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
+            return ZhiPuAiImageOptions.builder()
+                    .model(model.getModel())
+                    .build();
+        }
+        throw new IllegalArgumentException("不支持的 AI 平台:" + model.getPlatform());
+    }
+
+    @Override
+    public void deleteImageMy(Long id, Long userId) {
+        // 1. 校验是否存在
+        AiImageDO image = validateImageExists(id);
+        if (ObjUtil.notEqual(image.getUserId(), userId)) {
+            throw exception(IMAGE_NOT_EXISTS);
+        }
+        // 2. 删除记录
+        imageMapper.deleteById(id);
+    }
+
+    @Override
+    public PageResult<AiImageDO> getImagePage(AiImagePageReqVO pageReqVO) {
+        return imageMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public void updateImage(AiImageUpdateReqVO updateReqVO) {
+        // 1. 校验存在
+        validateImageExists(updateReqVO.getId());
+        // 2. 更新发布状态
+        imageMapper.updateById(BeanUtils.toBean(updateReqVO, AiImageDO.class));
+    }
+
+    @Override
+    public void deleteImage(Long id) {
+        // 1. 校验存在
+        validateImageExists(id);
+        // 2. 删除
+        imageMapper.deleteById(id);
+    }
+
+    private AiImageDO validateImageExists(Long id) {
+        AiImageDO image = imageMapper.selectById(id);
+        if (image == null) {
+            throw exception(IMAGE_NOT_EXISTS);
+        }
+        return image;
+    }
+
+    // ================ midjourney 专属 ================
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO drawReqVO) {
+        // 1. 校验模型
+        AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
+        Assert.equals(model.getPlatform(), AiPlatformEnum.MIDJOURNEY.getPlatform(), "平台不匹配");
+        MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(model.getId());
+
+        // 2. 保存数据库
+        AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
+                .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
+                .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()).setModelId(model.getId()).setModel(model.getName());
+        imageMapper.insert(image);
+
+        // 3. 调用 Midjourney Proxy 提交任务
+        List<String> base64Array = StrUtil.isBlank(drawReqVO.getReferImageUrl()) ? null :
+                Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(drawReqVO.getReferImageUrl()))));
+        MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(
+                base64Array, drawReqVO.getPrompt(),null,
+                MidjourneyApi.ImagineRequest.buildState(drawReqVO.getWidth(),
+                        drawReqVO.getHeight(), drawReqVO.getVersion(), model.getModel()));
+        MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest);
+
+        // 4.1 情况一【失败】:抛出业务异常
+        if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) {
+            String description = imagineResponse.description().contains("quota_not_enough") ?
+                    "账户余额不足" : imagineResponse.description();
+            throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
+        }
+
+        // 4.2 情况二【成功】:更新 taskId 和参数
+        imageMapper.updateById(new AiImageDO().setId(image.getId())
+                .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(drawReqVO)));
+        return image.getId();
+    }
+
+    @Override
+    public Integer midjourneySync() {
+        // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image
+        List<AiImageDO> images = imageMapper.selectListByStatusAndPlatform(
+                AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform());
+        if (CollUtil.isEmpty(images)) {
+            return 0;
+        }
+        // 1.2 调用 Midjourney Proxy 获取任务进展
+        MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(images.get(0).getModelId());
+        List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(images, AiImageDO::getTaskId));
+        Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id);
+
+        // 2. 逐个处理,更新进展
+        int count = 0;
+        for (AiImageDO image : images) {
+            MidjourneyApi.Notify notify = taskMap.get(image.getTaskId());
+            if (notify == null) {
+                log.error("[midjourneySync][image({}) 查询不到进展]", image);
+                continue;
+            }
+            count++;
+            updateMidjourneyStatus(image, notify);
+        }
+        return count;
+    }
+
+    @Override
+    public void midjourneyNotify(MidjourneyApi.Notify notify) {
+        // 1. 校验 image 存在
+        AiImageDO image = imageMapper.selectByTaskId(notify.id());
+        if (image == null) {
+            log.warn("[midjourneyNotify][回调任务({}) 不存在]", notify.id());
+            return;
+        }
+        // 2. 更新状态
+        updateMidjourneyStatus(image, notify);
+    }
+
+    private void updateMidjourneyStatus(AiImageDO image, MidjourneyApi.Notify notify) {
+        // 1. 转换状态
+        Integer status = null;
+        LocalDateTime finishTime = null;
+        if (StrUtil.isNotBlank(notify.status())) {
+            MidjourneyApi.TaskStatusEnum taskStatusEnum = MidjourneyApi.TaskStatusEnum.valueOf(notify.status());
+            if (MidjourneyApi.TaskStatusEnum.SUCCESS == taskStatusEnum) {
+                status = AiImageStatusEnum.SUCCESS.getStatus();
+                finishTime = LocalDateTime.now();
+            } else if (MidjourneyApi.TaskStatusEnum.FAILURE == taskStatusEnum) {
+                status = AiImageStatusEnum.FAIL.getStatus();
+                finishTime = LocalDateTime.now();
+            }
+        }
+
+        // 2. 上传图片
+        String picUrl = null;
+        if (StrUtil.isNotBlank(notify.imageUrl())) {
+            try {
+                picUrl = fileApi.createFile(HttpUtil.downloadBytes(notify.imageUrl()));
+            } catch (Exception e) {
+                picUrl = notify.imageUrl();
+                log.warn("[updateMidjourneyStatus][图片({}) 地址({}) 上传失败]", image.getId(), notify.imageUrl(), e);
+            }
+        }
+
+        // 3. 更新 image 状态
+        imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(status)
+                .setPicUrl(picUrl).setButtons(notify.buttons()).setErrorMessage(notify.failReason())
+                .setFinishTime(finishTime));
+    }
+
+    @Override
+    public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) {
+        // 1.1 检查 image
+        AiImageDO image = validateImageExists(reqVO.getId());
+        if (ObjUtil.notEqual(userId, image.getUserId())) {
+            throw exception(IMAGE_NOT_EXISTS);
+        }
+        MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(image.getModelId());
+        // 1.2 检查 customId
+        MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(),
+                buttonX -> buttonX.customId().equals(reqVO.getCustomId()));
+        if (button == null) {
+            throw exception(IMAGE_CUSTOM_ID_NOT_EXISTS);
+        }
+
+        // 2. 调用 Midjourney Proxy 提交任务
+        MidjourneyApi.SubmitResponse actionResponse = midjourneyApi.action(
+                new MidjourneyApi.ActionRequest(button.customId(), image.getTaskId(), null));
+        if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(actionResponse.code())) {
+            String description = actionResponse.description().contains("quota_not_enough") ?
+                    "账户余额不足" : actionResponse.description();
+            throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
+        }
+
+        // 3. 新增 image 记录
+        AiImageDO newImage = new AiImageDO().setUserId(image.getUserId()).setPublicStatus(false).setPrompt(image.getPrompt())
+                .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
+                .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform())
+                .setModel(image.getModel()).setWidth(image.getWidth()).setHeight(image.getHeight())
+                .setOptions(image.getOptions()).setTaskId(actionResponse.result());
+        imageMapper.insert(newImage);
+        return newImage.getId();
+    }
+
+    /**
+     * 获得自身的代理对象,解决 AOP 生效问题
+     *
+     * @return 自己
+     */
+    private AiImageServiceImpl getSelf() {
+        return SpringUtil.getBean(getClass());
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeDocumentService.java
new file mode 100644
index 0000000..e75dddc
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeDocumentService.java
@@ -0,0 +1,118 @@
+package com.iailab.module.ai.service.knowledge;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertMap;
+
+/**
+ * AI 知识库文档 Service 接口
+ *
+ * @author xiaoxin
+ */
+public interface AiKnowledgeDocumentService {
+
+    /**
+     * 创建文档(单个)
+     *
+     * @param createReqVO 文档创建 Request VO
+     * @return 文档编号
+     */
+    Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO);
+
+    /**
+     * 创建文档(多个)
+     *
+     * @param createListReqVO 批量创建 Request VO
+     * @return 文档编号列表
+     */
+    List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO);
+
+    /**
+     * 获取文档分页
+     *
+     * @param pageReqVO 分页参数
+     * @return 文档分页
+     */
+    PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO);
+
+    /**
+     * 获取文档详情
+     *
+     * @param id 文档编号
+     * @return 文档详情
+     */
+    AiKnowledgeDocumentDO getKnowledgeDocument(Long id);
+
+    /**
+     * 更新文档
+     *
+     * @param reqVO 更新信息
+     */
+    void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO);
+
+    /**
+     * 更新文档状态
+     *
+     * @param reqVO 更新状态信息
+     */
+    void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO);
+
+    /**
+     * 更新文档检索次数(增加 +1)
+     *
+     * @param ids 文档编号列表
+     */
+    void updateKnowledgeDocumentRetrievalCountIncr(Collection<Long> ids);
+
+    /**
+     * 删除文档
+     *
+     * @param id 文档编号
+     */
+    void deleteKnowledgeDocument(Long id);
+
+    /**
+     * 校验文档是否存在
+     *
+     * @param id 文档编号
+     * @return 文档信息
+     */
+    AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id);
+
+    /**
+     * 读取 URL 内容
+     *
+     * @param url URL
+     * @return 内容
+     */
+    String readUrl(String url);
+
+    /**
+     * 获取文档列表
+     *
+     * @param ids 文档编号列表
+     * @return 文档列表
+     */
+    List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids);
+
+    /**
+     * 获取文档 Map
+     *
+     * @param ids 文档编号列表
+     * @return 文档 Map
+     */
+    default Map<Long, AiKnowledgeDocumentDO> getKnowledgeDocumentMap(Collection<Long> ids) {
+        return convertMap(getKnowledgeDocumentList(ids), AiKnowledgeDocumentDO::getId);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
new file mode 100644
index 0000000..35f0018
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
@@ -0,0 +1,214 @@
+package com.iailab.module.ai.service.knowledge;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
+import com.iailab.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.reader.tika.TikaDocumentReader;
+import org.springframework.ai.tokenizer.TokenCountEstimator;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.*;
+
+/**
+ * AI 知识库文档 Service 实现类
+ *
+ * @author xiaoxin
+ */
+@Service
+@Slf4j
+public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService {
+
+    @Resource
+    private AiKnowledgeDocumentMapper knowledgeDocumentMapper;
+
+    @Resource
+    private TokenCountEstimator tokenCountEstimator;
+
+    @Resource
+    private AiKnowledgeSegmentService knowledgeSegmentService;
+    @Resource
+    @Lazy // 延迟加载,避免循环依赖
+    private AiKnowledgeService knowledgeService;
+
+    @Override
+    public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) {
+        // 1. 校验参数
+        knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
+
+        // 2. 下载文档
+        String content = readUrl(createReqVO.getUrl());
+
+        // 3. 文档记录入库
+        AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class)
+                .setContent(content).setContentLength(content.length()).setTokens(tokenCountEstimator.estimate(content))
+                .setStatus(CommonStatusEnum.ENABLE.getStatus());
+        knowledgeDocumentMapper.insert(documentDO);
+
+        // 4. 文档切片入库(异步)
+        knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), content);
+        return documentDO.getId();
+    }
+
+    @Override
+    public List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO) {
+        // 1. 校验参数
+        knowledgeService.validateKnowledgeExists(createListReqVO.getKnowledgeId());
+
+        // 2. 下载文档
+        List<String> contents = convertList(createListReqVO.getList(), document -> readUrl(document.getUrl()));
+
+        // 3. 文档记录入库
+        List<AiKnowledgeDocumentDO> documentDOs = new ArrayList<>(createListReqVO.getList().size());
+        for (int i = 0; i < createListReqVO.getList().size(); i++) {
+            AiKnowledgeDocumentCreateListReqVO.Document documentVO = createListReqVO.getList().get(i);
+            String content = contents.get(i);
+            documentDOs.add(BeanUtils.toBean(documentVO, AiKnowledgeDocumentDO.class)
+                    .setKnowledgeId(createListReqVO.getKnowledgeId())
+                    .setContent(content).setContentLength(content.length())
+                    .setTokens(tokenCountEstimator.estimate(content))
+                    .setSegmentMaxTokens(createListReqVO.getSegmentMaxTokens())
+                    .setStatus(CommonStatusEnum.ENABLE.getStatus()));
+        }
+        knowledgeDocumentMapper.insertBatch(documentDOs);
+
+        // 4. 批量创建文档切片(异步)
+        documentDOs.forEach(documentDO -> knowledgeSegmentService
+                .createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), documentDO.getContent()));
+        return convertList(documentDOs, AiKnowledgeDocumentDO::getId);
+    }
+
+    @Override
+    public PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) {
+        return knowledgeDocumentMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public AiKnowledgeDocumentDO getKnowledgeDocument(Long id) {
+        return knowledgeDocumentMapper.selectById(id);
+    }
+
+    @Override
+    public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) {
+        // 1. 校验文档是否存在
+        AiKnowledgeDocumentDO oldDocument = validateKnowledgeDocumentExists(reqVO.getId());
+
+        // 2. 更新文档
+        AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class);
+        knowledgeDocumentMapper.updateById(document);
+
+        // 3. 如果处于开启状态,并且最大 tokens 发生变化,则 segment 需要重新索引
+        if (CommonStatusEnum.isEnable(oldDocument.getStatus())
+                && reqVO.getSegmentMaxTokens() != null
+                && ObjUtil.notEqual(reqVO.getSegmentMaxTokens(), oldDocument.getSegmentMaxTokens())) {
+            // 删除旧的文档切片
+            knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());
+            // 重新创建文档切片
+            knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), oldDocument.getContent());
+        }
+    }
+
+    @Override
+    public void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
+        // 1. 校验存在
+        AiKnowledgeDocumentDO document = validateKnowledgeDocumentExists(reqVO.getId());
+
+        // 2. 更新状态
+        knowledgeDocumentMapper.updateById(new AiKnowledgeDocumentDO()
+                .setId(reqVO.getId()).setStatus(reqVO.getStatus()));
+
+        // 3. 处理文档切片
+        if (CommonStatusEnum.isEnable(reqVO.getStatus())) {
+            knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), document.getContent());
+        } else {
+            knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteKnowledgeDocument(Long id) {
+        // 1. 校验存在
+        validateKnowledgeDocumentExists(id);
+
+        // 2. 删除
+        knowledgeDocumentMapper.deleteById(id);
+
+        // 3. 删除对应的段落
+        knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(id);
+    }
+
+    @Override
+    public void updateKnowledgeDocumentRetrievalCountIncr(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return;
+        }
+        knowledgeDocumentMapper.updateRetrievalCountIncr(ids);
+    }
+
+    @Override
+    public AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) {
+        AiKnowledgeDocumentDO knowledgeDocument = knowledgeDocumentMapper.selectById(id);
+        if (knowledgeDocument == null) {
+            throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS);
+        }
+        return knowledgeDocument;
+    }
+
+    @Override
+    public String readUrl(String url) {
+        // 下载文件
+        ByteArrayResource resource;
+        try {
+            byte[] bytes = HttpUtil.downloadBytes(url);
+            if (bytes.length == 0) {
+                throw exception(KNOWLEDGE_DOCUMENT_FILE_EMPTY);
+            }
+            resource = new ByteArrayResource(bytes);
+        } catch (Exception e) {
+            log.error("[readUrl][url({}) 读取失败]", url, e);
+            throw exception(KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL);
+        }
+
+        // 读取文件
+        TikaDocumentReader loader = new TikaDocumentReader(resource);
+        List<Document> documents = loader.get();
+        Document document = CollUtil.getFirst(documents);
+        if (document == null || StrUtil.isEmpty(document.getText())) {
+            throw exception(KNOWLEDGE_DOCUMENT_FILE_READ_FAIL);
+        }
+        return document.getText();
+    }
+
+    @Override
+    public List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        return knowledgeDocumentMapper.selectBatchIds(ids);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeSegmentService.java
new file mode 100644
index 0000000..c9b1544
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeSegmentService.java
@@ -0,0 +1,150 @@
+package com.iailab.module.ai.service.knowledge;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
+import com.iailab.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
+import com.iailab.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
+import org.springframework.scheduling.annotation.Async;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertMap;
+
+/**
+ * AI 知识库段落 Service 接口
+ *
+ * @author xiaoxin
+ */
+public interface AiKnowledgeSegmentService {
+
+    /**
+     * 获取知识库段落详情
+     *
+     * @param id 段落编号
+     * @return 段落详情
+     */
+    AiKnowledgeSegmentDO getKnowledgeSegment(Long id);
+
+    /**
+     * 获取知识库段落列表
+     *
+     * @param ids 段落编号列表
+     * @return 段落列表
+     */
+    List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids);
+
+    /**
+     * 获取知识库段落 Map
+     *
+     * @param ids 段落编号列表
+     * @return 段落 Map
+     */
+    default Map<Long, AiKnowledgeSegmentDO> getKnowledgeSegmentMap(Collection<Long> ids) {
+        return convertMap(getKnowledgeSegmentList(ids), AiKnowledgeSegmentDO::getId);
+    }
+
+    /**
+     * 获取段落分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 文档分页
+     */
+    PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO);
+
+    /**
+     * 基于 content 内容,切片创建多个段落
+     *
+     * @param documentId 知识库文档编号
+     * @param content    文档内容
+     */
+    void createKnowledgeSegmentBySplitContent(Long documentId, String content);
+
+    /**
+     * 【异步】基于 content 内容,切片创建多个段落
+     *
+     * @param documentId 知识库文档编号
+     * @param content    文档内容
+     */
+    @Async
+    default void createKnowledgeSegmentBySplitContentAsync(Long documentId, String content) {
+        createKnowledgeSegmentBySplitContent(documentId, content);
+    }
+
+    /**
+     * 创建知识库段落
+     *
+     * @param createReqVO 创建信息
+     * @return 段落编号
+     */
+    Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO);
+
+    /**
+     * 更新段落的内容
+     *
+     * @param reqVO 更新内容
+     */
+    void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO);
+
+    /**
+     * 更新段落的状态
+     *
+     * @param reqVO 更新内容
+     */
+    void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
+
+    /**
+     * 重新索引知识库下的所有文档段落
+     *
+     * @param knowledgeId 知识库编号
+     */
+    void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId);
+
+    /**
+     * 【异步】重新索引知识库下的所有文档段落
+     *
+     * @param knowledgeId 知识库编号
+     */
+    @Async
+    default void reindexByKnowledgeIdAsync(Long knowledgeId) {
+        reindexKnowledgeSegmentByKnowledgeId(knowledgeId);
+    }
+
+    /**
+     * 根据文档编号删除段落
+     *
+     * @param documentId 文档编号
+     */
+    void deleteKnowledgeSegmentByDocumentId(Long documentId);
+
+    /**
+     * 搜索知识库段落,并返回结果
+     *
+     * @param reqBO 搜索请求信息
+     * @return 搜索结果段落列表
+     */
+    List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO);
+
+    /**
+     * 根据 URL 内容,切片创建多个段落
+     *
+     * @param url              URL 地址
+     * @param segmentMaxTokens 段落最大 Token 数
+     * @return 切片后的段落列表
+     */
+    List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens);
+
+    /**
+     * 获取文档处理进度(多个)
+     *
+     * @param documentIds 文档编号列表
+     * @return 文档处理列表
+     */
+    List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
new file mode 100644
index 0000000..d619b24
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -0,0 +1,357 @@
+package com.iailab.module.ai.service.knowledge;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
+import com.iailab.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
+import com.iailab.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
+import com.iailab.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
+import com.iailab.module.ai.service.model.AiModelService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.tokenizer.TokenCountEstimator;
+import org.springframework.ai.transformer.splitter.TextSplitter;
+import org.springframework.ai.transformer.splitter.TokenTextSplitter;
+import org.springframework.ai.vectorstore.SearchRequest;
+import org.springframework.ai.vectorstore.VectorStore;
+import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS;
+
+/**
+ * AI 知识库分片 Service 实现类
+ *
+ * @author xiaoxin
+ */
+@Service
+@Slf4j
+public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService {
+
+    private static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = "knowledgeId";
+    private static final String VECTOR_STORE_METADATA_DOCUMENT_ID = "documentId";
+    private static final String VECTOR_STORE_METADATA_SEGMENT_ID = "segmentId";
+
+    private static final Map<String, Class<?>> VECTOR_STORE_METADATA_TYPES = Map.of(
+            VECTOR_STORE_METADATA_KNOWLEDGE_ID, String.class,
+            VECTOR_STORE_METADATA_DOCUMENT_ID, String.class,
+            VECTOR_STORE_METADATA_SEGMENT_ID, String.class);
+
+    @Resource
+    private AiKnowledgeSegmentMapper segmentMapper;
+
+    @Resource
+    private AiKnowledgeService knowledgeService;
+    @Resource
+    @Lazy // 延迟加载,避免循环依赖
+    private AiKnowledgeDocumentService knowledgeDocumentService;
+    @Resource
+    private AiModelService modelService;
+
+    @Resource
+    private TokenCountEstimator tokenCountEstimator;
+
+    @Override
+    public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) {
+        return segmentMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public void createKnowledgeSegmentBySplitContent(Long documentId, String content) {
+        // 1. 校验
+        AiKnowledgeDocumentDO documentDO = knowledgeDocumentService.validateKnowledgeDocumentExists(documentId);
+        AiKnowledgeDO knowledgeDO = knowledgeService.validateKnowledgeExists(documentDO.getKnowledgeId());
+        VectorStore vectorStore = getVectorStoreById(knowledgeDO);
+
+        // 2. 文档切片
+        List<Document> documentSegments = splitContentByToken(content, documentDO.getSegmentMaxTokens());
+
+        // 3.1 存储切片
+        List<AiKnowledgeSegmentDO> segmentDOs = convertList(documentSegments, segment -> {
+            if (StrUtil.isEmpty(segment.getText())) {
+                return null;
+            }
+            return new AiKnowledgeSegmentDO().setKnowledgeId(documentDO.getKnowledgeId()).setDocumentId(documentId)
+                    .setContent(segment.getText()).setContentLength(segment.getText().length())
+                    .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)
+                    .setTokens(tokenCountEstimator.estimate(segment.getText()))
+                    .setStatus(CommonStatusEnum.ENABLE.getStatus());
+        });
+        segmentMapper.insertBatch(segmentDOs);
+        // 3.2 切片向量化
+        for (int i = 0; i < documentSegments.size(); i++) {
+            Document segment = documentSegments.get(i);
+            AiKnowledgeSegmentDO segmentDO = segmentDOs.get(i);
+            writeVectorStore(vectorStore, segmentDO, segment);
+        }
+    }
+
+    @Override
+    public void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO) {
+        // 1. 校验
+        AiKnowledgeSegmentDO oldSegment = validateKnowledgeSegmentExists(reqVO.getId());
+
+        // 2. 删除向量
+        VectorStore vectorStore = getVectorStoreById(oldSegment.getKnowledgeId());
+        deleteVectorStore(vectorStore, oldSegment);
+
+        // 3.1 更新切片
+        AiKnowledgeSegmentDO newSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
+        segmentMapper.updateById(newSegment);
+        // 3.2 重新向量化,必须开启状态
+        if (CommonStatusEnum.isEnable(oldSegment.getStatus())) {
+            newSegment.setKnowledgeId(oldSegment.getKnowledgeId()).setDocumentId(oldSegment.getDocumentId());
+            writeVectorStore(vectorStore, newSegment, new Document(newSegment.getContent()));
+        }
+    }
+
+    @Override
+    public void deleteKnowledgeSegmentByDocumentId(Long documentId) {
+        // 1. 查询需要删除的段落
+        List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByDocumentId(documentId);
+        if (CollUtil.isEmpty(segments)) {
+            return;
+        }
+
+        // 2. 批量删除段落记录
+        segmentMapper.deleteByIds(convertList(segments, AiKnowledgeSegmentDO::getId));
+
+        // 3. 删除向量存储中的段落
+        VectorStore vectorStore = getVectorStoreById(segments.get(0).getKnowledgeId());
+        vectorStore.delete(convertList(segments, AiKnowledgeSegmentDO::getVectorId));
+    }
+
+    @Override
+    public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
+        // 1. 校验
+        AiKnowledgeSegmentDO segment = validateKnowledgeSegmentExists(reqVO.getId());
+
+        // 2. 获取知识库向量实例
+        VectorStore vectorStore = getVectorStoreById(segment.getKnowledgeId());
+
+        // 3. 更新状态
+        segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(reqVO.getId()).setStatus(reqVO.getStatus()));
+
+        // 4. 更新向量
+        if (CommonStatusEnum.isEnable(reqVO.getStatus())) {
+            writeVectorStore(vectorStore, segment, new Document(segment.getContent()));
+        } else {
+            deleteVectorStore(vectorStore, segment);
+        }
+    }
+
+    @Override
+    public void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId) {
+        // 1.1 校验知识库存在
+        AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);
+        // 1.2 获取知识库向量实例
+        VectorStore vectorStore = getVectorStoreById(knowledge);
+
+        // 2.1 查询知识库下的所有启用状态的段落
+        List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByKnowledgeIdAndStatus(
+                knowledgeId, CommonStatusEnum.ENABLE.getStatus());
+        if (CollUtil.isEmpty(segments)) {
+            return;
+        }
+        // 2.2 遍历所有段落,重新索引
+        for (AiKnowledgeSegmentDO segment : segments) {
+            // 删除旧的向量
+            deleteVectorStore(vectorStore, segment);
+            // 重新创建向量
+            writeVectorStore(vectorStore, segment, new Document(segment.getContent()));
+        }
+        log.info("[reindexKnowledgeSegmentByKnowledgeId][知识库({}) 重新索引完成,共处理 {} 个段落]",
+                knowledgeId, segments.size());
+    }
+
+    private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) {
+        // 1. 向量存储
+        // 为什么要 toString 呢?因为部分 VectorStore 实现,不支持 Long 类型,例如说 QdrantVectorStore
+        segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId().toString());
+        segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId().toString());
+        segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId().toString());
+        vectorStore.add(List.of(segment));
+
+        // 2. 更新向量 ID
+        segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId()).setVectorId(segment.getId()));
+    }
+
+    private void deleteVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO) {
+        // 1. 更新向量 ID
+        if (StrUtil.isEmpty(segmentDO.getVectorId())) {
+            return;
+        }
+        segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId())
+                .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY));
+
+        // 2. 删除向量
+        vectorStore.delete(List.of(segmentDO.getVectorId()));
+    }
+
+    @Override
+    public List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO) {
+        // 1. 校验
+        AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId());
+
+        // 2.1 向量检索
+        VectorStore vectorStore = getVectorStoreById(knowledge);
+        List<Document> documents = vectorStore.similaritySearch(SearchRequest.builder()
+                .query(reqBO.getContent())
+                .topK(ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK()))
+                .similarityThreshold(
+                        ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold()))
+                .filterExpression(new FilterExpressionBuilder()
+                        .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId().toString())
+                        .build())
+                .build());
+        if (CollUtil.isEmpty(documents)) {
+            return ListUtil.empty();
+        }
+        // 2.2 段落召回
+        List<AiKnowledgeSegmentDO> segments = segmentMapper
+                .selectListByVectorIds(convertList(documents, Document::getId));
+        if (CollUtil.isEmpty(segments)) {
+            return ListUtil.empty();
+        }
+
+        // 3. 增加召回次数
+        segmentMapper.updateRetrievalCountIncrByIds(convertList(segments, AiKnowledgeSegmentDO::getId));
+
+        // 4. 构建结果
+        List<AiKnowledgeSegmentSearchRespBO> result = convertList(segments, segment -> {
+            Document document = CollUtil.findOne(documents, // 找到对应的文档
+                    doc -> Objects.equals(doc.getId(), segment.getVectorId()));
+            if (document == null) {
+                return null;
+            }
+            return BeanUtils.toBean(segment, AiKnowledgeSegmentSearchRespBO.class)
+                    .setScore(document.getScore());
+        });
+        result.sort((o1, o2) -> Double.compare(o2.getScore(), o1.getScore())); // 按照分数降序排序
+        return result;
+    }
+
+    @Override
+    public List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens) {
+        // 1. 读取 URL 内容
+        String content = knowledgeDocumentService.readUrl(url);
+
+        // 2. 文档切片
+        List<Document> documentSegments = splitContentByToken(content, segmentMaxTokens);
+
+        // 3. 转换为段落对象
+        return convertList(documentSegments, segment -> {
+            if (StrUtil.isEmpty(segment.getText())) {
+                return null;
+            }
+            return new AiKnowledgeSegmentDO()
+                    .setContent(segment.getText())
+                    .setContentLength(segment.getText().length())
+                    .setTokens(tokenCountEstimator.estimate(segment.getText()));
+        });
+    }
+
+    /**
+     * 校验段落是否存在
+     *
+     * @param id 文档编号
+     * @return 段落信息
+     */
+    private AiKnowledgeSegmentDO validateKnowledgeSegmentExists(Long id) {
+        AiKnowledgeSegmentDO knowledgeSegment = segmentMapper.selectById(id);
+        if (knowledgeSegment == null) {
+            throw exception(KNOWLEDGE_SEGMENT_NOT_EXISTS);
+        }
+        return knowledgeSegment;
+    }
+
+    private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) {
+        return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId(), VECTOR_STORE_METADATA_TYPES);
+    }
+
+    private VectorStore getVectorStoreById(Long knowledgeId) {
+        AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);
+        return getVectorStoreById(knowledge);
+    }
+
+    private static List<Document> splitContentByToken(String content, Integer segmentMaxTokens) {
+        TextSplitter textSplitter = buildTokenTextSplitter(segmentMaxTokens);
+        return textSplitter.apply(Collections.singletonList(new Document(content)));
+    }
+
+    private static TextSplitter buildTokenTextSplitter(Integer segmentMaxTokens) {
+        return TokenTextSplitter.builder()
+                .withChunkSize(segmentMaxTokens)
+                .withMinChunkSizeChars(Integer.MAX_VALUE) // 忽略字符的截断
+                .withMinChunkLengthToEmbed(1) // 允许的最小有效分段长度
+                .withMaxNumChunks(Integer.MAX_VALUE)
+                .withKeepSeparator(true) // 保留分隔符
+                .build();
+    }
+
+    @Override
+    public List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds) {
+        if (CollUtil.isEmpty(documentIds)) {
+            return Collections.emptyList();
+        }
+        return segmentMapper.selectProcessList(documentIds);
+    }
+
+    @Override
+    public Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO) {
+        // 1.1 校验文档是否存在
+        AiKnowledgeDocumentDO document = knowledgeDocumentService
+                .validateKnowledgeDocumentExists(createReqVO.getDocumentId());
+        // 1.2 获取知识库信息
+        AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(document.getKnowledgeId());
+        // 1.3 校验 token 熟练
+        Integer tokens = tokenCountEstimator.estimate(createReqVO.getContent());
+        if (tokens > document.getSegmentMaxTokens()) {
+            throw exception(KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG, tokens, document.getSegmentMaxTokens());
+        }
+
+        // 2. 保存段落
+        AiKnowledgeSegmentDO segment = BeanUtils.toBean(createReqVO, AiKnowledgeSegmentDO.class)
+                .setKnowledgeId(knowledge.getId()).setDocumentId(document.getId())
+                .setContentLength(createReqVO.getContent().length()).setTokens(tokens)
+                .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)
+                .setRetrievalCount(0).setStatus(CommonStatusEnum.ENABLE.getStatus());
+        segmentMapper.insert(segment);
+
+        // 3. 向量化
+        writeVectorStore(getVectorStoreById(knowledge), segment, new Document(segment.getContent()));
+        return segment.getId();
+    }
+
+    @Override
+    public AiKnowledgeSegmentDO getKnowledgeSegment(Long id) {
+        return segmentMapper.selectById(id);
+    }
+
+    @Override
+    public List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        return segmentMapper.selectBatchIds(ids);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeService.java
new file mode 100644
index 0000000..bbbcc27
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeService.java
@@ -0,0 +1,63 @@
+package com.iailab.module.ai.service.knowledge;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
+
+import java.util.List;
+
+/**
+ * AI 知识库-基础信息 Service 接口
+ *
+ * @author xiaoxin
+ */
+public interface AiKnowledgeService {
+
+    /**
+     * 创建知识库
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createKnowledge(AiKnowledgeSaveReqVO createReqVO);
+
+    /**
+     * 更新知识库
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO);
+
+    /**
+     * 获得知识库
+     *
+     * @param id 编号
+     * @return 知识库
+     */
+    AiKnowledgeDO getKnowledge(Long id);
+
+    /**
+     * 校验知识库是否存在
+     *
+     * @param id 记录编号
+     */
+    AiKnowledgeDO validateKnowledgeExists(Long id);
+
+    /**
+     * 获得知识库分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 知识库分页
+     */
+    PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO);
+
+    /**
+     * 获得指定状态的知识库列表
+     *
+     * @param status 状态
+     * @return 知识库列表
+     */
+    List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
new file mode 100644
index 0000000..baf471d
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
@@ -0,0 +1,93 @@
+package com.iailab.module.ai.service.knowledge;
+
+import cn.hutool.core.util.ObjUtil;
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
+import com.iailab.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
+import com.iailab.module.ai.service.model.AiModelService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS;
+
+/**
+ * AI 知识库-基础信息 Service 实现类
+ *
+ * @author xiaoxin
+ */
+@Service
+@Slf4j
+public class AiKnowledgeServiceImpl implements AiKnowledgeService {
+
+    @Resource
+    private AiKnowledgeMapper knowledgeMapper;
+
+    @Resource
+    private AiModelService modelService;
+    @Resource
+    private AiKnowledgeSegmentService knowledgeSegmentService;
+
+    @Override
+    public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) {
+        // 1. 校验模型配置
+        AiModelDO model = modelService.validateModel(createReqVO.getEmbeddingModelId());
+
+        // 2. 插入知识库
+        AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
+                .setEmbeddingModel(model.getModel());
+        knowledgeMapper.insert(knowledge);
+        return knowledge.getId();
+    }
+
+    @Override
+    public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) {
+        // 1.1 校验知识库存在
+        AiKnowledgeDO oldKnowledge = validateKnowledgeExists(updateReqVO.getId());
+        // 1.2 校验模型配置
+        AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId());
+
+        // 2. 更新知识库
+        AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class)
+                .setEmbeddingModel(model.getModel());
+        knowledgeMapper.updateById(updateObj);
+
+        // 3. 如果模型变化,需要 reindex 所有的文档
+        if (ObjUtil.notEqual(oldKnowledge.getEmbeddingModelId(), updateReqVO.getEmbeddingModelId())) {
+            knowledgeSegmentService.reindexByKnowledgeIdAsync(updateReqVO.getId());
+        }
+    }
+
+    @Override
+    public AiKnowledgeDO getKnowledge(Long id) {
+        return knowledgeMapper.selectById(id);
+    }
+
+    @Override
+    public AiKnowledgeDO validateKnowledgeExists(Long id) {
+        AiKnowledgeDO knowledgeBase = knowledgeMapper.selectById(id);
+        if (knowledgeBase == null) {
+            throw exception(KNOWLEDGE_NOT_EXISTS);
+        }
+        return knowledgeBase;
+    }
+
+    @Override
+    public PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO) {
+        return knowledgeMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status) {
+        return knowledgeMapper.selectListByStatus(status);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java
new file mode 100644
index 0000000..18a08df
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java
@@ -0,0 +1,39 @@
+package com.iailab.module.ai.service.knowledge.bo;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+import jakarta.validation.constraints.NotEmpty;
+
+/**
+ * AI 知识库段落搜索 Request BO
+ *
+ * @author Iailab
+ */
+@Data
+public class AiKnowledgeSegmentSearchReqBO {
+
+    /**
+     * 知识库编号
+     */
+    @NotNull(message = "知识库编号不能为空")
+    private Long knowledgeId;
+
+    /**
+     * 内容
+     */
+    @NotEmpty(message = "内容不能为空")
+    private String content;
+
+    /**
+     * 最大返回数量
+     */
+    private Integer topK;
+
+    /**
+     * 相似度阈值
+     */
+    private Double similarityThreshold;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java
new file mode 100644
index 0000000..2b3e5f0
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java
@@ -0,0 +1,45 @@
+package com.iailab.module.ai.service.knowledge.bo;
+
+import lombok.Data;
+
+/**
+ * AI 知识库段落搜索 Response BO
+ *
+ * @author Iailab
+ */
+@Data
+public class AiKnowledgeSegmentSearchRespBO {
+
+    /**
+     * 段落编号
+     */
+    private Long id;
+    /**
+     * 文档编号
+     */
+    private Long documentId;
+    /**
+     * 知识库编号
+     */
+    private Long knowledgeId;
+
+    /**
+     * 内容
+     */
+    private String content;
+    /**
+     * 内容长度
+     */
+    private Integer contentLength;
+
+    /**
+     * Token 数量
+     */
+    private Integer tokens;
+
+    /**
+     * 相似度分数
+     */
+    private Double score;
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/mindmap/AiMindMapService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/mindmap/AiMindMapService.java
new file mode 100644
index 0000000..ed4e626
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/mindmap/AiMindMapService.java
@@ -0,0 +1,41 @@
+package com.iailab.module.ai.service.mindmap;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
+import com.iailab.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
+import com.iailab.module.ai.dal.dataobject.mindmap.AiMindMapDO;
+import reactor.core.publisher.Flux;
+
+/**
+ * AI 思维导图 Service 接口
+ *
+ * @author xiaoxin
+ */
+public interface AiMindMapService {
+
+    /**
+     * 生成思维导图内容
+     *
+     * @param generateReqVO 请求参数
+     * @param userId        用户编号
+     * @return 生成结果
+     */
+    Flux<CommonResult<String>> generateMindMap(AiMindMapGenerateReqVO generateReqVO, Long userId);
+
+    /**
+     * 删除思维导图
+     *
+     * @param id 编号
+     */
+    void deleteMindMap(Long id);
+
+    /**
+     * 获得思维导图分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 思维导图分页
+     */
+    PageResult<AiMindMapDO> getMindMapPage(AiMindMapPageReqVO pageReqVO);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/mindmap/AiMindMapServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/mindmap/AiMindMapServiceImpl.java
new file mode 100644
index 0000000..868783f
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/mindmap/AiMindMapServiceImpl.java
@@ -0,0 +1,161 @@
+package com.iailab.module.ai.service.mindmap;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import com.iailab.framework.ai.core.enums.AiModelTypeEnum;
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.ai.core.util.AiUtils;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.tenant.core.util.TenantUtils;
+import com.iailab.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
+import com.iailab.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
+import com.iailab.module.ai.dal.dataobject.mindmap.AiMindMapDO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.dal.mysql.mindmap.AiMindMapMapper;
+import com.iailab.module.ai.enums.AiChatRoleEnum;
+import com.iailab.module.ai.enums.ErrorCodeConstants;
+import com.iailab.module.ai.service.model.AiChatRoleService;
+import com.iailab.module.ai.service.model.AiModelService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.framework.common.pojo.CommonResult.error;
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.*;
+
+/**
+ * AI 思维导图 Service 实现类
+ *
+ * @author xiaoxin
+ */
+@Service
+@Slf4j
+public class AiMindMapServiceImpl implements AiMindMapService {
+
+    @Resource
+    private AiModelService modalService;
+    @Resource
+    private AiChatRoleService chatRoleService;
+
+    @Resource
+    private AiMindMapMapper mindMapMapper;
+
+    @Override
+    public Flux<CommonResult<String>> generateMindMap(AiMindMapGenerateReqVO generateReqVO, Long userId) {
+        // 1. 获取导图模型。尝试获取思维导图助手角色,如果没有则使用默认模型
+        AiChatRoleDO role = CollUtil.getFirst(
+                chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName()));
+        // 1.1 获取导图执行模型
+        AiModelDO model = getModel(role);
+        // 1.2 获取角色设定消息
+        String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage())
+                ? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage();
+        // 1.3 校验平台
+        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
+        ChatModel chatModel = modalService.getChatModel(model.getId());
+
+        // 2. 插入思维导图信息
+        AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, mindMap -> mindMap.setUserId(userId)
+                .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));
+        mindMapMapper.insert(mindMapDO);
+
+        // 3.1 构建 Prompt,并进行调用
+        Prompt prompt = buildPrompt(generateReqVO, model, systemMessage);
+        Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
+
+        // 3.2 流式返回
+        StringBuffer contentBuffer = new StringBuffer();
+        return streamResponse.map(chunk -> {
+            String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
+            newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
+            contentBuffer.append(newContent);
+            // 响应结果
+            return success(newContent);
+        }).doOnComplete(() -> {
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() ->
+                    mindMapMapper.updateById(new AiMindMapDO().setId(mindMapDO.getId()).setGeneratedContent(contentBuffer.toString())));
+        }).doOnError(throwable -> {
+            log.error("[generateWriteContent][generateReqVO({}) 发生异常]", generateReqVO, throwable);
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() ->
+                    mindMapMapper.updateById(new AiMindMapDO().setId(mindMapDO.getId()).setErrorMessage(throwable.getMessage())));
+        }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
+
+    }
+
+    private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {
+        // 1. 构建 message 列表
+        List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
+        // 2. 构建 options 对象
+        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
+        ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());
+        return new Prompt(chatMessages, options);
+    }
+
+    private static List<Message> buildMessages(AiMindMapGenerateReqVO generateReqVO, String systemMessage) {
+        List<Message> chatMessages = new ArrayList<>();
+        // 1. 角色设定
+        if (StrUtil.isNotBlank(systemMessage)) {
+            chatMessages.add(new SystemMessage(systemMessage));
+        }
+        // 2. 用户输入
+        chatMessages.add(new UserMessage(generateReqVO.getPrompt()));
+        return chatMessages;
+    }
+
+    private AiModelDO getModel(AiChatRoleDO role) {
+        AiModelDO model = null;
+        if (role != null && role.getModelId() != null) {
+            model = modalService.getModel(role.getModelId());
+        }
+        if (model == null) {
+            model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
+        }
+        // 校验模型存在、且合法
+        if (model == null) {
+            throw exception(MODEL_NOT_EXISTS);
+        }
+        if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) {
+            throw exception(MODEL_USE_TYPE_ERROR);
+        }
+        return model;
+    }
+
+    @Override
+    public void deleteMindMap(Long id) {
+        // 校验存在
+        validateMindMapExists(id);
+        // 删除
+        mindMapMapper.deleteById(id);
+    }
+
+    private void validateMindMapExists(Long id) {
+        if (mindMapMapper.selectById(id) == null) {
+            throw exception(MIND_MAP_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public PageResult<AiMindMapDO> getMindMapPage(AiMindMapPageReqVO pageReqVO) {
+        return mindMapMapper.selectPage(pageReqVO);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiApiKeyService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiApiKeyService.java
new file mode 100644
index 0000000..44b9d64
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiApiKeyService.java
@@ -0,0 +1,80 @@
+package com.iailab.module.ai.service.model;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiApiKeyDO;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/**
+ * AI API 密钥 Service 接口
+ *
+ * @author Iailab
+ */
+public interface AiApiKeyService {
+
+    /**
+     * 创建 API 密钥
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createApiKey(@Valid AiApiKeySaveReqVO createReqVO);
+
+    /**
+     * 更新 API 密钥
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateApiKey(@Valid AiApiKeySaveReqVO updateReqVO);
+
+    /**
+     * 删除 API 密钥
+     *
+     * @param id 编号
+     */
+    void deleteApiKey(Long id);
+
+    /**
+     * 获得 API 密钥
+     *
+     * @param id 编号
+     * @return API 密钥
+     */
+    AiApiKeyDO getApiKey(Long id);
+
+    /**
+     * 校验 API 密钥
+     *
+     * @param id 比那好
+     * @return API 密钥
+     */
+    AiApiKeyDO validateApiKey(Long id);
+
+    /**
+     * 获得 API 密钥分页
+     *
+     * @param pageReqVO 分页查询
+     * @return API 密钥分页
+     */
+    PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO);
+
+    /**
+     * 获得 API 密钥列表
+     *
+     * @return API 密钥列表
+     */
+    List<AiApiKeyDO> getApiKeyList();
+
+    /**
+     * 获得默认的 API 密钥
+     *
+     * @param platform 平台
+     * @param status 状态
+     * @return API 密钥
+     */
+    AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status);
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiApiKeyServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiApiKeyServiceImpl.java
new file mode 100644
index 0000000..f1ec456
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiApiKeyServiceImpl.java
@@ -0,0 +1,99 @@
+package com.iailab.module.ai.service.model;
+
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiApiKeyDO;
+import com.iailab.module.ai.dal.mysql.model.AiApiKeyMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.API_KEY_DISABLE;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.API_KEY_NOT_EXISTS;
+
+/**
+ * AI API 密钥 Service 实现类
+ *
+ * @author Iailab
+ */
+@Service
+@Validated
+public class AiApiKeyServiceImpl implements AiApiKeyService {
+
+    @Resource
+    private AiApiKeyMapper apiKeyMapper;
+
+    @Override
+    public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
+        // 插入
+        AiApiKeyDO apiKey = BeanUtils.toBean(createReqVO, AiApiKeyDO.class);
+        apiKeyMapper.insert(apiKey);
+        // 返回
+        return apiKey.getId();
+    }
+
+    @Override
+    public void updateApiKey(AiApiKeySaveReqVO updateReqVO) {
+        // 校验存在
+        validateApiKeyExists(updateReqVO.getId());
+        // 更新
+        AiApiKeyDO updateObj = BeanUtils.toBean(updateReqVO, AiApiKeyDO.class);
+        apiKeyMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteApiKey(Long id) {
+        // 校验存在
+        validateApiKeyExists(id);
+        // 删除
+        apiKeyMapper.deleteById(id);
+    }
+
+    private AiApiKeyDO validateApiKeyExists(Long id) {
+        AiApiKeyDO apiKey = apiKeyMapper.selectById(id);
+        if (apiKey == null) {
+            throw exception(API_KEY_NOT_EXISTS);
+        }
+        return apiKey;
+    }
+
+    @Override
+    public AiApiKeyDO getApiKey(Long id) {
+        return apiKeyMapper.selectById(id);
+    }
+
+    @Override
+    public AiApiKeyDO validateApiKey(Long id) {
+        AiApiKeyDO apiKey = validateApiKeyExists(id);
+        if (CommonStatusEnum.isDisable(apiKey.getStatus())) {
+            throw exception(API_KEY_DISABLE);
+        }
+        return apiKey;
+    }
+
+    @Override
+    public PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO) {
+        return apiKeyMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<AiApiKeyDO> getApiKeyList() {
+        return apiKeyMapper.selectList();
+    }
+
+    @Override
+    public AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status) {
+        AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform, status);
+        if (apiKey == null) {
+            throw exception(API_KEY_NOT_EXISTS);
+        }
+        return apiKey;
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiChatRoleService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiChatRoleService.java
new file mode 100644
index 0000000..63185a9
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiChatRoleService.java
@@ -0,0 +1,129 @@
+package com.iailab.module.ai.service.model;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import jakarta.validation.Valid;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertMap;
+
+/**
+ * AI 聊天角色 Service 接口
+ *
+ * @author fansili
+ */
+public interface AiChatRoleService {
+
+    /**
+     * 创建聊天角色
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createChatRole(@Valid AiChatRoleSaveReqVO createReqVO);
+
+    /**
+     * 创建【我的】聊天角色
+     *
+     * @param createReqVO 创建信息
+     * @param userId      用户编号
+     * @return 编号
+     */
+    Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId);
+
+    /**
+     * 更新聊天角色
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateChatRole(@Valid AiChatRoleSaveReqVO updateReqVO);
+
+    /**
+     * 创建【我的】聊天角色
+     *
+     * @param updateReqVO 更新信息
+     * @param userId      用户编号
+     */
+    void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId);
+
+    /**
+     * 删除聊天角色
+     *
+     * @param id 编号
+     */
+    void deleteChatRole(Long id);
+
+    /**
+     * 删除【我的】聊天角色
+     *
+     * @param id     编号
+     * @param userId 用户编号
+     */
+    void deleteChatRoleMy(Long id, Long userId);
+
+    /**
+     * 获得聊天角色
+     *
+     * @param id 编号
+     * @return AI 聊天角色
+     */
+    AiChatRoleDO getChatRole(Long id);
+
+    /**
+     * 获得聊天角色列表
+     *
+     * @param ids 编号数组
+     * @return 聊天角色列表
+     */
+    List<AiChatRoleDO> getChatRoleList(Collection<Long> ids);
+
+    default Map<Long, AiChatRoleDO> getChatRoleMap(Collection<Long> ids) {
+        return convertMap(getChatRoleList(ids), AiChatRoleDO::getId);
+    }
+
+    /**
+     * 校验聊天角色是否合法
+     *
+     * @param id 角色编号
+     */
+    AiChatRoleDO validateChatRole(Long id);
+
+    /**
+     * 获得聊天角色分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 聊天角色分页
+     */
+    PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO);
+
+    /**
+     * 获得【我的】聊天角色分页
+     *
+     * @param pageReqVO 分页查询
+     * @param userId    用户编号
+     * @return 聊天角色分页
+     */
+    PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId);
+
+    /**
+     * 获得聊天角色的分类列表
+     *
+     * @return 分类列表
+     */
+    List<String> getChatRoleCategoryList();
+
+    /**
+     * 根据名字获得聊天角色
+     *
+     * @param name 名字
+     * @return 聊天角色列表
+     */
+    List<AiChatRoleDO> getChatRoleListByName(String name);
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiChatRoleServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiChatRoleServiceImpl.java
new file mode 100644
index 0000000..a84b4a7
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiChatRoleServiceImpl.java
@@ -0,0 +1,200 @@
+package com.iailab.module.ai.service.model;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.iailab.module.ai.dal.mysql.model.AiChatRoleMapper;
+import com.iailab.module.ai.service.knowledge.AiKnowledgeService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_DISABLE;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_NOT_EXISTS;
+
+/**
+ * AI 聊天角色 Service 实现类
+ *
+ * @author fansili
+ */
+@Service
+@Slf4j
+public class AiChatRoleServiceImpl implements AiChatRoleService {
+
+    @Resource
+    private AiChatRoleMapper chatRoleMapper;
+
+    @Resource
+    private AiKnowledgeService knowledgeService;
+    @Resource
+    private AiToolService toolService;
+
+    @Override
+    public Long createChatRole(AiChatRoleSaveReqVO createReqVO) {
+        // 校验文档
+        validateDocuments(createReqVO.getKnowledgeIds());
+        // 校验工具
+        validateTools(createReqVO.getToolIds());
+
+        // 保存角色
+        AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class);
+        chatRoleMapper.insert(chatRole);
+        return chatRole.getId();
+    }
+
+    @Override
+    public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) {
+        // 校验文档
+        validateDocuments(createReqVO.getKnowledgeIds());
+        // 校验工具
+        validateTools(createReqVO.getToolIds());
+
+        // 保存角色
+        AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId)
+                .setStatus(CommonStatusEnum.ENABLE.getStatus()).setPublicStatus(false);
+        chatRoleMapper.insert(chatRole);
+        return chatRole.getId();
+    }
+
+    @Override
+    public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) {
+        // 校验存在
+        validateChatRoleExists(updateReqVO.getId());
+        // 校验文档
+        validateDocuments(updateReqVO.getKnowledgeIds());
+        // 校验工具
+        validateTools(updateReqVO.getToolIds());
+
+        // 更新角色
+        AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
+        chatRoleMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId) {
+        // 校验存在
+        AiChatRoleDO chatRole = validateChatRoleExists(updateReqVO.getId());
+        if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {
+            throw exception(CHAT_ROLE_NOT_EXISTS);
+        }
+        // 校验文档
+        validateDocuments(updateReqVO.getKnowledgeIds());
+        // 校验工具
+        validateTools(updateReqVO.getToolIds());
+
+        // 更新
+        AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
+        chatRoleMapper.updateById(updateObj);
+    }
+
+    /**
+     * 校验知识库是否存在
+     *
+     * @param knowledgeIds 知识库编号列表
+     */
+    private void validateDocuments(List<Long> knowledgeIds) {
+        if (CollUtil.isEmpty(knowledgeIds)) {
+            return;
+        }
+        // 校验文档是否存在
+        knowledgeIds.forEach(knowledgeService::validateKnowledgeExists);
+    }
+
+    /**
+     * 校验工具是否存在
+     *
+     * @param toolIds 工具编号列表
+     */
+    private void validateTools(List<Long> toolIds) {
+        if (CollUtil.isEmpty(toolIds)) {
+            return;
+        }
+        // 遍历校验每个工具是否存在
+        toolIds.forEach(toolService::validateToolExists);
+    }
+
+    @Override
+    public void deleteChatRole(Long id) {
+        // 校验存在
+        validateChatRoleExists(id);
+        // 删除
+        chatRoleMapper.deleteById(id);
+    }
+
+    @Override
+    public void deleteChatRoleMy(Long id, Long userId) {
+        // 校验存在
+        AiChatRoleDO chatRole = validateChatRoleExists(id);
+        if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {
+            throw exception(CHAT_ROLE_NOT_EXISTS);
+        }
+        // 删除
+        chatRoleMapper.deleteById(id);
+    }
+
+    private AiChatRoleDO validateChatRoleExists(Long id) {
+        AiChatRoleDO chatRole = chatRoleMapper.selectById(id);
+        if (chatRole == null) {
+            throw exception(CHAT_ROLE_NOT_EXISTS);
+        }
+        return chatRole;
+    }
+
+    @Override
+    public AiChatRoleDO getChatRole(Long id) {
+        return chatRoleMapper.selectById(id);
+    }
+
+    @Override
+    public List<AiChatRoleDO> getChatRoleList(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        return chatRoleMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public AiChatRoleDO validateChatRole(Long id) {
+        AiChatRoleDO chatRole = validateChatRoleExists(id);
+        if (CommonStatusEnum.isDisable(chatRole.getStatus())) {
+            throw exception(CHAT_ROLE_DISABLE, chatRole.getName());
+        }
+        return chatRole;
+    }
+
+    @Override
+    public PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO) {
+        return chatRoleMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId) {
+        return chatRoleMapper.selectPageByMy(pageReqVO, userId);
+    }
+
+    @Override
+    public List<String> getChatRoleCategoryList() {
+        List<AiChatRoleDO> list = chatRoleMapper.selectListGroupByCategory(CommonStatusEnum.ENABLE.getStatus());
+        return convertList(list, AiChatRoleDO::getCategory,
+                role -> role != null && StrUtil.isNotBlank(role.getCategory()));
+    }
+
+    @Override
+    public List<AiChatRoleDO> getChatRoleListByName(String name) {
+        return chatRoleMapper.selectListByName(name);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiModelService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiModelService.java
new file mode 100644
index 0000000..2883e16
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiModelService.java
@@ -0,0 +1,142 @@
+package com.iailab.module.ai.service.model;
+
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import com.iailab.framework.ai.core.model.suno.api.SunoApi;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import jakarta.validation.Valid;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.image.ImageModel;
+import org.springframework.ai.vectorstore.VectorStore;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * AI 模型 Service 接口
+ *
+ * @author fansili
+ * @since 2024/4/24 19:42
+ */
+public interface AiModelService {
+
+    /**
+     * 创建模型
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createModel(@Valid AiModelSaveReqVO createReqVO);
+
+    /**
+     * 更新模型
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateModel(@Valid AiModelSaveReqVO updateReqVO);
+
+    /**
+     * 删除模型
+     *
+     * @param id 编号
+     */
+    void deleteModel(Long id);
+
+    /**
+     * 获得模型
+     *
+     * @param id 编号
+     * @return 模型
+     */
+    AiModelDO getModel(Long id);
+
+    /**
+     * 根据模型名称获得模型(工业大模型使用)
+     *
+     * @param name
+     * @return 模型
+     */
+    AiModelDO getModelByName(String name);
+
+    /**
+     * 获得默认的模型
+     *
+     * 如果获取不到,则抛出 {@link com.iailab.framework.common.exception.ServiceException} 业务异常
+     *
+     * @return 模型
+     */
+    AiModelDO getRequiredDefaultModel(Integer type);
+
+    /**
+     * 获得模型分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 模型分页
+     */
+    PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO);
+
+    /**
+     * 校验模型是否可使用
+     *
+     * @param id 编号
+     * @return 模型
+     */
+    AiModelDO validateModel(Long id);
+
+    /**
+     * 获得模型列表
+     *
+     * @param status 状态
+     * @param type 类型
+     * @param platform 平台,允许空
+     * @return 模型列表
+     */
+    List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type,
+                                                @Nullable String platform);
+
+    // ========== 与 Spring AI 集成 ==========
+
+    /**
+     * 获得 ChatModel 对象
+     *
+     * @param id 编号
+     * @return ChatModel 对象
+     */
+    ChatModel getChatModel(Long id);
+
+    /**
+     * 获得 ImageModel 对象
+     *
+     * @param id 编号
+     * @return ImageModel 对象
+     */
+    ImageModel getImageModel(Long id);
+
+    /**
+     * 获得 MidjourneyApi 对象
+     *
+     * @param id 编号
+     * @return MidjourneyApi 对象
+     */
+    MidjourneyApi getMidjourneyApi(Long id);
+
+    /**
+     * 获得 SunoApi 对象
+     *
+     * @return SunoApi 对象
+     */
+    SunoApi getSunoApi();
+
+    /**
+     * 获得 VectorStore 对象
+     *
+     * @param id 编号
+     * @param metadataFields 元数据的定义
+     * @return VectorStore 对象
+     */
+    VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiModelServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiModelServiceImpl.java
new file mode 100644
index 0000000..fead3d1
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiModelServiceImpl.java
@@ -0,0 +1,192 @@
+package com.iailab.module.ai.service.model;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.ai.core.factory.AiModelFactory;
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import com.iailab.framework.ai.core.model.suno.api.SunoApi;
+import com.iailab.framework.common.enums.CommonStatusEnum;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.iailab.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiApiKeyDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.dal.mysql.model.AiChatMapper;
+import jakarta.annotation.Resource;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.image.ImageModel;
+import org.springframework.ai.vectorstore.SimpleVectorStore;
+import org.springframework.ai.vectorstore.VectorStore;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.*;
+
+/**
+ * AI 模型 Service 实现类
+ *
+ * @author fansili
+ */
+@Service
+@Validated
+public class AiModelServiceImpl implements AiModelService {
+
+    @Resource
+    private AiApiKeyService apiKeyService;
+
+    @Resource
+    private AiChatMapper modelMapper;
+
+    @Resource
+    private AiModelFactory modelFactory;
+
+    @Override
+    public Long createModel(AiModelSaveReqVO createReqVO) {
+        // 1. 校验
+        AiPlatformEnum.validatePlatform(createReqVO.getPlatform());
+        apiKeyService.validateApiKey(createReqVO.getKeyId());
+
+        //模型名称不能重复
+        List<AiModelDO> aiModelDOS = modelMapper.selectList(new LambdaQueryWrapperX<AiModelDO>().eq(AiModelDO::getName, createReqVO.getName()));
+        if (aiModelDOS.size() > 0) {
+            throw exception(MODEL_NAME_EXISTS);
+        }
+
+        // 2. 插入
+        AiModelDO model = BeanUtils.toBean(createReqVO, AiModelDO.class);
+        modelMapper.insert(model);
+        return model.getId();
+    }
+
+    @Override
+    public void updateModel(AiModelSaveReqVO updateReqVO) {
+        // 1. 校验
+        validateModelExists(updateReqVO.getId());
+        AiPlatformEnum.validatePlatform(updateReqVO.getPlatform());
+        apiKeyService.validateApiKey(updateReqVO.getKeyId());
+
+        //模型名称不能重复
+        List<AiModelDO> aiModelDOS = modelMapper.selectList(new LambdaQueryWrapperX<AiModelDO>()
+                .eq(AiModelDO::getName, updateReqVO.getName())
+                .ne(AiModelDO::getId, updateReqVO.getId()));
+        if (aiModelDOS.size() > 0) {
+            throw exception(MODEL_NAME_EXISTS);
+        }
+
+        // 2. 更新
+        AiModelDO updateObj = BeanUtils.toBean(updateReqVO, AiModelDO.class);
+        modelMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteModel(Long id) {
+        // 校验存在
+        validateModelExists(id);
+        // 删除
+        modelMapper.deleteById(id);
+    }
+
+    private AiModelDO validateModelExists(Long id) {
+        AiModelDO model = modelMapper.selectById(id);
+        if (modelMapper.selectById(id) == null) {
+            throw exception(MODEL_NOT_EXISTS);
+        }
+        return model;
+    }
+
+    @Override
+    public AiModelDO getModel(Long id) {
+        return modelMapper.selectById(id);
+    }
+
+    @Override
+    public AiModelDO getModelByName(String name) {
+        return modelMapper.selectOne(new LambdaQueryWrapper<AiModelDO>().eq(AiModelDO::getName, name));
+    }
+
+    @Override
+    public AiModelDO getRequiredDefaultModel(Integer type) {
+        AiModelDO model = modelMapper.selectFirstByStatus(type, CommonStatusEnum.ENABLE.getStatus());
+        if (model == null) {
+            throw exception(MODEL_DEFAULT_NOT_EXISTS);
+        }
+        return model;
+    }
+
+    @Override
+    public PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO) {
+        return modelMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public AiModelDO validateModel(Long id) {
+        AiModelDO model = validateModelExists(id);
+        if (CommonStatusEnum.isDisable(model.getStatus())) {
+            throw exception(MODEL_DISABLE);
+        }
+        return model;
+    }
+
+    @Override
+    public List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type, String platform) {
+        return modelMapper.selectListByStatusAndType(status, type, platform);
+    }
+
+    // ========== 与 Spring AI 集成 ==========
+
+    @Override
+    public ChatModel getChatModel(Long id) {
+        AiModelDO model = validateModel(id);
+        AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
+        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
+        return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl());
+    }
+
+    @Override
+    public ImageModel getImageModel(Long id) {
+        AiModelDO model = validateModel(id);
+        AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
+        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
+        return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl());
+    }
+
+    @Override
+    public MidjourneyApi getMidjourneyApi(Long id) {
+        AiModelDO model = validateModel(id);
+        AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
+        return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());
+    }
+
+    @Override
+    public SunoApi getSunoApi() {
+        AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey(
+                AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
+        return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());
+    }
+
+    @Override
+    public VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields) {
+        // 获取模型 + 密钥
+        AiModelDO model = validateModel(id);
+        AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
+        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
+
+        // 创建或获取 EmbeddingModel 对象
+        EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel(
+                platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel());
+
+        // 创建或获取 VectorStore 对象
+         return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields);
+//         return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields);
+//         return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields);
+//         return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields);
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiToolService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiToolService.java
new file mode 100644
index 0000000..58c9498
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiToolService.java
@@ -0,0 +1,80 @@
+package com.iailab.module.ai.service.model;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiToolDO;
+import jakarta.validation.Valid;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * AI 工具 Service 接口
+ *
+ * @author Iailab
+ */
+public interface AiToolService {
+
+    /**
+     * 创建工具
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createTool(@Valid AiToolSaveReqVO createReqVO);
+
+    /**
+     * 更新工具
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateTool(@Valid AiToolSaveReqVO updateReqVO);
+
+    /**
+     * 删除工具
+     *
+     * @param id 编号
+     */
+    void deleteTool(Long id);
+
+    /**
+     * 校验工具是否存在
+     *
+     * @param id 编号
+     */
+    void validateToolExists(Long id);
+
+    /**
+     * 获得工具
+     *
+     * @param id 编号
+     * @return 工具
+     */
+    AiToolDO getTool(Long id);
+
+    /**
+     * 获得工具列表
+     *
+     * @param ids 编号列表
+     * @return 工具列表
+     */
+    List<AiToolDO> getToolList(Collection<Long> ids);
+
+    /**
+     * 获得工具分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 工具分页
+     */
+    PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO);
+
+    /**
+     * 获得工具列表
+     *
+     * @param status 状态
+     * @return 工具列表
+     */
+    List<AiToolDO> getToolListByStatus(Integer status);
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiToolServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiToolServiceImpl.java
new file mode 100644
index 0000000..8971032
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/AiToolServiceImpl.java
@@ -0,0 +1,100 @@
+package com.iailab.module.ai.service.model;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
+import com.iailab.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiToolDO;
+import com.iailab.module.ai.dal.mysql.model.AiToolMapper;
+import jakarta.annotation.Resource;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.Collection;
+import java.util.List;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.TOOL_NAME_NOT_EXISTS;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.TOOL_NOT_EXISTS;
+
+/**
+ * AI 工具 Service 实现类
+ *
+ * @author Iailab
+ */
+@Service
+@Validated
+public class AiToolServiceImpl implements AiToolService {
+
+    @Resource
+    private AiToolMapper toolMapper;
+
+    @Override
+    public Long createTool(AiToolSaveReqVO createReqVO) {
+        // 校验名称是否存在
+        validateToolNameExists(createReqVO.getName());
+
+        // 插入
+        AiToolDO tool = BeanUtils.toBean(createReqVO, AiToolDO.class);
+        toolMapper.insert(tool);
+        return tool.getId();
+    }
+
+    @Override
+    public void updateTool(AiToolSaveReqVO updateReqVO) {
+        // 1.1 校验存在
+        validateToolExists(updateReqVO.getId());
+        // 1.2 校验名称是否存在
+        validateToolNameExists(updateReqVO.getName());
+
+        // 2. 更新
+        AiToolDO updateObj = BeanUtils.toBean(updateReqVO, AiToolDO.class);
+        toolMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteTool(Long id) {
+        // 校验存在
+        validateToolExists(id);
+        // 删除
+        toolMapper.deleteById(id);
+    }
+
+    @Override
+    public void validateToolExists(Long id) {
+        if (toolMapper.selectById(id) == null) {
+            throw exception(TOOL_NOT_EXISTS);
+        }
+    }
+
+    private void validateToolNameExists(String name) {
+        try {
+            SpringUtil.getBean(name);
+        } catch (NoSuchBeanDefinitionException e) {
+            throw exception(TOOL_NAME_NOT_EXISTS, name);
+        }
+    }
+
+    @Override
+    public AiToolDO getTool(Long id) {
+        return toolMapper.selectById(id);
+    }
+
+    @Override
+    public List<AiToolDO> getToolList(Collection<Long> ids) {
+        return toolMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO) {
+        return toolMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<AiToolDO> getToolListByStatus(Integer status) {
+        return toolMapper.selectListByStatus(status);
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/tool/DirectoryListToolFunction.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/tool/DirectoryListToolFunction.java
new file mode 100644
index 0000000..aed1023
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/tool/DirectoryListToolFunction.java
@@ -0,0 +1,99 @@
+package com.iailab.module.ai.service.model.tool;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.annotation.JsonClassDescription;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+
+/**
+ * 工具:列出指定目录的文件列表
+ *
+ * @author Iailab
+ */
+@Component("directory_list")
+public class DirectoryListToolFunction implements Function<DirectoryListToolFunction.Request, DirectoryListToolFunction.Response> {
+
+    @Data
+    @JsonClassDescription("列出指定目录的文件列表")
+    public static class Request {
+
+        /**
+         * 目录路径
+         */
+        @JsonProperty(required = true, value = "path")
+        @JsonPropertyDescription("目录路径,例如说:/Users/yunai")
+        private String path;
+
+    }
+
+    @Data
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class Response {
+
+        /**
+         * 文件列表
+         */
+        private List<File> files;
+
+        @Data
+        public static class File {
+
+            /**
+             * 是否为目录
+             */
+            private Boolean directory;
+
+            /**
+             * 名称
+             */
+            private String name;
+
+            /**
+             * 大小,仅对文件有效
+             */
+            private String size;
+
+            /**
+             * 最后修改时间
+             */
+            private String lastModified;
+
+        }
+
+    }
+
+    @Override
+    public Response apply(Request request) {
+        // 校验目录存在
+        String path = StrUtil.blankToDefault(request.getPath(), "/");
+        if (!FileUtil.exist(path) || !FileUtil.isDirectory(path)) {
+            return new Response(Collections.emptyList());
+        }
+        // 列出目录
+        File[] files = FileUtil.ls(path);
+        if (ArrayUtil.isEmpty(files)) {
+            return new Response(Collections.emptyList());
+        }
+        return new Response(convertList(Arrays.asList(files), file ->
+                new Response.File().setDirectory(file.isDirectory()).setName(file.getName())
+                        .setLastModified(LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN))));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/tool/WeatherQueryToolFunction.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/tool/WeatherQueryToolFunction.java
new file mode 100644
index 0000000..48ff944
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/model/tool/WeatherQueryToolFunction.java
@@ -0,0 +1,118 @@
+package com.iailab.module.ai.service.model.tool;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.annotation.JsonClassDescription;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.function.Function;
+
+import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;
+
+/**
+ * 工具:查询指定城市的天气信息
+ *
+ * @author Iailab
+ */
+@Component("weather_query")
+public class WeatherQueryToolFunction
+        implements Function<WeatherQueryToolFunction.Request, WeatherQueryToolFunction.Response> {
+
+    private static final String[] WEATHER_CONDITIONS = { "晴朗", "多云", "阴天", "小雨", "大雨", "雷雨", "小雪", "大雪" };
+
+    @Data
+    @JsonClassDescription("查询指定城市的天气信息")
+    public static class Request {
+
+        /**
+         * 城市名称
+         */
+        @JsonProperty(required = true, value = "city")
+        @JsonPropertyDescription("城市名称,例如:北京、上海、广州")
+        private String city;
+
+    }
+
+    @Data
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class Response {
+
+        /**
+         * 城市名称
+         */
+        private String city;
+
+        /**
+         * 天气信息
+         */
+        private WeatherInfo weatherInfo;
+
+        @Data
+        @AllArgsConstructor
+        @NoArgsConstructor
+        public static class WeatherInfo {
+
+            /**
+             * 温度(摄氏度)
+             */
+            private Integer temperature;
+
+            /**
+             * 天气状况
+             */
+            private String condition;
+
+            /**
+             * 湿度百分比
+             */
+            private Integer humidity;
+
+            /**
+             * 风速(km/h)
+             */
+            private Integer windSpeed;
+
+            /**
+             * 查询时间
+             */
+            private String queryTime;
+
+        }
+
+    }
+
+    @Override
+    public Response apply(Request request) {
+        // 检查城市名称是否为空
+        if (StrUtil.isBlank(request.getCity())) {
+            return new Response("未知城市", null);
+        }
+
+        // 获取天气数据
+        String city = request.getCity();
+        Response.WeatherInfo weatherInfo = generateMockWeatherInfo();
+        return new Response(city, weatherInfo);
+    }
+
+    /**
+     * 生成模拟的天气数据
+     * 在实际应用中,应替换为真实 API 调用
+     */
+    private Response.WeatherInfo generateMockWeatherInfo() {
+        int temperature = RandomUtil.randomInt(-5, 30);
+        int humidity = RandomUtil.randomInt(1, 100);
+        int windSpeed = RandomUtil.randomInt(1, 30);
+        String condition = RandomUtil.randomEle(WEATHER_CONDITIONS);
+        return new Response.WeatherInfo(temperature, condition, humidity, windSpeed,
+                LocalDateTimeUtil.format(LocalDateTime.now(), NORM_DATETIME_PATTERN));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/music/AiMusicService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/music/AiMusicService.java
new file mode 100644
index 0000000..49ad232
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/music/AiMusicService.java
@@ -0,0 +1,90 @@
+package com.iailab.module.ai.service.music;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
+import com.iailab.module.ai.controller.admin.music.vo.AiMusicUpdateMyReqVO;
+import com.iailab.module.ai.controller.admin.music.vo.AiMusicUpdateReqVO;
+import com.iailab.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
+import com.iailab.module.ai.dal.dataobject.music.AiMusicDO;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/**
+ * AI 音乐 Service 接口
+ *
+ * @author xiaoxin
+ */
+public interface AiMusicService {
+
+    /**
+     * 音乐生成
+     *
+     * @param userId 用户编号
+     * @param reqVO  请求参数
+     * @return 生成的音乐ID
+     */
+    List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO);
+
+    /**
+     * 同步音乐任务
+     *
+     * @return 同步数量
+     */
+    Integer syncMusic();
+
+    /**
+     * 更新音乐发布状态
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateMusic(@Valid AiMusicUpdateReqVO updateReqVO);
+
+    /**
+     * 更新我的音乐
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateMyMusic(@Valid AiMusicUpdateMyReqVO updateReqVO, Long userId);
+
+    /**
+     * 删除AI 音乐
+     *
+     * @param id 编号
+     */
+    void deleteMusic(Long id);
+
+    /**
+     * 删除【我的】音乐记录
+     *
+     * @param id     音乐编号
+     * @param userId 用户编号
+     */
+    void deleteMusicMy(Long id, Long userId);
+
+    /**
+     * 获得AI 音乐
+     *
+     * @param id 音乐编号
+     * @return 音乐内容
+     */
+    AiMusicDO getMusic(Long id);
+
+    /**
+     * 获得音乐分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 音乐分页
+     */
+    PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO);
+
+    /**
+     * 获得【我的】音乐分页
+     *
+     * @param pageReqVO 分页查询
+     * @param userId    用户编号
+     * @return 音乐分页
+     */
+    PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/music/AiMusicServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/music/AiMusicServiceImpl.java
new file mode 100644
index 0000000..a44b827
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/music/AiMusicServiceImpl.java
@@ -0,0 +1,218 @@
+package com.iailab.module.ai.service.music;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.StrPool;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
+import com.iailab.framework.ai.core.model.suno.api.SunoApi;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
+import com.iailab.module.ai.controller.admin.music.vo.AiMusicUpdateMyReqVO;
+import com.iailab.module.ai.controller.admin.music.vo.AiMusicUpdateReqVO;
+import com.iailab.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
+import com.iailab.module.ai.dal.dataobject.music.AiMusicDO;
+import com.iailab.module.ai.dal.mysql.music.AiMusicMapper;
+import com.iailab.module.ai.enums.music.AiMusicGenerateModeEnum;
+import com.iailab.module.ai.enums.music.AiMusicStatusEnum;
+import com.iailab.module.ai.service.model.AiModelService;
+import com.iailab.module.infra.api.file.FileApi;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertMap;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.IMAGE_NOT_EXISTS;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXISTS;
+
+/**
+ * AI 音乐 Service 实现类
+ *
+ * @author xiaoxin
+ */
+@Service
+@Slf4j
+public class AiMusicServiceImpl implements AiMusicService {
+
+    @Resource
+    private AiModelService modelService;
+
+    @Resource
+    private AiMusicMapper musicMapper;
+
+    @Resource
+    private FileApi fileApi;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {
+        // 1. 调用 Suno 生成音乐
+        SunoApi sunoApi = modelService.getSunoApi();
+        List<SunoApi.MusicData> musicDataList;
+        if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
+            // 1.1 描述模式
+            SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
+                    reqVO.getPrompt(), reqVO.getModel(), reqVO.getMakeInstrumental());
+            musicDataList = sunoApi.generate(generateRequest);
+        } else if (Objects.equals(AiMusicGenerateModeEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {
+            // 1.2 歌词模式
+            SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
+                    reqVO.getPrompt(), reqVO.getModel(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
+            musicDataList = sunoApi.customGenerate(generateRequest);
+        } else {
+            throw new IllegalArgumentException(StrUtil.format("未知生成模式({})", reqVO));
+        }
+
+        // 2. 插入数据库
+        if (CollUtil.isEmpty(musicDataList)) {
+            return Collections.emptyList();
+        }
+        List<AiMusicDO> musicList = buildMusicDOList(musicDataList);
+        musicList.forEach(music -> music.setUserId(userId).setPlatform(reqVO.getPlatform()).setGenerateMode(reqVO.getGenerateMode()));
+        musicMapper.insertBatch(musicList);
+        return convertList(musicList, AiMusicDO::getId);
+    }
+
+    @Override
+    public Integer syncMusic() {
+        List<AiMusicDO> streamingTask = musicMapper.selectListByStatus(AiMusicStatusEnum.IN_PROGRESS.getStatus());
+        if (CollUtil.isEmpty(streamingTask)) {
+            return 0;
+        }
+        log.info("[syncMusic][Suno 开始同步, 共 ({}) 个任务]", streamingTask.size());
+
+        // GET 请求,为避免参数过长,分批次处理
+        SunoApi sunoApi = modelService.getSunoApi();
+        CollUtil.split(streamingTask, 36).forEach(chunkList -> {
+            Map<String, Long> taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId);
+            List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
+            if (CollUtil.isEmpty(musicTaskList)) {
+                log.warn("Suno 任务同步失败, 任务ID: [{}]", taskIdMap.keySet());
+                return;
+            }
+            // 更新进度
+            List<AiMusicDO> updateMusicList = buildMusicDOList(musicTaskList);
+            updateMusicList.forEach(music -> music.setId(taskIdMap.get(music.getTaskId())));
+            musicMapper.updateBatch(updateMusicList);
+        });
+        return streamingTask.size();
+    }
+
+    @Override
+    public void updateMusic(AiMusicUpdateReqVO updateReqVO) {
+        // 校验存在
+        validateMusicExists(updateReqVO.getId());
+        // 更新
+        musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setPublicStatus(updateReqVO.getPublicStatus()));
+    }
+
+    @Override
+    public void updateMyMusic(AiMusicUpdateMyReqVO updateReqVO, Long userId) {
+        // 校验音乐是否存在
+        AiMusicDO musicDO = validateMusicExists(updateReqVO.getId());
+        if (ObjUtil.notEqual(musicDO.getUserId(), userId)) {
+            throw exception(MUSIC_NOT_EXISTS);
+        }
+        // 更新
+        musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setTitle(updateReqVO.getTitle()));
+    }
+
+    @Override
+    public void deleteMusic(Long id) {
+        // 校验存在
+        validateMusicExists(id);
+        // 删除
+        musicMapper.deleteById(id);
+    }
+
+    @Override
+    public void deleteMusicMy(Long id, Long userId) {
+        // 1. 校验是否存在
+        AiMusicDO music = validateMusicExists(id);
+        if (ObjUtil.notEqual(music.getUserId(), userId)) {
+            throw exception(IMAGE_NOT_EXISTS);
+        }
+        // 2. 删除记录
+        musicMapper.deleteById(id);
+    }
+
+    @Override
+    public AiMusicDO getMusic(Long id) {
+        return musicMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO) {
+        return musicMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId) {
+        return musicMapper.selectPageByMy(pageReqVO, userId);
+    }
+
+    /**
+     * 构建 AiMusicDO 集合
+     *
+     * @param musicList suno 音乐任务列表
+     * @return AiMusicDO 集合
+     */
+    private List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {
+        return convertList(musicList, musicData -> {
+            Integer status = Objects.equals("complete", musicData.status()) ? AiMusicStatusEnum.SUCCESS.getStatus()
+                    : Objects.equals("error", musicData.status()) ? AiMusicStatusEnum.FAIL.getStatus()
+                    : AiMusicStatusEnum.IN_PROGRESS.getStatus();
+            return new AiMusicDO()
+                    .setTaskId(musicData.id()).setModel(musicData.modelName())
+                    .setDescription(musicData.gptDescriptionPrompt())
+                    .setAudioUrl(downloadFile(status, musicData.audioUrl()))
+                    .setVideoUrl(downloadFile(status, musicData.videoUrl()))
+                    .setImageUrl(downloadFile(status, musicData.imageUrl()))
+                    .setTitle(musicData.title()).setDuration(musicData.duration())
+                    .setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
+                    .setErrorMessage(musicData.errorMessage())
+                    .setStatus(status);
+        });
+    }
+
+    /**
+     * 音乐生成好后,将音频文件上传到文件服务器
+     *
+     * @param status 音乐状态
+     * @param url    音频文件地址
+     * @return 内部文件地址
+     */
+    private String downloadFile(Integer status, String url) {
+        if (StrUtil.isBlank(url) || ObjectUtil.notEqual(status, AiMusicStatusEnum.SUCCESS.getStatus())) {
+            return url;
+        }
+        try {
+            byte[] bytes = HttpUtil.downloadBytes(url);
+            return fileApi.createFile(bytes);
+        } catch (Exception e) {
+            log.error("[downloadFile][url({}) 下载失败]", url, e);
+            return url;
+        }
+    }
+
+    /**
+     * 校验音乐是否存在
+     *
+     * @param id 音乐编号
+     * @return 音乐信息
+     */
+    private AiMusicDO validateMusicExists(Long id) {
+        AiMusicDO music = musicMapper.selectById(id);
+        if (music == null) {
+            throw exception(MUSIC_NOT_EXISTS);
+        }
+        return music;
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questionparamsetting/QuestionParamSettingService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questionparamsetting/QuestionParamSettingService.java
new file mode 100644
index 0000000..2bfcab9
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questionparamsetting/QuestionParamSettingService.java
@@ -0,0 +1,53 @@
+package com.iailab.module.ai.service.questionparamsetting;
+
+import javax.validation.*;
+import com.iailab.module.ai.controller.admin.questionparamsetting.vo.*;
+import com.iailab.module.ai.dal.dataobject.questionparamsetting.QuestionParamSettingDO;
+import com.iailab.framework.common.pojo.PageResult;
+
+/**
+ * 大模型问题设置参数 Service 接口
+ *
+ * @author 超级管理员
+ */
+public interface QuestionParamSettingService {
+
+    /**
+     * 创建大模型问题设置参数
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    String createQuestionParamSetting(@Valid QuestionParamSettingSaveReqVO createReqVO);
+
+    /**
+     * 更新大模型问题设置参数
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateQuestionParamSetting(@Valid QuestionParamSettingSaveReqVO updateReqVO);
+
+    /**
+     * 删除大模型问题设置参数
+     *
+     * @param id 编号
+     */
+    void deleteQuestionParamSetting(String id);
+
+    /**
+     * 获得大模型问题设置参数
+     *
+     * @param id 编号
+     * @return 大模型问题设置参数
+     */
+    QuestionParamSettingDO getQuestionParamSetting(String id);
+
+    /**
+     * 获得大模型问题设置参数分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 大模型问题设置参数分页
+     */
+    PageResult<QuestionParamSettingDO> getQuestionParamSettingPage(QuestionParamSettingPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questionparamsetting/QuestionParamSettingServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questionparamsetting/QuestionParamSettingServiceImpl.java
new file mode 100644
index 0000000..f652a3a
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questionparamsetting/QuestionParamSettingServiceImpl.java
@@ -0,0 +1,71 @@
+package com.iailab.module.ai.service.questionparamsetting;
+
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import com.iailab.module.ai.controller.admin.questionparamsetting.vo.*;
+import com.iailab.module.ai.dal.dataobject.questionparamsetting.QuestionParamSettingDO;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+
+import com.iailab.module.ai.dal.mysql.questionparamsetting.QuestionParamSettingMapper;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.*;
+
+/**
+ * 大模型问题设置参数 Service 实现类
+ *
+ * @author 超级管理员
+ */
+@Service
+@Validated
+public class QuestionParamSettingServiceImpl implements QuestionParamSettingService {
+
+    @Resource
+    private QuestionParamSettingMapper questionParamSettingMapper;
+
+    @Override
+    public String createQuestionParamSetting(QuestionParamSettingSaveReqVO createReqVO) {
+        // 插入
+        QuestionParamSettingDO questionParamSetting = BeanUtils.toBean(createReqVO, QuestionParamSettingDO.class);
+        questionParamSettingMapper.insert(questionParamSetting);
+        // 返回
+        return questionParamSetting.getId();
+    }
+
+    @Override
+    public void updateQuestionParamSetting(QuestionParamSettingSaveReqVO updateReqVO) {
+        // 校验存在
+        validateQuestionParamSettingExists(updateReqVO.getId());
+        // 更新
+        QuestionParamSettingDO updateObj = BeanUtils.toBean(updateReqVO, QuestionParamSettingDO.class);
+        questionParamSettingMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteQuestionParamSetting(String id) {
+        // 校验存在
+        validateQuestionParamSettingExists(id);
+        // 删除
+        questionParamSettingMapper.deleteById(id);
+    }
+
+    private void validateQuestionParamSettingExists(String id) {
+        if (questionParamSettingMapper.selectById(id) == null) {
+            throw exception(QUESTION_PARAM_SETTING_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public QuestionParamSettingDO getQuestionParamSetting(String id) {
+        return questionParamSettingMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<QuestionParamSettingDO> getQuestionParamSettingPage(QuestionParamSettingPageReqVO pageReqVO) {
+        return questionParamSettingMapper.selectPage(pageReqVO);
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateService.java
new file mode 100644
index 0000000..37dc162
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateService.java
@@ -0,0 +1,53 @@
+package com.iailab.module.ai.service.questiontemplate;
+
+import javax.validation.*;
+import com.iailab.module.ai.controller.admin.questiontemplate.vo.*;
+import com.iailab.module.ai.dal.dataobject.questiontemplate.QuestionTemplateDO;
+import com.iailab.framework.common.pojo.PageResult;
+
+/**
+ * 大模型问题模板 Service 接口
+ *
+ * @author 超级管理员
+ */
+public interface QuestionTemplateService {
+
+    /**
+     * 创建大模型问题模板
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    String createQuestionTemplate(@Valid QuestionTemplateSaveReqVO createReqVO);
+
+    /**
+     * 更新大模型问题模板
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateQuestionTemplate(@Valid QuestionTemplateSaveReqVO updateReqVO);
+
+    /**
+     * 删除大模型问题模板
+     *
+     * @param id 编号
+     */
+    void deleteQuestionTemplate(String id);
+
+    /**
+     * 获得大模型问题模板
+     *
+     * @param id 编号
+     * @return 大模型问题模板
+     */
+    QuestionTemplateDO getQuestionTemplate(String id);
+
+    /**
+     * 获得大模型问题模板分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 大模型问题模板分页
+     */
+    PageResult<QuestionTemplateDO> getQuestionTemplatePage(QuestionTemplatePageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateServiceImpl.java
new file mode 100644
index 0000000..6689b59
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateServiceImpl.java
@@ -0,0 +1,71 @@
+package com.iailab.module.ai.service.questiontemplate;
+
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import com.iailab.module.ai.controller.admin.questiontemplate.vo.*;
+import com.iailab.module.ai.dal.dataobject.questiontemplate.QuestionTemplateDO;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+
+import com.iailab.module.ai.dal.mysql.questiontemplate.QuestionTemplateMapper;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.*;
+
+/**
+ * 大模型问题模板 Service 实现类
+ *
+ * @author 超级管理员
+ */
+@Service
+@Validated
+public class QuestionTemplateServiceImpl implements QuestionTemplateService {
+
+    @Resource
+    private QuestionTemplateMapper questionTemplateMapper;
+
+    @Override
+    public String createQuestionTemplate(QuestionTemplateSaveReqVO createReqVO) {
+        // 插入
+        QuestionTemplateDO questionTemplate = BeanUtils.toBean(createReqVO, QuestionTemplateDO.class);
+        questionTemplateMapper.insert(questionTemplate);
+        // 返回
+        return questionTemplate.getId();
+    }
+
+    @Override
+    public void updateQuestionTemplate(QuestionTemplateSaveReqVO updateReqVO) {
+        // 校验存在
+        validateQuestionTemplateExists(updateReqVO.getId());
+        // 更新
+        QuestionTemplateDO updateObj = BeanUtils.toBean(updateReqVO, QuestionTemplateDO.class);
+        questionTemplateMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteQuestionTemplate(String id) {
+        // 校验存在
+        validateQuestionTemplateExists(id);
+        // 删除
+        questionTemplateMapper.deleteById(id);
+    }
+
+    private void validateQuestionTemplateExists(String id) {
+        if (questionTemplateMapper.selectById(id) == null) {
+            throw exception(QUESTION_TEMPLATE_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public QuestionTemplateDO getQuestionTemplate(String id) {
+        return questionTemplateMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<QuestionTemplateDO> getQuestionTemplatePage(QuestionTemplatePageReqVO pageReqVO) {
+        return questionTemplateMapper.selectPage(pageReqVO);
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/workflow/AiWorkflowService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/workflow/AiWorkflowService.java
new file mode 100644
index 0000000..63b4cf4
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/workflow/AiWorkflowService.java
@@ -0,0 +1,62 @@
+package com.iailab.module.ai.service.workflow;
+
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO;
+import com.iailab.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO;
+import com.iailab.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO;
+import com.iailab.module.ai.dal.dataobject.workflow.AiWorkflowDO;
+import jakarta.validation.Valid;
+
+/**
+ * AI 工作流 Service 接口
+ *
+ * @author lesan
+ */
+public interface AiWorkflowService {
+
+    /**
+     * 创建 AI 工作流
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createWorkflow(@Valid AiWorkflowSaveReqVO createReqVO);
+
+    /**
+     * 更新 AI 工作流
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateWorkflow(@Valid AiWorkflowSaveReqVO updateReqVO);
+
+    /**
+     * 删除 AI 工作流
+     *
+     * @param id 编号
+     */
+    void deleteWorkflow(Long id);
+
+    /**
+     * 获得 AI 工作流
+     *
+     * @param id 编号
+     * @return AI 工作流
+     */
+    AiWorkflowDO getWorkflow(Long id);
+
+    /**
+     * 获得 AI 工作流分页
+     *
+     * @param pageReqVO 分页查询
+     * @return AI 工作流分页
+     */
+    PageResult<AiWorkflowDO> getWorkflowPage(AiWorkflowPageReqVO pageReqVO);
+
+    /**
+     * 测试 AI 工作流
+     *
+     * @param testReqVO 测试数据
+     */
+    Object testWorkflow(AiWorkflowTestReqVO testReqVO);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/workflow/AiWorkflowServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/workflow/AiWorkflowServiceImpl.java
new file mode 100644
index 0000000..55f175b
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/workflow/AiWorkflowServiceImpl.java
@@ -0,0 +1,150 @@
+package com.iailab.module.ai.service.workflow;
+
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO;
+import com.iailab.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO;
+import com.iailab.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiApiKeyDO;
+import com.iailab.module.ai.dal.dataobject.workflow.AiWorkflowDO;
+import com.iailab.module.ai.dal.mysql.workflow.AiWorkflowMapper;
+import com.iailab.module.ai.service.model.AiApiKeyService;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import dev.tinyflow.core.Tinyflow;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.WORKFLOW_CODE_EXISTS;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.WORKFLOW_NOT_EXISTS;
+
+/**
+ * AI 工作流 Service 实现类
+ *
+ * @author lesan
+ */
+@Service
+@Slf4j
+public class AiWorkflowServiceImpl implements AiWorkflowService {
+
+    @Resource
+    private AiWorkflowMapper workflowMapper;
+
+    @Resource
+    private AiApiKeyService apiKeyService;
+
+    @Override
+    public Long createWorkflow(AiWorkflowSaveReqVO createReqVO) {
+        validateWorkflowForCreateOrUpdate(null, createReqVO.getCode());
+        AiWorkflowDO workflow = BeanUtils.toBean(createReqVO, AiWorkflowDO.class);
+        workflowMapper.insert(workflow);
+        return workflow.getId();
+    }
+
+    @Override
+    public void updateWorkflow(AiWorkflowSaveReqVO updateReqVO) {
+        validateWorkflowForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getCode());
+        AiWorkflowDO workflow = BeanUtils.toBean(updateReqVO, AiWorkflowDO.class);
+        workflowMapper.updateById(workflow);
+    }
+
+    @Override
+    public void deleteWorkflow(Long id) {
+        validateWorkflowExists(id);
+        workflowMapper.deleteById(id);
+    }
+
+    @Override
+    public AiWorkflowDO getWorkflow(Long id) {
+        return workflowMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<AiWorkflowDO> getWorkflowPage(AiWorkflowPageReqVO pageReqVO) {
+        return workflowMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public Object testWorkflow(AiWorkflowTestReqVO testReqVO) {
+        Map<String, Object> variables = testReqVO.getParams();
+        Tinyflow tinyflow = parseFlowParam(testReqVO.getGraph());
+        return tinyflow.toChain().executeForResult(variables);
+    }
+
+    private void validateWorkflowForCreateOrUpdate(Long id, String code) {
+        validateWorkflowExists(id);
+        validateCodeUnique(id, code);
+    }
+
+    private void validateWorkflowExists(Long id) {
+        if (ObjUtil.isNull(id)) {
+            return;
+        }
+        AiWorkflowDO workflow = workflowMapper.selectById(id);
+        if (ObjUtil.isNull(workflow)) {
+            throw exception(WORKFLOW_NOT_EXISTS);
+        }
+    }
+
+    private void validateCodeUnique(Long id, String code) {
+        if (StrUtil.isBlank(code)) {
+            return;
+        }
+        AiWorkflowDO workflow = workflowMapper.selectByCode(code);
+        if (ObjUtil.isNull(workflow)) {
+            return;
+        }
+        if (ObjUtil.isNull(id)) {
+            throw exception(WORKFLOW_CODE_EXISTS);
+        }
+        if (ObjUtil.notEqual(workflow.getId(), id)) {
+            throw exception(WORKFLOW_CODE_EXISTS);
+        }
+    }
+
+    private Tinyflow parseFlowParam(String graph) {
+        // TODO @lesan:可以使用 jackson 哇?
+        JSONObject json = JSONObject.parseObject(graph);
+        JSONArray nodeArr = json.getJSONArray("nodes");
+        Tinyflow tinyflow = new Tinyflow(json.toJSONString());
+        for (int i = 0; i < nodeArr.size(); i++) {
+            JSONObject node = nodeArr.getJSONObject(i);
+            switch (node.getString("type")) {
+                case "llmNode":
+                    JSONObject data = node.getJSONObject("data");
+                    AiApiKeyDO apiKey = apiKeyService.getApiKey(data.getLong("llmId"));
+                    switch (apiKey.getPlatform()) {
+                        // TODO @lesan 需要讨论一下这里怎么弄
+                        // TODO @lesan llmId 对应 model 的编号如何?这样的话,就是 apiModelService 提供一个获取 LLM 的方法。然后,创建的方法,也在 AiModelFactory 提供。可以先接个 deepseek 先。deepseek yyds!
+                        case "OpenAI":
+                            break;
+                        case "Ollama":
+                            break;
+                        case "YiYan":
+                            break;
+                        case "XingHuo":
+                            break;
+                        case "TongYi":
+                            break;
+                        case "DeepSeek":
+                            break;
+                        case "ZhiPu":
+                            break;
+                    }
+                    break;
+                case "internalNode":
+                    break;
+                default:
+                    break;
+            }
+        }
+        return tinyflow;
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/write/AiWriteService.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/write/AiWriteService.java
new file mode 100644
index 0000000..214c643
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/write/AiWriteService.java
@@ -0,0 +1,41 @@
+package com.iailab.module.ai.service.write;
+
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
+import com.iailab.module.ai.controller.admin.write.vo.AiWritePageReqVO;
+import com.iailab.module.ai.dal.dataobject.write.AiWriteDO;
+import reactor.core.publisher.Flux;
+
+/**
+ * AI 写作 Service 接口
+ *
+ * @author xiaoxin
+ */
+public interface AiWriteService {
+
+    /**
+     * 生成写作内容
+     *
+     * @param generateReqVO 作文生成请求参数
+     * @param userId        用户编号
+     * @return 生成结果
+     */
+    Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId);
+
+    /**
+     * 删除写作
+     *
+     * @param id 编号
+     */
+    void deleteWrite(Long id);
+
+    /**
+     * 获得写作分页
+     *
+     * @param pageReqVO 分页查询
+     * @return AI 写作分页
+     */
+    PageResult<AiWriteDO> getWritePage(AiWritePageReqVO pageReqVO);
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/write/AiWriteServiceImpl.java b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/write/AiWriteServiceImpl.java
new file mode 100644
index 0000000..5e972bb
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/write/AiWriteServiceImpl.java
@@ -0,0 +1,181 @@
+package com.iailab.module.ai.service.write;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import com.iailab.framework.ai.core.enums.AiModelTypeEnum;
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.ai.core.util.AiUtils;
+import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.tenant.core.util.TenantUtils;
+import com.iailab.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
+import com.iailab.module.ai.controller.admin.write.vo.AiWritePageReqVO;
+import com.iailab.module.ai.dal.dataobject.model.AiChatRoleDO;
+import com.iailab.module.ai.dal.dataobject.model.AiModelDO;
+import com.iailab.module.ai.dal.dataobject.write.AiWriteDO;
+import com.iailab.module.ai.dal.mysql.write.AiWriteMapper;
+import com.iailab.module.ai.enums.AiChatRoleEnum;
+import com.iailab.module.ai.enums.DictTypeConstants;
+import com.iailab.module.ai.enums.ErrorCodeConstants;
+import com.iailab.module.ai.enums.write.AiWriteTypeEnum;
+import com.iailab.module.ai.service.model.AiChatRoleService;
+import com.iailab.module.ai.service.model.AiModelService;
+import com.iailab.module.system.api.dict.DictDataApi;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.model.StreamingChatModel;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.framework.common.pojo.CommonResult.error;
+import static com.iailab.framework.common.pojo.CommonResult.success;
+import static com.iailab.module.ai.enums.ErrorCodeConstants.*;
+
+/**
+ * AI 写作 Service 实现类
+ *
+ * @author xiaoxin
+ */
+@Service
+@Slf4j
+public class AiWriteServiceImpl implements AiWriteService {
+
+    @Resource
+    private AiModelService modalService;
+    @Resource
+    private AiChatRoleService chatRoleService;
+
+    @Resource
+    private AiWriteMapper writeMapper;
+
+    @Resource
+    private DictDataApi dictDataApi;
+
+    @Override
+    public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) {
+        // 1 获取写作模型。尝试获取写作助手角色,没有则使用默认模型
+        AiChatRoleDO writeRole = CollUtil.getFirst(
+                chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName()));
+        // 1.1 获取写作执行模型
+        AiModelDO model = getModel(writeRole);
+        // 1.2 获取角色设定消息
+        String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage())
+                ? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage();
+        // 1.3 校验平台
+        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
+        StreamingChatModel chatModel = modalService.getChatModel(model.getId());
+
+        // 2. 插入写作信息
+        AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, write -> write.setUserId(userId)
+                        .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));
+        writeMapper.insert(writeDO);
+
+        // 3.1 构建 Prompt,并进行调用
+        Prompt prompt = buildPrompt(generateReqVO, model, systemMessage);
+        Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
+
+        // 3.2 流式返回
+        StringBuffer contentBuffer = new StringBuffer();
+        return streamResponse.map(chunk -> {
+            String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
+            newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
+            contentBuffer.append(newContent);
+            // 响应结果
+            return success(newContent);
+        }).doOnComplete(() -> {
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() ->
+                    writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString())));
+        }).doOnError(throwable -> {
+            log.error("[generateWriteContent][generateReqVO({}) 发生异常]", generateReqVO, throwable);
+            // 忽略租户,因为 Flux 异步无法透传租户
+            TenantUtils.executeIgnore(() ->
+                    writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage())));
+        }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
+    }
+
+    private AiModelDO getModel(AiChatRoleDO writeRole) {
+        AiModelDO model = null;
+        if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) {
+            model = modalService.getModel(writeRole.getModelId());
+        }
+        if (model == null) {
+            model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
+        }
+        // 校验模型存在、且合法
+        if (model == null) {
+            throw exception(MODEL_NOT_EXISTS);
+        }
+        if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) {
+            throw exception(MODEL_USE_TYPE_ERROR);
+        }
+        return model;
+    }
+
+    private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {
+        // 1. 构建 message 列表
+        List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
+        // 2. 构建 options 对象
+        AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
+        ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());
+        return new Prompt(chatMessages, options);
+    }
+
+    private List<Message> buildMessages(AiWriteGenerateReqVO generateReqVO, String systemMessage) {
+        List<Message> chatMessages = new ArrayList<>();
+        if (StrUtil.isNotBlank(systemMessage)) {
+            // 1.1 角色设定
+            chatMessages.add(new SystemMessage(systemMessage));
+        }
+        // 1.2 用户输入
+        chatMessages.add(new UserMessage(buildUserMessage(generateReqVO)));
+        return chatMessages;
+    }
+
+    private String buildUserMessage(AiWriteGenerateReqVO generateReqVO) {
+        String format = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_FORMAT, generateReqVO.getFormat());
+        String tone = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_TONE, generateReqVO.getTone());
+        String language = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LANGUAGE, generateReqVO.getLanguage());
+        String length = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LENGTH, generateReqVO.getLength());
+        // 格式化 prompt
+        String prompt = generateReqVO.getPrompt();
+        if (Objects.equals(generateReqVO.getType(), AiWriteTypeEnum.WRITING.getType())) {
+            return StrUtil.format(AiWriteTypeEnum.WRITING.getPrompt(), prompt, format, tone, language, length);
+        } else {
+            return StrUtil.format(AiWriteTypeEnum.REPLY.getPrompt(), generateReqVO.getOriginalContent(), prompt, format, tone, language, length);
+        }
+    }
+
+    @Override
+    public void deleteWrite(Long id) {
+        // 校验存在
+        validateWriteExists(id);
+        // 删除
+        writeMapper.deleteById(id);
+    }
+
+    private void validateWriteExists(Long id) {
+        if (writeMapper.selectById(id) == null) {
+            throw exception(WRITE_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public PageResult<AiWriteDO> getWritePage(AiWritePageReqVO pageReqVO) {
+        return writeMapper.selectPage(pageReqVO);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-dev.yaml b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-dev.yaml
new file mode 100644
index 0000000..1961fcd
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-dev.yaml
@@ -0,0 +1,122 @@
+--- #################### 数据库相关配置 ####################
+spring:
+  # 数据源配置项
+  autoconfigure:
+    exclude:
+      - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
+      - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建
+      - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建
+  datasource:
+    druid: # Druid 【监控】相关的全局配置
+      web-stat-filter:
+        enabled: true
+      stat-view-servlet:
+        enabled: true
+        allow: # 设置白名单,不填则允许所有访问
+        url-pattern: /druid/*
+        login-username: # 控制台管理用户名和密码
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          log-slow-sql: true # 慢 SQL 记录
+          slow-sql-millis: 100
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+    dynamic: # 多数据源配置
+      druid: # Druid 【连接池】相关的全局配置
+        initial-size: 1 # 初始连接数
+        min-idle: 1 # 最小连接池数量
+        max-active: 20 # 最大连接池数量
+        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
+          #          url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例
+          #          url: jdbc:postgresql://127.0.0.1:5432/iailab_plat_system # PostgreSQL 连接的示例
+          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
+          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=iailab_plat_system # SQLServer 连接的示例
+          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
+          username: root
+          password: 123456
+        #          username: sa # SQL Server 连接的示例
+        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例
+        #          username: SYSDBA # DM 连接的示例
+        #          password: SYSDBA # DM 连接的示例
+        slave: # 模拟从库,可根据自己需要修改
+          lazy: true # 开启懒加载,保证启动速度
+          url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
+          username: root
+          password: 123456
+
+  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+  data:
+    redis:
+      host: 127.0.0.1 # 地址
+      port: 6379 # 端口
+      database: 0 # 数据库索引
+      password: 123456 # 密码,建议生产环境开启
+
+--- #################### MQ 消息队列相关配置 ####################
+
+--- #################### 定时任务相关配置 ####################
+
+#xxl:
+#  job:
+#    admin:
+#      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项
+lock4j:
+  acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
+  expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
+
+--- #################### 监控相关配置 ####################
+
+# Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      exposure:
+        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+
+# Spring Boot Admin 配置项
+spring:
+  boot:
+    admin:
+      # Spring Boot Admin Client 客户端的相关配置
+      client:
+        instance:
+          service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
+
+# 日志文件配置
+logging:
+  level:
+    # 配置自己写的 MyBatis Mapper 打印日志
+    com.iailab.module.ai.dal.mysql: debug
+    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO Iailab:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示
+
+--- #################### Iailab相关配置 ####################
+
+# Iailab配置项,设置当前项目所有自定义的配置
+iailab:
+  env: # 多环境的配置项
+    tag: ${HOSTNAME}
+  security:
+    mock-enable: true
+  access-log: # 访问日志的配置项
+    enable: false
+  demo: false
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-local.yaml b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-local.yaml
new file mode 100644
index 0000000..7006f29
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-local.yaml
@@ -0,0 +1,121 @@
+--- #################### 数据库相关配置 ####################
+spring:
+  # 数据源配置项
+  autoconfigure:
+    exclude:
+      - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
+      - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建
+      - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建
+  datasource:
+    druid: # Druid 【监控】相关的全局配置
+      web-stat-filter:
+        enabled: true
+      stat-view-servlet:
+        enabled: true
+        allow: # 设置白名单,不填则允许所有访问
+        url-pattern: /druid/*
+        login-username: # 控制台管理用户名和密码
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          log-slow-sql: true # 慢 SQL 记录
+          slow-sql-millis: 100
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+    dynamic: # 多数据源配置
+      druid: # Druid 【连接池】相关的全局配置
+        initial-size: 1 # 初始连接数
+        min-idle: 1 # 最小连接池数量
+        max-active: 20 # 最大连接池数量
+        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
+          #          url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例
+          #          url: jdbc:postgresql://127.0.0.1:5432/iailab_plat_system # PostgreSQL 连接的示例
+          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
+          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=iailab_plat_system # SQLServer 连接的示例
+          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
+          username: root
+          password: 123456
+        #          username: sa # SQL Server 连接的示例
+        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例
+        #          username: SYSDBA # DM 连接的示例
+        #          password: SYSDBA # DM 连接的示例
+        slave: # 模拟从库,可根据自己需要修改
+          lazy: true # 开启懒加载,保证启动速度
+          url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
+          username: root
+          password: 123456
+
+  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+  data:
+    redis:
+      host: 127.0.0.1 # 地址
+      port: 6379 # 端口
+      database: 0 # 数据库索引
+      password: 123456 # 密码,建议生产环境开启
+
+--- #################### MQ 消息队列相关配置 ####################
+
+--- #################### 定时任务相关配置 ####################
+
+#xxl:
+#  job:
+#    admin:
+#      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项
+lock4j:
+  acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
+  expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
+
+--- #################### 监控相关配置 ####################
+
+# Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      exposure:
+        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+
+# Spring Boot Admin 配置项
+spring:
+  boot:
+    admin:
+      # Spring Boot Admin Client 客户端的相关配置
+      client:
+        instance:
+          service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
+
+# 日志文件配置
+logging:
+  level:
+    # 配置自己写的 MyBatis Mapper 打印日志
+    com.iailab.module.ai.dal.mysql: debug
+    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO Iailab:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示
+
+--- #################### Iailab相关配置 ####################
+
+# Iailab配置项,设置当前项目所有自定义的配置
+iailab:
+  env: # 多环境的配置项
+    tag: ${HOSTNAME}
+  security:
+    mock-enable: true
+  access-log: # 访问日志的配置项
+    enable: false
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-test.yaml b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-test.yaml
new file mode 100644
index 0000000..b3d9ed8
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application-test.yaml
@@ -0,0 +1,122 @@
+--- #################### 数据库相关配置 ####################
+spring:
+  # 数据源配置项
+  autoconfigure:
+    exclude:
+      - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
+      - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建
+      - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建
+  datasource:
+    druid: # Druid 【监控】相关的全局配置
+      web-stat-filter:
+        enabled: true
+      stat-view-servlet:
+        enabled: true
+        allow: # 设置白名单,不填则允许所有访问
+        url-pattern: /druid/*
+        login-username: # 控制台管理用户名和密码
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          log-slow-sql: true # 慢 SQL 记录
+          slow-sql-millis: 100
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+    dynamic: # 多数据源配置
+      druid: # Druid 【连接池】相关的全局配置
+        initial-size: 1 # 初始连接数
+        min-idle: 1 # 最小连接池数量
+        max-active: 20 # 最大连接池数量
+        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          url: jdbc:mysql://172.16.8.100:3306/iailab_ai?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
+          #          url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例
+          #          url: jdbc:postgresql://127.0.0.1:5432/iailab_plat_system # PostgreSQL 连接的示例
+          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
+          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=iailab_plat_system # SQLServer 连接的示例
+          #          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
+          username: root
+          password: 123456
+        #          username: sa # SQL Server 连接的示例
+        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例
+        #          username: SYSDBA # DM 连接的示例
+        #          password: SYSDBA # DM 连接的示例
+        slave: # 模拟从库,可根据自己需要修改
+          lazy: true # 开启懒加载,保证启动速度
+          url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
+          username: root
+          password: 123456
+
+  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+  data:
+    redis:
+      host: 172.16.8.100 # 地址
+      port: 6379 # 端口
+      database: 0 # 数据库索引
+      password: 123456 # 密码,建议生产环境开启
+
+--- #################### MQ 消息队列相关配置 ####################
+
+--- #################### 定时任务相关配置 ####################
+
+#xxl:
+#  job:
+#    admin:
+#      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项
+lock4j:
+  acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
+  expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
+
+--- #################### 监控相关配置 ####################
+
+# Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      exposure:
+        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+
+# Spring Boot Admin 配置项
+spring:
+  boot:
+    admin:
+      # Spring Boot Admin Client 客户端的相关配置
+      client:
+        instance:
+          service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
+
+# 日志文件配置
+logging:
+  level:
+    # 配置自己写的 MyBatis Mapper 打印日志
+    com.iailab.module.ai.dal.mysql: debug
+    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO Iailab:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示
+
+--- #################### Iailab相关配置 ####################
+
+# Iailab配置项,设置当前项目所有自定义的配置
+iailab:
+  env: # 多环境的配置项
+    tag: ${HOSTNAME}
+  security:
+    mock-enable: true
+  access-log: # 访问日志的配置项
+    enable: false
+  demo: false
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml
new file mode 100644
index 0000000..90a8245
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml
@@ -0,0 +1,224 @@
+spring:
+  application:
+    name: ai-server
+
+  profiles:
+    active: @profiles.active@
+
+  cloud:
+    nacos:
+      server-addr: @nacos.server@ # Nacos 服务器地址
+      username: @nacos.username@
+      password: @nacos.password@
+      discovery: # 【配置中心】配置项
+        namespace: @profiles.active@
+        group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+        metadata:
+          version: @nacos.metadata.version@ # 服务实例的版本号,可用于灰度发布
+      config: # 【注册中心】配置项
+        namespace: @profiles.active@
+        group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+
+  main:
+    allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
+    allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务
+
+  config:
+    import:
+      - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置
+      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置
+
+  # Servlet 配置
+  servlet:
+    # 文件上传相关配置项
+    multipart:
+      max-file-size: 16MB # 单个文件大小
+      max-request-size: 32MB # 设置总上传的文件大小
+
+  # Jackson 配置项
+  jackson:
+    serialization:
+      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳
+      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
+      write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
+      fail-on-empty-beans: false # 允许序列化无属性的 Bean
+
+  # Cache 配置项
+  cache:
+    type: REDIS
+    redis:
+      time-to-live: 1h # 设置过期时间为 1 小时
+
+server:
+  port: 48090
+  servlet:
+    encoding:
+      enabled: true
+      charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题
+      force: true
+
+logging:
+  file:
+    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
+
+--- #################### 接口文档配置 ####################
+
+springdoc:
+  api-docs:
+    enabled: true # 1. 是否开启 Swagger 接文档的元数据
+    path: /v3/api-docs
+  swagger-ui:
+    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面
+    path: /swagger-ui
+  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
+
+knife4j:
+  enable: false # TODO Iailab:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874
+  setting:
+    language: zh_cn
+
+# MyBatis Plus 的配置项
+mybatis-plus:
+  configuration:
+    map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
+  global-config:
+    db-config:
+      id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
+      #      id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
+      #      id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
+      #      id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
+      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
+      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
+    banner: false # 关闭控制台的 Banner 打印
+  type-aliases-package: ${iailab.info.base-package}.module.*.dal.dataobject
+  encryptor:
+    password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成
+
+mybatis-plus-join:
+  banner: false # 关闭控制台的 Banner 打印
+
+# VO 转换(数据翻译)相关
+easy-trans:
+  is-enable-global: true # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口
+
+--- #################### RPC 远程调用相关配置 ####################
+
+--- #################### MQ 消息队列相关配置 ####################
+
+--- #################### 定时任务相关配置 ####################
+
+xxl:
+  job:
+    executor:
+      appname: ${spring.application.name} # 执行器 AppName
+      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径
+    accessToken: default_token # 执行器通讯TOKEN
+
+--- #################### AI 相关配置 ####################
+
+spring:
+  ai:
+    vectorstore: # 向量存储
+      redis:
+        initialize-schema: true
+        index: knowledge_index # Redis 中向量索引的名称:用于存储和检索向量数据的索引标识符,所有相关的向量搜索操作都会基于这个索引进行
+        prefix: "knowledge_segment:" # Redis 中存储向量数据的键名前缀:这个前缀会添加到每个存储在 Redis 中的向量数据键名前,每个 document 都是一个 hash 结构
+      qdrant:
+        initialize-schema: true
+        collection-name: knowledge_segment # Qdrant 中向量集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行
+        host: 127.0.0.1
+        port: 6334
+      milvus:
+        initialize-schema: true
+        database-name: default # Milvus 中数据库的名称
+        collection-name: knowledge_segment # Milvus 中集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行
+        client:
+          host: 127.0.0.1
+          port: 19530
+    qianfan: # 文心一言
+      api-key: x0cuLZ7XsaTCU08vuJWO87Lg
+      secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK
+    zhipuai: # 智谱 AI
+      api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs
+    openai: # OpenAI 官方
+      api-key: sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17
+      base-url: https://api.gptsapi.net
+    azure: # OpenAI 微软
+      openai:
+        endpoint: https://eastusprejade.openai.azure.com
+        api-key: xxx
+    ollama:
+      base-url: http://127.0.0.1:11434
+      chat:
+        model: llama3
+    stabilityai:
+      api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx
+    dashscope: # 通义千问
+      api-key: sk-71800982914041848008480000000000
+    minimax: # Minimax:https://www.minimaxi.com/
+      api-key: xxxx
+    moonshot: # 月之暗灭(KIMI)
+      api-key: sk-abc
+
+iailab:
+  ai:
+    deep-seek: # DeepSeek
+      enable: true
+      api-key: sk-e94db327cc7d457d99a8de8810fc6b12
+      model: deepseek-chat
+    doubao: # 字节豆包
+      enable: true
+      api-key: 5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272
+      model: doubao-1-5-lite-32k-250115
+    hunyuan: # 腾讯混元
+      enable: true
+      api-key: sk-abc
+      model: hunyuan-turbo
+    siliconflow: # 硅基流动
+      enable: true
+      api-key: sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz
+      model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
+    xinghuo: # 讯飞星火
+      enable: true
+      appKey: 75b161ed2aef4719b275d6e7f2a4d4cd
+      secretKey: YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz
+      model: generalv3.5
+    baichuan: # 百川智能
+      enable: true
+      api-key: sk-abc
+      model: Baichuan4-Turbo
+    midjourney:
+      enable: true
+  #    base-url: https://api.holdai.top/mj-relax/mj
+      base-url: https://api.holdai.top/mj
+      api-key: sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf
+      notify-url: http://java.nat300.top/admin-api/ai/image/midjourney/notify
+    suno:
+      enable: true
+  #    base-url: https://suno-55ishh05u-status2xxs-projects.vercel.app
+      base-url: http://127.0.0.1:3001
+
+--- #################### Iailab相关配置 ####################
+
+iailab:
+  info:
+    version: 1.0.0
+    base-package: com.iailab.module.ai
+  web:
+    admin-ui:
+      url: http://dashboard.iailab.Iailab.cn # Admin 管理后台 UI 的地址
+  xss:
+    enable: false
+    exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系
+      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
+  swagger:
+    title: 管理后台
+    description: 提供管理员管理的所有功能
+    version: ${iailab.info.version}
+  tenant: # 多租户相关配置项
+    enable: true
+    ignore-tables:
+      - ai_api_key
+      - ai_model
+
+debug: true
diff --git a/iailab-module-ai/iailab-module-ai-biz/src/main/resources/logback-spring.xml b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..db8f353
--- /dev/null
+++ b/iailab-module-ai/iailab-module-ai-biz/src/main/resources/logback-spring.xml
@@ -0,0 +1,76 @@
+<configuration>
+    <!-- 引用 Spring Boot 的 logback 基础配置 -->
+    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
+    <!-- 变量 iailab.info.base-package,基础业务包 -->
+    <springProperty scope="context" name="iailab.info.base-package" source="iailab.info.base-package"/>
+    <!-- 格式化输出:%d 表示日期,%X{tid} SkWalking 链路追踪编号,%thread 表示线程名,%-5level:级别从左显示 5 个字符宽度,%msg:日志消息,%n是换行符 -->
+    <property name="PATTERN_DEFAULT" value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} | %highlight(${LOG_LEVEL_PATTERN:-%5p} ${PID:- }) | %boldYellow(%thread [%tid]) %boldGreen(%-40.40logger{39}) | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
+
+    <!-- 控制台 Appender -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">     
+        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
+            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
+                <pattern>${PATTERN_DEFAULT}</pattern>
+            </layout>
+        </encoder>
+    </appender>
+
+    <!-- 文件 Appender -->
+    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->
+    <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
+            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
+                <pattern>${PATTERN_DEFAULT}</pattern>
+            </layout>
+        </encoder>
+        <!-- 日志文件名 -->
+        <file>${LOG_FILE}</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <!-- 滚动后的日志文件名 -->
+            <fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
+            <!-- 启动服务时,是否清理历史日志,一般不建议清理 -->
+            <cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
+            <!-- 日志文件,到达多少容量,进行滚动 -->
+            <maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
+            <!-- 日志文件的总大小,0 表示不限制 -->
+            <totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
+            <!-- 日志文件的保留天数 -->
+            <maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
+        </rollingPolicy>
+    </appender>
+    <!-- 异步写入日志,提升性能 -->
+    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+        <!-- 不丢失日志。默认的,如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->
+        <discardingThreshold>0</discardingThreshold>
+        <!-- 更改默认的队列的深度,该值会影响性能。默认值为 256 -->
+        <queueSize>256</queueSize>
+        <appender-ref ref="FILE"/>
+    </appender>
+
+    <!-- SkyWalking GRPC 日志收集,实现日志中心。注意:SkyWalking 8.4.0 版本开始支持 -->
+    <appender name="GRPC" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
+        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
+            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
+                <pattern>${PATTERN_DEFAULT}</pattern>
+            </layout>
+        </encoder>
+    </appender>
+
+    <!-- 本地环境 -->
+    <springProfile name="local">
+        <root level="INFO">
+            <appender-ref ref="STDOUT"/>
+            <appender-ref ref="GRPC"/> <!-- 本地环境下,如果不想接入 SkyWalking 日志服务,可以注释掉本行 -->
+            <appender-ref ref="ASYNC"/>  <!-- 本地环境下,如果不想打印日志,可以注释掉本行 -->
+        </root>
+    </springProfile>
+    <!-- 其它环境 -->
+    <springProfile name="dev,test,stage,prod,default">
+        <root level="INFO">
+            <appender-ref ref="STDOUT"/>
+            <appender-ref ref="ASYNC"/>
+            <appender-ref ref="GRPC"/>
+        </root>
+    </springProfile>
+
+</configuration>
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml b/iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml
new file mode 100644
index 0000000..ac3bd3e
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.iailab</groupId>
+        <artifactId>iailab-module-ai</artifactId>
+        <version>${ai.revision}</version>
+    </parent>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>17</source>
+                    <target>17</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>iailab-spring-boot-starter-ai</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>AI 大模型拓展,接入国内外大模型</description>
+    <properties>
+        <spring-ai.version>1.0.0-M6</spring-ai.version>
+        <tinyflow.version>1.0.0-rc.3</tinyflow.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common</artifactId>
+        </dependency>
+
+        <!-- Spring AI Model 模型接入 -->
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+        <dependency>
+            <!-- 通义千问 -->
+            <groupId>com.alibaba.cloud.ai</groupId>
+            <artifactId>spring-ai-alibaba-starter</artifactId>
+            <version>${spring-ai.version}.1</version>
+        </dependency>
+        <dependency>
+            <!-- 文心一言 -->
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-qianfan-spring-boot-starter</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+        <dependency>
+            <!-- 智谱 GLM -->
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-minimax-spring-boot-starter</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-moonshot-spring-boot-starter</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+
+        <!-- 向量存储:https://db-engines.com/en/ranking/vector+dbms -->
+        <dependency>
+            <!-- Qdrant:https://qdrant.tech/ -->
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-qdrant-store</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+
+        <dependency>
+            <!-- Redis:https://redis.io/docs/latest/develop/get-started/vector-database/ -->
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-redis-store</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.iailab</groupId>
+            <artifactId>iailab-common-redis</artifactId>
+        </dependency>
+
+        <dependency>
+            <!-- Milvus:https://milvus.io/ -->
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-milvus-store</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+
+        <dependency>
+            <!-- Tika:负责内容的解析 -->
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-tika-document-reader</artifactId>
+            <version>${spring-ai.version}</version>
+        </dependency>
+
+        <!-- TinyFlow:AI 工作流 -->
+        <dependency>
+            <groupId>dev.tinyflow</groupId>
+            <artifactId>tinyflow-java-core</artifactId>
+            <version>${tinyflow.version}</version>
+            <exclusions>
+                <exclusion>
+                    <!-- 解决 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1318/ 问题 -->
+                    <groupId>com.agentsflex</groupId>
+                    <artifactId>agents-flex-store-elasticsearch</artifactId>
+                </exclusion>
+                <exclusion>
+                    <!-- TODO @Iailab:暂时移除 groovy,和 iot 冲突 -->
+                    <groupId>org.codehaus.groovy</groupId>
+                    <artifactId>groovy-all</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/config/IailabAiAutoConfiguration.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/config/IailabAiAutoConfiguration.java
new file mode 100644
index 0000000..5535090
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/config/IailabAiAutoConfiguration.java
@@ -0,0 +1,253 @@
+package com.iailab.framework.ai.config;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.iailab.framework.ai.core.factory.AiModelFactory;
+import com.iailab.framework.ai.core.factory.AiModelFactoryImpl;
+import com.iailab.framework.ai.core.model.baichuan.BaiChuanChatModel;
+import com.iailab.framework.ai.core.model.deepseek.DeepSeekChatModel;
+import com.iailab.framework.ai.core.model.doubao.DouBaoChatModel;
+import com.iailab.framework.ai.core.model.hunyuan.HunYuanChatModel;
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
+import com.iailab.framework.ai.core.model.suno.api.SunoApi;
+import com.iailab.framework.ai.core.model.xinghuo.XingHuoChatModel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties;
+import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties;
+import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties;
+import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
+import org.springframework.ai.embedding.BatchingStrategy;
+import org.springframework.ai.embedding.TokenCountBatchingStrategy;
+import org.springframework.ai.model.tool.ToolCallingManager;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
+import org.springframework.ai.tokenizer.TokenCountEstimator;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * Iailab AI 自动配置
+ *
+ * @author fansili
+ */
+@AutoConfiguration
+@EnableConfigurationProperties({ IailabAiProperties.class,
+        QdrantVectorStoreProperties.class, // 解析 Qdrant 配置
+        RedisVectorStoreProperties.class, // 解析 Redis 配置
+        MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 解析 Milvus 配置
+})
+@Slf4j
+public class IailabAiAutoConfiguration {
+
+    @Bean
+    public AiModelFactory aiModelFactory() {
+        return new AiModelFactoryImpl();
+    }
+
+    // ========== 各种 AI Client 创建 ==========
+
+    @Bean
+    @ConditionalOnProperty(value = "iailab.ai.deepseek.enable", havingValue = "true")
+    public DeepSeekChatModel deepSeekChatModel(IailabAiProperties IailabAiProperties) {
+        IailabAiProperties.DeepSeekProperties properties = IailabAiProperties.getDeepseek();
+        return buildDeepSeekChatModel(properties);
+    }
+
+    public DeepSeekChatModel buildDeepSeekChatModel(IailabAiProperties.DeepSeekProperties properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(DeepSeekChatModel.MODEL_DEFAULT);
+        }
+        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+                .openAiApi(OpenAiApi.builder()
+                        .baseUrl(DeepSeekChatModel.BASE_URL)
+                        .apiKey(properties.getApiKey())
+                        .build())
+                .defaultOptions(OpenAiChatOptions.builder()
+                        .model(properties.getModel())
+                        .temperature(properties.getTemperature())
+                        .maxTokens(properties.getMaxTokens())
+                        .topP(properties.getTopP())
+                        .build())
+                .toolCallingManager(getToolCallingManager())
+                .build();
+        return new DeepSeekChatModel(openAiChatModel);
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "iailab.ai.doubao.enable", havingValue = "true")
+    public DouBaoChatModel douBaoChatClient(IailabAiProperties IailabAiProperties) {
+        IailabAiProperties.DouBaoProperties properties = IailabAiProperties.getDoubao();
+        return buildDouBaoChatClient(properties);
+    }
+
+    public DouBaoChatModel buildDouBaoChatClient(IailabAiProperties.DouBaoProperties properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(DouBaoChatModel.MODEL_DEFAULT);
+        }
+        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+                .openAiApi(OpenAiApi.builder()
+                        .baseUrl(DouBaoChatModel.BASE_URL)
+                        .apiKey(properties.getApiKey())
+                        .build())
+                .defaultOptions(OpenAiChatOptions.builder()
+                        .model(properties.getModel())
+                        .temperature(properties.getTemperature())
+                        .maxTokens(properties.getMaxTokens())
+                        .topP(properties.getTopP())
+                        .build())
+                .toolCallingManager(getToolCallingManager())
+                .build();
+        return new DouBaoChatModel(openAiChatModel);
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "iailab.ai.siliconflow.enable", havingValue = "true")
+    public SiliconFlowChatModel siliconFlowChatClient(IailabAiProperties IailabAiProperties) {
+        IailabAiProperties.SiliconFlowProperties properties = IailabAiProperties.getSiliconflow();
+        return buildSiliconFlowChatClient(properties);
+    }
+
+    public SiliconFlowChatModel buildSiliconFlowChatClient(IailabAiProperties.SiliconFlowProperties properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(SiliconFlowApiConstants.MODEL_DEFAULT);
+        }
+        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+                .openAiApi(OpenAiApi.builder()
+                        .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
+                        .apiKey(properties.getApiKey())
+                        .build())
+                .defaultOptions(OpenAiChatOptions.builder()
+                        .model(properties.getModel())
+                        .temperature(properties.getTemperature())
+                        .maxTokens(properties.getMaxTokens())
+                        .topP(properties.getTopP())
+                        .build())
+                .toolCallingManager(getToolCallingManager())
+                .build();
+        return new SiliconFlowChatModel(openAiChatModel);
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "iailab.ai.hunyuan.enable", havingValue = "true")
+    public HunYuanChatModel hunYuanChatClient(IailabAiProperties IailabAiProperties) {
+        IailabAiProperties.HunYuanProperties properties = IailabAiProperties.getHunyuan();
+        return buildHunYuanChatClient(properties);
+    }
+
+    public HunYuanChatModel buildHunYuanChatClient(IailabAiProperties.HunYuanProperties properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(HunYuanChatModel.MODEL_DEFAULT);
+        }
+        // 特殊:由于混元大模型不提供 deepseek,而是通过知识引擎,所以需要区分下 URL
+        if (StrUtil.isEmpty(properties.getBaseUrl())) {
+            properties.setBaseUrl(
+                    StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL
+                            : HunYuanChatModel.BASE_URL);
+        }
+        // 创建 OpenAiChatModel、HunYuanChatModel 对象
+        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+                .openAiApi(OpenAiApi.builder()
+                        .baseUrl(properties.getBaseUrl())
+                        .apiKey(properties.getApiKey())
+                        .build())
+                .defaultOptions(OpenAiChatOptions.builder()
+                        .model(properties.getModel())
+                        .temperature(properties.getTemperature())
+                        .maxTokens(properties.getMaxTokens())
+                        .topP(properties.getTopP())
+                        .build())
+                .toolCallingManager(getToolCallingManager())
+                .build();
+        return new HunYuanChatModel(openAiChatModel);
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "iailab.ai.xinghuo.enable", havingValue = "true")
+    public XingHuoChatModel xingHuoChatClient(IailabAiProperties IailabAiProperties) {
+        IailabAiProperties.XingHuoProperties properties = IailabAiProperties.getXinghuo();
+        return buildXingHuoChatClient(properties);
+    }
+
+    public XingHuoChatModel buildXingHuoChatClient(IailabAiProperties.XingHuoProperties properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(XingHuoChatModel.MODEL_DEFAULT);
+        }
+        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+                .openAiApi(OpenAiApi.builder()
+                        .baseUrl(XingHuoChatModel.BASE_URL)
+                        .apiKey(properties.getAppKey() + ":" + properties.getSecretKey())
+                        .build())
+                .defaultOptions(OpenAiChatOptions.builder()
+                        .model(properties.getModel())
+                        .temperature(properties.getTemperature())
+                        .maxTokens(properties.getMaxTokens())
+                        .topP(properties.getTopP())
+                        .build())
+                .toolCallingManager(getToolCallingManager())
+                .build();
+        return new XingHuoChatModel(openAiChatModel);
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "iailab.ai.baichuan.enable", havingValue = "true")
+    public BaiChuanChatModel baiChuanChatClient(IailabAiProperties IailabAiProperties) {
+        IailabAiProperties.BaiChuanProperties properties = IailabAiProperties.getBaichuan();
+        return buildBaiChuanChatClient(properties);
+    }
+
+    public BaiChuanChatModel buildBaiChuanChatClient(IailabAiProperties.BaiChuanProperties properties) {
+        if (StrUtil.isEmpty(properties.getModel())) {
+            properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);
+        }
+        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+                .openAiApi(OpenAiApi.builder()
+                        .baseUrl(BaiChuanChatModel.BASE_URL)
+                        .apiKey(properties.getApiKey())
+                        .build())
+                .defaultOptions(OpenAiChatOptions.builder()
+                        .model(properties.getModel())
+                        .temperature(properties.getTemperature())
+                        .maxTokens(properties.getMaxTokens())
+                        .topP(properties.getTopP())
+                        .build())
+                .toolCallingManager(getToolCallingManager())
+                .build();
+        return new BaiChuanChatModel(openAiChatModel);
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "iailab.ai.midjourney.enable", havingValue = "true")
+    public MidjourneyApi midjourneyApi(IailabAiProperties IailabAiProperties) {
+        IailabAiProperties.MidjourneyProperties config = IailabAiProperties.getMidjourney();
+        return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl());
+    }
+
+    @Bean
+    @ConditionalOnProperty(value = "iailab.ai.suno.enable", havingValue = "true")
+    public SunoApi sunoApi(IailabAiProperties IailabAiProperties) {
+        return new SunoApi(IailabAiProperties.getSuno().getBaseUrl());
+    }
+
+    // ========== RAG 相关 ==========
+
+    @Bean
+    public TokenCountEstimator tokenCountEstimator() {
+        return new JTokkitTokenCountEstimator();
+    }
+
+    @Bean
+    public BatchingStrategy batchingStrategy() {
+        return new TokenCountBatchingStrategy();
+    }
+
+    private static ToolCallingManager getToolCallingManager() {
+        return SpringUtil.getBean(ToolCallingManager.class);
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/config/IailabAiProperties.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/config/IailabAiProperties.java
new file mode 100644
index 0000000..6974677
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/config/IailabAiProperties.java
@@ -0,0 +1,164 @@
+package com.iailab.framework.ai.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Iailab AI 配置类
+ *
+ * @author fansili
+ * @since 1.0
+ */
+@ConfigurationProperties(prefix = "iailab.ai")
+@Data
+public class IailabAiProperties {
+
+    /**
+     * DeepSeek
+     */
+    @SuppressWarnings("SpellCheckingInspection")
+    private DeepSeekProperties deepseek;
+
+    /**
+     * 字节豆包
+     */
+    @SuppressWarnings("SpellCheckingInspection")
+    private DouBaoProperties doubao;
+
+    /**
+     * 腾讯混元
+     */
+    @SuppressWarnings("SpellCheckingInspection")
+    private HunYuanProperties hunyuan;
+
+    /**
+     * 硅基流动
+     */
+    @SuppressWarnings("SpellCheckingInspection")
+    private SiliconFlowProperties siliconflow;
+
+    /**
+     * 讯飞星火
+     */
+    @SuppressWarnings("SpellCheckingInspection")
+    private XingHuoProperties xinghuo;
+
+    /**
+     * 百川
+     */
+    @SuppressWarnings("SpellCheckingInspection")
+    private BaiChuanProperties baichuan;
+
+    /**
+     * Midjourney 绘图
+     */
+    private MidjourneyProperties midjourney;
+
+    /**
+     * Suno 音乐
+     */
+    @SuppressWarnings("SpellCheckingInspection")
+    private SunoProperties suno;
+
+    @Data
+    public static class DeepSeekProperties {
+
+        private String enable;
+        private String apiKey;
+
+        private String model;
+        private Double temperature;
+        private Integer maxTokens;
+        private Double topP;
+
+    }
+
+    @Data
+    public static class DouBaoProperties {
+
+        private String enable;
+        private String apiKey;
+
+        private String model;
+        private Double temperature;
+        private Integer maxTokens;
+        private Double topP;
+
+    }
+
+    @Data
+    public static class HunYuanProperties {
+
+        private String enable;
+        private String baseUrl;
+        private String apiKey;
+
+        private String model;
+        private Double temperature;
+        private Integer maxTokens;
+        private Double topP;
+
+    }
+
+    @Data
+    public static class SiliconFlowProperties {
+
+        private String enable;
+        private String apiKey;
+
+        private String model;
+        private Double temperature;
+        private Integer maxTokens;
+        private Double topP;
+
+    }
+
+    @Data
+    public static class XingHuoProperties {
+
+        private String enable;
+        private String appId;
+        private String appKey;
+        private String secretKey;
+
+        private String model;
+        private Double temperature;
+        private Integer maxTokens;
+        private Double topP;
+
+    }
+
+    @Data
+    public static class  BaiChuanProperties {
+
+        private String enable;
+        private String apiKey;
+
+        private String model;
+        private Double temperature;
+        private Integer maxTokens;
+        private Double topP;
+
+    }
+
+    @Data
+    public static class MidjourneyProperties {
+
+        private String enable;
+        private String baseUrl;
+
+        private String apiKey;
+        private String notifyUrl;
+
+    }
+
+    @Data
+    public static class SunoProperties {
+
+        private boolean enable = false;
+
+        private String baseUrl;
+
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/enums/AiModelTypeEnum.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/enums/AiModelTypeEnum.java
new file mode 100644
index 0000000..6b30ae2
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/enums/AiModelTypeEnum.java
@@ -0,0 +1,41 @@
+package com.iailab.framework.ai.core.enums;
+
+import com.iailab.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * AI 模型类型的枚举
+ *
+ * @author Iailab
+ */
+@Getter
+@RequiredArgsConstructor
+public enum AiModelTypeEnum implements ArrayValuable<Integer> {
+
+    CHAT(1, "对话"),
+    IMAGE(2, "图片"),
+    VOICE(3, "语音"),
+    VIDEO(4, "视频"),
+    EMBEDDING(5, "向量"),
+    RERANK(6, "重排序");
+
+    /**
+     * 类型
+     */
+    private final Integer type;
+    /**
+     * 类型名
+     */
+    private final String name;
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiModelTypeEnum::getType).toArray(Integer[]::new);
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/enums/AiPlatformEnum.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/enums/AiPlatformEnum.java
new file mode 100644
index 0000000..bfe0607
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/enums/AiPlatformEnum.java
@@ -0,0 +1,70 @@
+package com.iailab.framework.ai.core.enums;
+
+import com.iailab.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * AI 模型平台
+ *
+ * @author fansili
+ */
+@Getter
+@AllArgsConstructor
+public enum AiPlatformEnum implements ArrayValuable<String> {
+
+    // ========== 国内平台 ==========
+
+    TONG_YI("TongYi", "通义千问"), // 阿里
+    YI_YAN("YiYan", "文心一言"), // 百度
+    DEEP_SEEK("DeepSeek", "DeepSeek"), // DeepSeek
+    ZHI_PU("ZhiPu", "智谱"), // 智谱 AI
+    XING_HUO("XingHuo", "星火"), // 讯飞
+    DOU_BAO("DouBao", "豆包"), // 字节
+    HUN_YUAN("HunYuan", "混元"), // 腾讯
+    SILICON_FLOW("SiliconFlow", "硅基流动"), // 硅基流动
+    MINI_MAX("MiniMax", "MiniMax"), // 稀宇科技
+    MOONSHOT("Moonshot", "月之暗灭"), // KIMI
+    BAI_CHUAN("BaiChuan", "百川智能"), // 百川智能
+
+    // ========== 国外平台 ==========
+
+    OPENAI("OpenAI", "OpenAI"), // OpenAI 官方
+    AZURE_OPENAI("AzureOpenAI", "AzureOpenAI"), // OpenAI 微软
+    OLLAMA("Ollama", "Ollama"),
+
+    STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI
+    MIDJOURNEY("Midjourney", "Midjourney"), // Midjourney
+    SUNO("Suno", "Suno"), // Suno AI
+    LLAMA_FACTORY("LlamaFactory", "LlamaFactory"), // LlamaFactory
+
+    ;
+
+    /**
+     * 平台
+     */
+    private final String platform;
+    /**
+     * 平台名
+     */
+    private final String name;
+
+    public static final String[] ARRAYS = Arrays.stream(values()).map(AiPlatformEnum::getPlatform).toArray(String[]::new);
+
+    public static AiPlatformEnum validatePlatform(String platform) {
+        for (AiPlatformEnum platformEnum : AiPlatformEnum.values()) {
+            if (platformEnum.getPlatform().equals(platform)) {
+                return platformEnum;
+            }
+        }
+        throw new IllegalArgumentException("非法平台: " + platform);
+    }
+
+    @Override
+    public String[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/factory/AiModelFactory.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/factory/AiModelFactory.java
new file mode 100644
index 0000000..7867773
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/factory/AiModelFactory.java
@@ -0,0 +1,113 @@
+package com.iailab.framework.ai.core.factory;
+
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import com.iailab.framework.ai.core.model.suno.api.SunoApi;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.image.ImageModel;
+import org.springframework.ai.vectorstore.VectorStore;
+
+import java.util.Map;
+
+/**
+ * AI Model 模型工厂的接口类
+ *
+ * @author fansili
+ */
+public interface AiModelFactory {
+
+    /**
+     * 基于指定配置,获得 ChatModel 对象
+     *
+     * 如果不存在,则进行创建
+     *
+     * @param platform 平台
+     * @param apiKey API KEY
+     * @param url API URL
+     * @return ChatModel 对象
+     */
+    ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url);
+
+    /**
+     * 基于默认配置,获得 ChatModel 对象
+     *
+     * 默认配置,指的是在 application.yaml 配置文件中的 spring.ai 相关的配置
+     *
+     * @param platform 平台
+     * @return ChatModel 对象
+     */
+    ChatModel getDefaultChatModel(AiPlatformEnum platform);
+
+    /**
+     * 基于默认配置,获得 ImageModel 对象
+     *
+     * 默认配置,指的是在 application.yaml 配置文件中的 spring.ai 相关的配置
+     *
+     * @param platform 平台
+     * @return ImageModel 对象
+     */
+    ImageModel getDefaultImageModel(AiPlatformEnum platform);
+
+    /**
+     * 基于指定配置,获得 ImageModel 对象
+     *
+     * 如果不存在,则进行创建
+     *
+     * @param platform 平台
+     * @param apiKey API KEY
+     * @param url API URL
+     * @return ImageModel 对象
+     */
+    ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url);
+
+    /**
+     * 基于指定配置,获得 MidjourneyApi 对象
+     *
+     * 如果不存在,则进行创建
+     *
+     * @param apiKey API KEY
+     * @param url API URL
+     * @return MidjourneyApi 对象
+     */
+    MidjourneyApi getOrCreateMidjourneyApi(String apiKey, String url);
+
+    /**
+     * 基于指定配置,获得 SunoApi 对象
+     *
+     * 如果不存在,则进行创建
+     *
+     * @param apiKey API KEY
+     * @param url API URL
+     * @return SunoApi 对象
+     */
+    SunoApi getOrCreateSunoApi(String apiKey, String url);
+
+    /**
+     * 基于指定配置,获得 EmbeddingModel 对象
+     *
+     * 如果不存在,则进行创建
+     *
+     * @param platform 平台
+     * @param apiKey   API KEY
+     * @param url      API URL
+     * @param model     模型
+     * @return ChatModel 对象
+     */
+    EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model);
+
+    /**
+     * 基于指定配置,获得 VectorStore 对象
+     *
+     * 如果不存在,则进行创建
+     *
+     * @param type           向量存储类型
+     * @param embeddingModel 向量模型
+     * @param metadataFields 元数据字段
+     * @return VectorStore 对象
+     */
+    VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type,
+                                       EmbeddingModel embeddingModel,
+                                       Map<String, Class<?>> metadataFields);
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/factory/AiModelFactoryImpl.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/factory/AiModelFactoryImpl.java
new file mode 100644
index 0000000..c0934e5
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/factory/AiModelFactoryImpl.java
@@ -0,0 +1,752 @@
+package com.iailab.framework.ai.core.factory;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.lang.Singleton;
+import cn.hutool.core.lang.func.Func0;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.RuntimeUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration;
+import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
+import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
+import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
+import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
+import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel;
+import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions;
+import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;
+import com.azure.ai.openai.OpenAIClientBuilder;
+import com.iailab.framework.ai.config.IailabAiAutoConfiguration;
+import com.iailab.framework.ai.config.IailabAiProperties;
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import com.iailab.framework.ai.core.model.baichuan.BaiChuanChatModel;
+import com.iailab.framework.ai.core.model.deepseek.DeepSeekChatModel;
+import com.iailab.framework.ai.core.model.doubao.DouBaoChatModel;
+import com.iailab.framework.ai.core.model.hunyuan.HunYuanChatModel;
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowImageApi;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowImageModel;
+import com.iailab.framework.ai.core.model.suno.api.SunoApi;
+import com.iailab.framework.ai.core.model.xinghuo.XingHuoChatModel;
+import com.iailab.framework.common.util.spring.SpringUtils;
+import io.micrometer.observation.ObservationRegistry;
+import io.milvus.client.MilvusServiceClient;
+import io.qdrant.client.QdrantClient;
+import io.qdrant.client.QdrantGrpcClient;
+import lombok.SneakyThrows;
+import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration;
+import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties;
+import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionProperties;
+import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiEmbeddingProperties;
+import org.springframework.ai.autoconfigure.minimax.MiniMaxAutoConfiguration;
+import org.springframework.ai.autoconfigure.moonshot.MoonshotAutoConfiguration;
+import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration;
+import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration;
+import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration;
+import org.springframework.ai.autoconfigure.stabilityai.StabilityAiImageAutoConfiguration;
+import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientConnectionDetails;
+import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties;
+import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration;
+import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties;
+import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration;
+import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties;
+import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration;
+import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
+import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration;
+import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
+import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.document.MetadataMode;
+import org.springframework.ai.embedding.BatchingStrategy;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.image.ImageModel;
+import org.springframework.ai.minimax.MiniMaxChatModel;
+import org.springframework.ai.minimax.MiniMaxChatOptions;
+import org.springframework.ai.minimax.MiniMaxEmbeddingModel;
+import org.springframework.ai.minimax.MiniMaxEmbeddingOptions;
+import org.springframework.ai.minimax.api.MiniMaxApi;
+import org.springframework.ai.model.function.FunctionCallbackResolver;
+import org.springframework.ai.model.tool.ToolCallingManager;
+import org.springframework.ai.moonshot.MoonshotChatModel;
+import org.springframework.ai.moonshot.MoonshotChatOptions;
+import org.springframework.ai.moonshot.api.MoonshotApi;
+import org.springframework.ai.ollama.OllamaChatModel;
+import org.springframework.ai.ollama.OllamaEmbeddingModel;
+import org.springframework.ai.ollama.api.OllamaApi;
+import org.springframework.ai.ollama.api.OllamaOptions;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiEmbeddingModel;
+import org.springframework.ai.openai.OpenAiEmbeddingOptions;
+import org.springframework.ai.openai.OpenAiImageModel;
+import org.springframework.ai.openai.api.OpenAiApi;
+import org.springframework.ai.openai.api.OpenAiImageApi;
+import org.springframework.ai.openai.api.common.OpenAiApiConstants;
+import org.springframework.ai.qianfan.QianFanChatModel;
+import org.springframework.ai.qianfan.QianFanEmbeddingModel;
+import org.springframework.ai.qianfan.QianFanEmbeddingOptions;
+import org.springframework.ai.qianfan.QianFanImageModel;
+import org.springframework.ai.qianfan.api.QianFanApi;
+import org.springframework.ai.qianfan.api.QianFanImageApi;
+import org.springframework.ai.stabilityai.StabilityAiImageModel;
+import org.springframework.ai.stabilityai.api.StabilityAiApi;
+import org.springframework.ai.vectorstore.SimpleVectorStore;
+import org.springframework.ai.vectorstore.VectorStore;
+import org.springframework.ai.vectorstore.milvus.MilvusVectorStore;
+import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention;
+import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
+import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore;
+import org.springframework.ai.vectorstore.redis.RedisVectorStore;
+import org.springframework.ai.zhipuai.*;
+import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
+import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
+import org.springframework.web.client.RestClient;
+import redis.clients.jedis.JedisPooled;
+
+import java.io.File;
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
+import static org.springframework.ai.retry.RetryUtils.DEFAULT_RETRY_TEMPLATE;
+
+/**
+ * AI Model 模型工厂的实现类
+ *
+ * @author Iailab
+ */
+public class AiModelFactoryImpl implements AiModelFactory {
+
+    @Override
+    public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url) {
+        String cacheKey = buildClientCacheKey(ChatModel.class, platform, apiKey, url);
+        return Singleton.get(cacheKey, (Func0<ChatModel>) () -> {
+            // noinspection EnhancedSwitchMigration
+            switch (platform) {
+                case TONG_YI:
+                    return buildTongYiChatModel(apiKey);
+                case YI_YAN:
+                    return buildYiYanChatModel(apiKey);
+                case DEEP_SEEK:
+                    return buildDeepSeekChatModel(apiKey);
+                case DOU_BAO:
+                    return buildDouBaoChatModel(apiKey);
+                case HUN_YUAN:
+                    return buildHunYuanChatModel(apiKey, url);
+                case SILICON_FLOW:
+                    return buildSiliconFlowChatModel(apiKey);
+                case ZHI_PU:
+                    return buildZhiPuChatModel(apiKey, url);
+                case MINI_MAX:
+                    return buildMiniMaxChatModel(apiKey, url);
+                case MOONSHOT:
+                    return buildMoonshotChatModel(apiKey, url);
+                case XING_HUO:
+                    return buildXingHuoChatModel(apiKey);
+                case BAI_CHUAN:
+                    return buildBaiChuanChatModel(apiKey);
+                case OPENAI:
+                    return buildOpenAiChatModel(apiKey, url);
+                case AZURE_OPENAI:
+                    return buildAzureOpenAiChatModel(apiKey, url);
+                case OLLAMA:
+                    return buildOllamaChatModel(url);
+                default:
+                    throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
+            }
+        });
+    }
+
+    @Override
+    public ChatModel getDefaultChatModel(AiPlatformEnum platform) {
+        // noinspection EnhancedSwitchMigration
+        switch (platform) {
+            case TONG_YI:
+                return SpringUtil.getBean(DashScopeChatModel.class);
+            case YI_YAN:
+                return SpringUtil.getBean(QianFanChatModel.class);
+            case DEEP_SEEK:
+                return SpringUtil.getBean(DeepSeekChatModel.class);
+            case DOU_BAO:
+                return SpringUtil.getBean(DouBaoChatModel.class);
+            case HUN_YUAN:
+                return SpringUtil.getBean(HunYuanChatModel.class);
+            case SILICON_FLOW:
+                return SpringUtil.getBean(SiliconFlowChatModel.class);
+            case ZHI_PU:
+                return SpringUtil.getBean(ZhiPuAiChatModel.class);
+            case MINI_MAX:
+                return SpringUtil.getBean(MiniMaxChatModel.class);
+            case MOONSHOT:
+                return SpringUtil.getBean(MoonshotChatModel.class);
+            case XING_HUO:
+                return SpringUtil.getBean(XingHuoChatModel.class);
+            case BAI_CHUAN:
+                return SpringUtil.getBean(AzureOpenAiChatModel.class);
+            case OPENAI:
+                return SpringUtil.getBean(OpenAiChatModel.class);
+            case AZURE_OPENAI:
+                return SpringUtil.getBean(AzureOpenAiChatModel.class);
+            case OLLAMA:
+                return SpringUtil.getBean(OllamaChatModel.class);
+            default:
+                throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
+        }
+    }
+
+    @Override
+    public ImageModel getDefaultImageModel(AiPlatformEnum platform) {
+        // noinspection EnhancedSwitchMigration
+        switch (platform) {
+            case TONG_YI:
+                return SpringUtil.getBean(DashScopeImageModel.class);
+            case YI_YAN:
+                return SpringUtil.getBean(QianFanImageModel.class);
+            case ZHI_PU:
+                return SpringUtil.getBean(ZhiPuAiImageModel.class);
+            case SILICON_FLOW:
+                return SpringUtil.getBean(SiliconFlowImageModel.class);
+            case OPENAI:
+                return SpringUtil.getBean(OpenAiImageModel.class);
+            case STABLE_DIFFUSION:
+                return SpringUtil.getBean(StabilityAiImageModel.class);
+            default:
+                throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
+        }
+    }
+
+    @Override
+    public ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url) {
+        // noinspection EnhancedSwitchMigration
+        switch (platform) {
+            case TONG_YI:
+                return buildTongYiImagesModel(apiKey);
+            case YI_YAN:
+                return buildQianFanImageModel(apiKey);
+            case ZHI_PU:
+                return buildZhiPuAiImageModel(apiKey, url);
+            case OPENAI:
+                return buildOpenAiImageModel(apiKey, url);
+            case SILICON_FLOW:
+                return buildSiliconFlowImageModel(apiKey,url);
+            case STABLE_DIFFUSION:
+                return buildStabilityAiImageModel(apiKey, url);
+            default:
+                throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
+        }
+    }
+
+    @Override
+    public MidjourneyApi getOrCreateMidjourneyApi(String apiKey, String url) {
+        String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey,
+                url);
+        return Singleton.get(cacheKey, (Func0<MidjourneyApi>) () -> {
+            IailabAiProperties.MidjourneyProperties properties = SpringUtil.getBean(IailabAiProperties.class)
+                    .getMidjourney();
+            return new MidjourneyApi(url, apiKey, properties.getNotifyUrl());
+        });
+    }
+
+    @Override
+    public SunoApi getOrCreateSunoApi(String apiKey, String url) {
+        String cacheKey = buildClientCacheKey(SunoApi.class, AiPlatformEnum.SUNO.getPlatform(), apiKey, url);
+        return Singleton.get(cacheKey, (Func0<SunoApi>) () -> new SunoApi(url));
+    }
+
+    @Override
+    @SuppressWarnings("EnhancedSwitchMigration")
+    public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model) {
+        String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url, model);
+        return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> {
+            switch (platform) {
+                case TONG_YI:
+                    return buildTongYiEmbeddingModel(apiKey, model);
+                case YI_YAN:
+                    return buildYiYanEmbeddingModel(apiKey, model);
+                case ZHI_PU:
+                    return buildZhiPuEmbeddingModel(apiKey, url, model);
+                case MINI_MAX:
+                    return buildMiniMaxEmbeddingModel(apiKey, url, model);
+                case OPENAI:
+                    return buildOpenAiEmbeddingModel(apiKey, url, model);
+                case AZURE_OPENAI:
+                    return buildAzureOpenAiEmbeddingModel(apiKey, url, model);
+                case OLLAMA:
+                    return buildOllamaEmbeddingModel(url, model);
+                default:
+                    throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
+            }
+        });
+    }
+
+    @Override
+    public VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type,
+                                              EmbeddingModel embeddingModel,
+                                              Map<String, Class<?>> metadataFields) {
+        String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type);
+        return Singleton.get(cacheKey, (Func0<VectorStore>) () -> {
+            if (type == SimpleVectorStore.class) {
+                return buildSimpleVectorStore(embeddingModel);
+            }
+            if (type == QdrantVectorStore.class) {
+                return buildQdrantVectorStore(embeddingModel);
+            }
+            if (type == RedisVectorStore.class) {
+                return buildRedisVectorStore(embeddingModel, metadataFields);
+            }
+            if (type == MilvusVectorStore.class) {
+                return buildMilvusVectorStore(embeddingModel);
+            }
+            throw new IllegalArgumentException(StrUtil.format("未知类型({})", type));
+        });
+    }
+
+    private static String buildClientCacheKey(Class<?> clazz, Object... params) {
+        if (ArrayUtil.isEmpty(params)) {
+            return clazz.getName();
+        }
+        return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_"));
+    }
+
+    // ========== 各种创建 spring-ai 客户端的方法 ==========
+
+    /**
+     * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeChatModel 方法
+     */
+    private static DashScopeChatModel buildTongYiChatModel(String key) {
+        DashScopeApi dashScopeApi = new DashScopeApi(key);
+        DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(DashScopeApi.DEFAULT_CHAT_MODEL)
+                .withTemperature(0.7).build();
+        return new DashScopeChatModel(dashScopeApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE);
+    }
+
+    /**
+     * 可参考 {@link DashScopeAutoConfiguration} 的 dashScopeImageModel 方法
+     */
+    private static DashScopeImageModel buildTongYiImagesModel(String key) {
+        DashScopeImageApi dashScopeImageApi = new DashScopeImageApi(key);
+        return new DashScopeImageModel(dashScopeImageApi);
+    }
+
+    /**
+     * 可参考 {@link QianFanAutoConfiguration} 的 qianFanChatModel 方法
+     */
+    private static QianFanChatModel buildYiYanChatModel(String key) {
+        List<String> keys = StrUtil.split(key, '|');
+        Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
+        String appKey = keys.get(0);
+        String secretKey = keys.get(1);
+        QianFanApi qianFanApi = new QianFanApi(appKey, secretKey);
+        return new QianFanChatModel(qianFanApi);
+    }
+
+    /**
+     * 可参考 {@link QianFanAutoConfiguration} 的 qianFanImageModel 方法
+     */
+    private QianFanImageModel buildQianFanImageModel(String key) {
+        List<String> keys = StrUtil.split(key, '|');
+        Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
+        String appKey = keys.get(0);
+        String secretKey = keys.get(1);
+        QianFanImageApi qianFanApi = new QianFanImageApi(appKey, secretKey);
+        return new QianFanImageModel(qianFanApi);
+    }
+
+    /**
+     * 可参考 {@link IailabAiAutoConfiguration#deepSeekChatModel(IailabAiProperties)}
+     */
+    private static DeepSeekChatModel buildDeepSeekChatModel(String apiKey) {
+        IailabAiProperties.DeepSeekProperties properties = new IailabAiProperties.DeepSeekProperties()
+                .setApiKey(apiKey);
+        return new IailabAiAutoConfiguration().buildDeepSeekChatModel(properties);
+    }
+
+    /**
+     * 可参考 {@link IailabAiAutoConfiguration#douBaoChatClient(IailabAiProperties)}
+     */
+    private ChatModel buildDouBaoChatModel(String apiKey) {
+        IailabAiProperties.DouBaoProperties properties = new IailabAiProperties.DouBaoProperties()
+                .setApiKey(apiKey);
+        return new IailabAiAutoConfiguration().buildDouBaoChatClient(properties);
+    }
+
+    /**
+     * 可参考 {@link IailabAiAutoConfiguration#hunYuanChatClient(IailabAiProperties)}
+     */
+    private ChatModel buildHunYuanChatModel(String apiKey, String url) {
+        IailabAiProperties.HunYuanProperties properties = new IailabAiProperties.HunYuanProperties()
+                .setBaseUrl(url).setApiKey(apiKey);
+        return new IailabAiAutoConfiguration().buildHunYuanChatClient(properties);
+    }
+
+    /**
+     * 可参考 {@link IailabAiAutoConfiguration#siliconFlowChatClient(IailabAiProperties)}
+     */
+    private ChatModel buildSiliconFlowChatModel(String apiKey) {
+        IailabAiProperties.SiliconFlowProperties properties = new IailabAiProperties.SiliconFlowProperties()
+                .setApiKey(apiKey);
+        return new IailabAiAutoConfiguration().buildSiliconFlowChatClient(properties);
+    }
+
+    /**
+     * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiChatModel 方法
+     */
+    private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) {
+        ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey)
+                : new ZhiPuAiApi(url, apiKey);
+        ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build();
+        return new ZhiPuAiChatModel(zhiPuAiApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE);
+    }
+
+    /**
+     * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiImageModel 方法
+     */
+    private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) {
+        ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey)
+                : new ZhiPuAiImageApi(url, apiKey, RestClient.builder());
+        return new ZhiPuAiImageModel(zhiPuAiApi);
+    }
+
+    /**
+     * 可参考 {@link MiniMaxAutoConfiguration} 的 miniMaxChatModel 方法
+     */
+    private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) {
+        MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey)
+                : new MiniMaxApi(url, apiKey);
+        MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build();
+        return new MiniMaxChatModel(miniMaxApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE);
+    }
+
+    /**
+     * 可参考 {@link MoonshotAutoConfiguration} 的 moonshotChatModel 方法
+     */
+    private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) {
+        MoonshotApi moonshotApi = StrUtil.isEmpty(url)? new MoonshotApi(apiKey)
+                : new MoonshotApi(url, apiKey);
+        MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build();
+        return new MoonshotChatModel(moonshotApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE);
+    }
+
+    /**
+     * 可参考 {@link IailabAiAutoConfiguration#xingHuoChatClient(IailabAiProperties)}
+     */
+    private static XingHuoChatModel buildXingHuoChatModel(String key) {
+        List<String> keys = StrUtil.split(key, '|');
+        Assert.equals(keys.size(), 2, "XingHuoChatClient 的密钥需要 (appKey|secretKey) 格式");
+        IailabAiProperties.XingHuoProperties properties = new IailabAiProperties.XingHuoProperties()
+                .setAppKey(keys.get(0)).setSecretKey(keys.get(1));
+        return new IailabAiAutoConfiguration().buildXingHuoChatClient(properties);
+    }
+
+    /**
+     * 可参考 {@link IailabAiAutoConfiguration#baiChuanChatClient(IailabAiProperties)}
+     */
+    private BaiChuanChatModel buildBaiChuanChatModel(String apiKey) {
+        IailabAiProperties.BaiChuanProperties properties = new IailabAiProperties.BaiChuanProperties()
+                .setApiKey(apiKey);
+        return new IailabAiAutoConfiguration().buildBaiChuanChatClient(properties);
+    }
+
+    /**
+     * 可参考 {@link OpenAiAutoConfiguration} 的 openAiChatModel 方法
+     */
+    private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) {
+        url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL);
+        OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build();
+        return OpenAiChatModel.builder().openAiApi(openAiApi).toolCallingManager(getToolCallingManager()).build();
+    }
+
+    // TODO @Iailab:手头暂时没密钥,使用建议再测试下
+    /**
+     * 可参考 {@link AzureOpenAiAutoConfiguration}
+     */
+    private static AzureOpenAiChatModel buildAzureOpenAiChatModel(String apiKey, String url) {
+        AzureOpenAiAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiAutoConfiguration();
+        // 创建 OpenAIClient 对象
+        AzureOpenAiConnectionProperties connectionProperties = new AzureOpenAiConnectionProperties();
+        connectionProperties.setApiKey(apiKey);
+        connectionProperties.setEndpoint(url);
+        OpenAIClientBuilder openAIClient = azureOpenAiAutoConfiguration.openAIClientBuilder(connectionProperties, null);
+        // 获取 AzureOpenAiChatProperties 对象
+        AzureOpenAiChatProperties chatProperties = SpringUtil.getBean(AzureOpenAiChatProperties.class);
+        return azureOpenAiAutoConfiguration.azureOpenAiChatModel(openAIClient, chatProperties,
+                getToolCallingManager(), null, null);
+    }
+
+    /**
+     * 可参考 {@link OpenAiAutoConfiguration} 的 openAiImageModel 方法
+     */
+    private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) {
+        url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL);
+        OpenAiImageApi openAiApi = OpenAiImageApi.builder().baseUrl(url).apiKey(openAiToken).build();
+        return new OpenAiImageModel(openAiApi);
+    }
+
+    /**
+     * 创建 SiliconFlowImageModel 对象
+     */
+    private SiliconFlowImageModel buildSiliconFlowImageModel(String apiToken, String url) {
+        url = StrUtil.blankToDefault(url, SiliconFlowApiConstants.DEFAULT_BASE_URL);
+        SiliconFlowImageApi openAiApi = new SiliconFlowImageApi(url, apiToken);
+        return new SiliconFlowImageModel(openAiApi);
+    }
+
+    /**
+     * 可参考 {@link OllamaAutoConfiguration} 的 ollamaApi 方法
+     */
+    private static OllamaChatModel buildOllamaChatModel(String url) {
+        OllamaApi ollamaApi = new OllamaApi(url);
+        return OllamaChatModel.builder().ollamaApi(ollamaApi).toolCallingManager(getToolCallingManager()).build();
+    }
+
+    /**
+     * 可参考 {@link StabilityAiImageAutoConfiguration} 的 stabilityAiImageModel 方法
+     */
+    private StabilityAiImageModel buildStabilityAiImageModel(String apiKey, String url) {
+        url = StrUtil.blankToDefault(url, StabilityAiApi.DEFAULT_BASE_URL);
+        StabilityAiApi stabilityAiApi = new StabilityAiApi(apiKey, StabilityAiApi.DEFAULT_IMAGE_MODEL, url);
+        return new StabilityAiImageModel(stabilityAiApi);
+    }
+
+    // ========== 各种创建 EmbeddingModel 的方法 ==========
+
+    /**
+     * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeEmbeddingModel 方法
+     */
+    private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) {
+        DashScopeApi dashScopeApi = new DashScopeApi(apiKey);
+        DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model).build();
+        return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions);
+    }
+
+    /**
+     * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法
+     */
+    private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) {
+        ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey)
+                : new ZhiPuAiApi(url, apiKey);
+        ZhiPuAiEmbeddingOptions zhiPuAiEmbeddingOptions = ZhiPuAiEmbeddingOptions.builder().model(model).build();
+        return new ZhiPuAiEmbeddingModel(zhiPuAiApi, MetadataMode.EMBED, zhiPuAiEmbeddingOptions);
+    }
+
+    /**
+     * 可参考 {@link MiniMaxAutoConfiguration} 的 miniMaxEmbeddingModel 方法
+     */
+    private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) {
+        MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey)
+                : new MiniMaxApi(url, apiKey);
+        MiniMaxEmbeddingOptions miniMaxEmbeddingOptions = MiniMaxEmbeddingOptions.builder().model(model).build();
+        return new MiniMaxEmbeddingModel(miniMaxApi, MetadataMode.EMBED, miniMaxEmbeddingOptions);
+    }
+
+    /**
+     * 可参考 {@link QianFanAutoConfiguration} 的 qianFanEmbeddingModel 方法
+     */
+    private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) {
+        List<String> keys = StrUtil.split(key, '|');
+        Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
+        String appKey = keys.get(0);
+        String secretKey = keys.get(1);
+        QianFanApi qianFanApi = new QianFanApi(appKey, secretKey);
+        QianFanEmbeddingOptions qianFanEmbeddingOptions = QianFanEmbeddingOptions.builder().model(model).build();
+        return new QianFanEmbeddingModel(qianFanApi, MetadataMode.EMBED, qianFanEmbeddingOptions);
+    }
+
+    private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) {
+        OllamaApi ollamaApi = new OllamaApi(url);
+        OllamaOptions ollamaOptions = OllamaOptions.builder().model(model).build();
+        return OllamaEmbeddingModel.builder().ollamaApi(ollamaApi).defaultOptions(ollamaOptions).build();
+    }
+
+    /**
+     * 可参考 {@link OpenAiAutoConfiguration} 的 openAiEmbeddingModel 方法
+     */
+    private OpenAiEmbeddingModel buildOpenAiEmbeddingModel(String openAiToken, String url, String model) {
+        url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL);
+        OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build();
+        OpenAiEmbeddingOptions openAiEmbeddingProperties = OpenAiEmbeddingOptions.builder().model(model).build();
+        return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, openAiEmbeddingProperties);
+    }
+
+    // TODO @Iailab:手头暂时没密钥,使用建议再测试下
+    /**
+     * 可参考 {@link AzureOpenAiAutoConfiguration} 的 azureOpenAiEmbeddingModel 方法
+     */
+    private AzureOpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String apiKey, String url, String model) {
+        AzureOpenAiAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiAutoConfiguration();
+        // 创建 OpenAIClient 对象
+        AzureOpenAiConnectionProperties connectionProperties = new AzureOpenAiConnectionProperties();
+        connectionProperties.setApiKey(apiKey);
+        connectionProperties.setEndpoint(url);
+        OpenAIClientBuilder openAIClient = azureOpenAiAutoConfiguration.openAIClientBuilder(connectionProperties, null);
+        // 获取 AzureOpenAiChatProperties 对象
+        AzureOpenAiEmbeddingProperties embeddingProperties = SpringUtil.getBean(AzureOpenAiEmbeddingProperties.class);
+        return azureOpenAiAutoConfiguration.azureOpenAiEmbeddingModel(openAIClient, embeddingProperties,
+                null, null);
+    }
+
+    // ========== 各种创建 VectorStore 的方法 ==========
+
+    /**
+     * 注意:仅适合本地测试使用,生产建议还是使用 Qdrant、Milvus 等
+     */
+    @SneakyThrows
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    private SimpleVectorStore buildSimpleVectorStore(EmbeddingModel embeddingModel) {
+        SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build();
+        // 启动加载
+        File file = new File(StrUtil.format("{}/vector_store/simple_{}.json",
+                FileUtil.getUserHomePath(), embeddingModel.getClass().getSimpleName()));
+        if (!file.exists()) {
+            FileUtil.mkParentDirs(file);
+            file.createNewFile();
+        } else if (file.length() > 0) {
+            vectorStore.load(file);
+        }
+        // 定时持久化,每分钟一次
+        Timer timer = new Timer("SimpleVectorStoreTimer-" + file.getAbsolutePath());
+        timer.scheduleAtFixedRate(new TimerTask() {
+
+            @Override
+            public void run() {
+                vectorStore.save(file);
+            }
+
+        }, Duration.ofMinutes(1).toMillis(), Duration.ofMinutes(1).toMillis());
+        // 关闭时,进行持久化
+        RuntimeUtil.addShutdownHook(() -> vectorStore.save(file));
+        return vectorStore;
+    }
+
+    /**
+     * 参考 {@link QdrantVectorStoreAutoConfiguration} 的 vectorStore 方法
+     */
+    @SneakyThrows
+    private QdrantVectorStore buildQdrantVectorStore(EmbeddingModel embeddingModel) {
+        QdrantVectorStoreAutoConfiguration configuration = new QdrantVectorStoreAutoConfiguration();
+        QdrantVectorStoreProperties properties = SpringUtil.getBean(QdrantVectorStoreProperties.class);
+        // 参考 QdrantVectorStoreAutoConfiguration 实现,创建 QdrantClient 对象
+        QdrantGrpcClient.Builder grpcClientBuilder = QdrantGrpcClient.newBuilder(
+                properties.getHost(), properties.getPort(), properties.isUseTls());
+        if (StrUtil.isNotEmpty(properties.getApiKey())) {
+            grpcClientBuilder.withApiKey(properties.getApiKey());
+        }
+        QdrantClient qdrantClient = new QdrantClient(grpcClientBuilder.build());
+        // 创建 QdrantVectorStore 对象
+        QdrantVectorStore vectorStore = configuration.vectorStore(embeddingModel, properties, qdrantClient,
+                getObservationRegistry(), getCustomObservationConvention(), getBatchingStrategy());
+        // 初始化索引
+        vectorStore.afterPropertiesSet();
+        return vectorStore;
+    }
+
+    /**
+     * 参考 {@link RedisVectorStoreAutoConfiguration} 的 vectorStore 方法
+     */
+    private RedisVectorStore buildRedisVectorStore(EmbeddingModel embeddingModel,
+                                                   Map<String, Class<?>> metadataFields) {
+        // 创建 JedisPooled 对象
+        RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);
+        JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort());
+        // 创建 RedisVectorStoreProperties 对象
+        RedisVectorStoreAutoConfiguration configuration = new RedisVectorStoreAutoConfiguration();
+        RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class);
+        RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel)
+                .indexName(properties.getIndex()).prefix(properties.getPrefix())
+                .initializeSchema(properties.isInitializeSchema())
+                .metadataFields(convertList(metadataFields.entrySet(), entry -> {
+                    String fieldName = entry.getKey();
+                    Class<?> fieldType = entry.getValue();
+                    if (Number.class.isAssignableFrom(fieldType)) {
+                        return RedisVectorStore.MetadataField.numeric(fieldName);
+                    }
+                    if (Boolean.class.isAssignableFrom(fieldType)) {
+                        return RedisVectorStore.MetadataField.tag(fieldName);
+                    }
+                    return RedisVectorStore.MetadataField.text(fieldName);
+                }))
+                .observationRegistry(getObservationRegistry().getObject())
+                .customObservationConvention(getCustomObservationConvention().getObject())
+                .batchingStrategy(getBatchingStrategy())
+                .build();
+        // 初始化索引
+        redisVectorStore.afterPropertiesSet();
+        return redisVectorStore;
+    }
+
+    /**
+     * 参考 {@link MilvusVectorStoreAutoConfiguration} 的 vectorStore 方法
+     */
+    @SneakyThrows
+    private MilvusVectorStore buildMilvusVectorStore(EmbeddingModel embeddingModel) {
+        MilvusVectorStoreAutoConfiguration configuration = new MilvusVectorStoreAutoConfiguration();
+        // 获取配置属性
+        MilvusVectorStoreProperties serverProperties = SpringUtil.getBean(MilvusVectorStoreProperties.class);
+        MilvusServiceClientProperties clientProperties = SpringUtil.getBean(MilvusServiceClientProperties.class);
+
+        // 创建 MilvusServiceClient 对象
+        MilvusServiceClient milvusClient = configuration.milvusClient(serverProperties, clientProperties,
+                new MilvusServiceClientConnectionDetails() {
+
+                    @Override
+                    public String getHost() {
+                        return clientProperties.getHost();
+                    }
+
+                    @Override
+                    public int getPort() {
+                        return clientProperties.getPort();
+                    }
+
+                }
+        );
+        // 创建 MilvusVectorStore 对象
+        MilvusVectorStore vectorStore = configuration.vectorStore(milvusClient, embeddingModel, serverProperties,
+                getBatchingStrategy(), getObservationRegistry(), getCustomObservationConvention());
+
+        // 初始化索引
+        vectorStore.afterPropertiesSet();
+        return vectorStore;
+    }
+
+    private static ObjectProvider<ObservationRegistry> getObservationRegistry() {
+        return new ObjectProvider<>() {
+
+            @Override
+            public ObservationRegistry getObject() throws BeansException {
+                return SpringUtil.getBean(ObservationRegistry.class);
+            }
+
+        };
+    }
+
+    private static ObjectProvider<VectorStoreObservationConvention> getCustomObservationConvention() {
+        return new ObjectProvider<>() {
+            @Override
+            public VectorStoreObservationConvention getObject() throws BeansException {
+                return new DefaultVectorStoreObservationConvention();
+            }
+        };
+    }
+
+    private static BatchingStrategy getBatchingStrategy() {
+        return SpringUtil.getBean(BatchingStrategy.class);
+    }
+
+    private static ToolCallingManager getToolCallingManager() {
+        return SpringUtil.getBean(ToolCallingManager.class);
+    }
+
+    private static FunctionCallbackResolver getFunctionCallbackResolver() {
+        return SpringUtil.getBean(FunctionCallbackResolver.class);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/baichuan/BaiChuanChatModel.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/baichuan/BaiChuanChatModel.java
new file mode 100644
index 0000000..6addc5b
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/baichuan/BaiChuanChatModel.java
@@ -0,0 +1,45 @@
+package com.iailab.framework.ai.core.model.baichuan;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import reactor.core.publisher.Flux;
+
+/**
+ * 百川 {@link ChatModel} 实现类
+ *
+ * @author Iailab
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class BaiChuanChatModel implements ChatModel {
+
+    public static final String BASE_URL = "https://api.baichuan-ai.com";
+
+    public static final String MODEL_DEFAULT = "Baichuan4-Turbo";
+
+    /**
+     * 兼容 OpenAI 接口,进行复用
+     */
+    private final OpenAiChatModel openAiChatModel;
+
+    @Override
+    public ChatResponse call(Prompt prompt) {
+        return openAiChatModel.call(prompt);
+    }
+
+    @Override
+    public Flux<ChatResponse> stream(Prompt prompt) {
+        return openAiChatModel.stream(prompt);
+    }
+
+    @Override
+    public ChatOptions getDefaultOptions() {
+        return openAiChatModel.getDefaultOptions();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/deepseek/DeepSeekChatModel.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/deepseek/DeepSeekChatModel.java
new file mode 100644
index 0000000..a3573ac
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/deepseek/DeepSeekChatModel.java
@@ -0,0 +1,45 @@
+package com.iailab.framework.ai.core.model.deepseek;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import reactor.core.publisher.Flux;
+
+/**
+ * DeepSeek {@link ChatModel} 实现类
+ *
+ * @author fansili
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class DeepSeekChatModel implements ChatModel {
+
+    public static final String BASE_URL = "https://api.deepseek.com";
+
+    public static final String MODEL_DEFAULT = "deepseek-chat";
+
+    /**
+     * 兼容 OpenAI 接口,进行复用
+     */
+    private final OpenAiChatModel openAiChatModel;
+
+    @Override
+    public ChatResponse call(Prompt prompt) {
+        return openAiChatModel.call(prompt);
+    }
+
+    @Override
+    public Flux<ChatResponse> stream(Prompt prompt) {
+        return openAiChatModel.stream(prompt);
+    }
+
+    @Override
+    public ChatOptions getDefaultOptions() {
+        return openAiChatModel.getDefaultOptions();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/doubao/DouBaoChatModel.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/doubao/DouBaoChatModel.java
new file mode 100644
index 0000000..25d1014
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/doubao/DouBaoChatModel.java
@@ -0,0 +1,45 @@
+package com.iailab.framework.ai.core.model.doubao;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import reactor.core.publisher.Flux;
+
+/**
+ * 字节豆包 {@link ChatModel} 实现类
+ *
+ * @author fansili
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class DouBaoChatModel implements ChatModel {
+
+    public static final String BASE_URL = "https://ark.cn-beijing.volces.com/api";
+
+    public static final String MODEL_DEFAULT = "doubao-1-5-lite-32k-250115";
+
+    /**
+     * 兼容 OpenAI 接口,进行复用
+     */
+    private final OpenAiChatModel openAiChatModel;
+
+    @Override
+    public ChatResponse call(Prompt prompt) {
+        return openAiChatModel.call(prompt);
+    }
+
+    @Override
+    public Flux<ChatResponse> stream(Prompt prompt) {
+        return openAiChatModel.stream(prompt);
+    }
+
+    @Override
+    public ChatOptions getDefaultOptions() {
+        return openAiChatModel.getDefaultOptions();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/hunyuan/HunYuanChatModel.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/hunyuan/HunYuanChatModel.java
new file mode 100644
index 0000000..8674bf9
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/hunyuan/HunYuanChatModel.java
@@ -0,0 +1,52 @@
+package com.iailab.framework.ai.core.model.hunyuan;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import reactor.core.publisher.Flux;
+
+/**
+ * 腾云混元 {@link ChatModel} 实现类
+ *
+ * 1. 混元大模型:基于 <a href="https://cloud.tencent.com/document/product/1729/111007">知识引擎原子能力</a> 实现
+ * 2. 知识引擎原子能力:基于 <a href="https://cloud.tencent.com/document/product/1772/115969">知识引擎原子能力</a> 实现
+ *
+ * @author fansili
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class HunYuanChatModel implements ChatModel {
+
+    public static final String BASE_URL = "https://api.hunyuan.cloud.tencent.com";
+
+    public static final String MODEL_DEFAULT = "hunyuan-turbo";
+
+    public static final String DEEP_SEEK_BASE_URL = "https://api.lkeap.cloud.tencent.com";
+
+    public static final String DEEP_SEEK_MODEL_DEFAULT = "deepseek-v3";
+
+    /**
+     * 兼容 OpenAI 接口,进行复用
+     */
+    private final OpenAiChatModel openAiChatModel;
+
+    @Override
+    public ChatResponse call(Prompt prompt) {
+        return openAiChatModel.call(prompt);
+    }
+
+    @Override
+    public Flux<ChatResponse> stream(Prompt prompt) {
+        return openAiChatModel.stream(prompt);
+    }
+
+    @Override
+    public ChatOptions getDefaultOptions() {
+        return openAiChatModel.getDefaultOptions();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/midjourney/api/MidjourneyApi.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/midjourney/api/MidjourneyApi.java
new file mode 100644
index 0000000..c0ae6f4
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/midjourney/api/MidjourneyApi.java
@@ -0,0 +1,351 @@
+package com.iailab.framework.ai.core.model.midjourney.api;
+
+import cn.hutool.core.util.StrUtil;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.iailab.framework.common.util.json.JsonUtils;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.HttpStatusCode;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Midjourney API
+ *
+ * @author fansili
+ * @since 1.0
+ */
+@Slf4j
+public class MidjourneyApi {
+
+    private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
+
+    private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
+            reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
+                HttpRequest request = response.request();
+                log.error("[midjourney-api] 调用失败!请求方式:[{}],请求地址:[{}],请求参数:[{}],响应数据: [{}]",
+                        request.getMethod(), request.getURI(), reqParam, responseBody);
+                sink.error(new IllegalStateException("[midjourney-api] 调用失败!"));
+            });
+
+    private final WebClient webClient;
+
+    /**
+     * 回调地址
+     */
+    private final String notifyUrl;
+
+    public MidjourneyApi(String baseUrl, String apiKey, String notifyUrl) {
+        this.webClient = WebClient.builder()
+                .baseUrl(baseUrl)
+                .defaultHeaders(httpHeaders -> {
+                    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
+                    httpHeaders.setBearerAuth(apiKey);
+                })
+                .build();
+        this.notifyUrl = notifyUrl;
+    }
+
+    /**
+     * imagine - 根据提示词提交绘画任务
+     *
+     * @param request 请求
+     * @return 提交结果
+     */
+    public SubmitResponse imagine(ImagineRequest request) {
+        if (StrUtil.isEmpty(request.getNotifyHook())) {
+            request.setNotifyHook(notifyUrl);
+        }
+        String response = post("/submit/imagine", request);
+        return JsonUtils.parseObject(response, SubmitResponse.class);
+    }
+
+    /**
+     * action - 放大、缩小、U1、U2...
+     *
+     * @param request 请求
+     * @return 提交结果
+     */
+    public SubmitResponse action(ActionRequest request) {
+        if (StrUtil.isEmpty(request.getNotifyHook())) {
+            request.setNotifyHook(notifyUrl);
+        }
+        String response = post("/submit/action", request);
+        return JsonUtils.parseObject(response, SubmitResponse.class);
+    }
+
+    /**
+     * 批量查询 task 任务
+     *
+     * @param ids 任务编号数组
+     * @return task 任务
+     */
+    public List<Notify> getTaskList(Collection<String> ids) {
+        String res = post("/task/list-by-condition", ImmutableMap.of("ids", ids));
+        return JsonUtils.parseArray(res, Notify.class);
+    }
+
+    private String post(String uri, Object body) {
+        return webClient.post()
+                .uri(uri)
+                .body(Mono.just(JsonUtils.toJsonString(body)), String.class)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(body))
+                .bodyToMono(String.class)
+                .block();
+    }
+
+    // ========== record 结构 ==========
+
+    /**
+     * Imagine 请求(生成图片)
+     */
+    @Data
+    public static final class ImagineRequest {
+
+        /**
+         * 垫图(参考图) base64 数组
+         */
+        private List<String> base64Array;
+        /**
+         * 提示词
+         */
+        private String prompt;
+        /**
+         * 通知地址
+         */
+        private String notifyHook;
+        /**
+         * 自定义参数
+         */
+        private String state;
+
+        public ImagineRequest(List<String> base64Array, String prompt, String notifyHook, String state) {
+            this.base64Array = base64Array;
+            this.prompt = prompt;
+            this.notifyHook = notifyHook;
+            this.state = state;
+        }
+
+        public static String buildState(Integer width, Integer height, String version, String model) {
+            StringBuilder params = new StringBuilder();
+            //  --ar 来设置尺寸
+            params.append(String.format(" --ar %s:%s ", width, height));
+            // --niji 模型
+            if (ModelEnum.NIJI.getModel().equals(model)) {
+                params.append(String.format(" --niji %s ", version));
+            } else {
+                params.append(String.format(" --v %s ", version));
+            }
+            return params.toString();
+        }
+
+    }
+
+    /**
+     * Action 请求
+     */
+    @Data
+    public static final class ActionRequest {
+
+        private String customId;
+        private String taskId;
+        private String notifyHook;
+
+        public ActionRequest(String taskId, String customId, String notifyHook) {
+            this.customId = customId;
+            this.taskId = taskId;
+            this.notifyHook = notifyHook;
+        }
+
+    }
+
+    /**
+     * Submit 统一返回
+     *
+     * @param code 状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)
+     * @param description 描述
+     * @param properties 扩展字段
+     * @param result 任务ID
+     */
+    public record SubmitResponse(String code,
+                                 String description,
+                                 Map<String, Object> properties,
+                                 String result) {
+    }
+
+    /**
+     * 通知 request
+     *
+     * @param id job id
+     * @param action 任务类型 {@link TaskActionEnum}
+     * @param status 任务状态 {@link TaskStatusEnum}
+     * @param prompt 提示词
+     * @param promptEn 提示词-英文
+     * @param description 任务描述
+     * @param state 自定义参数
+     * @param submitTime 提交时间
+     * @param startTime 开始执行时间
+     * @param finishTime 结束时间
+     * @param imageUrl 图片url
+     * @param progress 任务进度
+     * @param failReason 失败原因
+     * @param buttons 任务完成后的可执行按钮
+     */
+    public record Notify(String id,
+                         String action,
+                         String status,
+
+                         String prompt,
+                         String promptEn,
+
+                         String description,
+                         String state,
+
+                         Long submitTime,
+                         Long startTime,
+                         Long finishTime,
+
+                         String imageUrl,
+                         String progress,
+                         String failReason,
+                         List<Button> buttons) {
+
+    }
+
+    /**
+     * button
+     *
+     * @param customId MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识
+     * @param emoji 图标 emoji
+     * @param label Make Variations 文本
+     * @param type 类型,系统内部使用
+     * @param style 样式: 2(Primary)、3(Green)
+     */
+    public record Button(String customId,
+                         String emoji,
+                         String label,
+                         String type,
+                         String style) {
+    }
+
+    // ============ enums ============
+
+    /**
+     * 模型枚举
+     */
+    @AllArgsConstructor
+    @Getter
+    public enum ModelEnum {
+
+        MIDJOURNEY("midjourney", "midjourney"),
+        NIJI("niji", "niji"),
+        ;
+
+        private final String model;
+        private final String name;
+
+    }
+
+    /**
+     * 提交返回的状态码的枚举
+     */
+    @Getter
+    @AllArgsConstructor
+    public enum SubmitCodeEnum {
+
+        SUBMIT_SUCCESS("1", "提交成功"),
+        ALREADY_EXISTS("21", "已存在"),
+        QUEUING("22", "排队中"),
+        ;
+
+        public static final List<String> SUCCESS_CODES = Lists.newArrayList(
+                SUBMIT_SUCCESS.code,
+                ALREADY_EXISTS.code,
+                QUEUING.code
+        );
+
+        private final String code;
+        private final String name;
+
+    }
+
+    /**
+     * Action 枚举
+     */
+    @Getter
+    @AllArgsConstructor
+    public enum TaskActionEnum {
+
+        /**
+         * 生成图片
+         */
+        IMAGINE,
+        /**
+         * 选中放大
+         */
+        UPSCALE,
+        /**
+         * 选中其中的一张图,生成四张相似的
+         */
+        VARIATION,
+        /**
+         * 重新执行
+         */
+        REROLL,
+        /**
+         * 图转 prompt
+         */
+        DESCRIBE,
+        /**
+         * 多图混合
+         */
+        BLEND
+
+    }
+
+    /**
+     * 任务状态枚举
+     */
+    @Getter
+    @AllArgsConstructor
+    public enum TaskStatusEnum {
+
+        /**
+         * 未启动
+         */
+        NOT_START(0),
+        /**
+         * 已提交
+         */
+        SUBMITTED(1),
+        /**
+         * 执行中
+         */
+        IN_PROGRESS(3),
+        /**
+         * 失败
+         */
+        FAILURE(4),
+        /**
+         * 成功
+         */
+        SUCCESS(4);
+
+        private final int order;
+
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java
new file mode 100644
index 0000000..88590d7
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.iailab.framework.ai.core.model.siliconflow;
+
+/**
+ * SiliconFlow API 枚举类
+ *
+ * @author zzt
+ */
+public final class SiliconFlowApiConstants {
+
+	public static final String DEFAULT_BASE_URL = "https://api.siliconflow.cn";
+
+	public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B";
+
+    public static final String DEFAULT_IMAGE_MODEL = "Kwai-Kolors/Kolors";
+
+	public static final String PROVIDER_NAME = "Siiconflow";
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java
new file mode 100644
index 0000000..648e840
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java
@@ -0,0 +1,43 @@
+package com.iailab.framework.ai.core.model.siliconflow;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import reactor.core.publisher.Flux;
+
+/**
+ * 硅基流动 {@link ChatModel} 实现类
+ *
+ * 1. API 文档:<a href="https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions">API 文档</a>
+ *
+ * @author fansili
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class SiliconFlowChatModel implements ChatModel {
+
+    /**
+     * 兼容 OpenAI 接口,进行复用
+     */
+    private final OpenAiChatModel openAiChatModel;
+
+    @Override
+    public ChatResponse call(Prompt prompt) {
+        return openAiChatModel.call(prompt);
+    }
+
+    @Override
+    public Flux<ChatResponse> stream(Prompt prompt) {
+        return openAiChatModel.stream(prompt);
+    }
+
+    @Override
+    public ChatOptions getDefaultOptions() {
+        return openAiChatModel.getDefaultOptions();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java
new file mode 100644
index 0000000..e61e188
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2023-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.iailab.framework.ai.core.model.siliconflow;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.springframework.ai.model.ApiKey;
+import org.springframework.ai.model.NoopApiKey;
+import org.springframework.ai.model.SimpleApiKey;
+import org.springframework.ai.openai.api.OpenAiImageApi;
+import org.springframework.ai.retry.RetryUtils;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.ResponseErrorHandler;
+import org.springframework.web.client.RestClient;
+
+import java.util.Map;
+
+/**
+ * 硅基流动 Image API
+ *
+ * @see <a href= "https://docs.siliconflow.cn/cn/api-reference/images/images-generations">Images</a>
+ *
+ * @author zzt
+ */
+public class SiliconFlowImageApi {
+
+	private final RestClient restClient;
+
+	public SiliconFlowImageApi(String aiToken) {
+		this(SiliconFlowApiConstants.DEFAULT_BASE_URL, aiToken, RestClient.builder());
+	}
+
+    public SiliconFlowImageApi(String baseUrl, String openAiToken) {
+        this(baseUrl, openAiToken, RestClient.builder());
+    }
+
+	public SiliconFlowImageApi(String baseUrl, String openAiToken, RestClient.Builder restClientBuilder) {
+		this(baseUrl, openAiToken, restClientBuilder, RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);
+	}
+
+	public SiliconFlowImageApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder,
+                               ResponseErrorHandler responseErrorHandler) {
+		this(baseUrl, apiKey, CollectionUtils.toMultiValueMap(Map.of()), restClientBuilder, responseErrorHandler);
+	}
+
+	public SiliconFlowImageApi(String baseUrl, String apiKey, MultiValueMap<String, String> headers,
+                               RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
+		this(baseUrl, new SimpleApiKey(apiKey), headers, restClientBuilder, responseErrorHandler);
+	}
+
+	public SiliconFlowImageApi(String baseUrl, ApiKey apiKey, MultiValueMap<String, String> headers,
+                               RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
+
+		// @formatter:off
+		this.restClient = restClientBuilder.baseUrl(baseUrl)
+			.defaultHeaders(h -> {
+				if(!(apiKey instanceof NoopApiKey)) {
+					h.setBearerAuth(apiKey.getValue());
+				}
+				h.setContentType(MediaType.APPLICATION_JSON);
+				h.addAll(headers);
+			})
+			.defaultStatusHandler(responseErrorHandler)
+			.build();
+		// @formatter:on
+	}
+
+	public ResponseEntity<OpenAiImageApi.OpenAiImageResponse> createImage(SiliconflowImageRequest siliconflowImageRequest) {
+		Assert.notNull(siliconflowImageRequest, "Image request cannot be null.");
+		Assert.hasLength(siliconflowImageRequest.prompt(), "Prompt cannot be empty.");
+
+		return this.restClient.post()
+			.uri("v1/images/generations")
+			.body(siliconflowImageRequest)
+			.retrieve()
+			.toEntity(OpenAiImageApi.OpenAiImageResponse.class);
+	}
+
+
+	// @formatter:off
+	@JsonInclude(JsonInclude.Include.NON_NULL)
+	public record SiliconflowImageRequest (
+			@JsonProperty("prompt") String prompt,
+			@JsonProperty("model") String model,
+			@JsonProperty("batch_size") Integer batchSize,
+			@JsonProperty("negative_prompt") String negativePrompt,
+			@JsonProperty("seed") Integer seed,
+			@JsonProperty("num_inference_steps") Integer numInferenceSteps,
+			@JsonProperty("guidance_scale") Float guidanceScale,
+			@JsonProperty("image") String image) {
+
+		public SiliconflowImageRequest(String prompt, String model) {
+			this(prompt, model, null, null, null, null, null, null);
+		}
+	}
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java
new file mode 100644
index 0000000..ff6ef89
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2023-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.iailab.framework.ai.core.model.siliconflow;
+
+import io.micrometer.observation.ObservationRegistry;
+import lombok.Setter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ai.image.*;
+import org.springframework.ai.image.observation.DefaultImageModelObservationConvention;
+import org.springframework.ai.image.observation.ImageModelObservationContext;
+import org.springframework.ai.image.observation.ImageModelObservationConvention;
+import org.springframework.ai.image.observation.ImageModelObservationDocumentation;
+import org.springframework.ai.model.ModelOptionsUtils;
+import org.springframework.ai.openai.OpenAiImageModel;
+import org.springframework.ai.openai.api.OpenAiImageApi;
+import org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata;
+import org.springframework.ai.retry.RetryUtils;
+import org.springframework.http.ResponseEntity;
+import org.springframework.lang.Nullable;
+import org.springframework.retry.support.RetryTemplate;
+import org.springframework.util.Assert;
+
+import java.util.List;
+
+/**
+ * 硅基流动 {@link ImageModel} 实现类
+ *
+ * 参考 {@link OpenAiImageModel} 实现
+ *
+ * @author zzt
+ */
+public class SiliconFlowImageModel implements ImageModel {
+
+	private static final Logger logger = LoggerFactory.getLogger(SiliconFlowImageModel.class);
+
+	private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention();
+
+	private final SiliconFlowImageOptions defaultOptions;
+
+	private final RetryTemplate retryTemplate;
+
+	private final SiliconFlowImageApi siliconFlowImageApi;
+
+	private final ObservationRegistry observationRegistry;
+
+    @Setter
+	private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION;
+
+	public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi) {
+		this(siliconFlowImageApi, SiliconFlowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE);
+	}
+
+	public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate) {
+		this(siliconFlowImageApi, options, retryTemplate, ObservationRegistry.NOOP);
+	}
+
+	public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate,
+                                 ObservationRegistry observationRegistry) {
+		Assert.notNull(siliconFlowImageApi, "OpenAiImageApi must not be null");
+		Assert.notNull(options, "options must not be null");
+		Assert.notNull(retryTemplate, "retryTemplate must not be null");
+		Assert.notNull(observationRegistry, "observationRegistry must not be null");
+		this.siliconFlowImageApi = siliconFlowImageApi;
+		this.defaultOptions = options;
+		this.retryTemplate = retryTemplate;
+		this.observationRegistry = observationRegistry;
+	}
+
+	@Override
+	public ImageResponse call(ImagePrompt imagePrompt) {
+        SiliconFlowImageOptions requestImageOptions = mergeOptions(imagePrompt.getOptions(), this.defaultOptions);
+        SiliconFlowImageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt, requestImageOptions);
+
+		var observationContext = ImageModelObservationContext.builder()
+			.imagePrompt(imagePrompt)
+			.provider(SiliconFlowApiConstants.PROVIDER_NAME)
+			.requestOptions(imagePrompt.getOptions())
+			.build();
+
+		return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION
+			.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
+					this.observationRegistry)
+			.observe(() -> {
+				ResponseEntity<OpenAiImageApi.OpenAiImageResponse> imageResponseEntity = this.retryTemplate
+					.execute(ctx -> this.siliconFlowImageApi.createImage(imageRequest));
+
+				ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest);
+
+				observationContext.setResponse(imageResponse);
+
+				return imageResponse;
+			});
+	}
+
+	private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt,
+                                                                      SiliconFlowImageOptions requestImageOptions) {
+		String instructions = imagePrompt.getInstructions().get(0).getText();
+
+		SiliconFlowImageApi.SiliconflowImageRequest imageRequest = new SiliconFlowImageApi.SiliconflowImageRequest(instructions,
+                SiliconFlowApiConstants.DEFAULT_IMAGE_MODEL);
+
+		return ModelOptionsUtils.merge(requestImageOptions, imageRequest, SiliconFlowImageApi.SiliconflowImageRequest.class);
+	}
+
+	private ImageResponse convertResponse(ResponseEntity<OpenAiImageApi.OpenAiImageResponse> imageResponseEntity,
+										  SiliconFlowImageApi.SiliconflowImageRequest siliconflowImageRequest) {
+		OpenAiImageApi.OpenAiImageResponse imageApiResponse = imageResponseEntity.getBody();
+		if (imageApiResponse == null) {
+			logger.warn("No image response returned for request: {}", siliconflowImageRequest);
+			return new ImageResponse(List.of());
+		}
+
+		List<ImageGeneration> imageGenerationList = imageApiResponse.data()
+			.stream()
+			.map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()),
+					new OpenAiImageGenerationMetadata(entry.revisedPrompt())))
+			.toList();
+
+		ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created());
+		return new ImageResponse(imageGenerationList, openAiImageResponseMetadata);
+	}
+
+    private SiliconFlowImageOptions mergeOptions(@Nullable ImageOptions runtimeOptions, SiliconFlowImageOptions defaultOptions) {
+        var runtimeOptionsForProvider = ModelOptionsUtils.copyToTarget(runtimeOptions, ImageOptions.class,
+                SiliconFlowImageOptions.class);
+
+        if (runtimeOptionsForProvider == null) {
+            return defaultOptions;
+        }
+
+        return SiliconFlowImageOptions.builder()
+                // Handle portable image options
+                .model(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel()))
+                .batchSize(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN()))
+                .width(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth()))
+                .height(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight()))
+                // Handle SiliconFlow specific image options
+                .negativePrompt(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNegativePrompt(), defaultOptions.getNegativePrompt()))
+                .numInferenceSteps(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNumInferenceSteps(), defaultOptions.getNumInferenceSteps()))
+                .guidanceScale(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getGuidanceScale(), defaultOptions.getGuidanceScale()))
+                .seed(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getSeed(), defaultOptions.getSeed()))
+                .build();
+    }
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java
new file mode 100644
index 0000000..153eeea
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java
@@ -0,0 +1,105 @@
+package com.iailab.framework.ai.core.model.siliconflow;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.ai.image.ImageOptions;
+
+/**
+ * 硅基流动 {@link ImageOptions}
+ *
+ * @author zzt
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class SiliconFlowImageOptions implements ImageOptions {
+
+    @JsonProperty("model")
+    private String model;
+
+    @JsonProperty("negative_prompt")
+    private String negativePrompt;
+
+    /**
+     * The number of images to generate. Must be between 1 and 4.
+     */
+    @JsonProperty("image_size")
+    private String imageSize;
+
+    /**
+     * The number of images to generate. Must be between 1 and 4.
+     */
+    @JsonProperty("batch_size")
+    private Integer batchSize = 1;
+
+    /**
+     * number of inference steps
+     */
+    @JsonProperty("num_inference_steps")
+    private Integer numInferenceSteps = 25;
+
+    /**
+     * This value is used to control the degree of match between the generated image and the given prompt. The higher the value, the more the generated image will tend to strictly match the text prompt. The lower the value, the more creative and diverse the generated image will be, potentially containing more unexpected elements.
+     *
+     * Required range: 0 <= x <= 20
+     */
+    @JsonProperty("guidance_scale")
+    private Float guidanceScale = 0.75F;
+
+    /**
+     * 如果想要每次都生成固定的图片,可以把 seed 设置为固定值
+     *
+     */
+    @JsonProperty("seed")
+    private Integer seed =  (int)(Math.random() * 1_000_000_000);
+
+    /**
+     * The image that needs to be uploaded should be converted into base64 format.
+     */
+    @JsonProperty("image")
+    private String image;
+
+    /**
+     * 宽
+     */
+    private Integer width;
+
+    /**
+     * 高
+     */
+    private Integer height;
+
+    public void setHeight(Integer height) {
+        this.height = height;
+        if (this.width != null && this.height != null) {
+            this.imageSize = this.width + "x" + this.height;
+        }
+    }
+
+    public void setWidth(Integer width) {
+        this.width = width;
+        if (this.width != null && this.height != null) {
+            this.imageSize = this.width + "x" + this.height;
+        }
+    }
+
+    @Override
+    public Integer getN() {
+        return batchSize;
+    }
+
+    @Override
+    public String getResponseFormat() {
+        return "url";
+    }
+
+    @Override
+    public String getStyle() {
+        return null;
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/suno/api/SunoApi.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/suno/api/SunoApi.java
new file mode 100644
index 0000000..d543e64
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/suno/api/SunoApi.java
@@ -0,0 +1,200 @@
+package com.iailab.framework.ai.core.model.suno.api;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.StrPool;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.HttpStatusCode;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Suno API
+ * <p>
+ * 对接 Suno Proxy:<a href="https://github.com/gcui-art/suno-api">suno-api</a>
+ *
+ * @author xiaoxin
+ */
+@Slf4j
+public class SunoApi {
+
+    private final WebClient webClient;
+
+    private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
+
+    private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
+            reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
+                HttpRequest request = response.request();
+                log.error("[suno-api] 调用失败!请求方式:[{}],请求地址:[{}],请求参数:[{}],响应数据: [{}]",
+                        request.getMethod(), request.getURI(), reqParam, responseBody);
+                sink.error(new IllegalStateException("[suno-api] 调用失败!"));
+            });
+
+    public SunoApi(String baseUrl) {
+        this.webClient = WebClient.builder()
+                .baseUrl(baseUrl)
+                .defaultHeaders((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
+                .build();
+    }
+
+    public List<MusicData> generate(MusicGenerateRequest request) {
+        return this.webClient.post()
+                .uri("/api/generate")
+                .body(Mono.just(request), MusicGenerateRequest.class)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
+                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
+                })
+                .block();
+    }
+
+    public List<MusicData> customGenerate(MusicGenerateRequest request) {
+        return this.webClient.post()
+                .uri("/api/custom_generate")
+                .body(Mono.just(request), MusicGenerateRequest.class)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
+                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
+                })
+                .block();
+    }
+
+    public LyricsData generateLyrics(String prompt) {
+        return this.webClient.post()
+                .uri("/api/generate_lyrics")
+                .body(Mono.just(new MusicGenerateRequest(prompt)), MusicGenerateRequest.class)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(prompt))
+                .bodyToMono(LyricsData.class)
+                .block();
+    }
+
+    public List<MusicData> getMusicList(List<String> ids) {
+        return this.webClient.get()
+                .uri(uriBuilder -> uriBuilder
+                        .path("/api/get")
+                        .queryParam("ids", CollUtil.join(ids, StrPool.COMMA))
+                        .build())
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(ids))
+                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
+                })
+                .block();
+    }
+
+    public LimitUsageData getLimitUsage() {
+        return this.webClient.get()
+                .uri("/api/get_limit")
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(null))
+                .bodyToMono(LimitUsageData.class)
+                .block();
+    }
+
+    /**
+     * 根据提示生成音频
+     *
+     * @param prompt           用于生成音乐音频的提示
+     * @param tags             音乐风格
+     * @param title            音乐名称
+     * @param model            模型
+     * @param waitAudio        false 表示后台模式,仅返回音频任务信息,需要调用 get API 获取详细的音频信息。
+     *                         true 表示同步模式,API 最多等待 100s,音频生成完毕后直接返回音频链接等信息,建议在 GPT 等 agent 中使用。
+     * @param makeInstrumental 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record MusicGenerateRequest(
+            String prompt,
+            String tags,
+            String title,
+            String model,
+            @JsonProperty("wait_audio") boolean waitAudio,
+            @JsonProperty("make_instrumental") boolean makeInstrumental
+    ) {
+
+        public MusicGenerateRequest(String prompt) {
+            this(prompt, null, null, null, false, false);
+        }
+
+        public MusicGenerateRequest(String prompt, String model, boolean makeInstrumental) {
+            this(prompt, null, null, model, false, makeInstrumental);
+        }
+
+        public MusicGenerateRequest(String prompt, String model, String tags, String title) {
+            this(prompt, tags, title, model, false, false);
+        }
+
+    }
+
+    /**
+     * Suno API 响应的音频数据
+     *
+     * @param id                   音乐数据的 ID
+     * @param title                音乐音频的标题
+     * @param imageUrl             音乐音频的图片 URL
+     * @param lyric                音乐音频的歌词
+     * @param audioUrl             音乐音频的 URL
+     * @param videoUrl             音乐视频的 URL
+     * @param createdAt            音乐音频的创建时间
+     * @param modelName            模型名称
+     * @param status               submitted、queued、streaming、complete
+     * @param gptDescriptionPrompt 描述词
+     * @param prompt               生成音乐音频的提示
+     * @param type                 操作类型
+     * @param tags                 音乐类型标签
+     * @param duration             音乐时长
+     */
+    public record MusicData(
+            String id,
+            String title,
+            @JsonProperty("image_url") String imageUrl,
+            String lyric,
+            @JsonProperty("audio_url") String audioUrl,
+            @JsonProperty("video_url") String videoUrl,
+            @JsonProperty("created_at") String createdAt,
+            @JsonProperty("model_name") String modelName,
+            String status,
+            @JsonProperty("gpt_description_prompt") String gptDescriptionPrompt,
+            @JsonProperty("error_message") String errorMessage,
+            String prompt,
+            String type,
+            String tags,
+            Double duration
+    ) {
+    }
+
+    /**
+     * Suno API 响应的歌词数据。
+     *
+     * @param text   歌词
+     * @param title  标题
+     * @param status 状态
+     */
+    public record LyricsData(
+            String text,
+            String title,
+            String status
+    ) {
+    }
+
+    /**
+     * Suno API 响应的限额数据,目前每日免费 50
+     */
+    public record LimitUsageData(
+            @JsonProperty("credits_left") Long creditsLeft,
+            String period,
+            @JsonProperty("monthly_limit") Long monthlyLimit,
+            @JsonProperty("monthly_usage") Long monthlyUsage
+    ) {
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/wenduoduo/api/WenDuoDuoPptApi.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/wenduoduo/api/WenDuoDuoPptApi.java
new file mode 100644
index 0000000..7be1148
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/wenduoduo/api/WenDuoDuoPptApi.java
@@ -0,0 +1,381 @@
+package com.iailab.framework.ai.core.model.wenduoduo.api;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.iailab.framework.common.util.json.JsonUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.HttpStatusCode;
+import org.springframework.http.MediaType;
+import org.springframework.util.Assert;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * 文多多 API
+ *
+ * @author xiaoxin
+ * @see <a href="https://docmee.cn/open-platform/api">PPT 生成 API</a>
+ */
+@Slf4j
+public class WenDuoDuoPptApi {
+
+    public static final String BASE_URL = "https://docmee.cn";
+    public static final String TOKEN_NAME = "token";
+
+    private final WebClient webClient;
+
+    private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
+
+    private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
+            reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
+                HttpRequest request = response.request();
+                log.error("[WenDuoDuoPptApi] 调用失败!请求方式:[{}],请求地址:[{}],请求参数:[{}],响应数据: [{}]",
+                        request.getMethod(), request.getURI(), reqParam, responseBody);
+                sink.error(new IllegalStateException("[WenDuoDuoPptApi] 调用失败!"));
+            });
+
+    public WenDuoDuoPptApi(String token) {
+        Assert.hasText(token, "token 不能为空");
+        this.webClient = WebClient.builder()
+                .baseUrl(BASE_URL)
+                .defaultHeaders((headers) -> {
+                    headers.setContentType(MediaType.APPLICATION_JSON);
+                    headers.add(TOKEN_NAME, token);
+                })
+                .build();
+    }
+
+    /**
+     * 创建 token
+     *
+     * @param request 请求信息
+     * @return token
+     */
+    public String createApiToken(CreateTokenRequest request) {
+        return this.webClient.post()
+                .uri("/api/user/createApiToken")
+                .header("Api-Key", request.apiKey)
+                .body(Mono.just(request), CreateTokenRequest.class)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
+                .bodyToMono(ApiResponse.class)
+                .<String>handle((response, sink) -> {
+                    if (response.code != 0) {
+                        sink.error(new IllegalStateException("创建 token 异常," + response.message));
+                        return;
+                    }
+                    sink.next(response.data.get("token").toString());
+                })
+                .block();
+    }
+
+    /**
+     * 创建任务
+     *
+     * @param type    类型
+     * @param content 内容
+     * @param files   文件列表
+     * @return 任务 ID
+     * @see <a href="https://docmee.cn/open-platform/api#%E5%88%9B%E5%BB%BA%E4%BB%BB%E5%8A%A1">创建任务</a>
+     */
+    public ApiResponse createTask(Integer type, String content, List<MultipartFile> files) {
+        MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
+        formData.add("type", type);
+        if (content != null) {
+            formData.add("content", content);
+        }
+        if (files != null) {
+            for (MultipartFile file : files) {
+                formData.add("file", file.getResource());
+            }
+        }
+        return this.webClient.post()
+                .uri("/api/ppt/v2/createTask")
+                .contentType(MediaType.MULTIPART_FORM_DATA)
+                .body(BodyInserters.fromMultipartData(formData))
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(formData))
+                .bodyToMono(ApiResponse.class)
+                .block();
+    }
+
+    /**
+     * 获取生成选项
+     *
+     * @param lang 语种
+     * @return 生成选项
+     */
+    public Map<String, Object> getOptions(String lang) {
+        String uri = "/api/ppt/v2/options";
+        if (lang != null) {
+            uri += "?lang=" + lang;
+        }
+        return this.webClient.get()
+                .uri(uri)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(lang))
+                .bodyToMono(new ParameterizedTypeReference<ApiResponse>() {
+                })
+                .<Map<String, Object>>handle((response, sink) -> {
+                    if (response.code != 0) {
+                        sink.error(new IllegalStateException("获取生成选项异常," + response.message));
+                        return;
+                    }
+                    sink.next(response.data);
+                })
+                .block();
+    }
+
+    /**
+     * 分页查询 PPT 模板
+     *
+     * @param token   令牌
+     * @param request 请求体
+     * @return 模板列表
+     */
+    public PagePptTemplateInfo getTemplatePage(TemplateQueryRequest request) {
+        return this.webClient.post()
+                .uri("/api/ppt/templates")
+                .bodyValue(request)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
+                .bodyToMono(new ParameterizedTypeReference<PagePptTemplateInfo>() {
+                })
+                .block();
+    }
+
+    /**
+     * 生成大纲内容
+     *
+     * @return 大纲内容流
+     */
+    public Flux<Map<String, Object>> createOutline(CreateOutlineRequest request) {
+        return this.webClient.post()
+                .uri("/api/ppt/v2/generateContent")
+                .body(Mono.just(request), CreateOutlineRequest.class)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
+                .bodyToFlux(new ParameterizedTypeReference<>() {
+                });
+    }
+
+    /**
+     * 修改大纲内容
+     *
+     * @param request 请求体
+     * @return 大纲内容流
+     */
+    public Flux<Map<String, Object>> updateOutline(UpdateOutlineRequest request) {
+        return this.webClient.post()
+                .uri("/api/ppt/v2/updateContent")
+                .body(Mono.just(request), UpdateOutlineRequest.class)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
+                .bodyToFlux(new ParameterizedTypeReference<>() {
+                });
+    }
+
+    /**
+     * 生成 PPT
+     *
+     * @return PPT信息
+     */
+    public PptInfo create(PptCreateRequest request) {
+        return this.webClient.post()
+                .uri("/api/ppt/v2/generatePptx")
+                .body(Mono.just(request), PptCreateRequest.class)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
+                .bodyToMono(ApiResponse.class)
+                .<PptInfo>handle((response, sink) -> {
+                    if (response.code != 0) {
+                        sink.error(new IllegalStateException("生成 PPT 异常," + response.message));
+                        return;
+                    }
+                    sink.next(Objects.requireNonNull(JsonUtils.parseObject(JsonUtils.toJsonString(response.data.get("pptInfo")), PptInfo.class)));
+                })
+                .block();
+    }
+
+    /**
+     * 创建 Token 请求参数
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record CreateTokenRequest(
+            String apiKey,
+            String uid,
+            Integer limit
+    ) {
+
+        public CreateTokenRequest(String apiKey) {
+            this(apiKey, null, null);
+        }
+
+    }
+
+    /**
+     * API 通用响应
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record ApiResponse(
+            Integer code,
+            String message,
+            Map<String, Object> data
+    ) {
+    }
+
+    /**
+     * 创建任务
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record CreateTaskRequest(
+            Integer type,
+            String content,
+            List<MultipartFile> files
+    ) {
+    }
+
+    /**
+     * 生成大纲内容请求
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record CreateOutlineRequest(
+            String id,
+            String length,
+            String scene,
+            String audience,
+            String lang,
+            String prompt
+    ) {
+    }
+
+    /**
+     * 修改大纲内容请求
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record UpdateOutlineRequest(
+            String id,
+            String markdown,
+            String question
+    ) {
+    }
+
+    /**
+     * 生成 PPT 请求参数
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record PptCreateRequest(
+            String id,
+            String templateId,
+            String markdown
+    ) {
+    }
+
+    /**
+     * PPT 信息
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record PptInfo(
+            String id,
+            String name,
+            String subject,
+            String coverUrl,
+            String fileUrl,
+            String templateId,
+            String pptxProperty,
+            String userId,
+            String userName,
+            int companyId,
+            @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+            LocalDateTime updateTime,
+            @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+            LocalDateTime createTime,
+            String createUser,
+            String updateUser
+    ) {
+    }
+
+    /**
+     * 模板查询请求参数
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record TemplateQueryRequest(
+            int page,
+            int size,
+            Filter filters
+    ) {
+
+        /**
+         * 模板查询过滤条件
+         */
+        @JsonInclude(value = JsonInclude.Include.NON_NULL)
+        public record Filter(
+                int type,
+                String category,
+                String style,
+                String themeColor
+        ) {
+        }
+
+    }
+
+    /**
+     * PPT模板分页信息
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record PagePptTemplateInfo(
+            List<PptTemplateInfo> data,
+            String total
+    ) {
+    }
+
+    /**
+     * PPT模板信息
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record PptTemplateInfo(
+            String id,
+            int type,
+            Integer subType,
+            String layout,
+            String category,
+            String style,
+            String themeColor,
+            String lang,
+            boolean animation,
+            String subject,
+            String coverUrl,
+            String fileUrl,
+            List<String> pageCoverUrls,
+            String pptxProperty,
+            int sort,
+            int num,
+            Integer imgNum,
+            int isDeleted,
+            String userId,
+            int companyId,
+            @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+            LocalDateTime updateTime,
+            @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+            LocalDateTime createTime,
+            String createUser,
+            String updateUser
+    ) {
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/xinghuo/XingHuoChatModel.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/xinghuo/XingHuoChatModel.java
new file mode 100644
index 0000000..e7147fc
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/xinghuo/XingHuoChatModel.java
@@ -0,0 +1,45 @@
+package com.iailab.framework.ai.core.model.xinghuo;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import reactor.core.publisher.Flux;
+
+/**
+ * 讯飞星火 {@link ChatModel} 实现类
+ *
+ * @author fansili
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class XingHuoChatModel implements ChatModel {
+
+    public static final String BASE_URL = "https://spark-api-open.xf-yun.com";
+
+    public static final String MODEL_DEFAULT = "generalv3.5";
+
+    /**
+     * 兼容 OpenAI 接口,进行复用
+     */
+    private final OpenAiChatModel openAiChatModel;
+
+    @Override
+    public ChatResponse call(Prompt prompt) {
+        return openAiChatModel.call(prompt);
+    }
+
+    @Override
+    public Flux<ChatResponse> stream(Prompt prompt) {
+        return openAiChatModel.stream(prompt);
+    }
+
+    @Override
+    public ChatOptions getDefaultOptions() {
+        return openAiChatModel.getDefaultOptions();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/xinghuo/api/XunFeiPptApi.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/xinghuo/api/XunFeiPptApi.java
new file mode 100644
index 0000000..6b0c18b
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/model/xinghuo/api/XunFeiPptApi.java
@@ -0,0 +1,522 @@
+package com.iailab.framework.ai.core.model.xinghuo.api;
+
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.digest.HmacAlgorithm;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.iailab.framework.common.util.json.JsonUtils;
+import lombok.Builder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.http.HttpStatusCode;
+import org.springframework.http.MediaType;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * 讯飞智能 PPT 生成 API
+ *
+ * @author xiaoxin
+ * @see <a href="https://www.xfyun.cn/doc/spark/PPTv2.html">智能 PPT 生成 API</a>
+ */
+@Slf4j
+public class XunFeiPptApi {
+
+    public static final String BASE_URL = "https://zwapi.xfyun.cn/api/ppt/v2";
+    private static final String HEADER_APP_ID = "appId";
+    private static final String HEADER_TIMESTAMP = "timestamp";
+    private static final String HEADER_SIGNATURE = "signature";
+
+    private final WebClient webClient;
+    private final String appId;
+    private final String apiSecret;
+
+    private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
+
+    private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
+            reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
+                log.error("[XunFeiPptApi] 调用失败!请求参数:[{}],响应数据: [{}]", reqParam, responseBody);
+                sink.error(new IllegalStateException("[XunFeiPptApi] 调用失败!"));
+            });
+
+    public XunFeiPptApi(String appId, String apiSecret) {
+        this.appId = appId;
+        this.apiSecret = apiSecret;
+        this.webClient = WebClient.builder()
+                .baseUrl(BASE_URL)
+                .defaultHeaders((headers) -> {
+                    headers.setContentType(MediaType.APPLICATION_JSON);
+                    headers.add(HEADER_APP_ID, appId);
+                })
+                .build();
+
+    }
+
+    /**
+     * 获取签名
+     *
+     * @return 签名信息
+     */
+    private SignatureInfo getSignature() {
+        long timestamp = System.currentTimeMillis() / 1000;
+        String ts = String.valueOf(timestamp);
+        String signature = generateSignature(appId, apiSecret, timestamp);
+        return new SignatureInfo(ts, signature);
+    }
+
+    /**
+     * 生成签名
+     *
+     * @param appId     应用ID
+     * @param apiSecret 应用密钥
+     * @param timestamp 时间戳(秒)
+     * @return 签名
+     */
+    private String generateSignature(String appId, String apiSecret, long timestamp) {
+        String auth = SecureUtil.md5(appId + timestamp);
+        return SecureUtil.hmac(HmacAlgorithm.HmacSHA1, apiSecret).digestBase64(auth, false);
+    }
+
+    /**
+     * 获取 PPT 模板列表
+     *
+     * @param style    风格,如"商务"
+     * @param pageSize 每页数量
+     * @return 模板列表
+     */
+    public TemplatePageResponse getTemplatePage(String style, Integer pageSize) {
+        SignatureInfo signInfo = getSignature();
+        Map<String, Object> requestBody = new HashMap<>();
+        requestBody.put("style", style);
+        requestBody.put("pageSize", ObjUtil.defaultIfNull(pageSize, 20));
+        return this.webClient.post()
+                .uri("/template/list")
+                .header(HEADER_TIMESTAMP, signInfo.timestamp)
+                .header(HEADER_SIGNATURE, signInfo.signature)
+                .contentType(MediaType.APPLICATION_JSON)
+                .bodyValue(requestBody)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(requestBody))
+                .bodyToMono(TemplatePageResponse.class)
+                .block();
+    }
+
+    /**
+     * 创建大纲(通过文本)
+     *
+     * @param query 查询文本
+     * @return 大纲创建响应
+     */
+    public CreateResponse createOutline(String query) {
+        SignatureInfo signInfo = getSignature();
+        MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
+        formData.add("query", query);
+        return this.webClient.post()
+                .uri("/createOutline")
+                .header(HEADER_TIMESTAMP, signInfo.timestamp)
+                .header(HEADER_SIGNATURE, signInfo.signature)
+                .contentType(MediaType.MULTIPART_FORM_DATA)
+                .body(BodyInserters.fromMultipartData(formData))
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(formData))
+                .bodyToMono(CreateResponse.class)
+                .block();
+    }
+
+
+    /**
+     * 直接创建 PPT(简化版 - 通过文本)
+     *
+     * @param query 查询文本
+     * @return 创建响应
+     */
+    public CreateResponse create(String query) {
+        CreatePptRequest request = CreatePptRequest.builder()
+                .query(query)
+                .build();
+        return create(request);
+    }
+
+    /**
+     * 直接创建 PPT(简化版 - 通过文件)
+     *
+     * @param file     文件
+     * @param fileName 文件名
+     * @return 创建响应
+     */
+    public CreateResponse create(MultipartFile file, String fileName) {
+        CreatePptRequest request = CreatePptRequest.builder()
+                .file(file).fileName(fileName).build();
+        return create(request);
+    }
+
+    /**
+     * 直接创建 PPT(完整版)
+     *
+     * @param request 请求参数
+     * @return 创建响应
+     */
+    public CreateResponse create(CreatePptRequest request) {
+        SignatureInfo signInfo = getSignature();
+        MultiValueMap<String, Object> formData = buildCreatePptFormData(request);
+        return this.webClient.post()
+                .uri("/create")
+                .header(HEADER_TIMESTAMP, signInfo.timestamp)
+                .header(HEADER_SIGNATURE, signInfo.signature)
+                .contentType(MediaType.MULTIPART_FORM_DATA)
+                .body(BodyInserters.fromMultipartData(formData))
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(formData))
+                .bodyToMono(CreateResponse.class)
+                .block();
+    }
+
+
+    /**
+     * 通过大纲创建 PPT(简化版)
+     *
+     * @param outline 大纲内容
+     * @param query   查询文本
+     * @return 创建响应
+     */
+    public CreateResponse createPptByOutline(OutlineData outline, String query) {
+        CreatePptByOutlineRequest request = CreatePptByOutlineRequest.builder()
+                .outline(outline)
+                .query(query)
+                .build();
+        return createPptByOutline(request);
+    }
+
+    /**
+     * 通过大纲创建 PPT(完整版)
+     *
+     * @param request 请求参数
+     * @return 创建响应
+     */
+    public CreateResponse createPptByOutline(CreatePptByOutlineRequest request) {
+        SignatureInfo signInfo = getSignature();
+        return this.webClient.post()
+                .uri("/createPptByOutline")
+                .header(HEADER_TIMESTAMP, signInfo.timestamp)
+                .header(HEADER_SIGNATURE, signInfo.signature)
+                .contentType(MediaType.APPLICATION_JSON)
+                .bodyValue(request)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
+                .bodyToMono(CreateResponse.class)
+                .block();
+    }
+
+    /**
+     * 检查 PPT 生成进度
+     *
+     * @param sid 任务 ID
+     * @return 进度响应
+     */
+    public ProgressResponse checkProgress(String sid) {
+        SignatureInfo signInfo = getSignature();
+        return this.webClient.get()
+                .uri(uriBuilder -> uriBuilder
+                        .path("/progress")
+                        .queryParam("sid", sid)
+                        .build())
+                .header(HEADER_TIMESTAMP, signInfo.timestamp)
+                .header(HEADER_SIGNATURE, signInfo.signature)
+                .retrieve()
+                .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(sid))
+                .bodyToMono(ProgressResponse.class)
+                .block();
+    }
+
+    /**
+     * 签名信息
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    private record SignatureInfo(
+            String timestamp,
+            String signature
+    ) {
+    }
+
+    /**
+     * 模板列表响应
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record TemplatePageResponse(
+            boolean flag,
+            int code,
+            String desc,
+            Integer count,
+            TemplatePageData data
+    ) {
+    }
+
+    /**
+     * 模板列表数据
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record TemplatePageData(
+            String total,
+            List<TemplateInfo> records,
+            Integer pageNum
+    ) {
+    }
+
+    /**
+     * 模板信息
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record TemplateInfo(
+            String templateIndexId,
+            Integer pageCount,
+            String type,
+            String color,
+            String industry,
+            String style,
+            String detailImage
+    ) {
+    }
+
+    /**
+     * 创建响应
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record CreateResponse(
+            boolean flag,
+            int code,
+            String desc,
+            Integer count,
+            CreateResponseData data
+    ) {
+    }
+
+    /**
+     * 创建响应数据
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record CreateResponseData(
+            String sid,
+            String coverImgSrc,
+            String title,
+            String subTitle,
+            OutlineData outline
+    ) {
+    }
+
+    /**
+     * 大纲数据结构
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record OutlineData(
+            String title,
+            String subTitle,
+            List<Chapter> chapters
+    ) {
+
+        /**
+         * 章节结构
+         */
+        @JsonInclude(value = JsonInclude.Include.NON_NULL)
+        public record Chapter(
+                String chapterTitle,
+                List<ChapterContent> chapterContents
+        ) {
+
+            /**
+             * 章节内容
+             */
+            @JsonInclude(value = JsonInclude.Include.NON_NULL)
+            public record ChapterContent(
+                    String chapterTitle
+            ) {
+            }
+
+        }
+
+        /**
+         * 将大纲对象转换为JSON字符串
+         *
+         * @return 大纲JSON字符串
+         */
+        public String toJsonString() {
+            return JsonUtils.toJsonString(this);
+        }
+    }
+
+    /**
+     * 进度响应
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record ProgressResponse(
+            int code,
+            String desc,
+            ProgressResponseData data
+    ) {
+    }
+
+    /**
+     * 进度响应数据
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    public record ProgressResponseData(
+            int process,
+            String pptId,
+            String pptUrl,
+            String pptStatus,
+            String aiImageStatus,
+            String cardNoteStatus,
+            String errMsg,
+            Integer totalPages,
+            Integer donePages
+    ) {
+
+        /**
+         * 是否全部完成
+         *
+         * @return 是否全部完成
+         */
+        public boolean isAllDone() {
+            return "done".equals(pptStatus)
+                    && ("done".equals(aiImageStatus) || aiImageStatus == null)
+                    && ("done".equals(cardNoteStatus) || cardNoteStatus == null);
+        }
+
+        /**
+         * 是否失败
+         *
+         * @return 是否失败
+         */
+        public boolean isFailed() {
+            return "build_failed".equals(pptStatus);
+        }
+
+        /**
+         * 获取进度百分比
+         *
+         * @return 进度百分比
+         */
+        public int getProgressPercent() {
+            if (totalPages == null || totalPages == 0 || donePages == null) {
+                return process; // 兼容旧版返回
+            }
+            return (int) (donePages * 100.0 / totalPages);
+        }
+
+    }
+
+    /**
+     * 通过大纲创建 PPT 请求参数
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    @Builder
+    public record CreatePptByOutlineRequest(
+            String query,                // 用户生成PPT要求(最多8000字)
+            String outlineSid,           // 已生成大纲后,响应返回的请求大纲唯一id
+            OutlineData outline,         // 大纲内容
+            String templateId,           // 模板ID
+            String businessId,           // 业务ID(非必传)
+            String author,               // PPT作者名
+            Boolean isCardNote,          // 是否生成PPT演讲备注
+            Boolean search,              // 是否联网搜索
+            String language,             // 语种
+            String fileUrl,              // 文件地址
+            String fileName,             // 文件名(带文件名后缀)
+            Boolean isFigure,            // 是否自动配图
+            String aiImage               // ai配图类型:normal、advanced
+    ) {
+    }
+
+
+    /**
+     * 构建创建 PPT 的表单数据
+     *
+     * @param request 请求参数
+     * @return 表单数据
+     */
+    private MultiValueMap<String, Object> buildCreatePptFormData(CreatePptRequest request) {
+        MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
+        if (request.file() != null) {
+            try {
+                formData.add("file", new ByteArrayResource(request.file().getBytes()) {
+                    @Override
+                    public String getFilename() {
+                        return request.file().getOriginalFilename();
+                    }
+                });
+            } catch (IOException e) {
+                log.error("[XunFeiPptApi] 文件处理失败", e);
+                throw new IllegalStateException("[XunFeiPptApi] 文件处理失败", e);
+            }
+        }
+        Map<String, Object> param = new HashMap<>();
+        addIfPresent(param, "query", request.query());
+        addIfPresent(param, "fileUrl", request.fileUrl());
+        addIfPresent(param, "fileName", request.fileName());
+        addIfPresent(param, "templateId", request.templateId());
+        addIfPresent(param, "businessId", request.businessId());
+        addIfPresent(param, "author", request.author());
+        addIfPresent(param, "isCardNote", request.isCardNote());
+        addIfPresent(param, "search", request.search());
+        addIfPresent(param, "language", request.language());
+        addIfPresent(param, "isFigure", request.isFigure());
+        addIfPresent(param, "aiImage", request.aiImage());
+        param.forEach(formData::add);
+        return formData;
+    }
+
+    public static <K, V> void addIfPresent(Map<K, V> map, K key, V value) {
+        if (ObjUtil.isNull(key) || ObjUtil.isNull(map)) {
+            return;
+        }
+
+        boolean isPresent = false;
+        if (ObjUtil.isNotNull(value)) {
+            if (value instanceof String) {
+                // 字符串:需要有实际内容
+                isPresent = StringUtils.hasText((String) value);
+            } else {
+                // 其他类型:非 null 即视为存在
+                isPresent = true;
+            }
+        }
+        if (isPresent) {
+            map.put(key, value);
+        }
+    }
+
+    /**
+     * 直接生成PPT请求参数
+     */
+    @JsonInclude(value = JsonInclude.Include.NON_NULL)
+    @Builder
+    public record CreatePptRequest(
+            String query,                // 用户生成PPT要求(最多8000字)
+            MultipartFile file,          // 上传文件
+            String fileUrl,              // 文件地址
+            String fileName,             // 文件名(带文件名后缀)
+            String templateId,           // 模板ID
+            String businessId,           // 业务ID(非必传)
+            String author,               // PPT作者名
+            Boolean isCardNote,          // 是否生成PPT演讲备注
+            Boolean search,              // 是否联网搜索
+            String language,             // 语种
+            Boolean isFigure,            // 是否自动配图
+            String aiImage               // ai配图类型:normal、advanced
+    ) {
+
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/util/AiUtils.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/util/AiUtils.java
new file mode 100644
index 0000000..c3b7eed
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/core/util/AiUtils.java
@@ -0,0 +1,87 @@
+package com.iailab.framework.ai.core.util;
+
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
+import com.iailab.framework.ai.core.enums.AiPlatformEnum;
+import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
+import org.springframework.ai.chat.messages.*;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.minimax.MiniMaxChatOptions;
+import org.springframework.ai.moonshot.MoonshotChatOptions;
+import org.springframework.ai.ollama.api.OllamaOptions;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.qianfan.QianFanChatOptions;
+import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Spring AI 工具类
+ *
+ * @author Iailab
+ */
+public class AiUtils {
+
+    public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) {
+        return buildChatOptions(platform, model, temperature, maxTokens, null);
+    }
+
+    public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens,
+                                               Set<String> toolNames) {
+        toolNames = ObjUtil.defaultIfNull(toolNames, Collections.emptySet());
+        // noinspection EnhancedSwitchMigration
+        switch (platform) {
+            case TONG_YI:
+                return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens)
+                        .withFunctions(toolNames).build();
+            case YI_YAN:
+                return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build();
+            case ZHI_PU:
+                return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
+                        .functions(toolNames).build();
+            case MINI_MAX:
+                return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
+                        .functions(toolNames).build();
+            case MOONSHOT:
+                return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
+                        .functions(toolNames).build();
+            case OPENAI:
+            case DEEP_SEEK: // 复用 OpenAI 客户端
+            case DOU_BAO: // 复用 OpenAI 客户端
+            case HUN_YUAN: // 复用 OpenAI 客户端
+            case XING_HUO: // 复用 OpenAI 客户端
+            case SILICON_FLOW: // 复用 OpenAI 客户端
+            case BAI_CHUAN: // 复用 OpenAI 客户端
+                return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
+                        .toolNames(toolNames).build();
+            case AZURE_OPENAI:
+                // TODO Iailab:貌似没 model 字段???!
+                return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens)
+                        .toolNames(toolNames).build();
+            case OLLAMA:
+                return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens)
+                        .toolNames(toolNames).build();
+            default:
+                throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
+        }
+    }
+
+    public static Message buildMessage(String type, String content) {
+        if (MessageType.USER.getValue().equals(type)) {
+            return new UserMessage(content);
+        }
+        if (MessageType.ASSISTANT.getValue().equals(type)) {
+            return new AssistantMessage(content);
+        }
+        if (MessageType.SYSTEM.getValue().equals(type)) {
+            return new SystemMessage(content);
+        }
+        if (MessageType.TOOL.getValue().equals(type)) {
+            throw new UnsupportedOperationException("暂不支持 tool 消息:" + content);
+        }
+        throw new IllegalArgumentException(StrUtil.format("未知消息类型({})", type));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/package-info.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/package-info.java
new file mode 100644
index 0000000..ce674d9
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/java/com/iailab/framework/ai/package-info.java
@@ -0,0 +1,13 @@
+/**
+ * AI 大模型组件,基于 Spring AI 拓展
+ *
+ * models 包路径:
+ *  1. xinghuo 包:【讯飞】星火,自己实现
+ *  2. deepseek 包:【深度求索】DeepSeek,自己实现
+ *  3. doubao 包:【字节豆包】DouBao,自己实现
+ *  4. hunyuan 包:【腾讯混元】HunYuan,自己实现
+ *  5. siliconflow 包:【硅基硅流】SiliconFlow,自己实现
+ *  6. midjourney 包:Midjourney API,对接 https://github.com/novicezk/midjourney-proxy 实现
+ *  7. suno 包:Suno API,对接 https://github.com/gcui-art/suno-api 实现
+ */
+package com.iailab.framework.ai;
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..43bfc39
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.iailab.framework.ai.config.IailabAiAutoConfiguration
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/AzureOpenAIChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/AzureOpenAIChatModelTests.java
new file mode 100644
index 0000000..4b9b824
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/AzureOpenAIChatModelTests.java
@@ -0,0 +1,69 @@
+package com.iailab.framework.ai.chat;
+
+import com.azure.ai.openai.OpenAIClientBuilder;
+import com.azure.core.credential.AzureKeyCredential;
+import com.azure.core.util.ClientOptions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
+import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties.DEFAULT_DEPLOYMENT_NAME;
+
+/**
+ * {@link AzureOpenAiChatModel} 集成测试
+ *
+ * @author Iailab
+ */
+public class AzureOpenAIChatModelTests {
+
+    // TODO @Iailab:晚点在调整
+    private final OpenAIClientBuilder openAiApi = new OpenAIClientBuilder()
+            .endpoint("https://eastusprejade.openai.azure.com")
+            .credential(new AzureKeyCredential("xxx"))
+            .clientOptions((new ClientOptions()).setApplicationId("spring-ai"));
+    private final AzureOpenAiChatModel chatModel = new AzureOpenAiChatModel(openAiApi,
+            AzureOpenAiChatOptions.builder().deploymentName(DEFAULT_DEPLOYMENT_NAME).build());
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/BaiChuanChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/BaiChuanChatModelTests.java
new file mode 100644
index 0000000..3b05dd4
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/BaiChuanChatModelTests.java
@@ -0,0 +1,68 @@
+package com.iailab.framework.ai.chat;
+
+import com.iailab.framework.ai.core.model.baichuan.BaiChuanChatModel;
+import com.iailab.framework.ai.core.model.deepseek.DeepSeekChatModel;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link BaiChuanChatModel} 集成测试
+ *
+ * @author Iailab
+ */
+public class BaiChuanChatModelTests {
+
+    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl(BaiChuanChatModel.BASE_URL)
+                    .apiKey("sk-61b6766a94c70786ed02673f5e16af3c") // apiKey
+                    .build())
+            .defaultOptions(OpenAiChatOptions.builder()
+                    .model("Baichuan4-Turbo") // 模型(https://platform.baichuan-ai.com/docs/api)
+                    .temperature(0.7)
+                    .build())
+            .build();
+
+    private final DeepSeekChatModel chatModel = new DeepSeekChatModel(openAiChatModel);
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(System.out::println).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DeepSeekChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DeepSeekChatModelTests.java
new file mode 100644
index 0000000..7b862b2
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DeepSeekChatModelTests.java
@@ -0,0 +1,67 @@
+package com.iailab.framework.ai.chat;
+
+import com.iailab.framework.ai.core.model.deepseek.DeepSeekChatModel;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link DeepSeekChatModel} 集成测试
+ *
+ * @author Iailab
+ */
+public class DeepSeekChatModelTests {
+
+    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl(DeepSeekChatModel.BASE_URL)
+                    .apiKey("sk-e52047409b144d97b791a6a46a2d") // apiKey
+                    .build())
+            .defaultOptions(OpenAiChatOptions.builder()
+                    .model("deepseek-chat") // 模型
+                    .temperature(0.7)
+                    .build())
+            .build();
+
+    private final DeepSeekChatModel chatModel = new DeepSeekChatModel(openAiChatModel);
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(System.out::println).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DifyChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DifyChatModelTests.java
new file mode 100644
index 0000000..9cd2c3b
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DifyChatModelTests.java
@@ -0,0 +1,63 @@
+package com.iailab.framework.ai.chat;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 基于 {@link OpenAiChatModel} 集成 Dify 测试
+ *
+ * @author Iailab
+ */
+public class DifyChatModelTests {
+
+    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl("http://127.0.0.1:3000")
+                    .apiKey("app-4hy2d7fJauSbrKbzTKX1afuP") // apiKey
+                    .build())
+            .build();
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DouBaoChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DouBaoChatModelTests.java
new file mode 100644
index 0000000..5e38c9f
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/DouBaoChatModelTests.java
@@ -0,0 +1,69 @@
+package com.iailab.framework.ai.chat;
+
+import com.iailab.framework.ai.core.model.doubao.DouBaoChatModel;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link DouBaoChatModel} 集成测试
+ *
+ * @author Iailab
+ */
+public class DouBaoChatModelTests {
+
+    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl(DouBaoChatModel.BASE_URL)
+                    .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey
+                    .build())
+            .defaultOptions(OpenAiChatOptions.builder()
+                    .model("doubao-1-5-lite-32k-250115") // 模型(doubao)
+//                    .model("deepseek-r1-250120") // 模型(deepseek)
+                    .temperature(0.7)
+                    .build())
+            .build();
+
+    private final DouBaoChatModel chatModel = new DouBaoChatModel(openAiChatModel);
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+    }
+
+    // TODO @Iailab:因为使用的是 v1 api,导致 deepseek-r1-250120 不返回 think 过程,后续需要优化
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(System.out::println).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/FastGPTChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/FastGPTChatModelTests.java
new file mode 100644
index 0000000..f86d555
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/FastGPTChatModelTests.java
@@ -0,0 +1,63 @@
+package com.iailab.framework.ai.chat;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 基于 {@link OpenAiChatModel} 集成 FastGPT 测试
+ *
+ * @author Iailab
+ */
+public class FastGPTChatModelTests {
+
+    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl("https://cloud.fastgpt.cn/api")
+                    .apiKey("fastgpt-aqcc61kFtF8CeaglnGAfQOCIDWwjGdJVJHv6hIlMo28otFlva2aZNK") // apiKey
+                    .build())
+            .build();
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/HunYuanChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/HunYuanChatModelTests.java
new file mode 100644
index 0000000..7c23ea4
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/HunYuanChatModelTests.java
@@ -0,0 +1,110 @@
+package com.iailab.framework.ai.chat;
+
+import com.iailab.framework.ai.core.model.hunyuan.HunYuanChatModel;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link HunYuanChatModel} 集成测试
+ *
+ * @author Iailab
+ */
+public class HunYuanChatModelTests {
+
+    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl(HunYuanChatModel.BASE_URL)
+                    .apiKey("sk-bcd") // apiKey
+                    .build())
+            .defaultOptions(OpenAiChatOptions.builder()
+                    .model(HunYuanChatModel.MODEL_DEFAULT) // 模型
+                    .temperature(0.7)
+                    .build())
+            .build();
+
+    private final HunYuanChatModel chatModel = new HunYuanChatModel(openAiChatModel);
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(System.out::println).then().block();
+    }
+
+    private final OpenAiChatModel deepSeekOpenAiChatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl(HunYuanChatModel.DEEP_SEEK_BASE_URL)
+                    .apiKey("sk-abc") // apiKey
+                    .build())
+            .defaultOptions(OpenAiChatOptions.builder()
+//                    .model(HunYuanChatModel.DEEP_SEEK_MODEL_DEFAULT) // 模型("deepseek-v3")
+                    .model("deepseek-r1") // 模型("deepseek-r1")
+                    .temperature(0.7)
+                    .build())
+            .build();
+
+    private final HunYuanChatModel deepSeekChatModel = new HunYuanChatModel(deepSeekOpenAiChatModel);
+
+    @Test
+    @Disabled
+    public void testCall_deepseek() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = deepSeekChatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testStream_deekseek() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = deepSeekChatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(System.out::println).then().block();
+    }
+
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/LlamaChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/LlamaChatModelTests.java
new file mode 100644
index 0000000..9eae948
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/LlamaChatModelTests.java
@@ -0,0 +1,65 @@
+package com.iailab.framework.ai.chat;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.ollama.OllamaChatModel;
+import org.springframework.ai.ollama.api.OllamaApi;
+import org.springframework.ai.ollama.api.OllamaModel;
+import org.springframework.ai.ollama.api.OllamaOptions;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link OllamaChatModel} 集成测试
+ *
+ * @author Iailab
+ */
+public class LlamaChatModelTests {
+
+    private final OllamaChatModel chatModel = OllamaChatModel.builder()
+            .ollamaApi(new OllamaApi("http://127.0.0.1:11434")) // Ollama 服务地址
+            .defaultOptions(OllamaOptions.builder()
+                    .model(OllamaModel.LLAMA3.getName()) // 模型
+                    .build())
+            .build();
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/MiniMaxChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/MiniMaxChatModelTests.java
new file mode 100644
index 0000000..414aaec
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/MiniMaxChatModelTests.java
@@ -0,0 +1,62 @@
+package com.iailab.framework.ai.chat;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.minimax.MiniMaxChatModel;
+import org.springframework.ai.minimax.MiniMaxChatOptions;
+import org.springframework.ai.minimax.api.MiniMaxApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link MiniMaxChatModel} 的集成测试
+ *
+ * @author Iailab
+ */
+public class MiniMaxChatModelTests {
+
+    private final MiniMaxChatModel chatModel = new MiniMaxChatModel(
+            new MiniMaxApi("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiLnjovmlofmlowiLCJVc2VyTmFtZSI6IueOi-aWh-aWjCIsIkFjY291bnQiOiIiLCJTdWJqZWN0SUQiOiIxODk3Mjg3MjQ5NDU2ODA4MzQ2IiwiUGhvbmUiOiIxNTYwMTY5MTM5OSIsIkdyb3VwSUQiOiIxODk3Mjg3MjQ5NDQ4NDE5NzM4IiwiUGFnZU5hbWUiOiIiLCJNYWlsIjoiIiwiQ3JlYXRlVGltZSI6IjIwMjUtMDMtMTEgMTI6NTI6MDIiLCJUb2tlblR5cGUiOjEsImlzcyI6Im1pbmltYXgifQ.aAuB7gWW_oA4IYhh-CF7c9MfWWxKN49B_HK-DYjXaDwwffhiG-H1571z1WQhp9QytWG-DqgLejneeSxkiq1wQIe3FsEP2wz4BmGBct31LehbJu8ehLxg_vg75Uod1nFAHbm5mZz6JSVLNIlSo87Xr3UtSzJhAXlapEkcqlA4YOzOpKrZ8l5_OJPTORTCmHWZYgJcRS-faNiH62ZnUEHUozesTFhubJHo5GfJCw_edlnmfSUocERV1BjWvenhZ9My-aYXNktcW9WaSj9l6gayV7A0Ium_PL55T9ln1PcI8gayiVUKJGJDoqNyF1AF9_aF9NOKtTnQzwNqnZdlTYH6hw"), // 密钥
+            MiniMaxChatOptions.builder()
+                    .model(MiniMaxApi.ChatModel.ABAB_6_5_G_Chat.getValue()) // 模型
+                    .build());
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/MoonshotChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/MoonshotChatModelTests.java
new file mode 100644
index 0000000..98be8c3
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/MoonshotChatModelTests.java
@@ -0,0 +1,62 @@
+package com.iailab.framework.ai.chat;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.moonshot.MoonshotChatModel;
+import org.springframework.ai.moonshot.MoonshotChatOptions;
+import org.springframework.ai.moonshot.api.MoonshotApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link MoonshotChatModel} 的集成测试
+ *
+ * @author Iailab
+ */
+public class MoonshotChatModelTests {
+
+    private final MoonshotChatModel chatModel = new MoonshotChatModel(
+            new MoonshotApi("sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA"), // 密钥
+            MoonshotChatOptions.builder()
+                    .model("moonshot-v1-8k") // 模型
+                    .build());
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/OllamaChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/OllamaChatModelTests.java
new file mode 100644
index 0000000..8177135
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/OllamaChatModelTests.java
@@ -0,0 +1,65 @@
+package com.iailab.framework.ai.chat;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.ollama.OllamaChatModel;
+import org.springframework.ai.ollama.api.OllamaApi;
+import org.springframework.ai.ollama.api.OllamaOptions;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link OllamaChatModel} 集成测试
+ *
+ * @author Iailab
+ */
+public class OllamaChatModelTests {
+
+    private final OllamaChatModel chatModel = OllamaChatModel.builder()
+            .ollamaApi(new OllamaApi("http://127.0.0.1:11434")) // Ollama 服务地址
+            .defaultOptions(OllamaOptions.builder()
+//                    .model("qwen") // 模型(https://ollama.com/library/qwen)
+                    .model("deepseek-r1") // 模型(https://ollama.com/library/deepseek-r1)
+                    .build())
+            .build();
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/OpenAIChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/OpenAIChatModelTests.java
new file mode 100644
index 0000000..0234f9c
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/OpenAIChatModelTests.java
@@ -0,0 +1,68 @@
+package com.iailab.framework.ai.chat;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link OpenAiChatModel} 集成测试
+ *
+ * @author Iailab
+ */
+public class OpenAIChatModelTests {
+
+    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl("https://api.holdai.top")
+                    .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17") // apiKey
+                    .build())
+            .defaultOptions(OpenAiChatOptions.builder()
+                    .model(OpenAiApi.ChatModel.GPT_4_O) // 模型
+                    .temperature(0.7)
+                    .build())
+            .build();
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/SiliconFlowChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/SiliconFlowChatModelTests.java
new file mode 100644
index 0000000..1bed946
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/SiliconFlowChatModelTests.java
@@ -0,0 +1,70 @@
+package com.iailab.framework.ai.chat;
+
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link SiliconFlowChatModel} 集成测试
+ *
+ * @author Iailab
+ */
+public class SiliconFlowChatModelTests {
+
+    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
+                    .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey
+                    .build())
+            .defaultOptions(OpenAiChatOptions.builder()
+                    .model(SiliconFlowApiConstants.MODEL_DEFAULT) // 模型
+//                    .model("deepseek-ai/DeepSeek-R1") // 模型(deepseek-ai/DeepSeek-R1)可用赠费
+//                    .model("Pro/deepseek-ai/DeepSeek-R1") // 模型(Pro/deepseek-ai/DeepSeek-R1)需要付费
+                    .temperature(0.7)
+                    .build())
+            .build();
+
+    private final SiliconFlowChatModel chatModel = new SiliconFlowChatModel(openAiChatModel);
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(System.out::println).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/TongYiChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/TongYiChatModelTests.java
new file mode 100644
index 0000000..035000c
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/TongYiChatModelTests.java
@@ -0,0 +1,66 @@
+package com.iailab.framework.ai.chat;
+
+import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
+import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
+import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link DashScopeChatModel} 集成测试类
+ *
+ * @author fansili
+ */
+public class TongYiChatModelTests {
+
+    private final DashScopeChatModel chatModel = new DashScopeChatModel(
+            new DashScopeApi("sk-7d903764249848cfa912733146da12d1"),
+            DashScopeChatOptions.builder()
+                    .withModel("qwen1.5-72b-chat") // 模型
+//                    .withModel("deepseek-r1") // 模型(deepseek-r1)
+//                    .withModel("deepseek-v3") // 模型(deepseek-v3)
+//                    .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b)
+                    .build());
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/XingHuoChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/XingHuoChatModelTests.java
new file mode 100644
index 0000000..2dfe95f
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/XingHuoChatModelTests.java
@@ -0,0 +1,67 @@
+package com.iailab.framework.ai.chat;
+
+import com.iailab.framework.ai.core.model.xinghuo.XingHuoChatModel;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link XingHuoChatModel} 集成测试
+ *
+ * @author fansili
+ */
+public class XingHuoChatModelTests {
+
+    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+        .openAiApi(OpenAiApi.builder()
+                .baseUrl(XingHuoChatModel.BASE_URL)
+                .apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey
+                .build())
+        .defaultOptions(OpenAiChatOptions.builder()
+                .model("generalv3.5") // 模型
+                .temperature(0.7)
+                .build())
+        .build();
+
+    private final XingHuoChatModel chatModel = new XingHuoChatModel(openAiChatModel);
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(System.out::println).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/YiYanChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/YiYanChatModelTests.java
new file mode 100644
index 0000000..1d3f6cf
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/YiYanChatModelTests.java
@@ -0,0 +1,62 @@
+package com.iailab.framework.ai.chat;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.qianfan.QianFanChatModel;
+import org.springframework.ai.qianfan.QianFanChatOptions;
+import org.springframework.ai.qianfan.api.QianFanApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// TODO @Iailab:百度千帆 API 提供了 V2 版本,目前 Spring AI 不兼容,可关键 <https://github.com/spring-projects/spring-ai/issues/2179> 进展
+/**
+ * {@link QianFanChatModel} 的集成测试
+ *
+ * @author fansili
+ */
+public class YiYanChatModelTests {
+
+    private final QianFanChatModel chatModel = new QianFanChatModel(
+            new QianFanApi("qS8k8dYr2nXunagK4SSU8Xjj", "pHGbx51ql2f0hOyabQvSZezahVC3hh3e"), // 密钥
+            QianFanChatOptions.builder()
+                    .model(QianFanApi.ChatModel.ERNIE_4_0_8K_Preview.getValue())
+                    .build()
+    );
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        // TODO @Iailab:文心一言,只要带上 system message 就报错,已经各种测试,很莫名!
+//        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        // TODO @Iailab:文心一言,只要带上 system message 就报错,已经各种测试,很莫名!
+//        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(System.out::println).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/ZhiPuAiChatModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/ZhiPuAiChatModelTests.java
new file mode 100644
index 0000000..c93985b
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/chat/ZhiPuAiChatModelTests.java
@@ -0,0 +1,64 @@
+package com.iailab.framework.ai.chat;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
+import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
+import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
+import reactor.core.publisher.Flux;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link ZhiPuAiChatModel} 的集成测试
+ *
+ * @author Iailab
+ */
+public class ZhiPuAiChatModelTests {
+
+    private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel(
+            new ZhiPuAiApi("32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs"), // 密钥
+            ZhiPuAiChatOptions.builder()
+                    .model(ZhiPuAiApi.ChatModel.GLM_4.getName()) // 模型
+                    .build()
+    );
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        ChatResponse response = chatModel.call(new Prompt(messages));
+        // 打印结果
+        System.out.println(response);
+        System.out.println(response.getResult().getOutput());
+    }
+
+    @Test
+    @Disabled
+    public void testStream() {
+        // 准备参数
+        List<Message> messages = new ArrayList<>();
+        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
+        messages.add(new UserMessage("1 + 1 = ?"));
+
+        // 调用
+        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
+        // 打印结果
+        flux.doOnNext(response -> {
+//            System.out.println(response);
+            System.out.println(response.getResult().getOutput());
+        }).then().block();
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/MidjourneyApiTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/MidjourneyApiTests.java
new file mode 100644
index 0000000..2e198c0
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/MidjourneyApiTests.java
@@ -0,0 +1,62 @@
+package com.iailab.framework.ai.image;
+
+import com.iailab.framework.ai.core.model.midjourney.api.MidjourneyApi;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@link MidjourneyApi} 集成测试
+ *
+ * @author Iailab
+ */
+public class MidjourneyApiTests {
+
+    private final MidjourneyApi midjourneyApi = new MidjourneyApi(
+            "https://api.holdai.top/mj", // 链接
+            "sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17", // 密钥
+            null);
+
+    @Test
+    @Disabled
+    public void testImagine() {
+        // 准备参数
+        MidjourneyApi.ImagineRequest request = new MidjourneyApi.ImagineRequest(null,
+                "生成一个小猫,可爱的", null,
+                MidjourneyApi.ImagineRequest.buildState(512, 512, "6.0", MidjourneyApi.ModelEnum.MIDJOURNEY.getModel()));
+
+        // 方法调用
+        MidjourneyApi.SubmitResponse response = midjourneyApi.imagine(request);
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testAction() {
+        // 准备参数
+        MidjourneyApi.ActionRequest request = new MidjourneyApi.ActionRequest("1720277033455953",
+                "MJ::JOB::upsample::1::ee267661-ee52-4ced-a530-0343ba95af3b", null);
+
+        // 方法调用
+        MidjourneyApi.SubmitResponse response = midjourneyApi.action(request);
+        // 打印结果
+        System.out.println(response);
+    }
+
+    @Test
+    @Disabled
+    public void testGetTaskList() {
+        // 准备参数。该参数可以通过 MidjourneyApi.SubmitResponse 的 result 获取
+//        String taskId = "1720277033455953";
+        String taskId = "1720277214045971";
+
+        // 方法调用
+        List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(Collections.singletonList(taskId));
+        // 打印结果
+        System.out.println(taskList);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/OpenAiImageModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/OpenAiImageModelTests.java
new file mode 100644
index 0000000..30fcb4b
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/OpenAiImageModelTests.java
@@ -0,0 +1,40 @@
+package com.iailab.framework.ai.image;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.image.ImageOptions;
+import org.springframework.ai.image.ImagePrompt;
+import org.springframework.ai.image.ImageResponse;
+import org.springframework.ai.openai.OpenAiImageModel;
+import org.springframework.ai.openai.OpenAiImageOptions;
+import org.springframework.ai.openai.api.OpenAiImageApi;
+
+/**
+ * {@link OpenAiImageModel} 集成测试类
+ *
+ * @author fansili
+ */
+public class OpenAiImageModelTests {
+
+    private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAiImageApi.builder()
+            .baseUrl("https://api.holdai.top") // apiKey
+            .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17")
+            .build());
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        ImageOptions options = OpenAiImageOptions.builder()
+                .withModel(OpenAiImageApi.ImageModel.DALL_E_2.getValue()) // 这个模型比较便宜
+                .withHeight(256).withWidth(256)
+                .build();
+        ImagePrompt prompt = new ImagePrompt("中国长城!", options);
+
+        // 方法调用
+        ImageResponse response = imageModel.call(prompt);
+        // 打印结果
+        System.out.println(response);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/QianFanImageTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/QianFanImageTests.java
new file mode 100644
index 0000000..6eb8d9f
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/QianFanImageTests.java
@@ -0,0 +1,43 @@
+package com.iailab.framework.ai.image;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.image.ImagePrompt;
+import org.springframework.ai.image.ImageResponse;
+import org.springframework.ai.qianfan.QianFanImageModel;
+import org.springframework.ai.qianfan.QianFanImageOptions;
+import org.springframework.ai.qianfan.api.QianFanImageApi;
+
+import static com.iailab.framework.ai.image.StabilityAiImageModelTests.viewImage;
+
+// TODO @Iailab:百度千帆 API 提供了 V2 版本,目前 Spring AI 不兼容,可关键 <https://github.com/spring-projects/spring-ai/issues/2179> 进展
+
+/**
+ * {@link QianFanImageModel} 集成测试类
+ */
+public class QianFanImageTests {
+
+    private final QianFanImageModel imageModel = new QianFanImageModel(
+            new QianFanImageApi("qS8k8dYr2nXunagK4SSU8Xjj", "pHGbx51ql2f0hOyabQvSZezahVC3hh3e")); // 密钥
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        // 只支持 1024x1024、768x768、768x1024、1024x768、576x1024、1024x576
+        QianFanImageOptions imageOptions = QianFanImageOptions.builder()
+                .model(QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue())
+                .width(1024).height(1024)
+                .N(1)
+                .build();
+        ImagePrompt prompt = new ImagePrompt("good", imageOptions);
+
+        // 方法调用
+        ImageResponse response = imageModel.call(prompt);
+        // 打印结果
+        String b64Json = response.getResult().getOutput().getB64Json();
+        System.out.println(response);
+        viewImage(b64Json);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/SiliconFlowImageModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/SiliconFlowImageModelTests.java
new file mode 100644
index 0000000..2c8ce3c
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/SiliconFlowImageModelTests.java
@@ -0,0 +1,35 @@
+package com.iailab.framework.ai.image;
+
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowImageApi;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowImageModel;
+import com.iailab.framework.ai.core.model.siliconflow.SiliconFlowImageOptions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.image.ImagePrompt;
+import org.springframework.ai.image.ImageResponse;
+
+/**
+ * {@link SiliconFlowImageModel} 集成测试
+ */
+public class SiliconFlowImageModelTests {
+
+    private final SiliconFlowImageModel imageModel = new SiliconFlowImageModel(
+            new SiliconFlowImageApi("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // 密钥
+    );
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        SiliconFlowImageOptions imageOptions = SiliconFlowImageOptions.builder()
+                .model("Kwai-Kolors/Kolors")
+                .build();
+        ImagePrompt prompt = new ImagePrompt("万里长城", imageOptions);
+
+        // 方法调用
+        ImageResponse response = imageModel.call(prompt);
+        // 打印结果
+        System.out.println(response);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/StabilityAiImageModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/StabilityAiImageModelTests.java
new file mode 100644
index 0000000..04bb438
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/StabilityAiImageModelTests.java
@@ -0,0 +1,65 @@
+package com.iailab.framework.ai.image;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.thread.ThreadUtil;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.image.ImageOptions;
+import org.springframework.ai.image.ImagePrompt;
+import org.springframework.ai.image.ImageResponse;
+import org.springframework.ai.openai.OpenAiImageOptions;
+import org.springframework.ai.stabilityai.StabilityAiImageModel;
+import org.springframework.ai.stabilityai.api.StabilityAiApi;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * {@link StabilityAiImageModel} 集成测试类
+ *
+ * @author fansili
+ */
+public class StabilityAiImageModelTests {
+
+    private final StabilityAiImageModel imageModel = new StabilityAiImageModel(
+            new StabilityAiApi("sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx") // 密钥
+    );
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        ImageOptions options = OpenAiImageOptions.builder()
+                .withModel("stable-diffusion-v1-6")
+                .withHeight(320).withWidth(320)
+                .build();
+        ImagePrompt prompt = new ImagePrompt("great wall", options);
+
+        // 方法调用
+        ImageResponse response = imageModel.call(prompt);
+        // 打印结果
+        String b64Json = response.getResult().getOutput().getB64Json();
+        System.out.println(response);
+        viewImage(b64Json);
+    }
+
+    public static void viewImage(String b64Json) {
+        // 创建一个 JFrame
+        JFrame frame = new JFrame("Byte Image Display");
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.setSize(800, 600);
+
+        // 创建一个 JLabel 来显示图片
+        byte[] imageBytes = Base64.decode(b64Json);
+        JLabel label = new JLabel(new ImageIcon(imageBytes));
+
+        // 将 JLabel 添加到 JFrame
+        frame.getContentPane().add(label, BorderLayout.CENTER);
+
+        // 显示 JFrame
+        frame.setVisible(true);
+        ThreadUtil.sleep(1, TimeUnit.HOURS);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/TongYiImagesModelTest.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/TongYiImagesModelTest.java
new file mode 100644
index 0000000..6ffc7c2
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/TongYiImagesModelTest.java
@@ -0,0 +1,38 @@
+package com.iailab.framework.ai.image;
+
+import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
+import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;
+import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.image.ImageOptions;
+import org.springframework.ai.image.ImagePrompt;
+import org.springframework.ai.image.ImageResponse;
+
+/**
+ * {@link DashScopeImageModel} 集成测试类
+ *
+ * @author fansili
+ */
+public class TongYiImagesModelTest {
+
+    private final DashScopeImageModel imageModel = new DashScopeImageModel(
+            new DashScopeImageApi("sk-7d903764249848cfa912733146da12d1"));
+
+    @Test
+    @Disabled
+    public void imageCallTest() {
+        // 准备参数
+        ImageOptions options = DashScopeImageOptions.builder()
+                .withModel("wanx-v1")
+                .withHeight(256).withWidth(256)
+                .build();
+        ImagePrompt prompt = new ImagePrompt("中国长城!", options);
+
+        // 方法调用
+        ImageResponse response = imageModel.call(prompt);
+        // 打印结果
+        System.out.println(response);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/ZhiPuAiImageModelTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/ZhiPuAiImageModelTests.java
new file mode 100644
index 0000000..6aa28fa
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/image/ZhiPuAiImageModelTests.java
@@ -0,0 +1,35 @@
+package com.iailab.framework.ai.image;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.image.ImagePrompt;
+import org.springframework.ai.image.ImageResponse;
+import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
+import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
+import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
+
+/**
+ * {@link ZhiPuAiImageModel} 集成测试
+ */
+public class ZhiPuAiImageModelTests {
+
+    private final ZhiPuAiImageModel imageModel = new ZhiPuAiImageModel(
+            new ZhiPuAiImageApi("78d3228c1d9e5e342a3e1ab349e2dd7b.VXLoq5vrwK2ofboy") // 密钥
+    );
+
+    @Test
+    @Disabled
+    public void testCall() {
+        // 准备参数
+        ZhiPuAiImageOptions imageOptions = ZhiPuAiImageOptions.builder()
+                .model(ZhiPuAiImageApi.ImageModel.CogView_3.getValue())
+                .build();
+        ImagePrompt prompt = new ImagePrompt("万里长城", imageOptions);
+
+        // 方法调用
+        ImageResponse response = imageModel.call(prompt);
+        // 打印结果
+        System.out.println(response);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/mcp/DouBaoMcpTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/mcp/DouBaoMcpTests.java
new file mode 100644
index 0000000..65643fc
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/mcp/DouBaoMcpTests.java
@@ -0,0 +1,122 @@
+package com.iailab.framework.ai.mcp;
+
+import com.iailab.framework.ai.core.model.doubao.DouBaoChatModel;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.api.OpenAiApi;
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.ai.tool.method.MethodToolCallbackProvider;
+
+public class DouBaoMcpTests {
+
+    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+            .openAiApi(OpenAiApi.builder()
+                    .baseUrl(DouBaoChatModel.BASE_URL)
+                    .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey
+                    .build())
+            .defaultOptions(OpenAiChatOptions.builder()
+                    .model("doubao-1-5-lite-32k-250115") // 模型(doubao)
+                    .temperature(0.7)
+                    .build())
+            .build();
+
+    private final DouBaoChatModel chatModel = new DouBaoChatModel(openAiChatModel);
+
+    private final MethodToolCallbackProvider provider = MethodToolCallbackProvider.builder()
+            .toolObjects(new UserService())
+            .build();
+
+    private final ChatClient chatClient = ChatClient.builder(chatModel)
+            .defaultTools(provider)
+            .build();
+
+    @Test
+    public void testMcpGetUserInfo() {
+
+        // 打印结果
+        System.out.println(chatClient.prompt()
+                .user("目前有哪些工具可以使用")
+                .call()
+                .content());
+        System.out.println("====================================");
+        // 打印结果
+        System.out.println(chatClient.prompt()
+                .user("小新的年龄是多少")
+                .call()
+                .content());
+        System.out.println("====================================");
+        // 打印结果
+        System.out.println(chatClient.prompt()
+                .user("获取小新的基本信息")
+                .call()
+                .content());
+        System.out.println("====================================");
+        // 打印结果
+        System.out.println(chatClient.prompt()
+                .user("小新是什么职业的")
+                .call()
+                .content());
+        System.out.println("====================================");
+        // 打印结果
+        System.out.println(chatClient.prompt()
+                .user("小新的教育背景")
+                .call()
+                .content());
+        System.out.println("====================================");
+        // 打印结果
+        System.out.println(chatClient.prompt()
+                .user("小新的兴趣爱好是什么")
+                .call()
+                .content());
+        System.out.println("====================================");
+
+    }
+
+
+    static class UserService {
+
+        @Tool(name = "getUserAge", description = "获取用户年龄")
+        public String getUserAge(String userName) {
+            return "《" + userName + "》的年龄为:18";
+        }
+
+        @Tool(name = "getUserSex", description = "获取用户性别")
+        public String getUserSex(String userName) {
+            return "《" + userName + "》的性别为:男";
+        }
+
+        @Tool(name = "getUserBasicInfo", description = "获取用户基本信息,包括姓名、年龄、性别等")
+        public String getUserBasicInfo(String userName) {
+            return "《" + userName + "》的基本信息:\n姓名:" + userName + "\n年龄:18\n性别:男\n身高:175cm\n体重:65kg";
+        }
+
+        @Tool(name = "getUserContact", description = "获取用户联系方式,包括电话、邮箱等")
+        public String getUserContact(String userName) {
+            return "《" + userName + "》的联系方式:\n电话:138****1234\n邮箱:" + userName.toLowerCase() + "@example.com\nQQ:123456789";
+        }
+
+        @Tool(name = "getUserAddress", description = "获取用户地址信息")
+        public String getUserAddress(String userName) {
+            return "《" + userName + "》的地址信息:北京市朝阳区科技园区88号";
+        }
+
+        @Tool(name = "getUserJob", description = "获取用户职业信息")
+        public String getUserJob(String userName) {
+            return "《" + userName + "》的职业信息:软件工程师,就职于ABC科技有限公司,工作年限5年";
+        }
+
+        @Tool(name = "getUserHobbies", description = "获取用户兴趣爱好")
+        public String getUserHobbies(String userName) {
+            return "《" + userName + "》的兴趣爱好:编程、阅读、旅游、摄影、打篮球";
+        }
+
+        @Tool(name = "getUserEducation", description = "获取用户教育背景")
+        public String getUserEducation(String userName) {
+            return "《" + userName + "》的教育背景:\n本科:计算机科学与技术专业,北京大学\n硕士:软件工程专业,清华大学";
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/music/SunoApiTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/music/SunoApiTests.java
new file mode 100644
index 0000000..0563aa7
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/music/SunoApiTests.java
@@ -0,0 +1,84 @@
+package com.iailab.framework.ai.music;
+
+import cn.hutool.core.collection.ListUtil;
+import com.iailab.framework.ai.core.model.suno.api.SunoApi;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+/**
+ * {@link SunoApi} 集成测试
+ *
+ * @author xiaoxin
+ */
+public class SunoApiTests {
+
+    private final SunoApi sunoApi = new SunoApi("https://suno-3tah0ycyt-status2xxs-projects.vercel.app");
+//    private final SunoApi sunoApi = new SunoApi("http://127.0.0.1:3001");
+
+    @Test // 描述模式
+    @Disabled
+    public void testGenerate() {
+        // 准备参数
+        SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
+                "happy music",
+                "chirp-v3-5",
+                false);
+
+        // 调用方法
+        List<SunoApi.MusicData> musicList = sunoApi.generate(generateRequest);
+        // 打印结果
+        System.out.println(musicList);
+    }
+
+    @Test // 歌词模式
+    @Disabled
+    public void testCustomGenerate() {
+        // 准备参数
+        SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
+                "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。",
+                "Happy",
+                "Happy Song",
+                "chirp-v3.5",
+                false,
+                false);
+
+        // 调用方法
+        List<SunoApi.MusicData> musicList = sunoApi.customGenerate(generateRequest);
+        // 打印结果
+        System.out.println(musicList);
+    }
+
+    @Test
+    @Disabled
+    public void testGenerateLyrics() {
+        // 调用方法
+        SunoApi.LyricsData lyricsData = sunoApi.generateLyrics("A soothing lullaby");
+        // 打印结果
+        System.out.println(lyricsData);
+    }
+
+    @Test
+    @Disabled
+    public void testGetMusicList() {
+        // 准备参数
+//        String id = "d460ddda-7c87-4f34-b751-419b08a590ca";
+        String id = "584729e5-0fe9-4157-86da-1b4803ff42bf";
+
+        // 调用方法
+        List<SunoApi.MusicData> musicList = sunoApi.getMusicList(ListUtil.of(id));
+        // 打印结果
+        System.out.println(musicList);
+    }
+
+    @Test
+    @Disabled
+    public void testGetLimitUsage() {
+        // 调用方法
+        SunoApi.LimitUsageData limitUsageData = sunoApi.getLimitUsage();
+        // 打印结果
+        System.out.println(limitUsageData);
+    }
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/ppt/wdd/WenDuoDuoPptApiTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/ppt/wdd/WenDuoDuoPptApiTests.java
new file mode 100644
index 0000000..424eb97
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/ppt/wdd/WenDuoDuoPptApiTests.java
@@ -0,0 +1,314 @@
+package com.iailab.framework.ai.ppt.wdd;
+
+import com.iailab.framework.ai.core.model.wenduoduo.api.WenDuoDuoPptApi;
+import com.iailab.framework.common.util.json.JsonUtils;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Flux;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * {@link WenDuoDuoPptApi} 集成测试
+ *
+ * @author xiaoxin
+ */
+public class WenDuoDuoPptApiTests {
+
+    private final String token = ""; // API Token
+    private final WenDuoDuoPptApi wenDuoDuoPptApi = new WenDuoDuoPptApi(token);
+
+    @Test
+    @Disabled
+    public void testCreateApiToken() {
+        // 准备参数
+        String apiKey = "";
+        WenDuoDuoPptApi.CreateTokenRequest request = new WenDuoDuoPptApi.CreateTokenRequest(apiKey);
+        // 调用方法
+        String token = wenDuoDuoPptApi.createApiToken(request);
+        // 打印结果
+        System.out.println(token);
+    }
+
+    /**
+     * 创建任务
+     */
+    @Test
+    @Disabled
+    public void testCreateTask() {
+        WenDuoDuoPptApi.ApiResponse apiResponse = wenDuoDuoPptApi.createTask(1, "dify 介绍", null);
+        System.out.println(apiResponse);
+    }
+
+
+    @Test // 创建大纲
+    @Disabled
+    public void testGenerateOutlineRequest() {
+        WenDuoDuoPptApi.CreateOutlineRequest request = new WenDuoDuoPptApi.CreateOutlineRequest(
+                "1901539019628613632", "medium", null, null, null, null);
+        // 调用
+        Flux<Map<String, Object>> flux = wenDuoDuoPptApi.createOutline(request);
+        StringBuffer contentBuffer = new StringBuffer();
+        flux.doOnNext(chunk -> {
+            contentBuffer.append(chunk.get("text"));
+            if (Objects.equals(Integer.parseInt(String.valueOf(chunk.get("status"))), 4)) {
+                // status 为 4,最终 markdown 结构树
+                System.out.println(JsonUtils.toJsonString(chunk.get("result")));
+                System.out.println(" ########################################################################");
+            }
+        }).then().block();
+        // 打印结果
+        System.out.println(contentBuffer);
+    }
+
+    /**
+     * 修改大纲
+     */
+    @Test
+    @Disabled
+    public void testUpdateOutlineRequest() {
+        WenDuoDuoPptApi.UpdateOutlineRequest request = new WenDuoDuoPptApi.UpdateOutlineRequest(
+                "1901539019628613632", TEST_OUT_LINE_CONTENT, "精简一点,三个章节即可");
+        // 调用
+        Flux<Map<String, Object>> flux = wenDuoDuoPptApi.updateOutline(request);
+        StringBuffer contentBuffer = new StringBuffer();
+        flux.doOnNext(chunk -> {
+            contentBuffer.append(chunk.get("text"));
+            if (Objects.equals(Integer.parseInt(String.valueOf(chunk.get("status"))), 4)) {
+                // status 为 4,最终 markdown 结构树
+                System.out.println(JsonUtils.toJsonString(chunk.get("result")));
+                System.out.println(" ########################################################################");
+            }
+        }).then().block();
+        // 打印结果
+        System.out.println(contentBuffer);
+
+    }
+
+    /**
+     * 获取 PPT 模版分页
+     */
+    @Test
+    @Disabled
+    public void testGetPptTemplatePage() {
+        // 准备参数
+        WenDuoDuoPptApi.TemplateQueryRequest.Filter filter = new WenDuoDuoPptApi.TemplateQueryRequest.Filter(
+                1, null, null, null);
+        WenDuoDuoPptApi.TemplateQueryRequest request = new WenDuoDuoPptApi.TemplateQueryRequest(1, 10, filter);
+        // 调用
+        WenDuoDuoPptApi.PagePptTemplateInfo pptTemplatePage = wenDuoDuoPptApi.getTemplatePage(request);
+        // 打印结果
+        System.out.println(pptTemplatePage);
+    }
+
+    /**
+     * 生成 PPT
+     */
+    @Test
+    @Disabled
+    public void testGeneratePptx() {
+        // 准备参数
+        WenDuoDuoPptApi.PptCreateRequest request = new WenDuoDuoPptApi.PptCreateRequest("1901539019628613632", "1805081814809960448", TEST_OUT_LINE_CONTENT);
+        // 调用
+        WenDuoDuoPptApi.PptInfo pptInfo = wenDuoDuoPptApi.create(request);
+        // 打印结果
+        System.out.println(pptInfo);
+    }
+
+    private final String TEST_OUT_LINE_CONTENT = """
+            # Dify:新一代AI应用开发平台
+            
+            ## 1 什么是Dify
+            ### 1.1 Dify定义:AI应用开发平台
+            #### 1.1.1 低代码开发
+            Dify是一个低代码AI应用开发平台,旨在简化AI应用的构建过程,让开发者无需编写大量代码即可快速创建各种智能应用。
+            #### 1.1.2 核心功能
+            Dify的核心功能包括数据集成、模型选择、流程编排和应用部署,提供一站式解决方案,加速AI应用的落地和迭代。
+            #### 1.1.3 开源与商业
+            Dify提供开源版本和商业版本,满足不同用户的需求,开源版本适合个人开发者和小型团队,商业版本则提供更强大的功能和技术支持。
+            
+            ### 1.2 Dify解决的问题:AI开发痛点
+            #### 1.2.1 开发周期长
+            传统AI应用开发周期长,需要大量的人力和时间投入,Dify通过可视化界面和预置组件,大幅缩短开发周期。
+            #### 1.2.2 技术门槛高
+            AI技术门槛高,需要专业的知识和技能,Dify降低技术门槛,让更多开发者能够参与到AI应用的开发中来。
+            #### 1.2.3 部署和维护复杂
+            AI应用的部署和维护复杂,需要专业的运维团队,Dify提供自动化的部署和维护工具,简化流程,降低成本。
+            
+            ### 1.3 Dify发展历程
+            #### 1.3.1 早期探索
+            Dify的早期版本主要关注于自然语言处理领域的应用,通过集成各种NLP模型,提供文本分类、情感分析等功能。
+            #### 1.3.2 功能扩展
+            随着用户需求的不断增长,Dify的功能逐渐扩展到图像识别、语音识别等领域,支持更多类型的AI应用。
+            #### 1.3.3 生态建设
+            Dify积极建设开发者生态,提供丰富的文档、教程和案例,帮助开发者更好地使用Dify平台,共同推动AI技术的发展。
+            
+            ## 2 Dify的核心功能
+            ### 2.1 数据集成:连接各种数据源
+            #### 2.1.1 支持多种数据源
+            Dify支持连接各种数据源,包括关系型数据库、NoSQL数据库、文件系统、云存储等,满足不同场景的数据需求。
+            #### 2.1.2 数据转换和清洗
+            Dify提供数据转换和清洗功能,可以将不同格式的数据转换为统一的格式,并去除无效数据,提高数据质量。
+            #### 2.1.3 数据安全
+            Dify注重数据安全,采用各种安全措施保护用户的数据,包括数据加密、访问控制、权限管理等。
+            
+            ### 2.2 模型选择:丰富的AI模型库
+            #### 2.2.1 预置模型
+            Dify预置了丰富的AI模型,包括自然语言处理、图像识别、语音识别等领域的模型,开发者可以直接使用这些模型,无需自行训练,极大的简化了开发流程。
+            #### 2.2.2 自定义模型
+            Dify支持开发者上传自定义模型,满足个性化的需求。开发者可以将自己训练的模型部署到Dify平台上,与其他开发者共享。
+            #### 2.2.3 模型评估
+            Dify提供模型评估功能,可以对不同模型进行评估,选择最优的模型,提高应用性能。
+            
+            ### 2.3 流程编排:可视化流程设计器
+            #### 2.3.1 可视化界面
+            Dify提供可视化的流程设计器,开发者可以通过拖拽组件的方式,设计AI应用的流程,无需编写代码,简单高效。
+            #### 2.3.2 灵活的流程控制
+            Dify支持灵活的流程控制,可以根据不同的条件执行不同的分支,实现复杂的业务逻辑。
+            #### 2.3.3 实时调试
+            Dify提供实时调试功能,可以在设计流程的过程中,实时查看流程的执行结果,及时发现和解决问题。
+            
+            ### 2.4 应用部署:一键部署和管理
+            #### 2.4.1 快速部署
+            Dify提供一键部署功能,可以将AI应用快速部署到各种环境,包括本地环境、云环境、容器环境等。
+            #### 2.4.2 自动伸缩
+            Dify支持自动伸缩,可以根据应用的负载自动调整资源,保证应用的稳定性和性能。
+            #### 2.4.3 监控和告警
+            Dify提供监控和告警功能,可以实时监控应用的状态,并在出现问题时及时告警,方便运维人员进行处理。
+            
+            ## 3 Dify的特点和优势
+            ### 3.1 低代码:降低开发门槛
+            #### 3.1.1 可视化开发
+            Dify采用可视化开发模式,开发者无需编写大量代码,只需通过拖拽组件即可完成AI应用的开发,降低了开发门槛。
+            #### 3.1.2 预置组件
+            Dify预置了丰富的组件,包括数据源组件、模型组件、流程控制组件等,开发者可以直接使用这些组件,提高开发效率。
+            #### 3.1.3 减少代码量
+            Dify可以显著减少代码量,降低开发难度,让更多开发者能够参与到AI应用的开发中来。
+            
+            ### 3.2 灵活:满足不同场景需求
+            #### 3.2.1 支持多种数据源
+            Dify支持多种数据源,可以连接各种数据源,满足不同场景的数据需求。
+            #### 3.2.2 支持自定义模型
+            Dify支持自定义模型,开发者可以将自己训练的模型部署到Dify平台上,满足个性化的需求。
+            #### 3.2.3 灵活的流程控制
+            Dify支持灵活的流程控制,可以根据不同的条件执行不同的分支,实现复杂的业务逻辑。
+            
+            ### 3.3 高效:加速应用落地
+            #### 3.3.1 快速开发
+            Dify通过可视化界面和预置组件,大幅缩短开发周期,加速AI应用的落地。
+            #### 3.3.2 快速部署
+            Dify提供一键部署功能,可以将AI应用快速部署到各种环境,提高部署效率。
+            #### 3.3.3 自动化运维
+            Dify提供自动化的运维工具,简化运维流程,降低运维成本。
+            
+            ### 3.4 开放:构建繁荣生态
+            #### 3.4.1 开源社区
+            Dify拥有活跃的开源社区,开发者可以在社区中交流经验、分享资源、共同推动Dify的发展。
+            #### 3.4.2 丰富的文档
+            Dify提供丰富的文档、教程和案例,帮助开发者更好地使用Dify平台。
+            #### 3.4.3 API支持
+            Dify提供API支持,开发者可以通过API将Dify集成到自己的系统中,扩展Dify的功能。
+            
+            ## 4 Dify的使用场景
+            ### 4.1 智能客服:提升客户服务质量
+            #### 4.1.1 自动回复
+            Dify可以用于构建智能客服系统,实现自动回复客户的常见问题,提高客户服务效率。
+            #### 4.1.2 情感分析
+            Dify可以对客户的语音或文本进行情感分析,判断客户的情绪,并根据情绪提供个性化的服务。
+            #### 4.1.3 知识库问答
+            Dify可以构建知识库问答系统,让客户通过提问的方式获取所需的信息,提高客户满意度。
+            
+            ### 4.2 金融风控:提高风险识别能力
+            #### 4.2.1 欺诈检测
+            Dify可以用于构建金融风控系统,实现欺诈检测,识别可疑交易,降低风险。
+            #### 4.2.2 信用评估
+            Dify可以对用户的信用进行评估,并根据评估结果提供不同的金融服务。
+            #### 4.2.3 反洗钱
+            Dify可以用于反洗钱,识别可疑资金流动,防止犯罪行为。
+            
+            ### 4.3 智慧医疗:提升医疗服务水平
+            #### 4.3.1 疾病诊断
+            Dify可以用于辅助疾病诊断,提高诊断准确率,缩短诊断时间。
+            #### 4.3.2 药物研发
+            Dify可以用于药物研发,加速新药的发现和开发。
+            #### 4.3.3 智能健康管理
+            Dify可以构建智能健康管理系统,为用户提供个性化的健康建议和服务。
+            
+            ### 4.4 智慧城市:提升城市管理效率
+            #### 4.4.1 交通优化
+            Dify可以用于交通优化,提高交通效率,缓解交通拥堵。
+            #### 4.4.2 环境监测
+            Dify可以用于环境监测,实时监测空气质量、水质等环境指标,及时发现和解决环境问题。
+            #### 4.4.3 智能安防
+            Dify可以用于智能安防,提高城市安全水平,预防犯罪行为。
+            
+            ## 5 Dify的成功案例
+            ### 5.1 Case 1:某电商平台的智能客服
+            #### 5.1.1 项目背景
+            该电商平台客户服务压力大,人工客服成本高,需要一种智能化的解决方案。
+            #### 5.1.2 解决方案
+            使用Dify构建智能客服系统,实现自动回复客户的常见问题,并根据客户的情绪提供个性化的服务。
+            #### 5.1.3 效果
+            客户服务效率提高50%,客户满意度提高20%,人工客服成本降低30%。
+            
+            ### 5.2 Case 2:某银行的金融风控系统
+            #### 5.2.1 项目背景
+            该银行面临日益增长的金融风险,需要一种更有效的风险识别和控制手段。
+            #### 5.2.2 解决方案
+            使用Dify构建金融风控系统,实现欺诈检测、信用评估和反洗钱等功能,提高风险识别能力。
+            #### 5.2.3 效果
+            欺诈交易识别率提高40%,信用评估准确率提高30%,洗钱风险降低25%。
+            
+            ### 5.3 Case 3:某医院的辅助疾病诊断系统
+            #### 5.3.1 项目背景
+            该医院医生工作压力大,疾病诊断准确率有待提高,需要一种辅助诊断工具。
+            #### 5.3.2 解决方案
+            使用Dify构建辅助疾病诊断系统,根据患者的病历和症状,提供诊断建议,提高诊断准确率。
+            #### 5.3.3 效果
+            疾病诊断准确率提高20%,诊断时间缩短15%,医生工作效率提高10%。
+            
+            ## 6 Dify的未来展望
+            ### 6.1 技术升级
+            #### 6.1.1 模型优化
+            Dify将不断优化预置模型,提高模型性能,并支持更多类型的AI模型。
+            #### 6.1.2 流程引擎升级
+            Dify将升级流程引擎,提高流程的灵活性和可扩展性,支持更复杂的业务逻辑。
+            #### 6.1.3 平台性能优化
+            Dify将不断优化平台性能,提高平台的稳定性和可靠性,满足大规模应用的需求。
+            
+            ### 6.2 生态建设
+            #### 6.2.1 社区建设
+            Dify将继续加强开源社区建设,吸引更多开发者参与,共同推动Dify的发展。
+            #### 6.2.2 合作伙伴拓展
+            Dify将拓展合作伙伴,与更多的企业和机构合作,共同推动AI技术的应用。
+            #### 6.2.3 应用商店
+            Dify将构建应用商店,让开发者可以分享自己的应用,用户可以购买和使用这些应用,构建繁荣的生态系统。
+            
+            ### 6.3 应用领域拓展
+            #### 6.3.1 智能制造
+            Dify将拓展到智能制造领域,为企业提供智能化的生产管理和质量控制解决方案。
+            #### 6.3.2 智慧农业
+            Dify将拓展到智慧农业领域,为农民提供智能化的种植和养殖管理解决方案。
+            #### 6.3.3 更多领域
+            Dify将拓展到更多领域,为各行各业提供智能化的解决方案,推动社会发展。
+            
+            ## 7 总结
+            ### 7.1 Dify的价值
+            #### 7.1.1 降低AI开发门槛
+            Dify通过低代码的方式,让更多开发者能够参与到AI应用的开发中来。
+            #### 7.1.2 加速AI应用落地
+            Dify提供一站式解决方案,加速AI应用的落地和迭代。
+            #### 7.1.3 构建繁荣的AI生态
+            Dify通过开源社区和应用商店,构建繁荣的AI生态系统。
+            
+            ### 7.2 共同发展
+            #### 7.2.1 欢迎加入Dify社区
+            欢迎更多开发者加入Dify社区,共同推动Dify的发展。
+            #### 7.2.2 合作共赢
+            期待与更多的企业和机构合作,共同推动AI技术的应用。
+            #### 7.2.3 共创未来
+            让我们一起用AI技术改变世界,共创美好未来。
+            """;
+
+}
diff --git a/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/ppt/xunfei/XunFeiPptApiTests.java b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/ppt/xunfei/XunFeiPptApiTests.java
new file mode 100644
index 0000000..25da855
--- /dev/null
+++ b/iailab-module-ai/iailab-spring-boot-starter-ai/src/test/java/com/iailab/framework/ai/ppt/xunfei/XunFeiPptApiTests.java
@@ -0,0 +1,319 @@
+package com.iailab.framework.ai.ppt.xunfei;
+
+import cn.hutool.core.io.FileUtil;
+import com.iailab.framework.ai.core.model.xinghuo.api.XunFeiPptApi;
+import com.iailab.framework.common.util.json.JsonUtils;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+
+/**
+ * {@link XunFeiPptApi} 集成测试
+ *
+ * @author xiaoxin
+ */
+public class XunFeiPptApiTests {
+
+    // 讯飞 API 配置信息,实际使用时请替换为您的应用信息
+    private static final String APP_ID = "6c8ac023";
+    private static final String API_SECRET = "Y2RjM2Q1MWJjZTdkYmFiODc0OGE5NmRk";
+
+    private final XunFeiPptApi xunfeiPptApi = new XunFeiPptApi(APP_ID, API_SECRET);
+
+    /**
+     * 获取 PPT 模板列表
+     */
+    @Test
+    @Disabled
+    public void testGetTemplatePage() {
+        // 调用方法
+        XunFeiPptApi.TemplatePageResponse response = xunfeiPptApi.getTemplatePage("商务", 10);
+        // 打印结果
+        System.out.println("模板列表响应:" + JsonUtils.toJsonString(response));
+
+        if (response != null && response.data() != null && response.data().records() != null) {
+            System.out.println("模板总数:" + response.data().total());
+            System.out.println("当前页码:" + response.data().pageNum());
+            System.out.println("模板数量:" + response.data().records().size());
+
+            // 打印第一个模板的信息(如果存在)
+            if (!response.data().records().isEmpty()) {
+                XunFeiPptApi.TemplateInfo firstTemplate = response.data().records().get(0);
+                System.out.println("模板ID:" + firstTemplate.templateIndexId());
+                System.out.println("模板风格:" + firstTemplate.style());
+                System.out.println("模板颜色:" + firstTemplate.color());
+                System.out.println("模板行业:" + firstTemplate.industry());
+            }
+        }
+    }
+
+    /**
+     * 创建大纲(通过文本)
+     */
+    @Test
+    @Disabled
+    public void testCreateOutline() {
+        XunFeiPptApi.CreateResponse response = getCreateResponse();
+        // 打印结果
+        System.out.println("创建大纲响应:" + JsonUtils.toJsonString(response));
+
+        // 保存 sid 和 outline 用于后续测试
+        if (response != null && response.data() != null) {
+            System.out.println("sid: " + response.data().sid());
+            if (response.data().outline() != null) {
+                // 使用 OutlineData 的 toJsonString 方法
+                System.out.println("outline: " + response.data().outline().toJsonString());
+                // 将 outline 对象转换为 JSON 字符串,用于后续 createPptByOutline 测试
+                String outlineJson = response.data().outline().toJsonString();
+                System.out.println("可用于 createPptByOutline 的 outline 字符串: " + outlineJson);
+            }
+        }
+    }
+
+    /**
+     * 创建大纲(通过文本)
+     *
+     * @return 创建大纲响应
+     */
+    private XunFeiPptApi.CreateResponse getCreateResponse() {
+        String param = "智能体平台 Dify 介绍";
+        return xunfeiPptApi.createOutline(param);
+    }
+
+    /**
+     * 通过大纲创建 PPT(完整参数)
+     */
+    @Test
+    @Disabled
+    public void testCreatePptByOutlineWithFullParams() {
+        // 创建大纲对象
+        XunFeiPptApi.CreateResponse createResponse = getCreateResponse();
+        // 调用方法
+        XunFeiPptApi.CreateResponse response = xunfeiPptApi.createPptByOutline(createResponse.data().outline(), "精简一些,不要超过6个章节");
+        // 打印结果
+        System.out.println("通过大纲创建 PPT 响应:" + JsonUtils.toJsonString(response));
+
+        // 保存sid用于后续进度查询
+        if (response != null && response.data() != null) {
+            System.out.println("sid: " + response.data().sid());
+            if (response.data().coverImgSrc() != null) {
+                System.out.println("封面图片: " + response.data().coverImgSrc());
+            }
+        }
+    }
+
+    /**
+     * 检查 PPT 生成进度
+     */
+    @Test
+    @Disabled
+    public void testCheckProgress() {
+        // 准备参数 - 使用之前创建 PPT 时返回的 sid
+        String sid = "e96dac09f2ec4ee289f029a5fb874ecd"; // 替换为实际的sid
+
+        // 调用方法
+        XunFeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid);
+        // 打印结果
+        System.out.println("检查进度响应:" + JsonUtils.toJsonString(response));
+
+        // 安全地访问响应数据
+        if (response != null && response.data() != null) {
+            XunFeiPptApi.ProgressResponseData data = response.data();
+
+            // 打印PPT生成状态
+            System.out.println("PPT 构建状态: " + data.pptStatus());
+            System.out.println("AI 配图状态: " + data.aiImageStatus());
+            System.out.println("演讲备注状态: " + data.cardNoteStatus());
+
+            // 打印进度信息
+            if (data.totalPages() != null && data.donePages() != null) {
+                System.out.println("总页数: " + data.totalPages());
+                System.out.println("已完成页数: " + data.donePages());
+                System.out.println("完成进度: " + data.getProgressPercent() + "%");
+            } else {
+                System.out.println("进度: " + data.process() + "%");
+            }
+
+            // 检查是否完成
+            if (data.isAllDone()) {
+                System.out.println("PPT 生成已完成!");
+                System.out.println("PPT 下载链接: " + data.pptUrl());
+            }
+            // 检查是否失败
+            else if (data.isFailed()) {
+                System.out.println("PPT 生成失败!");
+                System.out.println("错误信息: " + data.errMsg());
+            }
+            // 正在进行中
+            else {
+                System.out.println("PPT 生成中,请稍后再查询...");
+            }
+        }
+    }
+
+    /**
+     * 轮询检查 PPT 生成进度直到完成
+     */
+    @Test
+    @Disabled
+    public void testPollCheckProgress() throws InterruptedException {
+        // 准备参数 - 使用之前创建 PP T时返回的 sid
+        String sid = "1690ef6ee0344e72b5c5434f403b8eaa"; // 替换为实际的sid
+
+        // 最大轮询次数
+        int maxPolls = 20;
+        // 轮询间隔(毫秒)- 讯飞 API 限流为 3 秒一次
+        long pollInterval = 3500;
+
+        for (int i = 0; i < maxPolls; i++) {
+            System.out.println("第" + (i + 1) + "次查询进度...");
+
+            // 调用方法
+            XunFeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid);
+
+            // 安全地访问响应数据
+            if (response != null && response.data() != null) {
+                XunFeiPptApi.ProgressResponseData data = response.data();
+
+                // 打印进度信息
+                System.out.println("PPT 构建状态: " + data.pptStatus());
+                if (data.totalPages() != null && data.donePages() != null) {
+                    System.out.println("完成进度: " + data.donePages() + "/" + data.totalPages()
+                            + " (" + data.getProgressPercent() + "%)");
+                }
+
+                // 检查是否完成
+                if (data.isAllDone()) {
+                    System.out.println("PPT 生成已完成!");
+                    System.out.println("PPT 下载链接: " + data.pptUrl());
+                    break;
+                }
+                // 检查是否失败
+                else if (data.isFailed()) {
+                    System.out.println("PPT 生成失败!");
+                    System.out.println("错误信息: " + data.errMsg());
+                    break;
+                }
+                // 正在进行中,继续轮询
+                else {
+                    System.out.println("PPT 生成中,等待" + (pollInterval / 1000) + "秒后继续查询...");
+                    Thread.sleep(pollInterval);
+                }
+            } else {
+                System.out.println("查询失败,等待" + (pollInterval / 1000) + "秒后重试...");
+                Thread.sleep(pollInterval);
+            }
+        }
+    }
+
+    /**
+     * 直接创建 PPT(通过文本)
+     */
+    @Test
+    @Disabled
+    public void testCreatePptByText() {
+        // 准备参数
+        String query = "合肥天气趋势分析,包括近5年的气温变化、降水量变化、极端天气事件,以及对城市生活的影响";
+
+        // 调用方法
+        XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(query);
+        // 打印结果
+        System.out.println("直接创建 PPT 响应:" + JsonUtils.toJsonString(response));
+
+        // 保存 sid 用于后续进度查询
+        if (response != null && response.data() != null) {
+            System.out.println("sid: " + response.data().sid());
+            if (response.data().coverImgSrc() != null) {
+                System.out.println("封面图片: " + response.data().coverImgSrc());
+            }
+            System.out.println("标题: " + response.data().title());
+            System.out.println("副标题: " + response.data().subTitle());
+        }
+    }
+
+    /**
+     * 直接创建 PPT(通过文件)
+     */
+    @Test
+    @Disabled
+    public void testCreatePptByFile() {
+        // 准备参数
+        File file = new File("src/test/resources/test.txt"); // 请确保此文件存在
+        MultipartFile multipartFile = convertFileToMultipartFile(file);
+
+        // 调用方法
+        XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(multipartFile, file.getName());
+        // 打印结果
+        System.out.println("通过文件创建PPT响应:" + JsonUtils.toJsonString(response));
+
+        // 保存 sid 用于后续进度查询
+        if (response != null && response.data() != null) {
+            System.out.println("sid: " + response.data().sid());
+            if (response.data().coverImgSrc() != null) {
+                System.out.println("封面图片: " + response.data().coverImgSrc());
+            }
+            System.out.println("标题: " + response.data().title());
+            System.out.println("副标题: " + response.data().subTitle());
+        }
+    }
+
+    /**
+     * 直接创建 PPT(完整参数)
+     */
+    @Test
+    @Disabled
+    public void testCreatePptWithFullParams() {
+        // 准备参数
+        String query = "合肥天气趋势分析,包括近 5 年的气温变化、降水量变化、极端天气事件,以及对城市生活的影响";
+
+        // 创建请求对象
+        XunFeiPptApi.CreatePptRequest request = XunFeiPptApi.CreatePptRequest.builder()
+                .query(query)
+                .language("cn")
+                .isCardNote(true)
+                .search(true)
+                .isFigure(true)
+                .aiImage("advanced")
+                .author("测试用户")
+                .build();
+
+        // 调用方法
+        XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(request);
+        // 打印结果
+        System.out.println("使用完整参数创建 PPT 响应:" + JsonUtils.toJsonString(response));
+
+        // 保存 sid 用于后续进度查询
+        if (response != null && response.data() != null) {
+            String sid = response.data().sid();
+            System.out.println("sid: " + sid);
+            if (response.data().coverImgSrc() != null) {
+                System.out.println("封面图片: " + response.data().coverImgSrc());
+            }
+            System.out.println("标题: " + response.data().title());
+            System.out.println("副标题: " + response.data().subTitle());
+
+            // 立即查询一次进度
+            System.out.println("立即查询进度...");
+            XunFeiPptApi.ProgressResponse progressResponse = xunfeiPptApi.checkProgress(sid);
+            if (progressResponse != null && progressResponse.data() != null) {
+                XunFeiPptApi.ProgressResponseData progressData = progressResponse.data();
+                System.out.println("PPT 构建状态: " + progressData.pptStatus());
+                if (progressData.totalPages() != null && progressData.donePages() != null) {
+                    System.out.println("完成进度: " + progressData.donePages() + "/" + progressData.totalPages()
+                            + " (" + progressData.getProgressPercent() + "%)");
+                }
+            }
+        }
+    }
+
+    /**
+     * 将 File 转换为 MultipartFile
+     */
+    private MultipartFile convertFileToMultipartFile(File file) {
+        return new MockMultipartFile("file", file.getName(), "text/plain", FileUtil.readBytes(file));
+    }
+
+}
\ No newline at end of file
diff --git a/iailab-module-ai/pom.xml b/iailab-module-ai/pom.xml
new file mode 100644
index 0000000..6972f1d
--- /dev/null
+++ b/iailab-module-ai/pom.xml
@@ -0,0 +1,409 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.iailab</groupId>
+        <artifactId>iailab-plat</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.iailab</groupId>
+    <artifactId>iailab-module-ai</artifactId>
+    <version>${ai.revision}</version>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>iailab-module-ai-api</module>
+        <module>iailab-module-ai-biz</module>
+        <module>iailab-spring-boot-starter-ai</module>
+    </modules>
+
+    <description>
+        ai 模块下,接入 LLM 大模型,支持聊天、绘图、音乐、写作、思维导图等功能。
+        目前已接入各种模型,不限于:
+          国内:通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek
+          国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno
+    </description>
+
+    <properties>
+        <ai.revision>1.0.0-jdk17</ai.revision>
+        <revision>1.0.0</revision>
+        <!-- Maven 相关 -->
+        <java.version>17</java.version>
+        <maven.compiler.source>${java.version}</maven.compiler.source>
+        <maven.compiler.target>${java.version}</maven.compiler.target>
+        <maven-surefire-plugin.version>3.2.2</maven-surefire-plugin.version>
+        <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
+        <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
+        <!-- 看看咋放到 bom 里 -->
+        <lombok.version>1.18.36</lombok.version>
+        <spring.boot.version>3.4.1</spring.boot.version>
+        <mapstruct.version>1.6.3</mapstruct.version>
+        <!-- 统一依赖管理 -->
+        <spring.cloud.version>2024.0.0</spring.cloud.version>
+        <spring.cloud.alibaba.version>2023.0.3.2</spring.cloud.alibaba.version>
+        <!-- Web 相关 -->
+        <springdoc.version>2.7.0</springdoc.version>
+        <knife4j.version>4.6.0</knife4j.version>
+        <!-- 工具类相关 -->
+        <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
+        <netty.version>4.1.116.Final</netty.version>
+        <hutool-5.version>5.8.35</hutool-5.version>
+        <hutool-6.version>6.0.0-M19</hutool-6.version>
+        <skywalking.version>9.0.0</skywalking.version>
+        <!-- DB 相关 -->
+        <druid.version>1.2.24</druid.version>
+        <mybatis.version>3.5.17</mybatis.version>
+        <mybatis-plus.version>3.5.9</mybatis-plus.version>
+        <dynamic-datasource.version>4.3.1</dynamic-datasource.version>
+        <mybatis-plus-join.version>1.4.13</mybatis-plus-join.version>
+        <easy-trans.version>3.0.6</easy-trans.version>
+        <redisson.version>3.41.0</redisson.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- 统一依赖管理 -->
+            <dependency>
+                <groupId>io.netty</groupId>
+                <artifactId>netty-bom</artifactId>
+                <version>${netty.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring.boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring.cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.cloud</groupId>
+                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
+                <version>${spring.cloud.alibaba.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <!-- 业务组件 -->
+            <dependency>
+                <groupId>io.github.mouzt</groupId>
+                <artifactId>bizlog-sdk</artifactId>
+                <version>${bizlog-sdk.version}</version>
+                <exclusions>
+                    <exclusion> <!-- 排除掉springboot依赖使用项目的 -->
+                        <groupId>org.springframework.boot</groupId>
+                        <artifactId>spring-boot-starter</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-biz-tenant</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-biz-data-permission</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-biz-ip</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <!-- Spring 核心 -->
+            <dependency>
+                <!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-configuration-processor</artifactId>
+                <version>${spring.boot.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-env</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-web</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-security</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-security</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-websocket</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.github.xingfudeshi</groupId> <!-- TODO Iailab:https://github.com/xiaoymin/knife4j/issues/874 -->
+                <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
+                <version>${knife4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springdoc</groupId>
+                <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
+                <version>${springdoc.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UI:knife4j【网关专属】 -->
+                <artifactId>knife4j-gateway-spring-boot-starter</artifactId>
+                <version>${knife4j.version}</version> <!-- TODO Iailab:等 4.5.0 => 4.6.0 -->
+            </dependency>
+
+            <!-- DB 相关 -->
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-mybatis</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-module-infra-api</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-module-system-api</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid-spring-boot-3-starter</artifactId>
+                <version>${druid.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mybatis</groupId>
+                <artifactId>mybatis</artifactId>
+                <version>${mybatis.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-jsqlparser</artifactId>
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>dynamic-datasource-spring-boot3-starter</artifactId> <!-- 多数据源 -->
+                <version>${dynamic-datasource.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.yulichang</groupId>
+                <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
+                <version>${mybatis-plus-join.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-spring-boot-autoconfigure</artifactId>
+                <version>3.5.9</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->
+                <artifactId>easy-trans-spring-boot-starter</artifactId>
+                <version>${easy-trans.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.springframework</groupId>
+                        <artifactId>spring-context</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.springframework.cloud</groupId>
+                        <artifactId>spring-cloud-commons</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>com.fhs-opensource</groupId>
+                <artifactId>easy-trans-mybatis-plus-extend</artifactId>
+                <version>${easy-trans.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.fhs-opensource</groupId>
+                <artifactId>easy-trans-anno</artifactId>
+                <version>${easy-trans.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-redis</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.redisson</groupId>
+                <artifactId>redisson-spring-boot-starter</artifactId>
+                <version>${redisson.version}</version>
+            </dependency>
+
+            <!-- Test 测试相关 -->
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-test</artifactId>
+                <version>${ai.revision}</version>
+                <scope>test</scope>
+            </dependency>
+
+            <!-- RPC 远程调用相关 -->
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-rpc</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <!-- 监控相关 -->
+            <dependency>
+                <groupId>com.iailab</groupId>
+                <artifactId>iailab-common-monitor</artifactId>
+                <version>${ai.revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-all</artifactId>
+                <version>${hutool-5.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.dromara.hutool</groupId>
+                <artifactId>hutool-extra</artifactId>
+                <version>${hutool-6.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.skywalking</groupId>
+                <artifactId>apm-toolkit-trace</artifactId>
+                <version>${skywalking.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.skywalking</groupId>
+                <artifactId>apm-toolkit-logback-1.x</artifactId>
+                <version>${skywalking.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.skywalking</groupId>
+                <artifactId>apm-toolkit-opentracing</artifactId>
+                <version>${skywalking.version}</version>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <!-- maven-surefire-plugin 插件,用于运行单元测试。 -->
+                <!-- 注意,需要使用 3.0.X+,因为要支持 Junit 5 版本 -->
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                </plugin>
+                <!-- maven-compiler-plugin 插件,解决 Lombok + MapStruct 组合 -->
+                <!-- https://stackoverflow.com/questions/33483697/re-run-spring-boot-configuration-annotation-processor-to-update-generated-metada -->
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>${maven-compiler-plugin.version}</version>
+                    <configuration>
+                        <annotationProcessorPaths>
+                            <path>
+                                <groupId>org.springframework.boot</groupId>
+                                <artifactId>spring-boot-configuration-processor</artifactId>
+                                <version>${spring.boot.version}</version>
+                            </path>
+                            <path>
+                                <groupId>org.projectlombok</groupId>
+                                <artifactId>lombok</artifactId>
+                                <version>${lombok.version}</version>
+                            </path>
+                            <path>
+                                <groupId>org.mapstruct</groupId>
+                                <artifactId>mapstruct-processor</artifactId>
+                                <version>${mapstruct.version}</version>
+                            </path>
+                        </annotationProcessorPaths>
+                        <!-- 编译参数写在 arg 内,解决 Spring Boot 3.2 的 Parameter Name Discovery 问题 -->
+                        <debug>false</debug>
+                        <compilerArgs>
+                            <arg>-parameters</arg>
+                        </compilerArgs>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <!-- 统一 revision 版本 -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>flatten-maven-plugin</artifactId>
+                <version>${flatten-maven-plugin.version}</version>
+                <configuration>
+                    <flattenMode>oss</flattenMode>
+                    <updatePomFile>true</updatePomFile>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>flatten</goal>
+                        </goals>
+                        <id>flatten</id>
+                        <phase>process-resources</phase>
+                    </execution>
+                    <execution>
+                        <goals>
+                            <goal>clean</goal>
+                        </goals>
+                        <id>flatten.clean</id>
+                        <phase>clean</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file

--
Gitblit v1.9.3