Toolmingo
Guides13 min read

Prompt Engineering Guide: Techniques, Patterns & Examples (2025)

A complete prompt engineering guide — zero-shot, few-shot, chain-of-thought, ReAct, structured output, and developer patterns with Python code examples for OpenAI and Anthropic APIs.

Prompt engineering is the practice of designing and optimizing inputs to large language models (LLMs) to get reliable, accurate, and useful outputs. A well-crafted prompt can turn a mediocre LLM response into a production-ready answer — without changing the model or fine-tuning.

Quick reference

Technique When to use Complexity
Zero-shot Simple, well-defined tasks Low
Few-shot Pattern matching, formatting Low
Chain-of-thought Math, logic, multi-step reasoning Medium
Role prompting Persona, tone, expertise level Low
System prompt Set model behaviour across all turns Low
Structured output JSON, tables, typed responses Medium
Prompt chaining Tasks too complex for one call Medium
ReAct Agents that need tool use + reasoning High
Self-consistency High-stakes, verify answers High
Tree of Thoughts Complex planning, creative exploration High

What is prompt engineering?

A prompt is everything you send to an LLM before it generates a response: the system instruction, conversation history, user message, examples, and any retrieved context.

Prompt engineering is the skill of structuring that input so the model:

  • Understands the task precisely
  • Stays on topic and format
  • Avoids common failure modes (hallucination, verbosity, wrong format)
  • Behaves consistently across many inputs

It matters more than most people think. GPT-4 and Claude 3 Opus given a bad prompt will underperform GPT-3.5 with a good one.

The anatomy of a prompt

┌─────────────────────────────────────────┐
│ SYSTEM PROMPT                           │
│ "You are a senior software engineer..." │
├─────────────────────────────────────────┤
│ CONTEXT / RETRIEVED DATA (optional)     │
│ "Here is the relevant documentation..." │
├─────────────────────────────────────────┤
│ EXAMPLES (optional, for few-shot)       │
│ "Input: ... Output: ..."               │
├─────────────────────────────────────────┤
│ USER MESSAGE                            │
│ "Refactor this Python function..."      │
├─────────────────────────────────────────┤
│ OUTPUT CONSTRAINTS (optional)           │
│ "Respond only with valid JSON."         │
└─────────────────────────────────────────┘

Core techniques

Zero-shot prompting

Ask the model to perform a task with no examples. Works well for tasks the model was trained on extensively.

import openai

client = openai.OpenAI()

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Translate 'Hello, how are you?' to French."}
    ]
)
print(response.choices[0].message.content)
# Bonjour, comment allez-vous ?

When to use: Translation, summarization, simple classification, grammar correction — tasks with clear universal patterns.

When it fails: Novel formatting requirements, domain-specific conventions, ambiguous tasks.


Few-shot prompting

Provide 2–8 input/output examples before the actual task. The model infers the pattern from the examples.

system = """You extract the main action verb from support tickets.

Examples:
Ticket: "My account was locked and I can't log in."
Verb: locked

Ticket: "The payment was declined at checkout."
Verb: declined

Ticket: "I accidentally deleted my project."
Verb: deleted"""

user = "Ticket: 'My subscription was cancelled without notice.'"

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": system},
        {"role": "user", "content": user}
    ]
)
# Verb: cancelled

Rules for good few-shot examples:

Rule Why it matters
3–5 examples is usually enough More adds cost without benefit
Examples should cover edge cases One tricky example prevents many failures
Format must be consistent Model copies the exact format
Shuffle example order Recency bias can affect output
Use real data when possible Synthetic examples can introduce biases

Chain-of-thought (CoT) prompting

Ask the model to reason step by step before giving the final answer. Dramatically improves performance on math, logic, and multi-step tasks.

Standard CoT — add "Think step by step":

user = """A store sells apples for $0.50 each and oranges for $0.75 each.
Sarah buys 6 apples and 4 oranges. She pays with a $10 bill.
How much change does she receive?

Think step by step."""

Zero-shot CoT trigger phrases (in order of effectiveness):

Phrase Notes
"Think step by step." Most reliable, works on all models
"Let's work through this carefully." Slightly more deliberate
"Before answering, reason through the problem." Useful for ambiguous tasks
"Show your work." Good for math/code

Few-shot CoT — include reasoning in examples:

system = """Solve word problems by reasoning step by step.

Problem: A train travels 60 km/h for 2 hours, then 80 km/h for 1 hour. Total distance?
Reasoning:
- First leg: 60 km/h × 2 h = 120 km
- Second leg: 80 km/h × 1 h = 80 km
- Total: 120 + 80 = 200 km
Answer: 200 km"""

When CoT helps most:

Task type Improvement
Arithmetic / math Large — often doubles accuracy
Multi-step logic Large
Code debugging Medium
Factual lookup Small (may hallucinate reasoning)
Simple classification None — adds unnecessary tokens

Role prompting

Give the model a persona with a specific expertise, tone, or perspective.

system = """You are a senior security engineer with 15 years of experience.
You review code for vulnerabilities with a focus on OWASP Top 10.
You are direct, concise, and prioritize critical issues first.
You always suggest the fix, not just the problem."""

Effective role patterns:

Pattern Example
Expert persona "You are a board-certified cardiologist..."
Audience-specific "Explain this as if the reader is a 12-year-old..."
Tone persona "You respond in the style of a Socratic tutor..."
Domain constraint "You only discuss topics related to Python and Linux."
Anti-pattern persona "You are a devil's advocate who challenges every claim."

Avoid: Vague roles like "You are a helpful assistant" — they add nothing. Be specific about expertise and behaviour.


System prompt design

The system prompt sets persistent behaviour across the entire conversation. It's your most powerful lever.

Structure your system prompt with sections:

You are [ROLE].

## Capabilities
- [What you can do]
- [What you cannot do]

## Tone & Style
- [How to respond]
- [What to avoid]

## Output Format
- [Specific formatting rules]
- [Length guidelines]

## Knowledge
- [Domain context]
- [Special terminology]

Real example — code review assistant:

system = """You are a code reviewer for a Python backend team.

## Capabilities
- Review Python, SQL, and Dockerfile code
- Identify bugs, security issues, and performance problems
- Suggest idiomatic Python rewrites

## Tone & Style
- Be direct and concise (max 3 sentences per issue)
- Label issues as CRITICAL / HIGH / MEDIUM / LOW
- Never suggest stylistic changes as HIGH or CRITICAL

## Output Format
- Group findings by severity
- Always include the line number reference
- End with a one-line summary verdict

## Knowledge
- The team uses Python 3.11, FastAPI, SQLAlchemy 2.0, and PostgreSQL
- PEP 8 is enforced via ruff; do not flag style issues ruff catches automatically"""

Advanced techniques

Structured output (JSON mode)

Force the model to return valid JSON — critical for production applications.

With OpenAI JSON mode:

import json

response = client.chat.completions.create(
    model="gpt-4o",
    response_format={"type": "json_object"},
    messages=[
        {
            "role": "system",
            "content": """Extract order information from the user message.
Return valid JSON with this schema:
{
  "product": string,
  "quantity": integer,
  "urgency": "low" | "medium" | "high"
}"""
        },
        {
            "role": "user",
            "content": "I urgently need 5 units of Model X laptops by Friday."
        }
    ]
)

order = json.loads(response.choices[0].message.content)
# {"product": "Model X laptops", "quantity": 5, "urgency": "high"}

With Anthropic Claude + Pydantic:

import anthropic
from pydantic import BaseModel

class OrderInfo(BaseModel):
    product: str
    quantity: int
    urgency: str

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-5",
    max_tokens=1024,
    system=f"""Extract order information and respond with valid JSON matching:
{OrderInfo.model_json_schema()}
Respond with JSON only, no other text.""",
    messages=[{
        "role": "user",
        "content": "I urgently need 5 units of Model X laptops by Friday."
    }]
)

order = OrderInfo.model_validate_json(response.content[0].text)

Structured output tips:

Tip Why
Include a schema in the prompt Model matches your schema more reliably
Use json_object mode (OpenAI) Guarantees valid JSON syntax
Add an example JSON in the prompt Reduces schema interpretation errors
Validate with Pydantic Catches type mismatches at runtime
Handle null explicitly Models often omit optional fields

Prompt chaining

Break a complex task into a pipeline of smaller prompts. Each step's output feeds the next.

def chain(steps: list[dict], initial_input: str) -> str:
    """Run a sequence of prompts, passing output as input."""
    value = initial_input
    for step in steps:
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": step["system"]},
                {"role": "user", "content": step["user_template"].format(input=value)}
            ]
        )
        value = response.choices[0].message.content
    return value

steps = [
    {
        "system": "You extract the core technical problem from bug reports.",
        "user_template": "Bug report:\n{input}\n\nCore problem in one sentence:"
    },
    {
        "system": "You generate 3 hypotheses for a given technical problem.",
        "user_template": "Problem: {input}\n\nList 3 possible root causes:"
    },
    {
        "system": "You write a systematic debugging plan.",
        "user_template": "Hypotheses:\n{input}\n\nDebugging steps:"
    }
]

result = chain(steps, bug_report_text)

When to chain vs single prompt:

Use chaining when… Use single prompt when…
Task has distinct phases Task is simple and coherent
You need to validate between steps Latency matters more than quality
Different roles/expertise per step Token context is sufficient
You want to log intermediate results Cost is a concern

ReAct (Reasoning + Acting)

The model alternates between reasoning about what to do and taking actions (calling tools). Basis for most modern AI agents.

system = """You have access to these tools:
- search(query): Search the web for current information
- calculate(expression): Evaluate a math expression
- get_weather(city): Get current weather

To use a tool, write:
Thought: [your reasoning]
Action: tool_name(argument)

When you have the final answer, write:
Thought: I now have enough information.
Answer: [final answer]"""

user = "What is the current temperature in Tokyo in Fahrenheit?"

# Model response might be:
# Thought: I need to get Tokyo's current weather.
# Action: get_weather(Tokyo)
# [system injects: "22°C, partly cloudy"]
# Thought: I need to convert 22°C to Fahrenheit.
# Action: calculate(22 * 9/5 + 32)
# [system injects: "71.6"]
# Thought: I have the answer.
# Answer: The current temperature in Tokyo is 71.6°F (22°C), partly cloudy.

Self-consistency

Run the same prompt multiple times, then take the most common answer. Significantly improves accuracy on reasoning tasks.

from collections import Counter

def self_consistent_answer(prompt: str, n: int = 5) -> str:
    """Get the most consistent answer across n runs."""
    answers = []
    for _ in range(n):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7  # Increase variation between runs
        )
        answers.append(response.choices[0].message.content.strip())
    
    # Return most common answer
    return Counter(answers).most_common(1)[0][0]

# For classification tasks, parse the label before counting

Self-consistency tradeoffs:

Aspect Value
Accuracy improvement +5–15% on reasoning tasks
Cost multiplier n× (5 runs = 5× cost)
Latency Can parallelize all n calls
Best for Math, logic, factual classification
Worst for Creative tasks, open-ended generation

Practical patterns for developers

Classification with confidence

system = """Classify customer support tickets.
Categories: billing | technical | account | general
Confidence: high | medium | low

Return JSON:
{"category": "...", "confidence": "...", "reason": "..."}"""

Extraction with fallback

system = """Extract the requested fields from the text.
If a field is not present, use null.
Never hallucinate values that are not in the text.

Return JSON: {"name": string|null, "email": string|null, "phone": string|null}"""

Summarization with length control

system = """Summarize the following text.
Rules:
- Maximum 3 bullet points
- Each bullet maximum 15 words
- Start each bullet with an action verb
- Do not include information not in the text"""

Code generation with constraints

system = """You generate Python functions.
Rules:
- Use type hints on all parameters and return values
- Include a docstring with Args and Returns sections
- Handle edge cases with clear ValueError messages
- Write idiomatic Python 3.11+
- Do not use deprecated APIs"""

Translation with terminology

system = """You are a technical translator from English to German.
Rules:
- Preserve all code snippets exactly (do not translate code)
- Keep technical terms in English unless they have a standard German translation
- Use formal register (Sie, not du)
- Preserve markdown formatting"""

Model comparison for prompting

Model Instruction following JSON output CoT strength Context window
GPT-4o Excellent Native JSON mode Strong 128k
GPT-4o mini Good JSON mode Moderate 128k
Claude Opus 4.5 Excellent Reliable Very strong 200k
Claude Sonnet 4.6 Excellent Reliable Strong 200k
Claude Haiku 4.5 Good Reliable Moderate 200k
Gemini 1.5 Pro Good Requires prompting Strong 1M
Llama 3.1 70B Moderate Requires prompting Moderate 128k

Measuring prompt quality

Never ship a prompt without evaluation. Even a 5-example manual test catches most regressions.

test_cases = [
    {"input": "...", "expected": "..."},
    {"input": "...", "expected": "..."},
]

def evaluate_prompt(system: str, test_cases: list[dict]) -> float:
    correct = 0
    for case in test_cases:
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": system},
                {"role": "user", "content": case["input"]}
            ]
        )
        output = response.choices[0].message.content.strip()
        if output == case["expected"]:
            correct += 1
    return correct / len(test_cases)

Evaluation metrics by task type:

Task Metric
Classification Accuracy, F1
Extraction Exact match, token F1
Summarization ROUGE, human rating
Code generation Test pass rate
Open-ended LLM-as-judge (GPT-4 grades outputs)

Common mistakes table

Mistake Problem Fix
Vague instructions "Write a good summary" produces inconsistent results Specify length, format, and what "good" means
Overloading one prompt Complex tasks have too many failure points Use prompt chaining
No output format Model chooses its own structure Always specify format and provide an example
Ignoring temperature High temp for structured output → invalid JSON Use temperature=0 for structured output
Not testing edge cases Prompt works for common inputs, fails on edge cases Include edge-case examples in few-shot
Prompt injection risk User input overrides system instructions Separate system and user content clearly
Negative-only instructions "Don't mention X" is less reliable than "Only discuss Y" Reframe as positive constraints
No fallback handling API errors or bad output crash the app Validate output; retry with simplified prompt

Prompt engineering vs fine-tuning

Prompt engineering Fine-tuning
Setup cost Zero High (data + compute)
Expertise needed Low High (ML knowledge)
Iteration speed Minutes Hours to days
Best for New tasks, low data High-volume, consistent format
Portability Stays with your code Tied to model version
Output consistency Variable High
When to switch When prompt engineering plateaus

Rule of thumb: Exhaust prompt engineering before fine-tuning. Fine-tuning is often 10× more effort than a well-engineered prompt.


Frequently asked questions

What is the difference between zero-shot and few-shot prompting?

Zero-shot gives the model only the task description. Few-shot includes 2–8 input/output examples before the actual input. Few-shot is more reliable for unusual formats or domain-specific tasks; zero-shot works well for tasks the model was extensively trained on.

How many few-shot examples should I include?

Start with 3, test with 5, and rarely go beyond 8. More examples increase cost and token usage. If accuracy plateaus after 5 examples, consider fine-tuning instead.

When should I use chain-of-thought prompting?

Use CoT for multi-step reasoning tasks: arithmetic, logic puzzles, debugging, and anything requiring intermediate steps. It has little benefit — and wastes tokens — on simple classification or lookup tasks.

How do I prevent prompt injection attacks?

Clearly separate system instructions from user content. Use structured formats (XML or JSON delimiters) to demarcate untrusted input. Validate outputs and never pass raw LLM output directly to sensitive systems (databases, file systems, APIs).

What temperature should I use?

Use temperature=0 for deterministic tasks (JSON extraction, classification, code generation). Use 0.3–0.7 for creative tasks. Use 0.7–1.0 for brainstorming or when you want diverse outputs. Never use high temperature with structured output requirements.

Is prompt engineering still relevant now that models are smarter?

Yes. Smarter models follow better instructions — which means prompting skill matters more, not less. The gap between a carefully engineered prompt and a vague one widens with model capability. Additionally, structured output, latency, and cost optimization require deliberate prompt design regardless of model quality.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools