Introduction and Context Setting

Welcome to the lesson on Prompt Structure and Variables. In this lesson, we will explore how prompts are structured and how variables are used to create dynamic and flexible interactions. Prompts are essential in guiding behavior, allowing us to tailor responses to specific needs. By the end of this lesson, you will understand how to load and render templates using variables — a crucial skill in building the AI Cooking Helper.

Recall: Basics of File Handling

Before we dive into templates, let's briefly recall how to handle files and navigate directories. This knowledge is essential, as we will be loading template files stored in specific folders. Instead of using hardcoded strings for paths, it is safer to use path manipulation tools to ensure that the application works regardless of where it is run.

import os

# Get the directory of the current script
current_dir = os.path.dirname(__file__)

# Construct a path to a file in the same directory
file_path = os.path.join(current_dir, "example.txt")

# Open and read the file
with open(file_path, "r", encoding="utf-8") as handle:
    content = handle.read()
print(content)

In this snippet, os.path.join creates a valid path, and the with statement ensures that the file is handled properly and closed automatically. We use encoding="utf-8" to ensure that text characters are read correctly.

Understanding Template Structure

Our templates will represent prompts that can be enriched with context. These are stored as text files with placeholders. Placeholders are enclosed in double curly braces, like {{variable_name}}.

For example, consider a template for a recipe generator:

Create a recipe for {{dish_name}} that serves {{servings}} people.
Include the following dietary restrictions: {{restrictions}}.

In this template, {{dish_name}}, {{servings}}, and {{restrictions}} are placeholders. When rendering the template, we provide actual values for these keys, making the prompt adaptable.

Loading Templates with load_template

To use a template, we first need to load it from the project's file structure. In our project, prompt files are stored in a specific directory: static/prompts.

import os

def load_template(template_name: str) -> str:
    """
    Load a template file from the 'static/prompts' directory.
    Automatically appends '.txt' to the filename.
    """
    # Navigate to the parent directory to find the 'static' folder
    base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
    prompts_dir = os.path.join(base_dir, "static", "prompts")
    file_path = os.path.join(prompts_dir, f"{template_name}.txt")

    with open(file_path, "r", encoding="utf-8") as handle:
        return handle.read()
  • base_dir calculates the absolute path to the project root.
  • prompts_dir points specifically to the static/prompts folder.
  • The function automatically appends .txt to the template_name, so you only need to provide the base name (e.g., recipe_request).
Rendering Templates with Variables

Once a template is loaded as a string, we need to replace the {{variable}} placeholders with real data. We use regular expressions (the re module) to find these patterns and swap them out.

import re
from typing import Dict

def render_template(template_str: str, variables: Dict[str, object]) -> str:
    """
    Replace placeholders in the form {{VAR}} with values from variables.
    If a variable is missing, leave the placeholder unchanged and log a warning.
    """
    def replacer(match: re.Match) -> str:
        # match.group(1) is the text inside the {{ }}
        var_name = match.group(1)
        if var_name in variables:
            return str(variables[var_name])
        
        # If the variable isn't in our dictionary, log a warning
        print(f"Warning: Missing variable '{var_name}'")
        # Return the original placeholder (match.group(0) is the full {{VAR}})
        return match.group(0)

    # Search for patterns like {{anything_here}}
    pattern = re.compile(r"\{\{(\w+)\}\}")
    return pattern.sub(replacer, template_str)
  • The replacer function is a helper that determines what to put in place of a match.
  • If the variable exists in our variables dictionary, it returns the value as a string.
  • If the variable is missing, it prints a warning to the console and returns match.group(0), which keeps the original {{variable_name}} text in the prompt so we can identify what went wrong.
Combining Template Loading and Rendering

To make the process seamless, we combine these two steps into a single function. This allows us to get a final, usable prompt string by simply providing a file name and the data.

def render_prompt_from_file(template_name: str, variables: Dict[str, object]) -> str:
    """
    Load a template from file, perform variable substitution, and return the final string.
    """
    template = load_template(template_name)
    return render_template(template, variables)

This approach keeps our prompt logic clean: the text is managed in external files, and the code handles the injection of dynamic content.

Summary and Preparation for Practice

In this lesson, you learned how to structure prompts using templates and variables. We covered:

  1. Navigating directories to locate template files.
  2. Loading text files using the correct encoding.
  3. Using regular expressions to find and replace placeholders.
  4. Handling missing data by providing warnings while keeping the prompt structure intact.

As you move on to the practice exercises, you will implement these functions to build the foundation for the AI Cooking Helper. Experiment with different variable names and see how the template engine handles them!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal