当前位置: 首页 > news >正文

Java 8老系统AI Workflow实战:把一次性AI对话升级成可恢复工作流

Java 8老系统AI Workflow实战:把一次性AI对话升级成可恢复工作流

很多 AI 应用刚开始都是一个聊天框。

用户输入需求,模型输出结果。

这适合原型验证,但进入企业流程后,很快会遇到问题:

中途失败怎么办? 关键节点谁确认? 上一次输出在哪里? 能不能从失败节点恢复? 每一步输入输出能不能审计?

所以第 9 讲要把一次性 AI 对话升级成工作流。

最终效果

代码目录:

code/spring-ai-enterprise-lab/labs/chapter09-ai-workflow

运行:

.\compile-and-run.ps1

Demo 会演示一个"需求到测试用例"的工作流:

需求提取 ↓ 接口设计 ↓ 人工审批 ↓ 测试用例生成 ↓ 发布检查清单

启动后,流程会停在api-design节点。

审批后,流程继续执行到COMPLETED

代码结构

核心代码分三块:

src/main/java/com/ynzz/lab/chapter09 ├── common │ ├── WorkflowStartRequest ← 工作流启动参数 │ ├── WorkflowRun ← 流程实例(运行时状态) │ └── WorkflowNodeSnapshot ← 节点快照 ├── nodes │ ├── RequirementExtractNode ← 需求提取节点 │ ├── ApiDesignNode ← 接口设计节点(含 HumanApproval) │ ├── TestcaseGenerateNode ← 测试用例生成节点 │ └── ReleaseChecklistNode ← 发布检查清单节点 └── runtime └── WorkflowRuntime ← 运行时引擎

WorkflowRuntime负责控制流程何时继续,何时停住等待审批。

WorkflowRuntime 的控制流

WorkflowRuntime的核心不是"调用几个 AI 节点",而是控制状态怎么流动。

当前 Demo 的start方法做了四件事:

创建 WorkflowRun ↓ 运行 RequirementExtractNode,写入第一个快照 ↓ 把 lastOutput 交给 ApiDesignNode,写入第二个快照 ↓ 把流程状态改成 WAITING_APPROVAL,waitingNodeId=api-design

这里的lastOutput()很关键。

它表示节点之间不是靠一大段聊天上下文传递信息,而是靠上一个节点的结构化输出继续往下走:

需求原文 ↓ RequirementExtractNode.output ↓ ApiDesignNode.input ↓ ApiDesignNode.output ↓ 审批通过后进入 TestcaseGenerateNode.input

节点之间的数据传递靠lastOutput()完成:它返回最近一个SUCCESSAPPROVED快照的output字段,作为下一个节点的input。快照格式里包含nodeIdstatusinputoutputapprovedByerrorCoderetryCount——失败时 Runtime 靠这些字段定位恢复点:status=SUCCESS/APPROVED的快照是安全恢复点,output是重跑失败节点的输入,retryCount用于控制重试次数。这样设计是为了避免失败后从头重跑整个工作流。

审批发生在api-design节点。

审批前,流程停住:

status=WAITING_APPROVAL waitingNodeId=api-design

审批后,approve方法会:

找到 api-design 快照 ↓ 写入 approvedBy,并把节点状态改成 APPROVED ↓ 把流程状态恢复为 RUNNING ↓ 运行 TestcaseGenerateNode ↓ 运行 ReleaseChecklistNode ↓ 把流程状态改成 COMPLETED

所以这个 Demo 真正想表达的是:企业 AI 工作流要能暂停、能继续、能看到每一步的输入输出,而不是把所有事情塞进一次模型调用。

当前 Stub 还没有实现失败节点回退,但它已经把恢复所需的最小结构留出来了:每个节点都有inputoutputstatus。真实落地时,失败节点应该记录FAILED、错误原因和重试次数,Runtime 从最近一个SUCCESSAPPROVED快照继续,而不是从头重跑。

每个节点都要有快照

启动工作流后,输出里会看到:

{"status":"WAITING_APPROVAL","waitingNodeId":"api-design","snapshots":[{"nodeId":"requirement-extract","status":"SUCCESS"},{"nodeId":"api-design","status":"WAITING_APPROVAL"}]}

这就是企业工作流的基本形态。

不是模型一次性吐出全部内容,而是每一步都有记录:

节点 ID 节点输入 节点输出 节点状态 审批人

对应到当前WorkflowNodeSnapshot,快照字段是:

{"nodeId":"api-design","status":"WAITING_APPROVAL","input":"需求点:订单超过 48 小时未发货时触发延迟预警,并提醒客服介入。","output":"POST /api/orders/delay-alerts,输入 thresholdHours=48,输出待介入订单列表。","approvedBy":""}

这样才能审计、复盘、重试和恢复。

四个节点的职责边界也应该讲清楚:

节点输入输出卡住或失败时的处理重点
RequirementExtractNode用户需求原文结构化需求点 + 不确定项列表需求含糊时输出不确定项,转人工确认;自身失败记录 errorCode,需求作为 input 重跑
ApiDesignNode需求摘要(lastOutput)接口草案(请求/响应/错误码)卡在 WAITING_APPROVAL;被驳回则带着修改意见重跑;自身失败从 requirement-extract 快照恢复
TestcaseGenerateNode审批后的接口设计(approved snapshot output)测试用例集合前置节点未审批则拒绝执行;自身失败记录 errorCode,从 api-design 快照恢复重跑
ReleaseChecklistNode测试用例输出(lastOutput)发布检查清单 + 待办项发布条件不完整时输出待办清单;自身失败从 testcase-generate 快照恢复;始终不输出"强制通过"

这张表比类名列表更重要。

因为企业工作流里,每个节点都要回答三个问题:接收什么、产出什么、什么时候停下来。

当前 Demo 完整跑一遍

本讲 Demo 的启动输入是:

tenantId=demo operatorId=u1001 requirementText=新增订单延迟预警功能:当订单超过 48 小时未发货时,系统需要提醒客服介入。

第一步,RequirementExtractNode接收原始需求,输出需求摘要:

input=新增订单延迟预警功能:当订单超过 48 小时未发货时,系统需要提醒客服介入。 output=需求点:订单超过 48 小时未发货时触发延迟预警,并提醒客服介入。 status=SUCCESS

第二步,WorkflowRuntime调用run.lastOutput(),把这段需求摘要交给ApiDesignNode

input=需求点:订单超过 48 小时未发货时触发延迟预警,并提醒客服介入。 output=POST /api/orders/delay-alerts,输入 thresholdHours=48,输出待介入订单列表。 status=WAITING_APPROVAL

然后 Runtime 把整个流程停住:

WorkflowRun.status=WAITING_APPROVAL WorkflowRun.waitingNodeId=api-design

这时还不会生成测试用例,也不会生成发布清单。

只有当审批调用发生:

runtime.approve(run, "api-design", true, "tech-lead")

Runtime 才继续执行后两个节点。

第三步,TestcaseGenerateNode接收已经审批过的接口设计:

input=POST /api/orders/delay-alerts,输入 thresholdHours=48,输出待介入订单列表。 output=测试用例:48 小时边界、未发货订单、已发货订单、重复提醒幂等、客服可见性。 status=SUCCESS

第四步,ReleaseChecklistNode接收测试用例输出:

input=测试用例:48 小时边界、未发货订单、已发货订单、重复提醒幂等、客服可见性。 output=发布检查:灰度租户、SQL 索引、告警阈值、客服通知模板、回滚开关。 status=SUCCESS

最后流程完成:

WorkflowRun.status=COMPLETED WorkflowRun.waitingNodeId=""

这条时间线说明了一件事:节点之间传递的是上一个节点的output,不是把所有原始上下文无限追加给模型。

失败恢复应该怎么落地

当前 Stub 只演示了成功和审批暂停,没有真正模拟FAILED。但它的快照结构已经能说明恢复设计。

真实项目里,节点快照至少应该扩展成:

{"workflowId":"wf-001","nodeId":"testcase-generate","status":"FAILED","input":"POST /api/orders/delay-alerts...","output":"","errorCode":"MODEL_TIMEOUT","retryCount":2,"createdAt":"2026-06-15T10:00:00","updatedAt":"2026-06-15T10:02:00"}

恢复时不是从需求提取重新开始,而是:

找到最后一个 SUCCESS / APPROVED 快照 ↓ 读取它的 output ↓ 作为失败节点的 input ↓ 重跑失败节点 ↓ 写入新的快照版本

比如TestcaseGenerateNode失败,最近的稳定节点是api-design

api-design.status=APPROVED api-design.output=POST /api/orders/delay-alerts... ↓ 重试 testcase-generate

这样才能做到"失败可恢复",而不是"失败后从头再问一遍模型"。

WorkflowStateRepository:为什么当前是 Stub,持久化后有什么不同

当前 Demo 的WorkflowStateRepository是内存 Stub,故意不依赖外部存储。原因有两个:

第一,降低上手成本。不需要装数据库、不需要配 Redis,跑compile-and-run.ps1就能看到完整工作流效果。

第二,先跑通流程,再落地存储。工作流引擎的正确性是第一步——节点拆分对不对、快照字段全不全、恢复逻辑合不合理。这些问题不依赖存储也能验证。

但内存 Stub 有明显局限:服务重启,所有快照丢失

真实落地时,WorkflowStateRepository应该持久化到数据库或 Redis。持久化后的差异:

维度内存 Stub持久化(DB / Redis)
服务重启快照全丢,工作流只能重新开始快照不丢,工作流从断点恢复
多实例部署不支持(每个实例内存独立)支持(共享存储)
审计与复盘无法实现可以查询历史快照
长时间运行的工作流不适合(可能 OOM)适合(存储外包)

当前 Stub 的定位很明确:先把工作流跑通,验证节点拆分和快照设计;持久化是下一步,但快照字段设计从一开始就要为持久化做准备——这也是为什么WorkflowNodeSnapshot里有一组结构化字段,而不是随便写一个output就完事。

关键节点必须经过 HumanApprovalNode

接口设计是一个关键节点。

如果接口设计错了,后面的测试用例、发布检查清单都会跟着错。

ApiDesignNode内嵌了HumanApprovalNode

ApiDesignNode 执行 ↓ 输出接口草案(请求/响应/错误码) ↓ HumanApprovalNode 暂停 ↓ 审批人看到草案 + 上一节点输出 ↓ 审批(通过 / 驳回) ↓ APPROVED → 继续下一个节点 REJECTED → 打回修改

审批人看到的是结构化输出,不是原始提示词界面。这是企业协作的基础——让人看到该看的,不需要理解 AI 内部逻辑。

不确定项要显式输出

本讲 Demo 会输出不确定项:

客服提醒渠道是短信、企微还是站内信,需要业务确认。

这类信息不能藏在正文里。

工作流报告应该显式列出:

哪些事情已确定 哪些事情待确认 哪些节点需要人工审批 哪些输出不能直接执行

企业 AI 应用不是追求模型看起来很自信,而是追求流程可靠。

企业避坑

第一个坑:不要把复杂任务都做成一次聊天。

复杂任务应该拆节点。

第二个坑:不要让关键节点自动越过审批。

接口设计、发布计划、生产变更都应该停一下。

第三个坑:不要丢失中间过程。

没有节点快照,失败后就很难恢复——更不要在内存里存快照,服务重启就全丢了。

第四个坑:不要隐藏不确定项。

AI 不确定的地方,应该变成流程里的待办。

从 Demo 到落地,还差什么

本讲 Demo 验证了"节点拆分 + 快照记录 + 人工审批 + 失败可恢复"的工作流基础,但企业 AI Workflow 落地还差几步:

状态持久化:当前 Stub 用内存模拟WorkflowStateRepository,真实项目需要把快照落库(PostgreSQL / MySQL)或存 Redis,配合定时清理策略,避免快照膨胀。

可视化审批界面:当前审批是代码里调用 API 触发,真实项目需要一个审批控制台(参考approval-console子模块),让非技术人员在页面上做审批操作。

工作流定义 DSL:当前节点定义是 Java 类,真实项目需要把工作流定义抽成 YAML 或 JSON,让业务人员可以配置流程而不需要改代码。

节点失败自动告警:某个节点连续失败 N 次后,应该自动通知工作流发起人,而不是等到人工发现工作流卡住了。

并行节点支持:当前 Demo 是串行节点(一个接一个),真实项目经常有"接口设计和测试用例生成可以并行"的需求,这需要WorkflowRuntime支持 DAG 调度。

第 9 讲的工作流编排能力,也是第 10 讲多 Agent 编排的基础——多 Agent 的输出最终也需要进入工作流、由人类做审批决策。


小结

企业 AI 应用的成熟形态不是:

用户提问 ↓ 模型回答

而是:

任务输入 ↓ 节点执行(快照持久化) ↓ 人工审批(关键节点) ↓ 失败恢复(从上一个成功节点) ↓ 最终报告

把 AI 放进工作流里,它才更像企业系统的一部分。

http://www.jsqmd.com/news/1110439/

相关文章:

  • 外贸独立站:从“没人看”到“AI主动推”,我只改了这几点
  • 零代码应用平台从0到1搭建指南
  • 终极Mac散热解决方案:如何使用smcFanControl让Intel Mac运行更凉爽
  • 嵌入式系统电源管理:TPS65263多路降压设计指南
  • 4个高价值时间序列预测实战项目:趋势、周期、多源与置信区间
  • 万亿参数模型为何只激活2%?MoE稀疏推理的工程真相
  • LeRobot训练可视化终极指南:3步解决机器人模型“黑箱“难题
  • Claude 4移除System Prompt层:架构坍缩与工程重构指南
  • 大模型参数量与MoE激活机制:如何辨别技术谣言与工程事实
  • Claude 3.5 ZeroLayer:胶水层归零与原生推理重构
  • 如何智能激活Windows和Office:KMS_VL_ALL_AIO终极指南
  • 豆包实测:中文会议纪要AI如何实现语义级理解与决策级输出
  • 大模型应用栈的‘层蒸发’:从中间件冗余到协议内聚
  • 2026年南京大学生CPA培训指南:选对机构成就未来
  • 豆包专家模式与超能模式的本质区别与协同用法
  • 宠物家庭选添可、追觅还是石头?真实养宠用户的购买反馈
  • LangChain Pandas Agent:用自然语言驱动数据分析的实战指南
  • 电磁干扰的“四条暗道“与屏蔽接地的“防御工事“:硬核拆解工业级EMC设计的底层逻辑
  • 工业4-20mA电流环设计与DAC161S997应用解析
  • AI Agent记忆管理优化:压缩技术与动态分配实战
  • AutoCAD_2026安装教程
  • GPT-4稀疏激活原理:揭秘2%参数如何驱动万亿模型
  • mysql数据库知识个人记录
  • Claude语义压缩层蒸发:AI可控性向结果可信性的范式迁移
  • 中文会议纪要AI生成:96%准确率背后的语义理解工程
  • 3分钟快速上手:B站缓存视频转换工具m4s-converter完全指南
  • 海外网红营销:头部网红vs中腰部网红,2026年品牌预算该往哪投?
  • 终极指南:5分钟快速部署Home Assistant智能家居操作系统
  • Windows系统文件BdeHdCfgLib.dll丢失找不到问题解决
  • 企业微信生态下的复杂审批流微服务治理架构