基于 n8n 实现自动化

为 AI 筑巢:基于 n8n 实现自动化

最近的几个月中,ChatGPT 和相关工具的爆炸式涌现,互联网也重现生机。可是对于普通用户,在测试之后觉得好玩,大部分还是会很快忘记这些工具,继续回到自己原有的工作轨迹上。我父母和周围的朋友大都如此。一方面是因为各种网络问题和成本,导致链接 GPT 相关的一些应用比较不便;另一方面则是因为 AI 应用确实有一些不便。

ChatGPT 和他的各种衍生品确实带来了自动化,简单的劳务性工作可以被替代。可是相应作为代价,这些工具的使用流程还是相对繁杂。例如为了写一个邮件,打开 GPT 相关的一个工具,然后找到自己的 prompt,经过修改 prompt、等待响应之后和多次修改之后,才可以用上自己需要的片段。对于大多数用户来说,这显然是不合算的。对大部分人来说,可能觉得自己忍一忍,麻烦一下就过去了。大部分人还是会顺从并且维持自己的「工作惯性」,用自己更习惯的方式去工作的。

作为一个比较喜欢折腾的人,我对这种现象进行了比较深入的思考。今天的故事,从工具的意义出发,结合我最近使用 API 实现的一系列自动化,探讨我个人对如何用好 AI ,如何跟进这一轮产业革命的一些小小看法。

本文速通

  • 新的生产力工具是怎样被接纳的
  • 我的自动化工作的方案
  • 个人展望

(本文很多想法可能不够成熟,欢迎大家批评指正)

生产力工具

一个优秀的工具能够得到推广乃至出圈,设计时的考量往往会超越工具,对「场景」和「客户群体」进行更深度地分析。若是要替换已有的工作流程中的某些环节,大部分人一定会自然地权衡。权衡的标准则是考虑新工具带来的收益。如果「学习成本 < 新工具带来的效率提升」,那么大家就会愿意去学习使用新工具。

这里做一个简单的分析,分别基于老工具和新工具对一些任务进行工时的评估。如果「新工具工时 + 学习成本 < 老工具工时」,就说明「简单的学习可以带来巨大的效率」。

工时比较

首先分析一些正面案例。在上一个时代,学习成本极低的工具最先得到推广普及,例如 Markdown 和 Excel。他们为文档和表格处理带来了便利。虽然也有进阶的使用方法,但是他们的学习成本总体相对低廉,并且直接带来了巨大的效率提升。所以就算学习效率较低的人,也都很乐意花费一定时间去学习。

另一些工具,学习成本相对较高,但是可以带来显著的长期收益,比如我个人常用的 git,Latex,Obsidian。这些工具和一些具有直观的图形界面的工具比起来,确实学起来不那么简单。但是上手之后,我认为他们可以带来可观的长期收益,所以我也会努力学习掌握这些工具。

还有一些工具,虽然效率提升不是那么明显,但是他们的「无痛」和「易得」为他们带来了机会。例如 Notion,flomo,Xmind。他们最初发布的时候,确实就是「小而美」的精致产品。有简单的出发点和简单的设计,再用极低的学习成本吸引用户之后,基于原有的工具生态拓展功能,发展成新时代的效率利器。

(上面的理论对于一些特殊产品不能使用,有些时候大家会用「无法理解的想象力和行动力」去开发学习一些工具,比如「用 Diffusion 画小姐姐」)

所以为了做好工具,我个人认为有两个重要维度:

  • 降低学习成本
    • 简化设计
    • 提供详细的手册文档
    • 已有工作环境中,无痛植入
  • 带来效率提升
    • 定制设计,找准切入点
    • 自动化

作为深度使用 AI 相关工具的用户,我目前还不认为 AI 能完全取代我的工作,在效率上的提升也并没有达到我的预期。更准确地说,AI 工具目前不具备稳定性,有些时候重复同样的问题,得到的问题的正确性也不能一直保证。但是在一些泛用性比较强和比较灵活的地方,AI 还是非常有价值的。那么如何将独立的 AI 产品嵌入自己的工作流程呢?

API:工具孤岛之间的桥梁

很久以前,我曾经试图希望将工具进行便捷的整合,实现「All in One」。结果为了实现工具的融合,工具不断变短更加臃肿和笨重。折腾很多,后来还是逐渐放弃了。现在自己的笔记用的是 Obsidian 和 Notion 共同使用,任务管理用滴答清单,剪藏管理用 Cubox(都是少数派商店里面的好货)。工具拆解之后,很多自己的需求从原本的「复杂脚本」,变成了「直接需求」。虽然工作确实简单了一些,但是这些工具之间缺乏了一些灵性。不过工具的联动,还是有一些可行的好方法。这里,引入了互联网中近年来兴起的一类工具:基于 API 的自动化平台。

Zapier:自动化轻奢

Zapier 和 IFTTT 作为此类型产品中的行业领先者,也是我最早接触的自动化产品。他们提供了非常丰富的功能:

  • Oath2 和多种类型的登录认证
  • 数据的自动对接
  • 循环和分支的自动处理
  • 请求失败自动重试 ……

在少数派上也有不少相关的深度教程,例如:

所以,这里我也不进行赘述。这些教程基于 API 实现了自动化,将不同的产品进行数据互联。原本「将滴答清单完成的任务,记录进入 Notion」这件事情需要手动处理。现在有了 API 自动化工具,在完成任务的同时,API 自动化平台可以将数据更新反馈给 Notion,自动记录。节约了用户的时间,也确保了数据的可靠性。

这些这些 API 自动化管理工具非常好,但是他们有一个共同的缺点,就是太贵了(或者说这是我自己的缺点导致的……)。可以对比一下此类产品的价格:

产品 价格
Zapier 免费每月 100 tasks。升级 HK$159.11/month,750 tasks
IFTTT 免费提供几个 Applets。升级 HK$20/month,20 Applets
集简云 免费每月 500 次执行。升级一年 ¥1990,每月可执行 1500 次
HiFlow(腾讯轻联) 免费每月 1000 次执行。升级一年 ¥980,每月 3000 次

个人角度上看,我是非常支持这种有创新精神的互联网企业创业的。但是这个价格不论怎么说都有些过高了。特别是 Zapier,已经属于轻度奢侈品的范畴了。在经历一系列摸索之后,我最终找到了一个「可用且免费」的替代品。

n8n:开源的自动化控制台

n8n,一个可以帮你轻松实现自动化的工具。设计简洁,图形化界面可以非常清晰的展示数据在各种互联网产品之间流动的过程。提供了丰富的模板,也有活跃的社区支持。

虽然说 n8n 的定价也不便宜,每月 20 欧元只能跑 5 个 Workflow,但是胜在开源。如果自己部署则不需要任何额外开销。

项目地址: n8n(Github.com)

安装的时候个人推荐使用 docker-compose,方便后续更新设置。

# clone n8n source code
git clone https://github.com/n8n-io/n8n.git
# go to docker-compose directory
cd n8n/docker/compose/withPostgres

目录下有四个文件,分别如下操作:

  • .env(必须): 打开编辑三处用户名和密码。前两组用于数据库,最后一组用于浏览器登录使用。
  • docker-compose.yml(可选):打开编辑 n8n 环境,如果自己的服务器有域名,建议绑定域名。直接使用 IP 也可以。在 35 行之后添加 - N8N_HOST="[domain name or IP]"。例如我自己购买了一个xxx.site的域名,就会添加 - N8N_HOST="xxx.site"。不需要添加 http 和 https。如果有更换端口的需求也可以自行更改。
  • README.md、init-data.sh(不修改):维持原样不动。

之后执行:

docker-compose up -d

Fix on 2023-08-26, docker-compose 更新

由于升级到 1.0 版本之后,之前的 docker-compose 方案有部分问题,这里更新一版(个人比较喜欢 nightly 中的更新,可以根据个人喜好调整):

注意,下面文件中 [ ] 中的内容需要手动更改,共四处,分别为2处域名、用户名和密码

version: '3.8'

volumes:
  db_storage:
  n8n_storage:

services:
  postgres:
    image: postgres:11
    restart: always
    environment:
      - POSTGRES_USER
      - POSTGRES_PASSWORD
      - POSTGRES_DB
      - POSTGRES_NON_ROOT_USER
      - POSTGRES_NON_ROOT_PASSWORD
    volumes:
      - db_storage:/var/lib/postgresql/data
      - ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
      interval: 5s
      timeout: 5s
      retries: 10


  n8n:
    image: docker.n8n.io/n8nio/n8n:nightly
    restart: always
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
      - DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
      - N8N_HOST=http://[Domain Name]:5678
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=[Username]
      - N8N_BASIC_AUTH_PASSWORD=[Password]
      - N8N_PROTOCOL=https
      - N8N_EDITOR_BASE_URL=http://[Domain Name]:5678
    ports:
      - 5678:5678
    links:
      - postgres
    volumes:
      - n8n_storage:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

即可成功部署。因为 n8n 还是一个不够成熟的产品,经常需要更新部署。docker-compose 会稍微方便一些。访问 [服务器域名 or IP]:5678 时,会用到刚刚在 .env 中编辑的最后一组登录密码。

管理页面非常简洁,左侧分别为:

  • Workflows:管理自己的每一个编辑好的自动化事务
  • Templates:社区已有的一些模板,可以直接将社区模版拉出来使用
  • Credentials:管理自己的登录验证信息
  • All executions:已经执行过的日志记录

之后,点击 「Add Workflow」 就可以创建新的工作流程了。在创建我们的工作流之前,简单的介绍 Workflow 中最重要的两个元素。

触发器和节点

触发器是一个 Workflow 中的起点。被触发之后就会执行后续的流程。一个 Workflow 中可以有多个触发器。

进入新 Workflow 之后第一步就可以看到添加一个起点,即触发器。

触发器的类型有如下几种:

  1. On app event:对应 app 的事务触发。目前由于 n8n 开发还不够成熟,还有很多应用没有得到支持。
  2. On a schedule:定时触发。
  3. On webhook call:通过 webhook 触发,即在其他应用中发送 HTTP Request 触发。
  4. Manually:比较少用,点击触发。
  5. When called by another workflow:通过调用触发。
  6. 其他场景:针对特殊场景处理。

这些触发基本可以覆盖绝大部分的应用范围。

下面是节点,触发之后,会根据流程去依次执行每一个节点中的指令:

节点中的类型非常多样,大家可以自己根据 n8n 的文档摸索。我首先对下面用到的几个节点进行一些简单介绍。

首先是最重要的节点 HTTP Request 节点:

可以设置 URL、Query Parameters、Headers 和 Body。还可以使用 json 格式的数据。如果对 HTTP Request 比较陌生,也可以使用浏览器开发者模式中拷贝出的 cURL 直接变成 HTTP Request 节点。

其次是 Execute Command 节点,是我最常用的节点。这里可以直接执行我们需要的 Shell Script。这一节点还有一个特殊的功能,在后面会进行介绍。

然后是各种控制流节点,可以进行分支和循环等。

最后是各类应用节点,可以和各种应用联动,获取信息。不过对于不同的应用,需要分别设置他们的登录认证。

通过这些节点,可以实现可视化的脚本编程。满足我们多样化的需求。

从滴答清单到 Notion

下面进入正题:我是如何将「滴答清单」和「Notion」进行有机结合的。

首先,最难处理的问题在于「如何让第三方应用登录滴答清单」。滴答清单官方 API 显然有些过时了。在 https://developer.dida365.com/docs#/openapi 中虽然写出了二次认证的方法,配合 n8n 的二次认证模块也可以授权,但是不能正常的读取信息和任务列表。此外,官方网站的 API 似乎完全不对。

为此,我研究了一些第三方基于滴答清单的工具。还是有所收获的:

在 Github 上搜索 Dida365 和 API,就可以抄作业了。简单来说,使用用户名和密码,登录获取 token,之后将 token 做为 header 中 Cookie 的一部分,就可以进行对滴答清单任务的编辑和查询了。

登录 Workflow

首先我设置了一个专门用于登录的 Workflow。由于登录一次之后,Cookie 可以保存一段时间,所以我们可以利用这种机制减少登录频率,防止被滴答清单封号。

第一个节点作为定时触发器,我在每天早上 8 点的时候会触发这个工作流,存储我的 token 供当日的所有其他 Workflow 使用。

这里可以使用 Crontab 模式的时间设置方案。可以配合 Crontab guru 查看定时器的具体含义。

接下来是登录节点,添加一个 HTTP 节点。

API 和 header 的信息按照上图进行设置。

注意 URL 设置为:https://api.dida365.com/api/v2/user/signon?wc=true&remember=true。截图没有截完整。

header 的信息是:

  • authority: api.dida365.com
  • referer: https://dida365.com/webapp/
  • origin: https://dida365.com

这里是我从 cURL 中解析出来的。就直接沿用了。然后勾选 Send Body,在其中设置 Json 类型。在其中填入滴答清单的用户名和密码,用户名为手机号码或者邮箱。

在该节点设置完成之后就可以进行一些简单的测试了,n8n 中的每一个节点都有 Execute node 按钮。可以单独执行每一个节点进行调试。

测试的结果可以把输出用表格的形式输出,也可以用 json 的格式查看。

这里成功获取了我们需要的 token。

那么如何让其他的 Workflow 使用这里的 token 呢?这里就需要使用 n8n 特殊的设计:Execute Command 节点。Execute Command 相当于 n8n 在运行环境(这里是我们的 docker 容器)中运行我们给定的 Shell 脚本。这里的 Shell 脚本可以直接访问 docker 容器中的各个目录,例如 /tmp。我们可以将各种变量写入 /tmp 目录的文件中,从而作为「全局变量」在各个 Workflow 中进行使用。

所以我将这里的 token 拖到编辑框中(拖动表头可以直接将变量拖动),也可以直接输入:

echo  {{ $json.token }} > /tmp/token.txt

注意这里,如果使用了变量,需要将内容的类型从 Fixed 改为 Expression。这样就将 token 存入了 /tmp/token.txt 文件中。下次需要使用的时候,使用 Execute Command 节点去读取该文件的内容就可以访问全局变量了。

同步 Workflow

同步的 Workflow 相对稍微复杂一些(之后的脚本在名称里有一些 typo,是我在调试 Workflow 的时候心情烦躁导致的,大家见谅)。

Notion 登录

首先是和 Notion 联动的部分:

在 Add node 中添加 Notion 相关的节点,通过「Create New Credential」绑定自己的 Notion 账户。这里需要 Notion API 进行配合,需要在 My integrations (Notion.so) 页面中添加新的 integration。创建成功之后,从 Secrets 中复制 token:

刚刚的页面中,Capabilities 可以编辑 token 的权限范围,可以根据自己的需求编辑。之后不要忘记在自己需要编辑的 Notion 页面中添加和 integrations 的连接:

这里介绍比较粗糙,具体的创建方法在:Create a notion integration 中有详细的说明。

有了 Notion API 的加持,n8n 就可以自动化的完成创建页面和编辑页面等一系列操作了。

创建页面节点

Notion 节点可以添加 Blocks。在创建 Page 之后,我顺手添加了一个 Daily Report 的 H1 级别标题。

存储 notion 页面 ID

刚刚创建完成 Notion Page 之后,会生成一个页面的 ID。由于后续会对页面进行编辑,而 n8n 的节点不便进行 Notion 的页面检索,所以这里我直接把页面的信息存下来。

echo  {{ $json.id }} > /tmp/lastid.txt
获取滴答清单 token

获取另一个 Workflow 中的 token 信息。

获取当日任务列表

URL 部分,使用了 Query Parameters,用于限制自己的查询范围,只查询自己在昨天内完成的任务,可以根据个人需求自己更改配置。

URL: https://api.dida365.com/api/v2/project/all/completed from: {{ $now.minus({days:1}).setZone('Asia/Shanghai').toISO().slice(0, 11).replace(/-/g, "-").replace("T", " ") + "00:00:00"}} to: {{ $now.minus({days:1}).setZone('Asia/Shanghai').toISO().slice(0, 11).replace(/-/g, "-").replace("T", " ") + "23:59:59"}} limit: 50

header 部分需要将之前取到的 token 写入 cookie 中。

使用 $node["节点名"] 可以获取特定节点的输出,只要保持节点名称一致即可(请无视这里的 typo)。

Split in Batch 节点

输入 1,每次只向 Notion 页面中写入一个任务的信息。这个节点可以将刚刚获取的一串数据依次送入下一个节点。

获取 Notion 页面 ID

将刚刚的 notion 页面 id 取回:

cat /tmp/lastid.txt
任务信息写入 Notion 页面

这里使用了多个节点的信息,需要进行一定的限制。拖动输入有时候不能正常的导入数据,会默认从上一个最近的节点获取数据(n8n 的 bug,之后可能会修复),这里需要区分源自不同的节点的数据,所以需要手动编辑一下。

  • Block: {{ $node["Fetch Last ID"].json.stdout }}
  • Task Text: {{ $node["Split In Batches"].json.title }}

页面串接好并启用之后,每天的 8 点,我在上班的时候就能看到我前一天的任务日报了。

更多可能性?

n8n 中,将许多有 API 支持的互联网产品都进行了整理和简化:

在丰富的 API 中,我无意中发现了 OpenAI 也有专门的节点:

回到本文早些时候提出的问题,如何将 AI 顺利的融入自己的工作流。在刚刚的任务流程上,可以进行一点小小的提升。在 Split in Batches 节点结束之后,可以让 OpenAI 帮我写一个总结,再写回 Notion 中:

不过这里的 Workflow 中,AI 的娱乐性大于实用性。我对于生成的日报并不是特别有兴趣。但是,这里也让我看到了更多的自动化可能性,即将 AI 和自己的工作流程进行深度绑定,实现以前从未想过的自动工作流。例如我将一些 RSS 订阅在这里用 AI 提取形成周报,写回我的 Notion;将自己感兴趣的课题自动检索,加入 Zotero;将自己 Zotero 中读完的文章自动导出笔记进入 Notion。社区上还有模板,可以用 AI 将自己的邮箱变成一个智能客服。更多的使用方法还有待挖掘和思考。

本质和思考

其实话说回来,n8n 虽然看起来美观简洁,但是上述的所有工作流程都是可以通过直接写脚本完成的。所有应用也都可以使用 HTTP 调用他们各自的 API 完成;而 HTTP Request 也可以使用 curl 命令,在 Execute Command 节点中完成。所以,我认为 n8n 和脚本的关系更类似与 Jupyter 相较于 Python。我个人看来,比起传统的脚本编辑,n8n 有着下面的优势:

  1. 提供模块化的编辑环境,可以直观快速理解 Workflow 的作用,可以分步编辑脚本
  2. 提供了便捷的调试,可以独立执行每一个节点查看输出
  3. 简化编辑,可以将前置节点的输出直接拖动到当前节点中作为输入
  4. 简化管理,可以随时对 Workflow 进行开关
  5. 便于共享(自带的共享功能要花钱,但是可以导出 json 文件分享给他人)
  6. 简化流程,对于二次认证和一些复杂的认证,直接使用 n8n credentials 可以大幅降低工作量
  7. 全局变量,利用 Execute Command 将信息存入特定目录,达到临时变量的效果。

相比市面上已有的其他 API 工具,n8n 强大的拓展性和自定义属性在目前也是无法替代的。例如可以使用 Shell 写入文件达到全局变量的效果;可以在 docker 中安装 python 和更多其他命令行工具提升 Execute Command 的可用性;可以进入 docker 用 npm 提升 JavaScript 节点的拓展性;还可以将不同的 Workflow 互相调用。这些功能是目前其他的 API 工具暂时所不具有的。

尽管 n8n 目前还不够成熟,我使用也遇到了很多 bug,例如 OAth2 通过了验证但是无法访问数据,Notion 编辑的时候偶尔会找不到 Block ID,Webhook 触发器可能会失效。n8n 的用户体验也并不是非常完善,节点不能复制到其他的 Workflow,不能批量选中节点进行复制。缺点很多,但是社区的大家也在共同努力。和其他的 API 自动化工具相比,n8n 在价格和功能上都已经实现了双重碾压。n8n 不嫌弃我穷,我也不会嫌弃他 bug 多呀。

在写完文章之后,我再次回顾了自己工作中的不同场景。我的知识储备作为根基,各种工具作为树干和树枝。在树形的框架中,新生的 AI 工具是外来者,我用 API 工具在树枝上筑巢,让他可以在自己的工作流上栖息,为我的工作流注入了新的活力和生机。虽然我未来的成果未必能顺利开花结果,但是我会一直热爱自己的工作和事业,让自己的事业树永远生机勃勃。