🚀 欢迎来到我的CSDN博客:Optimistic _ chen
一名热爱技术与分享的全栈开发者,在这里记录成长,专注分享编程技术与实战经验,助力你的技术成长之路,与你共同进步!


🚀我的专栏推荐

专栏 内容特色 适合人群
🔥C语言从入门到精通 系统讲解基础语法、指针、内存管理、项目实战 零基础新手、考研党、复习
🔥Java基础语法 系统解释了基础语法、类与对象、继承 Java初学者
🔥Java核心技术 面向对象、集合框架、多线程、网络编程、新特性解析 有一定语法基础的开发者
🔥Java EE 进阶实战 Servlet、JSP、SpringBoot、MyBatis、项目案例拆解 想快速入门Java Web开发的同学
🔥Java数据结构与算法 图解数据结构、LeetCode刷题解析、大厂面试算法题 面试备战、算法爱好者、计算机专业学生
🔥Redis系列 从数据类型到核心特性解析 项目必备

🚀我的承诺:
✅ 文章配套代码:每篇技术文章都提供完整的可运行代码示例

✅ 持续更新:专栏内容定期更新,紧跟技术趋势

✅ 答疑交流:欢迎在文章评论区留言讨论,我会及时回复(支持互粉)


🚀 关注我,解锁更多技术干货!
⏳ 每天进步一点点,未来惊艳所有人!✍️ 持续更新中,记得⭐收藏关注⭐不迷路 ✨

📌 标签:#技术博客#编程学习#Java#C语言#算法#程序员

Agent 简述

  Agent(智能体)是一个能够感知环境输入、自主决策、规划行动路径,并可调用工具或执行操作以达成目标的自主性软件实体,核心就是由⼤语⾔模型(LLM)动态控制流程⾛向

简单来说:LLM是大脑,Agent是执行大脑发出指令的“手脚”。

Agents:通常由多个Agent组成的协作系统共同处理复杂指令,实现各司其职、高效配合的目标,也被称为多智能体系统

Chain Workflow(链式工作流)

  链式工作流模式是一种将复杂的任务分解为多个"有序,依赖性强"的子任务,每个步骤由一个独立处理单元完成,前一步的输出作为下一步的输入,最终形成一条"数据流动的链条"。

链式处理的主要目的是:当任务可解析为多个子步骤时,通过分布降低单次推理难度,提升准确率。

Spring AI 链式工作流

下面是一个基于 Spring AI 的链式工作流示例,实现"用户问题 → 意图识别 → 信息提取 → 答案生成"的三步链式处理:

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.Map;

/**
 * Spring AI 链式工作流示例
 * 将复杂任务拆解为:意图识别 → 信息提取 → 答案生成 三个有序步骤
 */
@Service
public class ChainWorkflowService {

    private final ChatClient chatClient;

    public ChainWorkflowService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    /**
     * 执行链式工作流
     *
     * @param userInput 用户原始输入
     * @return 最终生成的答案
     */
    public String executeChain(String userInput) {
        // 步骤1:意图识别 —— 判断用户想做什么
        String intent = identifyIntent(userInput);
        System.out.println("【步骤1】意图识别结果: " + intent);

        // 步骤2:信息提取 —— 从用户输入中提取关键参数
        String extractedInfo = extractInformation(userInput, intent);
        System.out.println("【步骤2】提取的关键信息: " + extractedInfo);

        // 步骤3:答案生成 —— 基于意图和提取的信息生成最终回答
        String finalAnswer = generateAnswer(intent, extractedInfo);
        System.out.println("【步骤3】最终答案: " + finalAnswer);

        return finalAnswer;
    }

    /**
     * 步骤1:意图识别
     */
    private String identifyIntent(String userInput) {
        String prompt = """
                请分析以下用户输入,判断其意图类别(仅返回一个词):
                - QUERY:查询/询问信息
                - CODE:需要编写代码
                - EXPLAIN:需要解释概念
                - OTHER:其他
                
                用户输入:{input}
                意图类别:
                """;

        return chatClient.prompt(new PromptTemplate(prompt, Map.of("input", userInput)))
                .call()
                .content();
    }

    /**
     * 步骤2:信息提取
     */
    private String extractInformation(String userInput, String intent) {
        String prompt = """
                用户输入:{input}
                意图:{intent}
                
                请根据上述意图,从用户输入中提取关键信息(实体、参数、约束条件等),
                以简洁的要点形式返回。
                """;

        return chatClient.prompt(new PromptTemplate(prompt, Map.of(
                "input", userInput,
                "intent", intent
        ))).call().content();
    }

    /**
     * 步骤3:答案生成
     */
    private String generateAnswer(String intent, String extractedInfo) {
        String prompt = """
                你是一个智能助手。
                
                意图:{intent}
                关键信息:{info}
                
                请基于以上信息,生成准确、详细的回答。
                """;

        return chatClient.prompt(new PromptTemplate(prompt, Map.of(
                "intent", intent,
                "info", extractedInfo
        ))).call().content();

Parallelization Workflow(并⾏化⼯作流)

  并行化工作流模式是一种通过并发执行多个独立的LLM调用,并在完成后自动聚合结果的处理模式。

并行意味着多个LLM调用同时进行,而不是串行执行

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 并行化工作流实现类
 * 用于并发执行多个LLM调用并聚合结果
 */
public class ParallelizationWorkflow {
   
   // 聊天客户端,用于与LLM交互
   private final ChatClient chatClient;
   
   /**
    * 构造函数
    * @param chatClient 聊天客户端实例
    */
   public ParallelizationWorkflow(ChatClient chatClient) {
       this.chatClient = chatClient;
   }
   
   /**
    * 并行处理多个输入
    * @param prompt 提示词模板
    * @param inputs 输入数据列表
    * @param nWorkers 工作线程数
    * @return 处理结果列表,顺序与输入列表一致
    */
   public List<String> parallel(String prompt, List<String> inputs, int nWorkers) {
       // 参数校验
       Assert.notNull(prompt, "Prompt cannot be null");
       Assert.notEmpty(inputs, "Inputs list cannot be empty");
       Assert.isTrue(nWorkers > 0, "Number of workers must be greater than 0");
        
       // 创建固定大小的线程池
       ExecutorService executor = Executors.newFixedThreadPool(nWorkers);
       
       try {
          // 为每个输入创建异步任务
          List<CompletableFuture<String>> futures = inputs.stream()
                        .map(input -> CompletableFuture.supplyAsync(() -> {
                              try {
                                 // 构建完整提示词并调用LLM
                                 String fullPrompt = prompt + "\nInput:" + input;
                                 return chatClient.prompt(fullPrompt).call().content(); 
                              } catch (Exception e) {
                                  // 异常处理:包装并抛出运行时异常
                                  throw new RuntimeException("Failed to process input: " + input, e);
                              }
                        }, executor))  // 指定线程池执行
                        .collect(Collectors.toList());  // 收集所有Future
           
           // 等待所有任务完成
           CompletableFuture<Void> allFutures = CompletableFuture.allOf(
               futures.toArray(new CompletableFuture[0])
           );
           allFutures.join();  // 阻塞直到所有任务完成

           // 提取所有任务的结果
           return futures.stream()
                    .map(CompletableFuture::join)  // 获取每个任务的结果
                    .collect(Collectors.toList()); // 收集为列表
                        
       } finally {
         // 确保线程池被关闭
         executor.shutdown();
       }
   }
}
  1. 对inputs列表中的每一个元素:使用CompletableFuture.supplyAsync(...)在线程池中提交一个异步任务,每个任务都会:
  • 组合完整提示prompt+"\nInput:"+input;
  • 调用chatClient发起请求到LLM
  • 获取响应内容.content()
  • 返回结果,若失败则包装为RuntimeException
  1. 等等所有任务完成
  2. 收集结果,并保持顺序,最终返回一个与原始输入顺序完全一致的结果列表

总结:并⾏化⼯作流就是把⼀个⼤任务拆成多个⼩任务,让多个LLM调⽤同时运⾏,最后由程序⾃动把结果合并起来,从⽽实现⾼效、智能、可扩展

Routing Workflow(路由工作流)

路由工作流模式实现了智能任务分发,能够根据不同类型的输入进行专业化处理。

核心:根据输入内容的类型或特征,自动将任务"路由"到最合适处理它的子系统或模块

类似于医院的导医台,在挂号的时候,不同的病症知道该去适合治疗的科室。这个导医台就是程序中的LLM。

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
 * Spring AI 路由工作流示例
 * 根据输入内容类型自动路由到不同的处理模块
 */
@Service
public class RoutingWorkflowService {

    private final ChatClient chatClient;

    public RoutingWorkflowService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    /**
     * 执行路由工作流
     *
     * @param userInput 用户原始输入
     * @return 路由到对应模块处理后的结果
     */
    public String executeRouting(String input,Map<String,String> routes) {
        Assert.notNull(input,"输入文本不能为空");
        Assert.notEmpty(routers,"路由表映射不能为空");
        // 步骤1:路由决策 —— 判断输入应该由哪个模块处理
        String routeKey = determineRoute(input);
        //获取对应的专业提示词
        String selectedPrompt=routes.get(routKey);
        if(selectedPrompt==null){
           throw new IllegalArgumentException("选中的路由"+routeKey+"在路由表中未定义");
        }
        return chatClient.prompt(selectedPrompt+"\n输入内容:\n"+input).call().content();
    }

    /**
     * 步骤1:路由决策 - 判断输入类型
     */
    private String determineRoute(String userInput,Iterable<String> availableRoutes) {
        String selectorPrompt = String.format("""
                请分析以下用户输入,判断其最适合由哪个模块处理:%s:
                说明你的判断理由,然后以如下JSON格式输出结果:
                {
                  “reasoning”:"简要解释为何分配给该团队,考虑关键词、用户意图和紧急程度",
                  "selection":“所选团队的名称”
                }
                用户请求:%s
                """,availableRoutes,userInput);

      RoutingResponse response=chatClient.prompt(selectorPrompt)
                .call()
                .entity(RoutingResponse.class);
      return response.selection();
   

测试

import java.util.List;
import java.util.Map;

@Configuration
public class RoutingWorkflowTest {

    @Bean
    public CommandLineRunner commandLineRunner(ChatClient.Builder chatClientBuilder) {
        return args -> {
            // 定义四个简单明了的处理路线(更短的提示词)
            Map<String, String> simpleRoutes = Map.of(
                "财务",
                "你是财务助手,请回答账单、扣费、退款等问题。开头写【财务】。",
                "技术",
                "你是技术支持,请回答登录、功能使用、系统错误等问题。开头写【技术】。",
                "账户",
                "你是账户助手,请处理账号找回、密码重置、安全验证等问题。开头写【账户】。",
                "产品",
                "你是产品顾问,请回答功能咨询、如何使用、推荐操作等问题。开头写【产品】。"
            );

            // 模拟三个非常简短的用户提问(贴近日常对话)
            List<String> simpleTickets = List.of(
                "我忘记密码了,登不进去账号",           // → 应路由到"账户"
                "上个月怎么多扣了50块钱?",            // → 应路由到"财务"
                "导出数据的功能在哪?怎么用?"          // → 应路由到"产品"
            );

            var routerWorkflow = new RoutingWorkflow(chatClientBuilder.build());
            
            System.out.println("🚀 开始路由工作流测试");
            System.out.println("=".repeat(50));
            
            int i = 1;
            for (String ticket : simpleTickets) {
                System.out.println("\n💬 问题 " + i++);
                System.out.println("-".repeat(30));
                System.out.println("用户提问: " + ticket);
                System.out.println("-".repeat(30));
                
                String response = routerWorkflow.route(ticket, simpleRoutes);
                System.out.println("AI回复: " + response);
                
                // 验证路由结果
                String expectedRoute = switch (ticket) {
                    case "我忘记密码了,登不进去账号" -> "账户";
                    case "上个月怎么多扣了50块钱?" -> "财务";
                    case "导出数据的功能在哪?怎么用?" -> "产品";
                    default -> "未知";
                };
                
                if (response.contains("【" + expectedRoute + "】")) {
                    System.out.println("✅ 路由正确: " + expectedRoute);
                } else {
                    System.out.println("❌ 路由可能错误,预期: " + expectedRoute);
                }
            }
         
        };
    }
}

适合输入任务具有明显不同的类别,不同类别的任务需要不同的处理逻辑,这些任务差异很大,如果让同一个LLM去处理,很容易出错;但是通过路由模式,可以让每个任务由最擅长的Agent

Orchestrator-Workers(编排器工作者模式)

Orchestrator-Workers 模式是一种用于处理复杂、动态任务的 AI 系统架构,其核心思想是:

  • Orchestrator(编排器):负责分析任务,动态分解为多个子任务,并协调执行。
  • Workers(工作者):并行处理各自的子任务,每个 Worker 专注于特定领域或视角。
  • 结果聚合:将所有 Worker 的输出整合为最终响应。

该模式适用于无法预先确定子任务、需要多角度解决方案或自适应问题解决的场景。例如:学校举办一场活动,需要通知各学院、活动准备人员、工作人员、招聘志愿者和活动人员等,需要解决各种问题。


/**
 * Orchestrator-Workers 模式实现
 * 负责将复杂任务分解为多个子任务,并行处理并聚合结果
 */
public class OrchestratorWorkers {
    private final ChatClient chatClient;
    private final String orchestratorPrompt;
    private final String workerPrompt;
    private final ExecutorService executorService;
    
    // 默认编排器提示词
    public static final String DEFAULT_ORCHESTRATOR_PROMPT = """
        请分析以下任务,并将其分解为2-3种不同的写作风格或视角:
        
        任务:{task}
        
        要求:
        1. 解释你对该任务的理解
        2. 不同写法应服务于不同受众或使用场景
        3. 每个子任务需明确类型与具体描述
        
        以如下 JSON 格式返回结果:
        {
          "analysis": "简要说明你的理解,以及为何这些角度有价值",
          "tasks": [
            {
              "type": "专业",
              "description": "用正式、技术性强的语言突出产品参数和认证标准"
            },
            {
              "type": "通俗易懂",
              "description": "用生活化语言讲故事,强调使用体验和情感共鸣"
            }
          ]
        }
        """;
    
    // 默认工作者提示词
    public static final String DEFAULT_WORKER_PROMPT = """
        请根据以下要求撰写内容:
        
        原始任务:{original_task}
        写作风格:{task_type}
        具体要求:{task_description}
        
        输出要求:语言流畅自然,适合发布。
        """;
    
    /**
     * 表示由编排器生成的一个子任务
     * @param type        风格类型,如"专业"、"亲民"
     * @param description 子任务的具体说明
     */
    public static record Task(String type, String description) {
    }
    
    /**
     * 编排器的响应:包含对任务的理解和子任务列表
     * @param analysis 任务分析说明
     * @param tasks    拆分出的多个子任务
     */
    public static record OrchestratorResponse(String analysis, List<Task> tasks) {
    }
    
    /**
     * 最终输出结果
     * @param analysis        编排器的任务理解
     * @param workerResponses 各工作者的输出结果
     */
    public static record FinalResponse(String analysis, List<String> workerResponses) {
    }
    
    /**
     * 使用默认提示词创建实例
     * @param chatClient ChatClient 实例
     */
    public OrchestratorWorkers(ChatClient chatClient) {
        this(chatClient, DEFAULT_ORCHESTRATOR_PROMPT, DEFAULT_WORKER_PROMPT);
    }
    
    /**
     * 使用自定义提示词创建实例
     * @param chatClient         ChatClient 实例
     * @param orchestratorPrompt 编排器提示词
     * @param workerPrompt       工作者提示词
     */
    public OrchestratorWorkers(ChatClient chatClient, String orchestratorPrompt, String workerPrompt) {
        Assert.notNull(chatClient, "ChatClient 不能为空");
        Assert.hasText(orchestratorPrompt, "编排器提示词不能为空");
        Assert.hasText(workerPrompt, "工作者提示词不能为空");
        
        this.chatClient = chatClient;
        this.orchestratorPrompt = orchestratorPrompt;
        this.workerPrompt = workerPrompt;
        this.executorService = Executors.newFixedThreadPool(3); // 创建线程池用于并行处理
    }
    
    /**
     * 执行"编排-协作"工作流:
     * 1. 编排器分析任务 → 拆分为多视角子任务
     * 2. 多个工作者并行处理各自风格的内容
     * 3. 聚合所有结果
     *
     * @param taskDescription 主任务描述
     * @return 包含分析过程和最终输出的结果
     */
    @SuppressWarnings("null")
    public FinalResponse process(String taskDescription) {
        Assert.hasText(taskDescription, "任务描述不能为空");
        
        try {
            // Step 1: 编排器分析任务结构
            OrchestratorResponse orchestratorResponse = analyzeTask(taskDescription);
            
            // Step 2: 并行执行每个子任务
            List<String> workerResponses = executeTasksInParallel(taskDescription, orchestratorResponse.tasks());
            
            // Step 3: 返回最终结果
            return new FinalResponse(orchestratorResponse.analysis(), workerResponses);
            
        } catch (Exception e) {
            System.err.println("处理任务时发生错误: " + e.getMessage());
            e.printStackTrace();
            throw new RuntimeException("任务处理失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 分析任务并拆分子任务
     * @param taskDescription 任务描述
     * @return 编排器响应
     */
    private OrchestratorResponse analyzeTask(String taskDescription) {
        OrchestratorResponse response = this.chatClient.prompt()
                .user(u -> u.text(this.orchestratorPrompt)
                        .param("task", taskDescription))
                .call()
                .entity(OrchestratorResponse.class);
        
        System.out.println(String.format("\n=== 编排器决策 ===\n分析: %s\n\n拆分任务: %s\n",
                response.analysis(), response.tasks()));
        
        return response;
    }
    
    /**
     * 并行执行所有子任务
     * @param originalTask 原始任务
     * @param tasks 子任务列表
     * @return 工作者响应列表
     */
    private List<String> executeTasksInParallel(String originalTask, List<Task> tasks) {
        List<CompletableFuture<String>> futures = tasks.stream()
                .map(task -> CompletableFuture.supplyAsync(() -> 
                    executeSingleTask(originalTask, task), executorService))
                .collect(Collectors.toList());
        
        // 等待所有任务完成
        return futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
    }
    
    /**
     * 执行单个子任务
     * @param originalTask 原始任务
     * @param task 子任务
     * @return 工作者响应
     */
    private String executeSingleTask(String originalTask, Task task) {
        String response = this.chatClient.prompt()
                .user(u -> u.text(this.workerPrompt)
                        .param("original_task", originalTask)
                        .param("task_type", task.type())
                        .param("task_description", task.description()))
                .call()
                .content();
        
        System.out.println(String.format("工作者[%s] 处理完成,输出长度: %d", 
                task.type(), response.length()));
        
        return response;
    }
    
    /**
     * 关闭线程池资源
     */
    public void shutdown() {
        if (executorService != null && !executorService.isShutdown()) {
            executorService.shutdown();
            System.out.println("OrchestratorWorkers 线程池已关闭");
        }
    }
    
    /**
     * 使用 try-with-resources 风格的关闭方法
     */
    public void close() {
        shutdown();
    }
}

Orchestrator-Workers 模式本质上就是⼀个最简单的Multi-Agent架构

评估-优化循环模式

Evaluator-Optimizer 模式(简称EO模式)是一种在大模型系统中实现迭代质量提升的闭环架构设计模式。
该模式通过两个主要组件:

  • 评估器(Evaluator):对当前输出进行多维度的质量评估,比如是否完整、准确
  • 优化器(Optimizer):根据评估结果提出改进建议,并重新生成更好的内容

有点像我们人类写作文时的样子,先写初稿,再检查,然后修改、斟酌,最后完成,比直接一次性写完质量更高。

import java.util.ArrayList;
import java.util.List;

/**
 * Evaluator-Optimizer 模式实现类
 * 通过评估-优化循环迭代提升生成内容质量
 * 
 * 核心思想:通过生成-评估-优化的循环迭代,逐步改进输出质量
 * 适用于需要高质量文本生成的场景,如沟通文案、报告撰写等
 */
public class EvaluatorOptimizer {
    
    // 生成器提示语:用于撰写沟通文案
    // 定义了生成器AI的任务、要求和输出格式
    public static final String DEFAULT_GENERATOR_PROMPT = """
            你的任务是帮助用户撰写一段合适的沟通文字,
            用于与老师或辅导员交流某个学习相关请求。
            
            如果有之前的尝试和反馈,请认真反思并进行改进。
            
            要求:
            - 使用礼貌、尊重、诚恳的语气
            - 理由充分,体现责任感和规划能力
            - 避免借口式表达,突出主动性和成长意愿
            
            输出必须为单行 JSON 格式,严格遵循如下模板:
            {"thoughts":"简要说明你的写作思路","response":"具体的沟通内容文本"}
            
            规则:
            1. 所有换行用 \\n 表示
            2. 所有双引号写成 \\\"
            3. 整个响应在一行内完成,无真实回车
            
            示例:
            {"thoughts":"强调实习对职业发展的价值","response":"老师您好,\\n我想和你沟通一下...\\n感谢您的理解和支持!"}
            
            请只返回这个 JSON 结构。
            """;
    
    // 评估器提示语:评估沟通文案质量
    // 定义了评估器AI的评估维度和标准
    public static final String DEFAULT_EVALUATOR_PROMPT = """
            请评估以下沟通文案是否适合用于与高校教师或辅导员对话。
            
            从以下几个维度打分:
            - 礼貌性:是否使用敬语,态度是否谦逊
            - 逻辑性:理由是否充分、真实可信
            - 清晰度:目标是否明确,结构是否清晰
            - 合规性:是否符合学校规定,不鼓励逃课
            - 改进建议:指出具体可优化之处
            
            回复必须是单行 JSON,格式如下:
            {"evaluation":"PASS, NEEDS_IMPROVEMENT, 或 FAIL", "feedback":"具体评价和建议"}
            
            evaluation 字段只能取值:"PASS"(完全合格)、"NEEDS_IMPROVEMENT"(基本可行但需调整)、"FAIL"(不得体或违规)
            
            只有当文案非常得体且无需修改时才可标记为 PASS。
            """;
    
    /**
     * 生成结果记录类
     * @param thoughts 生成时的思考过程,说明写作思路
     * @param response 生成的沟通文案内容
     */
    public static record Generation(String thoughts, String response) {
    }
    
    /**
     * 评估结果记录类
     * @param evaluation 评估结果枚举:PASS/NEEDS_IMPROVEMENT/FAIL
     * @param feedback 具体的评估反馈和改进建议
     */
    public static record EvaluationResponse(Evaluation evaluation, String feedback) {
        /**
         * 评估结果枚举
         * PASS: 完全合格,无需修改
         * NEEDS_IMPROVEMENT: 基本可行但需要调整
         * FAIL: 不得体或违规,需要重写
         */
        public enum Evaluation {
            PASS, NEEDS_IMPROVEMENT, FAIL
        }
    }
    
    /**
     * 最终优化后的响应
     * @param solution 最终被接受的解决方案(沟通文案)
     * @param chainOfThought 整个思考链,记录每次迭代的生成结果
     */
    public static record RefinedResponse(String solution, List<Generation> chainOfThought) {
    }
    
    // 依赖的AI聊天客户端,用于调用大模型
    private final ChatClient chatClient;
    // 生成器使用的提示语
    private final String generatorPrompt;
    // 评估器使用的提示语
    private final String evaluatorPrompt;
    
    /**
     * 构造函数:使用默认提示语
     * @param chatClient AI聊天客户端,不能为空
     */
    public EvaluatorOptimizer(ChatClient chatClient) {
        this(chatClient, DEFAULT_GENERATOR_PROMPT, DEFAULT_EVALUATOR_PROMPT);
    }
    
    /**
     * 构造函数:自定义提示语
     * @param chatClient AI聊天客户端,不能为空
     * @param generatorPrompt 生成器提示语,不能为空
     * @param evaluatorPrompt 评估器提示语,不能为空
     */
    public EvaluatorOptimizer(ChatClient chatClient, String generatorPrompt, String evaluatorPrompt) {
        // 参数校验
        Assert.notNull(chatClient, "ChatClient 不能为空");
        Assert.hasText(generatorPrompt, "生成器提示不能为空");
        Assert.hasText(evaluatorPrompt, "评估器提示不能为空");
        
        this.chatClient = chatClient;
        this.generatorPrompt = generatorPrompt;
        this.evaluatorPrompt = evaluatorPrompt;
    }
    
    /**
     * 启动优化循环的公共入口方法
     * @param task 用户的具体请求任务描述
     * @return 优化后的最终响应,包含解决方案和思考链
     */
    public RefinedResponse loop(String task) {
        // 初始化记忆列表,存储所有尝试过的文案
        List<String> memory = new ArrayList<>();
        // 初始化思考链,记录每次生成的结果
        List<Generation> chainOfThought = new ArrayList<>();
        // 调用私有递归方法开始循环
        return loop(task, "", memory, chainOfThought);
    }
    
    /**
     * 私有递归方法:实现评估-优化的核心循环逻辑
     * @param task 用户请求任务
     * @param context 当前上下文,包含之前的尝试和反馈
     * @param memory 记忆列表,存储所有历史尝试
     * @param chainOfThought 思考链,记录每次生成
     * @return 优化后的最终响应
     */
    private RefinedResponse loop(String task, String context, List<String> memory,
                                 List<Generation> chainOfThought) {
        // 1. 生成阶段:根据任务和上下文生成文案
        Generation generation = generate(task, context);
        // 将生成的文案加入记忆
        memory.add(generation.response());
        // 将生成结果加入思考链
        chainOfThought.add(generation);
        
        // 2. 评估阶段:评估生成的文案质量
        EvaluationResponse evaluationResponse = evaluate(generation.response(), task);
        
        // 3. 判断是否通过评估
        if (evaluationResponse.evaluation().equals(EvaluationResponse.Evaluation.PASS)) {
            // 解决方案被接受!返回最终结果
            return new RefinedResponse(generation.response(), chainOfThought);
        }
        
        // 4. 未通过评估,构建新的上下文进行下一轮迭代
        // 新的上下文包含:所有历史尝试 + 最新反馈
        StringBuilder newContext = new StringBuilder();
        newContext.append("之前的尝试:");
        for (String m : memory) {
            newContext.append("\n- ").append(m);
        }
        newContext.append("\n最新反馈: ").append(evaluationResponse.feedback());
        
        // 5. 递归调用,开始下一轮生成-评估循环
        return loop(task, newContext.toString(), memory, chainOfThought);
    }
    
    /**
     * 生成方法:调用AI生成沟通文案
     * @param task 用户请求任务
     * @param context 上下文信息(历史尝试和反馈)
     * @return 生成结果,包含思考过程和文案内容
     */
    private Generation generate(String task, String context) {
        // 调用AI聊天客户端,传入生成器提示语、上下文和任务
        Generation generationResponse = chatClient.prompt()
                .user(u -> u.text("{prompt}\\n{context}\\n当前请求: {task}")
                        .param("prompt", this.generatorPrompt)
                        .param("context", context)
                        .param("task", task))
                .call()
                .entity(Generation.class);
        
        // 打印生成结果,便于调试和观察
        System.out.println(String.format("\\n=== 生成器输出 ===\\n思考: %s\\n文案: %s\\n",
                generationResponse.thoughts(), generationResponse.response()));
        
        return generationResponse;
    }
    
    /**
     * 评估方法:调用AI评估文案质量
     * @param content 待评估的文案内容
     * @param task 原始请求任务
     * @return 评估结果,包含评估等级和反馈建议
     */
    private EvaluationResponse evaluate(String content, String task) {
        // 调用AI聊天客户端,传入评估器提示语、任务和待评估文案
        EvaluationResponse evaluationResponse = chatClient.prompt()
                .user(u -> u.text("{prompt}\\n原始请求: {task}\\n待评估文案: {content}")
                        .param("prompt", this.evaluatorPrompt)
                        .param("task", task)
                        .param("content", content))
                .call()
                .entity(EvaluationResponse.class);
        
        // 打印评估结果,便于调试和观察
        System.out.println(String.format("\\n=== 评估器输出 ===\\n结果: %s\\n反馈: %s\\n",
                evaluationResponse.evaluation(), evaluationResponse.feedback()));
        
        return evaluationRe;

总结

本文详细介绍了 Spring AI 中五种核心的 Agent 工作流模式,每种模式都有其独特的适用场景和优势。下面总结这五种工作流模式的使用场景。

1. Chain Workflow(链式工作流)

使用场景:

  • 顺序处理任务:当任务需要按照固定顺序执行,且前一步的输出是后一步的输入时
  • 简单业务流程:如数据清洗、文档处理、审批流程等线性流程
  • 教学演示:适合初学者理解 Agent 的基本工作原理
  • 小型项目:需求明确、流程简单的应用场景

特点: 结构简单、易于理解和实现,但缺乏灵活性。

2. Parallelization Workflow(并行化工作流)

使用场景:

  • 性能敏感应用:需要同时处理多个独立任务以提高效率
  • 数据聚合:从多个数据源并行获取信息后汇总
  • 竞品分析:同时分析多个竞争对手或产品
  • 实时监控:并行监控多个系统指标或日志
  • 批量处理:同时处理大量相似但独立的任务

特点: 显著提升处理速度,适合 I/O 密集型或网络请求密集的场景。

3. Routing Workflow(路由工作流)

使用场景:

  • 智能客服:根据用户问题类型路由到不同的专业客服 Agent
  • 内容分类:自动识别内容类型并分发给相应的处理模块
  • 多领域专家系统:根据问题领域选择最合适的专家 Agent
  • 工作流分支:根据条件判断选择不同的处理路径
  • 优先级调度:根据任务紧急程度或重要性分配资源

特点: 灵活智能,能够根据输入动态选择处理路径。

4. Orchestrator-Workers(编排器工作者模式)

使用场景:

  • 复杂业务流程:需要协调多个子任务完成复杂目标
  • 项目管理:分解大任务为小任务并分配给不同团队/Agent
  • 系统集成:协调多个子系统或微服务完成端到端流程
  • 科研计算:将大型计算任务分解为多个子任务并行计算
  • 供应链管理:协调采购、生产、物流等多个环节

特点: 层次化架构,适合复杂、多步骤的业务流程。

5. 评估-优化循环模式

使用场景:

  • 内容生成优化:如文章、代码、设计方案的迭代改进
  • A/B 测试:生成多个版本并选择最优结果
  • 质量控制:对输出结果进行质量评估和自动修正
  • 学习系统:通过反馈循环不断优化模型或策略
  • 创意工作:如广告文案、产品设计的多次迭代优化

特点: 自我改进机制,适合质量要求高、需要持续优化的场景。

总结建议

  1. 从简单开始:对于新项目,建议从 Chain Workflow 开始,逐步引入更复杂的模式
  2. 模式组合使用:实际项目中经常需要组合多种模式,如并行处理后再路由
  3. 性能监控:复杂工作流需要完善的监控和日志,便于问题排查
  4. 测试策略:为每种工作流设计专门的测试用例,特别是并发和路由逻辑
  5. 文档化:清晰记录工作流设计决策和架构图,便于团队理解和维护

  掌握这五种工作流模式,能够帮助你在实际项目中设计出更加高效、灵活和可靠的 AI Agent 系统。根据具体业务需求选择合适的工作流模式,是构建成功 A

Spring AI 与 LangGraph 工作流模式对比

为了更好地理解 Spring AI 中的工作流模式,下面将其与 LangGraph 框架中的对应模式进行对比分析:

工作流模式 Spring AI 实现方式 LangGraph 实现方式 主要区别
Chain Workflow 通过 Chain 接口和 SimpleChain 实现,支持顺序执行 使用 StateGraph 和线性边连接节点 Spring AI 更轻量,LangGraph 提供更丰富的状态管理和条件分支
Parallelization Workflow 使用 ParallelAgentExecutor 并行执行多个 Agent 通过 ConditionalEdges 和并行分支实现 Spring AI 并行化更直接,LangGraph 的并行控制更精细
Routing Workflow 基于 Router 接口和条件判断实现路由逻辑 使用 Router 节点和条件边动态选择路径 两者概念相似,但 LangGraph 的路由机制更可视化、可调试
Orchestrator-Workers 通过主 Agent 协调多个子 Agent 完成任务 使用层次化图结构,父图调用子图 Spring AI 更侧重 Agent 间协调,LangGraph 提供完整的图嵌套机制
评估-优化循环模式 通过评估器反馈循环迭代优化结果 使用循环边和状态持久化实现迭代优化 Spring AI 循环逻辑更显式,LangGraph 的循环机制更内置于图结构

核心差异总结

  1. 抽象层次不同

    • Spring AI:更贴近业务层,以 Agent 为中心的工作流设计
    • LangGraph:更底层的图计算框架,提供通用的状态机和工作流引擎
  2. 状态管理方式

    • Spring AI:状态通常通过消息传递或上下文对象管理
    • LangGraph:内置 State 对象,支持复杂的状态持久化和传递
  3. 可视化与调试

    • Spring AI:依赖日志和监控工具进行调试
    • LangGraph:原生支持图可视化,便于理解和调试复杂工作流
  4. 学习曲线

    • Spring AI:对 Spring 开发者更友好,集成 Spring 生态更紧密
    • LangGraph:需要理解图计算概念,但功能更强大灵活
  5. 适用场景

    • Spring AI:适合 Spring 生态内的 AI 应用,快速构建业务工作流
    • LangGraph:适合需要复杂状态管理、循环和条件分支的通用工作流

选择建议

  • 选择 Spring AI:如果你的项目基于 Spring 生态,需要快速集成 AI 能力,且工作流相对简单直接
  • 选择 LangGraph:如果你需要构建复杂、多分支、有状态的工作流,或者需要高度可视化和可调试的工作流引擎
  • 混合使用:在实际项目中,也可以考虑使用 Spring AI 处理业务逻辑,而将复杂的工作流编排交给 LangGraph

两种框架各有优势,选择哪种取决于具体的项目需求、团队技术栈和工作流复杂度。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐