Setup

Before working with LLMs programmatically, you need an API client and a reusable helper function. The OpenAI client handles authentication and HTTP communication with the API, while the ask_llm() wrapper abstracts away the chat completion boilerplate so each example can focus on the prompt itself. The temperature parameter controls randomness: 0.0 produces deterministic outputs (best for classification and extraction), while higher values (0.7-1.0) increase creativity (better for brainstorming and creative writing). Loading the API key from a .env file via python-dotenv keeps credentials out of your code.

# Install required packages
# !pip install openai anthropic langchain python-dotenv
import os
from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

def ask_llm(prompt, model="gpt-3.5-turbo", temperature=0.7):
    """Helper function to query LLM."""
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        temperature=temperature
    )
    return response.choices[0].message.content

Example 1: Basic vs. Improved Prompt

The single most impactful technique in prompt engineering is specificity. A vague prompt like “Tell me about Python” gives the model too many degrees of freedom – it might discuss the language’s history, syntax, ecosystem, or philosophy. A well-structured prompt constrains the scope (list comprehensions), specifies the audience (beginner), defines the format (numbered list), and sets a length limit (150 words). The difference in output quality is dramatic, and this applies to every LLM task from summarization to code generation.

# ❌ Vague prompt
bad_prompt = "Tell me about Python"

# ✅ Specific prompt
good_prompt = """
Explain Python's list comprehensions to a beginner programmer.
Include:
1. What they are (1-2 sentences)
2. Basic syntax
3. One simple example
4. One common use case

Keep the explanation under 150 words.
"""

print("=== Vague Prompt ===")
print(ask_llm(bad_prompt)[:200], "...\n")

print("=== Specific Prompt ===")
print(ask_llm(good_prompt))

Example 2: Few-Shot Learning

Few-shot prompting provides input-output examples directly in the prompt, teaching the model the desired behavior through demonstration rather than description. By showing two examples of sentence-to-JSON conversion, the model learns the exact output schema, key names, and extraction logic without any fine-tuning. This technique leverages the model’s in-context learning ability – a property where transformers can infer patterns from examples provided at inference time. Few-shot prompting is especially effective for structured extraction, classification, and format conversion tasks.

few_shot_prompt = """
Convert the following sentences to JSON format.

Example 1:
Input: "John lives in New York and works as a doctor."
Output: {"name": "John", "city": "New York", "occupation": "doctor"}

Example 2:
Input: "Sarah is from London and she is a teacher."
Output: {"name": "Sarah", "city": "London", "occupation": "teacher"}

Now convert this:
Input: "Mike lives in Tokyo and works as an engineer."
Output:
"""

print(ask_llm(few_shot_prompt, temperature=0))

Example 3: Chain-of-Thought Reasoning

Chain-of-thought (CoT) prompting improves accuracy on multi-step reasoning tasks by asking the model to show its work. Adding “Let’s solve this step by step” to a math problem causes the model to decompose the problem into intermediate steps, dramatically reducing errors. The intuition is that LLMs are autoregressive – each token is conditioned on all previous tokens – so generating intermediate reasoning steps gives the model a “scratch pad” that guides subsequent tokens toward the correct answer. CoT is most beneficial for arithmetic, logic, commonsense reasoning, and any task requiring more than one inference step.

# Without CoT
simple_prompt = """
A store had 20 apples. They sold 12 apples in the morning and 5 in the afternoon.
Then they received a delivery of 15 apples. How many apples do they have now?
"""

# With CoT
cot_prompt = simple_prompt + "\nLet's solve this step by step:"

print("=== Without Chain-of-Thought ===")
print(ask_llm(simple_prompt, temperature=0), "\n")

print("=== With Chain-of-Thought ===")
print(ask_llm(cot_prompt, temperature=0))

Example 4: System Prompts

The system prompt is a special message that sets the model’s persona, behavior constraints, and response style for the entire conversation. Unlike user messages, the system prompt persists across turns and acts as persistent instructions. By changing the system prompt from “computer science professor” to “patient teacher for beginners,” you get fundamentally different explanations of the same concept – technical depth versus accessible analogies. In production applications, system prompts define the core personality and guardrails of your AI feature, making them one of the most important design decisions.

def ask_with_system(system_prompt, user_prompt):
    """Use system prompt to set behavior."""
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt}
        ],
        temperature=0.7
    )
    return response.choices[0].message.content

# Example: Expert vs. Beginner explanations
question = "What is recursion in programming?"

expert_system = "You are a computer science professor. Explain concepts with technical precision and examples."
beginner_system = "You are a patient teacher for absolute beginners. Use simple language and everyday analogies."

print("=== Expert Mode ===")
print(ask_with_system(expert_system, question), "\n")

print("=== Beginner Mode ===")
print(ask_with_system(beginner_system, question))

Example 5: Structured Output

Requesting structured output (JSON, XML, CSV) transforms an LLM from a free-text generator into a reliable data extraction tool. The prompt below specifies an exact JSON schema with field names, value constraints, and data types, then instructs the model to return only JSON with no surrounding text. The result can be parsed with json.loads() and consumed by downstream code. This technique is the foundation of LLM-powered data pipelines, where unstructured text (reviews, emails, documents) is systematically converted into structured records for databases and analytics.

structured_prompt = """
Analyze the following product review and extract information in this exact JSON format:

{
  "sentiment": "positive/negative/neutral",
  "rating_guess": "1-5",
  "key_points": ["point1", "point2"],
  "mentioned_features": ["feature1", "feature2"]
}

Review: "This laptop is amazing! Super fast processor and the battery lasts all day. 
Only complaint is it's a bit heavy to carry around."

Return ONLY the JSON, no other text:
"""

result = ask_llm(structured_prompt, temperature=0)
print(result)

# Parse as JSON
import json
data = json.loads(result)
print("\nParsed data:", data)

Key Takeaways

1. Be Specific

  • Clear instructions

  • Defined format

  • Constraints and requirements

2. Use Examples (Few-Shot)

  • Show desired input/output

  • 2-5 examples work well

  • Consistent format

3. Add Reasoning (CoT)

  • “Let’s think step by step”

  • “First…, then…, finally…”

  • Better for complex tasks

4. Set Context (System Prompts)

  • Define role and behavior

  • Set tone and style

  • Add constraints

5. Structure Output

  • Specify exact format

  • Use delimiters

  • Request JSON/XML/etc.

Next Steps

  1. 01_basic_prompting.ipynb - Master fundamental techniques

  2. 02_chain_of_thought.ipynb - Advanced reasoning

  3. 03_react_prompting.ipynb - Tool use and actions

  4. Practice! - Try improving prompts you use daily

Remember: Prompt engineering is iterative. Test, analyze, and refine! 🚀