Engineering Prompts
Engineering Prompts
Systematically design prompts using proven patterns instead of trial-and-error iteration, to achieve reliable outputs and reduce cost.
Pick a technique based on the task, don't default to "just ask nicely":
Python# Structured extraction: use JSON mode, not text parsing from openai import OpenAI client = OpenAI() response = client.chat.completions.create( model="gpt-4", messages=[ {"role": "system", "content": "Extract user data as JSON."}, {"role": "user", "content": "Sarah, 28, sarah@example.com"} ], response_format={"type": "json_object"}, temperature=0 )
TypeScript// TypeScript: schema-validated structured output import { generateObject } from 'ai'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; const { object } = await generateObject({ model: openai('gpt-4'), schema: z.object({ name: z.string(), age: z.number() }), prompt: 'Extract: "Sarah, 28"', });
Need structured JSON? → JSON mode / tool calling
Complex reasoning? → Chain-of-thought ("think step by step")
Specific format/style? → Few-shot (2-5 examples)
Knowledge from documents? → RAG
Multi-step workflow? → Prompt chaining
Agent using tools? → Tool use / ReAct
Simple, well-defined task? → Zero-shot
| Technique | Cost | Reliability | Use Case |
|---|---|---|---|
| Zero-shot | Minimal | Medium | Translation, simple summarization |
| Few-shot | Medium | High | Classification, entity extraction |
| Chain-of-thought | Higher | Very High | Math, logic, multi-hop QA |
| JSON mode / tools | Low-Med | Very High | API responses, data extraction |
| Prompt chaining | Medium | High | Pipelines, complex tasks |
| RAG | Higher | High | QA over documents |
| ReAct (tool use) | Highest | Medium | Multi-tool agent tasks |
Progress:
- Step 1: Identify task type using the decision framework above
- Step 2: Draft prompt with explicit instructions, format, and constraints
- Step 3: Add examples (few-shot) or reasoning scaffold (CoT) if needed
- Step 4: Set
temperature=0for deterministic tasks; add schema/tool validation for structured output - Step 5: Test against edge cases; check for injection vulnerabilities
- Step 6: Version the prompt and log token usage/cost
- Step 7: If porting across models, adjust for provider-specific style (see below)
Zero-shot: Clear imperative instruction + input + output format. Use for simple, well-defined tasks.
Pythonprompt = """Summarize the following review in 2 sentences, focusing on key concerns: Review: {text} Summary:"""
Few-shot: Task description + 2-5 diverse examples + actual task. Quality over quantity; randomize example order to avoid position bias; explicitly label edge cases.
Pythonprompt = """Classify sentiment. Review: "Absolutely fantastic!" → Sentiment: positive Review: "Waste of time." → Sentiment: negative Review: "It was okay." → Sentiment: neutral Review: "{new_review}" → Sentiment:"""
Chain-of-thought: Add "Let's think step by step" for zero-shot CoT, or provide worked examples showing reasoning for few-shot CoT. Use for math, logic, multi-hop reasoning. Yields 20-50% accuracy gains on reasoning benchmarks (Wei et al. 2022).
Structured output: Prefer native JSON mode or tool/function calling over asking the model to "output JSON" in free text — avoids parsing failures.
Python# Anthropic tool use for structured extraction tools = [{ "name": "record_data", "description": "Record structured user information", "input_schema": { "type": "object", "properties": {"name": {"type": "string"}, "age": {"type": "integer"}}, "required": ["name", "age"] } }]
System prompts/personas: Structure as Role → Capabilities → Behavior guidelines → Output format → Safety boundaries. Keep under 1000 tokens. Version control like code. Claude responds well to XML tags (<capabilities>, <guidelines>).
Tool use: Write specific, actionable tool descriptions — vague descriptions cause the model to misuse or ignore tools.
Python# BAD: "description": "Search for stuff" # GOOD: "description": "Search knowledge base for product docs. Use when user asks about features or troubleshooting. Returns top 5 articles."
Prompt chaining: Break complex tasks into sequential prompts (output of step N feeds step N+1). Improves debuggability and enables prompt caching for cost reduction (Anthropic cache_control: ephemeral gives ~90% cost reduction on repeated large context).
Example 1:
Input: "My JSON parsing keeps failing when extracting data from LLM output."
Output: Switch from free-text JSON instructions to native JSON mode (OpenAI response_format={"type": "json_object"}) or tool calling (Anthropic) with a defined schema; validate with Zod/Pydantic; set temperature=0.
Example 2: Input: "Model gives wrong answers on multi-step math word problems." Output: Apply zero-shot chain-of-thought: append "Let's think through this step by step" before asking for the final answer, or provide 2-3 few-shot examples with explicit reasoning chains.
Example 3: Input: "Need consistent classification labels across thousands of support tickets." Output: Use few-shot prompting with 3-5 representative examples per label class, consistent formatting, and explicit handling of edge cases (e.g., ambiguous/mixed sentiment).
- Set
temperature=0for deterministic/structured tasks; raise it only for creative generation. - Use native structured-output features (JSON mode, tool calling, Zod/Pydantic schemas) instead of parsing free text.
- Version prompts like code; track changes and rationale.
- Log token usage and cost per call; monitor for regressions.
- Wrap LLM calls with retry logic (exponential backoff) for rate limits/transient errors.
- Sanitize user input to guard against prompt injection (e.g., reject "ignore previous instructions" patterns, keep user content clearly delimited from instructions).
- Write automated test cases (expected-contains / should-not-contain assertions) before shipping prompt changes.
- Match style to provider: Claude favors XML structure and detailed instructions; GPT-4 favors concise system messages; open-source models (Llama) need more explicit instructions and benefit heavily from few-shot examples.
- Vague instructions ("Analyze this data") instead of specific, structured asks ("Identify top 3 products, growth trends, anomalies; present as table").
- Interpolating raw user input directly into prompts without delimiting it from system instructions — opens injection risk.
- Asking the model to "output valid JSON" in prose instead of using JSON mode/tool calling — causes brittle parsing.
- Using few-shot examples that are too similar (no diversity) or too many (diminishing returns beyond ~5).
- Not setting
temperature=0for tasks requiring reproducibility. - Copy-pasting the same prompt across model providers without adjusting style — Claude/GPT/Gemini/Llama respond differently to structure and verbosity.
- Skipping intermediate output inspection in prompt chains, making failures hard to debug.
- Ignoring cost: not leveraging prompt caching for large repeated context, or not monitoring token usage in production.