Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<module>spring-ai-evaluation</module>
<module>spring-ai-chat-memory</module>
<module>spring-ai-tool-calling</module>
<module>spring-ai-agent</module>
</modules>

<properties>
Expand Down
23 changes: 23 additions & 0 deletions spring-ai-agent/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.glmapper</groupId>
<artifactId>spring-ai-summary</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>spring-ai-agent</artifactId>
<packaging>pom</packaging>
<name>spring-ai-agent</name>

<properties>
<java.version>21</java.version>
<spring-ai.version>1.0.0</spring-ai.version>
</properties>

<modules>
<module>spring-ai-workflow</module>
</modules>

</project>
1 change: 1 addition & 0 deletions spring-ai-agent/spring-ai-workflow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# todo 待补充
25 changes: 25 additions & 0 deletions spring-ai-agent/spring-ai-workflow/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.glmapper</groupId>
<artifactId>spring-ai-agent</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>spring-ai-workflow</artifactId>
<name>spring-ai-workflow</name>
<description>Spring AI Workflow Module</description>

<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<WorkflowStep> steps) {
return new ChainWorkflow(steps);
}

/**
* 并行工作流实现:同时执行多个工作流步骤,所有步骤使用相同的输入,最终结果是所有步骤结果的集合
*
* @param steps 工作流步骤
* @return 创建的工作流
*/
public Workflow createParallelizationWorkflow(List<WorkflowStep> steps) {
return new ParallelizationWorkflow(steps);
}

/**
* 路由工作流实现:基于路由规则选择合适的工作流步骤执行
*
* @param stepMap 工作流步骤
* @return 创建的工作流
*/
public Workflow createRoutingWorkflow(Map<String, WorkflowStep> stepMap) {
// 创建AI路由选择器
WorkflowStep routerSelector = workflowStepFactory.createAiRouterSelector("AI路由选择器", stepMap);
return new RoutingWorkflow(routerSelector, stepMap);
}
}
Original file line number Diff line number Diff line change
@@ -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<String, WorkflowStep> stepMap) {
return new RouterSelectorWorkflowStep(chatClient, stepMap, name);
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<String, WorkflowStep> stepMap;
private final String name;

public RouterSelectorWorkflowStep(ChatClient chatClient, Map<String, WorkflowStep> 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;
}

}
Original file line number Diff line number Diff line change
@@ -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);
}
Loading