SpringAi_10_工作流
前言
Github:https://github.com/HealerJean
一、工作流
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类,但你可以利用FunctionCalling(函数调用) 和 Java 逻辑 轻松构建 Agent。
- 工具 (Tools):使用
@Tool注解标记你的 Java 方法。 - 决策 (Decision):由大模型通过
FunctionCalling决定是否调用工具。 - 循环 (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)
-
核心理念: 分而治之。将一个复杂的大任务拆解为一系列连续的、可管理的子步骤。
- 工作原理:上一步的输出直接作为下一步的输入。
- 适用场景:
- 任务逻辑清晰,呈线性顺序。
- 需要逐步细化信息(例如:先总结 -> 再提炼 -> 后分类)。
- 对输出质量要求高,愿意以增加延迟为代价换取准确性。

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

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

4)编排器-工作者模式 (Orchestrator-Workers)
- 核心理念: 动态规划,协同作战。
-
与并行工作流的区别:并行工作流的子任务是预先定义好的,而编排器模式的子任务是动态生成的,取决于任务本身。
- 工作原理:
- 编排器 (
Orchestrator):接收复杂任务,动态分析并将其拆解为多个子任务。 - 工作者 (
Workers):接收子任务并执行(可以并行)。 - 聚合:编排器收集所有结果并合成最终答案。
- 编排器 (
- 适用场景:
- 无法预先预测子任务数量的复杂问题(如:“帮我调研一下 iPhone 17 的供应链情况”)。
- 需要跨领域知识融合的任务。

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

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);
}
}


