Prompt Engineering With Python: Templates, Tests, and Reuse

Prompt engineering with Python means building prompts as code: templates you can test, version, and call from an application. This guide covers template structure, output testing, library choices, and where to keep approved instructions.

Updated 6 min read

Prompt engineering with Python means treating prompts as code: templates you can version, test across inputs, and call from an application. Instead of typing an instruction into a chat interface and adjusting it manually, you write it in Python, parameterize it for different inputs, and run automated checks on the outputs. Tools like fenbs hold the approved instructions and the decisions behind them outside the codebase, so they can be read by any connected AI app without being hardcoded in a file.

This guide is for developers who want a repeatable workflow for building and maintaining prompts rather than a one-off approach. The basic Python shown here works with any model that provides an API. The principles apply whether you are using the Anthropic SDK, the OpenAI library, or another client.

Why Python fits prompt work

Python is a practical choice for prompt work because it handles the repetitive parts well. String formatting, API calls, and result parsing are all straightforward. You can run the same prompt on a batch of inputs, collect the outputs, and compare them in a script that takes a few minutes to write.

Python also integrates naturally with the testing tools developers already use: pytest for assertions, logging for recording outputs, and version control for tracking what changed and why. Treating a prompt as a Python string in a versioned file gives you the same review and rollback tools you use for code.

Building a prompt template in Python

A prompt template is a string with placeholders that you fill in before sending the request. The simplest version uses Python's f-string or .format(). For more complex templates with conditional sections, a templating library like Jinja2 gives you more control.

Simple prompt template with f-string
def build_summary_prompt(document: str, audience: str, max_bullets: int) -> str:
    return (
        f"Summarize the following document for {audience}.\n"
        f"Format: up to {max_bullets} bullet points, each one sentence.\n"
        "Constraint: include only findings, not recommendations.\n\n"
        f"{document}"
    )

Keep the template in a separate function rather than inline in the API call. That separation makes it easier to test the template itself, swap it out without touching the application logic, and review changes in a pull request. If the prompt changes often, consider loading it from a file or a configuration source rather than hardcoding it.

Loading a prompt from a file
from pathlib import Path

def load_prompt(name: str) -> str:
    return (Path("prompts") / f"{name}.txt").read_text()

Testing prompt outputs programmatically

Automated testing for prompts works differently from unit tests. You cannot assert an exact output string because model outputs vary. Instead, you assert structural properties: the output is valid JSON, it contains the required fields, it is under a certain length, or it does not contain a known failure pattern.

Assert structural properties of a prompt output
import json

def test_summary_output(output: str) -> None:
    # Check the output contains at least one bullet point
    assert output.strip().startswith("-") or output.strip().startswith("\u2022"), \
        "Output should start with a bullet point"
    # Check the output does not include the word "recommend"
    assert "recommend" not in output.lower(), \
        "Output should not include recommendations"

Run each prompt template on a small fixture set: five to ten real inputs that represent the range of cases you encounter. Save the fixture inputs and the assertions in your test suite. When you change the prompt, run the tests to check whether the change broke anything.

For tasks where the output quality matters but is hard to assert automatically, log the outputs and review a sample manually. Model-based evaluation, where you ask a second AI call to judge the first output, is a useful complement to structural assertions. See prompt engineering vs. RAG vs. fine-tuning for when evaluation needs go beyond the prompt itself.

Choosing a prompt library

Several Python libraries support prompt engineering work. The right choice depends on what you need to manage.

Python prompt libraries compared
LibraryGood forAdds
Jinja2Complex templates with conditionals, loops, and partials.A templating engine; install separately. No AI-specific features.
LangChain (prompt components)Projects already using LangChain for chains or agents.PromptTemplate, ChatPromptTemplate, few-shot helpers. Adds a large dependency.
LlamaIndexProjects that combine prompts with document retrieval.Prompt management tied to query pipelines. Best when retrieval is part of the task.
Plain Python stringsSimple templates where a function and a text file are enough.No extra dependency. Fewer abstractions to learn or debug.

For most projects, plain Python functions and a prompts/ directory are sufficient. Add a library when the project outgrows that: when templates become complex enough that Jinja2 conditionals are cleaner than Python string logic, or when a framework like LangChain is already in use.

Keeping approved prompts and their change history

A prompt that is approved and in production should be treated like a configuration file: changed deliberately, with a reason recorded and the change reviewed. Store production prompts in version control. The commit message should explain why the prompt changed, not just what changed.

For prompts that need to be readable and writable by non-developers or by connected AI apps, a separate store outside the codebase is practical. A developer changes the prompt, the reason is recorded, and other team members or AI tools can read the current version without opening a repository.

How to do this with fenbs

fenbs keeps a Skills and prompts library outside any codebase. A connected AI app can read current approved instructions before working, without those instructions being hardcoded in a file.

  1. Create a free account. Add a project for the application where these prompts are used.
  2. Add each approved prompt to Skills and prompts. Include the task it covers, any parameter notes, and which model version it was tested against.
  3. Use Decisions and rules to record why a prompt was written a certain way, such as "always request JSON output for this pipeline because the parser expects it." Future changes start from that reasoning.
  4. Connect Claude Code, Cursor, or another coding assistant from Integrations. Ask it to read the project context, including the approved prompts, before suggesting changes.
  5. When a prompt changes, add a note on what changed and why. That history is more useful to the next developer than reading the git blame.
How fenbs helps

Keep approved prompts outside the codebase

fenbs holds your Skills and prompts library alongside the decisions and context that explain them. Connected coding assistants like Claude Code and Cursor read the library before working. When a prompt changes, the reason lives in fenbs, not in a comment that may get removed on the next cleanup.

Create a free account

Free for one person · No credit card · Works with Claude, ChatGPT, Cursor and more

Questions people ask.

Should prompts be stored in code or in a separate file?

A separate file is usually better for prompts that change independently of the surrounding code. It makes review easier, allows non-developers to read or suggest changes, and keeps the prompt out of a Python string where formatting and escaping add noise.

How do I version control prompts without committing them to the repository?

Keep them in a repository that developers can read but that is not part of your main application build. Alternatively, store them in a configuration service or a tool like fenbs that versioning and history are already built into.

Do I need a prompt engineering library or is plain Python enough?

Plain Python is enough for most projects. A library becomes useful when your templates have complex conditional logic that would be cleaner in Jinja2, or when you are already using a framework like LangChain that includes prompt management utilities.

How do I test a prompt that produces creative or open-ended output?

Test structural properties you can assert: output length, required sections, the presence or absence of specific patterns. For quality, log a sample of outputs and review them manually on each prompt change. Model-based evaluation, where a second call judges the output, is a practical addition for high-volume tasks.

What is a prompt engineering library in Python?

A Python library that provides utilities for building, formatting, and managing prompts. The most common are LangChain's prompt components, LlamaIndex for retrieval-augmented workflows, and Jinja2 for complex string templates. Plain Python string functions also work well for simpler cases.

More in Prompts and skills for AI work

All Prompts and skills for AI work articles

Start with one thing.

Free for one person · No credit card · Works with Claude, ChatGPT, Cursor and more

There is nothing to set up first. Write one line and you’ve started.