Stop Hardcoding Prompts in Your Python Files: Meet PromptFill (best balance of curiosity and search terms) A developer released PromptFill, a zero-dependency Python package that loads prompt templates from .md or .txt files and substitutes $placeholder variables at runtime using the standard library's string.Template. The tool raises FileNotFoundError for missing files and ValueError for missing variables, and was designed to avoid treating curly braces in prompts as placeholders. Open any LLM-powered Python project that's been alive for a few weeks and you'll probably find the same thing: a function with a prompt inside it. Then the prompt grows a few more instructions, a formatting rule, an example. Before long, the logic is buried under a wall of text and nobody wants to touch the file. That's the itch behind PromptFill , a small, zero-dependency Python package that lets you keep prompts in their own files and fill in the variables at runtime. pip install promptfill This post covers what it does, how it works under the hood it's short enough to read in a minute , and where it fits. Prompts aren't really code, but we treat them like code because they happen to be strings. That causes predictable problems: Think of a form letter. Nobody retypes the whole letter for each recipient. You keep one template with blanks like Dear , and fill in the names when it's time to send. Prompts deserve the same treatment: one template file, blanks filled in by your code. PromptFill loads a prompt template from a text file, replaces the placeholders with values you pass from Python, and returns a plain string. That's the whole job, and it's deliberately small. .md or .txt file. load prompt and supply those values. What you get: ValueError FileNotFoundError Create reviewer.md : You are an expert $role. Review the following code written in $language for bugs, security vulnerabilities, and performance issues. Code to review: $code snippet Provide your findings and suggest improvements. Placeholders use a $ prefix: $role , $language , $code snippet . python from promptfill import load prompt prompt = load prompt file path="reviewer.md", role="Senior Security Engineer", language="Python", code snippet="print 'Hello, World ' " print prompt You are an expert Senior Security Engineer. Review the following code written in Python for bugs, security vulnerabilities, and performance issues. Code to review: print 'Hello, World ' Provide your findings and suggest improvements. The return value is a regular Python string, so it drops straight into whatever client you already use. The whole implementation fits on one screen: python from pathlib import Path from string import Template from typing import Union def load prompt file path: Union str, Path , kwargs - str: path = Path file path if not path.is file : raise FileNotFoundError f"Prompt file not found at: {file path}" content = path.read text encoding="utf-8" template = Template content try: return template.substitute kwargs except KeyError as e: raise ValueError f"Missing prompt variable for placeholder: {e}" Here's what happens when you call it: string.Template from the contents. $placeholder substitution, which is why placeholders look the way they do. substitute with your keyword arguments. $name in the template is replaced with the matching value. Template raises a KeyError internally, and PromptFill catches it and re-raises it as a I chose string.Template over f-strings or str.format on purpose. It only cares about $ , so curly braces in your prompt a JSON example, say aren't treated as placeholders. And since it's part of the standard library, there's nothing extra to install, audit, or keep updated. The real payoff comes when one prompt serves several cases: python from promptfill import load prompt template path = "reviewer.md" python prompt = load prompt file path=template path, role="Python Developer", language="Python", code snippet="print 10 + 20 " javascript prompt = load prompt file path=template path, role="JavaScript Developer", language="JavaScript", code snippet="console.log 10 + 20 " No duplicated text and no drift between copies. Improve the wording in reviewer.md and every caller benefits. Say your template expects $role , $language , and $code snippet , but you forget language : python from promptfill import load prompt prompt = load prompt file path="reviewer.md", role="Developer", code snippet="x = 1" You get: ValueError: Missing prompt variable for placeholder: 'language' It fails loudly, and it names the exact variable you forgot. That beats sending an incomplete prompt to a model and wondering why the output is strange. Because PromptFill builds on string.Template , a few of its standard behaviors carry over: $ in a prompt for example, a price , write $$ in the template file. ${name} also works. ${language} review . PromptFill only formats prompts. It doesn't call any model itself, so you're free to use whichever provider you like. Here's an example with an OpenAI-compatible client: python from promptfill import load prompt from openai import OpenAI client = OpenAI prompt = load prompt file path="reviewer.md", role="Python Expert", language="Python", code snippet="print 'Hello, World ' " response = client.chat.completions.create model="YOUR MODEL NAME", messages= {"role": "user", "content": prompt} print response.choices 0 .message.content The OpenAI SDK is installed separately pip install openai and only for this example. PromptFill itself stays dependency-free. Once prompts are files, organizing them is easy: my-ai-project/ ├── prompts/ │ ├── reviewer.md │ ├── summarizer.md │ └── chatbot.txt ├── main.py ├── requirements.txt └── README.md One file per prompt, named for its purpose. It also makes prompt changes easy to review: a diff on prompts/summarizer.md reads like a diff on a document, not a tangle of string literals. PromptFill is aimed at anyone who keeps prompts in Python projects: PromptFill is a text formatter. It doesn't make prompts or inserted values safe on its own, so a few habits still apply: PromptFill is young and the roadmap is open. Ideas I'm considering: If one of these matters to you, tell me. Feedback shapes what gets built first. pip install promptfill If you try it, I'd love to hear how it goes. Open an issue, suggest a feature, or send a pull request. And if it saves you from one more 80-line string in a Python file, a star on the repo goes a long way. How do you manage prompts in your projects: inline strings, files, or something else? Let me know in the comments.