AI 应用后端工程化:从原型到可交付系统 · 第 21 篇 · 第六章 · 编排与模型
用节点、边和变量表达执行图,再引入节点协议、数据传递和运行前校验,构成工作流引擎的核心。
拖拽几个方块、连上箭头,看起来就有了工作流。画布只是配置编辑器,真正的引擎必须判断图是否合法、变量从哪里来、节点以什么顺序执行、失败后怎样停止,并把每个节点结果传给下一步。
四个最小概念
flowchart LR
Start[开始节点] --> HTTP[HTTP 节点]
HTTP --> Transform[模板转换]
Transform --> LLM[模型节点]
LLM --> Retrieve[知识检索]
Retrieve --> End[结束节点]
Vars[(变量状态)] -.读写.-> HTTP
Vars -.读写.-> Transform
Vars -.读写.-> LLM
Vars -.读写.-> Retrieve
节点描述一个可执行单元,边描述允许的控制流,变量保存节点之间的数据,执行器根据图推进状态。开始和结束节点给流程明确边界;没有它们,入口数据和最终输出只能靠约定猜测。
节点需要统一生命周期
不同节点做的事情不同,但可以共享验证、运行和结果协议。
from dataclasses import dataclass
@dataclass(frozen=True)
class NodeResult:
outputs: dict[str, object]
metrics: dict[str, float]
class Node:
def validate(self, context) -> list[str]:
raise NotImplementedError
def run(self, inputs: dict[str, object], context) -> NodeResult:
raise NotImplementedError
实体保存配置,节点实现执行。模型节点可以验证模型是否可用,HTTP 节点验证 URL 与方法,知识检索节点验证数据集归属。运行前收集全部错误,比走到第五个节点才发现配置缺失更友好。
变量引用要可解析
模板中写 {{start.query}} 表示读取开始节点输出。引擎需要解析引用、检查源节点存在、字段类型兼容,并确认控制流上源节点一定先执行。字符串替换无法处理对象、数组和空值,也难以给出准确错误位置。
可以给每个节点声明输入输出 Schema,编辑器据此提示可用变量,引擎发布前进行类型检查。运行时状态按 node_id.output_name 保存,避免不同节点输出同名字段互相覆盖。
图验证比节点验证更重要
每个节点单独合法,组合仍可能错误:没有开始节点、存在环、结束节点不可达、一条分支永远没有输出、节点引用了后续节点变量。发布前至少检查唯一入口、可达性、边端点、允许的环和必填输出。
若产品支持循环,必须显式建模循环节点和最大次数,不能把普通图中的意外环当功能。并行分支需要合并语义:全部完成、任一完成还是允许部分失败。
数据集检索节点揭示了权限问题
检索节点配置一个数据集 ID,结构上很简单,运行时却必须确认该数据集属于当前应用或调用者。发布者有权限不代表所有终端用户都能读,权限策略要随执行上下文传入节点。
节点结果还应带来源与分数,后续模型节点才能引用证据。若只输出拼接文本,工作流失去了可观察性。
失败和重试属于节点协议
HTTP 超时可以重试,代码节点语法错误不能靠重试修复,模型限流需要退避。节点声明错误类型与幂等性,执行器根据策略决定重试、跳过、走错误分支或终止。
有副作用节点重试前需要幂等键。工作流整体重跑也不能重复发送邮件。执行记录保存 workflow version、node ID、attempt、输入摘要与结果引用,为恢复提供事实。
一个典型错误设计
编辑器把每条边的目标节点存进数组,后端收到配置后按数组顺序执行。用户在画布中移动节点或 JSON 序列化顺序变化,执行结果也变化;条件分支和并行更无法表达。
正确方式是由边建立邻接关系,通过拓扑或显式调度计算可运行节点。UI 坐标只负责展示,不参与业务顺序。
举一反三
CI Pipeline、审批流、ETL 和营销自动化都有节点、边、变量、校验、重试与执行记录。练习:创建开始、模板、HTTP、结束四个节点,故意加入不可达节点、错误变量引用和一个环,确认验证器能一次返回三类错误;修复后打印每步输入输出与状态。
五次提交从数据结构走向引擎
d864ac9:建立工作流实体与多种节点实现。4d39c8d:加入节点与边的示例配置,执行图开始可观察。ba18211:补充节点输入输出,变量流动进入模型。8269f5d:实现知识检索节点,外部能力接入图中。f78cd3a:重构处理与验证,工作流从配置对象走向可执行契约。
真正的分界点不是节点数量,而是图能否在运行前被证明合法、运行后被解释。这也是工作流引擎区别于一串函数调用的地方。