Prompt Engineering
Write effective prompts using few-shot examples, chain-of-thought reasoning, role prompting, and structured output techniques.
Real-World Scenario
An ML engineer at a legal-tech startup needs to extract contract clauses, classify risk levels, and generate plain-language summaries from 10,000 contracts. The accuracy of a naive prompt is 62%. After applying few-shot examples, chain-of-thought reasoning, and output format constraints, accuracy reaches 91% — without changing the model or fine-tuning. Prompt engineering is free performance.
Zero-Shot Prompting
The simplest approach: describe the task clearly and let the model generalize.
import anthropic
client = anthropic.Anthropic()
def zero_shot(task_description: str, input_text: str) -> str:
return client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
messages=[{
"role": "user",
"content": f"{task_description}\n\nInput: {input_text}"
}]
).content[0].text
# Classification
result = zero_shot(
task_description="Classify the sentiment of the following review as POSITIVE, NEGATIVE, or NEUTRAL.",
input_text="The battery life is amazing but the screen has terrible glare."
)
print(result) # MIXED or NEUTRAL (depends on model judgment)
# Extraction
result = zero_shot(
task_description="Extract the product name, price, and availability from the following text.",
input_text="The Sony WH-1000XM5 headphones are currently in stock at $349.99."
)
print(result)
Few-Shot Prompting
Show the model worked examples to constrain the output format and improve accuracy.
import anthropic
client = anthropic.Anthropic()
FEW_SHOT_SYSTEM = """You classify customer support tickets into categories.
Categories: BILLING, TECHNICAL, SHIPPING, RETURNS, ACCOUNT, OTHER
Examples:
Input: "I was charged twice for my order #12345"
Output: BILLING
Input: "My app keeps crashing when I try to open the dashboard"
Output: TECHNICAL
Input: "My package hasn't arrived — it was supposed to be delivered yesterday"
Output: SHIPPING
Input: "I want to return the item I bought last week, it doesn't fit"
Output: RETURNS
Reply with only the category name, nothing else."""
def classify_ticket(ticket: str) -> str:
return client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=20,
system=FEW_SHOT_SYSTEM,
messages=[{"role": "user", "content": ticket}]
).content[0].text.strip()
tickets = [
"I can't log in — my password reset email never arrived",
"The invoice shows $200 but I was charged $250",
"How do I export my data before I cancel?",
]
for ticket in tickets:
category = classify_ticket(ticket)
print(f"{category:12s} | {ticket}")
Chain-of-Thought Reasoning
For complex reasoning tasks, asking the model to think step-by-step dramatically improves accuracy — the model gets to “show its work” before committing to an answer.
import anthropic
client = anthropic.Anthropic()
STANDARD_PROMPT = """
A company had 240 employees at the start of Q1.
They hired 15% more in Q1, laid off 20 people in Q2,
and grew 10% in Q3.
How many employees do they have now?
Answer with just the number.
"""
COT_PROMPT = """
A company had 240 employees at the start of Q1.
They hired 15% more in Q1, laid off 20 people in Q2,
and grew 10% in Q3.
How many employees do they have now?
Think through each step carefully before giving your final answer.
Format your response as:
Step 1: [calculation]
Step 2: [calculation]
...
Final answer: [number]
"""
def ask(prompt: str) -> str:
return client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
messages=[{"role": "user", "content": prompt}]
).content[0].text
print("Standard:", ask(STANDARD_PROMPT))
print("\nChain-of-thought:")
print(ask(COT_PROMPT))
Role Prompting
Assigning the model a specific expert persona shapes the style, depth, and focus of responses.
import anthropic
client = anthropic.Anthropic()
PERSONAS = {
"security_engineer": """You are a senior security engineer with 10 years of experience.
When reviewing code, you focus exclusively on security vulnerabilities:
SQL injection, XSS, authentication flaws, insecure deserialization,
secret exposure, and OWASP Top 10. Be direct, specific, and actionable.
Rate severity as CRITICAL, HIGH, MEDIUM, or LOW.""",
"staff_engineer": """You are a Staff Engineer with expertise in system design and code quality.
When reviewing code, focus on: scalability, maintainability, performance,
and architectural soundness. Avoid nitpicking style issues — focus on
decisions that will matter at 10x scale.""",
"junior_tutor": """You are a patient programming tutor helping beginners.
Use simple language, avoid jargon, explain every concept from scratch,
and always include analogies to everyday life.""",
}
def review_with_persona(code: str, persona: str) -> str:
return client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=PERSONAS[persona],
messages=[{"role": "user", "content": f"Review this code:\n\n```python\n{code}\n```"}]
).content[0].text
code = """
def get_user(username):
query = f"SELECT * FROM users WHERE username = '{username}'"
return db.execute(query).fetchone()
"""
print("Security review:")
print(review_with_persona(code, "security_engineer"))
Output Format Constraints
Forcing a specific structure makes LLM output programmatically reliable.
import anthropic
import json
from typing import TypedDict
client = anthropic.Anthropic()
class ContractAnalysis(TypedDict):
contract_type: str
parties: list[str]
effective_date: str | None
key_obligations: list[str]
termination_clauses: list[str]
risk_level: str # LOW | MEDIUM | HIGH
risk_reasons: list[str]
def analyze_contract(contract_text: str) -> ContractAnalysis:
system = """You are a contract lawyer. Analyze contracts and return ONLY valid JSON.
The JSON must exactly match this schema — no extra fields, no markdown, no explanation:
{
"contract_type": "string",
"parties": ["string"],
"effective_date": "YYYY-MM-DD or null",
"key_obligations": ["string"],
"termination_clauses": ["string"],
"risk_level": "LOW | MEDIUM | HIGH",
"risk_reasons": ["string"]
}"""
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=system,
messages=[{"role": "user", "content": contract_text}]
)
return json.loads(response.content[0].text)
sample_contract = """
SERVICE AGREEMENT
Between Acme Corp ("Client") and TechVendor Inc ("Provider")
Effective Date: March 1, 2025
Provider agrees to deliver software development services at $150/hour.
Client shall pay within 30 days of invoice. Provider may terminate with
14 days notice. Liability is limited to fees paid in the prior 3 months.
Auto-renews annually unless 60 days notice is given.
"""
analysis = analyze_contract(sample_contract)
print(json.dumps(analysis, indent=2))
Prompt Templates
from string import Template
import anthropic
client = anthropic.Anthropic()
# Use string templates for reusable prompts with variable injection
SUMMARIZE_TEMPLATE = Template("""
Summarize the following $content_type for a $audience audience.
Constraints:
- Maximum $max_words words
- Use bullet points for key findings
- End with one actionable recommendation
Content:
$content
""")
def summarize(content: str, content_type: str, audience: str, max_words: int = 100) -> str:
prompt = SUMMARIZE_TEMPLATE.substitute(
content_type=content_type,
audience=audience,
max_words=max_words,
content=content,
)
return client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=512,
messages=[{"role": "user", "content": prompt}]
).content[0].text
article = """
A new study from MIT found that developers who write tests before code (TDD)
produce 40% fewer bugs and spend 15% less time on debugging. The study tracked
120 engineers across 6 months. However, TDD teams initially shipped 20% slower
before catching up by month 3.
"""
print(summarize(article, "research paper", "engineering manager", max_words=75))
Common Mistakes
1. Vague instructions — the model guesses your intent:
# Bad
"Write something about Python."
# Good
"Write a 3-sentence explanation of Python's GIL for a Java developer
who is evaluating Python for a high-concurrency web service."
2. No output format specification:
# Bad: returns a paragraph, hard to parse programmatically
"Extract the price from this text: ..."
# Good: constrains the format
"Extract the price from this text and return ONLY the number with no currency symbol or text.
If no price is found, return null.
Text: ..."
3. Contradictory instructions:
# Bad: model has to choose between brief and thorough
"Write a brief and thorough explanation of neural networks."
# Good: be explicit about the tradeoff
"Write a 3-paragraph explanation of neural networks. Cover what they are,
how they learn, and one real-world use case. Skip mathematical notation."