All skills
jeffallan avatar

/prompt-engineer

@efebc44
by jeffallanjeffallan/claude-skills12k stars
1,127

Writes, refactors, and evaluates prompts for LLMs — generating optimized prompt templates, structured output schemas, evaluation rubrics, and test suites. Use when designing prompts for new LLM applications, refactoring existing prompts for better accuracy or token efficiency, implementing chain-of-thought or few-shot learning, creating system prompts with personas and guardrails, building JSON/function-calling schemas, or developing prompt evaluation frameworks to measure and improve model performance.

Use this Skill: https://skilld.dev/gh/jeffallan/claude-skills/prompt-engineer

This session only. Nothing lands on disk.

referencesstructured-outputs.md

≈4.8k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Structured Outputs


Structured Output Methods

┌─────────────────────────────────────────────────────────────────────────────┐
│                    STRUCTURED OUTPUT APPROACHES                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐            │
│  │   Prompt-Based  │  │   JSON Mode     │  │ Function Calling│            │
│  │                 │  │                 │  │ (Tool Use)      │            │
│  ├─────────────────┤  ├─────────────────┤  ├─────────────────┤            │
│  │ Reliability: ~  │  │ Reliability: ++ │  │ Reliability: +++│            │
│  │ Flexibility: +++│  │ Flexibility: ++ │  │ Flexibility: +  │            │
│  │ Validation: --- │  │ Validation: +   │  │ Validation: +++ │            │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘            │
│                                                                             │
│  Use when:          Use when:             Use when:                        │
│  - Simple extracts  - Need valid JSON     - Strict schemas required        │
│  - Flexible schemas - Moderate complexity - Tool orchestration             │
│  - Quick prototypes - Claude/GPT models   - Type-safe parsing              │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Prompt-Based Structured Output

Basic JSON Request

Extract the following information from the text and return as JSON:
- person_name: string
- company: string
- role: string
- email: string or null

Text: {text}

Return only valid JSON, no other text:

With Schema Definition

Extract meeting information from the transcript.

Return a JSON object matching this schema:
{
  "meeting_title": "string - the main topic discussed",
  "date": "string - ISO 8601 format (YYYY-MM-DD) or null if not mentioned",
  "attendees": ["array of strings - names of participants"],
  "action_items": [
    {
      "task": "string - what needs to be done",
      "assignee": "string - who is responsible",
      "due_date": "string - ISO 8601 format or null"
    }
  ],
  "decisions": ["array of strings - key decisions made"],
  "next_meeting": "string - ISO 8601 datetime or null"
}

Rules:
- Use null for fields not mentioned in the transcript
- Use empty arrays [] for list fields with no items
- Dates must be in ISO 8601 format
- Return ONLY the JSON object, no explanation

Transcript:
{transcript}

Output Wrapping Technique

Analyze the code and identify issues. Return your analysis in this exact format:

<analysis>
{
  "summary": "one sentence overview",
  "issues": [
    {
      "severity": "critical|high|medium|low",
      "type": "bug|security|performance|style",
      "location": "file:line or function name",
      "description": "what's wrong",
      "suggestion": "how to fix"
    }
  ],
  "quality_score": 1-10
}
</analysis>

Code:
{code}

Parsing with tags:

import re
import json

def extract_tagged_json(response: str, tag: str = "analysis") -> dict:
    """Extract JSON from tagged output."""
    pattern = rf"<{tag}>\s*(.*?)\s*</{tag}>"
    match = re.search(pattern, response, re.DOTALL)

    if not match:
        raise ValueError(f"No <{tag}> tags found in response")

    return json.loads(match.group(1))

JSON Mode (Claude & OpenAI)

Claude JSON Mode

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-5-20251101",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": """Extract entities from this text and return as JSON.

            Required fields:
            - people: array of {name, role}
            - organizations: array of {name, type}
            - locations: array of strings

            Text: {text}"""
        }
    ],
    # Claude uses system prompt to enforce JSON
    system="You are a JSON extraction assistant. Always respond with valid JSON only, no other text."
)

# Parse the response
result = json.loads(response.content[0].text)

OpenAI JSON Mode

from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model="gpt-4-turbo-preview",
    response_format={"type": "json_object"},  # Enforces JSON output
    messages=[
        {
            "role": "system",
            "content": "Extract information and return as JSON."
        },
        {
            "role": "user",
            "content": f"Extract people and companies from: {text}"
        }
    ]
)

result = json.loads(response.choices[0].message.content)

JSON Mode Best Practices

Practice Why
Always describe expected schema Model needs to know structure
Specify null handling "Use null for missing fields"
Define array behavior "Return empty array if none found"
Include field descriptions Improves extraction accuracy
Add type annotations "date: string in YYYY-MM-DD format"

Function Calling / Tool Use

Claude Tool Use

import anthropic

client = anthropic.Anthropic()

# Define the tool schema
tools = [
    {
        "name": "extract_contact",
        "description": "Extract contact information from text",
        "input_schema": {
            "type": "object",
            "properties": {
                "name": {
                    "type": "string",
                    "description": "Full name of the person"
                },
                "email": {
                    "type": "string",
                    "description": "Email address"
                },
                "phone": {
                    "type": "string",
                    "description": "Phone number in E.164 format"
                },
                "company": {
                    "type": "string",
                    "description": "Company or organization name"
                },
                "title": {
                    "type": "string",
                    "description": "Job title or role"
                }
            },
            "required": ["name"]
        }
    }
]

response = client.messages.create(
    model="claude-opus-4-5-20251101",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "extract_contact"},  # Force tool use
    messages=[
        {
            "role": "user",
            "content": f"Extract contact info from: {business_card_text}"
        }
    ]
)

# Get structured output from tool call
for block in response.content:
    if block.type == "tool_use":
        contact = block.input  # Already parsed as dict
        print(f"Name: {contact['name']}")
        print(f"Email: {contact.get('email', 'N/A')}")

OpenAI Function Calling

from openai import OpenAI

client = OpenAI()

functions = [
    {
        "name": "analyze_sentiment",
        "description": "Analyze sentiment of customer feedback",
        "parameters": {
            "type": "object",
            "properties": {
                "sentiment": {
                    "type": "string",
                    "enum": ["positive", "negative", "neutral", "mixed"]
                },
                "confidence": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1
                },
                "key_phrases": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Phrases that indicate sentiment"
                },
                "topics": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Main topics discussed"
                }
            },
            "required": ["sentiment", "confidence"]
        }
    }
]

response = client.chat.completions.create(
    model="gpt-4-turbo-preview",
    messages=[
        {"role": "user", "content": f"Analyze this feedback: {feedback}"}
    ],
    functions=functions,
    function_call={"name": "analyze_sentiment"}  # Force specific function
)

# Parse function call
fn_call = response.choices[0].message.function_call
result = json.loads(fn_call.arguments)

Schema Design Patterns

Enum Constraints

{
  "type": "object",
  "properties": {
    "priority": {
      "type": "string",
      "enum": ["critical", "high", "medium", "low"],
      "description": "Issue priority level"
    },
    "status": {
      "type": "string",
      "enum": ["open", "in_progress", "blocked", "resolved", "closed"]
    },
    "category": {
      "type": "string",
      "enum": ["bug", "feature", "improvement", "documentation"]
    }
  }
}

Nested Objects

{
  "type": "object",
  "properties": {
    "order": {
      "type": "object",
      "properties": {
        "id": {"type": "string"},
        "total": {"type": "number"},
        "currency": {"type": "string", "enum": ["USD", "EUR", "GBP"]},
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "product_id": {"type": "string"},
              "name": {"type": "string"},
              "quantity": {"type": "integer", "minimum": 1},
              "unit_price": {"type": "number", "minimum": 0}
            },
            "required": ["product_id", "quantity", "unit_price"]
          }
        },
        "shipping_address": {
          "$ref": "#/definitions/address"
        }
      },
      "required": ["id", "items"]
    }
  },
  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street": {"type": "string"},
        "city": {"type": "string"},
        "state": {"type": "string"},
        "postal_code": {"type": "string"},
        "country": {"type": "string", "pattern": "^[A-Z]{2}$"}
      },
      "required": ["street", "city", "country"]
    }
  }
}

Conditional Fields

{
  "type": "object",
  "properties": {
    "contact_method": {
      "type": "string",
      "enum": ["email", "phone", "mail"]
    },
    "email": {"type": "string", "format": "email"},
    "phone": {"type": "string"},
    "address": {"$ref": "#/definitions/address"}
  },
  "required": ["contact_method"],
  "allOf": [
    {
      "if": {"properties": {"contact_method": {"const": "email"}}},
      "then": {"required": ["email"]}
    },
    {
      "if": {"properties": {"contact_method": {"const": "phone"}}},
      "then": {"required": ["phone"]}
    },
    {
      "if": {"properties": {"contact_method": {"const": "mail"}}},
      "then": {"required": ["address"]}
    }
  ]
}

Validation and Error Handling

Pydantic Validation (Python)

from pydantic import BaseModel, Field, validator
from typing import Optional, List
from enum import Enum

class Severity(str, Enum):
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"

class CodeIssue(BaseModel):
    severity: Severity
    type: str = Field(..., pattern="^(bug|security|performance|style)$")
    location: str
    description: str = Field(..., min_length=10, max_length=500)
    suggestion: Optional[str] = None

    @validator('location')
    def validate_location(cls, v):
        if ':' not in v and '(' not in v:
            raise ValueError('Location must be file:line or function()')
        return v

class CodeAnalysis(BaseModel):
    summary: str = Field(..., max_length=200)
    issues: List[CodeIssue]
    quality_score: int = Field(..., ge=1, le=10)

    @validator('issues')
    def critical_issues_first(cls, v):
        return sorted(v, key=lambda x: list(Severity).index(x.severity))

# Usage
def parse_analysis(llm_response: str) -> CodeAnalysis:
    """Parse and validate LLM response."""
    try:
        data = json.loads(llm_response)
        return CodeAnalysis(**data)
    except json.JSONDecodeError as e:
        raise ValueError(f"Invalid JSON: {e}")
    except ValidationError as e:
        raise ValueError(f"Schema validation failed: {e}")

Zod Validation (TypeScript)

import { z } from 'zod';

const SeveritySchema = z.enum(['critical', 'high', 'medium', 'low']);

const CodeIssueSchema = z.object({
  severity: SeveritySchema,
  type: z.enum(['bug', 'security', 'performance', 'style']),
  location: z.string().regex(/[:()]/, 'Must be file:line or function()'),
  description: z.string().min(10).max(500),
  suggestion: z.string().optional(),
});

const CodeAnalysisSchema = z.object({
  summary: z.string().max(200),
  issues: z.array(CodeIssueSchema),
  quality_score: z.number().int().min(1).max(10),
});

type CodeAnalysis = z.infer<typeof CodeAnalysisSchema>;

function parseAnalysis(llmResponse: string): CodeAnalysis {
  const data = JSON.parse(llmResponse);
  return CodeAnalysisSchema.parse(data);
}

Retry with Correction

def get_structured_output(
    prompt: str,
    schema: dict,
    max_retries: int = 3
) -> dict:
    """Get structured output with automatic retry on validation failure."""

    for attempt in range(max_retries):
        response = llm.complete(prompt)

        try:
            data = json.loads(response)
            validate(data, schema)  # JSON Schema validation
            return data
        except json.JSONDecodeError as e:
            error_msg = f"Invalid JSON at position {e.pos}: {e.msg}"
        except ValidationError as e:
            error_msg = format_validation_error(e)

        # Retry with error feedback
        if attempt < max_retries - 1:
            prompt = f"""Your previous response had an error:
{error_msg}

Please fix and try again. Return only valid JSON matching the schema.

Original request:
{prompt}"""

    raise ValueError(f"Failed to get valid output after {max_retries} attempts")

Complex Extraction Patterns

Multi-Entity Extraction

Extract all entities from the following document.

Return JSON with this structure:
{
  "people": [
    {
      "name": "full name",
      "aliases": ["nicknames or alternate names"],
      "role": "their role/position if mentioned",
      "mentioned_with": ["names of people they're associated with"]
    }
  ],
  "organizations": [
    {
      "name": "organization name",
      "type": "company|nonprofit|government|education|other",
      "location": "headquarters if mentioned"
    }
  ],
  "events": [
    {
      "name": "event name",
      "date": "YYYY-MM-DD or null",
      "location": "where it happened",
      "participants": ["people or organizations involved"]
    }
  ],
  "relationships": [
    {
      "entity1": "name",
      "entity2": "name",
      "type": "works_at|acquired|partnered|competed|invested",
      "details": "additional context"
    }
  ]
}

Document:
{document}

Hierarchical Data Extraction

Parse this organizational structure and return as JSON:

{
  "organization": {
    "name": "company name",
    "departments": [
      {
        "name": "department name",
        "head": "department head name",
        "teams": [
          {
            "name": "team name",
            "lead": "team lead name",
            "members": ["member names"],
            "responsibilities": ["key responsibilities"]
          }
        ]
      }
    ]
  }
}

Text:
{org_description}

Form Data Extraction

Extract form data from this image/document.

Return JSON:
{
  "form_type": "detected form type",
  "fields": {
    "field_name": {
      "value": "extracted value",
      "confidence": 0.0-1.0,
      "location": "where on form (if applicable)"
    }
  },
  "checkboxes": {
    "checkbox_label": true/false
  },
  "signatures": [
    {
      "signer": "name if readable",
      "date": "date if present",
      "location": "signature location on form"
    }
  ],
  "missing_fields": ["fields that appear required but are empty"]
}

Document content:
{document}

Performance Optimization

Batch Processing

async def extract_structured_batch(
    items: list,
    schema: dict,
    batch_size: int = 10
) -> list:
    """Process multiple items efficiently."""
    results = []

    for i in range(0, len(items), batch_size):
        batch = items[i:i+batch_size]

        # Create batch prompt
        prompt = f"""Extract information from each item below.
Return a JSON array with one object per item, matching this schema:
{json.dumps(schema, indent=2)}

Items:
{json.dumps([{"index": j, "content": item} for j, item in enumerate(batch)])}

Response (JSON array only):"""

        response = await llm.complete_async(prompt)
        batch_results = json.loads(response)
        results.extend(batch_results)

    return results

Schema Simplification

Overly complex schema (expensive):

{
  "analysis": {
    "sentiment": {
      "overall": {"score": -1 to 1, "label": "string"},
      "aspects": [{"aspect": "string", "sentiment": {...}}]
    },
    "entities": [...],
    "topics": [...],
    "summary": {...}
  }
}

Simplified schema (cost-effective):

{
  "sentiment": "positive|negative|neutral",
  "confidence": 0.0-1.0,
  "key_points": ["string"]
}

Common Pitfalls

Pitfall Problem Solution
No schema in prompt Model invents structure Always specify expected schema
Ambiguous field names Inconsistent extraction Use descriptive names with examples
Missing null handling Errors on optional fields Explicitly state "null if not found"
Complex nested schemas Inconsistent output Flatten when possible
No validation Silent failures Always validate with Pydantic/Zod
Large schemas Token waste, confusion Split into multiple calls

Related Skills

  • API Designer - Schema design for APIs
  • Data Engineer - Data validation pipelines
  • RAG Architect - Structured extraction for retrieval

Source: SKILL.md on GitHub

1 alert17d5 checks · Risk CRITICAL
  • Gen Agent Trust Hub17d

    This skill is a comprehensive reference guide for prompt engineering. While it contains numerous strings associated with prompt injection (such as jailbreak attempts and instructions to ignore previous rules), these are explicitly used as examples for defensive design and automated security testing. All external links point to the author's own infrastructure or well-known service providers.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: MEDIUM · 1 issue

  • Runlayer6mo

    2/6 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at efebc44. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 5 months ago
Other metadata
metadata
{
  "author": "https://github.com/Jeffallan",
  "version": "1.2.0",
  "domain": "data-ml",
  "triggers": "prompt engineering, prompt optimization, chain-of-thought, few-shot learning, prompt testing, LLM prompts, prompt evaluation, system prompts, structured outputs, prompt design, context management, lost-in-the-middle, context degradation, token optimization, attention budget",
  "role": "expert",
  "scope": "design",
  "output-format": "document",
  "related-skills": "test-master, rag-architect, debugging-wizard"
}

README badge

README badge for jeffallan/claude-skills/prompt-engineer