Stackvora.tech

Prompt Engineering Patterns That Actually Work

Effectively interacting with large language models (LLMs) moves beyond simple queries into a structured discipline known as prompt engineering. This…

Effectively interacting with large language models (LLMs) moves beyond simple queries into a structured discipline known as prompt engineering. This discipline focuses on crafting inputs that elicit precise, reliable, and desired outputs from these powerful, yet often unpredictable, models. This article outlines five practical, battle-tested patterns that significantly enhance LLM interaction, moving from basic conversation to robust, production-ready applications.

The core idea behind these patterns is to reduce ambiguity and guide the model towards specific reasoning paths and output formats. Understanding and applying these techniques can transform LLM usage from an experimental curiosity into a dependable component of automated workflows and intelligent systems.

1. The System Prompt: Role, Goal, and Constraints

The system prompt is the foundation of any predictable LLM interaction. It sets the context, defines the model's persona, and establishes boundaries for its behavior. This is particularly crucial for API interactions where the "system" role is explicitly defined. A well-crafted system prompt typically includes three key components:

  • Role: Assigns a persona or identity to the model (e.g., "You are a senior Python developer," "You are a customer support agent specialized in network diagnostics."). This helps the model align its tone, knowledge, and response style.
  • Goal: Clearly states the primary objective of the interaction (e.g., "Your goal is to refactor legacy code snippets," "Your goal is to provide concise, accurate answers to common troubleshooting questions."). This focuses the model's efforts.
  • Constraints: Specifies limitations, formatting requirements, or forbidden actions (e.g., "Do not invent non-existent APIs," "Always respond in markdown format," "Never reveal sensitive customer information."). These are critical for safety, compliance, and output consistency.

Example System Prompt (for a code assistant):

You are a senior Python developer. Your goal is to refactor provided Python code snippets to improve readability, performance, and adherence to PEP 8 standards.

Constraints:
 - Only refactor the code; do not add new functionality unless explicitly requested.
 - Provide refactored code directly, enclosed in triple backticks (```python ... ```).
 - If the code is already optimal or cannot be improved significantly, state "No significant improvements needed."
 - Do not include conversational filler; get straight to the code or assessment.
 - Assume Python 3.9+ syntax and features.

This system prompt gives the LLM a clear mandate, helping it understand its purpose and boundaries before processing any user input.

2. Few-Shot Learning: Examples Over Explications

For tasks requiring specific output formats, stylistic nuances, or pattern recognition, providing a few examples (2-5, often) is significantly more effective than lengthy, verbose instructions. This technique, known as few-shot learning, leverages the LLM's ability to infer patterns from demonstrations.

When using few-shot, present pairs of input and desired output directly within the prompt. The model then uses these examples to generalize to new, unseen inputs.

Use Cases:

  • Data extraction into a specific format (e.g., JSON, YAML).
  • Sentiment analysis with custom categories.
  • Text summarization with a target length or style.
  • Code generation for repetitive patterns.

Example (Sentiment Analysis):

You are a sentiment analyzer. Classify the sentiment of the following text snippets as 'Positive', 'Negative', or 'Neutral'.

Input: "The new UI is a mess, completely unusable."
Output: Negative

Input: "I found the documentation surprisingly thorough and easy to follow."
Output: Positive

Input: "The system rebooted unexpectedly last night, but recovered quickly."
Output: Neutral

Input: "This feature update dramatically improved our workflow efficiency."
Output:

Instead of trying to describe "what positive sentiment means," the examples directly demonstrate it, which is a much more robust way to guide the model.

3. Chain-of-Thought (CoT): Deconstructing Complexity

Complex problems often lead to incorrect or incomplete LLM responses, especially when the model needs to perform multiple logical steps. Chain-of-Thought (CoT) prompting encourages the model to articulate its reasoning process before providing the final answer. This not only improves accuracy but also makes the model's "thought" process transparent, aiding debugging and understanding.

The simplest way to implement CoT is to append phrases like "Let's think step by step," "Explain your reasoning," or "Walk me through your process" to the prompt. For more advanced CoT, you can include examples of step-by-step reasoning (few-shot CoT).

Example (Mathematical Reasoning):

Problem: A server farm has 12 racks. Each rack has 24 U of space. If each server occupies 2U, how many servers can fit in 3 racks?

Let's think step by step:
1. First, calculate the total U space in one rack: 24 U.
2. Next, determine how many servers fit in one rack: 24 U / 2 U per server = 12 servers.
3. Then, calculate the total number of servers for 3 racks: 12 servers/rack * 3 racks = 36 servers.
Therefore, 36 servers can fit in 3 racks.

Problem: A network switch has 48 ports. If 15% of the ports are used for uplinks and the remaining ports are for client devices, how many ports are available for client devices?

Let's think step by step:
1. First, calculate the number of ports used for uplinks: 48 ports * 0.15 = 7.2 ports. Since ports must be whole numbers, we'll assume 7 ports for uplinks, or round up to 8 depending on context for fractional ports. For this calculation, let's use 7.
2. Next, calculate the remaining ports: 48 ports - 7 ports (uplinks) = 41 ports.
Therefore, 41 ports are available for client devices.

By forcing the model to show its work, errors become more apparent, and the final answer is more likely to be correct. This is invaluable for tasks requiring logical deduction, multi-step calculations, or complex conditional logic.

4. JSON Mode with Schema: Structured Output for Reliability

When integrating LLMs into software systems, consistent and machine-readable output is paramount. "JSON Mode" (or similar explicit structured output requests) combined with a JSON schema provides the strongest guarantee for receiving parseable data. Many modern LLM APIs (e.g., OpenAI's GPT-4, Anthropic's Claude) offer dedicated JSON output modes that enforce valid JSON. Even without a specific mode, instructing the model to output JSON and providing a schema greatly improves reliability.

Always specify the desired JSON structure, ideally with a schema that defines types, required fields, and even enum values.

Example (Network Device Information Extraction):

You are a network device information extractor. Your goal is to parse the provided text about network devices and extract key details into a JSON object.

Constraints:
 - Output strictly valid JSON.
 - The JSON must adhere to the following schema. If a field is not found, omit it unless it's explicitly marked as required in the schema and you can infer a sensible default.
 - The 'status' field must be one of "Online", "Offline", "Degraded".

JSON Schema:
{
  "type": "object",
  "properties": {
    "device_name": { "type": "string", "description": "The name of the network device" },
    "device_type": { "type": "string", "description": "The type of device, e.g., 'Router', 'Switch', 'Firewall'" },
    "ip_address": { "type": "string", "format": "ipv4", "description": "The primary IP address of the device" },
    "firmware_version": { "type": "string", "description": "The firmware version running on the device" },
    "status": { "type": "string", "enum": ["Online", "Offline", "Degraded"], "description": "The current operational status" },
    "ports": { "type": "integer", "description": "Number of active network ports" }
  },
  "required": ["device_name", "device_type", "ip_address", "status"]
}

Text to parse:
"The core_router_01 (a Cisco ASR 9000) with IP 192.168.10.1 is currently online, running IOS-XR 7.3.1. It has 48 active ports. Another device, an old Juniper EX2200, IP 10.0.0.5, is currently offline."

Extracted JSON:

The explicit schema provides the model with detailed instructions on the expected output structure, making parsing robust even if the input text varies.

5. Self-Critique / Self-Refinement: The Second Pass for Quality

Even with the best prompts, LLMs can make mistakes. The self-critique pattern involves asking the LLM to review its own initial output against a set of criteria and then refine it. This "second pass" often catches errors, improves completeness, or ensures adherence to complex guidelines. It's especially useful for tasks requiring high accuracy or adherence to strict policies.

The process typically involves two turns:

  1. Initial Generation: The LLM produces its first attempt.
  2. Critique and Refinement: The LLM is then prompted to review its previous answer, identify shortcomings based on new instructions or the original prompt's constraints, and provide a revised output.

Example (Code Review and Refinement):

--- TURN 1: Initial Code Generation ---
System: You are a Python code assistant. Generate a function to calculate the nth Fibonacci number.

User: Generate a Python function for the nth Fibonacci number using recursion.

Assistant:
```python
def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)
```

--- TURN 2: Self-Critique and Refinement ---
User: Review the `fibonacci` function you just provided. Does it handle large 'n' efficiently? Are there any potential issues with recursion depth or performance for calculating, say, fibonacci(30)? If not, provide an improved, more efficient version, perhaps using iteration or memoization. Explain your thought process.

Assistant:
My previous recursive implementation of `fibonacci` is correct for small `n` but is highly inefficient for larger `n` due to redundant calculations (O(2^n) time complexity) and potential recursion depth limits. For `fibonacci(30)`, it would be very slow.

A more efficient approach is to use iteration or memoization. Here's an iterative version, which has O(n) time complexity and O(1) space complexity:

```python
def fibonacci_iterative(n):
    if n < 0:
        raise ValueError("Input must be a non-negative integer.")
    elif n <= 1:
        return n
    else:
        a, b = 0, 1
        for _ in range(2, n + 1):
            a, b = b, a + b
        return b
```
This iterative approach avoids redundant calculations and recursion overhead, making it suitable for larger values of `n`.

This iterative process allows the LLM to correct its own errors, particularly when evaluating against non-trivial criteria like efficiency or best practices.

Common Pitfalls

  • Over-constraining: Too many rigid rules can sometimes make the model "freeze" or respond with "I cannot fulfill this request" if it detects conflicting constraints. Start with essential constraints and add more incrementally.
  • Ambiguous language: Even with patterns, vague terms like "good summary" or "better code" can lead to inconsistent results. Be as specific as possible. Define what "good" or "better" means in your context.
  • Ignoring context window limits: Especially with few-shot examples or long Chain-of-Thought reasoning, be mindful of the maximum context length of the model you're using. Truncation can lead to incomplete or nonsensical responses. (e.g., GPT-3.5-turbo has 16k tokens, GPT-4 has up to 128k depending on model variant).
  • Forgetting the user role: In API interactions, ensure that user-provided input is always delivered via the 'user' role, not merged into the 'system' prompt, to maintain clarity for the model.
  • Lack of negative examples: While few-shot focuses on positive examples, occasionally demonstrating what *not* to do can also be helpful, especially for subtle nuances or safety.

Back to the knowledge base · Ask the AI assistant