This article is the ultimate guide to Dify’s “Template Conversion” node, analyzing the dynamic text generation techniques based on the Jinja2 template engine, covering six major application scenarios, including multi-source text integration, knowledge retrieval structuring, dynamic API construction, and personalized content generation, helping developers efficiently utilize the template conversion node.
Many friends want to see the way Dify workflows, after all, this is how to implement the business
But to achieve this, you need to master one of Dify’s artifacts – template conversion.
The following content is pure knowledge, suitable for forwarding and collecting to follow practice one by one. After reading it, it can handle 80% of the high-frequency scenarios of “dynamic text generation”.
Dify’s template conversion node, based on the powerful Jinja2 template engine, provides AI application developers with the core ability to perform flexible data transformation and dynamic text generation in their workflows.
It allows developers to easily implement text splicing, format conversion, data structure reorganization and other operations through concise template syntax, thereby optimizing data flow efficiency and enhancing the personalization and intelligence of application output.
This article will provide an in-depth analysis of Jinja2, the core engine of the template conversion node, and combine it with the features of the Dify platform to demonstrate its application skills in key scenarios such as multi-source text synthesis, knowledge retrieval structuring, dynamic API construction, and personalized content generation, helping you navigate this powerful tool.
Core engine: Mechanics and features of Jinja2
Jinja2 is a mature, feature-rich, and efficient Python template engine with the core goal of clearly separating the business logic of an application from the presentation logic (such as HTML, XML, configuration files, or any text format).
It realizes the need for dynamic content embedded in static templates through elegant syntax, template inheritance, macros, filters, and extensibility.
Jinja2’s syntax is intuitive and mainly includes:
- {{ … }} Used to output variable or expression results (default automatic HTML escape in case of XSS attacks)
- {% … %} for control statements (e.g. loops, conditional judgments, macro definitions)
- and {# … #} for comments.
Its powerful features include template inheritance that allows for the definition of base layouts, and subtemplates that override specific blocks; macros are similar to functions, encapsulating reusable logic; Rich built-in filters (such as trim, join, length, default, tojson) and custom filter capabilities greatly enhance the flexibility of data processing.
What does a product manager need to do?
In the process of a product from scratch, it is not easy to do a good job in the role of product manager, in addition to the well-known writing requirements, writing requirements, writing requirements, there are many things to do. The product manager is not what you think, but will only ask you for trouble, make a request:
View details >
Jinja2 supports complete control structures such as {% if %} conditional judgment and {% for %} loops (which contain loop variables to access loop state), as well as variable assignment {% set %}. These features allow templates to not only display data, but also perform lightweight logical judgment and data iterative processing.
In Dify, the template conversion node leverages these capabilities of Jinja2, allowing developers to efficiently process and orchestrate text data in their workflows without writing complex code nodes.
The above is just the tip of the iceberg of Jinja2, more needs to be learned from Chinese documentation:https://docs.jinkan.org/docs/jinja2/
In order to help you “learn by doing”, I have compiled 6 high-frequency application scenarios of template conversion, and understand them, so you can master them well.
Key application scenarios and techniques of template conversion nodes
Dify’s template conversion node demonstrates great flexibility and utility in building AI application workflows with the help of the Jinja2 engine. The following will explore its core application methods based on specific scenarios and code examples, focusing on data integration, structured output, and dynamic generation capabilities.
1. Multi-source text and data splicing integration
In AI applications, it is often necessary to combine text fragments and data items from different nodes or variables into a single, structured text output that can be used as prompts, user responses, or logging for subsequent LLMs. The template conversion node provides a convenient solution for this. For example, combine scattered article titles, openings, and body content into a single complete document.
For more complex scenarios, such as processing multiple content items output from an iterative node, loops can be implemented. Suppose the iteration node outputs an array called arg, and each element of section needs to be spliced and separated by a separator:
{% for section in arg %}
{{ section }}
———————————-
{% endfor %}
This method is often used to summarize the results of iterative processing into a complete report or long text.
For example, in my workflow for the official account, there is an “iteration” responsible for publishing each piece of content to the official account
The next node of the iteration does a “template transformation”, displaying each successfully sent record one by one
2. Structure and beautify knowledge retrieval results
Information retrieved from a knowledge base often contains multiple pieces of content and their metadata (e.g., similarity score, source, etc.). The Template Transformation node elegantly formats this raw data into easy-to-read reports, such as Markdown format, for easy presentation or further processing.
Instance:
Define the array results retrieved from the knowledge base for variable retrieved_chunks
The core is to design a good display template:
## Knowledge Retrieval Report
{% if retrieved_chunks and retrieved_chunks | length > 0 %}
{% for chunk in retrieved_chunks %}
###
{{ loop.index }}. {{ chunk.title }}
(Similarity:
{{ “%.2f” | format(chunk.score | default(0)) }}
)
{{ chunk.content | replace(‘n’, ‘nn’) }}
—
{% endfor %}{% else %}
No relevant information was retrieved.
{% endif %}
The template traverses retrieved_chunks lists, creates subheadings for each knowledge fragment with sequence number, title, similarity (formatted using the format filter and provides a default value), and handles line breaks in the content to make them properly segmented in Markdown.
You can see that it has structured the results of the knowledge base search:
This code is a comprehensive demonstration of Jinja2’s capabilities and is quite typical.
Let’s break it down line by line:
1) {% if retrieved_chunks and retrieved_chunks | length > 0 %}: This is a conditional judgment statement.
Syntax: {% if … %} … {% else %} … {% endif %} is Jinja2’s conditional control structure.
Explanation: Here’s a double check: retrieved_chunks Make sure the variable exists and is not null; retrieved_chunks | length > 0 ensures that this variable (usually a list or array) is not empty length is a filter that obtains the length of a variable, similar to Python’s len(). This determination ensures that subsequent loop rendering is performed only when the content is actually retrieved.
2) {% for chunk in retrieved_chunks %}: This is a circular statement.
Syntax: {% for item in sequence %} … {% endfor %} is used to iterate over a sequence (e.g. list, array).
Explanation: It iterates through every element in retrieved_chunks array and assigns the current element to the temporary variable chunk. A chunk is usually an object in Dify that contains fields such as content, title, score, etc.
3) {{ loop.index }}: This is a special variable in Jinja2 in the loop.
Syntax: The loop variable is automatically available inside the for loop.
Explanation: loop.index returns the number of iterations of the current loop (starting from 1). Other commonly used ones are loop.index0 (starting from 0), loop.first (whether it is the first iteration), and loop.last (whether it is the last iteration), which are very useful when working with lists, such as without separators after the last item.
4) {{ “%.2f” | format(chunk.score | default(0)) }}: This is a complex expression that nests the use of filters.
Syntax: | symbols are used to pass the value on the left to the filter on the right. It can be used continuously to form a “pipeline”.
| default(0): Default value filter. If the chunk.score variable does not exist or is null, use 0 in parentheses as the default value. This is key to ensuring code robustness and avoiding errors due to missing optional fields.
| format(…): Format the filter. It uses the string on the left (“%.2f”) as a formatting template to handle the values in parentheses. Here “%.2f” means “formatted as a floating-point number that holds two decimal places”.
5) {{ chunk.content | replace(‘n’, ‘nn’) }}: String replacement.
| replace(from, to): Replace the filter. Replace all from substrings in the input string with to substrings. Here, it replaces a single line break n in the content with two, which in Markdown syntax represents a true line break (segmentation).
3. Dynamically build API request bodies and data structures
When integrating with external services, it is often necessary to construct an API request body in a specific format (such as JSON) based on dynamic input parameters. The template conversion node can flexibly generate such data structures by using Jinja2’s conditional judgment and variable substitution capabilities.
{
“query”: “{{ user_query }}”,
“filters”: {
“status”: “{{ filter_type | default(‘any’) }}”,
{% if user_id %}
“customer_id”: “{{ user_id }}”,
{% endif %}
“created_after”: “{{ start_date_iso }}”
},
“pagination”: {
“limit”: {{ page_size | default(10) }},
“offset”: 0
}
}
The core trick of this code lies in embedding dynamic logic in a static JSON structure to generate a request body that meets API requirements.
1) {{ … | default(…) }}: In this example, the default filter is used multiple times to handle optional parameters.
“status”: “{{ filter_type | default(‘any’) }}”: If an upstream node passes in filter_type variable, use its value; If not, use the default value ‘any’.
“limit”: {{ page_size | default(10) }}: Similarly, if page_size is not provided, the default is 10. Note that the output here is a number, so the default value is also the number 10, not the string ’10’. Jinja2 handles data types correctly.
2) {% if user_id %}: This is the finest part of this template – dynamically add and remove JSON fields.
Syntax: {% if variable %} … {% endif %}
Explanation: This if statement block wraps the entire line of “customer_id”: “{{ user_id }}”. Only when user_id variable is present and not empty will this part of the text (including the key, value, and comma at the end) be rendered into the final output. If user_id does not exist, these three lines are completely ignored, enabling the ability to dynamically add/remove customer_id fields.
Extension Tips and Notes: This method is very efficient for handling optional fields in JSON. But you need to pay attention to the position of the comma. In this example, because there is also a fixed created_after field after the if block, the comma can be safely placed inside the if block. If the if block is the last possible field in the object, adding a comma directly may cause the JSON to be malformed. In this case, more complex logic (e.g. using loop.last) is usually required to handle it.
4. Achieve personalized user experience and dynamic content customization
The template transformation node can generate personalized text content based on user attributes or contextual information, thereby enhancing the interactive experience of AI applications. For example, generate dynamic greetings based on user name and current time.
Hello, {{ user.name }}!
{% if current_time.hour < 6 %} It’s late at night, so rest early.
{% elif current_time.hour < 12 %}Good morning!
{% elif current_time.hour < 14 %}Good afternoon!
{% elif current_time.hour < 18 %}Good afternoon!
{% else %}Good evening!
{% endif %}
Is there anything I can help you with today?
Here we assume that user.name and current_time.hour are the variables passed in upstream, and the current time period is judged by Jinja2’s conditional logic and the corresponding greeting is output. This method is also suitable for scenarios such as customized product recommendations and dynamic email content generation.
The core of this template is to use multi-level conditional judgment to implement text branching based on different conditions.
1) {{ user.name }}: Access object properties.
Syntax: object.property
Explanation: This indicates that user is an object (or dictionary) that we use by dots. to access its property named name. This is the standard method for extracting nested data from variables.
2){% if … elif … else … %}: complete chain of conditional judgments.
{% if current_time.hour < 6 %}: First check if the hour is less than 6. < is the standard comparison operator.
{% elif current_time.hour < 12 %}: If the previous if is not true, check this elif (short for “else if”) condition.
Subsequent elif and else and so on. Jinja2 checks sequentially, executes the corresponding code block once a condition is true, and then jumps out of the entire if/elif/else structure.
{% else %}: If all of the above if and eLif conditions are not met, execute the code in the else block.
Syntax: {% if condition1 %} … {% elif condition2 %} … {% else %} … {% endif %}
Interpretation:
5. HTML form rendering and interactive content generation
Dify’s template conversion node also supports rendering HTML content, which can be used to dynamically generate forms and provide users with a richer interactive interface.
<form data-format=”json”>
// Default to text
<labelfor=”username”>Username:</label>
<inputtype=”text”name=”username”/>
<labelfor=”password”>Password:</label>
<inputtype=”password”name=”password”/>
<labelfor=”content”>Content:</label>
<textarea name=”content”></textarea>
<labelfor=”date”>Date:</label>
<inputtype=”date”name=”date”/>
<labelfor=”time”>Time:</label>
<inputtype=”time”name=”time”/>
<labelfor=”datetime”>Datetime:</label>
<inputtype=”datetime”name=”datetime”/>
<labelfor=”select”>Select:</label>
<inputtype=”select”name=”select”data-options='[“hello”,”world”]’/>
<inputtype=”checkbox”name=”check”data-tip=”By checking this means you agreed”/>
<button data-size=”small”data-variant=”primary”>Login</button></form>
The effect is this:
And each component is still selected, which is very convenient:
。 This code itself does not use Jinja2’s dynamic syntax (such as {{}} or {% %}), but demonstrates a feature specific to the Dify platform: the template conversion node can directly output HTML in a specific format, and the Dify frontend will parse and render it into an interactive form.
The key point here is to understand the HTML tagsdata-*attribute, they are instructions for the Dify platform, not standard HTML behavior.
<form data-format=”json”>:
Explanation: This property tells Dify that when a user submits this form, the form data should be aggregated into a JSON object instead of the default form string. This is the basis for structured data interaction with the backend or LLM nodes.
<input type=”select” data-options='[“hello”,”world”]’/>:
Explanation: Dify recognizes type=”select” and looks for the data-options property. It parses the value of this property (a string in JSON array format) and uses it to populate the drop-down selection box options. This is a convenient way to dynamically define options without the need for a Jinja2 loop.
<input type=”checkbox” data-tip=”By checking this means you agreed”/>:
Explanation: The data-tip attribute is used by Dify to generate a tooltip for this checkbox on the interface, enhancing the user experience.
<button data-size=”small” data-variant=”primary”>:
Explanation: Properties such as data-size and data-variant are used to control the visual style of the buttons rendered by Dify, such as size and color theme (e.g. primary is usually blue dominant).
In other words, the template conversion node can not only handle the text logic, but also act as a dynamic UI generator to dynamically generate these HTML in conjunction with the Jinja2 syntax.
6. Dynamic data analysis report generation
# Sales report
{{ date.today() | date_format(“%Y-%m-%d”) }}
**Total Sales**:
¥{{ total_sales | round(2) | thousands_separator }}
## Regional Performance
{% for region in regions %}- {{ region.name }}:
– Completion Rate: {{ (region.actual/region.target*100) | round(1) }}%
– YoY Growth: {{ region.growth_rate | percent }}{% endfor %}
This template is an advanced fusion of all the previous knowledge points to generate a data-driven, dynamic report with function calls, arithmetic operations, and a variety of specialized filters.
1) {{ date.today() | date_format(“%Y-%m-%d”) }}: Call the method and format it.
date.today(): This indicates that date is an object, and it has a method that can be called today(). Jinja2 allows this simple parameterless method call to be performed in the template.
| date_format(…): This is a date formatting filter that assumes existence (or customization) and is very useful. “%Y-%m-%d” is a formatting instruction that represents a four-digit year, a two-digit month, and a two-digit date, and will eventually output a format like 2023-10-27.
2) {{ total_sales | round(2) | thousands_separator }}: filter chain call.
| round(2): round filter, used for rounding. Parameter 2 means to retain two decimal places.
| thousands_separator: This is a very useful (usually custom or framework-specific) filter for adding thousand separators to numbers, such as converting 12345.67 to 12,345.67, which greatly improves the readability of amounts.
3) {{ (region.actual/region.target*100) | round(1) }}%: Do the math in the template.
(region.actual/region.target*100): This is an extremely powerful feature of Jinja2. You can directly use {{ … }} Use parentheses to wrap expressions for mathematical operations such as addition, subtraction, multiplication and division. The percentage of the sales completion rate is calculated here.
| round(1): Rounds the calculation to one decimal place. Note that the percent is written outside }} because it is a static text suffix.
4) {{ region.growth_rate | percent }}: dedicated data formatting.
| percent: Similar to date_format, this is a dedicated filter that assumes the existence of a decimal number (e.g., 0.15) and converts it directly to a string with a percentage sign (15%), which is more elegant than manually multiplying 100 with a percent sign.
By masterfully using Jinja2’s features, Dify’s template conversion node goes far beyond simple text replacement. Its ability to handle complex logic, transform data formats, beautify outputs, and even create interactive content is an integral part of building powerful and flexible AI applications.