This commit is contained in:
jack ning
2025-09-26 15:33:18 +08:00
parent ceb9c33c0a
commit bc6287d2eb
3 changed files with 140 additions and 136 deletions

View File

@@ -29,6 +29,10 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
/**
* https://spring.io/blog/2025/09/16/spring-ai-mcp-intro-blog
* McpServer Management Controller - Content mcpServer management and categorization APIs
*/
@RestController
@RequestMapping("/api/v1/mcp/server")
@AllArgsConstructor

View File

@@ -13,77 +13,77 @@
*/
package com.bytedesk.ai.workflow.alibaba;
import java.util.HashMap;
import java.util.Map;
// import java.util.HashMap;
// import java.util.Map;
import com.alibaba.cloud.ai.graph.CompileConfig;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.action.EdgeAction;
import com.alibaba.cloud.ai.graph.observation.GraphObservationLifecycleListener;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j;
// import com.alibaba.cloud.ai.graph.CompileConfig;
// import com.alibaba.cloud.ai.graph.CompiledGraph;
// import com.alibaba.cloud.ai.graph.exception.GraphStateException;
// import com.alibaba.cloud.ai.graph.OverAllState;
// import com.alibaba.cloud.ai.graph.StateGraph;
// import com.alibaba.cloud.ai.graph.action.EdgeAction;
// import com.alibaba.cloud.ai.graph.observation.GraphObservationLifecycleListener;
// import io.micrometer.observation.ObservationRegistry;
// import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// import org.springframework.beans.factory.ObjectProvider;
// import org.springframework.beans.factory.annotation.Qualifier;
// import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
// import org.springframework.web.bind.annotation.GetMapping;
// import org.springframework.web.bind.annotation.RequestMapping;
// import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/customer")
@ConditionalOnProperty(prefix = "spring.ai.workflow.graph", name = "enabled", havingValue = "true", matchIfMissing = false)
public class CustomerServiceController {
// @Slf4j
// @RestController
// @RequestMapping("/customer")
// @ConditionalOnProperty(prefix = "spring.ai.workflow.graph", name = "enabled", havingValue = "true", matchIfMissing = false)
// public class CustomerServiceController {
private CompiledGraph compiledGraph;
// private CompiledGraph compiledGraph;
public CustomerServiceController(@Qualifier("workflowGraph") StateGraph stateGraph,
ObjectProvider<ObservationRegistry> observationRegistry) throws GraphStateException {
this.compiledGraph = stateGraph.compile(CompileConfig.builder()
.withLifecycleListener(new GraphObservationLifecycleListener(
observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP)))
.build());
}
// public CustomerServiceController(@Qualifier("workflowGraph") StateGraph stateGraph,
// ObjectProvider<ObservationRegistry> observationRegistry) throws GraphStateException {
// this.compiledGraph = stateGraph.compile(CompileConfig.builder()
// .withLifecycleListener(new GraphObservationLifecycleListener(
// observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP)))
// .build());
// }
// http://localhost:9003/customer/chat?query=我收到的产品有快递破损,需要退换货?
// http://localhost:9003/customer/chat?query=我的产品不能正常工作了,要怎么去做维修?
// http://localhost:9003/customer/chat?query=商品收到了,非常好,下次还会买。
@GetMapping("/chat")
public String simpleChat(String query) throws Exception {
return compiledGraph.invoke(Map.of("input", query)).get().value("solution").get().toString();
}
// // http://localhost:9003/customer/chat?query=我收到的产品有快递破损,需要退换货?
// // http://localhost:9003/customer/chat?query=我的产品不能正常工作了,要怎么去做维修?
// // http://localhost:9003/customer/chat?query=商品收到了,非常好,下次还会买。
// @GetMapping("/chat")
// public String simpleChat(String query) throws Exception {
// return compiledGraph.invoke(Map.of("input", query)).get().value("solution").get().toString();
// }
public static class FeedbackQuestionDispatcher implements EdgeAction {
@Override
public String apply(OverAllState state) throws Exception {
String classifierOutput = (String) state.value("classifier_output").orElse("");
log.info("classifierOutput: {}", classifierOutput);
if (classifierOutput.contains("positive")) {
return "positive";
}
return "negative";
}
}
// public static class FeedbackQuestionDispatcher implements EdgeAction {
// @Override
// public String apply(OverAllState state) throws Exception {
// String classifierOutput = (String) state.value("classifier_output").orElse("");
// log.info("classifierOutput: {}", classifierOutput);
// if (classifierOutput.contains("positive")) {
// return "positive";
// }
// return "negative";
// }
// }
public static class SpecificQuestionDispatcher implements EdgeAction {
@Override
public String apply(OverAllState state) throws Exception {
String classifierOutput = (String) state.value("classifier_output").orElse("");
log.info("classifierOutput: {}", classifierOutput);
Map<String, String> classifierMap = new HashMap<>();
classifierMap.put("after-sale", "after-sale");
classifierMap.put("quality", "quality");
classifierMap.put("transportation", "transportation");
for (Map.Entry<String, String> entry : classifierMap.entrySet()) {
if (classifierOutput.contains(entry.getKey())) {
return entry.getValue();
}
}
return "others";
}
}
}
// public static class SpecificQuestionDispatcher implements EdgeAction {
// @Override
// public String apply(OverAllState state) throws Exception {
// String classifierOutput = (String) state.value("classifier_output").orElse("");
// log.info("classifierOutput: {}", classifierOutput);
// Map<String, String> classifierMap = new HashMap<>();
// classifierMap.put("after-sale", "after-sale");
// classifierMap.put("quality", "quality");
// classifierMap.put("transportation", "transportation");
// for (Map.Entry<String, String> entry : classifierMap.entrySet()) {
// if (classifierOutput.contains(entry.getKey())) {
// return entry.getValue();
// }
// }
// return "others";
// }
// }
// }

View File

@@ -13,88 +13,88 @@
*/
package com.bytedesk.ai.workflow.alibaba;
import com.alibaba.cloud.ai.graph.GraphRepresentation;
import com.alibaba.cloud.ai.graph.KeyStrategy;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.node.QuestionClassifierNode;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
// import com.alibaba.cloud.ai.graph.GraphRepresentation;
// import com.alibaba.cloud.ai.graph.KeyStrategy;
// import com.alibaba.cloud.ai.graph.StateGraph;
// import com.alibaba.cloud.ai.graph.exception.GraphStateException;
// import com.alibaba.cloud.ai.graph.node.QuestionClassifierNode;
// import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import lombok.extern.slf4j.Slf4j;
// import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// import org.springframework.ai.chat.client.ChatClient;
// import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
// import org.springframework.ai.chat.model.ChatModel;
// import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
// import org.springframework.context.annotation.Bean;
// import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// import java.util.HashMap;
// import java.util.List;
// import java.util.Map;
import static com.alibaba.cloud.ai.graph.StateGraph.END;
import static com.alibaba.cloud.ai.graph.StateGraph.START;
import static com.alibaba.cloud.ai.graph.action.AsyncEdgeAction.edge_async;
import static com.alibaba.cloud.ai.graph.action.AsyncNodeAction.node_async;
// import static com.alibaba.cloud.ai.graph.StateGraph.END;
// import static com.alibaba.cloud.ai.graph.StateGraph.START;
// import static com.alibaba.cloud.ai.graph.action.AsyncEdgeAction.edge_async;
// import static com.alibaba.cloud.ai.graph.action.AsyncNodeAction.node_async;
/**
*
*/
@Slf4j
@Configuration
@ConditionalOnProperty(prefix = "spring.ai.workflow.graph", name = "enabled", havingValue = "true", matchIfMissing = false)
public class WorkflowAutoConfiguration {
// /**
// *
// */
// @Slf4j
// @Configuration
// @ConditionalOnProperty(prefix = "spring.ai.workflow.graph", name = "enabled", havingValue = "true", matchIfMissing = false)
// public class WorkflowAutoConfiguration {
@Bean
public StateGraph workflowGraph(ChatModel chatModel) throws GraphStateException {
// @Bean
// public StateGraph workflowGraph(ChatModel chatModel) throws GraphStateException {
ChatClient chatClient = ChatClient.builder(chatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
// ChatClient chatClient = ChatClient.builder(chatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
QuestionClassifierNode feedbackClassifier = QuestionClassifierNode.builder()
.chatClient(chatClient)
.inputTextKey("input")
.outputKey("classifier_output")
.categories(List.of("positive feedback", "negative feedback"))
.classificationInstructions(
List.of("Try to understand the user's feeling when he/she is giving the feedback."))
.build();
// QuestionClassifierNode feedbackClassifier = QuestionClassifierNode.builder()
// .chatClient(chatClient)
// .inputTextKey("input")
// .outputKey("classifier_output")
// .categories(List.of("positive feedback", "negative feedback"))
// .classificationInstructions(
// List.of("Try to understand the user's feeling when he/she is giving the feedback."))
// .build();
QuestionClassifierNode specificQuestionClassifier = QuestionClassifierNode.builder()
.chatClient(chatClient)
.inputTextKey("input")
.outputKey("classifier_output")
.categories(List.of("after-sale service", "transportation", "product quality", "others"))
.classificationInstructions(List
.of("What kind of service or help the customer is trying to get from us? Classify the question based on your understanding."))
.build();
// QuestionClassifierNode specificQuestionClassifier = QuestionClassifierNode.builder()
// .chatClient(chatClient)
// .inputTextKey("input")
// .outputKey("classifier_output")
// .categories(List.of("after-sale service", "transportation", "product quality", "others"))
// .classificationInstructions(List
// .of("What kind of service or help the customer is trying to get from us? Classify the question based on your understanding."))
// .build();
StateGraph stateGraph = new StateGraph("Consumer Service Workflow Demo", () -> {
Map<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("input", new ReplaceStrategy());
strategies.put("classifier_output", new ReplaceStrategy());
strategies.put("solution", new ReplaceStrategy());
return strategies;
}).addNode("feedback_classifier", node_async(feedbackClassifier))
.addNode("specific_question_classifier", node_async(specificQuestionClassifier))
.addNode("recorder", node_async(new RecordingNode()))
// StateGraph stateGraph = new StateGraph("Consumer Service Workflow Demo", () -> {
// Map<String, KeyStrategy> strategies = new HashMap<>();
// strategies.put("input", new ReplaceStrategy());
// strategies.put("classifier_output", new ReplaceStrategy());
// strategies.put("solution", new ReplaceStrategy());
// return strategies;
// }).addNode("feedback_classifier", node_async(feedbackClassifier))
// .addNode("specific_question_classifier", node_async(specificQuestionClassifier))
// .addNode("recorder", node_async(new RecordingNode()))
.addEdge(START, "feedback_classifier")
.addConditionalEdges("feedback_classifier",
edge_async(new CustomerServiceController.FeedbackQuestionDispatcher()),
Map.of("positive", "recorder", "negative", "specific_question_classifier"))
.addConditionalEdges("specific_question_classifier",
edge_async(new CustomerServiceController.SpecificQuestionDispatcher()),
Map.of("after-sale", "recorder", "transportation", "recorder", "quality", "recorder", "others",
"recorder"))
.addEdge("recorder", END);
// .addEdge(START, "feedback_classifier")
// .addConditionalEdges("feedback_classifier",
// edge_async(new CustomerServiceController.FeedbackQuestionDispatcher()),
// Map.of("positive", "recorder", "negative", "specific_question_classifier"))
// .addConditionalEdges("specific_question_classifier",
// edge_async(new CustomerServiceController.SpecificQuestionDispatcher()),
// Map.of("after-sale", "recorder", "transportation", "recorder", "quality", "recorder", "others",
// "recorder"))
// .addEdge("recorder", END);
GraphRepresentation graphRepresentation = stateGraph.getGraph(GraphRepresentation.Type.PLANTUML,
"workflow graph");
// GraphRepresentation graphRepresentation = stateGraph.getGraph(GraphRepresentation.Type.PLANTUML,
// "workflow graph");
log.info("workflow graph: {}", graphRepresentation.content());
// log.info("workflow graph: {}", graphRepresentation.content());
return stateGraph;
}
// return stateGraph;
// }
}
// }