基于 Spring AI Alibaba WorkFlow 构建 Java 生产级 AI Agent

LangGraph 是 Python 生态广为人知的 Agent 状态图编排框架,凭借循环分支、检查点持久化、人在回路等能力被广泛使用。但对于 Java Spring 技术栈开发者,直接使用 LangGraph 会带来跨语言的运维与集成成本。Spring AI Alibaba WorkFlow 在设计理念上对标 LangGraph,提供图、节点、条件分支、检查点持久化、任务中断恢复一整套编排能力,让开发者可以在 Java/Spring 体系内实现复杂 AI Agent。本文结合工程样例讲解这套组件落地生产 AI Agent 的实践要点。

⚠️版本说明:Spring‑AI‑Alibaba 0.3.x+ 正式提供 WorkFlow 能力。0.3.x 系列属于早期版本,部分 API 存在变动可能性,升级版本时需要查阅官方迁移文档做好兼容性适配;Spring AI Alibaba WorkFlow 在设计理念对齐 LangGraph,但 API 是 Java 面向对象风格,并非直接移植 LangGraph。

一、概念对照:LangGraph - Spring AI Alibaba WorkFlow

表格

LangGraph(Python) Spring AI Alibaba WorkFlow(Java) 说明
StateGraph WorkFlowGraph 定义整个图,节点、边、条件分支
State WorkFlowState 全局状态,存储 Agent 全部上下文、子任务、中间结果
Node WorkFlowNode 业务节点(推理、工具调用、结果解析)
Edge WorkFlowEdge 普通流转边,A 节点执行完流转到 B
Conditional Edge ConditionalWorkFlowEdge 条件分支,根据状态选择下一个节点,实现 ReAct 循环
Checkpoint WorkFlowCheckpoint 检查点 / 持久化,断点续跑、Worker 崩溃恢复
Human‑in‑the‑Loop interrupt WorkFlowInterrupt 任务中断,人工介入之后恢复执行(人在回路)
MemorySaver / RedisSaver CheckpointRepository 状态持久化仓库:内存、Redis、数据库

核心思想对齐:

  1. 所有信息收敛到 State:不要把任务状态塞到大模型对话历史,全部放到WorkFlowState,对应前文生产原则:禁止完全依赖 LLM 上下文保存任务状态,外置状态机存储。
  2. 支持循环:ReAct"思考‑行动‑观察" 循环,依靠条件分支实现。
  3. 支持中断恢复:高风险操作触发 interrupt,人工审批完成,从 Checkpoint 继续执行。
  4. 可持久化:任务崩溃、服务重启,可以从最近 Checkpoint 恢复,不会重复调用工具、重复请求大模型。

二、架构视角:WorkFlow 对应 Agent 八大模块

WorkFlowGraph 本身是流程编排运行时底座,不是完整 Agent。

  • WorkFlowGraph + WorkFlowState = 规划模块 + 有限状态机 FSM
  • 各个 Node 内部实现:感知、大模型推理、工具调用、执行反馈逻辑
  • CheckpointRepository = 任务状态持久化
  • WorkFlowInterrupt = 人在回路
  • 记忆模块(向量库记忆、短期会话记忆)、管控安全、复盘评估属于外部组件,在 Node 内部调用。

重要:WorkFlow 只管流转与状态;记忆、安全校验、工具权限校验依然需要自己实现,框架不会自动处理业务安全。

三、Spring AI Alibaba WorkFlow 快速入门:实现 ReAct Agent(Java 完整示例)

3.1 Maven 依赖xml

复制代码
<dependencies>
    <!-- Spring AI Alibaba WorkFlow核心 -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring‑ai‑alibaba‑workflow</artifactId>
        <version>0.3.2.0</version>
    </dependency>
    <!-- Spring AI 大模型核心,通义千问 -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring‑ai‑alibaba‑dashscope‑spring‑boot‑starter</artifactId>
    </dependency>
    <!-- Redis Checkpoint持久化,生产必须;Demo可以用内存存储 -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring‑ai‑alibaba‑workflow‑redis</artifactId>
    </dependency>
</dependencies>

3.2 自定义 Agent 状态 WorkFlowState

对应 LangGraph State,存放全部任务信息,不要依赖 ChatMemory 承载任务进度。

复制代码
import com.alibaba.cloud.ai.workflow.WorkFlowState;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;

@Data
public class AgentState implements WorkFlowState {

    /** 唯一任务ID,对应TraceId */
    private String traceId;

    /** 用户原始query(感知模块输出) */
    private String userQuery;

    /** 大模型推理输出 */
    private String llmThought;

    /** 需要调用的工具名称 */
    private String toolName;

    /** 工具调用参数JSON */
    private String toolArguments;

    /** 工具返回观测结果(observation) */
    private String toolObservation;

    /** 最终回答,任务完成输出 */
    private String finalAnswer;

    /** 记录迭代轮次,防死循环,对应最大迭代轮次限制 */
    private int iteration = 0;

    /** 子任务列表,规划模块产出 */
    private List<String> subTasks = new ArrayList<>();

    /** 是否任务完成标记 */
    private boolean isFinished = false;

    /** 是否触发人工中断(人在回路标记) */
    private boolean needHumanInterrupt = false;

    @Override
    public String getWorkflowInstanceId() {
        // workflow实例唯一ID,checkpoint以此为key
        return traceId;
    }
}

3.3 定义数据传输实体 ReActOutput

用于接收并解析大模型 ReAct 格式输出。java

复制代码
import lombok.Data;

@Data
public class ReActOutput {
    /** 思考过程 */
    private String thought;
    /** 工具名称 */
    private String toolName;
    /** 工具入参 */
    private String args;
    /** 最终回答 */
    private String finalAnswer;
    /** 是否结束任务 */
    private boolean finish;
}

3.4 定义工具抽象接口 ToolProvider

统一工具调用抽象,便于实现工具白名单、参数校验逻辑。

java

复制代码
public interface ToolProvider {
    /** 判断工具是否在白名单 */
    boolean isAllowTool(String toolName);
    /** 执行工具调用 */
    String callTool(String toolName, String toolArguments);
}

3.5 定义各个 Node 节点(对应 Agent 业务逻辑)

ReAct 循环链路:用户输入解析节点 → LLM推理思考节点 →【条件判断】  - 如果需要调用工具:进入工具调用节点 → 返回推理节点(循环)  - 如果任务完成:进入输出结果节点结束  - 如果迭代超限 / 需要人工审批:触发 interrupt 人工中断

Node1:输入解析节点
复制代码
import com.alibaba.cloud.ai.workflow.WorkFlowNode;
import com.alibaba.cloud.ai.workflow.context.WorkFlowContext;

public class InputParseNode implements WorkFlowNode<AgentState> {

    @Override
    public AgentState execute(WorkFlowContext<AgentState> context) {
        AgentState state = context.getState();
        // 上层传入用户query;生产这里做输入清洗、脱敏、Prompt注入检测
        String rawInput = context.getVariable("rawUserInput");
        state.setUserQuery(rawInput);
        state.setIteration(0);
        state.setFinished(false);
        return state;
    }
}
Node2:LLM 推理思考节点
复制代码
import com.alibaba.cloud.ai.workflow.WorkFlowNode;
import com.alibaba.cloud.ai.workflow.context.WorkFlowContext;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.response.ChatResponse;

public class LLMReasonNode implements WorkFlowNode<AgentState> {

    private final ChatModel chatModel;

    public LLMReasonNode(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    @Override
    public AgentState execute(WorkFlowContext<AgentState> context) {
        AgentState state = context.getState();
        // 迭代计数,防止死循环,生产最大迭代轮次配置,例如10轮
        state.setIteration(state.getIteration() + 1);
        if(state.getIteration() > 10){
            state.setNeedHumanInterrupt(true);
            return state;
        }

        // 组装prompt:带入历史工具观测结果,实现ReAct思考
        Prompt prompt = buildReActPrompt(state);
        ChatResponse resp = chatModel.call(prompt);
        String content = resp.getResult().getOutput().getText();

        // 生产这里做JSON Schema解析容错,区分故障类型
        ReActOutput parseResult = parseReActOutput(content);
        state.setLlmThought(parseResult.getThought());
        state.setToolName(parseResult.getToolName());
        state.setToolArguments(parseResult.getArgs());
        state.setFinalAnswer(parseResult.getFinalAnswer());

        // 如果返回最终答案标记任务结束
        if(parseResult.isFinish()){
            state.setFinished(true);
        }
        return state;
    }

    private Prompt buildReActPrompt(AgentState state){
        // 业务自行实现:构造ReAct Prompt,带入userQuery、toolObservation
        return new Prompt("");
    }

    private ReActOutput parseReActOutput(String llmOutput){
        // 业务自行实现JSON解析、清洗容错逻辑,处理截断、多余文本等异常
        return new ReActOutput();
    }
}
Node3:工具调用节点(工具调用 + 执行反馈模块)
复制代码
import com.alibaba.cloud.ai.workflow.WorkFlowNode;
import com.alibaba.cloud.ai.workflow.context.WorkFlowContext;

public class ToolInvokeNode implements WorkFlowNode<AgentState> {

    private final ToolProvider toolProvider;

    public ToolInvokeNode(ToolProvider toolProvider) {
        this.toolProvider = toolProvider;
    }

    @Override
    public AgentState execute(WorkFlowContext<AgentState> context) {
        AgentState state = context.getState();
        // 生产安全校验
        // 1、工具白名单校验;2、参数前置校验;3、高危操作检测,高危设置needHumanInterrupt=true
        if(!toolProvider.isAllowTool(state.getToolName())){
            state.setToolObservation("error:工具不在白名单,禁止调用");
            return state;
        }
        // 执行工具
        String observation = toolProvider.callTool(state.getToolName(), state.getToolArguments());
        state.setToolObservation(observation);
        return state;
    }
}
Node4:输出完成节点
复制代码
import com.alibaba.cloud.ai.workflow.WorkFlowNode;
import com.alibaba.cloud.ai.workflow.context.WorkFlowContext;

public class OutputFinishNode implements WorkFlowNode<AgentState> {
    @Override
    public AgentState execute(WorkFlowContext<AgentState> context) {
        AgentState state = context.getState();
        System.out.println("Agent最终输出:" + state.getFinalAnswer());
        return state;
    }
}

3.6 构建 WorkFlowGraph,配置条件分支

重点:条件边 ConditionalWorkFlowEdge 实现 ReAct 循环;检测人工中断触发 Interrupt。

复制代码
import com.alibaba.cloud.ai.workflow.WorkFlowGraph;
import com.alibaba.cloud.ai.workflow.edge.ConditionalWorkFlowEdge;
import com.alibaba.cloud.ai.workflow.repository.CheckpointRepository;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AgentWorkFlowConfig {

    @Bean
    public WorkFlowGraph<AgentState> agentWorkFlowGraph(ChatModel chatModel,
                                                        CheckpointRepository checkpointRepository,
                                                        ToolProvider toolProvider){

        // 注册全部节点
        WorkFlowGraph<AgentState> graph = WorkFlowGraph.builder(AgentState.class)
                .addNode("inputParse", new InputParseNode())
                .addNode("llmReason", new LLMReasonNode(chatModel))
                .addNode("toolInvoke", new ToolInvokeNode(toolProvider))
                .addNode("outputFinish", new OutputFinishNode())
                // 设置checkpoint持久化仓库:Redis生产;Memory仅Demo
                .checkpointRepository(checkpointRepository)
                // 设置入口节点
                .entryPoint("inputParse")
                .build();

        // 普通边:输入解析完成,流转到推理节点
        graph.addEdge("inputParse", "llmReason");

        // 条件分支:ReAct核心循环逻辑
        graph.addConditionalEdge("llmReason", ConditionalWorkFlowEdge.<AgentState>builder()
                .condition(context -> {
                    AgentState state = context.getState();
                    // 优先级1:触发人工中断
                    if(state.isNeedHumanInterrupt()){
                        return "humanInterrupt";
                    }
                    // 优先级2:任务完成,输出结束
                    if(state.isFinished()){
                        return "finish";
                    }
                    // 优先级3:需要调用工具
                    if(state.getToolName() != null){
                        return "callTool";
                    }
                    // 默认回到推理,继续思考
                    return "reasonAgain";
                })
                .putTarget("humanInterrupt", graph.interrupt())
                .putTarget("finish", "outputFinish")
                .putTarget("callTool", "toolInvoke")
                .putTarget("reasonAgain", "llmReason")
                .build());

        // 工具调用结束,回到推理节点,继续ReAct循环
        graph.addEdge("toolInvoke", "llmReason");

        // 设置终止节点
        graph.setEndNode("outputFinish");
        return graph;
    }
}

3.7 运行 Workflow、中断恢复(人在回路示例)

复制代码
import com.alibaba.cloud.ai.workflow.WorkFlowExecuteResult;
import com.alibaba.cloud.ai.workflow.WorkFlowGraph;
import com.alibaba.cloud.ai.workflow.context.WorkFlowContext;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AgentController {

    private final WorkFlowGraph<AgentState> agentWorkFlowGraph;

    public AgentController(WorkFlowGraph<AgentState> agentWorkFlowGraph) {
        this.agentWorkFlowGraph = agentWorkFlowGraph;
    }

    /** 启动Agent任务 */
    @PostMapping("/agent/start")
    public String startAgent(@RequestParam String traceId,
                             @RequestParam String userInput){
        // 初始化上下文,传入外部变量
        WorkFlowContext<AgentState> context = agentWorkFlowGraph.createContext(traceId);
        context.setVariable("rawUserInput", userInput);
        // 执行,遇到interrupt会挂起,不会阻塞
        WorkFlowExecuteResult result = agentWorkFlowGraph.execute(context);
        if(result.isInterrupt()){
            return "任务挂起等待人工审批,traceId:" + traceId;
        }
        return context.getState().getFinalAnswer();
    }

    /** 人工审批完成,恢复被中断的任务,从Checkpoint断点继续执行 */
    @PostMapping("/agent/resume")
    public String resumeAgent(@RequestParam String traceId,
                              @RequestParam boolean humanApprove){
        WorkFlowContext<AgentState> resumeCtx = agentWorkFlowGraph.loadContext(traceId);
        AgentState state = resumeCtx.getState();
        state.setNeedHumanInterrupt(false);
        // 可将人工审批结果写入state供后续逻辑使用
        // state.setHumanApprove(humanApprove);
        WorkFlowExecuteResult result = agentWorkFlowGraph.resume(resumeCtx);
        return resumeCtx.getState().getFinalAnswer();
    }
}

关键特性说明:

  1. 如果使用 RedisCheckpointRepository,服务重启之后,调用loadContext(traceId)依然可以恢复任务,不会从头跑。
  2. graph.interrupt() 不会阻塞线程,长任务适合异步,不要用同步 HTTP 接口跑大循环 Agent。

四、WorkFlow 工程落地要点

4.1 状态设计避坑

  1. 不要把任务业务状态放在 ChatMemory 聊天记录,全部放入WorkFlowState;聊天历史只做 LLM 输入素材。
  2. State 中必须带上iteration迭代计数器,防止 ReAct 死循环;配置最大迭代上限。
  3. TraceId 作为 workflowInstanceId,打通全链路 Trace,用于日志、告警、故障排查。

4.2 Checkpoint 持久化(对应 Durable Execution)

Demo 环境可使用 InMemoryCheckpointRepository;该实现数据驻内存,服务重启数据全部丢失,严禁上生产。生产环境推荐 RedisCheckpointRepository,也可以自定义实现数据库存储。

注意:Checkpoint 保存的是 Workflow 流转状态,不等于 Agent 长期记忆。向量形式的业务记忆仍然需要单独接入 Milvus 等向量数据库。

4.3 人在回路 Human‑in‑the‑Loop

  1. interrupt 只用于不可逆高危操作,不要滥用,避免审批疲劳。
  2. 中断后,人工修改的业务数据写入AgentState,再 resume 恢复执行。
  3. 业务上增加超时机制:中断超过指定时长自动终止任务。

4.4 安全防护实现

编排框架本身不提供安全能力,相关逻辑需要在 Node 业务代码显式实现。

  1. 输入侧:输入清洗、脱敏,检测 Prompt 注入风险,隔离用户输入与系统 Prompt,禁止原始用户输入直接拼接系统指令。
  2. 工具侧:工具白名单、参数前置校验、高危操作识别,高危动作触发人工中断。
  3. 输出侧:大模型返回内容做合规校验,过滤违规输出,对关键业务输出做事实校验与证据溯源绑定。

4.5 记忆模块怎么和 WorkFlow 结合

WorkFlowGraph 没有内置记忆能力,需要在 Node 内部主动调用记忆组件:

  1. 推理节点执行前:在LLMReasonNode读取瞬时 / 短期 / 长期记忆,把召回的记忆内容送入 Prompt。
  2. 任务完成之后:在outputFinishNode,执行记忆写入逻辑,同时做记忆写入校验,防止记忆污染、记忆投毒。

4.6 成本与防死循环

  1. State 维护iteration迭代计数,条件分支直接拦截超限,强制 interrupt 或终止;
  2. 在 Node 中统计 token 消耗,state 记录 token 使用量,到达预算上限直接终止任务。

五、Java Agent 方案横向对比

表格

方案 特点 适用场景
Spring AI Alibaba WorkFlow 基于 Spring 生态,状态图编排,支持 Checkpoint、中断恢复;上层 Agent 能力需要自行封装 Spring Boot 业务系统,需要 Java 原生编排,需要断点续跑、人在回路
LangChain4j Java 生态 Agent 工具库,提供工具调用、记忆抽象;缺少原生状态图与持久化中断能力 简单 Agent、单轮 / 少轮任务,不需要复杂循环与断点续跑
自研状态机 完全自主可控,可深度定制;开发成本高,需要自行实现循环、检查点、中断等底层能力 高度定制、强监管业务,团队有充足研发人力

补充:Python 生态 LangGraph 可以通过 HTTP 接口供 Java 系统调用,适合团队混合技术栈,需要承担跨语言部署运维成本。

六、生产环境 Agent 测试策略

Agent 系统输出存在概率性、非确定性特征,传统单元测试不足以保障质量,需要分层测试体系。

  1. 确定性组件单元测试针对工具函数、参数解析器、状态流转逻辑、安全校验组件编写单元测试,保证基础模块行为稳定。

  2. Prompt 单元测试针对核心 Prompt 模板编写测试用例,覆盖正常场景、边界场景、Prompt 注入对抗用例,防止 Prompt 迭代后发生行为漂移。

  3. WorkFlow 集成测试编写集成测试,完整运行 Workflow 实例,校验状态流转、分支跳转、检查点持久化、中断恢复逻辑是否符合预期,不依赖大模型真实返回,可对 ChatModel 做 Mock。

  4. 端到端场景测试构建业务端到端测试集,包含普通业务用例、边界用例、对抗用例,运行完整 Agent 链路,评估任务完成率、工具调用准确率。

  5. 混沌测试主动注入故障,模拟工具调用超时、模型返回异常、记忆检索错误等场景,验证 Agent 的容错、重试、降级、终止逻辑。

  6. CI/CD 门禁将自动化测试纳入 CI 流水线,代码变更、Prompt 迭代自动执行测试套件,核心指标不达标阻止版本合入,防止能力退化。

七、生产环境落地架构建议

HTTP 接口 → 异步任务队列(Spring Async/RocketMQ)↓WorkFlowGraph(Spring AI Alibaba WorkFlow)├─ 各个 Node 节点│ ├─感知输入清洗、注入检测│ ├─记忆服务(瞬时 / 短期 / 向量长期记忆)│ ├─LLM 推理 + JSON 解析容错│ ├─工具服务(白名单、参数校验、沙箱)│ └─人在回路 Interrupt 中断├─Checkpoint Redis 持久化└─全链路 Trace 日志、指标监控、告警↓任务完成 → 复盘评估服务(高阶可选)

重要提醒:WorkFlow 解决的是任务流转、状态持久、循环分支、断点续跑;不等于开箱即用完整 Agent。管控安全、评估、测试、成本管控都需要业务侧自行实现。

八、生产环境常见风险点

  1. 不要直接使用 InMemoryCheckpointRepository 上生产,服务重启任务全部丢失。
  2. 业务状态全部存放在 ChatMemory,WorkFlowState 只保存少量字段,会引发任务漂移。
  3. ReAct 循环没有迭代计数器,大模型陷入死循环,持续消耗 Token 资源。
  4. 大量普通只读操作也使用 interrupt 中断,造成审批疲劳,降低人工审核有效性。
  5. 在同步 HTTP 接口执行长 Workflow,HTTP 超时断开,任务状态发生混乱。
  6. 将 Workflow Checkpoint 快照和向量长期记忆混淆,Checkpoint 只保存流程运行时状态。
  7. 忽略大模型输出解析容错,遇到模型输出格式异常直接引发流程崩溃。
  8. 缺少 Prompt 注入、输出合规校验,带来业务安全风险。
  9. 忽略 0.3.x 版本 API 迭代风险,升级版本没有查阅官方迁移文档。

九、总结

  1. Python 生态流行 LangGraph 做 Agent 状态编排,Java Spring 技术栈可选用 Spring AI Alibaba WorkFlow 实现理念相近的 Agent 图编排能力,支持循环、条件分支、Checkpoint 持久化、Interrupt 人在回路、断点续跑。
  2. WorkFlowGraph 只是编排运行底座,Agent 各类业务能力需要在 Node 中调用外部业务组件实现。
  3. 生产落地关键要点:外置状态 WorkFlowState、Redis 持久化 Checkpoint、迭代轮次防死循环、异步执行长任务、仅高危操作触发人工 Interrupt、完整输入输出安全校验、建立分层 Agent 测试体系。
  4. Java 技术栈存在多种 Agent 实现路线,需要结合业务场景、团队技术栈做选型权衡。
相关推荐
丘山录29 分钟前
本地部署 llama.cpp,3.9G 盒子只能开 5.8 万
人工智能
tinggu33 分钟前
从 $6.22 到 $113.96:拆解 AI 用纯 Python 在 Blender 5.2 中“手搓”百万面 3D 场景的工程真相
人工智能
Linye36 分钟前
AI 写的 HTML 原型全是空壳?聊聊真实 DOM 交互避坑
前端·人工智能
师夷长技以自愈1 小时前
手搓 AI Agent 第 7 篇:菜做砸了别硬炒——错误分类 + 双账本熔断
人工智能·python
用户112936112191 小时前
编译全绿,上线就崩:MyBatis 五层方法名大小写埋下的坑
java
调接口的阿光1 小时前
调用对话接口时输出被截断怎么办:先看 finish_reason,再决定是续写一次、缩小范围,还是换成更短的格式(Python)
人工智能·python
东鸿电子Eastron1 小时前
工厂节能降本落地指南:从精准能源计量搭建数字化管理基础
大数据·人工智能·能源·智能电表·能耗管理
师夷长技以自愈1 小时前
手搓 AI Agent 第 6 篇:先别动刀——Plan Mode 让 AI 报完方案才动手
人工智能
文丑颜不良啊1 小时前
LeetCode2901~2920题目解析
java·leetcode