| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.controller; |
| | | |
| | | import com.ruoyi.ai.assistant.SalesAgent; |
| | | import com.ruoyi.ai.assistant.SalesIntentExecutor; |
| | | import com.ruoyi.ai.bean.ChatForm; |
| | | import com.ruoyi.ai.context.AiSessionUserContext; |
| | | import com.ruoyi.ai.service.AiChatSessionService; |
| | | import com.ruoyi.ai.store.MongoChatMemoryStore; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import dev.langchain4j.data.message.AiMessage; |
| | | import dev.langchain4j.data.message.UserMessage; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import reactor.core.publisher.Flux; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.List; |
| | | |
| | | @Tag(name = "éå®å©ææºè½ä½") |
| | | @RestController |
| | | @RequestMapping("/sales-ai") |
| | | public class SalesAiController extends BaseController { |
| | | |
| | | private static final DateTimeFormatter CURRENT_DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | private static final ZoneId CHINA_ZONE_ID = ZoneId.of("Asia/Shanghai"); |
| | | |
| | | private final SalesAgent salesAgent; |
| | | private final SalesIntentExecutor salesIntentExecutor; |
| | | private final AiSessionUserContext aiSessionUserContext; |
| | | private final MongoChatMemoryStore mongoChatMemoryStore; |
| | | private final AiChatSessionService aiChatSessionService; |
| | | |
| | | public SalesAiController(SalesAgent salesAgent, |
| | | SalesIntentExecutor salesIntentExecutor, |
| | | AiSessionUserContext aiSessionUserContext, |
| | | MongoChatMemoryStore mongoChatMemoryStore, |
| | | AiChatSessionService aiChatSessionService) { |
| | | this.salesAgent = salesAgent; |
| | | this.salesIntentExecutor = salesIntentExecutor; |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | this.mongoChatMemoryStore = mongoChatMemoryStore; |
| | | this.aiChatSessionService = aiChatSessionService; |
| | | } |
| | | |
| | | @Operation(summary = "éå®å©æå¯¹è¯") |
| | | @PostMapping(value = "/chat", produces = "text/stream;charset=utf-8") |
| | | public Flux<String> chat(@RequestBody ChatForm chatForm) { |
| | | if (!StringUtils.hasText(chatForm.getMemoryId())) { |
| | | return Flux.just("memoryIdä¸è½ä¸ºç©º"); |
| | | } |
| | | if (!StringUtils.hasText(chatForm.getMessage())) { |
| | | return Flux.just("messageä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | String memoryId = chatForm.getMemoryId(); |
| | | String userMessage = chatForm.getMessage(); |
| | | |
| | | aiSessionUserContext.bind(memoryId, loginUser); |
| | | aiChatSessionService.touchSession(memoryId, loginUser, userMessage); |
| | | |
| | | String directResponse = salesIntentExecutor.tryExecute(memoryId, userMessage); |
| | | if (StringUtils.isNotEmpty(directResponse)) { |
| | | mongoChatMemoryStore.appendMessages( |
| | | memoryId, |
| | | List.of(UserMessage.from(userMessage), AiMessage.from(directResponse)) |
| | | ); |
| | | aiChatSessionService.refreshSessionStats(memoryId, loginUser); |
| | | return Flux.just(directResponse); |
| | | } |
| | | |
| | | if (isBusinessDataIntent(userMessage)) { |
| | | String noGuessResponse = "æªè¯å«å°å¯æ§è¡çæ°æ®æ¥è¯¢æ¡ä»¶ã为ä¿è¯ç»æåç¡®ï¼å½åä¸ä¼æ¨æµæç¼é æ°æ®ï¼è¯·è¡¥å
æç¡®æ¶é´èå´ãå®¢æ·æåå·ååæ¥è¯¢ã"; |
| | | mongoChatMemoryStore.appendMessages( |
| | | memoryId, |
| | | List.of(UserMessage.from(userMessage), AiMessage.from(noGuessResponse)) |
| | | ); |
| | | aiChatSessionService.refreshSessionStats(memoryId, loginUser); |
| | | return Flux.just(noGuessResponse); |
| | | } |
| | | |
| | | return salesAgent.chat(memoryId, userMessage, currentDateForPrompt()) |
| | | .doOnComplete(() -> aiChatSessionService.refreshSessionStats(memoryId, loginUser)) |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(memoryId, loginUser)); |
| | | } |
| | | |
| | | @Operation(summary = "éå®å©æä¼è¯å表") |
| | | @GetMapping("/history/sessions") |
| | | public R<?> listSessions() { |
| | | return R.ok(aiChatSessionService.listCurrentUserSessions(SecurityUtils.getLoginUser())); |
| | | } |
| | | |
| | | @Operation(summary = "éå®å©æä¼è¯æ¶æ¯") |
| | | @GetMapping("/history/messages/{memoryId}") |
| | | public R<?> listMessages(@PathVariable String memoryId) { |
| | | return R.ok(aiChatSessionService.listCurrentUserMessages(memoryId, SecurityUtils.getLoginUser())); |
| | | } |
| | | |
| | | @Operation(summary = "å é¤éå®å©æä¼è¯") |
| | | @DeleteMapping("/history/{memoryId}") |
| | | public R<?> deleteSession(@PathVariable String memoryId) { |
| | | aiSessionUserContext.remove(memoryId); |
| | | aiChatSessionService.deleteCurrentUserSession(memoryId, SecurityUtils.getLoginUser()); |
| | | return R.ok(); |
| | | } |
| | | |
| | | private boolean isBusinessDataIntent(String message) { |
| | | if (!StringUtils.hasText(message)) { |
| | | return false; |
| | | } |
| | | String text = message.trim(); |
| | | return containsAny(text, |
| | | "æ¥è¯¢", "æ¥ç", "ç»è®¡", "åæ", "建议", "å®¢æ·æ¡£æ¡", "ç§æµ·", "å
¬æµ·", |
| | | "é宿¥ä»·", "éå®å°è´¦", "éå®éè´§", "客æ·å¾æ¥", "åè´§å°è´¦", "忬¾", "æ¥ä»·", "é£é©"); |
| | | } |
| | | |
| | | private boolean containsAny(String text, String... keywords) { |
| | | for (String keyword : keywords) { |
| | | if (text.contains(keyword)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private String currentDateForPrompt() { |
| | | return LocalDate.now(CHINA_ZONE_ID).format(CURRENT_DATE_FMT); |
| | | } |
| | | } |