很多人第一次接触 Dify 时,会把 Prompt 理解成一个文本框:写几句话,保存,然后交给模型。
从 Dify 1.14.2 的源码看,Prompt 更像一条流水线。它有两种来源,经过不同的保存与调用路径,最后在模板解析和 Prompt Transform 层汇合,才变成模型真正收到的消息。
这条链路可以回答几个常见问题:
- Prompt IDE 里的内容保存在哪里?
{``{#context#}}、{``{#query#}}这些变量是谁注入的?- Simple 和 Advanced 到底差在哪里?
- 对话标题、代码生成、知识库摘要使用的 Prompt,是否和应用 Prompt 是同一套配置?
答案是:它们共享解析能力,但不共享生命周期。

一、Dify 里有两个 Prompt 世界
Dify 的 Prompt 管理可以分成两条主线。
| 类型 | 主要使用者 | 来源与存储 | 作用 |
|---|---|---|---|
| 用户自定义 Prompt | 应用开发者 | Prompt IDE;应用配置保存到数据库,工作流节点保存到 workflow.graph |
描述业务角色、任务规则、上下文处理方式 |
| 系统内置 Prompt | Dify 平台自身 | api/core/llm_generator/prompts.py 中的代码常量 |
驱动对话命名、代码生成、规则配置、摘要等平台功能 |
用户自定义 Prompt 服务于某个应用。开发者在 Prompt IDE 中写下角色设定、任务说明和变量,最终用户发起请求时,系统再把这些内容渲染成模型消息。
系统内置 Prompt 服务于 Dify 自己。比如,Dify 要为一轮对话生成标题,要根据任务描述生成 Prompt 模板,或要为知识库段落生成摘要,都需要调用内置 Prompt。应用用户通常看不到这些文本。
两条路径最后都会用到 PromptTemplateParser,但入口不同:用户 Prompt 由 SimplePromptTransform 或 AdvancedPromptTransform 处理;内置 Prompt 由 LLMGenerator 的各个方法调用。
二、用户 Prompt 的完整生命周期
1. 在 Prompt IDE 中编辑
用户保存应用模型配置时,前端会向以下接口提交数据:
text
POST /console/api/apps/{app_id}/model-config
Simple 模式的核心字段是 pre_prompt。Advanced 模式则提交完整的消息列表,例如:
json
{
"prompt_type": "advanced",
"chat_prompt_config": {
"prompt": [
{"role": "system", "text": "你是一个专业助手。"},
{"role": "user", "text": "{{#query#}}"}
]
}
}
工作流中的 LLM 节点走另一条路。它不使用 AppModelConfig,而是把 Prompt 配置放进工作流图的 JSON 数据中,并随工作流版本管理。
2. API 层验证并补齐默认值
配置进入后端后,PromptTemplateConfigManager.validate_and_set_defaults() 会先检查结构:
prompt_type只能是simple或advanced;- Simple 模式的
pre_prompt必须是字符串; - Advanced Chat 模式的消息条数不超过 10 条;
- Completion 模式缺少角色前缀时,会补上
Human和Assistant。
这一步把前端传来的 JSON 变成后续代码能够稳定处理的配置。它不是模型调用前的 Prompt 渲染,而是配置保存前的边界检查。
3. 保存为应用配置快照
应用配置保存到 PostgreSQL 的 app_model_configs 表。Simple 和 Advanced 的内容分别落在不同字段中:
prompt_type:模式;pre_prompt:Simple 模式的核心 Prompt;chat_prompt_config:Advanced Chat 配置;completion_prompt_config:Advanced Completion 配置。
发布新版本时,系统会创建新的 AppModelConfig 记录,再让应用通过 app_model_config_id 指向当前版本。这样,Prompt 不是一份不断覆盖的文本,而是应用版本的一部分,历史配置也有机会回溯。
4. 运行时转换为 Prompt 实体
应用运行时,配置会经过 PromptTemplateConfigManager.convert(),转换成内存中的 PromptTemplateEntity。它包含模式、Simple 模板或 Advanced 模板等信息。
每次请求都会从配置构建这类实体。源码注释的设计意图是让请求拥有独立的运行时对象,而不是把带有请求变量的结果长期缓存下来。
5. 注入变量并渲染
系统会把两类变量交给模板解析器:
- 开发者在
user_input_form中定义的业务变量,例如{``{language}}、{``{content}}; - Dify 在运行时注入的特殊变量,例如知识库上下文、当前问题和对话历史。
6. 生成消息并调用模型
经过 Transform 后,Prompt 不再只是模板字符串,而会变成 PromptMessage 列表,随后交给 ModelInstance.invoke_llm()。
简化后的链路是:
text
Prompt IDE / 工作流节点
↓
配置验证与版本保存
↓
PromptTemplateEntity
↓
SimplePromptTransform 或 AdvancedPromptTransform
↓
PromptTemplateParser 渲染变量
↓
PromptMessage 列表
↓
ModelInstance.invoke_llm()

三、模板引擎只负责替换,不负责写逻辑
PromptTemplateParser 是两类 Prompt 共用的底层能力。它做两件事:提取变量名,替换变量值。
普通变量与特殊变量
普通变量使用双花括号:
text
请把以下{{language}}内容翻译成英文:{{content}}
传入:
json
{
"language": "中文",
"content": "你好,世界"
}
得到:
text
请把以下中文内容翻译成英文:你好,世界
Dify 还定义了系统特殊变量:
| 变量 | 运行时内容 |
|---|---|
{``{#context#}} |
知识库检索结果 |
{``{#query#}} |
用户当前输入 |
{``{#histories#}} |
Completion 模式使用的对话历史文本 |
普通变量要求以字母或下划线开头,只能包含字母、数字和下划线,长度不超过 16 个字符。特殊变量使用带 # 的语法,由对应的渲染流程负责注入。
它不是 Jinja2
虽然也使用 {``{}},但 Dify 的 Prompt 模板不是 Jinja2。它没有条件语句、循环和过滤器:
text
没有 {% if %}
没有 {% for %}
没有 {{ value | filter }}
判断上下文是否存在、历史消息放在哪里、查询没有占位符时如何追加,这些逻辑都写在 Python 的 Transform 和 Runner 中。模板负责表达位置,代码负责表达行为。
未匹配变量的处理
format() 默认会把没有匹配到的 {``{var}} 处理成 {var},避免用户输入中的模板语法触发二次渲染。例如,用户输入本身包含 {``{secret_var}} 时,系统不会把它当成下一轮模板继续执行。
内置 Prompt 的多步调用有时会把 remove_template_variables 设为 False,让未替换变量保留到后续步骤。这解释了一个容易混淆的现象:用户 Prompt 偏向一次渲染,平台内置 Prompt 则可能刻意保留中间模板。

四、Simple 与 Advanced:两种不同的控制权
Simple:只写核心指令,系统负责搭框架
Simple 模式中,开发者主要填写 pre_prompt。Dify 会根据模型类型和应用类型读取系统框架模板,再把多个片段拼起来。
常见的组装顺序是:
text
知识库上下文(有检索结果时)
+
用户编写的 pre_prompt
+
对话历史(有历史时)
+
当前问题
对于 Chat 模型,系统会把规则和上下文组织成 system 消息,把当前问题作为 user 消息。对于 Completion 模型,系统会把内容拼成一段文本,用 Human 和 Assistant 前缀区分角色,并使用停止词防止模型越过当前轮次继续生成。
Simple 模式的优点是上手快。开发者不必处理每一种上下文为空、历史为空或查询追加的位置。代价是控制粒度有限:RAG 片段、历史和查询的位置由框架模板决定。
Advanced:自己定义消息结构
Advanced 模式允许开发者按消息列表编排 system、user、assistant 内容。每条消息的文本都可以包含变量。
运行时,一条消息大致会经过以下处理:
- 渲染普通业务变量;
- 替换
{``{#context#}}; - 处理用户消息中的文件或图片内容;
- 根据配置插入历史消息;
- 把当前 query 注入最后一个 user 消息,或在缺少位置时追加。
Advanced 模式适合需要 few-shot 示例、多轮角色编排、指定上下文位置,或需要让历史信息出现在特定消息之间的场景。它把更多责任交给开发者:占位符放错、角色顺序不合理、上下文重复注入,都会直接影响模型输入。
两种模式怎么选
| 维度 | Simple | Advanced |
|---|---|---|
| 控制粒度 | 只写核心指令 | 控制完整消息列表 |
| 上下文位置 | 系统框架决定 | 开发者指定 |
| 对话历史 | 系统按框架处理 | 可控制插入位置 |
| Few-shot 示例 | 不适合复杂示例 | 适合预置多轮示例 |
| 上手成本 | 低 | 较高 |
| 适用场景 | 通用问答、快速搭建 | 复杂角色、精细编排、实验性 Prompt |
一个实用判断是:先用 Simple 验证业务效果;当问题变成"上下文必须放在这里""历史需要插入某个角色之后""必须保留几轮示例"时,再切换到 Advanced。

五、系统内置 Prompt:Dify 的平台能力层
用户写的 Prompt 决定应用如何回答问题;系统内置 Prompt 则帮助 Dify 完成平台功能。prompts.py 中定义了 16 个内置 Prompt 常量,由 LLMGenerator 负责调用。
它们可以按用途分为几组:
| 类别 | 代表功能 |
|---|---|
| 对话管理 | 自动生成对话标题、生成回答后的建议问题 |
| 代码生成 | 生成 Python 或 JavaScript Code 节点代码 |
| 规则配置生成 | 根据任务描述生成 Prompt、提取变量、生成欢迎语 |
| 结构化输出 | 生成 JSON Schema,指导模型按 Schema 输出 |
| 知识库索引 | 从文档生成问答对、为段落生成摘要 |
| Prompt 优化 | 根据上次运行结果修改 Prompt 或 Code,生成修复指令 |
这些 Prompt 的一个共同特点是:它们不一定由用户在 Prompt IDE 中编辑。部分默认值允许业务配置覆盖,例如建议问题指令和知识库摘要 Prompt;其余内容仍然由平台代码固定控制。
最复杂的例子:规则配置生成
当用户根据任务描述自动生成应用 Prompt 时,Dify 不只发起一次模型调用,而是串起三步:
text
任务描述
↓
生成带变量的 Prompt 模板
↓
从模板中提取变量列表
↓
生成聊天机器人的欢迎语
↓
prompt + variables + opening_statement
如果是工作流的"无变量"模式,系统只生成 Prompt 模板,跳过变量提取和欢迎语生成。这个设计说明,内置 Prompt 不只是一些散落的提示词,它们也参与平台配置对象的生成。
六、把一次模型请求拆开看
假设开发者在 Simple 模式中写了:
text
你是一名专业的产品顾问。请根据知识库内容回答用户问题。
用户输入是:
text
如何为新产品设计试用流程?
系统又检索到了一段知识库内容,并且已有历史对话。运行时不会把这几块内容无序地拼在一起,而是按当前模型对应的框架模板组装:
text
context_prompt
知识库检索结果
pre_prompt
你是一名专业的产品顾问......
histories_prompt
历史对话
query_prompt
用户当前问题
Chat 模型最终收到的可能是一组带角色的消息;Completion 模型则更接近一段带前缀的完整文本。两者都来源于同一份应用配置,但最终形态不同。
换成 Advanced 模式后,开发者可以把同样的信息安排成:
text
system:你是一名专业的产品顾问。
user:以下是知识库内容......
assistant:我会结合资料回答。
user:{{#query#}}
这里的关键变化不是"Prompt 写得更长",而是消息结构的所有权发生了变化:Simple 把结构交给系统模板,Advanced 把结构交给应用开发者。

七、从源码结构得到的几个实践建议
1. 调试时先判断问题属于哪条链路
应用回答不符合预期时,可以按以下顺序定位:
- 保存失败:检查 Prompt 配置结构、模式和消息条数;
- 变量没有替换:检查变量名、请求输入和特殊变量注入位置;
- 上下文位置不对:检查 Simple 框架模板或 Advanced 消息列表;
- 历史消息异常:检查模型类型和历史注入逻辑;
- 平台功能生成结果异常:检查
LLMGenerator对应的内置 Prompt,而不是只看应用的pre_prompt。
2. 把变量名当成接口契约
变量名连接了前端输入表单、后端解析器和最终 Prompt。建议保持命名稳定,避免用一套变量名承载多种含义。{``{language}} 是语言,{``{content}} 是正文,职责越清楚,调试成本越低。
3. 不要把双花括号当成模板编程语言
Dify 的模板层不负责流程判断。需要条件分支、数据清洗或复杂拼装时,应把逻辑放到工作流节点、代码节点或上游数据处理中,再把结果交给 Prompt。
4. 复杂结构要谨慎切换 Advanced
Advanced 给了更大的控制权,也带来了更多输入错误的可能。切换前先明确三件事:上下文放在哪条消息、历史放在哪个位置、当前 query 没有占位符时如何进入 Prompt。
5. 把 Prompt 当成版本资产
应用发布会生成新的配置记录,Prompt 应与模型、变量表、知识库策略一起做版本管理。只保存一份"最新文本",很难解释一次发布后为什么结果发生变化。
结语
Dify 的 Prompt 管理不是一个文本框,而是一条从配置到运行时消息的转换链:
text
编辑配置 → 验证保存 → 版本快照 → 变量注入 → 消息组装 → 模型调用
用户自定义 Prompt 决定应用的业务行为,系统内置 Prompt 支撑 Dify 的平台功能;模板解析器负责替换变量,Prompt Transform 负责决定消息如何成形。
理解这几层之后,Simple 与 Advanced 不再只是两个 UI 选项,{``{#context#}} 也不再像一个神秘占位符。你看到的每一句 Prompt,背后都有明确的来源、生命周期和渲染位置。
源码依据
本文根据 Dify 1.14.2 目录 07-others/prompt-management 下的 5 篇源码分析稿整理:
00-overview.md01-builtin-prompts.md02-user-prompt-lifecycle.md03-template-engine.md04-prompt-transform.md
关键源码入口包括:api/core/llm_generator/prompts.py、api/core/llm_generator/llm_generator.py、api/core/prompt/utils/prompt_template_parser.py、api/core/prompt/simple_prompt_transform.py 和 api/core/prompt/advanced_prompt_transform.py。
想继续深入 Dify 1.14.2?
私信获取详情。