AI 时代,我们需要怎样的工作流引擎?(一)
当下,工作流引擎已经成为了 AI 时代的重要基础设施,常见于各种 AI 落地场景,一些专为 AI 编排而生的工作流平台也如雨后春笋般不断出现。这便引发了一个思考,相对于过去,AI 时代对工作流引擎有哪些要求?
工作流引擎主要解决了哪些问题
在谈论 AI 与工作流的结合之前,必须得先从工作流引擎的本质入手 —— 为什么需要工作流引擎?工作流引擎解决了哪些问题?我个人认为,工作流引擎主要解决了 4 个方面的问题:
流程结构化
异构系统的集成
长事务
提升抽象层次
流程结构化
就像编程语言会为用户提供分支、循环、异步(async/await)、异常处理(try/catch/finally)、模式匹配等控制结构,工作流引擎也必须提供表达能力足够强的控制抽象用来描述各种复杂的流程逻辑。比如 BPMN 提供了 Sequence、Gateway、子流程等抽象,来满足各种复杂的工业级流程场景。工作流引擎采用的控制抽象,决定了流程建模的形式、具体方法和覆盖业务场景的多寡。 可以通过 Workflow Patterns 来衡量一款工作流引擎表达能力的强弱。
对于工作流引擎的实现而言,最重要的不是当下提供了多少控制类型,而是可以被不断扩展出更多类型,甚至让用户自己就很容易添加需要的控制,实现针对特定场景的二次开发。这就要求工作流引擎的底层架构不能够与这些控制完全绑定,而是要采用更抽象的实现,比如基于事件循环构建的有限状态机,就如同一些编程语言的虚拟机一样,可以在后续更新中不断添加新的指令,或者也可以通过在流程定义层面提供类似“宏”、“模板”这样的机制,让用户能够自定义更适合自身业务场景的控制结构。
异构系统的集成
现实世界的多数业务流程都需要跨组织和角色进行协作,这些角色可能是应用程序,远程服务,也可能是人。工作流引擎需要根据业务流程的编排,将任务从一个角色传导到另一个角色。这里的难点在于,这些角色完全是异构的,没有统一的通信标准,而且通信方式既有同步也有异步。比如,有本地执行的代码,有微服务,有各种数据库,有通过消息触发,有执行命令行,还有通过 Email 或者 UI 进行人机交互。工作流引擎需要接纳这些差异巨大的系统,与它们进行通信,并尽可能降低各种系统的接入成本,提升开发体验,还要通过重试、路由、死信队列(Dead letter queue)等手段来保障通信的可靠性。
长事务
日常说到事务,多指 OLTP 数据库所支持的 ACID 事务,作用域通常仅限于单一数据库,同步执行,快速响应,结果明确:要么成功,要么失败回滚。我们可以称这种事务为“短事务”。
工作流引擎所提供的是跨越多个异构系统的“长事务”:即便流程中某些环节出现错误,甚至工作流引擎自身出现故障,那么也能通过某些手段不断进行修复,让整个流程的最终状态是确定的。工作流引擎必须保障流程不会因故障丢失状态,也不会在损坏的状态下彻底终止,无法修复。
工作流引擎不能对流程的可靠性做出任何假设,而是应当认为错误是常态,总是会发生,对流程运转过程中的任何错误都能做出响应,并采取适当的手段进行补救。诸如以下常见的手段:
正向恢复 (Forward Recovery):如果中途某个任务执行失败,那么会一直对它进行重试,直到成功为止。
反向恢复 (Backward Recovery):为每个任务都设计一个补偿操作,当一个任务失败后,已经执行过的任务会进行补偿,最终让流程完全回滚,如同没有执行一般,即我们通常所说的 Saga 事务。
人工处理:通知人工进行处理,手工进行事务补偿或者状态修复。
这些手段并非是孤立的,而是可以配合起来使用:比如优先使用正向恢复,如果达到了设置的最大重试次数依然没有恢复正常,那么可以通知人工实施事务补偿。
要实现长事务,工作流引擎的底层实现必须具备以下关键点:
统一的事务管理:在复杂流程当中,状态可能涉及到多个方面,比如事件源的消费进度、任务之间的共享数据、流程的执行进度、历史记录等等,需要将这些分散的状态纳入到统一的事务当中,才能保证状态的一致性。简单的做法是将所有的状态数据使用同一个数据库进行存储,这样就能通过数据库自身的“短事务”来实现一致性,而如果这些状态分散在多个异构系统当中,则必须通过一些复杂的协议来实现统一事务,比如引入 XA 事务管理器。
工作流进程本身能够从崩溃中恢复,这种恢复可以有不同的方式,比如:
通过副本自动恢复:当一个进程崩溃时,会有其它的副本无缝接替它的工作,用户往往感知不到任何故障发生。这种恢复方式虽然表面看上去用户体验良好,但实现成本比较高昂 —— 需要共识算法来在多个节点之间达成崩溃的共识,还需要冗余资源,以及考虑状态在副本之间的一致性。而且,这种自动切换在现实的运维场景中,情况往往并没有那么理想,总会有些意想不到的问题需要手工干预,甚至形成微妙的连锁反应。
崩溃-重启(Fail - Recover)式恢复:即通过重启来恢复崩溃的工作流进程。通过将应用部署到 Kubernetes 或者使用 Supervisord 这样的简单运维工具就可以实现这一点,工作流引擎无需提供额外的支持,只需要严格保证状态的操作满足 ACID 事务即可,这样状态不会因为崩溃而损坏。这种方式可能会让用户感知到中断的产生,但考虑到绝大多数工作流的应用场景并不需要即时响应,而且都是异步执行,UI 上也有很多优化的空间(用户在前端只是看到流程正在执行这一步,至于中间发生了什么,则完全不需要知道)。重启大法尽管简单直接,但对工作流而言,这种方式往往是效果最佳成本最低的恢复方式。
流程隔离:工作流引擎往往需要同时调度多个流程实例,确保这些实例之间是完全隔离的,一个实例出错不会影响其它的实例。
可介入的生命周期:需要为流程的各个生命周期事件提供一系列的“切点”,让用户在流程的状态发生变更时能够自定义操作,对于长事务,最重要的是在错误发生时,向相关的系统或者人发送消息。
可观测:除了工作流引擎可以主动向外部发送通知,运维人员或者其它系统也必须能够非常容易就可以观测到工作流引擎的执行情况,比如错误日志、流程的执行历史、各种性能指标、流程的执行状态等等。
防呆机制:工作流引擎必须为用户提供能够强制手动干预流程的方法,可以手动控制流程的执行或者修改流程的状态甚至是定义,无论流程目前正处于何种阶段,这些操作必须具有最高优先级,能够中断当前的活动,并且同时需要保证状态的一致性。
在过去,每当谈及工作流,我们想到的都是流程自动化相关的场景,对于长事务却谈的很少。但现在,即便是很简单的应用,也往往需要涉及到各种云服务、微服务、中间件之间的交互,我觉着未来工作流在长事务方面的需求甚至可能超过流程自动化的需求,成为构建可靠的跨系统服务的必不可少的组件。
提升抽象层次
软件工程的发展,是通过不断提升抽象层次来实现的,从机器指令,到具有少量结构的汇编语言,到 C 这样的全面结构化的编程语言,再到完全看不见硬件痕迹的面向对象或者函数式编程。从编排指令,到编排语句,再到编排函数、对象和模块,软件行业的生产力得到了极大的飞跃,让规模庞大的系统得以诞生,也让更多不同背景的人得以参与生产 —— 即便不熟知计算机底层的硬件细节,也可以编写代码。
但每一次提升也同时伴随着一些代价,比如性能的损失、难以使用特定的硬件功能、难以进行更细致的性能优化、只能受限于编程语言自身所提供的表达能力、程序员更容易被公司替换压榨等等。
在这场波澜壮阔的“提升运动”当中,工作流更进了一步:可以编排服务、设备甚至是人。这让更多的用户得以将自己的工作自动化或者半自动化,甚至可以通过拖拽图形界面而非使用编程语言来开发程序。在各种 Codeless 产品当中,工作流引擎也就成为了产品的核心。
尽管提升了生产效率,但也同样产生了代价,其中最大的代价便是让逻辑的表达力度变得更粗,尤其是当图形界面作为主要表达方式的时候。使用图形界面看上去很美好,但是当面对真实业务场景那些复杂的逻辑时,它会造成灾难,无论是逻辑表达能力、调试还是可维护性,图形都远不如编程语言。所以,一款工作流引擎,如果只提供了图形这一种表达方式,跟 GUI 深度绑定,并且以“不需要写代码”作为卖点,那它在应对那些真实复杂的业务场景时往往是无力的。
一款设计良好的工作流引擎,应该提供多个层次的定义,而非将自己完全绑定在单一的抽象层次,这样才能满足不同场景和用户的需求。
比如,底层使用 Java 这样的编程语言来定义流程,中间层使用 Groovy 或者是结构化的标记语言(XML/YML)来构建 DSL ,最上层则是图形界面。专业程序员可以直接使用 Java 来定义流程,开发复杂逻辑以及给上面两层编写更多可复用的自定义组件;前端通过将最上层的 GUI 转换为中间层的 DSL 来实现流程定义;业务人员则无需接触任何代码(可能需要写一些简单的表达式),直接使用最上层的图形界面。最终,所有形式的流程定义都会落实为工作流引擎最底层统一的原语。通过这种方式,才能在用户体验和复杂业务场景之间取得一个较好的平衡。
关于 AI
在如今的 AI 时代,工作流的用武之地变得更多,但其解决的关键问题,核心依然还是上述四点,无论是我们将 AI 嵌入到流程当中,还是让 AI 决策流程的执行,甚至是直接让 AI 来创造流程。我们在引入一款工作流引擎时,依然需要在架构层面围绕这四点来展开思考和评估。
但同时,AI 时代也带来了非常多的变化,尤其是更多的不确定性,除了对以上核心四点产生了很多全新的更高的诉求,也有很多额外的诉求。这篇已经写得很长了,我们将会在下一篇来探讨这些新的诉求。
版权声明: 本文为 InfoQ 作者【阿呆】的原创文章。
原文链接:【http://xie.infoq.cn/article/acce51c800f6ec8cf4b888dd9】。文章转载请联系作者。







评论