Skip to content

Prompt templating

Prompts provides a powerful domain-specific language to write and manage prompts, via what we call prompt functions. Prompt functions are Python functions that contain a template for the prompt in their docstring, and their arguments correspond to the variables used in the prompt. When called, a prompt function returns the template rendered with the values of the arguments.

The aim of prompt functions is to solve several recurrent problems with prompting:

  1. Building complex prompts quickly leads to messy code. This problem has already been solved in the web development community by using templating, so why not use it here?
  2. Composing prompts is difficult. Why not just compose functions?
  3. Separating prompts from code. Encapsulation in functions allows a clean separation between prompts and code. Moreover, like any function, prompt functions can be imported from other modules.

Prompts uses the Jinja templating engine to render prompts, which allows to easily compose complex prompts.

Prompt rendering

Prompt functions are opinionated when it comes to prompt rendering. These opinions are meant to avoid common prompting errors, but can have unintended consequences if you are doing something unusual. We advise to always print the prompt before using it. You can also read the reference section if you want to know more.

Performance

Prompt templates introduce some overhead compared to standard Python functions, although the rendering time is still very reasonable. In the unlikely scenario where rendering templates are a bottleneck you can replace them with functions that use standard string manipulation.

Your first prompt

The following snippet showcases a very simple prompt. The variables between curly brackets {{ }} are placeholders for the values of the arguments you will pass to the prompt function.

import prompts

@prompts.template
def greetings(name, question):
    return """Hello, {{ name }}!
    {{ question }}
    """

prompt = greetings("user", "How are you?")
print(prompt)
Hello, user!
How are you?

If a variable is missing in the function's arguments, Jinja2 will throw an UndefinedError exception:

import prompts

@prompts.template
def greetings(name):
    return """Hello, {{ surname }}!"""

prompt = greetings("user")
Traceback (most recent call last):
  File "<stdin>", line 9, in <module>
  File "/home/remi/projects/normal/prompts/prompts.templates.py", line 38, in __call__
      return render(self.template, **bound_arguments.arguments)
  File "/home/remi/projects/normal/prompts/prompts.templates.py", line 213, in render
      return jinja_template.render(**values)
  File "/home/remi/micromamba/envs/prompts/lib/python3.9/site-packages/jinja2/environment.py", line 1301, in render
      self.environment.handle_exception()
  File "/home/remi/micromamba/envs/prompts/lib/python3.9/site-packages/jinja2/environment.py", line 936, in handle_exception
      raise rewrite_traceback_stack(source=source)
  File "<template>", line 1, in top-level template code
  jinja2.exceptions.UndefinedError: 'surname' is undefined

Importing prompt functions

Prompt functions are functions, and thus can be imported from other modules:

import prompts

@prompts.template
def greetings(name, question):
    return """Hello, {{ name }}!
    {{ question }}
    """
from .prompts import greetings

prompt = greetings("John Doe", "How are you today?")
Hello, John Doe!
How are you today?

Few-shot prompting

Few-shot prompting can lead to messy code. Prompt functions allow you to loop over lists or dictionaries from the template. In the following example we demonstrate how we can generate a prompt by passing a list of dictionaries with keys question and answer to the prompt function:

import prompts

@prompts.template
def few_shots(instructions, examples, question):
    return """{{ instructions }}

    Examples
    --------

    {% for example in examples %}
    Q: {{ example.question }}
    A: {{ example.answer }}

    {% endfor %}
    Question
    --------

    Q: {{ question }}
    A:
    """

instructions = "Please answer the following question following the examples"
examples = [
    {"question": "2+2=?", "answer":4},
    {"question": "3+3=?", "answer":6}
]
question = "4+4 = ?"

prompt = few_shots(instructions, examples, question)
print(prompt)
Please answer the following question following the examples

Examples
--------

Q: 2+2=?
A: 4

Q: 3+3=?
A: 6

Question
--------

Q: 4+4 = ?
A:

Conditionals, filters, etc.

Jinja2 has many features beyond looping that are not described here: conditionals, filtering, formatting, etc. Please refer to the Jinja documentation for more information about the syntax of the templating language. The Jinja syntax is powerful, and we recommend you take some time to read their documentation if you are building complex prompts.

Formatting conventions

Prompt functions are opinionated when it comes to rendering, and these opinions are meant to avoid prompting mistakes and help with formatting.

Whitespaces

If you have experience working with strings between triple quotes you know that indenting has an influence on the string's formatting. Prompt functions adopt a few conventions so you don't have to think about indents when writing prompt.

First, whether you start the prompt right after the triple quotes or on the line below does not matter for formatting:

import prompts

@prompts.template
def prompt1():
    return """My prompt
    """

@prompts.template
def prompt2():
    return """
    My prompt
    """

print(prompt1())
print(prompt2())
My prompt
My prompt

Indentation is relative to the second line of the docstring, and leading spaces are removed:

import prompts

@prompts.template
def example1():
    return """First line
    Second line
    """

@prompts.template
def example2():
    return """
      Second line
      Third line
    """

@prompts.template
def example3():
    return """
      Second line
        Third line
    """

print(example1())
print(example2())
print(example3())
First line
Second line

Second line
Third line

Second line
  Third line

Trailing whitespaces are not removed, unless they follow a linebreak symbol \ (see linebreaks).

Linebreaks

You can use the backslash \ to break a long line of text. It will render as a single line:

import prompts

@prompts.template
def example():
   return """
   Break in \
   several lines \
   But respect the indentation
       on line breaks.
   And after everything \
   Goes back to normal
   """

print(example())
Break in several lines But respect the indentation
    on line breaks.
And after everything Goes back to normal