前面的学习已经完成了 Memory → RAG → Tool → MCP → Structured Output。这些组件解决了 Agent 能知道什么、能够调用什么以及怎样把结果交给 Java 的问题。
当 Tool 数量越来越多、任务越来越长时,下一个问题会变成:谁来决定程序下一步执行什么?
有些业务流程可以由 Java 提前确定,有些任务需要 LLM 根据实际情况动态选择。Spring AI 的 Agent Workflow正是在处理这一层问题。

一、先理解 Agent 中的两种控制方式
传统 Java 程序具有非常明确的控制路径:
if 条件成立
↓
调用 Service A
↓
调用 Service B
↓
返回结果
相同输入和状态通常会进入相同的代码路径。
LLM 的输出具有概率性。模型面对同一个问题时,受到 Prompt、上下文、模型版本和采样参数影响,可能产生不同判断。
因此,一个实际 Agent 通常同时存在两种控制力量:
Java / Workflow
→ 决定必须遵守的业务流程
LLM
→ 处理语义理解和无法提前写死的决策
例如退款流程可以固定为:
识别退款意图
↓
查询订单
↓
检查退款资格
↓
满足条件后申请退款
其中"用户到底想退款、咨询退款规则还是查询退款进度"很适合交给 LLM 判断。
而:
if (refundAmount > 5000) {
requireManualApproval();
}
这种业务规则更适合继续由 Java 控制。
Spring AI 官方将这两类 Agentic System 分别称为 Workflow 和 Agent。Workflow 使用预定义代码路径组织模型与工具**【Workflow静态】,Agent 允许模型动态决定自己的执行过程【Agent动态,都类似于工作的模式,或者说智能系统Agentic System】**。
对于规则明确的企业任务,Workflow 往往具有更稳定的可预测性。
二、Workflow:把 LLM 放进确定的业务步骤
最简单的是 Chain Workflow,即多个步骤顺序执行。
例如:
用户输入
↓
提取需求
↓
生成方案
↓
检查方案
↓
生成最终结果
代码本质仍然是普通 Java:
String requirement = chatClient.prompt()
.user("提取需求:" + input)
.call()
.content();
String plan = chatClient.prompt()
.user("根据需求生成方案:" + requirement)
.call()
.content();
String result = chatClient.prompt()
.user("检查并完善方案:" + plan)
.call()
.content();
LLM 负责每一步内部的智能处理,Java 负责规定步骤顺序。
Spring AI 官方的 Chain Workflow 示例采用同样的结构:前一步输出作为后一步输入,非常适合具有明确顺序的复杂任务。
三、Routing:先判断意图,再进入不同流程
很多 Agent 的第一步其实是 Routing,也就是意图路由。
例如客服系统收到:
"为什么昨天扣了我两次钱?"
模型可以先输出:
billing
然后 Java 将它路由到对应业务:
用户请求
↓
LLM 意图识别
↓
billing / technical / general
↓
不同 Workflow
实际开发时,最好让模型返回结构化结果:
public record RouteDecision(
String route,
String reason
) {}
然后:
RouteDecision decision = chatClient
.prompt()
.user("""
判断下面问题属于 billing、technical 或 general:
""" + message)
.call()
.entity(RouteDecision.class);
Java 再根据结果执行:
return switch (decision.route()) {
case "billing" -> billingService.handle(message);
case "technical" -> technicalService.handle(message);
default -> generalService.handle(message);
};
这里体现了 Structured Output 的一个重要价值:自然语言决策可以转换成 Java 能够稳定读取的控制信号。
Routing 也是 Spring AI 官方列出的基础 Agent Workflow 模式。
四、长任务还需要 Task State
学习过 ChatMemory 后,很容易把所有"记忆"都放到 Memory 中。实际 Agent 还需要区分 Conversation Memory 和 Task State。【对于长任务,需要补充State】
ChatMemory 主要保存:
用户之前说过什么
模型之前回答过什么
当前对话需要哪些上下文
Task State 保存的是任务执行状态,例如:
任务目标:完成退款
当前步骤:检查退款资格
orderId:A1024
订单状态:PAID
资格检查:PASSED
下一步:申请退款
它可以直接使用普通 Java 对象:
public record RefundState(
String orderId,
String currentStep,
boolean eligible,
String status
) {}
复杂 Agent 的运行过程因此更适合理解成:
ChatMemory
→ 对话上下文
Task State
→ 当前任务执行到了哪里
Database / Tool
→ 外部业务世界现在是什么状态
Task State 可以存入 Redis、数据库或者工作流引擎。它属于普通软件系统状态管理,并不要求全部塞进 Prompt。
五、任务复杂以后:Planning 与 Orchestrator-Workers
有些任务无法提前知道具体步骤。
例如:
"分析这个项目代码并给出性能优化方案。"
系统可能需要:
读取项目结构
分析数据库代码
分析缓存
分析接口
运行性能测试
汇总问题
不同项目产生的子任务还可能完全不同。
这时可以让 LLM 承担 Orchestrator:
复杂目标
↓
LLM 分解任务
↓
Task A Task B Task C
↓ ↓ ↓
Worker Worker Worker
└───────┬───────┘
↓
汇总结果
Spring AI 将这种模式称为 Orchestrator-Workers。Orchestrator 动态产生子任务,Worker 分别执行,再汇总结果 。官方建议将它用于无法提前预测子任务的复杂任务**【比较简单、模糊的复杂任务口令】**。
如果多个任务彼此独立,还可以并行执行:
任务
↓
├── 分析代码质量
├── 分析性能
└── 分析安全风险
↓
汇总
这对应Parallelization Workflow ,可以降低多个独立 LLM 任务串行执行带来的延迟。【Workflow有点像是代码预定义的一种工作模式,chain/分布式拆解/并行执行等】
六、Evaluator-Optimizer:让一次任务内部自我修正
还有一类模式非常重要:
生成结果
↓
Evaluator
↓
达到标准?
↓否
给出修改意见
↓
再次生成
↓
重新评价
例如生成 Java 代码后,可以让另一个步骤检查:
是否线程安全?
是否满足接口要求?
是否遗漏异常处理?
没有达到要求就重新生成。
Spring AI 官方称其为**Evaluator-Optimizer Workflow**,适合具有明确评价标准,并且多轮修改能够稳定提高结果质量的任务。
Spring AI 2.0 中已经存在类似思想。例如:
.entity(
Result.class,
spec -> spec.validateSchema()
)
如果模型输出不符合 JSON Schema,StructuredOutputValidationAdvisor 会获得具体验证错误,再请求模型修正,默认最多执行多次尝试。
Tool Calling 本身也是一种递归循环:
LLM
↓
Tool
↓
Tool Result
↓
LLM
↓
继续判断
Spring AI 2.0 的 ToolCallingAdvisor 正是一个 Recursive Advisor。
七、运行时 Evaluator-Optimizer 与 Eval 需要区分
这里需要区分两个很容易混淆的概念:Evaluator-Optimizer 是 Agent 的运行时优化模式,Eval 是评价 AI 输出质量的方法和工程过程。
Evaluator-Optimizer 工作在当前任务内部:
生成
↓
评价
↓
发现问题
↓
提供反馈
↓
重新生成
↓
再次评价
它的目标是利用评价结果继续修改当前输出。例如模型生成代码以后,Evaluator 检查是否满足接口要求;如果缺少异常处理,就把问题反馈给模型,让模型继续修改。
而 Eval 的核心目标是衡量结果好不好。它既可以评价一个案例,也可以批量运行整个测试集。
例如单个案例:
用户问题
+
RAG Context
+
模型回答
↓
RelevancyEvaluator
↓
Pass / Fail
如果进一步准备 100 个测试案例:
100个 Eval Case
↓
分别执行 Evaluator
↓
聚合评价结果
↓
得到当前版本质量指标
于是可以比较:
v1 Prompt:87 / 100
v2 Prompt:93 / 100
因此更准确的关系是:
Evaluator-Optimizer
→ 评价之后继续修改当前任务结果
→ 属于运行时 Agent Workflow
Eval
→ 评价结果质量
→ 可以作用于单个 Case、整个 Dataset、
RAG、Tool Calling 或完整 Agent
Regression Test
→ 把一批固定 Eval Case
在系统修改以后重新运行
→ 比较不同版本有没有提升或退化
还有一个很重要的细节:两者可以使用相似的评价技术。
例如都可以使用 LLM-as-a-Judge:
Evaluator-Optimizer:
回答
↓
Judge LLM
↓
"不完整,需要补充来源"
↓
重新生成
而离线 Eval 中:
回答
↓
Judge LLM
↓
得分:3 / 4
↓
记录到测试结果
Spring AI 官方现在甚至已经给出了基于 Recursive Advisor 构建 LLM-as-a-Judge 自我修正循环的示例,这说明"评价"本身既可以用于测试,也可以进入运行时优化循环。区别主要在评价结果是否继续驱动当前任务重新生成。
因此以后最好统一记成一句:
Evaluator 是"怎么评价";Eval 是"建立评价过程";Evaluator-Optimizer 是"评价以后根据反馈继续优化当前结果";回归测试是"把一组 Eval Case 在新版本上重新跑一遍"。
八、Agent 出错时,先判断错误发生在哪一层
到这里,一个 Agent 已经包含很多组件:
用户
↓
Routing
↓
Memory / Context
↓
RAG
↓
LLM
↓
Workflow / Planning
↓
Tool
↓
外部系统
↓
Structured Output
因此用户最终看到一句错误回答时,原因可能完全不同。
例如:
知识库根本没有退款规则
→ Knowledge 问题
知识库有规则,但 RAG 找错文档
→ Retrieval 问题
正确文档已经进入 Prompt,模型理解错误
→ Prompt / Model 问题
模型判断正确,但调用错 Tool
→ Tool Calling 问题
Tool 正确,但数据库数据错误
→ Business Data 问题
答案正确,但 JSON 格式错误
→ Output 问题
这就是后续模型效果优化最重要的前置认识:
大模型应用的最终效果来自多个组件共同作用,所以优化工作首先需要定位错误发生在哪个环节。
Spring AI 的 Observability 也围绕这种思想设计。当前框架能够记录 ChatClient、Advisor、ChatModel、Tool Calling、EmbeddingModel 和 VectorStore 等组件的 metrics(性能指标) 与 traces,为后续问题定位提供基础。
九、当前阶段的完整心智模型
现在可以把 Spring AI Agent 理解成三个层次:
第一层:信息
Memory
RAG
Context
Business Data
↓
第二层:决策与编排
LLM
Routing
Planning
Workflow
Evaluator
↓
第三层:执行
Tool
MCP
Java Service
Database / API
外围还有一层工程保障:
Structured Output
Validation
Observability
Eval
Error Handling
因此,成熟的 Agent 开发已经越来越接近普通软件工程:模型只是其中一个概率性组件。Java 继续负责状态、权限、事务、流程和可靠执行,LLM 负责语言理解以及适合动态处理的决策。
下一篇就可以正式回答一个更加实际的问题:
当大模型应用效果不好时,到底应该改 RAG、Prompt、Tool、Workflow,建立 Eval,还是进一步进行 SFT、LoRA、量化和蒸馏?
届时可以按照"知识缺失 → 检索错误 → 上下文利用错误 → 工具执行问题 → 偶发 Badcase → 稳定能力缺陷 → 成本与延迟"的顺序建立一套完整的模型效果优化判断路径。