diff --git a/pom.xml b/pom.xml index d2674b3..36fa265 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ spring-ai-evaluation spring-ai-chat-memory spring-ai-tool-calling + spring-ai-agent diff --git a/spring-ai-agent/pom.xml b/spring-ai-agent/pom.xml new file mode 100644 index 0000000..00c241f --- /dev/null +++ b/spring-ai-agent/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + com.glmapper + spring-ai-summary + 0.0.1 + + spring-ai-agent + pom + spring-ai-agent + + + 21 + 1.0.0 + + + + spring-ai-workflow + + + \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/README.md b/spring-ai-agent/spring-ai-workflow/README.md new file mode 100644 index 0000000..4f81f1f --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/README.md @@ -0,0 +1 @@ +# todo 待补充 \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/pom.xml b/spring-ai-agent/spring-ai-workflow/pom.xml new file mode 100644 index 0000000..105ca73 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + com.glmapper + spring-ai-agent + 0.0.1 + + spring-ai-workflow + spring-ai-workflow + Spring AI Workflow Module + + + + org.springframework.ai + spring-ai-starter-model-openai + + + org.springframework.boot + spring-boot-starter-test + test + + + \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/WorkflowApplication.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/WorkflowApplication.java new file mode 100644 index 0000000..2632c86 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/WorkflowApplication.java @@ -0,0 +1,13 @@ +package com.glmapper.ai.workflow; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + + +@SpringBootApplication +public class WorkflowApplication { + + public static void main(String[] args) { + SpringApplication.run(WorkflowApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/config/OpenaiChatClientConfigs.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/config/OpenaiChatClientConfigs.java new file mode 100644 index 0000000..345fce7 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/config/OpenaiChatClientConfigs.java @@ -0,0 +1,30 @@ +package com.glmapper.ai.workflow.config; + +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * @Classname OpenaiChatClientConfigs + * @Description 注入 ChatClient + * + * @Date 2025/6/10 09:23 + * @Created by Gepeng18 + */ +@Configuration +public class OpenaiChatClientConfigs { + + /** + * 注入ChatClient + * + * @param chatModel + * @return + */ + @Bean + public ChatClient chatClient(OpenAiChatModel chatModel) { + return ChatClient.builder(chatModel) + .build(); + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/WorkflowFactory.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/WorkflowFactory.java new file mode 100644 index 0000000..ba07ee1 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/WorkflowFactory.java @@ -0,0 +1,58 @@ +package com.glmapper.ai.workflow.core; + +import com.glmapper.ai.workflow.core.workflow.Workflow; +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import com.glmapper.ai.workflow.core.workflow.impl.ChainWorkflow; +import com.glmapper.ai.workflow.core.workflow.impl.ParallelizationWorkflow; +import com.glmapper.ai.workflow.core.workflow.impl.RoutingWorkflow; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * @Classname WorkflowFactory + * @Description 工作流工厂 + * + * @Date 2025/6/10 15:40 + * @Created by Gepeng18 + */ +@Component +@AllArgsConstructor +public class WorkflowFactory { + + private final WorkflowStepFactory workflowStepFactory; + + /** + * 链式工作流实现:按顺序执行一系列工作流步骤,前一步骤的输出作为后一步骤的输入 + * + * @param steps 工作流步骤 + * @return 创建的工作流 + */ + public Workflow createChainWorkflow(List steps) { + return new ChainWorkflow(steps); + } + + /** + * 并行工作流实现:同时执行多个工作流步骤,所有步骤使用相同的输入,最终结果是所有步骤结果的集合 + * + * @param steps 工作流步骤 + * @return 创建的工作流 + */ + public Workflow createParallelizationWorkflow(List steps) { + return new ParallelizationWorkflow(steps); + } + + /** + * 路由工作流实现:基于路由规则选择合适的工作流步骤执行 + * + * @param stepMap 工作流步骤 + * @return 创建的工作流 + */ + public Workflow createRoutingWorkflow(Map stepMap) { + // 创建AI路由选择器 + WorkflowStep routerSelector = workflowStepFactory.createAiRouterSelector("AI路由选择器", stepMap); + return new RoutingWorkflow(routerSelector, stepMap); + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/WorkflowStepFactory.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/WorkflowStepFactory.java new file mode 100644 index 0000000..e4db288 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/WorkflowStepFactory.java @@ -0,0 +1,48 @@ +package com.glmapper.ai.workflow.core; + +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import com.glmapper.ai.workflow.core.step.impl.DefaultWorkflowStep; +import com.glmapper.ai.workflow.core.step.impl.RouterSelectorWorkflowStep; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * @Classname WorkflowStepFactory + * @Description 提供创建AI工作流步骤的功能 + * + * @Date 2025/6/10 15:40 + * @Created by Gepeng18 + */ +@Service +@Slf4j +@AllArgsConstructor +public class WorkflowStepFactory { + + private final ChatClient chatClient; + + /** + * 创建AI工作流步骤 + * + * @param name 步骤名称 + * @param promptTemplate 提示词模板 + * @return 工作流步骤 + */ + public WorkflowStep createAiStep(String name, String promptTemplate) { + return new DefaultWorkflowStep(name, promptTemplate, chatClient); + } + + /** + * 创建AI路由选择器 + * + * @param name 步骤名称 + * @param stepMap 步骤映射 + * @return AI路由选择器 + */ + public RouterSelectorWorkflowStep createAiRouterSelector(String name, Map stepMap) { + return new RouterSelectorWorkflowStep(chatClient, stepMap, name); + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/step/WorkflowStep.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/step/WorkflowStep.java new file mode 100644 index 0000000..f5d718e --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/step/WorkflowStep.java @@ -0,0 +1,27 @@ +package com.glmapper.ai.workflow.core.step; + + +/** + * @Classname WorkflowStep + * @Description 工作流步骤接口 + * + * @Date 2025/6/10 11:40 + * @Created by Gepeng18 + */ +public interface WorkflowStep { + + /** + * 执行步骤 + * + * @param input 输入数据 + * @return 步骤执行结果 + */ + Object execute(Object input); + + /** + * 获取步骤名称 + * + * @return 步骤名称 + */ + String name(); +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/step/impl/DefaultWorkflowStep.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/step/impl/DefaultWorkflowStep.java new file mode 100644 index 0000000..317a3f7 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/step/impl/DefaultWorkflowStep.java @@ -0,0 +1,35 @@ +package com.glmapper.ai.workflow.core.step.impl; + +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.prompt.Prompt; + + +/** + * @Classname DefaultWorkflowStep + * @Description 工作流步骤 + * + * @Date 2025/6/10 17:23 + * @Created by Gepeng18 + */ +public record DefaultWorkflowStep(String name, String promptTemplate, ChatClient chatClient) implements WorkflowStep { + + /** + * 执行本步骤 + * + * @param input 输入数据 + * @return 步骤执行结果 + */ + @Override + public Object execute(Object input) { + String inputStr = input != null ? input.toString() : ""; + Prompt prompt = new Prompt( + new SystemMessage(promptTemplate), + new UserMessage(inputStr) + ); + + return chatClient.prompt(prompt).call().content(); + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/step/impl/RouterSelectorWorkflowStep.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/step/impl/RouterSelectorWorkflowStep.java new file mode 100644 index 0000000..3bdf58a --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/step/impl/RouterSelectorWorkflowStep.java @@ -0,0 +1,80 @@ +package com.glmapper.ai.workflow.core.step.impl; + +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import com.glmapper.ai.workflow.model.WorkflowRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.prompt.Prompt; + +import javax.validation.constraints.Null; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @Classname RouterSelectorWorkflowStep + * @Description AI路由选择器实现WorkflowStep接口,用于根据用户问题选择合适的路由 + * + * @Date 2025/6/10 16:36 + * @Created by Gepeng18 + */ +@Slf4j +public class RouterSelectorWorkflowStep implements WorkflowStep { + + private final ChatClient chatClient; + private final Map stepMap; + private final String name; + + public RouterSelectorWorkflowStep(ChatClient chatClient, Map stepMap, String name) { + this.chatClient = chatClient; + this.stepMap = stepMap; + this.name = name; + } + + /** + * 执行步骤 + * + * @param input 输入数据 + * @return 步骤执行结果 + */ + @Null + @Override + public Object execute(Object input) { + if (!(input instanceof WorkflowRequest)) { + throw new IllegalArgumentException("Input must be of type WorkflowRequest"); + } + + WorkflowRequest request = (WorkflowRequest) input; + + // 构建提示文本 + String routeInfo = stepMap.entrySet().stream() + .map(entry -> "- " + entry.getKey() + ": " + entry.getValue().name()) + .collect(Collectors.joining("\n")); + + String promptTemplate = "你是一个专业的路由选择器。根据用户的问题,从以下可用的路由中选择最合适的一个:\n\n" + + "可用路由:\n" + routeInfo + "\n\n" + + "请仅返回最合适的路由的键名,不要包含任何额外解释。例如,如果最合适的路由是\"technical\",只需返回\"technical\"。"; + + // 创建并发送提示 + Prompt prompt = new Prompt( + new SystemMessage(promptTemplate), + new UserMessage(request.getQuestion()) + ); + + String routeKey = chatClient.prompt(prompt).call().content().trim(); + + // 确保获取到的是有效路由 + if (!stepMap.containsKey(routeKey)) { + return null; + } + + return routeKey; + } + + @Override + public String name() { + return name; + } + +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/Workflow.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/Workflow.java new file mode 100644 index 0000000..bf032fe --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/Workflow.java @@ -0,0 +1,23 @@ +package com.glmapper.ai.workflow.core.workflow; + +import com.glmapper.ai.workflow.model.WorkflowRequest; +import com.glmapper.ai.workflow.model.WorkflowResponse; + + +/** + * @Classname Workflow + * @Description 工作流核心接口 + * + * @Date 2025/6/10 10:21 + * @Created by Gepeng18 + */ +public interface Workflow { + + /** + * 执行本工作流 + * + * @param input 输入的请求 + * @return 工作流执行结果 + */ + WorkflowResponse execute(WorkflowRequest input); +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/impl/ChainWorkflow.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/impl/ChainWorkflow.java new file mode 100644 index 0000000..be3c2ec --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/impl/ChainWorkflow.java @@ -0,0 +1,54 @@ +package com.glmapper.ai.workflow.core.workflow.impl; + +import com.glmapper.ai.workflow.model.WorkflowRequest; +import com.glmapper.ai.workflow.model.WorkflowResponse; +import com.glmapper.ai.workflow.core.workflow.Workflow; +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + + +/** + * @Classname ChainWorkflow + * @Description 链式工作流实现:按顺序执行一系列工作流步骤,前一步骤的输出作为后一步骤的输入 + * + * @Date 2025/6/10 14:21 + * @Created by Gepeng18 + */ +@Slf4j +public class ChainWorkflow implements Workflow { + + private final List steps; + + public ChainWorkflow(List steps) { + this.steps = steps; + } + + @Override + public WorkflowResponse execute(WorkflowRequest input) { + Object currentInput = input.getQuestion(); + + try { + log.info("开始执行链式工作流, 步骤数量: {}", steps.size()); + + for (WorkflowStep step : steps) { + log.info("执行步骤: {}, 模型输入:{}", step.name(), currentInput); + currentInput = step.execute(currentInput); + } + + log.info("链式工作流执行完成"); + return WorkflowResponse.builder() + .content(currentInput != null ? currentInput.toString() : null) + .success(true) + .build(); + + } catch (Exception e) { + log.error("链式工作流执行失败", e); + return WorkflowResponse.builder() + .success(false) + .errorMessage("工作流执行失败: " + e.getMessage()) + .build(); + } + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/impl/ParallelizationWorkflow.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/impl/ParallelizationWorkflow.java new file mode 100644 index 0000000..e9ff00e --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/impl/ParallelizationWorkflow.java @@ -0,0 +1,97 @@ +package com.glmapper.ai.workflow.core.workflow.impl; + +import com.glmapper.ai.workflow.model.WorkflowRequest; +import com.glmapper.ai.workflow.model.WorkflowResponse; +import com.glmapper.ai.workflow.core.workflow.Workflow; +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + + +/** + * @Classname ParallelizationWorkflow + * @Description 并行工作流实现:同时执行多个工作流步骤,所有步骤使用相同的输入,最终结果是所有步骤结果的集合 + * + * @Date 2025/6/10 14:27 + * @Created by Gepeng18 + */ +@Slf4j +public class ParallelizationWorkflow implements Workflow { + + private final List steps; + + public ParallelizationWorkflow(List steps) { + this.steps = steps; + } + + @Override + public WorkflowResponse execute(WorkflowRequest input) { + try { + log.info("开始执行并行工作流, 步骤数量: {}", steps.size()); + + List>> futures = new ArrayList<>(); + + // 为每个步骤创建一个异步任务 + for (WorkflowStep step : steps) { + CompletableFuture> future = CompletableFuture.supplyAsync(() -> { + log.info("执行步骤: {}", step.name()); + Object result = step.execute(input.getQuestion()); + return Map.entry(step.name(), result); + }); + futures.add(future); + } + + // 等待所有异步任务完成 + CompletableFuture allFutures = CompletableFuture.allOf( + futures.toArray(new CompletableFuture[0]) + ); + + // 收集所有结果 + CompletableFuture> resultFuture = allFutures.thenApply(v -> + futures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) + ); + + Map results = resultFuture.get(); + String content = formatResults(results); + log.info("并行工作流执行完成,结果数量: {}, 执行结果为:\n {}", results.size(), content); + + return WorkflowResponse.builder() + .success(true) + .content(content) + .build(); + + } catch (InterruptedException | ExecutionException e) { + log.error("并行工作流执行失败", e); + return WorkflowResponse.builder() + .success(false) + .errorMessage("工作流执行失败: " + e.getMessage()) + .build(); + } + } + + /** + * 格式化结果为可读字符串 + * + * @param results 结果映射 + * @return 格式化的结果字符串 + */ + private String formatResults(Map results) { + StringBuilder sb = new StringBuilder(); + sb.append("并行工作流执行结果:\n"); + + results.forEach((key, value) -> { + sb.append("步骤 [").append(key).append("]: \n"); + sb.append(value != null ? value.toString() : "null").append("\n\n"); + }); + + return sb.toString(); + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/impl/RoutingWorkflow.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/impl/RoutingWorkflow.java new file mode 100644 index 0000000..6c5a78c --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/core/workflow/impl/RoutingWorkflow.java @@ -0,0 +1,68 @@ +package com.glmapper.ai.workflow.core.workflow.impl; + +import com.glmapper.ai.workflow.model.WorkflowRequest; +import com.glmapper.ai.workflow.model.WorkflowResponse; +import com.glmapper.ai.workflow.core.workflow.Workflow; +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +/** + * @Classname RoutingWorkflow + * @Description 路由工作流实现:基于路由规则选择合适的工作流步骤执行 + * + * @Date 2025/6/10 16:36 + * @Created by Gepeng18 + */ +@Slf4j +public class RoutingWorkflow implements Workflow { + + private final WorkflowStep routerSelector; + private final Map stepMap; + + public RoutingWorkflow(WorkflowStep routerSelector, Map stepMap) { + this.routerSelector = routerSelector; + this.stepMap = stepMap; + } + + @Override + public WorkflowResponse execute(WorkflowRequest input) { + try { + log.info("开始执行路由工作流, 路由规则数量: {}", stepMap.size()); + + // 使用路由选择器确定最合适的路由 + String routeKey = (String) routerSelector.execute(input); + log.info("路由结果: {}", routeKey); + + // 查找对应的步骤 + WorkflowStep step = stepMap.get(routeKey); + + if (step == null) { + log.error("未找到路由对应的步骤: {}", routeKey); + return WorkflowResponse.builder() + .success(false) + .errorMessage("未找到路由对应的步骤: " + routeKey) + .build(); + } + + // 执行找到的步骤 + log.info("执行步骤: {}", step.name()); + Object result = step.execute(input.getQuestion()); + + log.info("路由工作流执行完成, 执行结果为: \n {}", result); + + return WorkflowResponse.builder() + .content(result != null ? result.toString() : null) + .success(true) + .build(); + + } catch (Exception e) { + log.error("路由工作流执行失败", e); + return WorkflowResponse.builder() + .success(false) + .errorMessage("工作流执行失败: " + e.getMessage()) + .build(); + } + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/model/WorkflowRequest.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/model/WorkflowRequest.java new file mode 100644 index 0000000..4c85398 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/model/WorkflowRequest.java @@ -0,0 +1,22 @@ +package com.glmapper.ai.workflow.model; + +import lombok.Builder; +import lombok.Data; + +/** + * @Classname WorkflowRequest + * @Description 用户请求 + * + * @Date 2025/6/10 11:23 + * @Created by Gepeng18 + */ +@Data +@Builder +public class WorkflowRequest { + + /** + * 请求内容 + */ + private String question; + +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/model/WorkflowResponse.java b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/model/WorkflowResponse.java new file mode 100644 index 0000000..7d60646 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/java/com/glmapper/ai/workflow/model/WorkflowResponse.java @@ -0,0 +1,32 @@ +package com.glmapper.ai.workflow.model; + +import lombok.Builder; +import lombok.Data; + +/** + * @Classname WorkflowResponse + * @Description 工作流响应 + * + * @Date 2025/6/10 14:32 + * @Created by Gepeng18 + */ +@Data +@Builder +public class WorkflowResponse { + + /** + * 响应内容 + */ + private String content; + + /** + * 是否成功 + */ + private boolean success; + + /** + * 错误信息 + */ + private String errorMessage; + +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/main/resources/application.yml b/spring-ai-agent/spring-ai-workflow/src/main/resources/application.yml new file mode 100644 index 0000000..208dcc9 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/main/resources/application.yml @@ -0,0 +1,14 @@ +server: + port: 8080 + +spring: + application: + name: spring-ai-workflow + ai: + openai: + api-key: ${OPENAI_API_KEY} + base-url: ${OPENAI_BASE_URL} + chat: + options: + model: gpt-4 + temperature: 0.7 diff --git a/spring-ai-agent/spring-ai-workflow/src/test/java/com/glmapper/ai/workflow/ChainWorkflowTest.java b/spring-ai-agent/spring-ai-workflow/src/test/java/com/glmapper/ai/workflow/ChainWorkflowTest.java new file mode 100644 index 0000000..329e7a7 --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/test/java/com/glmapper/ai/workflow/ChainWorkflowTest.java @@ -0,0 +1,64 @@ +package com.glmapper.ai.workflow; + +import com.glmapper.ai.workflow.model.WorkflowRequest; +import com.glmapper.ai.workflow.model.WorkflowResponse; +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import com.glmapper.ai.workflow.core.WorkflowFactory; +import com.glmapper.ai.workflow.core.WorkflowStepFactory; +import com.glmapper.ai.workflow.core.workflow.Workflow; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest +public class ChainWorkflowTest { + + @Autowired + private WorkflowStepFactory workflowStepFactory; + + @Autowired + private WorkflowFactory workflowFactory; + + @Test + @DisplayName("演示链式工作流基本用法") + void demonstrateChainWorkflowUsage() { + // 创建工作流步骤 + WorkflowStep outlineStep = workflowStepFactory.createAiStep( + "大纲生成", + "根据用户提供的主题,生成一个详细的内容大纲,包括引言、主要部分和结论。" + ); + + WorkflowStep expandStep = workflowStepFactory.createAiStep( + "内容扩写", + "根据提供的大纲,创作一篇完整的文章。" + ); + + WorkflowStep polishStep = workflowStepFactory.createAiStep( + "润色优化", + "对提供的文章进行润色和优化,改进语言表达。" + ); + + // 创建链式工作流 + List steps = Arrays.asList(outlineStep, expandStep, polishStep); + Workflow chainWorkflow = workflowFactory.createChainWorkflow(steps); + + // 执行工作流 + WorkflowRequest request = WorkflowRequest.builder() + .question("人工智能在医疗领域的应用") + .build(); + + WorkflowResponse response = chainWorkflow.execute(request); + + // 验证响应 + assertNotNull(response); + assertTrue(response.isSuccess()); + assertNotNull(response.getContent()); + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/test/java/com/glmapper/ai/workflow/ParallelizationWorkflowTest.java b/spring-ai-agent/spring-ai-workflow/src/test/java/com/glmapper/ai/workflow/ParallelizationWorkflowTest.java new file mode 100644 index 0000000..deca1eb --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/test/java/com/glmapper/ai/workflow/ParallelizationWorkflowTest.java @@ -0,0 +1,66 @@ +package com.glmapper.ai.workflow; + +import com.glmapper.ai.workflow.model.WorkflowRequest; +import com.glmapper.ai.workflow.model.WorkflowResponse; +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import com.glmapper.ai.workflow.core.WorkflowFactory; +import com.glmapper.ai.workflow.core.WorkflowStepFactory; +import com.glmapper.ai.workflow.core.workflow.Workflow; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest +public class ParallelizationWorkflowTest { + + @Autowired + private WorkflowStepFactory workflowStepFactory; + + @Autowired + private WorkflowFactory workflowFactory; + + @Test + @DisplayName("演示并行工作流基本用法") + void demonstrateParallelWorkflowUsage() { + // 创建工作流步骤 + WorkflowStep marketingStep = workflowStepFactory.createAiStep( + "市场营销评估", + "从市场营销角度对产品进行评估,包括市场定位和目标受众分析" + ); + + WorkflowStep productManagerStep = workflowStepFactory.createAiStep( + "产品经理评估", + "从产品管理角度对产品进行评估,分析产品定位和功能" + ); + + WorkflowStep uxDesignerStep = workflowStepFactory.createAiStep( + "用户体验评估", + "从用户体验角度对产品进行评估,分析界面设计和交互流程" + ); + + // 创建并行工作流 + List steps = Arrays.asList( + marketingStep, productManagerStep, uxDesignerStep + ); + Workflow parallelWorkflow = workflowFactory.createParallelizationWorkflow(steps); + + // 执行工作流 + WorkflowRequest request = WorkflowRequest.builder() + .question("智能家居控制系统,通过手机App控制家中设备") + .build(); + + WorkflowResponse response = parallelWorkflow.execute(request); + + // 验证响应 + assertNotNull(response); + assertTrue(response.isSuccess()); + assertNotNull(response.getContent()); + } +} \ No newline at end of file diff --git a/spring-ai-agent/spring-ai-workflow/src/test/java/com/glmapper/ai/workflow/RoutingWorkflowTest.java b/spring-ai-agent/spring-ai-workflow/src/test/java/com/glmapper/ai/workflow/RoutingWorkflowTest.java new file mode 100644 index 0000000..f10dadb --- /dev/null +++ b/spring-ai-agent/spring-ai-workflow/src/test/java/com/glmapper/ai/workflow/RoutingWorkflowTest.java @@ -0,0 +1,82 @@ +package com.glmapper.ai.workflow; + +import com.glmapper.ai.workflow.model.WorkflowRequest; +import com.glmapper.ai.workflow.model.WorkflowResponse; +import com.glmapper.ai.workflow.core.step.WorkflowStep; +import com.glmapper.ai.workflow.core.WorkflowFactory; +import com.glmapper.ai.workflow.core.WorkflowStepFactory; +import com.glmapper.ai.workflow.core.workflow.Workflow; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +@SpringBootTest +public class RoutingWorkflowTest { + + @Autowired + private WorkflowStepFactory workflowStepFactory; + + @Autowired + private WorkflowFactory workflowFactory; + + @Test + @DisplayName("演示传统路由工作流基本用法 - 使用函数进行路由") + void demonstrateLegacyRoutingWorkflowUsage() { + + // 创建各种专门的处理步骤,这里主要采用mock数据 + Map stepMap = new HashMap<>(); + + // 账单查询处理步骤 + stepMap.put("billing", workflowStepFactory.createAiStep( + "账单查询处理", + "如果用户问你关于账单的问题,你就回答:存在两个账单。其他内容不需要回答。" + )); + + // 技术支持步骤 + stepMap.put("technical", workflowStepFactory.createAiStep( + "技术支持处理", + "如果用户问你有关技术支持的问题,你就回答:电脑该重启了。其他内容不需要回答。" + )); + + // 产品信息步骤 + stepMap.put("product", workflowStepFactory.createAiStep( + "产品信息处理", + "如果用户问你关于产品信息的问题,你就回答:这个产品还不错。其他内容不需要回答。" + )); + + // 订单状态步骤 + stepMap.put("order", workflowStepFactory.createAiStep( + "订单状态处理", + "如果用户问你关于订单状态的问题,你就回答:没有订单。其他内容不需要回答。" + )); + + // 通用查询步骤 + stepMap.put("general", workflowStepFactory.createAiStep( + "通用查询处理", + "如果用户问你通用的问题,你就回答:通用问题请查询搜索引擎。其他内容不需要回答。" + )); + + // 创建路由工作流 + Workflow routingWorkflow = workflowFactory.createRoutingWorkflow(stepMap); + + // 执行工作流 + WorkflowRequest request = WorkflowRequest.builder() + .question("我想查看我的账单详情") + .build(); + + WorkflowResponse response = routingWorkflow.execute(request); + + // 验证响应 + assertNotNull(response); + assertTrue(response.isSuccess()); + assertNotNull(response.getContent()); + } +} \ No newline at end of file