Skip to content

Repository files navigation

NodeFlow Logo

NodeFlow

全中文可视化 AI 工作流编排框架

让每个会聊微信的人,都能用 AI 完成复杂需求

🎮 在线演示 · ✨ 核心特性 · 🚀 快速开始 · GitHub


⚠️ 重要提示:本项目目前仅供作者个人测试使用,距离理想状态还需要迭代多个版本,当前版本不建议在生产环境中使用。API 接口、工作流格式、节点定义等均可能发生破坏性变更。

🎯 项目介绍

NodeFlow 是一个全中文可视化的 AI 工作流编排框架。你用自然语言描述需求,AI 生成一张中文标注的流程图——每一步做什么、数据怎么传、分支怎么走,全部可视化地摊在面前。

不需要懂代码,不需要会英文,只要能看懂流程图,就能理解和掌控整个 AI 程序的运行。

💡 核心理念:数字平权 — 不该因为一个人不会英文或不会写代码,就剥夺他用 AI 解决实际问题的能力。


🖼️ 项目截图

📊 全中文可视化流程图

全中文流程图示例

⬆️ 截图占位:将评分助手工作流的完整流程图截图放在 docs/screenshots/flowchart-demo.png


🎯 实际应用:AI 作品自动评分助手

自动评分助手演示

⬆️ 截图占位:将评分助手生成的评分报告截图放在 docs/screenshots/scoring-demo.png


✨ 核心特性

特性 说明
🀄 全中文可视化 节点名称用中文描述工作内容,变量名配独立中文名。不懂英文也能看懂
🤖 自然语言生成 内置 nodeflow-builder Skill,用中文描述需求自动生成工作流
📦 声明式定义 节点 + 边 = 完整工作流,零胶水代码。JSON 配置清晰易读
🔍 可观测可调试 结构化执行日志,运行时流程图显示实际路径,对着图就能排查
🔀 丰富的控制流 条件分支、循环、并行(fan_out/fan_in),复杂逻辑轻松搞定
🧩 节点扩展简单 一个 @node 装饰器就能注册自定义节点,业务逻辑高度复用

🎮 在线演示

运行以下命令启动交互式演示:

python tests/run_demo.py

体验内容:

  • 全中文 SVG 流程图可视化
  • AI 作品自动评分助手
  • 声明式工作流代码展示

🚀 快速开始

安装

pip install -e .

运行演示

python tests/run_demo.py

生成流程图

from nodeflow import Flow

flow = Flow.from_json("tests/workflows/scoring_assistant/workflow.json")
flow.save_svg_html("scoring_flow.html")



📖 详细文档

目录

一句话介绍

NodeFlow 是一个轻量级工作流引擎。你可以把它理解成一个"流程导演":

  • 你告诉它有哪些步骤(节点)
  • 你告诉它步骤之间怎么走(边)
  • 它按顺序执行,自动调用大模型、读写文件、执行代码等

比如你可以用它做一个"自动写文章"的流程:输入主题 → 让 AI 写初稿 → 检查字数不够就续写 → 拼接自定义信息 → 输出最终结果。

这个项目能干什么

常见使用场景:

  • AI 写作:输入关键词,自动写短文、续写、汇总
  • 数据处理:读取文件 → 调用 LLM 分析 → 写入结果文件
  • 简单自动化:HTTP 请求 → 判断结果 → 发送通知
  • 学习工作流原理:代码量少,结构清晰,适合学习

推荐:用 AI IDE + Skill 通过 vibe coding 搭工作流

本项目自带一个 nodeflow-builder Skill(工作流构建助手),支持工作流生成、审查和优化。

安装 Skill

Skill 源码放在项目的 skills/ 目录下,你可以要求你的AI IDE读取该文件并添加到技能中。

使用方式

如果你使用的是支持 Skill 的 AI IDE(如 Trae),安装后直接描述需求即可:

"我想做一个工作流:先读取用户上传的 CSV,调用大模型逐行分析情感,把结果写回新的 CSV,如果某行失败就跳过并记录日志。"

Skill 会结合当前项目已注册的节点,自动生成:

  • 工作流设计说明
  • 可直接运行的 workflow.json
  • 必要的自定义节点代码(仅在业务上必须封装时才新建)
  • SVG 流程图

这样你不需要先背诵所有节点参数,只需要描述"做什么",由 Skill 帮你决定"怎么做"。

核心概念(3 个)

概念 大白话 例子
节点(Node) 一个步骤,做一件事 调用大模型、执行 Python 代码、读取文件
边(Edge) 步骤之间的连线 A 做完后做 B,或者根据条件选择 B 或 C
状态(State) 工作流运行时的"公共黑板" 每个节点都可以读和写上面的数据

项目结构

NodeFlow/
├── src/nodeflow/           # 核心代码
│   ├── core.py             # 节点注册、日志、JSON 加载
│   ├── flow.py             # 工作流引擎
│   ├── svg_flow.py         # SVG 流程图生成
│   ├── graph_layout.py     # SVG 布局引擎
│   ├── analysis.py         # 工作流分析工具(变量分析等)
│   └── nodes/              # 内置节点
│       ├── input.py        # 用户输入、搜索、数据库查询、HTTP、文件读取、正则提取、网页抓取
│       ├── llm.py          # 大模型对话
│       ├── condition.py    # 条件判断、意图识别(内置循环计数器)
│       ├── operation.py    # 代码执行、变量处理、数据库写入
│       └── output.py       # 文件写入、消息输出、通知
├── tests/                  # 测试和演示
│   ├── run_demo.py         # 交互式演示脚本(菜单驱动)
│   ├── test_core.py        # 核心功能测试
│   ├── test_flow.py        # 工作流引擎测试
│   ├── test_svg.py         # SVG 流程图测试
│   └── workflows/          # 所有工作流统一放这里
│       ├── beginner/       # 短文写作循环工作流
│       └── scoring_assistant/  # 自动评分助手工作流
├── skills/                 # Skill 源码(随 Git 提交)
│   └── nodeflow-builder/   # 工作流构建助手 Skill
├── docs/                   # 文档
├── README.md               # 本文件
└── pyproject.toml          # 项目配置

快速上手

第一步:安装

pip install -e .

第二步:配置大模型(可选)

如果你想让 LLM 节点真正调用大模型,可用以下三种方式之一(按优先级从高到低):

方式 A:环境变量(推荐用于容器/CI 场景)

# Windows PowerShell
$env:NODEFLOW_LLM_API_KEY = "你的 API Key"
$env:NODEFLOW_LLM_BASE_URL = "https://api.deepseek.com/v1"
$env:NODEFLOW_LLM_MODEL = "deepseek-chat"

# Linux/macOS
export NODEFLOW_LLM_API_KEY="你的 API Key"
export NODEFLOW_LLM_BASE_URL="https://api.deepseek.com/v1"
export NODEFLOW_LLM_MODEL="deepseek-chat"

方式 B:配置文件

复制配置模板并填入你的 Key:

cp config.json.example config.json

编辑 config.json

{
  "llm": {
    "api_key": "你的 API Key",
    "base_url": "https://api.deepseek.com/v1",
    "model": "deepseek-chat"
  }
}

方式 C:通过 NODEFLOW_CONFIG 环境变量指定自定义路径

export NODEFLOW_CONFIG=/path/to/your/config.json

三种方式可叠加,优先级:环境变量 A > NODEFLOW_CONFIG 指定文件 C > 当前目录 config.json B。 如果都不配置,LLM 节点会报错(当前版本已取消 mock 模式)。

方式 D:运行时注入(推荐用于 FastAPI 等应用)

通过 Flow.from_json(..., env={...}) 在加载工作流时注入,避免在容器镜像中固化密钥:

flow = Flow.from_json(
    "tests/workflows/beginner/workflow.json",
    env={
        "NODEFLOW_LLM_API_KEY": "动态获取的 Key",
        "NODEFLOW_LLM_BASE_URL": "https://api.deepseek.com/v1",
        "NODEFLOW_LLM_MODEL": "deepseek-chat",
    },
)

第三步:运行演示脚本

python tests/run_demo.py

这是一个带菜单的交互式脚本,菜单结构:

  1. 选择工作流并操作 — 列出所有可用工作流,选择后可执行以下操作:
    • 执行工作流(统一异步执行)
    • 生成静态流程图(不需要运行,直接看结构)
    • 生成运行时流程图(根据上次执行结果,画出实际走过的路径)
    • 生成变量清单
    • 验证工作流合法性
  2. 查看所有可用节点 — 列出全部内置节点和自定义节点
  3. 查看指定节点详情 — 看某个节点需要什么参数、返回什么

工作流会自动从 tests/workflows/ 下的子目录中发现,每个子目录如果包含 workflow.json 就是一个工作流。

第四步:看一个完整的工作流 JSON

打开 tests/workflows/beginner/workflow.json,里面详细注释了每个节点和边的作用。

它的执行流程是:

提示输入主题 → 获取主题 → LLM 写短文 → 检查字数
    ↓
字数 ≤ 500 且循环 < 10 次:续写 → 合并 → 再次检查
    ↓
字数 > 500 或循环达到 10 次:依次执行 5 个自定义节点 → 汇总输出

主要接口

1. 加载并运行工作流

from nodeflow import Flow

# 从 JSON 文件加载
flow = Flow.from_json("tests/workflows/beginner/workflow.json")

# 同步运行(命令行脚本用)
result = flow.run(input_state={"topic": "人工智能"})

# 异步运行(FastAPI/Jupyter 等已有事件循环的环境用)
# import asyncio
# result = asyncio.run(flow.arun(input_state={"topic": "人工智能"}))

# 打印结果
print(result)

关于异步:NodeFlow 内部完全异步。同步节点自动用 asyncio.to_thread 包装,不会阻塞事件循环。

  • run() — 同步接口,内部用 asyncio.run() 包装,适合命令行脚本
  • arun() — 异步接口,直接 await,适合 FastAPI、Jupyter 等已有事件循环的环境

2. 验证工作流

errors = flow.validate()
if errors:
    print("工作流有问题:", errors)
else:
    print("工作流合法")

3. 生成流程图

# 静态流程图:只显示结构(SVG 格式)
svg_code = flow.generate_svg()

# 运行时流程图:显示实际执行结果(需要先 run 一次)
flow.run(input_state={"topic": "测试"})
runtime_svg = flow.generate_runtime_svg()

# 保存为 HTML 文件
flow.save_svg_html("my_flow.html")

# 或者用内置方法导出
flow.export_graph("my_flow.html")

流程图是纯 SVG + HTML,不依赖任何外部 JS 库,直接用浏览器打开即可查看。

4. 查看可用节点

import nodeflow

# 列出所有节点
nodeflow.list_nodes(print_output=True)

# 查看某个节点详情
nodeflow.show_node("llm_chat", print_output=True)

5. 注册自定义节点

from nodeflow import node

@node(
    name="my_node",
    category="custom",
    description="我的自定义节点",
    inputs=[{"name": "value", "type": "int", "description": "输入数字"}],
    outputs=[{"name": "result", "type": "int", "description": "输出结果"}],
)
def my_node(state, params):
    value = params.get("value", 0)
    return {"result": value * 2}

只要这个文件被 import,节点就注册成功了,工作流 JSON 里可以直接用 "type": "my_node"

5.1 节点增强字段(可选)

@node 装饰器支持以下可选字段,用于声明节点的外部依赖、临时资源与重试策略:

字段 类型 默认值 说明
requires_tools List[str] [] 节点依赖的外部命令行工具名(如 ["ffmpeg", "yt-dlp"]),validate() 时会检查 PATH 中是否存在,缺失则生成 warning
temp_resources List[str] [] 节点返回值中的临时资源字段名(如 ["video_path"]),节点成功返回后引擎自动清理并从 state 中移除,避免传递到下游
retry int 0 节点抛异常时的最大重试次数(不含首次执行)。如 retry=2 表示最多执行 3 次
retry_delay float 0.0 重试之间的等待秒数
@node(
    name="download_video",
    category="custom",
    description="下载视频到临时目录",
    requires_tools=["ffmpeg"],           # validate() 检查 ffmpeg 是否在 PATH
    temp_resources=["video_path"],        # 节点返回后自动删除临时视频文件
    retry=2,                              # 失败时最多重试 2 次
    retry_delay=1.0,                      # 每次重试间隔 1 秒
)
def download_video(state, params):
    # ... 下载逻辑 ...
    return {"video_path": "/tmp/video.mp4", "duration": 60}
    # video_path 会被引擎清理,duration 保留传递到下游

6. 使用上下文管理器自动清理资源

涉及外部资源(浏览器、临时目录、文件句柄等)的工作流,推荐使用 with Flow() as f: 语法,确保即使中途异常也能执行清理:

from nodeflow import Flow

# 方式一:注册清理回调(with 退出时执行)
with Flow.from_json("workflow.json") as flow:
    flow.register_cleanup(lambda state: print(f"清理完成,最终状态键:{list(state.keys())}"))
    result = flow.run(input_state={"url": "https://example.com"})
    # ... 即使 run() 抛异常,register_cleanup 也会执行

# 方式二:通过 run() 参数传递一次性回调(不使用 with 时)
def my_cleanup(state):
    # 关闭浏览器、删除临时目录等
    pass

flow = Flow.from_json("workflow.json")
result = flow.run(
    input_state={"url": "https://example.com"},
    cleanup_handlers=[my_cleanup],  # run() 结束后执行(包括异常时)
)

注意register_cleanup 注册的回调仅在 with 语句退出时执行一次;run(cleanup_handlers=...) 传递的回调在每次 run() 结束时执行。两者可同时使用,互不重复。

7. 节点级错误路由(on_error 边)

默认情况下,节点抛异常会触发工作流的 global_exception_handler。如果只想对特定节点做局部降级,可以为该节点添加 on_error 边:

{
  "nodes": [
    {"id": "fetch", "type": "http_request", "params": {"url": "{target_url}"}},
    {"id": "fallback", "type": "variable_process", "params": {"outputs": {"recovered": true}}}
  ],
  "edges": [
    {"from": "fetch", "to": "fallback", "on_error": true},
    {"from": "fallback", "to": "END"}
  ]
}
  • fetch 节点抛异常时,引擎会写入 _error_error_node 到 state,并路由到 fallback 节点
  • fallback 节点可读取 state["_error"] 获取错误信息
  • 若节点同时声明了 retry,会先重试,重试耗尽后才走 on_error
  • on_error 边优先于 global_exception_handler

内置节点一览

节点名 类别 作用 主要参数
user_input input 从控制台读取用户输入 prompt, variable
file_read input 读取文件 file_path
http_request input 发送 HTTP 请求 method, url, headers, body
web_search input 联网搜索(Bing/DuckDuckGo) query, max_results
db_query input 数据库查询(mock) query_type, table, where
regex_extract input 正则提取 text, pattern, group
web_fetch input 网页抓取(提取标题/正文/链接/图片/代码块) url, max_text_length, timeout
llm_chat llm 调用大模型 system_prompt, user_input
condition condition 条件判断(内置循环计数器) conditions, logic, max_executions
intent_recognize condition 意图识别(基于 LLM) user_input, intent_candidates
variable_process operation 变量处理(拼接/运算/格式转换) variables, outputs
code_exec operation 执行 Python 代码 code
db_write operation 数据库写入(mock) mode, table, data
file_write output 写入文件 file_path, content, mode
message_output output 消息输出 title, content, channel
notification output 通知输出(message_output 别名) title, content

mock 是什么意思? 就是这些节点没有真的去调用搜索引擎或数据库,而是返回假数据。后续你可以替换成真实实现。

节点参数详细说明

每个内置节点的完整入参、出参、类型、默认值、详细说明都写在对应源码文件的函数 docstring 里。你也可以通过 AI IDE 的 nodeflow-workflow-studio Skill 直接询问某个节点怎么用。

下面给出各节点的参数速查表,方便你快速定位。

input 类

节点 主要入参 主要出参 说明 源码
user_input prompt, variable {variable}, success, error 从控制台读取用户输入 input.py
file_read file_path file_content, file_path, file_size, success, error 真实读取本地文件 input.py
http_request method, url, headers, body, timeout http_response, url, method, success, error 真实发送 HTTP 请求 input.py
web_search query, max_results, search_engine search_results, success 真实联网搜索(Bing/DuckDuckGo) input.py
db_query query_type, table, where, query_params query_result, success mock 数据库查询 input.py
regex_extract text, pattern, group match, matched, all_matches, success, error 正则提取 input.py
web_fetch url, max_text_length, timeout title, text, links, images, code_blocks, domain, content_hash, success, error 网页抓取并解析 HTML input.py

llm 类

节点 主要入参 主要出参 说明 源码
llm_chat model, temperature, system_prompt, user_input, api_key, base_url content, llm_result, usage, success, error 调用大语言模型生成文本 llm.py

condition 类

节点 主要入参 主要出参 说明 源码
condition conditions, logic, max_executions, counter_var _route(true/false), {counter_var} 多条件 AND/OR 判断,内置循环计数器 condition.py
intent_recognize user_input, intent_candidates intent, confidence, entities, llm_raw, success, error 基于 LLM 识别用户意图 llm.py

operation 类

节点 主要入参 主要出参 说明 源码
variable_process variables, outputs outputs 中定义的字段 变量处理:模板拼接、简单运算、格式转换 operation.py
code_exec code, raise_on_error code_result, success, error(如果 result 是 dict,字段会展开到 state 顶层) 执行 Python 代码处理数据或做路由决策 operation.py
db_write mode, table, data, where result, success, error mock 数据库增删改(mode: insert/update/delete) operation.py

output 类

节点 主要入参 主要出参 说明 源码
file_write file_path, content, mode written, file_path, content_length, success, error 将内容写入文件(mode: write/append) output.py
message_output title, content, channel sent, success, error 消息输出(当前仅 console 渠道) output.py
notification title, content sent, success, error message_output 别名(兼容旧名称) output.py

自定义节点

自定义节点通过 @node 装饰器注册,没有固定参数,完全由你定义。写法见 主要接口 / 注册自定义节点,示例源码见 beginner/nodes.py。

节点设计指南

何时用内置节点,何时写自定义节点?

优先使用内置节点。只有以下情况才需要写自定义节点:

  1. 业务逻辑无法用 code_exec + variable_process 组合实现 — 比如需要调用特定 SDK(Playwright、ffmpeg 等)
  2. 需要封装复杂的领域知识 — 比如评分量规解析、特定格式的报告生成
  3. 同一个逻辑在 3 个以上工作流中重复 — 值得抽象为可复用节点

反例:如果只是"从搜索结果里选一个主题",直接用 code_exec 写 3 行 Python 即可,不需要新建节点。

自定义节点能否升级为内置节点?

判断标准(五问法):

原则 判断方法 适合升级 不适合升级
通用性 3 个以上不相关场景都能用吗? file_read(文件读取)— 任何工作流都可能用 pick_hot_topic(选主题)— 只有搜索+写作场景
无业务耦合 是否依赖特定业务领域知识? code_exec(执行代码)— 纯通用能力 score_refine_rubric(量规细化)— 深度绑定评分
入参出参规范 参数能用基本类型表达吗? regex_extract(正则提取)— text/pattern 都是 string score_parse_input— 依赖特定输入格式
可独立测试 不依赖外部上下文能跑吗? variable_process— 给 state+params 就能跑 score_aggregate— 依赖 rubric 结构化数据
后续工作流通用 未来其他工作流会用到吗? web_fetch(网页抓取)— 通用场景 score_format_report— 只在评分场景

入参出参规范

参考 LangChain、Airflow 等开源项目,自定义节点的入参出参应遵循:

  1. 入参用基本类型:string/int/float/bool/list/dict,避免自定义类
  2. 每个入参必须有 default{"name": "url", "type": "string", "default": "", "description": "网页 URL"}
  3. 出参必须包含 successerror:统一错误处理模式
  4. 出参扁平化:不要返回嵌套过深的 dict
  5. 幂等性:相同入参应产出相同结果(LLM 节点除外)

节点分类与适用场景

分类 解决的问题 对应内置节点 设计模式
数据获取 从外部系统读取数据 file_readhttp_requestweb_fetchweb_searchdb_queryregex_extractuser_input input→output,无状态
数据处理 转换、计算、过滤数据 code_execvariable_process transform,入参即出参来源
流程控制 条件分支、循环、意图路由 conditionintent_recognize 只输出 _route,不修改业务数据
结果输出 写文件、发消息、存数据库 file_writemessage_outputdb_write 副作用型,记录写入路径

边的类型

边类型 作用 例子
normal 普通连接 {"from": "A", "to": "B"}
conditional 条件分支 {"from": "A", "to": "B", "when": "_route = pass"}
fan_out 一个节点并行分发到多个 {"from": "A", "to": "B", "type": "fan_out"}
fan_in 多个节点汇聚到一个 {"from": "A", "to": "C", "type": "fan_in", "mode": -1}

条件分支详解

条件边通过 when 属性指定判断条件,支持以下格式:

  1. 简单匹配(匹配 _route 字段):

    {"from": "A", "to": "B", "when": "search"}

    等价于 _route = search

  2. 完整条件(匹配任意字段):

    {"from": "A", "to": "B", "when": "status = success"}
  3. AND 组合

    {"from": "A", "to": "B", "when": "count < 10 AND status = active"}
  4. OR 组合

    {"from": "A", "to": "B", "when": "type = urgent OR priority = high"}

循环实现

循环不需要专门的循环边类型,只要让条件边指回上游节点即可:

{"from": "count_chars", "to": "continue_writing", "when": "_route = continue"}
{"from": "merge_essay", "to": "count_chars"}

注意:在节点中需要维护计数器,避免无限循环。例如在 count_chars 节点中:

  • loop_count < 10 时,设置 _route: "continue"(继续循环)
  • char_count >= 500loop_count >= 10 时,设置 _route: "done"(退出循环)

日志在哪里

每次执行会自动记录日志,默认位置由工作流 JSON 里的 log_path 决定。

例如 workflow.json 里写的是:

"log_path": "tests/logs/beginner.json"

打开这个文件,可以看到每次执行的:

  • 开始时间、结束时间
  • 每个节点的输入参数和输出结果
  • 执行顺序
  • 最终状态

常见问题

Q1: 我不会 Python,能用吗?

可以只改 JSON 工作流文件,基本不需要写 Python。但如果要加自定义节点,需要会一点 Python。

Q2: 自定义节点放哪里?

放在你自己的项目目录里,只要在工作流运行前被 import 就行。推荐一个文件放多个节点,然后 import 一次。

Q3: 为什么我的 LLM 节点报错了?

检查 config.json 是否配置了有效的 API Key。

Q4: 流程图打不开?

流程图是纯 SVG + HTML 文件,不依赖任何外部库,直接用浏览器打开即可查看。 如果显示异常,检查浏览器是否支持 SVG(所有现代浏览器都支持)。

Q5: 流程图中的参数显示规则是什么?

  • 输入参数:统一显示为 中文解释(变量名) 参数值(如 搜索关键词(query) 今日热点新闻
  • 输出参数:统一显示为 中文解释(变量名) 变量类型(如 是否成功(success) bool
  • 变量标签:输入参数中的模板变量引用仍会在 var_labels 中查找中文名,显示为 中文名(英文名) 格式

Q6: 边标签显示的是什么?

条件边会显示完整的判断条件,例如:

  • _route = search:当节点输出的 _route 等于 search 时走此边
  • status = success AND count < 10:多条件组合

Q7: 如何为变量添加中文名?

在工作流 JSON 的 var_labels 字段中定义映射:

{
  "var_labels": {
    "topic": "主题",
    "content": "LLM输出内容",
    "essay": "短文内容"
  }
}

Q8: 循环是怎么实现的?

通过条件边指回上游节点实现循环,配合计数器控制终止:

  1. 在节点中维护 loop_count 计数器
  2. 根据条件设置 _route(如 continuedone
  3. 条件边根据 _route 值决定走循环分支还是退出分支

下一步学习

  1. 运行 python tests/run_demo.py,把每个菜单选项都试一遍
  2. 打开 tests/workflows/beginner/workflow.json,逐行看注释
  3. 尝试改一个参数,比如把循环次数从 10 改成 5
  4. 写一个自己的简单工作流 JSON

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages