Flowise 全景:可视化搭 LLM 应用,图即程序
30 秒导读: Flowise 是一个「可视化拖拽搭 LLM 应用」的 IDE。你在浏览器画布上把「聊天模型 / 检索器 / 提示词 / 智能体」拖成节点、连成线,存下来是一坨 JSON(叫
flowData);后端把这坨 JSON 当源码读,编译成一条真正能跑的 LangChain 流水线(或一个有状态的解释器),接到用户提问就执行、把 token 流式吐回。一句话:图即程序,后端是编译器 + 运行时。
1. 这是什么(零基础也能懂)
一句话定义: Flowise 把「手写 LangChain 编排代码」这件事,变成了「在画布上拖节点、拉连线」。
解决谁的什么问题: 假设你要做一个「带知识库的客服机器人」——用 LangChain 手写,你得自己 new ChatOpenAI()、new Retriever()、ConversationalRetrievalQAChain.fromLLM(...),再把它们拼起来、处理记忆、处理流式。Flowise 让你把这些每一样都变成画布上的一个框,用连线表示「谁的输出喂给谁的输入」,连完点保存就能对外提供一个 /prediction API。给不想手写编排、但懂 LLM 概念的工程师用。
它能做什么:
- 可视化编排 LLM 链(Chatflow)、多智能体(Multi-Agent / Sequential Agent)、以及新一代的 AgentFlow V2。
- 内置 380+ 个集成节点(模型 / 向量库 / 工具 / 记忆 / 文档加载器……,见 §7 代码地图)。
- 一键把画布变成 REST API、可嵌入聊天气泡、流式(SSE)对话。
- 队列模式水平扩展、执行追踪与评测(observe 包)。
用起来什么样: 装好后 npx flowise start,打开 http://localhost:3000,画布上拖三个框——ChatOpenAI → Conversation Chain → 连上一个 Buffer Memory——保存,就得到一个能对话的 API:
curl http://localhost:3000/api/v1/prediction/<chatflow-id> \
-X POST -H "Content-Type: application/json" \
-d '{"question": "你好,你是谁?", "streaming": true}'
一句话直觉/类比: 把 flowData 当源代码,把后端当编译器 + 运行时——你画的图不是「配置」,它就是这个应用的程序,后端负责把它翻译成可执行的东西并跑起来。这条直觉贯穿全部后续章节。
本节不碰底层。记住一件事就行:画布产出一张图,后端把图当程序执行。
2. 顶层全景(它大概怎么转)
Flowise 是一个 pnpm monorepo,六个包分工明确。README 的 👨💻 Developers 段只点了前四个核心包,另外两个(agentflow / observe)是后来抽出的可嵌入前端组件(依据:README.md 的 Developers 段 + 各 packages/*/package.json 的 name 字段)。
2.1 六包分工
| 包目录 | package name | 职责一句话 |
|---|---|---|
packages/server | flowise | 后端:Express API、把图编译成执行、SSE、队列(本讲的主角) |
packages/ui | flowise-ui | 前端:基于 ReactFlow 的拖拽画布,产出 flowData |
packages/components | flowise-components | 380+ 个集成节点,每个节点是一个实现 init/run 的类 |
packages/agentflow | @flowiseai/agentflow | 可嵌入的 AgentFlow V2 画布 React 组件 |
packages/api-documentation | flowise-api | 由 express 自动生成的 swagger-ui API 文档 |
packages/observe | @flowiseai/observe | 可嵌入的「执行 / 评测 / 运行时」观测组件 |
2.2 一张图看懂主数据流
怎么读这张图:从上往下是一次对话的生命周期;左边是「设计期」(画图存图),右边是「运行期」(提问→执行→流式回)。命中哪条执行引擎,取决于 chatflow.type(§2.3)。
设计期(浏览器画布)
拖拽节点 + 连线 ──► ReactFlow.toObject() ──► flowData(JSON) 存进 DB
(packages/ui) (ChatFlow 实体)
│
════════════════════════════════════════════════════════│═══════════════
运行期(一次 /prediction) ▼
① 载入 ChatFlow
POST /prediction/:id │
│ ▼
▼ ② NodesPool 提供节点池
controllers/predictions ──► services/predictions ──► utils/buildChatflow
(设 SSE 头 / 建流) (薄封装) utilBuildChatflow
│
按 chatflow.type 分流 ③
┌─────────────────┴─────────────────┐
▼ ▼
经典引擎 executeFlow V2 引擎 executeAgentFlow
(CHATFLOW / MULTIAGENT) (AGENTFLOW)
拓扑构建→逐节点 init→ nodeExecutionQueue
ending 节点 run 取一个→跑→入队后继
│ │
└─────────────┬─────────────────────┘
▼
④ SSEStreamer 把 token/事件
逐条 SSE 推回浏览器
2.3 部件职责与落点
| 部件 | 干什么 | 在哪 |
|---|---|---|
| Prediction 控制器 | 校验来源、建 SSE 流、队列模式订阅 redis | packages/server/src/controllers/predictions/index.ts createPrediction |
| Prediction 服务 | 薄封装,调 utilBuildChatflow | packages/server/src/services/predictions/index.ts buildChatflow |
| 编译/执行总入口 | 组织数据、按 type 分流两大引擎 | packages/server/src/utils/buildChatflow.ts utilBuildChatflow、executeFlow |
| 节点池 NodesPool | 启动时扫描 components/dist/nodes,实例化每个节点类 | packages/server/src/NodesPool.ts NodesPool |
| 经典引擎 | 拓扑 BFS 构建 LangChain 流水线 | packages/server/src/utils/index.ts buildFlow |
| V2 引擎 | 队列驱动的有状态解释器 | packages/server/src/utils/buildAgentflow.ts executeAgentFlow |
| SSE 流器 | 把执行过程逐条推回客户端 | packages/server/src/utils/SSEStreamer.ts SSEStreamer |
3. 主线一句话走完(从请求到回答)
不进代码细节,先把一次 /prediction 的调用链在脑子里连成一条线:
createPrediction ← 控制器:校验 origin、若流式则设 event-stream 头
↓ (controllers/predictions/index.ts)
predictionsServices.buildChatflow ← 服务:薄封装
↓ (services/predictions/index.ts)
utilBuildChatflow ← 总入口:查 DB 拿 chatflow、准备输入
↓ (utils/buildChatflow.ts:990)
executeFlow ← 组织执行数据后,在这里按 type 分流
↓ (utils/buildChatflow.ts:301)
├─ chatflow.type === 'AGENTFLOW' ─────► executeAgentFlow (V2 队列解释器)
│ (utils/buildAgentflow.ts:1541)
└─ 否则(CHATFLOW / MULTIAGENT)───────► 经典拓扑构建 + buildFlow + ending.run
关键分流点是一行判断:utils/buildChatflow.ts:481 的 const isAgentFlowV2 = chatflow.type === 'AGENTFLOW'——命中就 return executeAgentFlow({...})(:482),把整条请求交给 V2 引擎;否则继续往下走经典路径。
记住这一层调用链,后面 01–04 章都是在放大这条线上的某一段。控制器如何建 SSE、队列模式怎么接,见 04。
4. 两大执行引擎:为什么有两套、怎么分流
Flowise 历史上先有「经典引擎」(面向 Chatflow 和第一代 Multi-Agent),后来新增了「AgentFlow V2」这套更灵活的画布与运行时。两者并存,由持久化在 ChatFlow 实体上的 type 字段决定走哪条(ChatFlow.type,见 packages/server/src/database/entities/ChatFlow.ts)。
4.1 两条路的分流依据
type 由前端保存时写入:经典画布存 CHATFLOW 或 MULTIAGENT(packages/ui/src/views/canvas/index.jsx:236 type: isAgentCanvas ? 'MULTIAGENT' : 'CHATFLOW');AgentFlow V2 画布存 AGENTFLOW。后端两处判断把请求劈开:
| 判断 | 位置 | 命中后走 |
|---|---|---|
chatflow.type === 'AGENTFLOW' | buildChatflow.ts:481 isAgentFlowV2 | V2 队列解释器 executeAgentFlow |
| 端点节点类别是 Multi/Sequential Agents | buildChatflow.ts:539 isAgentFlow | 经典引擎里的多智能体子路径 buildAgentGraph(网关在 :605) |
| 其余(普通链) | 同上,false | 经典引擎:buildFlow + ending 节点 .run() |
注意区分三个名字相近、含义不同的变量(别混,这是全篇最易错的一处):
| 变量 | 位置 | 判定什么 | 作用 |
|---|---|---|---|
isAgentFlowV2 | :481 | chatflow.type === 'AGENTFLOW' | 选引擎:V2 还是经典 |
isAgentFlow | :539 | 端点节点类别是不是 Multi Agents / Sequential Agents(endingNodes.filter(... category ...)) | 在经典引擎内部选子路径:走 buildAgentGraph(:605 的 if (isAgentFlow) 用的就是它)还是普通链 |
isAgentFlow | :1003 | 另一个同名变量,等于 chatflow.type === 'MULTIAGENT' | 仅用于指标计数(incrementSuccessMetricCounter 等,:1096/1108/1114),与 buildAgentGraph 分流无关 |
一句话记住分流层级:引擎级分流看 type(AGENTFLOW→V2);经典引擎内的多智能体子路径看端点类别(:539),而不是看 type === 'MULTIAGENT'(:1003 那个同名变量只喂指标)。
4.2 两套引擎的根本区别
| 维度 | 经典引擎(executeFlow) | AgentFlow V2(executeAgentFlow) |
|---|---|---|
| 触发 type | CHATFLOW / MULTIAGENT | AGENTFLOW |
| 核心思路 | 拓扑构建:把图当有向图,BFS 从起点初始化到端点,最后 run 端点 | 解释执行:一个 nodeExecutionQueue 队列,取一个节点跑、按输出把后继入队 |
| 控制流 | 由图的静态拓扑决定,一次性构建成流水线 | 运行时动态决定——支持条件分支、循环、人工介入(human-in-the-loop) |
| 状态 | 主要靠 LangChain 链自身 + 记忆节点 | 显式的执行状态 agentFlowExecutedData + 共享 state,逐节点累积 |
| 入口符号 | buildChatflow.ts:301 executeFlow | buildAgentflow.ts:1541 executeAgentFlow |
经典引擎的骨架:constructGraphs → getEndingNodes / getStartingNodes → buildFlow(逐节点 init)→ ending 节点 .run()(buildChatflow.ts:788 endingNodeInstance.run(...))。深入见 02。
V2 的骨架是一个 while (nodeExecutionQueue.length > 0 && status === 'INPROGRESS') 循环(buildAgentflow.ts:2020),每轮 shift() 一个节点执行、再 processNodeOutputs 把该走的后继压回队列,直到队空或触达 MAX_ITERATIONS(默认 1000,buildAgentflow.ts:1912)。深入见 03。
5. 阅读地图(建议顺序)
按「由浅入深、先数据后引擎」的顺序读:
| 顺序 | 章节 | 讲什么 | 什么时候读 |
|---|---|---|---|
| 1 | 01-flow-and-node-model.md | flowData 的结构(nodes/edges)、节点类的 init/run 契约、NodesPool 怎么把 380+ 节点装进内存 | 想先搞懂「图是怎么被表示的」 |
| 2 | 02-classic-engine.md | 经典引擎:constructGraphs → 拓扑 BFS → 逐节点 init → ending run,多智能体子路径 | 想懂 CHATFLOW/MULTIAGENT 怎么跑 |
| 3 | 03-agentflow-v2-engine.md | V2:队列解释器、条件/循环/人工介入、state 与 agentFlowExecutedData | 想懂 AGENTFLOW 的动态控制流 |
| 4 | 04-request-lifecycle-streaming-queue.md | 一次请求从 controller 到 SSE 的全生命周期、队列模式(BullMQ + Redis)水平扩展 | 想懂上线部署与流式细节 |
| 5 | 05-frontend-canvas.md | ReactFlow 画布怎么把拖拽变成 flowData、保存/加载/节点面板 | 想改前端或懂图是怎么产生的 |
6. 巧妙之处速览(读完带走的精华)
四个贯穿全局的设计决策,先白话点破,细节在各章:
- 图编译成执行,而不是解释配置。
flowData不是被逐字「读参数」,而是被当有向图做拓扑分析、构建成真正的对象图再跑。这让「拖拽的图」和「手写的 LangChain 代码」在能力上对齐。(utils/index.tsconstructGraphs、buildFlow) - 节点的 init/run 两段式。 每个节点类分两步:
init(nodeData, input, options)先把自己实例化成一个可用对象(一个 LLM、一个链、一个检索器),run(...)才真正产出结果。构建期把整张图init成对象图,执行期只run端点,让上游节点作为下游的「已初始化输入」 被注入。(packages/components/src/Interface.ts:162-163;utils/index.ts:641的newNodeInstance.init(...)) - 变量插值
{{ }}。 节点输入里写{{ $flow.sessionId }}、{{ chat_history }}、{{ 上游节点.output }}这类占位符,构建时由getVariableValue(utils/index.ts:874)在resolveVariables中解析成真实值——这是「连线传值」之外的第二套取值机制。V2 另有$iteration/$flow等运行时变量(buildAgentflow.ts内resolveVariables)。 - 队列模式水平扩展。 设
MODE=QUEUE后,Web 进程只负责接收请求与推 SSE,真正的执行被丢进 BullMQ 队列由 Worker 消费(queue/PredictionQueue.ts:107里 Worker 调executeFlow),执行事件经 Redis 发布/订阅回传给持有 SSE 连接的 Web 进程(RedisEventPublisher/RedisEventSubscriber)。这让执行层可以独立于接入层横向扩容。见 04。
7. 顶层代码地图(导航索引)
用符号名 grep 比行号抗漂移。下表是理解全景该打开的关键文件与符号:
| 主题 | 文件 | 符号 |
|---|---|---|
| 请求入口 / 建 SSE 流 | packages/server/src/controllers/predictions/index.ts | createPrediction |
| 服务薄封装 | packages/server/src/services/predictions/index.ts | buildChatflow |
| 编译/执行总入口 | packages/server/src/utils/buildChatflow.ts | utilBuildChatflow(:990)、executeFlow(:301) |
| 引擎分流点 | packages/server/src/utils/buildChatflow.ts | isAgentFlowV2(:481 选引擎)、isAgentFlow(:539 端点类别选子路径,:605 网关)、isAgentFlow(:1003 是 type==='MULTIAGENT' 的同名变量,仅用于指标) |
| 经典引擎:拓扑 BFS 构建 | packages/server/src/utils/index.ts | buildFlow(:516)、constructGraphs |
| 经典引擎:端点执行 | packages/server/src/utils/buildChatflow.ts | endingNodeInstance.run(:788) |
| 经典多智能体(LangGraph) | packages/server/src/utils/buildAgentGraph.ts | buildAgentGraph、compileMultiAgentsGraph、compileSeqAgentsGraph |
| V2 引擎:队列解释器 | packages/server/src/utils/buildAgentflow.ts | executeAgentFlow(:1541)、主循环(:2020)、executeNode(:1051) |
| 节点池 | packages/server/src/NodesPool.ts | NodesPool、initializeNodes |
| 节点契约 | packages/components/src/Interface.ts | INode.init(:162)、INode.run(:163) |
| 变量插值 | packages/server/src/utils/index.ts | getVariableValue(:874)、resolveVariables |
| SSE 流器 | packages/server/src/utils/SSEStreamer.ts | SSEStreamer、streamTokenEvent(:165)、streamMetadataEvent(:289) |
| 队列模式执行 | packages/server/src/queue/PredictionQueue.ts | executeFlow 调用(:107)、BaseQueue new Worker(:61) |
| 持久化实体 | packages/server/src/database/entities/ChatFlow.ts | flowData、type(CHATFLOW/MULTIAGENT/AGENTFLOW) |
| 前端存图 | packages/ui/src/views/canvas/index.jsx | handleSaveFlow、reactFlowInstance.toObject()(:226-228) |
本章只搭心智模型,不重复各章细节。下一步按 §5 的顺序进入 01。