Claude Code: Structuring Agent Output Formats

· 4 min read

Overview

Multi-agent systems are only as useful as the output they produce. A team of agents might perform brilliant research, synthesis, and review internally, but if the final output is unstructured text that downstream systems cannot parse, the entire pipeline breaks. Structuring agent output formats is not an afterthought — it is a core architectural decision that affects reliability, composability, and user experience.

The Claude Agent SDK provides several mechanisms for constraining and validating agent outputs. This guide covers practical patterns for defining output schemas, enforcing structure at every handoff point, and handling the inevitable cases where agents deviate from expected formats.

When to Use Structured Outputs

Structured output formats matter most when:

Defining Output Schemas with Zod

The most robust approach is to define output schemas using Zod and enforce them at the SDK level.

import { z } from "zod";
import { Agent } from "@anthropic-ai/agent-sdk";

const AgentReportSchema = z.object({
  title: z.string().min(1).max(200),
  summary: z.string().min(50).max(500),
  sections: z.array(z.object({
    heading: z.string(),
    content: z.string(),
    confidence: z.enum(["high", "medium", "low"]),
    sources: z.array(z.object({
      url: z.string().url().optional(),
      title: z.string(),
      relevance: z.number().min(0).max(1),
    })),
  })).min(1).max(10),
  recommendations: z.array(z.string()).min(1),
  metadata: z.object({
    generatedAt: z.string(),
    agentVersion: z.string(),
    totalTokensUsed: z.number().optional(),
  }),
});

type AgentReport = z.infer<typeof AgentReportSchema>;

Applying Schemas to Agents

const analysisAgent = new Agent({
  name: "analyst",
  model: "claude-sonnet-4-20250514",
  instructions: `Analyze the provided data and produce a structured report.
Your output MUST be valid JSON matching the specified schema.
Do not include any text outside the JSON object.`,
  outputSchema: AgentReportSchema,
});

const result = await analysisAgent.run(inputData);
// result.output is typed as AgentReport

When you specify an outputSchema, the SDK automatically validates the agent's response and will retry if the output does not conform. This eliminates an entire category of parsing errors.

Handling Partial and Streaming Outputs

For long-running agent tasks, you often want to stream partial results to the user. Structured outputs can be streamed section by section.

import { Agent, StreamEvent } from "@anthropic-ai/agent-sdk";

const SectionSchema = z.object({
  heading: z.string(),
  content: z.string(),
  status: z.enum(["complete", "in-progress"]),
});

const streamingAgent = new Agent({
  name: "report-writer",
  model: "claude-sonnet-4-20250514",
  instructions: "Write the report one section at a time. Output each section as a separate JSON object.",
  streaming: true,
});

const stream = streamingAgent.stream(inputData);

for await (const event of stream) {
  if (event.type === "partial_output") {
    const section = SectionSchema.safeParse(event.data);
    if (section.success) {
      renderSection(section.data);
    }
  }
}

Multi-Agent Output Aggregation

When multiple agents each produce a piece of the final output, you need an aggregation strategy. There are three common patterns.

Pattern 1: Schema Merge

Each agent produces output conforming to a sub-schema, and the orchestrator merges them.

const ResearchOutput = z.object({ findings: z.array(z.string()), sources: z.array(z.string()) });
const AnalysisOutput = z.object({ insights: z.array(z.string()), risks: z.array(z.string()) });
const RecommendationOutput = z.object({ actions: z.array(z.string()), priority: z.enum(["high", "medium", "low"]) });

const FinalReport = z.object({
  research: ResearchOutput,
  analysis: AnalysisOutput,
  recommendations: RecommendationOutput,
});

// In the orchestrator
const research = await researchAgent.run(input);
const analysis = await analysisAgent.run(JSON.stringify(research.output));
const recommendations = await recommendationAgent.run(JSON.stringify({
  research: research.output,
  analysis: analysis.output,
}));

const finalReport: z.infer<typeof FinalReport> = {
  research: research.output,
  analysis: analysis.output,
  recommendations: recommendations.output,
};

Pattern 2: Reducer Agent

A dedicated agent receives all outputs and produces the final structured result.

const reducerAgent = new Agent({
  name: "reducer",
  model: "claude-sonnet-4-20250514",
  instructions: `You receive outputs from multiple specialist agents.
Synthesize them into a single coherent report following the output schema exactly.
Resolve any contradictions by favoring higher-confidence findings.`,
  outputSchema: FinalReportSchema,
});

Pattern 3: Template Filling

Pre-define a template and have agents fill specific sections.

interface ReportTemplate {
  executiveSummary: string | null;
  marketAnalysis: string | null;
  technicalAssessment: string | null;
  financialProjection: string | null;
  recommendation: string | null;
}

const template: ReportTemplate = {
  executiveSummary: null,
  marketAnalysis: null,
  technicalAssessment: null,
  financialProjection: null,
  recommendation: null,
};

// Each agent fills its section
template.marketAnalysis = (await marketAgent.run(input)).output;
template.technicalAssessment = (await techAgent.run(input)).output;
// ...

Integration with Agent Teams

Generated agent teams from Build Agents Store include output format specifications for each agent in the team. When you generate a team configuration, the system defines not just each agent's role and prompt, but also the expected output format at each stage.

This is critical for reliability. A customer support triage team, for example, might define output schemas that include a severity level, category classification, suggested response, and escalation flag — all as typed fields rather than free text. The reviewing agent can then programmatically verify that the triage agent's output includes all required fields before approving the response.

When designing your own output formats, prioritize:

Skip the setup — generate agent teams instantly →