Jinja2 prompting — A guide on using jinja2 templates for prompt management in GenAI applications

Alex Gonzalez
5 min readMay 20, 2024

--

Jinja2 is a popular templating engine that generates dynamic outputs from static templates using placeholders, originally designed for dynamic HTML it is used in many applications that require dynamic text generation, such as AI prompting. Some features, like inheritance, looping and conditions, complement its use in Generative AI (GenAI) apps where prompts are built for LLMs to achieve specific outcomes. The content of these prompts typically varies based on user inputs, prior interactions, etc.

Why Use Jinja2 for Prompting?

Using Jinja2 templates for prompting offers several benefits:

  1. Since I started working on GenAI projects, I’ve found prompts embedded within the code, even in LangChain docs. This mixes LLM instructions with application logic, complicating code understanding. Using Jinja2 for prompts helps to maintain a clean and organized code.
  2. As GenAI projects scale, developers need to manage many unique, lengthy prompts. Storing these prompts in Jinja2 templates improves organization, facilitating prompt iteration.
  3. In prompt engineering, it’s common to reuse parts of prompts which is not optimal. Jinja2 allows to use a single styling template that can be imported into other prompts, avoiding duplication. Additionally, Jinja2’s built-in inheritance enables the use of base templates that other templates can inherit from.

How to Use Jinja2 for Prompt Management

Start by installing Jinja2 using pip or another package manager like Poetry.

pip install jinja2
# or
poetry add jinja2

Once installed, you can begin using Jinja2 by creating template files. Here is a basic example of how to invoke Jinja2 from Python, utilizing a .txt file as a template. For the sake of this example, let’s imagine we are instructing an LLM-based chatbot to address the user by name and adjust the complexity of its responses according to the user’s technical level:

  1. Create a template file: Save the content below in a file named prompt_template.txt
- When interacting with the user call him by his name.
- The name of the user is {{ user_name }}.

{% if is_user_technical == "True" %}
- The user is technical, you can answer the user using technical terms.
{% else %}
- The user is non technical, please answer him using non technical terms.
{% endif %}

2. Render the template in Python: Run the following code in the directory containing the template. If the template is in a different directory, adjust FileSystemLoader(directory_path).

env = Environment(loader=FileSystemLoader(""))
template = env.get_template("prompt_template.txt")
output = template.render({"user_name": "Alex", "is_user_technical": "True"})
print(output)

When the template is rendered, the resulting prompt will reflect the user’s name and adapt its language based on the user’s technical profile:

- When interacting with the user call him by his name.
- The name of the user is Alex.

- The user is technical, you can answer the user using technical terms.

You can find all the code and examples used in the post in this repository:

All In One Function: Integrating Jinja2 with LangChain

A downside of using Jinja2 directly for rendering templates as prompt strings is that LangChain’s built-in PromptTemplate objects are not used. However, by using the following function, we can still use Jinja2 templates while outputting the resulting prompt as a LangChain PromptTemplate. This integration takes advantage of the LCEL Runnable interface, which supports various operations like invoke and stream (LangChain docs) to implement complex chains that mix prompts, parsers, model calls, etc.

from langchain_core.prompts import PromptTemplate

def get_prompt_template_from_jinja2(
prompt_path: str,
prompt_name: str,
input_variables: list[str]=[],
partial_variables: dict[str, str]={},
jinja2_placeholders: dict[str, str]={},
) -> PromptTemplate:
"""
Loads a .txt file as a Jinja2 template and converts it into a LangChain PromptTemplate.

Parameters:
prompt_path: Path to the prompt.
prompt_name: Filename of the prompt, including its extension.
input_variables: A list of variable names expected by the LangChain PromptTemplate.
partial_variables: A dictionary of partial variables carried by the LangChain PromptTemplate.
jinja2_placeholders: A dictionary of placeholders to be replaced in the Jinja2 template.
"""

env = Environment(loader=FileSystemLoader(prompt_path))
template = env.get_template(prompt_name)
prompt_string = template.render(jinja2_placeholders)

prompt_template = PromptTemplate(
template=prompt_string,
input_variables=input_variables,
partial_variables=partial_variables,
)
return prompt_template
get_prompt_template_from_jinja2(
prompt_path="",
prompt_name="prompt_template.txt",
jinja2_placeholders={"user_name": "Alex", "is_user_technical": "True"}
)

After executing the function, we obtain a PromptTemplate object that can be used as a runnable within LCEL chains:

PromptTemplate(input_variables=[], template='- When interacting with the user call him by his name.\n- The name of the user is Alex.\n\n \n- The user is technical, you can answer the user using technical terms.\n')

Additional Jinja2 Template Applications for Prompting

Loops

Jinja2 templates can iterate through data structures such as lists. For example, if you have a list of programming languages that the user is familiar with, these can be dynamically included in the prompt by iterating through the list. This method eliminates the need to manually format the list into a string for the prompt:

- When answering coding questions from the user, you have to provide him the answer in the following languages:

{% for programming_language in programming_languages_list %}
Programming language number {{ loop.index }}: {{ programming_language }}
{% endfor %}

Execute the following code to render this Jinja2 template:

programming_languages_list = ["python", "r", "javascript", "c#"]

template = env.get_template("loop_prompt_template.txt")
rendered_output = template.render({"programming_languages_list": programming_languages_list})
print(rendered_output)

After rendering, the output is:

- When answering coding questions from the user, you have to provide him the answer in the following languages:

Programming language number 1: python

Programming language number 2: r

Programming language number 3: javascript

Programming language number 4: c#

Template inheritance

Jinja2 allows for the creation of specific templates from a base template, allowing to reuse text. This feature is particularly useful for using the same instructions across multiple prompts.

In this example, we want to have a solid style for the responses of our application, hence, we will be using the same styling instructions across all the prompts, therefore, we include this instructions in a base_template.txt and then build the rest of the prompts from this base template.

base_template.txt

- You are an AI assistant that helps to solve problems in various fields.
- Please, answer using formal language.
- Use short and direct sentences.

{% block user_type %}
{% endblock %}

specific_template.txt

{% extends "base_template.txt" %}

{% block user_type %}
{% if is_user_technical == "True" %}
- The user is technical, you can answer the user using technical terms.
{% else %}
- The user is non technical, please answer him using non technical terms.
{% endif %}
{% endblock %}

Rendering specific_template.txt :

template = env.get_template("specific_template.txt")
output = template.render({"is_user_technical": "True"})
print(output)

In the result below it is possible to observe how just by rendering the specific_template.txt the instructions from the inherited base_template.txtare also included.

- You are an AI assistant that helps to solve problems in various fields.
- Please, answer using formal language.
- Use short and direct sentences.
- The user is technical, you can answer the user using technical terms.

Conclusion

All in all, Jinja2 templates aid the management of prompts in GenAI projects, ensuring code remains organized. With features like loops, template inheritance and conditions, they facilitate efficient development across diverse scenarios. Here you can find a handy Jinja cheatsheet.

Thanks for reading, see you soon!

Feel free to follow me, to contact me and any feedback is welcome in the comments.

About me:

Alejandro González, Data Scientist / AI Engineer,

MSc in Data Science at Politechnique University of Madrid and Eötvös Loránd University of Budapest, with background in Telecommunications Engineering. Experience in different Data Science and Machine Learning domains like, Data Visualization, LLMs, Unsupervised Learning, etc.

You can find me on:
👨🏻‍💼Linekdin
👨🏻‍💻GitHub

--

--

Alex Gonzalez
Alex Gonzalez

Written by Alex Gonzalez

Data Science and AI. I love travelling, reading and learning 💻📗🚀

No responses yet