@@ -1,9 +1,12 @@
package com.lanyuanxiaoyao.service.ai.chat.controller ;
import cn.hutool.core.util.ObjectUtil ;
import cn.hutool.core.util.StrUtil ;
import com.lanyuanxiaoyao.service.ai.chat.entity.MessageVO ;
import com.lanyuanxiaoyao.service.ai.chat.tools.DatetimeTools ;
import com.lanyuanxiaoyao.service.forest.service.KnowledgeService ;
import java.io.IOException ;
import java.util.Optional ;
import org.eclipse.collections.api.list.ImmutableList ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
@@ -11,10 +14,14 @@ import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.AssistantMessage ;
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.model.Generation ;
import org.springframework.ai.deepseek.DeepSeekAssistantMessage ;
import org.springframework.stereotype.Controller ;
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.RequestParam ;
import org.springframework.web.bind.annotation.ResponseBody ;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter ;
@@ -28,26 +35,42 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@RequestMapping ( " chat " )
public class ChatController {
private static final Logger logger = LoggerFactory . getLogger ( ChatController . class ) ;
private static final String ROLE_ASSISTANT = " assistant " ;
private static final String ROLE_USER = " user " ;
private final ChatClient chatClient ;
private final KnowledgeService knowledgeService ;
public ChatController ( ChatClient . Builder builder ) {
@SuppressWarnings ( " SpringJavaInjectionPointsAutowiringInspection " )
public ChatController ( ChatClient . Builder builder , KnowledgeService knowledgeService ) {
this . chatClient = builder . build ( ) ;
this . knowledgeService = knowledgeService ;
}
private ChatClient . ChatClientRequestSpec buildRequest ( ImmutableList < MessageVO > messages ) {
private ChatClient . ChatClientRequestSpec buildRequest ( Long knowledgeId , ImmutableList< MessageVO > messages ) {
var systemPromptBuilder = new StringBuilder ( ) ;
systemPromptBuilder . append ( """
你是一名专业的AI运维助手, 负责“Hudi数据同步服务平台”的运维工作;
你将会友好地帮助用户解答关于该平台运维工作的问题,你会尽可能通过各种方式获取知识和数据来解答;
对于无法通过已有知识回答的问题,你会提示用户你无法解答该问题,而不是虚构不存在的数据或答案;
对于与该平台无关的问题,你会委婉地拒绝用户,并提示无法回答;
你将始终在中文语境下进行对话。
""" ) ;
if ( ObjectUtil . isNotNull ( knowledgeId ) ) {
var vo = messages . select ( message - > StrUtil . equals ( message . getRole ( ) , " user " ) ) . getLastOptional ( ) . orElseThrow ( ) ;
var documents = knowledgeService . query ( knowledgeId , vo . getContent ( ) ) ;
logger . info ( " Knowledge id:{}, content:{} " , knowledgeId , vo . getContent ( ) ) ;
if ( ObjectUtil . isNotEmpty ( documents ) ) {
systemPromptBuilder . append ( " 以下是与用户问题有关的外部知识,优先利用该知识回答用户的提问: \ n " ) ;
systemPromptBuilder . append ( documents . makeString ( " \ n " ) ) ;
}
}
return chatClient . prompt ( )
. system ( """
你是一名专业的AI运维助手, 负责“Hudi数据同步服务平台”的运维工作;
你将会友好地帮助用户解答关于该平台运维工作的问题,你会尽可能通过各种方式获取知识和数据来解答;
对于无法通过已有知识回答的问题,你会提示用户你无法解答该问题,而不是虚构不存在的数据或答案;
对于与该平台无关的问题,你会委婉地拒绝用户,并提示无法回答;
你将始终在中文语境下进行对话。
""" )
. system ( systemPromptBuilder . toString ( ) )
. tools ( new DatetimeTools ( ) )
. messages (
messages
. collect ( message - > StrUtil . equals ( message . getRole ( ) , " assistant " )
. collect ( message - > StrUtil . equals ( message . getRole ( ) , ROLE_ASSISTANT )
? new AssistantMessage ( message . getContent ( ) )
: new UserMessage ( message . getContent ( ) ) )
. collect ( message - > ( Message ) message )
@@ -57,23 +80,29 @@ public class ChatController {
@PostMapping ( " sync " )
@ResponseBody
public String chatSync ( @RequestBody ImmutableList < MessageVO > messages ) {
String content = buildRequest ( messages )
public MessageVO chatSync (
@RequestParam ( value = " knowledge_id " , required = false ) Long knowledgeId ,
@RequestBody ImmutableList < MessageVO > messages
) {
ChatResponse response = buildRequest ( knowledgeId , messages )
. call ( )
. content ( ) ;
return StrUtil . trimToEmpty ( content ) ;
. chatResponse ( ) ;
return toMessage ( response ) ;
}
@PostMapping ( " async " )
public SseEmitter chatAsync ( @RequestBody ImmutableList < MessageVO > messages ) {
public SseEmitter chatAsync (
@RequestParam ( value = " knowledge_id " , required = false ) Long knowledgeId ,
@RequestBody ImmutableList < MessageVO > messages
) {
SseEmitter emitter = new SseEmitter ( ) ;
buildRequest ( messages )
buildRequest ( knowledgeId , messages)
. stream ( )
. content ( )
. chatResponse ( )
. subscribe (
content - > {
response - > {
try {
emitter . send ( content ) ;
emitter . send ( toMessage ( response ) ) ;
} catch ( IOException e ) {
emitter . completeWithError ( e ) ;
throw new RuntimeException ( e ) ;
@@ -84,4 +113,18 @@ public class ChatController {
) ;
return emitter ;
}
private MessageVO toMessage ( ChatResponse response ) {
AssistantMessage message = Optional . ofNullable ( response )
. map ( ChatResponse : : getResult )
. map ( Generation : : getOutput )
. orElseThrow ( ( ) - > new RuntimeException ( " ChatResponse is null " ) ) ;
MessageVO vo = new MessageVO ( ) ;
vo . setRole ( ROLE_ASSISTANT ) ;
vo . setContent ( message . getText ( ) ) ;
if ( message instanceof DeepSeekAssistantMessage deepseekMessage ) {
vo . setReason ( deepseekMessage . getReasoningContent ( ) ) ;
}
return vo ;
}
}