obsidian/wiki/agent-sdk/structured-outputs.md
2026-04-17 12:48:28 +01:00

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