Define a Prompt Template
Learn how to define a prompt template for task data in this LLM Finetuning tutorial. Step 17 covers the core concept, hands-on implementation, common pitfalls, and next steps.
Focus: define a prompt template for task data
Your fine-tuning dataset is a pile of raw text snippets — conversation logs, support tickets, product reviews, code diffs. But your model doesn't learn from raw text. It learns from prompt-completion pairs: a structured instruction that tells it what to do, followed by the expected output. If those instructions are inconsistent — sometimes phrased as a question, sometimes as a command, sometimes missing context — your fine-tuned model will produce equally inconsistent results. Defining a prompt template for task data is the step that turns your messy corpus into a clean, trainable dataset. It's the difference between a model that knows what you want and one that guesses.
The Problem This Lesson Solves
When you download a raw dataset or scrape your own logs, you rarely get data in the exact shape your model needs. Each record might contain fields like question, answer, context, or metadata — but the model doesn't understand those fields. It only understands tokens. If you feed it raw JSON, it learns to mimic JSON. If you feed it conversational text, it learns to babble like a chat log. The pain is real: inconsistent prompts lead to inconsistent outputs, and you can't evaluate or deploy a model that behaves unpredictably.
Imagine training a support bot on thousands of ticket resolutions. Without a template, one sample might be "Customer says: Can't login. Resolution: Reset password." while another is "How do I fix login issues? -> Use reset process." The model never learns a consistent 'input → output' rule. It learns noise. A well-defined prompt template standardizes every sample into the same grammatical structure, so the model can focus on the actual task logic, not on guessing your intent.
Core Concept / Mental Model
Think of a prompt template as a fill-in-the-blank form for every piece of task data. You define a structure with placeholders, then fill those placeholders with the actual fields from each record. The filled-in version becomes the prompt, and the expected answer becomes the completion. This pair forms a single training example.
A template has two parts:
- Instruction: The fixed text that tells the model what to do (e.g., "Summarize the following paragraph:").
- Placeholders: Variables like {input} or {context} that get replaced per-sample.
You also need a separator (like \n\n### Response:\n) to clearly mark where the prompt ends and the completion begins. During training, the model learns to generate the completion given the prompt. During inference, you use the exact same template to format new input — this consistency is what makes fine-tuning effective.
A diagram-in-words:
Template: "Classify the sentiment of: {text}\n\nSentiment:"
Sample: "Classify the sentiment of: I love this product!\n\nSentiment: Positive"
The template is the mold; the data is the clay. The template is what gives the task its shape.
How It Works Step by Step
Step 1: Identify the Task Type
Before you write a template, know what task you're fine-tuning for. Is it classification, summarization, extraction, generation, or Q&A? The template's instruction should match the task. For classification, ask for a label. For summarization, ask for a summary. For extraction, ask for key values.
Step 2: Define the Fields
Look at your raw data. What fields exist per record? Typically you have an input (like text or context), and an output (like a label or answer). Sometimes you have extra context that should be included. Name these fields clearly (e.g., input_text, output_label).
Step 3: Write the Template String
Compose a template with fixed instruction text and placeholders. Use a consistent format across all samples. Common styles include:
- Alpaca-style: "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\n{instruction}\n\n### Input:\n{input}\n\n### Response:\n"
- Chat-style: <|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\n{user}<|im_end|>\n<|im_start|>assistant\n
- Simple task-specific: "Classify the sentiment of the following text as Neutral, Positive, or Negative.\n\nText: {text}\n\nSentiment: "
Step 4: Apply the Template
Write a function (or use a library) to take each raw record and produce a formatted string. This function is your template engine. It must be pure — same input always gives same output — because you'll reuse it at inference time.
Step 5: Create the Training Dataset
Combine the formatted prompt and the completion into a text field or separate prompt/completion fields. Many libraries (like Hugging Face's datasets) expect a text column for causal LM fine-tuning. The structure is: text = prompt + completion.
Hands-On Walkthrough
Let's implement a simple template for a sentiment classification dataset. We'll use Python and a mock dataset.
Example 1: Simple Template Function
# Define a prompt template for task data
TEMPLATE = "Classify the sentiment of the following text as Positive, Negative, or Neutral.\n\nText: {text}\n\nSentiment: "
def format_training_sample(text, label):
prompt = TEMPLATE.format(text=text)
completion = f"{label}\n"
return prompt, completion
# Mock data
samples = [
("I love this new laptop!", "Positive"),
("The battery dies too fast.", "Negative"),
("It's an okay phone.", "Neutral")
]
for text, label in samples:
prompt, completion = format_training_sample(text, label)
print(f"PROMPT: {prompt}")
print(f"COMPLETION: {completion}")
print("---")
Expected output:
PROMPT: Classify the sentiment of the following text as Positive, Negative, or Neutral.
Text: I love this new laptop!
Sentiment:
COMPLETION: Positive
---
...
Pro tip: Always include a trailing space or newline in the prompt after the final colon so the completion starts on its own token boundary. This improves training stability.
Example 2: Applying to a Hugging Face Dataset
from datasets import Dataset
def format_dataset(records):
prompts = [TEMPLATE.format(text=t) for t in records['text']]
completions = [f"{label}\n" for label in records['label']]
return {'prompt': prompts, 'completion': completions, 'text': [p + c for p, c in zip(prompts, completions)]}
mock_records = {
'text': ["Great value for money.", "Poor build quality."],
'label': ["Positive", "Negative"]
}
ds = Dataset.from_dict(mock_records)
ds = ds.map(format_dataset, batched=True)
print(ds[0])
Expected output: A dict with prompt, completion, and text fields, ready for training.
Example 3: Multi-Field Template (Q&A with Context)
QA_TEMPLATE = """Answer the question based on the provided context.\n\nContext: {context}\n\nQuestion: {question}\n\nAnswer: """
def format_qa(context, question, answer):
prompt = QA_TEMPLATE.format(context=context, question=question)
completion = f"{answer}\n"
return prompt + completion
context = "The Eiffel Tower was built in 1889 in Paris, France."
question = "When was the Eiffel Tower built?"
answer = "The Eiffel Tower was built in 1889."
sample = format_qa(context, question, answer)
print(sample)
This shows how templates can incorporate multiple variables — crucial for tasks that depend on context.
Compare Options / When to Choose What
| Template Style | Best For | Pros | Cons |
|---|---|---|---|
| Simple task-specific | Classification, single-input tasks | Minimal tokens, clear instruction | May lack robustness for complex tasks |
| Alpaca-style | General instruction following | Versatile, standard in many fine-tunes | More tokens, slightly increased training cost |
| Chat-style (with special tokens) | Conversational models, multi-turn | Matches inference API expectations | Requires tokenizer that supports special tokens |
| Prompt with context section | RAG, extractive QA | Gives model necessary background | Longer prompts, careful separator management |
When to choose what: If you're doing a single-label classification, keep it simple. If you're building a general assistant, use a standard like Alpaca. If your model will be deployed in a chat interface, use chat-style tokens (e.g., <|im_start|>). If your task depends on external context, include a {context} placeholder.
Variations to consider:
- Instruction-tuned snippets: Borrow from existing templates (Alpaca, Vicuna, ShareGPT) instead of writing from scratch.
- Dynamic placeholders: Use str.format_map or f-strings with dictionaries for flexibility.
- YAML/JSON configs: Store templates in config files to experiment without code changes.
Troubleshooting & Edge Cases
Problem: Inconsistent formatting across splits
Symptom: Model performs well on training but poorly on validation. Cause: You may have used different templates for train and validation. Fix: Always use the exact same template function for all splits and for inference.
Problem: Completion leaks into the prompt
Symptom: Model memorizes answers instead of learning a task. Cause: If your prompt contains the answer (e.g., the label appears in the instruction or context). Fix: Ensure the completion appears only after the prompt separator.
Problem: Special characters in data break formatting
Symptom: Prompt has broken newlines or braces from data. Cause: Placeholders like {text} can contain curly braces, which interfere with .format(). Fix: Use str.format_map with a safe dictionary, or escape braces manually. For example, use .format(text=data) but if data contains {}, use .replace('{', '{{').replace('}', '}}') first.
Problem: Multi-turn data with no separator
Symptom: Model blends turns together. Cause: Missing special token between turns. Fix: Add explicit tokens like \n### Assistant:\n between each exchange.
Edge Case: Empty fields
Symptom: Prompt says "Text: \n\nSentiment:" with blank input. Fix: Drop samples with missing required fields, or use a default placeholder like "No context provided.".
Edge Case: Length limits
Symptom: Prompt+completion exceeds model's max token limit. Fix: Truncate the input portion, but keep the instruction and separator intact. Never truncate the completion.
What You Learned & What's Next
You now know how to define a prompt template for task data — the fourth pillar of dataset preparation. You learned to identify task types, write templates with placeholders, apply them consistently with a pure function, and avoid common pitfalls like format inconsistency and leakage. You also saw how to compare template styles and picked the right one for your use case.
Connect to your learning objectives: You can explain the core idea (standardizing raw data into prompt-completion pairs) and you can complete a practical exercise (the hands-on functions above).
Next lesson: You'll move on to tokenization & sequence length considerations — how to convert these formatted text strings into token IDs while respecting the model's context window. With your template locked, the next step is feeding it correctly into the tokenizer. Keep your template function handy — you'll reuse it there.
Practice recap
Now define a prompt template for your own task data. Pick a small dataset (like a few support emails and their categories), write a template function, and generate a few formatted samples. Then manually inspect each sample for consistency and check that the completion never appears in the prompt. If you're using the Hugging Face datasets library, apply the function with .map() and verify the text column.
Common mistakes
- Using different templates for training vs. validation — always reuse the exact same formatting function.
- Including the answer in the prompt (e.g., putting the label inside the instruction), which causes the model to memorize rather than learn.
- Failing to handle curly braces in data when using
.format(), leading to runtime errors or malformed prompts. - Forgetting separators like
\n### Response:\n, causing the model to stitch prompt and completion together indistinguishably.
Variations
- Alpaca-style templates with separate instruction/input/response sections for general instruction following.
- Chat-style templates using special tokens like
<|im_start|>and<|im_end|>for conversational fine-tuning. - Template configs stored in YAML or JSON to allow rapid experimentation without code changes.
Real-world use cases
- Training a customer support classifier on ticket descriptions with a simple text-to-sentiment template.
- Fine-tuning a RAG-style Q&A model where each prompt includes a context field from retrieved documents.
- Building a multi-turn chat assistant using chat-style templates to preserve conversation history and roles.
Key takeaways
- A prompt template standardizes raw data into consistent prompt-completion pairs, which is essential for effective fine-tuning.
- Placeholders like
{text}are filled per-sample with a pure function that you reuse at inference. - The template structure must match the task type (classification, QA, generation) to give the model the right signal.
- Always include a clear separator between prompt and completion, and never truncate the completion.
- Choose between simple, Alpaca, or chat-style templates based on your deployment format and task complexity.
- Avoid data leakage, format inconsistency, and special-character pitfalls by testing your template on a few samples first.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.