A practical reference for the most effective prompt patterns, when to
use them, and how to combine them for complex tasks.
| Pattern | Best For | Complexity | Token Cost |
|---|---|---|---|
| Zero-shot | Simple, well-defined tasks | Low | Low |
| Few-shot | Tasks where output format matters | Medium | Medium |
| Chain-of-Thought | Reasoning, math, logic | Medium | High |
| Self-Consistency | High-stakes reasoning | High | Very High |
| ReAct | Agent tasks with tools | High | Variable |
| Tree-of-Thought | Complex problem solving | Very High | Very High |
Give the model a task with no examples. Works when the task is
unambiguous and the model has strong prior knowledge.
When to use: Classification, simple extraction, translation,
summarization of short texts.
When NOT to use: Tasks with specific output formats, domain-specific
terminology, or unusual conventions.
System: You are a sentiment classifier. Return exactly one word:
positive, negative, or neutral.
User: "The new update completely broke my workflow. Unacceptable."
Key principle: The more specific your system message, the better
zero-shot works. Constrain the output format tightly.
Provide 2-5 input/output examples before the actual task. The model
learns the pattern from examples rather than explicit instructions.
When to use: Custom output formats, domain-specific tasks,
tasks where "show don't tell" is easier.
When NOT to use: Tasks where examples would be too long (> 1K tokens
each), or where the pattern is trivially obvious.
1. Diverse: Cover different edge cases, not just the happy path
2. Representative: Match the distribution of real inputs
3. Correct: Wrong examples teach wrong patterns — double-check
4. Ordered: Put the most similar example to the expected input last
# BAD: The model will always output "Acme Corp" because all examples use it
Example 1: Input: "..." → {"company": "Acme Corp"}
Example 2: Input: "..." → {"company": "Acme Corp"}
Example 3: Input: "..." → {"company": "Acme Corp"}
# GOOD: Diverse examples prevent fixation on one pattern
Example 1: Input: "..." → {"company": "Northwind Inc"}
Example 2: Input: "..." → {"company": "DataCo"}
Example 3: Input: "..." → {"company": "StreamLine Labs"}
Ask the model to show its reasoning step by step before giving
the final answer. Dramatically improves accuracy on reasoning tasks.
When to use: Math problems, multi-hop reasoning, logic puzzles,
complex analysis, any task where intermediate steps matter.
Two approaches:
System: Think step by step before answering.
User: If a store has 240 apples and sells 30% on Monday,
then receives a shipment of 50 on Tuesday, how many apples
does it have on Wednesday?
Just add "Let's think step by step" to any prompt. Surprisingly
effective — often matches few-shot CoT performance.
For production use, separate the reasoning from the answer:
System: You solve math problems. Show your work in a <reasoning>
block, then give the final answer in a <answer> block.
User: ...
Expected output:
<reasoning>
Step 1: 240 × 0.30 = 72 apples sold
Step 2: 240 - 72 = 168 remaining after Monday
Step 3: 168 + 50 = 218 after Tuesday shipment
</reasoning>
<answer>218</answer>
Why this matters: You can programmatically extract just the
block while keeping the reasoning for debugging.
Run the same prompt multiple times (with temperature > 0), then
take the majority answer. Trades cost for accuracy.
When to use: High-stakes decisions, ambiguous inputs,
tasks where you need confidence calibration.
# Pseudocode for self-consistency
answers = []
for _ in range(5):
response = llm.complete(prompt, temperature=0.7)
answer = extract_answer(response)
answers.append(answer)
final_answer = most_common(answers)
confidence = answers.count(final_answer) / len(answers)Cost consideration: 5× the token cost. Use only when accuracy
is worth the expense (medical, financial, legal domains).
Assign the model a specific persona or expertise. This activates
domain-specific knowledge and adjusts tone/depth.
Effective roles:
"You are a senior Python developer with 15 years of experience."
"You are a tax accountant specializing in small business filings."
"You are a cybersecurity analyst performing a threat assessment."
Ineffective roles:
"You are the world's best AI." (too vague)
"You are an expert." (at what?)
"You are helpful and harmless." (default behavior anyway)
Combining with constraints:
You are a senior data engineer. You ONLY recommend solutions using:
- Python 3.10+
- Apache Spark 3.x
- Delta Lake
- Azure cloud services
If asked about other technologies, say "Outside my expertise area."
The most impactful technique for production systems. If you don't
control the output format, you can't reliably parse the response.
System: Return a JSON object with exactly these keys: name, age, city.
Return ONLY the JSON object, no additional text.
System: Format your response as a markdown table with columns:
| Feature | Status | Priority |
System: Use these delimiters in your response:
<analysis>Your analysis here</analysis>
<recommendation>Your recommendation</recommendation>
<confidence>0.0 to 1.0</confidence>
# BAD: No format control → unparseable output
System: Analyze this data and give me the results.
# GOOD: Explicit format → reliable parsing
System: Analyze this data. Return a JSON object with:
- "findings": array of strings
- "severity": "low" | "medium" | "high"
- "confidence": float 0.0-1.0
Return ONLY the JSON. No markdown fences, no commentary.
Break complex tasks into a pipeline of simpler prompts, where
each step's output feeds the next step's input.
Step 1: Extract entities → list of entities
Step 2: Classify relationships → entity graph
Step 3: Summarize findings → executive summary
Step 4: Generate recommendations → action items
Advantages over one giant prompt:
Disadvantage: Higher total latency (steps run sequentially).
Tell the model what NOT to do. Surprisingly effective for preventing
common failure modes.
System: You summarize documents.
DO NOT:
- Start with "This document discusses..."
- Include your own opinions or editorializing
- Use the phrase "in conclusion"
- Add information not present in the source
- Use bullet points (write flowing paragraphs)
Is the task simple and well-defined?
├── Yes → Zero-shot with format control
└── No
├── Does output format matter?
│ ├── Yes → Few-shot (2-3 examples)
│ └── No → Zero-shot with CoT
├── Does it require reasoning?
│ ├── Yes → Chain-of-Thought
│ │ └── Is accuracy critical? → Self-Consistency (5 runs)
│ └── No → Few-shot or zero-shot
├── Is it multi-step?
│ ├── Yes → Prompt chaining
│ └── No → Single prompt with CoT
└── Does it need tools/actions?
└── Yes → ReAct or Plan-and-Execute
1. System messages are cached in many APIs — put stable content there
2. Few-shot examples cost tokens every call — consider fine-tuning if
you're running > 10K calls/day with the same examples
3. CoT increases output tokens — budget for 2-3× the raw answer length
4. Batch related tasks into one call when possible (classify 10 items
at once instead of 10 separate calls)
5. Use tiered models: GPT-4o-mini for classification, GPT-4o for reasoning
Track these metrics for every prompt in production:
| Metric | How to Measure | Target |
|---|---|---|
| Parse success rate | Did the output match the expected format? | > 98% |
| Accuracy | Human evaluation or automated checks | Task-dependent |
| Latency | Time to first token + total generation time | < 5s for interactive |
| Token efficiency | Tokens used vs. useful output length | Monitor trend |
| Consistency | Same input → same output (at temp=0) | > 95% |
| Edge case handling | Test with adversarial inputs | No crashes |
How to manage, version, test, and deploy prompt changes using the
registry system included in this kit.
Prompts are code. They have the same failure modes:
knows what changed or when
Prompt versioning solves all of these by giving every prompt change
a version number, timestamp, author, and diff history.
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Draft │ → │ Test │ → │ Stage │ → │ Prod │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
Write & A/B test Shadow run Full traffic
iterate vs. current alongside (rollback-ready)
Create or modify a prompt template using the YAML format:
id: extraction.entity_extraction
name: "Named Entity Extraction"
version: "2.1" # ← Semantic version you assign
category: extraction
system_message: |
You are a precise entity-extraction engine...
user_template: |
Extract all named entities from: {input_text}Register it in the Python registry:
from src.registry import PromptRegistry
registry = PromptRegistry()
# Adding automatically increments the internal version counter.
# The YAML 'version' field is your human-readable semantic version.
record = registry.add(
prompt_id="extraction.entity_extraction",
name="Named Entity Extraction",
category="extraction",
content={
"system_message": "You are a precise entity-extraction engine...",
"user_template": "Extract all named entities from: {input_text}",
},
tags=["ner", "json-output"],
metadata={"author": "your-name", "semantic_version": "2.1"},
)
print(f"Registered as internal version {record.version}")Run the prompt against a held-out evaluation set and compare metrics:
from src.ab_testing import ABTestHarness, ABTestConfig
harness = ABTestHarness()
config = ABTestConfig(
test_name="entity-extraction-v2.1-vs-v2.0",
variant_a_name="v2.0-current",
variant_b_name="v2.1-candidate",
metric_names=["precision", "recall", "parse_success_rate"],
sample_size=100,
confidence_level=0.95,
)
test_id = harness.create_test(config)
# Run your evaluation loop, recording results for each variant...
# (See the A/B testing module documentation for details)
report = harness.get_report(test_id)
print(report)Run the new prompt in production alongside the current one, but only
use the current prompt's output. Compare results offline:
# Pseudocode for shadow mode
current_prompt = registry.get("extraction.entity_extraction", version=5)
candidate_prompt = registry.get("extraction.entity_extraction", version=6)
current_result = llm.complete(current_prompt.render(input_text=text))
candidate_result = llm.complete(candidate_prompt.render(input_text=text))
# Log both, serve only current_result to the user
log_shadow_comparison(current_result, candidate_result, input_text=text)Once the candidate passes shadow testing, promote it:
# Tag the version as production-active
registry.tag("extraction.entity_extraction", version=6, tag="production")history = registry.history("extraction.entity_extraction")
for record in history:
print(f"v{record.version} | {record.created_at} | {record.metadata}")from src.diff_tool import PromptDiff
differ = PromptDiff()
v5 = registry.get("extraction.entity_extraction", version=5)
v6 = registry.get("extraction.entity_extraction", version=6)
diff = differ.compare(v5.content, v6.content)
print(diff.summary())
# Output:
# system_message: 2 lines changed (added constraint about date formats)
# user_template: unchanged
# variables: 1 added (date_format)diff = registry.diff(
"extraction.entity_extraction",
version_a=5,
version_b=6,
)
# Returns: {"added": [...], "removed": [...], "changed": [...]}# Export the entire registry to a JSON file
registry.export_to_file(Path("prompts_backup_2025-06-17.json"))
# Export a single prompt's history
registry.export_prompt("extraction.entity_extraction", Path("entity_extraction_history.json"))new_registry = PromptRegistry()
new_registry.import_from_file(Path("prompts_backup_2025-06-17.json"))The kit includes 30 prompt YAML files in the prompts/ directory.
Load them all at once:
from src.template_engine import TemplateEngine
engine = TemplateEngine()
engine.load_directory(Path("prompts/"))
# All 30 templates are now available
template = engine.get("extraction.entity_extraction")
messages = template.build_messages(input_text="Acme Corp announced...")Never change the system message AND user template AND examples in
the same version. Change one component, test it, then change the next.
This makes it possible to attribute quality changes to specific edits.
Add a change_notes field to metadata when registering:
registry.add(
prompt_id="classification.sentiment",
name="Sentiment Classifier",
category="classification",
content=new_content,
metadata={
"change_notes": "Added 'mixed' sentiment category for texts with both positive and negative aspects",
"author": "your-name",
},
)For every prompt in production, maintain a set of 20-50 input/expected-output
pairs. Run this set against every new version before promoting.
eval_set = [
{"input": "Great product!", "expected_sentiment": "positive"},
{"input": "Terrible service.", "expected_sentiment": "negative"},
{"input": "It's okay I guess.", "expected_sentiment": "neutral"},
# ... 20+ more
]
accuracy = 0
for case in eval_set:
result = run_prompt(new_version, case["input"])
if result == case["expected_sentiment"]:
accuracy += 1
accuracy_pct = accuracy / len(eval_set) * 100
print(f"Accuracy: {accuracy_pct:.1f}%")
if accuracy_pct < 90:
print("FAIL: Accuracy below threshold. Do not promote.")Always know which version is currently in production so you can
roll back instantly:
# Before deploying v6
previous_production = registry.get_tagged("extraction.entity_extraction", tag="production")
# Save previous_production.version for rollback
# If v6 has issues
registry.tag("extraction.entity_extraction", version=previous_production.version, tag="production")Treat prompt changes like code changes: