A typed and extensible library for creating, configuring, and running Iterative AI Agents that generate structured JSON output. Includes adapters for OpenAI, Anthropic, Google Gemini, and DeepSeek. Supports custom adapter implementation.
This library orchestrates a Generator ↔ Reviewer cycle to ensure that the output from Large Language Models (LLMs) strictly adheres to a defined Zod Schema.
- Guaranteed JSON Output: Enforces strict adherence to Zod Schemas.
- Multi-Provider Support: Built-in adapters for OpenAI, Google GenAI (Gemini), Anthropic (Claude), and DeepSeek.
- Structured Outputs: Leverages native structured output capabilities of providers (e.g., OpenAI Structured Outputs, Anthropic Beta) when available.
- Iterative Self-Correction: Automatically detects validation errors and feeds them back to a "Reviewer" model to fix the output.
- Type-Safe: Built with TypeScript and Zod for full type inference and safety.
- Flexible Configuration: Mix and match different providers for generation and review (e.g., generate with GPT-4o, review with Claude 3.5 Sonnet).
npm install structured-json-agent zod openai @anthropic-ai/sdk @google/genaiNote: Install the SDKs for the providers you intend to use.
import { StructuredAgent } from "structured-json-agent";
import { z } from "zod";
import OpenAI from "openai";
import Anthropic from "@anthropic-ai/sdk";
// 1. Define your Schemas using Zod
const inputSchema = z.object({
topic: z.string(),
depth: z.enum(["basic", "advanced"])
});
const outputSchema = z.object({
title: z.string(),
keyPoints: z.array(z.string()),
summary: z.string()
});
// 2. Initialize Provider Instances
const openAiInstance = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const anthropicInstance = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
// 3. Initialize the Agent, you can use the same instance for generator and reviewer
const agent = new StructuredAgent({
// Generator Configuration
generator: {
llmService: openAiInstance, // Inject the instance directly
model: "gpt-5-nano", // Specify the model
},
// Reviewer Configuration (Optional but recommended)
reviewer: {
llmService: anthropicInstance,
model: "claude-sonnet-4-5",
},
// Schemas & Prompt
inputSchema,
outputSchema,
systemPrompt: "You are an expert summarizer. Create a structured summary based on the topic.",
});To run the agent, use the run method with your input data. Optionally, provide a reference (string or number) identifier for tracking in the second parameter.
async function main() {
try {
const result = await agent.run({
topic: "Clean Architecture",
depth: "advanced"
}, "12345"); // Optional reference for tracking
console.log("Output:", result.output);
console.log("Metadata:", result.metadata);
// Metadata includes provider, model, and iteration count
console.log("Reference:", result.ref);
// Result is typed as inferred from outputSchema
} catch (error) {
console.error("Agent failed:", error);
}
}
main();In TypeScript, you can use the run<T> method to get a typed result.
const result = await agent.run<T>({
topic: "Clean Architecture",
depth: "advanced"
}, "12345");The result object is of type AgentResult<T>, where T is the type inferred from outputSchema.
type AgentResult<T> = {
output: T; // Output as per outputSchema
metadata: {
provider: string; // e.g., "openai", "deepseek"
model: string; // e.g., "gpt-4o", "claude-3-5-sonnet"
inputTokens: number; // Number of tokens in the input
outputTokens: number; // Number of tokens in the output
step: string; // Step description ("generation", "review-1", "review-2", etc.)
validation: {
valid: boolean; // Whether the output is valid against outputSchema
errors?: string[]; // Validation errors if any
}
}[];
ref?: string | number; // Optional reference provided for tracking
};- Validation: The input JSON is validated against the
inputSchema(Zod). - Generation: The
generatormodel creates an initial response based on the system prompt and input.- If the provider supports native Structured Outputs (like OpenAI or Anthropic), it is used to maximize reliability.
- Verification Loop:
- The response is parsed and validated against
outputSchema. - If Valid: The result is returned immediately.
- If Invalid: The
reviewermodel (or generator if no reviewer is set) is invoked with the invalid JSON and specific validation errors. It attempts to fix the output.
- The response is parsed and validated against
- Convergence: This cycle repeats until a valid JSON is produced or
maxIterationsis reached.
Configuration object passed to new StructuredAgent(config).
| Property | Type | Description |
|---|---|---|
generator |
LLMConfig |
Configuration for the generation model. |
reviewer |
LLMConfig? |
Configuration for the reviewer model (optional). |
inputSchema |
ZodSchema |
Zod Schema for validating the input. |
outputSchema |
ZodSchema |
Zod Schema for the expected output. |
systemPrompt |
string |
Core instructions for the agent. |
maxIterations |
number? |
Max retries for correction. Default: 5. |
| Property | Type | Description |
|---|---|---|
llmService |
OpenAI | GoogleGenAI | Anthropic | ILLMService |
The provider instance or custom service. Supports DeepSeek via OpenAI SDK. |
model |
string |
Model ID (e.g., gpt-4o, claude-3-5-sonnet). |
config |
ModelConfig? |
Optional parameters (temperature, max_tokens, etc.). |
The project is structured by domain:
src/agent: Core orchestration logic (StructuredAgent).src/schemas: Validation logic using Zod.src/llm: Adapters and Factory for LLM providers (OpenAI,Google,Anthropic).src/errors: Custom error definitions.src/types: Shared interfaces.