前言

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-elsewhile 循环和依赖注入。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 类似于中间件,可以在请求前后拦截处理(类似于 LangChainCallbacks)。 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