name: dify-workflow description: Generate Dify workflow DSL files from natural language descriptions. Produces importable YAML/JSON workflow definitions with correct node schemas, edges, and layout.
Dify Workflow DSL Generator
Overview
This skill generates Dify workflow DSL files that can be directly imported into a Dify instance. Given a natural language description of a desired workflow, it produces a complete YAML (default) or JSON file containing all nodes, edges, layout positions, and configuration.
The skill triggers when the user asks to:
- Create, generate, or build a Dify workflow
- Convert a process description into a Dify DSL
- Scaffold a chatflow or workflow application
Output format is YAML by default (.dify.yml), with JSON (.dify.json) available on request.
Smart Interaction Logic
Before generating, assess whether the user's description is sufficient:
Proceed directly if the description includes:
- Clear input/output expectations
- Processing logic (what the workflow should do)
- Enough detail to select appropriate nodes
Ask clarifying questions (max 3 rounds) if unclear:
- "What inputs will the workflow receive, and what outputs should it produce?"
- "What processing steps are needed? (e.g., LLM call, knowledge retrieval, API call, conditional logic)"
- "Any specific models, tools, or knowledge bases to use?"
Once requirements are clear, proceed to generation.
Node Router Table
| Node | Type Key | Purpose | Key Params | Schema Path |
|---|---|---|---|---|
| Start | start |
Entry point; defines input variables | variables |
references/nodes/start.md |
| End | end |
Terminal node for Workflow mode; declares outputs | outputs |
references/nodes/end.md |
| Answer | answer |
Streams response in Chatflow mode | answer, variables |
references/nodes/answer.md |
| LLM | llm |
Invokes a large language model | model, prompt_template, context, vision |
references/nodes/llm.md |
| Knowledge Retrieval | knowledge-retrieval |
Searches knowledge bases for relevant chunks | query_variable_selector, dataset_ids, retrieval_mode |
references/nodes/knowledge-retrieval.md |
| Code | code |
Executes Python3/JavaScript/JSON code | code_language, code, variables, outputs |
references/nodes/code.md |
| HTTP Request | http-request |
Makes HTTP API calls | method, url, headers, body, authorization |
references/nodes/http-request.md |
| If/Else | if-else |
Conditional branching (IF/ELIF/ELSE) | cases |
references/nodes/if-else.md |
| Variable Aggregator | variable-aggregator |
Merges variables from multiple branches | output_type, variables |
references/nodes/variable-aggregator.md |
| Iteration | iteration |
Loops over array, runs sub-graph per element | iterator_selector, iterator_input_type, output_selector, start_node_id |
references/nodes/iteration.md |
| Document Extractor | document-extractor |
Extracts text from uploaded files (PDF/DOCX/etc.) | variable_selector, is_array_file |
references/nodes/document-extractor.md |
| Template Transform | template-transform |
Renders Jinja2 templates with variables | template, variables |
references/nodes/template-transform.md |
| Question Classifier | question-classifier |
Routes by classifying input into categories via LLM | query_variable_selector, model, classes |
references/nodes/question-classifier.md |
| Parameter Extractor | parameter-extractor |
Extracts structured params from text via LLM | query, model, parameters, reasoning_mode |
references/nodes/parameter-extractor.md |
| Tool | tool |
Invokes external tools (built-in, API, MCP) | provider_id, provider_type, tool_name, tool_parameters |
references/nodes/tool.md |
Generation Flow
Follow these steps to produce a valid DSL file:
Parse requirement -- Identify the app mode (
workfloworadvanced-chat), needed nodes, and data flow.- Use
workflowmode with Start/End nodes for batch processing tasks. - Use
advanced-chatmode with Start/Answer nodes for conversational chatbots.
- Use
Select nodes from the router table above. Load the corresponding schema file for each selected node to get the full field specification.
Check template match -- If the requirement closely matches a known pattern, start from a template (see Template Matching below). Adapt fields as needed.
Assemble from schemas -- If no template matches, build nodes individually. For each node:
- Generate a unique ID (13-digit timestamp string, e.g.,
"1711536487001"). - Fill required fields per the node schema.
- Use
{{#nodeId.variableName#}}syntax for variable references. - Use
{{#sys.query#}}for system query variable in chatflow mode.
- Generate a unique ID (13-digit timestamp string, e.g.,
Generate edges -- Connect nodes following the rules in
references/edge-and-layout.md:- Edge ID format:
{sourceId}-{sourceHandle}-{targetId}-{targetHandle} - Standard
sourceHandle:"source"for most nodes - If/Else branches:
"true"(first case), case_id (elif),"false"(else) - Question Classifier branches: topic
idas sourceHandle targetHandleis always"target"- All edges use
type: "custom"andzIndex: 0(or1002inside iterations)
- Edge ID format:
Calculate layout positions -- Place nodes on a left-to-right grid:
- Start node at
{x: 80, y: 282} - Horizontal spacing: 300px per step (
NODE_WIDTH 240 + X_OFFSET 60) - Vertical spacing for parallel branches: 200px apart
- Node width: 244px, height: varies by node (54-150px typical)
- Start node at
Output file -- Render as YAML (default) or JSON. Validate structure completeness.
DSL Structure Quick Reference
version: "0.6.0"
kind: app
app:
name: "Workflow Name"
mode: "advanced-chat" # or "workflow"
description: "..."
icon: "\U0001F916"
icon_background: "#FFEAD5"
icon_type: emoji
use_icon_as_answer_icon: false
dependencies: []
workflow:
environment_variables: []
conversation_variables: []
features:
file_upload:
enabled: false
opening_statement: "" # chatflow only
retriever_resource:
enabled: false
sensitive_word_avoidance:
enabled: false
speech_to_text:
enabled: false
suggested_questions: [] # chatflow only
suggested_questions_after_answer:
enabled: false
text_to_speech:
enabled: false
graph:
nodes: [] # Node objects
edges: [] # Edge objects
viewport:
x: 0
y: 0
zoom: 0.7
For the complete field-level specification, see references/dsl-format.md.
Output Rules
- Output location: Final workflow files (
.dify.yml/.dify.json) go to current working directory. Any intermediate/temp files go to/tmp/dify-workflow/. - Filename:
<kebab-case-name>.dify.yml(or.dify.jsonfor JSON output) - Required sections:
version,kind,app,workflow(withgraph,features) - Node IDs: 13-digit timestamp strings (e.g.,
"1711536487001"). Increment by a few thousand between nodes to simulate realistic IDs. - Coordinates: Start at
{x: 80, y: 282}. Each subsequent column at+300on x-axis. Parallel branches offset on y-axis by+200. - Variable references: Use
{{#nodeId.variableName#}}syntax. System variables usesysprefix:{{#sys.query#}},{{#sys.user_id#}}. - Model provider format:
"langgenius/<provider>/<provider>"(e.g.,"langgenius/openai/openai"). Use a real, currently-shipping model name formodel.name(e.g.deepseek-chat,gpt-4o-mini); fictional names likedeepseek-v4-prowill fail provider validation. - All string node IDs must be quoted in YAML to prevent type coercion.
Common Schema Pitfalls (must avoid)
These are the schema mistakes most likely to make a generated DSL fail import in Dify 0.6.0:
- Variable shape differs per node type. The shape of a single entry inside a node's variable list is not the same across nodes:
code,llm,template-transform,parameter-extractoruse objects:variables: - variable: my_arg value_selector: ["upstream_id", "field"] value_type: stringvariable-aggregatoruses bare nested lists (novalue_selector:wrapper, novariable:name):variables: - - "branch1_id" - "output" - - "branch2_id" - "output"document-extractoruses a singularvariable_selector(a flat array of strings), not a list:variable_selector: ["upstream_id", "field"]
memorybelongs only to chatflow LLM nodes. Inworkflow-mode apps,sys.querydoes not exist; LLM nodes must omit thememoryblock. Same applies for LLM nodes placed inside an iteration in a workflow app.- Iteration containers need dimensions in both places. Set
widthandheightboth insidedataand at the outer node level. Iteration-start child uses outertype: custom-iteration-startwithdata.type: iteration-start. - Iteration child nodes must declare
parentId: <iteration_id>,data.isInIteration: true,data.iteration_id: <iteration_id>, andzIndex: 1002. Theirpositionis relative to the iteration container (e.g. start near{x: 24, y: 68}). iterator_input_typemust match the real element type. For files:"array[file]". For numbers:"array[number]". Mismatch breaks runtime variable resolution.- Plugin dependencies. If you reference a model provider (e.g.
langgenius/deepseek/deepseek) or a tool provider not bundled with Dify, declare it in top-leveldependenciesso import flags missing plugins clearly. Emptydependencies: []is fine when the deployment is known to have those plugins installed. - End node
outputsis a list of{variable, value_selector, value_type}items; Code nodeoutputsis a dict keyed by variable name with{type, children}values. Do not mix the two shapes. - Edge
datablock should includesourceType,targetType,isInIteration,isInLoop. Edges inside iteration also neediteration_idandzIndex: 1002.
Template Matching
Use a template when the user's request closely matches one of these patterns. Load the template, then customize fields (model, prompts, variables) to fit the specific requirement.
| Template | Path | Matches When |
|---|---|---|
| Chatbot | references/templates/chatbot.yml |
Simple conversational bot: Start -> LLM -> Answer |
| RAG | references/templates/rag.yml |
Knowledge-base Q&A: Start -> Knowledge Retrieval -> LLM -> Answer |
| Agent | references/templates/agent.yml |
Tool-using agent with question classification or parameter extraction |
| Translation | references/templates/translation.yml |
Text transformation/translation: Start -> LLM (with specific system prompt) -> Answer/End |
If the requirement partially matches, use the closest template as a starting point and add/remove nodes as needed.
Few-Shot Example
A minimal chatflow (Start -> LLM -> Answer):
version: "0.6.0"
kind: app
app:
name: "Simple Chatbot"
mode: advanced-chat
icon: "\U0001F916"
icon_background: "#FFEAD5"
icon_type: emoji
description: "A minimal chatbot using GPT-4o-mini."
use_icon_as_answer_icon: false
dependencies: []
workflow:
environment_variables: []
conversation_variables: []
features:
file_upload:
enabled: false
opening_statement: "Hello! How can I help you today?"
retriever_resource:
enabled: false
sensitive_word_avoidance:
enabled: false
speech_to_text:
enabled: false
suggested_questions:
- "What can you help me with?"
suggested_questions_after_answer:
enabled: false
text_to_speech:
enabled: false
graph:
edges:
- id: "1711536487001-source-1711536522001-target"
source: "1711536487001"
sourceHandle: source
target: "1711536522001"
targetHandle: target
type: custom
zIndex: 0
data:
sourceType: start
targetType: llm
- id: "1711536522001-source-1711536558001-target"
source: "1711536522001"
sourceHandle: source
target: "1711536558001"
targetHandle: target
type: custom
zIndex: 0
data:
sourceType: llm
targetType: answer
nodes:
- id: "1711536487001"
type: custom
position: { x: 80, y: 282 }
data:
type: start
title: Start
desc: ""
variables: []
- id: "1711536522001"
type: custom
position: { x: 380, y: 282 }
data:
type: llm
title: LLM
desc: ""
model:
provider: "langgenius/openai/openai"
name: "gpt-4o-mini"
mode: "chat"
completion_params:
temperature: 0.7
prompt_template:
- role: "system"
text: "You are a helpful assistant."
variables: []
context:
enabled: false
variable_selector: []
vision:
enabled: false
memory:
query_prompt_template: "{{#sys.query#}}"
window:
enabled: false
size: 10
- id: "1711536558001"
type: custom
position: { x: 680, y: 282 }
data:
type: answer
title: Answer
desc: ""
answer: "{{#1711536522001.text#}}"
variables: []
viewport:
x: 0
y: 0
zoom: 0.7
For a complete version with all optional fields, see examples/simple-chatbot.yml. For a RAG workflow example, see examples/rag-with-rerank.yml.
Format Selection
- YAML (default): Output as
.dify.yml. Use standard YAML formatting with 2-space indentation. Quote all node ID strings. This is the preferred format for readability and Dify import. - JSON: Output as
.dify.jsonwhen the user explicitly requests JSON. Use the same structure with standard JSON formatting. Useful for programmatic consumption or API-based import.
Both formats are fully supported by Dify's import functionality.