先讲个背景。我之前很长一段时间都在用脚本硬编码做自动化任务比如定时抓数据、调大模型做摘要、往群里推消息。刚开始还好任务少脚本也就两三个。等到第四个、第五个任务出现的时候问题来了每个脚本都要单独维护调度逻辑乱成一团别人根本看不懂我在干什么稍微改一个需求就得把代码翻个底朝天。后来我在开源社区里翻到 deer-flow 这个项目一眼就明白了它想解决什么问题——把“流程”从代码里捞出来变成一张可视化的工作流图。这篇文章就把我实际部署和使用 deer-flow 的经验完整写出来从架构思路到避坑细节基本都能直接照着抄。deer-flow 定位成一个自托管的开源工作流编排引擎核心卖点是可视化编排、轻量部署、AI 原生支持。你可以在上面通过拖拽节点、连线的方式把大模型调用、HTTP 请求、代码执行、条件分支、定时触发这些事情串成一条完整的自动化流水线。跑起来之后每条任务的执行状态、输入输出、日志都能在界面上看到出问题直接在节点上查再也不用靠 print 和猜。这套东西适合谁适合后端开发、全栈工程师、运维也适合一些有技术底子的产品和运营。如果你手上正好有“每天定时抓某个网页、用大模型整理成表格发给飞书群”这类需求或者想在公司内网搭一个不依赖外部 SaaS 的自动化中枢那 deer-flow 几乎是量身定做的。接下来我按实际踩过的路从设计思路、部署、核心节点、示例流程到常见问题完整过一遍。1. 整体设计思路拆解为什么把流程从代码里拆出来1.1 硬编码自动化的痛点很多人一开始写自动化脚本都是图省事“需求不大写个 Python 脚本挂 cron 就完了”。这种想法没错但任务一旦多起来问题会集中爆发。首先是不可视脚本之间怎么调用、谁先谁后、中间产物长什么样只能靠人脑记忆其次是难变更想加一个“如果摘要失败就重试两次”的逻辑你得改代码、重启服务、再验证最要命的是不可复用这次写的请求逻辑、解析逻辑、通知逻辑换一个场景就全部重写。我自己经历过一个例子公司内部有个舆情监控需求要每隔半小时抓一次几个新闻源的 RSS让大模型生成摘要然后推送进企业微信群。第一版脚本跑了两个月后来需求变成“摘要要按负面/中性/正面分类再发到不同群”我改了整整一个下午。改完我就觉得不对劲这种改动明明应该是“把一条线改成三条分支”的事不应该那么痛苦。1.2 deer-flow 的核心设计理念deer-flow 的核心思想是把业务逻辑建模成一张有向图。节点代表“做一件事”连接线代表“数据怎么流动”。这个模型并不新鲜但 deer-flow 把它做得足够轻、足够贴近 AI 应用场景。它的设计里有几个关键点值得说第一节点是黑盒也是白盒。每个节点都有明确的输入输出结构你在画布上连好线数据就会自动从上游节点流向下游节点。双击节点又能看到全部参数和运行日志透明到每一个中间状态都能检查。第二AI 是原生公民而不是外挂。大模型调用不是一个“藏在代码里的工具”而是一个独立的、可配置的节点。这意味着你可以把“调用哪个模型”“温度调多少”“System Prompt 是什么”全部可视化改提示词不用改代码。第三运行时可观测性。每一次流程触发都会生成一个实例实例的每个节点都有详细的开始时间、结束时间、输入快照、输出快照。这个设计对排查问题太重要了稍后我会专门展开。1.3 技术选型层面的取舍从我读源码和配置文件的感受来看deer-flow 在技术选型上做了一个很务实的决策不重造轮子但把常用轮子都给你接到画布上。它没有像一些重量级 BPM 平台那样引入复杂的流程定义语言而是用 JSON 描述整张图。好处是存储简单改起来也直观坏处是太复杂的流程会显得 JSON 很臃肿但实际使用中大多数自动化流程控制在二三十个节点以内完全够用。服务端用了主流的技术栈部署上默认推荐 Docker Compose把 web 应用、服务端、数据库这些组件打包在一起一条命令就能拉起来。我特别欣赏的一点是它对模型供应商做了抽象。Ollama、通义、智谱这类国内可用或者本地部署的模型都有统一的接入方式。你只需要在每个大模型节点里选择对应的供应商和模型名不需要自己写一套兼容层。这意味着本地小模型和云端大模型完全可以混用敏感数据走本地模型复杂推理走云端模型。2. 从零开始部署Docker Compose 一键拉起2.1 部署前的思路准备先说结论如果你只是自己用或者小团队内部用不需要搞太复杂一台 2 核 4G 的云服务器就够。deer-flow 本身不重真正的资源消耗大头其实是本地大模型如果你打算通过 Ollama 跑本地模型那建议模型和 deer-flow 分开部署否则内存很容易被吃满。我建议的部署方案是deer-flow 本体用 Docker Compose 管理数据库独立数据卷所有配置通过环境变量注入。这样做的好处是升级方便换机器也能一键迁移。别一上来就追求 Kubernetes这个体量的应用用 K8s 纯属给自己找麻烦。2.2 编写 docker-compose.yml以我当时用的部署配置为例核心需要四个服务前端页面、后端服务、数据库、缓存。下面这份配置可以直接存下来用版本号按你拉取时的最新稳定版调整即可。version: 3.8 services: deer-flow-server: image: deer-flow/deer-flow-server:latest container_name: deer-flow-server restart: unless-stopped ports: - 8080:8080 environment: TZ: Asia/Shanghai DB_HOST: postgres DB_PORT: 5432 DB_NAME: deerflow DB_USER: deerflow DB_PASSWORD: change_this_password REDIS_HOST: redis REDIS_PORT: 6379 FLOW_DATA_DIR: /app/data volumes: - ./data:/app/data depends_on: - postgres - redis deer-flow-web: image: deer-flow/deer-flow-web:latest container_name: deer-flow-web restart: unless-stopped ports: - 8088:80 depends_on: - deer-flow-server postgres: image: postgres:15-alpine container_name: deer-flow-pg restart: unless-stopped environment: POSTGRES_DB: deerflow POSTGRES_USER: deerflow POSTGRES_PASSWORD: change_this_password volumes: - pg_data:/var/lib/postgresql/data redis: image: redis:7-alpine container_name: deer-flow-redis restart: unless-stopped volumes: - redis_data:/data volumes: pg_data: redis_data:这里有几个细节要说清楚。端口方面我让后端服务暴露在 8080前端暴露在 8088。你可以按自己的习惯改但要记得前端界面里配置的后端地址要对应上。数据库密码那个环境变量我用的是占位符实际部署务必改成强密码别偷懒用默认值。TZ 环境变量设成 Asia/Shanghai这个很重要。不设的话定时任务的执行时间会跟你的本地时间对不上你以为是早上 9 点触发实际跑的时候可能是凌晨。这个坑我踩过一次排查半天才发现是时区默认 UTC。2.3 启动与初始化配置写好后在 docker-compose.yml 同级目录执行docker compose up -d第一次启动会拉取镜像耐心等一会儿。等所有容器状态变成 healthy 或者 running 之后打开浏览器访问http://服务器IP:8088就会看到初始化页面。首次进入会让你创建管理员账号这里记得用一个靠得住的口令。登录进去之后第一件事不是急着画流程而是先到“系统设置”里把模型供应商配置好。我用的是 Ollama 加本地模型所以填的是http://localhost:11434模型名填qwen2.5:7b。如果你用云端 API就按供应商要求填 API Key 和模型名。配置完之后务必点一下“测试连接”这一步能过滤掉八成后续问题。2.4 源码部署方式适合二次开发如果你需要改源码、加自定义节点那就要用源码部署方式。大致流程是克隆代码仓库前端用 pnpm 安装依赖并启动 dev server后端用虚拟环境安装依赖并启动 API 服务数据库和缓存还是用 Docker 跑。这种方式只推荐有二次开发需求的人用普通使用场景完全没必要还容易把自己绕晕。我当时尝试源码部署主要是因为好奇想看看节点是怎么注册进系统的。看完之后我的结论是这个项目把节点抽象做得挺干净新增一个节点类型只需要继承基类、实现输入输出描述和运行逻辑然后在注册表里声明一下就行。但平时的自动化任务我更推荐用 Docker 版本把精力花在流程设计上。3. 把核心节点玩明白可视化编排的关键3.1 节点类型一览deer-flow 内置的节点类型基本覆盖了日常自动化九成以上的场景。我把它们整理成一张表方便对照着用。节点类型用途典型场景定时触发器按 cron 表达式周期性触发流程每半小时执行一次舆情监控Webhook 触发器通过 HTTP 请求触发流程接收外部系统回调手动触发在界面上点击按钮运行调试和临时执行HTTP 请求发起 HTTP 调用支持 GET/POST 等抓取网页、调用第三方 API代码节点执行自定义脚本Python/JS数据清洗、格式转换、解析 XML大模型节点调用配置好的模型支持提示词模板文本摘要、分类、信息抽取条件分支根据规则判断走哪条下游按情感分类分发到不同群数据转换在字段间做映射、拼接把上游输出重组成下游需要的结构通知节点发送企业微信、钉钉、飞书等消息告警、日报推送这里我重点讲三个用得最多、也最容易出问题的节点HTTP 请求节点、代码节点、大模型节点。3.2 HTTP 请求节点数据的入口和出口HTTP 请求节点本质上就是把 axios 或者 requests 的能力搬到了画布上。你可以配置请求方式、URL、请求头、查询参数、请求体。但很多人第一次用会忽略一个细节上游节点传来的数据怎么塞进请求里。deer-flow 的设计是上游节点的输出会作为一个名为input的对象注入到当前节点的上下文中。所以如果你想用上一个代码节点解析出来的url字段作为请求地址你需要写成{{ input.url }}。我见过不少新手在连线之后发现请求地址是空的就是因为在 URL 栏里直接抄了个固定地址忘了用模板语法。请求返回的响应体默认会被解析成 JSON 对象存到当前节点的输出里。要注意如果对方接口返回的是文本或 XML你得先经过一个代码节点做转换否则后续节点拿到的可能不是你想要的格式。3.3 代码节点做数据清洗的“中间人”代码节点是 deer-flow 里最灵活的节点相当于在流程里嵌了一个可执行脚本。我习惯把它当作“数据清洗中间人”专门用来处理大模型和外部接口之间的格式鸿沟。比如大模型输出的是 Markdown 表格你想转成结构化 JSON 数组再写入数据库这个操作放在代码节点里做最合适。代码节点的执行环境里已经注入了当前流程的input以及一些常用的工具库。具体有哪些库看当前版本的文档就行但渲染思路是一致的你只需要关注“我拿到什么数据、要输出什么数据”不需要关心这个脚本怎么被调度、怎么被重试框架都替你处理了。写代码节点的时候有一条经验尽量把异常处理写在脚本里不要指望上游数据一定符合预期。比如解析 JSON 之前先判断字段是否存在调用外部库之前先 try except 包裹。因为一个节点的异常会导致整条流程停在当前节点后面所有节点都不会执行。虽然 deer-flow 会在界面上醒目标红但流程已经中断了如果前面还有业务影响那损失已经造成了。3.4 大模型节点功能很强但提示词决定上限大模型节点是 deer-flow 最吸引人的部分。你可以选模型供应商、选具体模型、填 System Prompt 和用户提示词还可以把上游节点的数据通过模板语法拼接到提示词里。用大模型节点有一个容易被忽略的点模型输出是文本而下游节点可能需要结构化字段。比如让模型判断“这个新闻是正面还是负面”模型可能回一句“根据我的分析该新闻整体上是正面的”。如果你直接把整句话往下传下游的条件分支就很难做判断。我常用的解决办法是让模型只输出一个 JSON 对象然后在代码节点里JSON.parse出来。提示词写清楚“请只输出 JSON格式为 {sentiment: positive}。大部分模型都会乖乖听话偶尔多输出几句用代码节点里的字符串切割也能容忍。这个方法本质上是把“大模型的自然语言自由度”关进一个很小的笼子里换来的是结构化数据的确定性。在流程编排里确定性比自由度重要得多。4. 两个实战案例从空白画布到跑通全流程4.1 案例一定时抓取 RSS 并用大模型生成摘要推送这个流程是我最常用的模板完整链路是定时触发 - HTTP 抓取 RSS - 代码解析 XML - 大模型生成摘要 - 代码提取关键字段 - HTTP 推送到企业微信群机器人。先说定时触发节点。我用的 cron 表达式是*/30 * * * *意思是每 30 分钟执行一次。deer-flow 在界面上提供了 cron 语法预览不用背表达式点几下就能确认下个触发时间。注意避开整点峰值比如5 */1 * * *表示每小时第 5 分钟触发能有效避开大家集中跑批的时间段。HTTP 抓取节点很简单URL 填 RSS 地址请求方式选 GET请求头里加一个 User-Agent防止部分源拒绝非浏览器请求。接下来是代码解析节点。RSS 本质上是 XMLdeer-flow 的 HTTP 节点不会自动把 XML 转成 JSON所以需要手动解析。我贴一段我当时用的 Python 脚本删掉了一些无关逻辑保留核心思路import xml.etree.ElementTree as ET feed_text input[response_text] root ET.fromstring(feed_text) items [] for item in root.iter(item): title item.findtext(title) link item.findtext(link) pub_date item.findtext(pubDate) items.append({ title: title.strip() if title else , link: link.strip() if link else , pub_date: pub_date.strip() if pub_date else , }) # 取最新10条避免每次摘要太多 output {items: items[:10]}这里有个小细节我习惯把清洗后的数据统一放到output对象里字段名固定、结构扁平这样下游大模型节点的提示词模板写起来很顺手。大模型节点的 System Prompt 我大概是这么写的你是一个新闻摘要助手。根据用户提供的新闻条目列表为每一条新闻生成一句话摘要并判断情感倾向。 只输出 JSON 数组格式为 [{title: 原标题, summary: 一句话摘要, sentiment: 正面/负面/中性}]用户提示词里用模板语法引用上游数据请处理以下新闻条目 {{ input.items | tojson }}注意这个| tojson过滤器它能把 Python 对象安全地转成 JSON 字符串避免在提示词拼接时出现格式错乱。大模型输出之后再接一个代码节点把模型的文本输出中抽取出的 JSON 数组传给 HTTP 通知节点。推送企业微信机器人的时候消息内容可以直接用 Markdown 格式把新闻标题和摘要按列表拼起来。如果想让不同情感倾向的新闻进不同群就在推送前加一个条件分支按sentiment字段分流。这个流程从画节点到最终跑通我大概花了一个小时。真正耗时间的不是配置节点而是调试提示词。模型输出的格式不稳定这是所有大模型应用的通病不是 deer-flow 本身的问题。4.2 案例二Webhook 接入自动工单分类第二个案例更贴近团队协作。假设公司内部系统会往一个 Webhook 地址推送工单信息你想让 deer-flow 接收后自动判断工单类别再通知对应负责人。Webhook 触发节点会生成一个 HTTP 地址把这个地址填到内部系统的回调配置里即可。需要提醒的是一定要在节点配置里看一下是否支持鉴权如果支持开一个简单 Token 校验否则任何人知道地址就能往里灌数据。工单信息进来之后先经过一个代码节点做字段映射把系统原始的字段名改成 deer-flow 内部统一的命名。然后再走大模型节点做分类。分类不需要太多候选我当时的场景是“技术咨询 / 业务投诉 / 合作相关 / 其他”四类让模型只输出类别名然后接一个条件分支如果是技术咨询推到技术负责人群。如果是业务投诉推到客服负责人群。如果是合作相关推到商务负责人群。其他情况推到默认的工单汇总群。这套流程跑起来之后团队内部反馈非常好因为以前人工转发工单至少延迟十分钟现在秒级分发。最关键的是后续想调整分类逻辑根本不需要开发介入我在 deer-flow 界面上把提示词改一改点保存就生效了。这件事给我的冲击挺大的自动化逻辑终于不依赖具体某个人了。5. 常见问题与排查技巧实录5.1 按表现分类的问题速查表这部分直接干活都是我自己操作中遇到过以及帮同事排查过的问题。问题现象可能原因排查思路与解决办法容器起来了但页面打不开端口没有映射对或者防火墙没放行先docker compose ps看端口映射再用curl -I试探端口是否返回响应定时任务到点不触发容器时区不是北京时间检查环境变量 TZ 是否设为Asia/Shanghai改了之后重启容器大模型节点响应超时本地模型推理速度慢或云端 API 配置不对先单独在模型供应商配置页点“测试连接”再降低超时时间的预期值Webhook 收不到请求回调地址填错、外网不可达、缺少公网端口转发POST 一个测试请求看 deer-flow 的运行日志里有没有收到中文内容在推送群里乱码URL 编码问题或请求头缺少 charset在 HTTP 节点里显式设置请求头Content-Type: application/json; charsetutf-8代码节点报 key 不存在上游输出结构和你预期的不一致先查看上游节点的运行快照确认输出字段名再去代码节点里修正流程执行到一半停了前面节点抛了未捕获异常打开流程实例详情看哪个节点标红点进去看错误堆栈5.2 几个独家排查小技巧除了上面的速查表还有几个不大容易在文档里看到的小技巧。第一个技巧善用“手动触发”做调试。不要每次改完都等定时任务跑直接在画布上点执行按钮马上就能看到结果。调试通过之后再切换成定时模式效率翻倍。第二个技巧把大模型节点和代码节点拆开。如果你发现模型输出经常不稳定不要直接在提示词里反复打补丁而是加一个代码节点专门做清洗。让模型负责“想”让代码负责“拆”职责分开之后调试成本大大降低。第三个技巧流程跑通了之后把关键节点的输入输出快照导出来存成一个测试样例。以后每次改流程先用这份样例跑一轮回归确认没有改坏旧逻辑。这个习惯帮我避免了好几次“改了一个节点搞崩了整个流程”的事故。第四个技巧数据目录./data和数据库数据卷一定要定期备份。deer-flow 的流程定义、运行记录都在这两个地方万一服务器挂了只要备份还在分分钟恢复到新机器。5.3 关于版本升级的提醒我刚开始用的时候图省事镜像 tag 直接用latest结果有一次推了新版本我的一个流程突然跑不动了。查了半天发现是某个节点内部逻辑变了输入输出结构不兼容。从那以后我就学乖了生产环境固定到具体的版本号比如deer-flow/deer-flow-server:v0.5.2测试环境验证过新版正常之后再手动升级生产。这个经验对任何自托管的开源项目都适用。跟着 latest 跑短期省事长期就是拿稳定性换时间。写在最后的一点使用体会我在实际使用 deer-flow 的过程中最大的感受是它把“自动化能力”从少数会写脚本的人手里交还给了真正需要流程解决问题的业务团队。我现在很多业务逻辑的调整完全不需要动代码在画布上拖一拖、改一改提示词点保存就生效了。当然它不适合用来编排特别复杂、涉及大量事务性操作的业务系统那种场景还是老老实实写代码。但在 AI 应用编排和中小规模自动化场景里我目前没找到比它更顺手、更轻量的替代品。最后再分享一个小技巧如果你决定在团队内部推广 deer-flow第一件事不是教大家怎么画节点而是建好一套命名规范。给每个流程、每个节点起一个一看就懂的名字比如“舆情监控-每半小时抓取摘要推送”这样半年之后再回来维护你还能一眼看懂当初的思路。流程可以复杂但命名不能乱这就是我踩过无数坑之后最想说的一句话。