174 lines
5.6 KiB
Markdown
174 lines
5.6 KiB
Markdown
---
|
|
title: "Structured Outputs from Agents"
|
|
aliases: [structured-output, agent-json-output, validated-json-output]
|
|
tags: [agent-sdk, structured-outputs, zod, pydantic, json-schema, typescript, python]
|
|
sources: [raw/Get structured output from agents.md]
|
|
created: 2026-04-17
|
|
updated: 2026-04-17
|
|
---
|
|
|
|
# Structured Outputs from Agents
|
|
|
|
Return validated, typed JSON from an agent workflow instead of free-form text. The agent still runs its full tool loop; structured output is enforced at the end.
|
|
|
|
## Why Use It
|
|
|
|
- Agents return free-form text by default — usable for chat, not for app logic
|
|
- Structured outputs give you typed data ready for databases, UIs, APIs
|
|
- SDK validates output against your schema; re-prompts on mismatch
|
|
- If retries exceed the limit, result is an error (not bad data)
|
|
|
|
## Quick Start
|
|
|
|
Pass `outputFormat` (TS) / `output_format` (Python) to `query()`:
|
|
|
|
```typescript
|
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
|
|
const schema = {
|
|
type: "object",
|
|
properties: {
|
|
company_name: { type: "string" },
|
|
founded_year: { type: "number" },
|
|
headquarters: { type: "string" }
|
|
},
|
|
required: ["company_name"]
|
|
};
|
|
|
|
for await (const message of query({
|
|
prompt: "Research Anthropic and provide key company information",
|
|
options: {
|
|
outputFormat: { type: "json_schema", schema }
|
|
}
|
|
})) {
|
|
if (message.type === "result" && message.subtype === "success" && message.structured_output) {
|
|
console.log(message.structured_output);
|
|
// { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }
|
|
}
|
|
}
|
|
```
|
|
|
|
Validated data is on `message.structured_output` in the `result` message.
|
|
|
|
## Type-Safe Schemas: Zod (TS) and Pydantic (Python)
|
|
|
|
Instead of hand-writing JSON Schema, use library types — they generate the schema and give you full autocomplete + runtime validation.
|
|
|
|
**TypeScript with Zod:**
|
|
|
|
```typescript
|
|
import { z } from "zod";
|
|
|
|
const FeaturePlan = z.object({
|
|
feature_name: z.string(),
|
|
summary: z.string(),
|
|
steps: z.array(z.object({
|
|
step_number: z.number(),
|
|
description: z.string(),
|
|
estimated_complexity: z.enum(["low", "medium", "high"])
|
|
})),
|
|
risks: z.array(z.string())
|
|
});
|
|
|
|
const schema = z.toJSONSchema(FeaturePlan); // generate JSON Schema
|
|
// pass schema to outputFormat, then:
|
|
const parsed = FeaturePlan.safeParse(message.structured_output);
|
|
if (parsed.success) {
|
|
const plan = parsed.data; // fully typed FeaturePlan
|
|
}
|
|
```
|
|
|
|
**Benefits over raw JSON Schema:**
|
|
- Full type inference / type hints
|
|
- Runtime validation with `safeParse()` or `model_validate()`
|
|
- Better error messages
|
|
- Composable, reusable schemas
|
|
|
|
## outputFormat Config
|
|
|
|
```typescript
|
|
outputFormat: {
|
|
type: "json_schema", // only supported type
|
|
schema: <JSON Schema object>
|
|
}
|
|
```
|
|
|
|
Supported JSON Schema features: `object`, `array`, `string`, `number`, `boolean`, `null`, `enum`, `const`, `required`, nested objects, `$ref`.
|
|
|
|
## Multi-Step Tool Use + Structured Output
|
|
|
|
The agent freely uses tools (Grep, Bash, etc.) during execution. Structured output applies only to the **final result**, not individual tool calls.
|
|
|
|
Example — TODO tracker agent:
|
|
- Agent uses `Grep` to find TODO comments, `Bash` for git blame
|
|
- Returns a single validated object with `todos[]` and `total_count`
|
|
- Optional fields (`author`, `date`) handle cases where git info is unavailable
|
|
|
|
```typescript
|
|
const todoSchema = {
|
|
type: "object",
|
|
properties: {
|
|
todos: {
|
|
type: "array",
|
|
items: {
|
|
type: "object",
|
|
properties: {
|
|
text: { type: "string" },
|
|
file: { type: "string" },
|
|
line: { type: "number" },
|
|
author: { type: "string" }, // optional
|
|
date: { type: "string" } // optional
|
|
},
|
|
required: ["text", "file", "line"]
|
|
}
|
|
},
|
|
total_count: { type: "number" }
|
|
},
|
|
required: ["todos", "total_count"]
|
|
};
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
Check `message.subtype` on the `result` message:
|
|
|
|
| Subtype | Meaning |
|
|
|---------|---------|
|
|
| `success` | Output validated successfully |
|
|
| `error_max_structured_output_retries` | Agent failed to produce valid output after retries |
|
|
|
|
```typescript
|
|
if (msg.type === "result") {
|
|
if (msg.subtype === "success" && msg.structured_output) {
|
|
// use data
|
|
} else if (msg.subtype === "error_max_structured_output_retries") {
|
|
// retry with simpler prompt, fall back to unstructured, etc.
|
|
}
|
|
}
|
|
```
|
|
|
|
## Tips for Avoiding Errors
|
|
|
|
- **Keep schemas focused** — deeply nested + many required fields = harder to satisfy
|
|
- **Make fields optional** when the task might not surface that data
|
|
- **Write clear prompts** — ambiguity makes output unpredictable
|
|
|
|
## Key Takeaways
|
|
|
|
- Pass `outputFormat: { type: "json_schema", schema }` to `query()` to get validated JSON back
|
|
- Use **Zod** (TS) or **Pydantic** (Python) to generate the schema and get strong types
|
|
- The agent runs its full tool loop freely; structured output is enforced only at the end
|
|
- Validation failures auto-retry; persistent failure → `error_max_structured_output_retries`
|
|
- Optional fields are your friend — don't require data the agent might not find
|
|
- For single-turn requests without tool use, the Claude API has its own structured outputs endpoint
|
|
|
|
## Related
|
|
|
|
- [[wiki/agent-sdk/typescript-api-reference|TypeScript API Reference]] — full `query()` signature and options
|
|
- [[wiki/agent-sdk/python-api-reference|Python API Reference]] — Python `query()` equivalent
|
|
- [[wiki/agent-sdk/overview|Agent SDK Overview]] — built-in tools, features, quick orientation
|
|
- [[wiki/agent-sdk/mcp-integration|MCP Integration]] — adding custom tools the agent can call during its loop
|
|
|
|
## Sources
|
|
|
|
- `raw/Get structured output from agents.md` — official Agent SDK docs on structured outputs
|