前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、工作流

1、概念解释

1)为什么 Spring AI 没有“标准”的 Agent API?

传统的 AI 框架(如 LangChain)通常定义一个 Agent 类,它包含 LLM + Memory + Tools + Prompt,并有一个 Executor 循环调用直到完成。

Spring AI 的设计哲学是“去框架化”和“融入生态”:

  • 利用 Spring 的控制流:Java/Spring 擅长的是 if-else、while 循环和依赖注入。Spring AI 认为你应该用 Java 代码直接写逻辑,而不是把逻辑塞进一个框架定义的 Agent 对象里。
  • 核心是 ChatClient:文档中提到的核心组件是 ChatClient。它负责与模型交互。至于是生成一句话还是进行多轮工具调用(Agent 行为),由你调用 ChatClient 的方式决定。

2)Spring AI 提供了哪些辅助功能

功能 作用 对应文档概念
Function Calling 让模型知道何时调用工具。 spring.ai.chat.client.tool.names
Tool Observations 监控工具调用的耗时和参数。 spring.ai.tool (文档中的工具观测部分)
Chat Memory 保持会话状态(虽然文档提到 Advisor,但通常需要自己管理 messages 列表)。 spring.ai.chat.client.conversation.id
Advisors 类似于中间件,可以在请求前后拦截处理(类似于 LangChain 的 Callbacks)。 spring.ai.advisor

2)模拟一个 Agent 工作流?

虽然没有 Agent 类,但你可以利用 Function Calling (函数调用) 和 Java 逻辑 轻松构建 Agent。

  • 工具 (Tools):使用 @Tool 注解标记你的 Java 方法。
  • 决策 (Decision):由大模型通过 Function Calling 决定是否调用工具。
  • 循环 (Loop):由你手写的 while 循环实现,直到模型返回最终答案。
@Service
@RequiredArgsConstructor
public class AgentWorkflow {

    private final ChatClient chatClient;
    private final WeatherTool weatherTool; // 注入你的工具

    public String execute(String userQuestion) {
        // 1. 初始化消息列表(这就是 Memory 的雏形)
        List<AdvisedMessage> messages = new ArrayList<>();
        messages.add(new UserMessage(userQuestion));

        boolean finished = false;
        String finalResult = "";

        // 2. Agent 的核心循环 (ReAct 循环)
        while (!finished) {
            // 3. 调用模型
            // 注意:这里需要传递 tools 参数,告诉模型有哪些工具可用
            ChatResponse response = chatClient.prompt()
                    .messages(messages)
                    .tools(weatherTool) // 关键:动态传入工具
                    .call()
                    .chatResponse();

            // 4. 获取模型返回
            Message outputMessage = response.getResult().getOutput();
            messages.add(outputMessage); // 将模型回复加入上下文

            // 5. 判断模型是否调用了工具 (Agent Logic)
            if (outputMessage.getToolCalls() != null && !outputMessage.getToolCalls().isEmpty()) {
                // 模型想要调用工具
                // 你需要遍历 toolCalls,执行对应的 Java 方法
                // 然后将执行结果作为 "Tool Response" 追加到 messages 中
                // 然后 continue 循环,让模型根据工具结果生成最终回答
                
                // 伪代码示例:
                // for (ToolCall tc : outputMessage.getToolCalls()) {
                //    String result = executeTool(tc); // 执行工具
                //    messages.add(new ToolResponseMessage(result, tc.id())); // 追加结果
                // }
                // continue; // 回到循环顶部,让模型看工具结果
            } else {
                // 模型返回了普通文本,结束循环
                finalResult = outputMessage.getContent();
                finished = true;
            }
        }

        return finalResult;
    }
}

2、5 种标准工作流

模式名称 核心逻辑 最佳应用场景 复杂度
链式 (Chain) 线性步骤,环环相扣 任务分解、逐步推理 ⭐⭐
并行 (Parallel) 同时执行,结果汇总 批量处理、多视角分析 ⭐⭐
路由 (Routing) 分类判断,定向处理 客服分流、意图识别 ⭐⭐
编排器-工作者 动态拆解,分配任务 复杂调研、未知任务规划 ⭐⭐⭐⭐
评估器-优化器 生成-反馈-修改循环 高质量写作、代码审查 ⭐⭐⭐

1)链式工作流 (Chain Workflow)

  • 核心理念: 分而治之。将一个复杂的大任务拆解为一系列连续的、可管理的子步骤。

  • 工作原理:上一步的输出直接作为下一步的输入。
  • 适用场景:
    • 任务逻辑清晰,呈线性顺序。
    • 需要逐步细化信息(例如:先总结 -> 再提炼 -> 后分类)。
    • 对输出质量要求高,愿意以增加延迟为代价换取准确性。

image-20260401165607050

2)并行工作流 (Parallelization Workflow)

  • 核心理念: 同时处理,最后汇总。

  • 工作原理:将一个任务拆分为多个相互独立的子任务,同时发送给 LLM 处理,最后将结果聚合。
  • 适用场景:
    • 处理大量相似但独立的数据(如批量分析 100 条评论的情感)。
    • 需要从多个独立视角审视同一个问题(如:一个 AI 扮演乐观者,一个扮演悲观者,同时分析市场风险)。
    • 处理时间至关重要,需要利用并发提升速度。

image-20260401165702536

3)路由工作流 (Routing Workflow)

  • 核心理念: 智能分发,专人专事。

  • 工作原理:先通过一个轻量级的 LLM 调用对输入进行分类,然后根据分类结果,将任务路由到专门的处理逻辑或提示词模板上。
  • 适用场景:
    • 智能客服系统(区分“账单问题”、“技术故障”或“一般咨询”)。
    • 复杂的系统,不同类别的输入需要完全不同的处理策略或知识库。

image-20260401165810992

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

  • 核心理念: 动态规划,协同作战。
  • 与并行工作流的区别:并行工作流的子任务是预先定义好的,而编排器模式的子任务是动态生成的,取决于任务本身。

  • 工作原理:
    • 编排器 (Orchestrator):接收复杂任务,动态分析并将其拆解为多个子任务。
    • 工作者 (Workers):接收子任务并执行(可以并行)。
    • 聚合:编排器收集所有结果并合成最终答案。
  • 适用场景:
    • 无法预先预测子任务数量的复杂问题(如:“帮我调研一下 iPhone 17 的供应链情况”)。
    • 需要跨领域知识融合的任务。

image-20260401165924227

5)评估器-优化器模式 (Evaluator-Optimizer)

  • 核心理念: 自我反思,迭代完善。

  • 工作原理:
    • 生成者 (Generator):生成初稿。
    • 评估者 (Evaluator):根据特定标准(如准确性、风格)审查初稿并提供反馈。
    • 循环:如果评估不通过,生成者根据反馈修改,直到满足标准。
  • 适用场景:
    • 对输出质量要求极高的场景(如:自动化代码重构、创意文案写作)。
    • 需要减少幻觉,确保内容符合事实(类似于“反思”机制)。

image-20260401170005084

3、Spring AI 工作流演示代码

Spring AI 官方:只定义了 5 种工作流模式,并给出了手写演示代码,但没有提供统一的、可直接依赖的工作流框架 API(没有 Workflow 接口、没有 Graph 引擎、没有状态管理)。

1)ChainWorkflow

场景:用户输入 → 摘要 → 翻译 → 润色,一步一步串起来

import org.springframework.ai.chat.client.ChatClient;
import java.util.List;

public class ChainWorkflow {
    private final ChatClient chatClient;
    private final List<String> prompts;

    public ChainWorkflow(ChatClient chatClient, List<String> prompts) {
        this.chatClient = chatClient;
        this.prompts = prompts;
    }

    public String run(String input) {
        String context = input;
        for (String prompt : prompts) {
            context = chatClient.prompt()
                    .user(prompt + "\n内容:" + context)
                    .call()
                    .content();
        }
        return context;
    }
}

@RestController
@RequestMapping("/ai/workflow")
class ChainController {
    private final ChainWorkflow chainWorkflow;

    public ChainController(ChatClient.Builder chatClient) {
        var prompts = List.of(
            "请对以下内容进行摘要",
            "请将内容翻译成中文",
            "请润色成流畅通顺的段落"
        );
        this.chainWorkflow = new ChainWorkflow(chatClient.build(), prompts);
    }

    @GetMapping("/chain")
    public String chain(@RequestParam String text) {
        return chainWorkflow.run(text);
    }
}

2)ParallelizationWorkflow

场景:同时对多个问题并行调用 AI,提高速度

import org.springframework.ai.chat.client.ChatClient;
import java.util.List;
import java.util.stream.Collectors;

public class ParallelizationWorkflow {
    private final ChatClient chatClient;

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

    public List<String> run(List<String> tasks) {
        return tasks.parallelStream()
                .map(task -> chatClient.prompt()
                        .user(task)
                        .call()
                        .content())
                .collect(Collectors.toList());
    }
}
@RestController
@RequestMapping("/ai/workflow")
class ParallelController {
    private final ParallelizationWorkflow workflow;

    public ParallelController(ChatClient.Builder builder) {
        this.workflow = new ParallelizationWorkflow(builder.build());
    }

    @GetMapping("/parallel")
    public List<String> parallel() {
        var tasks = List.of(
            "解释Java线程池",
            "解释Spring事务",
            "解释Redis雪崩"
        );
        return workflow.run(tasks);
    }
}

3)RoutingWorkflow

场景:AI 自动识别问题类型 → 分发到不同专家

import org.springframework.ai.chat.client.ChatClient;

public class RoutingWorkflow {
    private final ChatClient chatClient;

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

    public String route(String input) {
        String type = chatClient.prompt()
                .user("""
                        请分类以下问题,只能返回一个词:
                        TECH / BILLING / GENERAL
                        问题:%s
                        """.formatted(input))
                .call()
                .content();

        return switch (type.trim().toUpperCase()) {
            case "TECH" -> handleTech(input);
            case "BILLING" -> handleBilling(input);
            default -> handleGeneral(input);
        };
    }

    private String handleTech(String input) {
        return chatClient.prompt()
                .user("你是技术客服,回答:" + input)
                .call()
                .content();
    }

    private String handleBilling(String input) {
        return chatClient.prompt()
                .user("你是账单客服,回答:" + input)
                .call()
                .content();
    }

    private String handleGeneral(String input) {
        return chatClient.prompt()
                .user("你是普通客服,友好回答:" + input)
                .call()
                .content();
    }
}
@RestController
@RequestMapping("/ai/workflow")
class RouteController {
    private final RoutingWorkflow router;

    public RouteController(ChatClient.Builder builder) {
        this.router = new RoutingWorkflow(builder.build());
    }

    @GetMapping("/route")
    public String route(@RequestParam String question) {
        return router.route(question);
    }
}

4)OrchestratorWorkersWorkflow

场景:主 AI 拆任务 → 工人 AI 执行 → 主 AI 汇总

import org.springframework.ai.chat.client.ChatClient;
import java.util.List;

public class OrchestratorWorkersWorkflow {
    private final ChatClient orchestrator;
    private final ChatClient worker;

    public OrchestratorWorkersWorkflow(ChatClient orchestrator, ChatClient worker) {
        this.orchestrator = orchestrator;
        this.worker = worker;
    }

    public String run(String task) {
        // 1. 主:拆分子任务
        String plan = orchestrator.prompt()
                .user("把任务拆成清晰步骤:\n" + task)
                .call()
                .content();

        // 2. 工人:执行
        String result = worker.prompt()
                .user("按步骤执行任务:\n" + plan)
                .call()
                .content();

        // 3. 主:汇总
        return orchestrator.prompt()
                .user("汇总最终结果:\n" + result)
                .call()
                .content();
    }
}
@RestController
@RequestMapping("/ai/workflow")
class OrchestratorController {
    private final OrchestratorWorkersWorkflow workflow;

    public OrchestratorController(ChatClient.Builder builder) {
        var chatClient = builder.build();
        this.workflow = new OrchestratorWorkersWorkflow(chatClient, chatClient);
    }

    @GetMapping("/orchestrate")
    public String orchestrate(@RequestParam String task) {
        return workflow.run(task);
    }
}

5)EvaluatorOptimizerWorkflow

场景:生成 → 评估 → 修正 → 最终输出(自我迭代)

import org.springframework.ai.chat.client.ChatClient;

public class EvaluatorOptimizerWorkflow {
    private final ChatClient generator;
    private final ChatClient evaluator;

    public EvaluatorOptimizerWorkflow(ChatClient generator, ChatClient evaluator) {
        this.generator = generator;
        this.evaluator = evaluator;
    }

    public String run(String input) {
        // 1. 先生成答案
        String answer = generator.prompt()
                .user(input)
                .call()
                .content();

        // 2. 评估答案,给出改进意见
        String feedback = evaluator.prompt()
                .user("""
                        评估以下答案是否准确、简洁、完整,给出修改建议:
                        问题:%s
                        答案:%s
                        """.formatted(input, answer))
                .call()
                .content();

        // 3. 根据意见优化答案
        return generator.prompt()
                .user("""
                        优化原答案,根据建议修改:
                        原答案:%s
                        建议:%s
                        """.formatted(answer, feedback))
                .call()
                .content();
    }
}
@RestController
@RequestMapping("/ai/workflow")
class EvaluatorController {
    private final EvaluatorOptimizerWorkflow workflow;

    public EvaluatorController(ChatClient.Builder builder) {
        var client = builder.build();
        this.workflow = new EvaluatorOptimizerWorkflow(client, client);
    }

    @GetMapping("/evaluate")
    public String evaluate(@RequestParam String question) {
        return workflow.run(question);
    }
}

ContactAuthor