Prompt Templates and Variables

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 AI interactions. Prompts are essential for guiding AI 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 in TypeScript

Before we dive into templates, let's briefly recall file handling in TypeScript. This knowledge is essential, as we will be loading template files in this lesson. In TypeScript, we use the fs module to work with files. The fs.promises.readFile function allows us to read the contents of a file asynchronously. Here’s a quick example:

import { promises as fs } from "fs";

const filePath = "example.txt";

async function readFileExample() {
  const content = await fs.readFile(filePath, "utf8");
  console.log(content);
}

readFileExample();

This code snippet reads the contents of a file named example.txt and prints them. The await keyword is used to wait for the file to be read before continuing.

Understanding Template Structure

Our templates will represent prompts that can be enriched with variables, in the form of text files with placeholders. These placeholders will be enclosed in curly braces, like {{input}}. Let's look at an example template:

Rewrite the following sentence in a different way that retains its original meaning:

Original: {{input}}

Return a list of up to 3 paraphrased versions as plain text strings.

In this template, {{input}} is a placeholder that will be replaced with a specific sentence when the template is rendered. This structure allows us to create flexible prompts that can adapt to different inputs.

Loading Templates with loadTemplate

To use a template, we first need to load it from a file. Let's break down the loadTemplate function:

We start by defining the loadTemplate function, which takes a name as an argument.

import { promises as fs } from "fs";
import { join } from "path";
import { config } from "../config";

export async function loadTemplate(name: string) {
  const file = join(config.promptsDir, `${name}.txt`);
  return fs.readFile(file, "utf8");
}

Here, we use the join function from the path module to construct the full path to the template file. The config.promptsDir variable points to the folder where prompt templates are stored. The file name is created by appending .txt to the template name.

Notice that we do not use the await keyword inside the loadTemplate function itself. Instead, we simply return the result of fs.readFile, which is a Promise. Because the function is marked as async, it will automatically wrap the returned Promise, allowing callers to use await when they call loadTemplate. This pattern is useful when you want to let the calling code decide when and how to wait for the result, or when you want to chain additional asynchronous operations.

For example, you can use await when calling loadTemplate elsewhere in your code:

const template = await loadTemplate("paraphrase");

This approach keeps the function concise and flexible, returning a Promise that resolves to the file contents as a string.

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