简介:本资源是一份面向AI智能体开发初学者的Dify Workflow实战教学文档,聚焦小红书文案自动生成这一典型应用场景,系统讲解Workflow各核心节点的功能与协同逻辑,包括输入变量定义、LLM文本生成、变量赋值、代码清洗、Jinja模板转换及最终输出控制,帮助读者掌握低代码构建AI工作流的关键能力。资源为单个PDF文件,共1.67MB,内容结构清晰,涵盖节点原理说明、Prompt设计要点、CSV批量运行示例及实际输出效果截图,便于边学边练。已有541人学习下载,适合希望快速上手Dify平台、理解LLM集成与数据流转机制的开发者,尤其对需定制化内容生成(如营销文案、多风格标题)的实践者具有直接参考价值。
1. 小红书文案自动生成不是“调个API就完事”:Dify Workflow 把关键词到成品的黑匣子,拆成可调试、可复现、可交接的六步流水线
你试过用大模型写小红书文案吗?输入“轻奢风咖啡馆探店”,返回一段带emoji、分段空行、结尾带话题标签的文本——看起来很美。但上线后发现:品牌名总拼错、促销时间格式不统一、图片描述和实际图库对不上、甚至把“限前50名”写成“限前500名”。这不是模型不行,是生成链路里缺了工程化控制点。这篇笔记讲的,就是用 Dify 的 Workflow 功能,把“关键词→文案→审核→发布准备”这条链路,变成像 CI/CD 流水线一样可配置、可断点、可回溯的六步操作。它不依赖你本地跑 LLM,也不要求你写 prompt 工程师级提示词;核心是用 Dify 内置的节点编排能力,把「模板转换」「代码执行」「条件分支」「上下文传递」这些能力串成真实业务流。适合正在做内容中台、新媒体 SaaS 工具、或需要批量生成合规文案的运营+技术混合团队。如果你卡在“每次改一个字都要重跑整个 chain”,或者“不同账号要换三套 prompt”,那这篇就是为你写的血泪经验。
2. Dify Workflow 是什么:不是 Prompt 编排器,而是带状态机的 AI 任务调度器
Dify 的 Workflow 和传统 Prompt Chain 的本质区别,在于它引入了显式状态管理和节点间数据契约。你不能只写“让 LLM 想个标题”,而必须定义:“上一个节点输出的 JSON 字段product_name,将作为下一个节点title_generator的input.product参数传入”。这种契约感,让整个流程从“玄学调参”变成“接口联调”。
2.1 为什么不用 LangChain/CrewAI 做这事?
LangChain 适合研究型快速验证,CrewAI 适合多 agent 协作推理,但它们在生产环境有三个硬伤:
- 无内置 UI 调试面板:出错时你要翻日志、查 trace ID、手动构造 input 重放,而 Dify Workflow 提供节点级输入/输出快照,点一下就能重试;
- 状态持久化弱:LangChain 的
RunnableWithMessageHistory在服务重启后会丢上下文,Dify 的 workflow execution record 默认存 PostgreSQL,支持断点续跑; - 权限与发布隔离缺失:你无法给运营同事开一个“只能改文案模板、不能动知识库”的子账户,而 Dify 的 workspace + role system 天然支持。
提示:Workflow 不是替代 LLM 推理,而是调度 LLM 推理。它本身不加载模型,所有 LLM 调用都走 Dify 的
/v1/chat/completions统一网关,这意味着你换本地 Ollama 模型、或切到 Azure OpenAI,Workflow 图形界面里只需改一个“LLM 节点”的 provider 配置,无需动任何代码。
2.2 Workflow 核心四类节点解析(附小红书场景映射)
| 节点类型 | 小红书文案场景举例 | 关键参数说明 | 是否必须 |
|---|---|---|---|
| LLM 节点 | 生成正文初稿、润色标题、生成话题标签 | model(选 qwen2.5-7b 或 gpt-4o)、temperature=0.3(控创意发散度)、system_prompt(固定角色设定如“你是一名有5年小红书运营经验的文案策划”) | ✅ 必须(至少1个) |
| Code Execution 节点 | 解析用户输入的原始关键词(如“上海静安寺·咖啡馆·人均80” → 提取city=上海,poi=静安寺,category=咖啡馆);校验促销文案中的日期是否为工作日 | 支持 Python 3.11,可 importre,datetime,json;输入自动转为input变量(dict 类型);输出必须是 dict,字段名会被下游节点引用 | ✅ 必须(用于结构化输入) |
| Template 节点 | 将 LLM 输出的 raw text 套入预设 Markdown 模板:## {title}\n\n{body}\n\n📍{location}\n⏰{time_range}\n#小红书 #探店 #{category} | 使用 Jinja2 语法;变量来自上游节点输出字段;支持 `{{ body | truncate(200) }}` 这类 filter |
| Condition 节点 | 判断生成文案是否含敏感词(如“最便宜”“第一”),或判断图片数量是否 ≥3 张(触发不同审核流) | 支持input.field_name == "xxx"、"违规" in input.text等简单表达式;可设多个分支(True/False/Default) | ⚠️ 按需(但强烈建议加) |
2.3 一个真实可用的小红书 Workflow 结构图(文字版)
[Start] ↓ (input: keywords="杭州西湖边·新中式茶馆·周末限定") [Code Execution: keyword_parser] → 提取 city="杭州", poi="西湖边", style="新中式", time_tag="周末限定" ↓ (output: {city, poi, style, time_tag}) [LLM Node: title_gen] → 输入 system_prompt + 提取字段 → 输出 title="西湖畔的新中式茶馆,周末限定款青梅冰茶来了!" ↓ (output: {title}) [LLM Node: body_gen] → 输入 title + style + time_tag → 输出 body="推开木格窗,满眼是西湖波光…(200字)" ↓ (output: {body}) [Template Node: format_post] → 套入模板 → 输出 final_text="## 西湖畔的新中式茶馆…\n\n📍杭州西湖边\n⏰周末限定\n#小红书 #新中式茶馆 #杭州探店" ↓ (output: {final_text}) [Condition Node: check_sensitive] → 检查 final_text 是否含“最”“首”“绝对” → True → 走 [LLM Node: compliance_rewrite];False → 直接 [End]这个结构不是理想模型,而是我们线上跑着的版本。它把“生成”和“合规”解耦,让法务同事能单独维护compliance_rewrite的 system_prompt,而不碰前端输入逻辑。
3. 从零搭建:6 步完成小红书文案 Workflow(含可直接复制的代码块)
下面步骤基于 Dify 社区版 v1.10.1(2024Q3 最稳定版本),所有操作均在 Web UI 完成,无需 CLI 或修改源码。假设你已完成 Dify 部署(Docker 方式),且已配置好至少一个 LLM Provider(如 Ollama 的 qwen2.5:7b)。
3.1 Step 1:创建新 Workflow 并设置全局输入 Schema
进入 Dify 控制台 → 「Workflows」→ 「+ New Workflow」→ 命名为xhs-post-generator。
在右侧面板「Input Variables」中,定义输入结构:
{ "keywords": "string", "brand_name": "string", "image_count": "number", "target_audience": "string" }说明:这里定义的是契约,不是默认值。后续调用 API 时必须传
{"keywords":"..."},否则 workflow 启动失败。image_count字段为 Condition 节点判断图片数量提供依据;target_audience用于 LLM 节点控制语气(如“学生党”用更多网络语,“宝妈”用更生活化表达)。
3.2 Step 2:添加 Code Execution 节点解析关键词
拖入「Code Execution」节点,命名为parse_keywords。
在代码编辑框中粘贴以下 Python 脚本:
import re import json # input 是 Dify 自动注入的 dict,结构由 Step 1 定义 raw = input.get('keywords', '') # 提取城市(常见前缀:北京/上海/广州/深圳/杭州/成都...) city_match = re.search(r'(北京|上海|广州|深圳|杭州|成都|武汉|西安|南京|重庆)', raw) city = city_match.group(1) if city_match else '全国' # 提取 POI(取第一个中文括号内内容,或“·”分隔的第一段) poi_match = re.search(r'【(.*?)】|((.*?))|·(.*?)(?:·|$)', raw) poi = (poi_match.group(1) or poi_match.group(2) or poi_match.group(3) or '').strip() or '未知地点' # 提取风格关键词(新中式/轻奢/复古/ins风/日系等) style_keywords = ['新中式', '轻奢', '复古', 'ins风', '日系', '北欧', '工业风', '森系'] style = next((s for s in style_keywords if s in raw), '通用') # 提取时间标签(周末限定/暑期特供/早鸟价等) time_tags = ['周末限定', '暑期特供', '早鸟价', '限时优惠', '节日限定'] time_tag = next((t for t in time_tags if t in raw), '常规') # 构建结构化输出(字段名将被下游节点引用) output = { "city": city, "poi": poi, "style": style, "time_tag": time_tag, "raw_keywords": raw }说明:这段代码不依赖外部库,仅用标准库
re。关键点在于output字典的 key(city,poi)必须和后续 LLM 节点的 prompt 中变量名严格一致。例如 LLM 节点 prompt 写请为{city}的{poi}写一段{style}风格文案,那么{city}就会自动替换为output["city"]的值。
3.3 Step 3:添加两个 LLM 节点生成标题与正文
拖入第一个 LLM 节点,命名为gen_title。
配置如下:
Model:选你已配置的 qwen2.5:7b(或 gpt-4o)
Temperature:0.3(降低胡编乱造概率)
System Prompt:
你是一名资深小红书文案策划,专注探店类内容。请严格按以下要求生成标题: - 长度 ≤ 20 字 - 必须包含 {poi} 和 {time_tag} 信息 - 使用感叹号或问号收尾,增强点击欲 - 禁用“超赞”“绝了”等泛滥词,用具体意象替代(如用“青梅冰茶”代替“好喝”)User Prompt:
为{city}的{poi}创作一个{style}风格的标题,突出{time_tag}信息。
拖入第二个 LLM 节点,命名为gen_body。
配置如下:
Model:同上
Temperature:0.5(正文需稍多创意)
System Prompt:
你擅长写小红书爆款正文,要求: - 分3段:首段场景代入(视觉+听觉描写),中段产品细节(材质/口味/工艺),末段行动号召(打卡/预约/限时) - 每段以 emoji 开头(📍/🍵/👇) - 总字数 180~220 字,禁止超长 - 若 target_audience 是“学生党”,加入价格锚点(如“30元搞定”);若是“宝妈”,强调空间安全(如“儿童友好区”)User Prompt:
基于标题:“{title}”,为{city}的{poi}写正文。风格:{style},时间标签:{time_tag},目标人群:{target_audience}
注意:
{title}是自动从上一个gen_title节点的输出中提取的字段。Dify 会自动做字段映射,你无需写input.title。
3.4 Step 4:用 Template 节点统一封装格式
拖入「Template」节点,命名为format_xhs_post。
模板内容(Jinja2 语法):
## {{ title }} {{ body }} 📍{{ city }}{{ poi }} ⏰{{ time_tag }} #小红书 #探店 #{{ style|replace('新中式','新中式茶馆')|replace('轻奢','轻奢风') }} {% if image_count >= 3 %} 📌配图建议:1.门头全景 2.产品特写 3.环境氛围 {% endif %}说明:
{{ style|replace(...) }}是 Jinja2 filter,用于把“新中式”转成“新中式茶馆”这类更精准的话题标签。{% if %}块实现动态文案,当image_count≥3 时才显示配图建议,避免给只有1张图的客户错误引导。
3.5 Step 5:插入 Condition 节点做合规拦截
拖入「Condition」节点,命名为check_compliance。
Expression 填写:
"最" in input.final_text or "第一" in input.final_text or "绝对" in input.final_text or " guaranteed" in input.final_text.lower()添加两个分支:
- True 分支→ 连接到新的 LLM 节点
rewrite_for_compliance - False 分支→ 直接连到 End 节点
为rewrite_for_compliance配置:
- System Prompt:
你是一名广告法合规顾问。请重写以下文案,删除所有《广告法》禁止的绝对化用语(最、第一、顶级、首选等),改用客观描述或用户证言替代。保持原意和小红书风格。 - User Prompt:
原文:{final_text}
提示:这个节点让法务团队能独立维护 rewrite 规则,而无需工程师改 workflow 图。他们只需更新 system prompt,效果立即生效。
3.6 Step 6:配置 Output 并测试
在 workflow 末尾的「Output Variables」面板中,定义最终输出:
{ "final_text": "string", "debug_info": "object" }其中final_text来自format_xhs_post或rewrite_for_compliance的输出;debug_info可设为{"parsed": input, "timestamp": "now()"}用于问题定位。
点击右上角「Test」按钮,输入测试数据:
{ "keywords": "杭州西湖边·新中式茶馆·周末限定", "brand_name": "山月集", "image_count": 4, "target_audience": "学生党" }观察各节点执行状态:绿色表示成功,红色表示报错。点击任一节点的「View Details」可看到输入/输出 JSON,这是排查问题的第一现场。
4. 避坑指南:六个真实翻车现场与抢救方案(附错误日志特征)
Dify Workflow 看似图形化很友好,但生产环境里几个经典坑会让新人卡住 2 天。以下是我们在 3 个客户项目中踩过的真坑,按发生频率排序。
4.1 现象:Workflow 执行卡在 Code Execution 节点,日志显示ModuleNotFoundError: No module named 'pandas'
原因:误以为 Code Execution 支持 pip install,其实 Dify 的沙箱环境只预装re,json,datetime,math,random等标准库。pandas/numpy/requests全部不可用。
解决:
- 用标准库替代:
re.findall()替代pandas.Series.str.extract(); - 复杂 HTTP 请求改用「HTTP Request」节点(Dify 内置),它支持 GET/POST 和 JSON body;
- 如真需第三方库,改用「LLM 节点」+ system prompt 描述计算逻辑(如“请把以下数字列表求平均值:[1,2,3]”),让 LLM 当计算器用(适合低频、非关键计算)。
4.2 现象:LLM 节点输出的title字段在 Template 节点里渲染为空,但 Debug 面板显示title有值
原因:字段名大小写不一致。Code Execution 节点output = {"City": "杭州"},而 LLM 节点 prompt 写{city}(小写 c)。Dify 字段匹配严格区分大小写。
解决:
- 全流程统一用小写字母+下划线命名(
city_name,poi_location); - 在每个节点的「Output Preview」里,点开 JSON 查看实际 key 名,不要凭记忆写;
- 在 Template 节点里用
{{ title|default('未生成') }}防空渲染。
4.3 现象:Condition 节点始终走 False 分支,即使输入文本含“最便宜”
原因:Condition 表达式里用了中文全角标点或隐藏空格。比如"最" in input.final_text看似正确,但input.final_text实际是"最便宜 "(末尾是全角空格),导致in判断失败。
解决:
- 在 Code Execution 节点末尾加清洗:
output["final_text"] = input["final_text"].strip().replace(' ', ' '); - Condition 表达式改用正则:
re.search(r'[最第首]|绝对| guaranteed', input.final_text, re.I)(需先在 Code Execution 里 import re,并确保 output 包含re对象——但更推荐前者)。
4.4 现象:调用 API 时返回422 Unprocessable Entity,错误信息input must contain field 'keywords'
原因:Workflow Input Schema 定义了keywords为 required 字段,但 API 请求 body 里没传,或传了{"Keywords": "..."}(大小写错)。
解决:
- 用 curl 测试时,务必检查 JSON key:
curl -X POST "http://your-dify/api/v1/workflows/run" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"inputs":{"keywords":"上海外滩咖啡馆"}}' - 在 Dify 的「API Reference」页直接点「Try it out」,它会自动生成正确格式的请求体。
4.5 现象:Workflow 执行成功,但最终final_text里没有#小红书标签,而是原样输出#小红书
原因:Template 节点里写了#小红书,但 Dify 的 Template 渲染引擎会把#当作注释符(Jinja2 语法)。
解决:
- 用转义:
\#小红书; - 或用变量包裹:
{{ "#小红书" }}; - 更推荐:把所有话题标签存在 Code Execution 的 output 里,如
output["hashtags"] = ["#小红书", "#探店"],Template 里写{{ hashtags|join(' ') }}。
5. 进阶技巧:用 Workflow 实现“一稿多发”与灰度发布(含灰度开关配置表)
做到上面 6 步,你已经能稳定产出小红书文案。但真实业务不止于此——同一份探店素材,要发小红书、公众号、抖音图文,且新文案先推给 5% 用户测点击率。Workflow 完全能支撑,关键是用好「并行分支」和「动态参数」。
5.1 一稿多发:单次运行输出三端格式
在format_xhs_post节点后,不直接连 Condition,而是加一个「Parallel」节点(Dify v1.10 新增)。它会同时触发三个子流程:
| 子流程 | 目标平台 | 关键差异点 | Template 示例片段 |
|---|---|---|---|
| xhs | 小红书 | 段落空行多、emoji 密集、话题标签多 | ## {title}\n\n{body}\n\n#小红书 #{style} |
| 微信公众号 | 首段加导语“Hi,这里是XX探店”,禁用 emoji,结尾加“点击预约”按钮链接 | {% set btn_url = "https://xxx.com/book?id=" + poi_id %}\n<a href="{{ btn_url }}">点击预约</a> | |
| douyin | 抖音图文 | 标题加【】符号,正文用短句+换行,末尾加“↓评论区抽3人送券” | `【{{ title }}】\n{{ body |
实现要点:Parallel 节点本身不处理数据,它只是分发。每个子流程的 Template 节点都接收相同的上游
input(含title,body,poi),但用不同模板渲染。最终 output 定义为:{ "xhs_post": "string", "wechat_post": "string", "douyin_post": "string" }
5.2 灰度发布:用 Condition + 环境变量控制流量
Dify 支持为每个 workflow 设置「Environment Variables」(环境变量),它在 workflow 执行时注入,可用于灰度开关。操作路径:workflow 编辑页 → 右上角「Settings」→ 「Environment Variables」。
| 变量名 | 类型 | 示例值 | 用途 |
|---|---|---|---|
GRAYSCALE_RATE | number | 0.05 | 灰度比例(5%) |
ENABLE_COMPLIANCE_CHECK | boolean | true | 合规检查开关(上线后可随时关闭) |
DEFAULT_AUDIENCE | string | "学生党" | 当 input 未传target_audience时的兜底值 |
在 Condition 节点check_compliance的 Expression 中,这样写:
input.get('is_gray', false) or (random.random() < env.GRAYSCALE_RATE)其中random.random()是 Dify 内置函数(无需 import),返回 0~1 的浮点数。这样,5% 的请求会强制走合规重写流,其余走直通流。
表:灰度开关配置与业务效果对照表
开关变量 值 生效场景 验证方式 GRAYSCALE_RATE=0.011% 流量进重写 新上线 rewrite prompt,怕误伤 查看 rewrite_for_compliance节点执行次数 / 总执行次数 ≈ 0.01ENABLE_COMPLIANCE_CHECK=false关闭所有合规检查 大促期间临时放开限制 Condition 节点显示 “Skipped” 状态 DEFAULT_AUDIENCE="宝妈"未传 audience 时按宝妈风格生成 老客户 API 未升级,仍传旧字段 检查 output 中 target_audience字段值
5.3 终极技巧:用 Code Execution 节点做 A/B 测试分流
有时灰度不够细,你需要对同一关键词,A 组用“价格锚点”话术,B 组用“体验感”话术。这时靠环境变量不够,要用 Code Execution 做哈希分流:
import hashlib # input 是原始请求体 raw_input = json.dumps(input, sort_keys=True) hash_val = int(hashlib.md5(raw_input.encode()).hexdigest()[:8], 16) group = "A" if hash_val % 2 == 0 else "B" # 输出分组标识,供后续 LLM 节点读取 output = { "group": group, "city": input.get("city", "全国"), "poi": input.get("poi", "未知") }然后在gen_body的 System Prompt 里加条件:
{% if group == "A" %} 请强调价格优势,使用“30元搞定”“学生价”等表述。 {% else %} 请强调空间体验,使用“阳光洒满”“木质香”“安静角落”等感官词。 {% endif %}这个技巧让我们在一个 workflow 里跑出两套文案策略,数据对比直接在 Dify 的 workflow execution log 里按group字段筛选即可。
从那以后我每次上线新 prompt,都强制走一遍灰度开关 + A/B 分流双保险。哪怕 LLM 突然抽风,也只影响 5% 用户,而不是全量翻车。希望帮到你。
本文还有配套的精品资源,点击获取