structured-output
Force LLMs to return typed, validated JSON — not free-text. Use when someone asks to "get structured data from LLM", "parse LLM response as JSON", "make AI return typed output", "validate LLM output", "extract structured data with AI", "use instructor with OpenAI", or "get reliable JSON from Claude/GPT". Covers OpenAI structured outputs, Anthropic tool_use for structured data, Instructor library, Zod schemas, Pydantic models, and retry strategies for malformed responses.
What this skill does
# Structured Output
## Overview
LLMs love to ramble. When you need JSON, you get JSON wrapped in markdown. When you need a list, you get a paragraph. Structured output forces the model to return exactly the schema you define — validated, typed, and ready to use in code without parsing gymnastics.
## When to Use
- Extracting structured data from unstructured text (invoices, emails, articles)
- Building API responses powered by LLMs that must match a schema
- Creating reliable data pipelines where LLM output feeds into databases
- Generating configuration files, test cases, or code from natural language
- Any time you need JSON from an LLM, not prose
## Instructions
### Strategy 1: OpenAI Structured Outputs (Native)
OpenAI's `response_format` with JSON Schema guarantees valid JSON matching your schema. The model literally cannot produce non-conforming output.
```typescript
// openai-structured.ts — Type-safe LLM outputs with OpenAI
/**
* Uses OpenAI's native structured outputs to guarantee
* responses match a JSON Schema. No parsing, no retries,
* no "please return valid JSON" prompting hacks.
*/
import OpenAI from "openai";
import { z } from "zod";
import { zodResponseFormat } from "openai/helpers/zod";
const openai = new OpenAI();
// Define your output schema with Zod
const ProductAnalysis = z.object({
name: z.string().describe("Product name"),
category: z.enum(["electronics", "clothing", "food", "software", "other"]),
sentiment: z.enum(["positive", "negative", "neutral", "mixed"]),
score: z.number().min(0).max(10).describe("Overall quality score"),
pros: z.array(z.string()).describe("List of positive aspects"),
cons: z.array(z.string()).describe("List of negative aspects"),
summary: z.string().max(200).describe("One-sentence summary"),
});
type ProductAnalysis = z.infer<typeof ProductAnalysis>;
async function analyzeReview(review: string): Promise<ProductAnalysis> {
const response = await openai.beta.chat.completions.parse({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: "Analyze the product review and extract structured data.",
},
{ role: "user", content: review },
],
response_format: zodResponseFormat(ProductAnalysis, "product_analysis"),
});
// Guaranteed to be valid — no try/catch needed
return response.choices[0].message.parsed!;
}
```
### Strategy 2: Anthropic Tool Use for Structured Data
Claude doesn't have native JSON mode, but tool_use forces structured output through a function call schema.
```typescript
// anthropic-structured.ts — Structured output from Claude via tool_use
/**
* Uses Anthropic's tool_use feature to extract structured data.
* Define a "tool" with your output schema — Claude fills it in
* as a structured tool call instead of free text.
*/
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic();
interface ExtractedEvent {
title: string;
date: string; // ISO 8601
location: string;
attendees: number;
description: string;
}
async function extractEvent(text: string): Promise<ExtractedEvent> {
const response = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
tools: [
{
name: "extract_event",
description: "Extract event details from text",
input_schema: {
type: "object" as const,
properties: {
title: { type: "string", description: "Event title" },
date: { type: "string", description: "Event date in ISO 8601 format" },
location: { type: "string", description: "Event location" },
attendees: { type: "number", description: "Expected number of attendees" },
description: { type: "string", description: "Brief event description" },
},
required: ["title", "date", "location", "attendees", "description"],
},
},
],
tool_choice: { type: "tool", name: "extract_event" }, // Force this tool
messages: [{ role: "user", content: `Extract event details:\n\n${text}` }],
});
// Find the tool_use block
const toolBlock = response.content.find((b) => b.type === "tool_use");
if (!toolBlock || toolBlock.type !== "tool_use") {
throw new Error("No tool_use block in response");
}
return toolBlock.input as ExtractedEvent;
}
```
### Strategy 3: Instructor Library (Python)
Instructor patches the OpenAI/Anthropic client to return Pydantic models directly. The most ergonomic way to get structured output in Python.
```python
# instructor_extract.py — Structured LLM output with Pydantic models
"""
Uses the Instructor library to patch OpenAI/Anthropic clients
and return validated Pydantic models. Handles retries automatically
when the model returns invalid data.
"""
import instructor
from pydantic import BaseModel, Field
from openai import OpenAI
from typing import Optional
# Patch the OpenAI client
client = instructor.from_openai(OpenAI())
class ContactInfo(BaseModel):
"""Extracted contact information from unstructured text."""
name: str = Field(description="Full name")
email: Optional[str] = Field(default=None, description="Email address")
phone: Optional[str] = Field(default=None, description="Phone number")
company: Optional[str] = Field(default=None, description="Company name")
role: Optional[str] = Field(default=None, description="Job title or role")
class EmailAnalysis(BaseModel):
"""Structured analysis of an email."""
sender: ContactInfo
intent: str = Field(description="Primary intent: inquiry, complaint, request, info, spam")
urgency: str = Field(description="low, medium, high, critical")
action_items: list[str] = Field(description="List of required actions")
sentiment: float = Field(ge=-1.0, le=1.0, description="Sentiment score -1 to 1")
summary: str = Field(max_length=200, description="One-sentence summary")
def analyze_email(email_text: str) -> EmailAnalysis:
"""Analyze an email and return structured data.
Args:
email_text: Raw email body text
Returns:
Validated EmailAnalysis with all fields populated
"""
return client.chat.completions.create(
model="gpt-4o-mini",
response_model=EmailAnalysis, # Instructor magic
max_retries=3, # Auto-retry on validation failure
messages=[
{"role": "system", "content": "Analyze the email and extract structured data."},
{"role": "user", "content": email_text},
],
)
# Usage
result = analyze_email("Hi, I'm John from Acme Corp. We need the API docs by Friday...")
print(result.model_dump_json(indent=2))
```
### Strategy 4: Retry with Validation (Generic)
When the model doesn't support native structured output, validate and retry.
```typescript
// retry-structured.ts — Validate and retry LLM output
/**
* Generic structured output with retry logic.
* Works with any LLM provider. Sends the validation error
* back to the model so it can self-correct.
*/
import { z, ZodSchema } from "zod";
async function structuredLLM<T>(
llmCall: (messages: Array<{ role: string; content: string }>) => Promise<string>,
schema: ZodSchema<T>,
prompt: string,
maxRetries: number = 3
): Promise<T> {
const messages: Array<{ role: string; content: string }> = [
{
role: "system",
content: `Respond with valid JSON matching this schema:\n${JSON.stringify(zodToJsonSchema(schema), null, 2)}`,
},
{ role: "user", content: prompt },
];
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const raw = await llmCall(messages);
// Extract JSON from response (handle markdown code blocks)
const jsonStr = raw.replace(/```json?\n?/g, "").replace(/```/g, "").trim();
try {
const parsed = JSON.parse(jsonStr);
return schema.parse(parsed); // Zod validation
} catch (error: any) {
if (attempt === maxRetries) {
thrRelated in AI Agents
skill-development
IncludedComprehensive meta-skill for creating, managing, validating, auditing, and distributing Claude Code skills and slash commands (unified in v2.1.3+). Provides skill templates, creation workflows, validation patterns, audit checklists, naming conventions, YAML frontmatter guidance, progressive disclosure examples, and best practices lookup. Use when creating new skills, validating existing skills, auditing skill quality, understanding skill architecture, needing skill templates, learning about YAML frontmatter requirements, progressive disclosure patterns, tool restrictions (allowed-tools), skill composition, skill naming conventions, troubleshooting skill activation issues, creating custom slash commands, configuring command frontmatter, using command arguments ($ARGUMENTS, $1, $2), bash execution in commands, file references in commands, command namespacing, plugin commands, MCP slash commands, Skill tool configuration, or deciding between skills vs slash commands. Delegates to docs-management skill for official documentation.
reprompter
IncludedTransform messy prompts into well-structured, effective prompts — single or multi-agent. Use when: "reprompt", "reprompt this", "clean up this prompt", "structure my prompt", rough text needing XML tags and best practices, "reprompter teams", "repromptception", "run with quality", "smart run", "smart agents", multi-agent tasks, audits, parallel work, anything going to agent teams. Don't use when: simple Q&A, pure chat, immediate execution-only tasks. See "Don't Use When" section for details. Outputs: Structured XML/Markdown prompt, quality score (before/after), optional team brief + per-agent sub-prompts, agent team output files. Success criteria: Single mode quality score ≥ 7/10; Repromptception per-agent prompt quality score 8+/10; all required sections present, actionable and specific.
adaptive-compaction
IncludedAdaptive add-on policy and recovery layer that decides WHEN to compact, prune, snapshot, or fork -- replacing fixed-percent auto-compaction across Claude Code, Codex, and MCP-capable hosts. Trigger on auto-compact timing or damage: "when should I compact", "is it safe to compact now or start a fresh session", "auto-compact fires too early/mid-task", "switching to an unrelated task but the window still has space", "context rot", "answers get worse the longer the session runs", "the agent forgot the plan or my decisions after it summarized", "add a layer on top that manages context without changing the agent", raising autoCompactWindow to give the policy room, or installing/tuning a cross-tool compaction policy or PreCompact hook -- even when "compaction" is never said but the problem is context-window pressure or post-summarization memory loss. Do NOT use to summarize a conversation, build RAG, write a summarization prompt (decides WHEN not HOW), or answer max-context-length trivia.
agent-skill-creator
IncludedCreate cross-platform agent skills from workflow descriptions. Activates when users ask to create an agent, automate a repetitive workflow, create a custom skill, or need advanced agent creation. Triggers on phrases like create agent for, automate workflow, create skill for, every day I have to, daily I need to, turn process into agent, need to automate, create a cross-platform skill, validate this skill, export this skill, migrate this skill. Supports single skills, multi-agent suites, transcript processing, template-based creation, interactive configuration, cross-platform export, and spec validation.
llm-wiki
IncludedUse when building or maintaining a persistent personal knowledge base (second brain) in Obsidian where an LLM incrementally ingests sources, updates entity/concept pages, maintains cross-references, and keeps a synthesis current. Triggers include "second brain", "Obsidian wiki", "personal knowledge management", "ingest this paper/article/book", "build a research wiki", "compound knowledge", "Memex", or whenever the user wants knowledge to accumulate across sessions instead of being re-derived by RAG on every query.
skill-master
IncludedAgent Skills authoring, evaluation, and optimization. Create, edit, validate, benchmark, and improve skills following the agentskills.io specification. Use when designing SKILL.md files, structuring skill folders (references, scripts, assets), ingesting external documentation into skills, running trigger evals, benchmarking skill quality, optimizing descriptions, or performing blind A/B comparisons. Keywords: agentskills.io, SKILL.md, skill authoring, eval, benchmark, trigger optimization.