Structured Output Enforcer
Prerequisites & Dependencies
- Node.js 18+ with npm or pnpm
- Mandatory packages:
npm i zod for runtime validation and type inference, or npm i jsonschema for pure JSON Schema validation
- Optional:
npm i ai-sdk or npm i openai to integrate with LLM APIs
- TypeScript for type-safe Zod schemas (highly recommended)
Execution Steps
- Define a Zod schema (or JSON Schema) that describes the expected output structure: required fields, types, enums, arrays, and nested objects
- Call the LLM with a prompt that instructs it to output JSON matching the schema (e.g.,
Output valid JSON only, matching this schema:...)
- Parse the LLM's raw response as JSON (strip markdown fences if needed)
- Validate the parsed object against the Zod schema using
z.parse(data) or schema.safeParse(data)
- If validation fails, retry with a refined prompt that includes the error details and schema excerpt
- On success, return the validated JSON object to the caller; log and discard invalid attempts after N retries
- Optionally generate TypeScript types from the Zod schema:
z.infer<typeof schema> for end-to-end type safety
// Example: Zod schema for LLM-structured output
import { z } from 'zod';
const UserProfileSchema = z.object({
name: z.string().min(1, 'Name is required'),
age: z.number().int().min(13).max(120, 'Age must be between 13 and 120'),
email: z.string().email('Invalid email address'),
role: z.enum(['admin', 'user', 'guest'], 'Role must be admin, user, or guest'),
preferences: z.object({
theme: z.enum(['light', 'dark', 'system']),
notifications: z.boolean(),
}),
});
type UserProfile = z.infer<typeof UserProfileSchema>;
// Simulated LLM output (would come from OpenAI/Anthropic API call)
const rawLLMOutput = `{
"name": "Alice",
"age": 30,
"email": "alice@example.com",
"role": "user",
"preferences": {
"theme": "dark",
"notifications": true
}
}`;
try {
const parsed = UserProfileSchema.parse(JSON.parse(rawLLMOutput));
console.log('Validated user profile:', parsed);
} catch (error) {
console.error('Validation errors:', error.errors);
}
npm i zod
npm i -D @types/zod
1---2name: structured-output-enforcer3description: Convert unstructured LLM output into validated JSON adhering to strict Zod or JSON Schema rules.4metadata:5 source: skills/structured-output-enforcer/structured-output-enforcer.md6---78# Structured Output Enforcer910## Prerequisites & Dependencies11- Node.js 18+ with npm or pnpm12- Mandatory packages: `npm i zod` for runtime validation and type inference, or `npm i jsonschema` for pure JSON Schema validation13- Optional: `npm i ai-sdk` or `npm i openai` to integrate with LLM APIs14- TypeScript for type-safe Zod schemas (highly recommended)1516## Execution Steps171. Define a Zod schema (or JSON Schema) that describes the expected output structure: required fields, types, enums, arrays, and nested objects182. Call the LLM with a prompt that instructs it to output JSON matching the schema (e.g., `Output valid JSON only, matching this schema:...`)193. Parse the LLM's raw response as JSON (strip markdown fences if needed)204. Validate the parsed object against the Zod schema using `z.parse(data)` or `schema.safeParse(data)`215. If validation fails, retry with a refined prompt that includes the error details and schema excerpt226. On success, return the validated JSON object to the caller; log and discard invalid attempts after N retries237. Optionally generate TypeScript types from the Zod schema: `z.infer<typeof schema>` for end-to-end type safety2425```typescript26// Example: Zod schema for LLM-structured output27import { z } from 'zod';2829const UserProfileSchema = z.object({30 name: z.string().min(1, 'Name is required'),31 age: z.number().int().min(13).max(120, 'Age must be between 13 and 120'),32 email: z.string().email('Invalid email address'),33 role: z.enum(['admin', 'user', 'guest'], 'Role must be admin, user, or guest'),34 preferences: z.object({35 theme: z.enum(['light', 'dark', 'system']),36 notifications: z.boolean(),37 }),38});3940type UserProfile = z.infer<typeof UserProfileSchema>;4142// Simulated LLM output (would come from OpenAI/Anthropic API call)43const rawLLMOutput = `{44 "name": "Alice",45 "age": 30,46 "email": "alice@example.com",47 "role": "user",48 "preferences": {49 "theme": "dark",50 "notifications": true51 }52}`;5354try {55 const parsed = UserProfileSchema.parse(JSON.parse(rawLLMOutput));56 console.log('Validated user profile:', parsed);57} catch (error) {58 console.error('Validation errors:', error.errors);59}60```6162```bash63npm i zod64npm i -D @types/zod65```