今天很高兴又来和大家分享我们的开源AI可视化工作流项目。
过去一个月,我们利用业余时间,一直在做一件事:让调用大模型这件事,变得像写代码一样可靠、可控、可调试。
所以这个项目的名字叫 Smart Flow,一个面向Agent的AI工作流编排平台。
现在,我们把它完整开源了。

这篇文章除了聊产品,我还会把架构设计、执行流程和核心代码实现一起摊开给大家看。
github:github.com/MrXujiang/s...
✦ ✦
一、为什么我们要造这个轮子
大模型火了之后,我们和很多团队一样,开始用工作流把一个个AI能力串起来。但用得越深,越痛苦:流程一长,调试全靠猜。
哪个节点出了问题?模型上一次到底返回了什么?中间变量长什么样?大多数平台只能给你一个最终结果,过程全是黑盒。
我们调研了市面上不少优秀产品,但作为开发者,总觉得差一口气:它们把工作流当「配置」,而我们想把工作流当「代码」------是代码,就应该能打断点、能单步、能看变量。

等不来,我们就自己做。于是有了 Smart Flow。
二、Smart Flow 是什么

一句话介绍:给开发者用的AI工作流IDE。
我们可以在可视化画布上拖拽节点、连线成流;
可以像调试代码一样单步执行;
可以一键把工作流发布成 Agent 直接用;
还能用 Webhook 和定时任务把它接进真实业务。
更关键的是:它零配置 。不用装数据库,一条命令 npm run dev,前后端一起跑起来。
三、Smart-Flow 亮点细节分享
1. 像调试代码一样调试工作流

这是 Smart Flow 和其他平台最大的不同:断点、单步执行、变量实时监控、Mock数据,四件套齐全。
在节点上点一下设置断点,运行到那里自动暂停,每个节点的输入输出看得清清楚楚。复杂流程出了问题,不再靠猜,而是靠「看」。
2. 17种节点,自由组合

除了大模型节点,我们还内置了意图分类、JSON提取、文本加工这类AI原生节点,以及代码、HTTP请求、条件分支、循环、子工作流这些工程节点。AI负责「想」,代码和HTTP负责「做」,一张画布全搞定。
3. 说句话,就能生成工作流

内置AI构建器:用一句自然语言描述需求,自动生成工作流草稿,再放到画布上微调。从「想法」到「能跑的流程」,只要几分钟。
4. 一键发布为Agent + 真实业务自动化

编排完之后,可以一键发布成Agent,对话页直接多轮调用;每个工作流还能生成Webhook地址、支持Cron定时执行,带HMAC签名和限流保护。它不是Demo,是真的能上生产的。
四、整体架构:一张图看懂
先上架构图。整体架构设计我们采用了经典的三层结构,但每一层我们都做了「为AI工作流定制」的设计:

几个关键选型的原因:**
**
1. 画布 用FlowGram(和Coze同源),拖拽和连线体验是企业级的;**
**
2. SQLite 「一条命令跑起来」的低成本本地数据库方案;**
**
3. SSE事件流 让前端能实时看到每个节点的执行状态,调试面板里的变量随时调整可配置,还能实时预览, 调试效率拉满。
五、运行的完整流程分享
再看流程图。从触发到出结果,一共六步:

而节点之间的数据流转,长这样(用占位符引用上游输出):

六、核心实现拆解(附代码)
下面是我觉得最值得聊的三段核心代码,都做了简化,但逻辑和真实实现一致。
1. 执行引擎:按「波次」推进的拓扑调度
工作流不是一条线,而是一张图。我们没有用简单的「谁排前面谁先跑」,而是先统计每个节点还有几个前置没完成(入度),每轮挑出「前置全部完成」的节点组成一波,并行执行:
server/src/engine/graph-scheduler.ts(简化)
// 统计每个节点的前置依赖数(入度)
const indegree = countInputs(nodes);
// 第一波:没有前置的节点(start)
let wave = nodes.filter(n => indegreen.id === 0);
while (wave.length) {
// 同一波内互不依赖,并行执行
await Promise.all(wave.map(n => this.runNode(n)));
// 完成的节点让下游入度 -1,挑出下一波
wave = nodes.filter(n => !done(n) && indegreen.id === 0);
}
// 跑完还有没执行的?说明图里有环,直接报错
if (hasPending()) throw new Error('Workflow contains a cycle');
这样做的好处很实在:顺序绝对正确 (一个节点的所有上游都跑完它才跑,不会读到空值),能并行的自动并行(省时间也省Token),还能顺手检测出「循环连线」这种画图失误。
2. 数据流转:{{ }} 模板插值
节点之间怎么传数据?我们在所有字符串配置里支持 {{ 表达式 }},核心就是一个带路径查找的替换:
server/src/engine/template.util.ts(简化)
// "订单 {{ input.order_id }} 的摘要:{{ nodes.llm_1.output }}"
// 整串只有一个表达式时,直接返回原始值(保留对象/数字类型)
const single = template.match(/^\s*{{(\^}+)}}\s*$/);
if (single) return resolvePath(single1, ctx);
// 否则逐个替换:null 变空串,对象序列化成 JSON
return template.replace(/{{(\^}+)}}/g, (_, expr) => {
const value = resolvePath(expr, ctx);
if (value == null) return '';
return typeof value === 'object' ? JSON.stringify(value) : String(value);
});
有个细节我们很得意:如果整个字符串就是一个表达式,会保留原始类型------上游传数组,下游循环节点拿到的就是真数组,而不是被转成字符串的「假数组」。
3. 代码节点:vm 沙箱 + 超时保护
让用户跑自定义代码,安全是第一位的。我们把代码包进一个函数,扔进Node.js的vm沙箱里执行,并加上超时:
server/src/engine/node-executors.ts(简化)
const sandbox = { input, nodes, variables };
const context = vm.createContext(sandbox, {
codeGeneration: { strings: false, wasm: false }, // 关掉动态代码生成
});
const script = new vm.Script(
result = (function main(input, nodes, variables) {\n +
code + \n})(input, nodes, variables);
);
script.runInContext(context, { timeout }); // 超时直接掐掉
一段写死的死循环代码,最多卡住自己这几毫秒,拖不垮整个服务。这就是我们平台敢开放代码节点的底气。
4. 调试会话:断点为什么能「暂停」一个服务端正向执行的流程
很多人好奇这个。答案不复杂:引擎每执行完一个节点,都会问一次调试会话「这里要不要停」,要停就把执行上下文挂起,等前端发「继续/单步」指令再恢复。调试不是魔法,是在引擎主循环里埋的一个个「检查站」。

最后是可扩展性:新增一种节点,只需实现一个执行器函数再注册进引擎,前端加一份表单schema。我们特意把贡献门槛留得很低,欢迎社区伙伴来加节点。
七、它能用在哪些场景
· 智能客服:意图分类节点先分流,再走不同的大模型应答分支;
· 内容生产线:生成、润色、格式化串成流水线,定时批量产出;
· 订单与邮件处理:Webhook接收业务事件,大模型摘要提炼,再通知到团队;
· 数据巡检与日报:Cron定时触发,拉数据、做分析、推报告,全自动。
我们自己公司内部,客服分流和日报生成两条流已经稳定跑了几个月。
✦ ✦
八、写在最后
作为AI创业者,我对小团队的焦虑感同身受:人手有限,又想又快又稳。我们做的开源 Smart Flow,既是对社区的一点回馈,也是一种「自救」------越多人用,越多问题被发现和修复,产品才会越来越好。
项目已在 GitHub 完整开源,中文、英文、日文、韩文文档都备齐了,应用内还自带一份开发文档,方便大家快速的上手使用和二次开发。
项目地址:github.com/MrXujiang/smart-flow
如果它帮到了你,欢迎点个Star;有想法,Issue和PR都随时欢迎。
也欢迎在留言区和我聊聊~