为什么你的提示词一直在打工,却从来不涨工资
我见过太多人写提示词的状态:每一次开新选题,打开一个空白的聊天框,把上一次的提示词复制过来,然后开始动手改——把「AI 绘画」换成「智能家居」,把「面向设计师」换成「面向宝妈」,把「3000 字」改成「5000 字」,改着改着发现上下文对不上了,索性从头再写一遍。一个小时后,你得到了一份全新的提示词,和上一份有 80% 的相似度。
这不是你提示词写得不好。这是你把「会变的东西」和「不会变的东西」搅在了一起。而 Dify 这类工作流平台的全部价值,恰恰在于把这两类东西切开:提示词是资产,主题是参数。
把主题硬编码进提示词的三个隐性成本
先说清楚「硬编码」到底贵在哪。很多人的直觉是:改一改嘛,又不难。真正的成本不在改写那十分钟,而在下面三件事上。
成本一:你无法比较两个版本哪个更好
如果提示词每周都变,你就永远得不到一个稳定的基线。这周出的文章结构好,是因为你优化了提示词,还是因为这次的主题本身就好写?下周效果差了,是提示词退步了,还是选题太难?你无法回答,因为你把两个变量同时动了。Dify 的工作流一旦锁定提示词,主题就变成唯一的自变量,你才第一次拥有可以对照实验的条件。
成本二:好的经验无法沉淀
某天你灵光一现,在提示词里加了一句「每节结尾必须留一个悬念,让读者想看下一节」,效果出奇地好。但当这句话随着主题一起被改掉,这个经验就蒸发了。三个月后你想不起来自己当初为什么那么写。而在 Dify 里,这一句应该被放在「分节扩写」节点的系统提示词里,它是一次性的投资,会跟着每一条主题跑。
成本三:你没法把活交出去
硬编码提示词的最终形态是「只有你会用」。同事想帮你出一篇稿,得先问你「你上次那套提示词在哪」,然后你花二十分钟解释哪里要改。而在 Dify 里,别人只需要在开始节点填六个框:主题、读者、关键词、字数、语气、禁用词。他甚至不需要知道后面的提示词长什么样。
一个反直觉的判断:写得越细的提示词,越难复用
这是我最想强调的一点,也是大多数人做错的地方。很多人以为提示词越具体越好——具体到「写一篇讲 AI 绘画在小红书变现的文章,面向 25-35 岁女性自由职业者,3000 字,语气亲切,多用 emoji」。这句话作为一次性指令确实很好,但它作为资产是零价值的,因为它把这个主题的所有特征都焊死了。
真正可复用的提示词,反而要写得「空」——它只描述结构和规则,不描述内容。「每节开头用一句反常识的判断」「每节必须有至少一个具体数字或案例」「禁止使用『综上所述』『众所周知』」。这些规则跟主题无关,跟内容无关,只跟质量有关。它们才是资产。Dify 的工作流让你第一次能系统地把这些规则拎出来,单独存放、反复调用。
先想清楚:什么该变成变量,什么必须留在提示词里
动手搭工作流之前,先做一次「切分手术」。拿一张纸,把你过去一年写过的提示词摊开,逐句问自己:这句话换主题后还成立吗?成立,留在提示词;不成立,抽成变量。
大部分情况下,切分结果出奇地一致。下面是我在几十个项目里反复验证过的变量清单。
六个必选变量与三个进阶变量
必选变量:没有它就跑不起来
- topic(主题):一句话说清要写什么。不要写成标题,写成陈述句,比如「用 Dify 搭建一个能自动出稿的文章工作流」比「Dify 文章工作流教程」给模型的信号强得多。
- audience(目标读者):写清楚读者的认知起点和真实痛点。「不懂代码的运营」和「会写 Python 的独立开发者」,拿到的同一篇稿子会长得完全不一样。
- keywords(核心关键词):三到六个必须自然出现的词。注意是「自然出现」,不是「强行堆砌」——这一点要在提示词里作为约束写死。
- word_count(字数):总字数。分节扩写时要能自动拆到每节,所以这个值最好是整数。
- tone(语气):专业、轻松、犀利、温和。语气决定了模型用不用第一人称、能不能用反问、敢不敢下判断。
- forbidden_words(禁用词):这个变量很多人漏掉,但它是最省事的一个。把所有你不想要的词——行业黑话、过度承诺、违禁表述——一次性列进去,后面每一条主题都自动生效。
进阶变量:让同一套工作流跑出不同形态
- reference(参考素材):粘贴的原文段落、数据表、竞品文章摘要。它决定了这篇稿子是「凭空生成」还是「有据可依」。
- section_count(章节数):让大纲节点知道该出几节。技术教程通常 6-8 节,观点长文 3-5 节。
- language_style(语言风格枚举):把语气拆成可枚举的值,比如「口语化」「书面化」「工具文档风」,方便后面做条件分支。
变量到底该定义在哪里:开始节点还是提示词节点
这是 Dify 新手最容易搞混的地方。Dify 里有两个地方可以「写东西」:开始节点的输入字段,和提示词节点里的文本。很多人的错误是把主题写在了提示词节点里,然后在开始节点里空着,结果就是——开始节点形同虚设,这条工作流还是没法复用。
记住一条铁律:所有会变的东西,一律定义在开始节点。开始节点是这条工作流的「接口」,它定义了外部世界能传什么进来。提示词节点是「实现」,它只负责消费这些变量,拼接成给模型的指令。换句话说:开始节点是函数签名,提示词节点是函数体。你不会把参数值写死在函数体里。
在 Dify 的开始节点里,每个字段要设置三样东西:变量名、字段类型(文本/段落/数字/下拉选项)、是否必填。主题、读者、关键词建议用「段落」类型,方便填长内容;字数和章节数用数字;语气用下拉选项,把预设选项固定下来,避免每次输入格式不一致。
为什么「参考素材」适合做可选字段
并不是每篇文章都有素材。如果把它设成必填,用户每次都要硬凑一段,反而制造噪音。更好的做法是设成可选,然后在提示词里加一句条件判断:「如果 reference 不为空,优先使用其中出现的数据和案例;如果为空,则依靠模型自身的知识,并避免编造具体数字」。这一句写进 Dify 的提示词节点,就永久生效了。
变量命名规范:为什么必须是英文蛇形
这一条看起来是小事,但踩坑的人极多。Dify 的提示词节点里引用变量用的是双花括号语法,比如 {{topic}}。同时,Dify 的很多节点支持在字段里写表达式,表达式里变量是作为标识符被解析的。
如果你把变量命名为中文,比如叫「主题」,那么在 {{主题}} 这种引用里,解析器可能把它当成一个未定义的标识符,或者在不同版本、不同节点里表现不一致。更麻烦的是 API 对接:Dify 的 API 调用要求你在 inputs 里传出字段名,如果这个字段名是中文,很多客户端库、服务端框架、网关会做编码处理,轻则报错,重则静默丢失,排查起来极其痛苦。
所以变量名一律用英文蛇形命名:topic、target_audience、core_keywords、word_count、tone_style、forbidden_words、reference_material、section_count。名字要能自解释,不要用 a、b1 这种。
可复制的变量定义表
| 变量名 | 显示名 | 类型 | 必填 | 示例值 | 用途 |
|--------------------|--------------|----------|------|----------------------------------------------------|--------------------------|
| topic | 主题 | 段落 | 是 | 用 Dify 搭建自动出稿的文章工作流 | 全文的唯一自变量 |
| target_audience | 目标读者 | 段落 | 是 | 不懂代码但有内容需求的运营、市场人员 | 决定语言深浅与举例方式 |
| core_keywords | 核心关键词 | 段落 | 是 | Dify,工作流,提示词模板,变量 | 必须自然出现的关键词 |
| word_count | 总字数 | 数字 | 是 | 7000 | 用于拆解到每节的字数 |
| tone_style | 语气 | 下拉 | 是 | 专业克制 / 口语轻松 / 犀利直白 / 温和科普 | 控制人称、修辞与判断强度 |
| forbidden_words | 禁用词 | 段落 | 否 | 赋能,闭环,抓手,生态位 | 全文不得出现的词 |
| reference_material | 参考素材 | 段落 | 否 | (粘贴原文、数据表或竞品摘要) | 提供事实依据,减少编造 |
| section_count | 章节数 | 数字 | 是 | 8 | 大纲节点输出几节 |
| language_style | 语言风格 | 下拉 | 否 | 口语化 / 书面化 / 工具文档风 | 供后续条件分支使用 |
三段式流程设计:大纲、扩写、合并
变量切好之后,就要设计流程。Dify 的工作流画布是可视化的,节点之间连线,数据顺着线流动。一条完整的文章工作流,最稳的结构就是三段:先出大纲,再逐节扩写,最后合并输出。
很多人会问,为什么不直接一个节点出全文?因为一个节点出的全文,你无法干预中间过程,也无法只重跑一部分。三段式的本质是把一次不可控的长生成,拆成三次可控的短生成。下面逐段讲。
段一:大纲生成——把主题变成可执行的结构
大纲节点的任务是:拿到六个变量,产出一份章节清单,每节带标题和要点。这个节点是整条链路的地基,它决定了后面所有节点的输入格式。
为什么大纲必须强制返回 JSON
这是新手最容易忽略的设计决策。如果你让模型返回一段普通文本的大纲,比如「第一节:为什么……,要点:A、B、C;第二节……」——人是能看懂的,但下一个节点看不懂。Dify 的迭代节点需要遍历一个数组,如果拿到的是一个字符串,它就没法逐项迭代。
所以大纲节点必须配上 Dify 的结构化输出能力:在提示词节点里开启输出格式,定义一个 schema,规定返回 { "outline": [ { "index": 1, "title": "...", "points": [...], "target_words": 900 } ] }。这样后面直接用变量引用就能取到数组,迭代节点直接吃。
这是 Dify 相对纯聊天界面的核心优势之一:节点之间靠结构化数据传参,而不是靠自然语言拼接。自然语言拼接在节点多了之后会失控——你不知道上一段的哪个词会干扰下一段,也不知道模型是不是漏写了某个字段。
大纲 JSON 提示词模板
你是一位有二十年经验的内容策划。请根据以下信息,为文章设计一份章节大纲。
【主题】{{topic}}
【目标读者】{{target_audience}}
【核心关键词】{{core_keywords}}
【总字数】{{word_count}}
【章节数】{{section_count}}
【语气】{{tone_style}}
【参考素材】{{reference_material}}
要求:
1. 章节数必须正好是 {{section_count}} 节,不多不少。
2. 每节标题不超过 20 个字,必须是一个具体的判断或动作,不能是「概述」「简介」这类空词。
3. 每节给 3 到 5 个要点,要点必须是可执行的动作或可验证的事实,不写套话。
4. 第一节要直接切入痛点,不要在开头做冗长的背景铺垫。
5. 每节的 target_words 按照总字数平均分配,最后两节可以略多,用于收尾和落地。
6. 核心关键词要分散到不同章节,不要全部挤在一节里。
只输出 JSON,不要输出任何解释、前后缀或 Markdown 代码块标记。
格式如下:
{
"outline": [
{
"index": 1,
"title": "章节标题",
"points": ["要点一", "要点二", "要点三"],
"target_words": 900
}
],
"overall_logic": "用一句话说明这篇稿子的主线逻辑"
}
大纲节点之后要加一个代码节点做兜底
模型偶尔会不听话。它可能在 JSON 前面加一句「好的,以下是大纲」,也可能用 Markdown 代码块包起来。所以在大纲节点和迭代节点之间,最好插一个代码节点(Dify 支持 Python/JavaScript),做三件事:去掉前后缀和代码块标记、检查 JSON 是否合法、检查数组长度是否等于 section_count。如果校验失败,走失败分支重试一次。这一段后面还会细讲。
段二:分节扩写——迭代节点是整条链路的发动机
拿到大纲数组之后,就该逐节写了。Dify 的迭代节点可以遍历一个数组,对每个元素跑一遍子流程,把所有结果收集起来。这正是分节扩写最自然的实现方式。
迭代节点里要传三样东西
每一节扩写时,模型需要三种上下文,缺一不可:
- 全局上下文:主题、读者、语气、禁用词、整份大纲。让模型知道自己在写整篇文章的第几节,前后是什么结构。
- 当前节要点:这一节的标题和 points 数组。这是本节的内容骨架。
- 上一节结尾:上一节最后两三句原文。这是解决「节间衔接生硬」的关键。
前两样比较好理解,第三样最容易漏。很多人第一次做分节扩写,跑出来的稿子每一节都像是单独的一篇,读起来有明显的断裂感——因为每一节都是从零开始的,模型不知道上一节已经讲到了哪。加上「上一节结尾」,模型就能自然地接上。
具体的做法是:迭代节点里维护一个「上一节结尾」的变量,初始为空;每一轮结束后,用代码节点截取本轮输出的最后 100-150 字,写回这个变量,供下一轮使用。Dify 的迭代节点支持在子流程内引用外层变量,这个回写稍微绕一点,最常见的做法是用变量赋值节点在迭代结束前更新。
分节扩写提示词模板
你是资深内容作者,正在为一位内容编辑撰写一篇长文的其中的一节。
【整篇文章主题】{{topic}}
【目标读者】{{target_audience}}
【全文语气】{{tone_style}}
【全文禁用词(出现即失败)】{{forbidden_words}}
【完整大纲(供你理解前后文,不要复述其他节的内容)】
{{outline_text}}
【你要写的这一节】
第 {{current_index}} 节,标题:{{current_title}}
本节要点:
{{current_points}}
【本节目标字数】{{current_words}} 字(上下浮动不超过 15%)
【上一节的结尾原文】
{{previous_ending}}
写作要求:
1. 开头不要重复本节标题,直接写内容。如果上一节结尾不为空,本节第一句要能自然衔接上一节,但不要写「正如上文所述」这类衔接词。
2. 只写本节,不要提前写后面章节的内容,也不要总结全文。
3. 每个要点至少用一个具体例子、数字或步骤来支撑,禁止只给观点不给证据。
4. 核心关键词出现在本节时保持自然,不要为了出现而出现。
5. 禁止使用以下词汇:{{forbidden_words}}
6. 结尾留一个自然的过渡,为下一节铺垫,但不要写「下一节我们将」这种直白的提示。
只输出本节的正文内容,不要输出标题、不要输出节序号、不要输出任何说明。
为什么每节都要把「完整大纲」喂进去
有人会觉得这样很浪费 token——八节就要把大纲传八遍。但这是必要成本。如果你只传当前节,模型就不知道前后是什么,很容易在某节里把后面该讲的东西提前讲了,导致后文无话可说、开始注水。把大纲传进去,相当于给模型一张地图,它知道自己在哪、要去哪、不该越界。
真正的成本优化在别处:把大纲压缩成「节序号 + 标题」的清单再传,而不是传完整的 points。这样 token 能省一半,效果几乎不损失。
段三:合并输出——拼装只是开始,格式清理才是关键
迭代节点跑完,你会得到一个结果数组。合并节点要做四件事:给全文加一个主标题、拼装各节正文、在最前面加一段摘要、做格式清理。
主标题单独生成,不要用大纲的第一节标题
很多人偷懒,直接把第一节标题当文章标题。这是错的。节标题和文章标题的功能完全不同:节标题服务于结构,文章标题服务于点击。所以合并节点之外,最好再挂一个小的提示词节点,专门根据主题和全文摘要生成主标题,要求给三个候选,再由一个代码节点或人选定一个。
摘要放在最后生成,不要放在最前
这是我在实践中反复验证过的顺序问题。如果你先让模型写摘要,它写的一定是「空对空」的预告。正确的做法是:全文拼装完成之后,把全文正文喂给一个摘要节点,让它压缩成 80-100 字。这样摘要里就会有真实的信息密度,而不是「本文将介绍……」这种废话。
合并节点的最小配置
Dify 的合并靠模板节点(或者代码节点里的字符串拼接)。最简版本就是一个模板,把主标题、摘要、各节正文按顺序拼起来。更稳的版本是在模板里加条件:如果某节正文为空或过短,插入一行「本节内容生成失败,请重试」,并在最终日志里标红。这样你一眼就能看出哪一节出了问题,而不是交出去之后才发现少了一段。
为什么分节扩写比一次性生成好:四个真实差异
总有人问:一次生成不香吗,省得搞这么复杂。下面这四个差异,是我在几十篇长文里对比出来的,不是理论推演。
差异一:一次性生成会「后段塌陷」
模型在生成长文时,注意力是有限的。写到三千字以后,开头定下的语气、结构、用词习惯会逐渐稀释,这就是「风格漂移」。更严重的是「后段塌陷」:文章的后三分之一开始重复前文的观点,句式变得模板化,判断变得含糊,「综上所述」「总而言之」开始高频出现。
分节扩写天然规避了这个问题,因为每一节都是一次独立的短生成,每一节都从「满血状态」开始。你不需要一篇 7000 字的稿子中途不崩,你只需要每一节 900 字不崩。后者对模型来说容易得多。
差异二:可控性——坏了只修一节
这是最实际的收益。一次性生成的稿子,如果你对第五节不满意,你只有两个选择:要么整篇重跑,赌这次第五节能写好;要么手动改。而分节扩写,你可以只重跑第五节——在 Dify 里就是把迭代节点单独跑一次,或复制一个测试运行只带第五个输入。
重跑的粒度决定了迭代速度
内容创作的本质是迭代。一篇稿子从初稿到能用,通常要改三到五轮。如果每轮都是整篇重跑,你的迭代成本是 O(n);如果每轮只是局部重跑,成本是 O(1)。这个数量级的差异,决定了你一天能出几篇稿。
差异三:但分节也有代价——衔接会生硬
这里必须诚实。分节扩写不是没有代价的。最明显的代价就是节间衔接。每一节单独看都很好,连起来读却像八个人各写一段。读者会在每一节开头感到一次「重新启动」,这对于追求流畅阅读体验的文章是硬伤。
解决方案有三层,逐层递进:一是提示词里带「上一节结尾原文」,让模型自然续接;二是要求每节开头「不重复标题、写一句承接上文的具体句子」,而不是抽象过渡;三是写完之后的润色环节,专门有一个节点只做「衔接修补」——把每两节交界处各取前后 100 字,让模型重写这 200 字,让它读起来像同一支笔写的。第三层最费事,但对成稿质量提升最明显。
差异四:字数分布更均匀
一次性生成 7000 字,模型很容易把一大半篇幅花在开头,到后面草草收尾。因为「开头好写,结尾难写」是模型的固有倾向。分节扩写则给了每节一个硬指标,模型在写每一节时都知道自己要凑够 900 字,分布的均匀性会好很多。这也是为什么大纲节点里要给每一节单独的 target_words,而不是只给总字数。
硬编码提示词与变量化提示词对比表
回到最初的论点,用一张表把两种做法的差异说清楚。
| 对比维度 | 硬编码提示词 | Dify 变量化提示词 |
|---|---|---|
| 换主题的操作 | 重写整段提示词,改 5-8 处 | 在开始节点改一个字段 topic |
| 单次换主题耗时 | 10 到 30 分钟,含调试 | 1 分钟内,不含生成时间 |
| 出错风险 | 高,容易漏改某处上下文 | 低,只有参数变,逻辑不变 |
| 质量基线 | 每次都在漂移,无法对比 | 稳定,可做 A/B 对照 |
| 经验沉淀 | 随主题一起被覆盖 | 沉淀在提示词节点里,持续生效 |
| 协作 | 只有作者本人会改 | 填写输入字段即可,零学习成本 |
| 批量出稿 | 基本不可行 | 通过 API 批量传参数即可 |
| 适用场景 | 一次性、极特殊的需求 | 持续性、系列化、模板化的选题 |
一次性生成与分节扩写对比表
| 对比维度 | 一次性生成全文 | 分节扩写 |
|---|---|---|
| 风格一致性 | 后段易漂移 | 整体稳定,交界处需修补 |
| 单节点可控性 | 低,只能整篇重跑 | 高,可单节重跑 |
| 修一次的成本 | 整篇 token 成本 | 单节 token 成本 |
| 字数控制 | 前多后少,分布不均 | 按 target_words 均匀分布 |
| 结构可控性 | 依赖模型自觉 | 由大纲节点锁定,逐节落实 |
| 段落衔接 | 自然 | 需靠上下文与润色修补 |
| 实现复杂度 | 一个节点搞定 | 需大纲、迭代、合并、代码兜底 |
| 适合篇幅 | 1500 字以内较稳 | 3000 字以上几乎必选 |
大多数人做错的地方:五个常见误区
这一节是我最想写的。前面讲的是怎么做对,这一节讲的是为什么你做了工作流却还是没变快。这些坑我自己全踩过。
误区一:把工作流做成「一键出稿」的黑盒
新手搭完工作流最喜欢干的事,是把所有节点连成一条直线,点一下运行,等三分钟,得到一篇稿子。看起来很美,但很快你就发现:遇到不合适的稿子,你根本不知道是哪一步出的问题。是主题理解偏了,还是大纲太散,还是某一节扩写注水了?
正确做法是在每个关键节点之后都留一个「可观察点」——大纲节点之后看一下 JSON,合并之前看一下每节字数,而不是等到最后才看结果。Dify 的运行日志里能看每个节点的输入输出,你要养成点进去看的习惯。工作流的价值不在于自动化,而在于让每一步都可检查。
误区二:把示例文章整篇塞进参考素材
很多人以为素材越多越好,把一篇写得好的范文整篇贴进 reference_material。结果模型写出来的稿子全是那篇范文的影子——句式、结构、甚至论点都照搬。参考素材的作用是提供事实,不是提供风格。风格应该由提示词规则来控制,而不是由一篇范文来暗示。
正确的做法是:素材只放数据和案例,不放成篇的论述。如果是竞品文章,只摘它的核心结论和数据出处,不摘它的表达。
误区三:在每一个节点里都重写一遍规则
我见过有人在大纲节点里写了一段「语气要专业克制」,然后在分节扩写节点又写一遍,合并节点再一次。三处规则不一致时,模型会遵循哪一条?你无法预测。更严重的是一旦你要改语气,得改三处,漏一处就打架。
正确的做法是:全局规则只定义一次。要么全部用变量传入,要么放在一个共享的系统提示词里。Dify 没有特别强的「全局提示词」机制,所以最稳的做法是把「语气、禁用词、读者」这些跨节点规则都做成开始节点的变量,然后在每个节点里用同一套引用。变量只在一处被改,全局同步。
误区四:只优化提示词,不优化数据结构
这是最有价值的一条。很多人调工作流调了一周,不停改提示词措辞,效果时好时坏。但他们从来没想过:也许问题不在措辞,而在数据结构。比如大纲的 points 如果是一整段话而不是数组,模型在扩写时就得自己拆解,每次拆得都不一样,稳定性自然差。如果改成三到五条独立字符串,扩写节点的输入就变得确定,输出也变得稳定。
在 Dify 里,数据结构的设计比提示词的字词选择更重要。同样一句话「本节要点」,用数组传和用字符串传,效果能差出两个档次。每次遇到不稳定,先问自己:是不是某个字段该是数组的,我传了字符串?
误区五:一次性把参数调到「完美」再上线
有人把一条工作流调试到十全十美才敢用,结果花了三周。更聪明的做法是:先用两个差异极大的主题跑通骨架,确认「换主题不改提示词」这条底线成立,然后再慢慢打磨。骨架对了,细节可以边跑边改;骨架不对,细节调得再好也是白搭。下面第四节就是讲这个验证方法。
换主题不改 Prompt:用一个对照实验证明给你看
光说不练没有说服力。这一节给你一个可以立刻自己做的验证:用同一条工作流,跑两个完全不同的主题,看提示词有没有改动一个字。
实验设计:两个「八竿子打不着」的主题
选主题有个原则:差异越大越能暴露问题。如果两个主题都属于「AI 工具教程」,它们天然相似,模型靠惯性也能写得差不多,验证不出什么。所以要故意选跨领域的。
- 主题 A:用 Dify 搭建自动出稿的文章工作流(面向运营,技术类)
- 主题 B:城郊周末露营的装备清单怎么配(面向新手家庭,生活类)
这两个主题的读者、语气、关键词、结构逻辑都完全不同。如果提示词里有任何硬编码的痕迹——比如写死了「本文聚焦 AI 工具」,或者默认读者是技术人——主题 B 一定会露馅:要么写出来还是一股技术味,要么直接跑偏。
只改开始节点的输入字段
实验的关键约束是:除了开始节点的输入,其它节点一律不动。下面是把主题 A 换成主题 B 时,你需要改的内容——只有这一张表的右列。
| 变量 | 主题 A 取值 | 主题 B 取值 |
|---|---|---|
| topic | 用 Dify 搭建自动出稿的文章工作流 | 新手家庭城郊周末露营的装备清单 |
| target_audience | 有内容需求的运营与市场人员 | 完全没有露营经验的城市家庭 |
| core_keywords | Dify,工作流,提示词模板 | 露营,装备清单,新手,预算 |
| tone_style | 专业克制 | 温和科普 |
| forbidden_words | 赋能,闭环,抓手 | 硬核,天花板,闭眼入 |
| section_count | 8 | 6 |
| word_count | 7000 | 5000 |
怎么证明「提示词零改动」
用提示词节点的导出功能对比
Dify 的工作流支持导出为 DSL 文件(YAML 格式)。做实验之前导出一份 v1.yml,两个主题都跑完之后再导出一份 v2.yml,用 diff 工具对比。如果提示词节点里的文本完全一致,diff 结果里只会出现运行记录相关的内容,不会有任何 prompt 字段的变化。这是最硬的证据。
观察两次输出的差异在哪
diff 证明提示词没变之后,再看输出。如果两次输出在结构上一致(都是同样的节数逻辑、同样的 JSON 结构、同样的每节要点密度),但在内容上完全不同(一个是技术流程,一个是露营清单),就说明变量化是真的生效了。
反过来,如果主题 B 的输出里出现了「我们会用到模型」「提示词设置」之类的词,说明某个提示词的角落还藏着硬编码的痕迹,回去找那句话,把它改成变量或者删掉。
一个容易忽略的坑:默认值里藏着的硬编码
有时候提示词明明没改,但主题 B 的效果还是不对。检查一下开始节点的默认值。很多人在搭工作流时,为了让测试跑得通,把 topic 的默认值填成了第一个主题,把 forbidden_words 填成了第一组词。结果换主题时忘了改默认值,模型收到的还是旧参数。
更隐蔽的是,有些人把「专业克制」这种语气直接写进了下拉选项的默认值里,而主题 B 本来就是温和科普。这类问题在 diff 里看不出来,因为提示词确实没变,但参数传错了。所以做对照实验时,一定要逐个核对开始节点的每一个字段,确认都换成了新值。
失败处理:三种最常见的报错与应对
工作流一旦跑起来,失败是常态。Dify 提供了失败分支、重试、超时设置,但关键是你要知道哪些失败需要兜底、哪些失败值得重试。下面是最常见的三种。
失败一:模型返回的不是合法 JSON
这是大纲节点最常见的失败。即便你开启了结构化输出,模型偶尔还是会返回带 Markdown 代码块标记的内容,或者在 JSON 前多说一句话。Dify 的结构化输出能拦住一部分,但拦不住全部。
三层兜底策略
- 第一层:开启结构化输出。在提示词节点里配置输出 schema,让 Dify 在模型返回时做格式约束。这一步能解决 80% 的问题。
- 第二层:代码节点清洗。写一个代码节点,先去掉首尾的
```json和```,再找到第一个{和最后一个}之间的内容,然后json.loads。如果解析失败,返回一个带ok: false标记的对象。 - 第三层:条件分支重试。判断
ok是否为真,为假时走一个重试节点,把原始返回和「你上次的返回不是合法 JSON,请只输出 JSON」一起传回去,最多重试两次。
import json
def main(raw: str) -> dict:
text = raw.strip()
if text.startswith("```"):
text = text.split("```")[1]
if text.startswith("json"):
text = text[4:]
start = text.find("{")
end = text.rfind("}")
if start == -1 or end == -1:
return {"ok": False, "data": [], "reason": "no json object found"}
try:
obj = json.loads(text[start:end + 1])
except Exception as e:
return {"ok": False, "data": [], "reason": str(e)}
outline = obj.get("outline", [])
if not isinstance(outline, list) or len(outline) == 0:
return {"ok": False, "data": [], "reason": "outline empty"}
return {"ok": True, "data": outline, "reason": ""}
注意这个代码节点输出的 data 是数组,可以直接接到迭代节点的输入上。这就是前面说的「数据结构优先」——代码节点不仅清洗格式,还顺便把结构对齐了。
失败二:某节字数不达标
字数不达标有两种情况:模型写少了,或者写多了。写少了更常见,也更麻烦。常见的表现是某节只写了两三百字,就草草收尾,然后开始总结。
判断标准要放宽,不要卡死
如果你把标准设成「必须正好 900 字」,那几乎每一节都会触发重试,成本爆炸。合理的标准是 ±20%。即 900 字的目标,720 到 1080 字都算合格。超出这个区间的,才走重试。
重试时要把「为什么重试」告诉模型
很多人重试时就是原样再跑一遍,结果大概率还是短的。正确的做法是把失败原因带回去:「你上次只写了 380 字,目标是 900 字。请把第三个要点展开成三个具体步骤,每个步骤给一个例子,重新写这一节」。带上诊断信息的重试,成功率会高很多。
另外,字数不达标的根因往往不在扩写节点,而在大纲节点。如果某一节的 points 只有两条空泛的要点,模型拿什么也扩不出 900 字。所以字数不达标时,先回头看看大纲那一节是不是写得太薄。
失败三:迭代节点中途失败
迭代节点是串行或并行地跑子流程,跑到第三节时模型超时了,整条迭代就中断了。Dify 的迭代节点支持设置最大并行数和错误处理策略,但默认的行为是中断整个迭代。
把「失败」变成「占位」
最稳的做法不是让迭代失败,而是让失败的节输出一个明确的占位符,比如 [本节生成失败:超时]。这样迭代能完整跑完,你在合并节点里一眼就能看出哪一节缺了,然后单独重跑那一节。如果让迭代直接中断,你前面跑完的五节就浪费了。
具体实现:在迭代子流程里,给扩写节点的失败分支接一个代码节点,返回占位字符串。同时在日志里记录失败节的 index。跑完看一遍日志,就知道该补哪一节。
并行度不要开满
Dify 的迭代节点可以设置并行数,并行跑能省时间,但并行数开太大会触发模型接口的限流,反而更容易失败。经验值是开 2 到 3。如果你用的是第三方模型的 API,限流更严格,建议开 1 到 2。
把工作流变成生产力:输出、接口与批量
工作流跑通了只是第一步。真正让它变成生产力的,是把它接出去,让结果能自动流到你需要的地方。
输出:先导出 Markdown,再考虑其他格式
合并节点的输出建议直接是 Markdown。原因很简单:Markdown 是纯文本,任何下游系统都能处理,转 HTML 一行代码,转 Word 用现成库,转富文本粘进编辑器也不变形。不要在工作流里直接拼接 HTML,因为 HTML 一旦带了标签,后面想改格式就要重新解析。
Markdown 输出的最小约定
- 主标题用一级标题,节标题用二级标题。
- 摘要放在标题下方,用引用块包起来。
- 每节之间用空行分隔,不要用多余的分隔线。
- 代码块统一用三反引号加语言标记。
这些约定看起来琐碎,但一旦固定下来,你的导出脚本就能稳定地按规则切分、重排、加样式。
接口:接 Webhook 还是走 API
Dify 的应用可以发布成 API,你在外部系统里用 HTTP 请求调用,把变量放在 inputs 里传进去。这是最通用的方式,适合自己做前端或者接到自己的后台。
但如果你的需求是「在 Notion 建一条记录就自动出稿」,那更适合查一下 Dify 是否支持工作流的 Webhook 触发,或者用外部工具做中转:Notion 触发 → 中转服务读取记录 → 调用 Dify API → 把结果写回 Notion。这条链路里 Dify 只负责「生成」,其它的编排交给外部工具。
API 调用的几个注意事项
- 参数名要和开始节点的变量名完全一致,这也是前面强调英文蛇形命名的原因。
- 可以传
response_mode: blocking等待结果,也可以传streaming边生成边取。长文建议用 streaming,避免请求超时。 - 记录每次调用的
task_id和workflow_run_id,出问题时能回溯到具体是哪一次运行。 - 在开始节点设好默认值,这样即使某个可选字段没传,工作流也不会直接报错。
批量:一次跑十篇的正确姿势
批量跑长文有个反直觉的结论:不要试图用 Dify 内部去批量遍历主题列表。虽然可以用迭代节点套迭代节点,但两层迭代会让日志难以追踪,失败定位也变成噩梦。
更好的做法是:Dify 只负责「一次出一篇」,批量交给外部的脚本。你写一个简单的 Python 脚本,读一个 CSV(每行一个主题及其参数),循环调用 API,把结果写进各自的文件。脚本挂了可以重跑某一行,日志清晰,成本也好算。
批量脚本的关键:幂等
写批量脚本时,最容易犯的错是不做幂等。脚本跑到第七篇时崩了,你重新跑一遍,前六篇又被覆盖,白白烧钱。正确做法是每篇输出完成后,在目标位置写一个完成标记(比如文件存在就跳过),重跑时自动续上。
节点配置清单:一次搭好,长期复用
最后给你一张完整的节点清单,照着配就行。这张表本身也是一份可以复制的资产。
【Dify 文章工作流 · 节点配置清单】
1. 开始节点
- 输入字段:topic / target_audience / core_keywords / word_count
/ tone_style / forbidden_words / reference_material
/ section_count / language_style
- 命名:全部英文蛇形,全部设默认值
2. 提示词节点 · 大纲生成
- 引用:start.topic, start.target_audience 等
- 开启结构化输出,schema 为 outline 数组
- temperature: 0.3(结构要稳)
3. 代码节点 · JSON 清洗与校验
- 输入:大纲节点的原始返回
- 输出:ok(布尔) / data(数组) / reason(字符串)
4. 条件分支 · 校验结果
- ok == true -> 进入迭代
- ok == false -> 重试节点(最多 2 次)
5. 迭代节点 · 分节扩写
- 输入:代码节点输出的 data 数组
- 并行数:2
- 子流程:
a) 变量赋值:previous_ending = 空字符串(初始化)
b) 提示词节点:分节扩写提示词
c) 代码节点:截取本轮结尾 150 字,回写 previous_ending
d) 代码节点:统计字数,标记是否达标
- 失败策略:输出占位符,不中断
6. 模板节点 · 全文合并
- 拼装:主标题 + 摘要 + 各节正文
- 空节检测:为空或过短时插入占位提示
7. 提示词节点 · 摘要生成
- 输入:合并后的全文
- 输出:80-100 字摘要,不放开头套话
8. 提示词节点 · 主标题生成
- 输入:主题 + 摘要
- 输出:三个候选标题
9. 结束节点 · 输出
- 输出:markdown(字符串)/ failed_sections(数组)
把这张清单变成团队规范
如果你们是多人协作,这张清单本身就应该是团队规范的一部分。新同事搭工作流时照抄这份配置,变量名怎么起、哪些节点开结构化输出、并行数开多少,都不用再讨论。这是「提示词是资产」这个论点的另一层含义——不只是提示词,整条工作流的配置都是资产。
常见问题
Q1:Dify 的工作流和直接在聊天窗口里写提示词,到底差在哪?
差的不是模型能力,而是有没有把变量和逻辑分开。在聊天窗口里,主题和规则混在一段话里,改一次主题就要重写整段。在 Dify 的工作流里,主题是开始节点的一个输入字段,规则是提示词节点里的固定文本,两者物理隔离。这正是「换主题不改 Prompt」能够成立的前提。而且 Dify 的节点能用结构化数据传参,这比聊天窗口里自然语言拼接要稳定得多。
Q2:我的主题差别很大,一条 Dify 工作流真的能通用吗?
能,但有边界。工作流通用的前提是「文章的结构逻辑」相似,比如都是「观点 + 论证 + 落地」。如果一篇是教程、另一篇是小说,那就不该用同一个大纲提示词。实际做法是:按文体拆几条 Dify 工作流,每条工作流内部主题可变、结构固定。大多数人需要的其实是两三条,而不是几十条。
Q3:分节扩写时,迭代节点跑得特别慢怎么办?
先看瓶颈在哪。如果是模型响应慢,可以适当提高并行数(但要留意接口限流);如果是每节的输入太长,可以把大纲压缩成「节序号 + 标题」再传,能省掉一半输入。另外,如果某一节本身就不长,可以考虑把它和下一节合并成一节,减少迭代次数。最后,把迭代节点放在 Dify 里跑本身就比在本地脚本里慢,这是平台调度的成本,接受它。
Q4:提示词里要不要写「请仔细思考」「你是一位专家」这类话?
「你是一位专家」在 Dify 的节点里价值不大,因为模型早就知道了,这句话更多是心理安慰。「请仔细思考」对新一代模型也没多大用。真正有用的是具体的结构约束:「每节给出三个要点」「不要复述标题」「禁用以下词」。在 Dify 工作流里,把情绪化的话换成可检验的规则,质量提升远比加一句「请认真」明显。
Q5:为什么大纲节点必须输出 JSON,普通文本不行吗?
因为下游的迭代节点需要一个数组来遍历。普通文本大纲是一整段字符串,迭代节点没法拆,只能再用一次模型调用去拆,既慢又不可控。强制 JSON 之后,大纲的每个字段都变成可寻址的变量,迭代节点直接取 title 和 points,中间没有任何自然语言解析。这是 Dify 工作流稳定性的地基。
Q6:这套 Dify 工作流适合写多长的文章?
1500 字以内,直接用一次性生成更简单,不值得拆成三段。3000 到 8000 字是分节扩写的最佳区间,结构清晰、每节可控。超过一万字,单靠这条链路会吃力,需要在上层再加一层「分部分」的结构,也就是先出大章节,再出小节,再逐小节扩写。但说实话,一万字以上的稿子,人工介入的价值已经开始超过自动化的价值了。
结语:把提示词当资产,把主题当参数
回到最开始那句话。大多数人觉得自己提示词写得不好,于是不停改措辞、加细节、找技巧。但真正的问题往往不在这里,而在于你把该变成参数的东西,硬编码进了规则里。Dify 这样的工作流平台,最大的价值不是「自动化」,而是逼你做一次清晰的分工:哪些是不变的资产,哪些是要变的参数。
一旦这个分工做对了,你会发现事情突然简单了——你的提示词不再是一个每周重写的消耗品,而是一个越用越顺手的工具。你换主题时只需要动开始节点的几个框,剩下的交给工作流。这就是「提示词是资产,主题是参数」的全部意思。
如果你的工作流里还有一句话是「跟主题有关」的,那它就是一根还没拔掉的硬编码。找出来,改成变量。这一步做完,你的 Dify 工作流才算真正属于你,而不是属于某一个主题。








暂无评论内容