SpyBara
Go Premium

agent-sdk/structured-outputs.md 2026-11-10 21:57 UTC to 2026-11-11 22:59 UTC

No changes for this document in this history range.

2026
Tue 10 21:57

Получение структурированного вывода от агентов

Возвращайте валидированный JSON из рабочих процессов агентов, используя JSON Schema, Zod или Pydantic. Получайте типобезопасные структурированные данные после многоходового использования инструментов.

Структурированные выводы позволяют вам определить точную форму данных, которые вы хотите получить от агента. Агент может использовать любые инструменты, необходимые для выполнения задачи, и вы все равно получите валидированный JSON, соответствующий вашей схеме в конце. Определите JSON Schema для нужной вам структуры, и SDK валидирует вывод в соответствии с ней, переспрашивая при несоответствии. Если валидация не будет успешной в пределах лимита повторных попыток, результатом будет ошибка вместо структурированных данных; см. Обработка ошибок.

Для полной типобезопасности используйте Zod (TypeScript) или Pydantic (Python) для определения вашей схемы и получения строго типизированных объектов.

Зачем нужны структурированные выходные данные?

По умолчанию агенты возвращают произвольный текст, что подходит для чата, но не для случаев, когда выходные данные нужно использовать программно. Структурированные выходные данные предоставляют типизированные данные, которые можно передавать непосредственно в логику приложения, базу данных или компоненты пользовательского интерфейса.

Рассмотрим приложение с рецептами, в котором агент ищет рецепты в интернете и возвращает их. Без структурированных выходных данных вы получаете произвольный текст, который придётся разбирать самостоятельно. Со структурированными выходными данными вы определяете нужную структуру и получаете типизированные данные, которые можно сразу использовать в приложении.

[accordion] Без структурированных выходных данных

Here's a classic chocolate chip cookie recipe!

**Chocolate Chip Cookies**
Prep time: 15 minutes | Cook time: 10 minutes

Ingredients:
- 2 1/4 cups all-purpose flour
- 1 cup butter, softened
...

Чтобы использовать это в приложении, вам пришлось бы извлекать название, преобразовывать «15 minutes» в число, отделять ингредиенты от инструкций и учитывать непоследовательное форматирование в разных ответах.

[accordion] Со структурированными выходными данными

{
  "name": "Chocolate Chip Cookies",
  "prep_time_minutes": 15,
  "cook_time_minutes": 10,
  "ingredients": [
    { "item": "all-purpose flour", "amount": 2.25, "unit": "cups" },
    { "item": "butter, softened", "amount": 1, "unit": "cup" }
    // ...
  ],
  "steps": ["Preheat oven to 375°F", "Cream butter and sugar" /* ... */]
}

Типизированные данные, которые можно сразу использовать в пользовательском интерфейсе.

Быстрый старт

Чтобы использовать структурированные выводы, определите JSON Schema, описывающую форму данных, которые вы хотите, затем передайте его в query() через опцию outputFormat (TypeScript) или output_format (Python). Когда агент завершит работу, сообщение результата включает поле structured_output с валидированными данными, соответствующими вашей схеме.

Прежде чем запускать примеры на этой странице, установите Claude Agent SDK, следуя руководству быстрый старт. Пример ниже просит агента исследовать Anthropic и вернуть название компании, год основания и штаб-квартиру как структурированный вывод.

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"]
};

try {
  for await (const message of query({
    prompt: "Research Anthropic and provide key company information",
    options: {
      outputFormat: {
        type: "json_schema",
        schema: schema
      }
    }
  })) {
    // Сообщение результата содержит structured_output с валидированными данными
    if (message.type === "result" && message.subtype === "success" && !message.is_error && message.structured_output) {
      console.log(message.structured_output);
      // { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }
    }
  }
} catch (error) {
  // Одиночный запрос query() выбрасывает исключение после выдачи результата ошибки, такого как
  // error_max_structured_output_retries; см. раздел Обработка ошибок.
  console.error(`Session ended with an error: ${error}`);
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

# Определите форму данных, которые вы хотите получить
schema = {
    "type": "object",
    "properties": {
        "company_name": {"type": "string"},
        "founded_year": {"type": "number"},
        "headquarters": {"type": "string"},
    },
    "required": ["company_name"],
}


async def main():
    try:
        async for message in query(
            prompt="Research Anthropic and provide key company information",
            options=ClaudeAgentOptions(
                output_format={"type": "json_schema", "schema": schema}
            ),
        ):
            # Сообщение результата содержит structured_output с валидированными данными
            if (
                isinstance(message, ResultMessage)
                and not message.is_error
                and message.structured_output
            ):
                print(message.structured_output)
                # {'company_name': 'Anthropic', 'founded_year': 2021, 'headquarters': 'San Francisco, CA'}
    except Exception as error:
        # Одиночный запрос query() выбрасывает исключение после выдачи результата ошибки, такого как
        # error_max_structured_output_retries; см. раздел Обработка ошибок.
        print(f"Session ended with an error: {error}")


asyncio.run(main())

Типобезопасные схемы с Zod и Pydantic

Вместо написания JSON Schema вручную, вы можете использовать Zod (TypeScript) или Pydantic (Python) для определения вашей схемы. Эти библиотеки генерируют JSON Schema для вас и позволяют разобрать ответ в полностью типизированный объект, который вы можете использовать во всей кодовой базе с автодополнением и проверкой типов.

Пример ниже определяет схему для плана реализации функции с резюме, списком шагов (каждый с уровнем сложности) и потенциальными рисками. Агент планирует функцию и возвращает типизированный объект FeaturePlan. Затем вы можете получить доступ к свойствам, таким как plan.summary, и перебирать plan.steps с полной типобезопасностью.

SDK проверяет схемы с использованием JSON Schema draft-07, поэтому схемы, которые объявляют более новую версию, отклоняются. Zod по умолчанию нацелен на draft 2020-12, поэтому передайте target: "draft-7" при преобразовании вашей схемы.

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

// Определите схему с 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())
});

type FeaturePlan = z.infer<typeof FeaturePlan>;

// Преобразуйте в JSON Schema, используя целевой draft-07, который ожидает SDK
const schema = z.toJSONSchema(FeaturePlan, { target: "draft-7" });

// Используйте в запросе
try {
  for await (const message of query({
    prompt:
      "Plan how to add dark mode support to a React app. Break it into implementation steps.",
    options: {
      outputFormat: {
        type: "json_schema",
        schema: schema
      }
    }
  })) {
    if (message.type === "result" && message.subtype === "success" && !message.is_error && message.structured_output) {
      // Валидируйте и получите полностью типизированный результат
      const parsed = FeaturePlan.safeParse(message.structured_output);
      if (parsed.success) {
        const plan: FeaturePlan = parsed.data;
        console.log(`Feature: ${plan.feature_name}`);
        console.log(`Summary: ${plan.summary}`);
        plan.steps.forEach((step) => {
          console.log(`${step.step_number}. [${step.estimated_complexity}] ${step.description}`);
        });
      }
    }
  }
} catch (error) {
  // Одноразовый запрос query() выбрасывает исключение после выдачи результата ошибки, такого как
  // error_max_structured_output_retries; см. раздел Обработка ошибок.
  console.error(`Session ended with an error: ${error}`);
}
import asyncio
from pydantic import BaseModel
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage


class Step(BaseModel):
    step_number: int
    description: str
    estimated_complexity: str  # 'low', 'medium', 'high'


class FeaturePlan(BaseModel):
    feature_name: str
    summary: str
    steps: list[Step]
    risks: list[str]


async def main():
    try:
        async for message in query(
            prompt="Plan how to add dark mode support to a React app. Break it into implementation steps.",
            options=ClaudeAgentOptions(
                output_format={
                    "type": "json_schema",
                    "schema": FeaturePlan.model_json_schema(),
                }
            ),
        ):
            if (
                isinstance(message, ResultMessage)
                and not message.is_error
                and message.structured_output
            ):
                # Валидируйте и получите полностью типизированный результат
                plan = FeaturePlan.model_validate(message.structured_output)
                print(f"Feature: {plan.feature_name}")
                print(f"Summary: {plan.summary}")
                for step in plan.steps:
                    print(
                        f"{step.step_number}. [{step.estimated_complexity}] {step.description}"
                    )
    except Exception as error:
        # Одноразовый запрос query() выбрасывает исключение после выдачи результата ошибки, такого как
        # error_max_structured_output_retries; см. раздел Обработка ошибок.
        print(f"Session ended with an error: {error}")


asyncio.run(main())

Конфигурация формата вывода

Опция outputFormat (TypeScript) или output_format (Python) принимает объект с:

  • type: Установите на "json_schema" для структурированных выводов
  • schema: Объект JSON Schema, определяющий структуру вашего вывода. Вы можете сгенерировать это из схемы Zod с z.toJSONSchema(schema, { target: "draft-7" }) или модели Pydantic с .model_json_schema()

SDK поддерживает стандартные функции JSON Schema, включая все базовые типы (object, array, string, number, boolean, null), enum, const, required, вложенные объекты и определения $ref. Для полного списка поддерживаемых функций и ограничений см. Ограничения JSON Schema.

Схема, которая не является действительной JSON Schema, приводит к сбою запуска при запуске с ошибкой, указывающей на проблему. До версии 2.1.205 недействительная схема молча игнорировалась, и агент возвращал неструктурированный текст.

Ключевое слово format, такое как "format": "email", принимается как аннотация и не применяется валидатором SDK. До версии 2.1.205 любая схема, содержащая format, рассматривалась как недействительная.

Пример: агент отслеживания TODO

Этот пример демонстрирует, как работают структурированные выводы с многошаговым использованием инструментов. Агент должен найти комментарии TODO в кодовой базе, а затем найти информацию git blame для каждого. Он автономно решает, какие инструменты использовать (Grep для поиска, Bash для запуска команд git) и объединяет результаты в один структурированный ответ.

Схема включает необязательные поля (author и date), так как информация git blame может быть недоступна для всех файлов. Агент заполняет то, что может найти, и опускает остальное.

import { query } from "@anthropic-ai/claude-agent-sdk";

// Определите структуру для извлечения TODO
const todoSchema = {
  type: "object",
  properties: {
    todos: {
      type: "array",
      items: {
        type: "object",
        properties: {
          text: { type: "string" },
          file: { type: "string" },
          line: { type: "number" },
          author: { type: "string" },
          date: { type: "string" }
        },
        required: ["text", "file", "line"]
      }
    },
    total_count: { type: "number" }
  },
  required: ["todos", "total_count"]
};

// Агент использует Grep для поиска TODO, Bash для получения информации git blame
try {
  for await (const message of query({
    prompt: "Find all TODO comments in this codebase and identify who added them",
    options: {
      outputFormat: {
        type: "json_schema",
        schema: todoSchema
      }
    }
  })) {
    if (message.type === "result" && message.subtype === "success" && !message.is_error && message.structured_output) {
      const data = message.structured_output as { total_count: number; todos: Array<{ file: string; line: number; text: string; author?: string; date?: string }> };
      console.log(`Found ${data.total_count} TODOs`);
      data.todos.forEach((todo) => {
        console.log(`${todo.file}:${todo.line} - ${todo.text}`);
        if (todo.author) {
          console.log(`  Added by ${todo.author} on ${todo.date}`);
        }
      });
    }
  }
} catch (error) {
  // A single-shot query() throws after yielding an error result, such as
  // error_max_structured_output_retries; see the Error handling section.
  console.error(`Session ended with an error: ${error}`);
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

# Определите структуру для извлечения TODO
todo_schema = {
    "type": "object",
    "properties": {
        "todos": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "text": {"type": "string"},
                    "file": {"type": "string"},
                    "line": {"type": "number"},
                    "author": {"type": "string"},
                    "date": {"type": "string"},
                },
                "required": ["text", "file", "line"],
            },
        },
        "total_count": {"type": "number"},
    },
    "required": ["todos", "total_count"],
}


async def main():
    # Агент использует Grep для поиска TODO, Bash для получения информации git blame
    try:
        async for message in query(
            prompt="Find all TODO comments in this codebase and identify who added them",
            options=ClaudeAgentOptions(
                output_format={"type": "json_schema", "schema": todo_schema}
            ),
        ):
            if (
                isinstance(message, ResultMessage)
                and not message.is_error
                and message.structured_output
            ):
                data = message.structured_output
                print(f"Found {data['total_count']} TODOs")
                for todo in data["todos"]:
                    print(f"{todo['file']}:{todo['line']} - {todo['text']}")
                    if "author" in todo:
                        print(f"  Added by {todo['author']} on {todo['date']}")
    except Exception as error:
        # A single-shot query() raises after yielding an error result, such as
        # error_max_structured_output_retries; see the Error handling section.
        print(f"Session ended with an error: {error}")


asyncio.run(main())

Распознавание вызова инструмента StructuredOutput

Когда вы задаёте outputFormat в TypeScript или output_format в Python, ваш поток сообщений содержит вызов инструмента с именем StructuredOutput, который вы никогда не определяли. Claude Code добавляет этот инструмент в сессию, и агент вызывает его, чтобы вернуть свой ответ. Системное сообщение init перечисляет его в массиве tools, а каждый вызов приходит в виде двух блоков:

  • Блок tool_use с именем StructuredOutput, входными данными которого является объект, созданный агентом по вашей схеме
  • Блок tool_result с текстом Structured output provided successfully

Когда этот объект не соответствует вашей схеме, tool_result вместо этого содержит is_error: true, и агент снова вызывает StructuredOutput, поэтому ваш поток может содержать несколько вызовов для одного запроса. Считывайте проверенные данные из structured_output в сообщении результата, а не из блока tool_use, и обратитесь к разделу Обработка ошибок, чтобы узнать, какой результат вы получите, если ни одна попытка не увенчается успехом.

Если ваше приложение отображает вызовы инструментов или записывает их в лог, пропускайте вызовы с именем StructuredOutput, чтобы этот механизм не выглядел как шаг, выбранный агентом. Блок tool_result не содержит имени инструмента, а только tool_use_id вызова, на который он отвечает, поэтому сопоставляйте его с id каждого пропущенного вами вызова.

Обработка ошибок

Генерация структурированного вывода может не удаться, когда агент не может создать валидный JSON, соответствующий вашей схеме. Это обычно происходит, когда схема слишком сложна для задачи, сама задача неоднозначна или агент достигает лимита повторных попыток при попытке исправить ошибки валидации. Это также может произойти без каких-либо ошибок валидации: переключение на резервную модель может отменить уже завершённый вывод в середине потока, и если никакая повторная попытка его не заменит, выполнение завершится с той же ошибкой. Проверьте список errors в сообщении результата с ошибкой, чтобы различить две причины перед отладкой вашей схемы.

Проверьте subtype сообщения результата:

Subtype Значение
success Выполнение завершилось без subtype ошибки
error_max_structured_output_retries После нескольких попыток не осталось валидного вывода (ошибки валидации или отмена вывода при переключении на резервную модель без успешной повторной попытки)

Subtype success сам по себе не означает, что выполнение дало ответ. Рассматривайте результат как ошибку, когда is_error результата равен true или structured_output отсутствует:

  • is_error в результате равен true: например, последний запрос к модели завершился ошибкой API или отказом. Не используйте значение structured_output из такого результата.
  • structured_output отсутствует: чтобы найти причину, см. structured_output is None but the result says success

Пример ниже применяет эти проверки:

import { query } from "@anthropic-ai/claude-agent-sdk";

const contactSchema = {
  type: "object",
  properties: {
    name: { type: "string" },
    email: { type: "string" }
  },
  required: ["name"]
};

try {
  for await (const msg of query({
    prompt: "Extract contact info from the document",
    options: {
      outputFormat: {
        type: "json_schema",
        schema: contactSchema
      }
    }
  })) {
    if (msg.type === "result") {
      if (msg.subtype === "success" && !msg.is_error && msg.structured_output) {
        // Use the validated output
        console.log(msg.structured_output);
      } else if (msg.subtype === "error_max_structured_output_retries") {
        console.error("Could not produce valid output");
      } else {
        console.error("Run ended with an error or without a structured output");
      }
    }
  }
} catch (error) {
  // A single-shot query() throws after yielding an error result. If the
  // failure was an error result, the error subtype branches above have
  // already run; connection or process failures yield no result message.
  console.log(`Session ended with an error: ${error}`);
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

contact_schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "email": {"type": "string"},
    },
    "required": ["name"],
}


async def main():
    try:
        async for message in query(
            prompt="Extract contact info from the document",
            options=ClaudeAgentOptions(
                output_format={"type": "json_schema", "schema": contact_schema}
            ),
        ):
            if isinstance(message, ResultMessage):
                if (
                    message.subtype == "success"
                    and not message.is_error
                    and message.structured_output
                ):
                    # Use the validated output
                    print(message.structured_output)
                elif message.subtype == "error_max_structured_output_retries":
                    print("Could not produce valid output")
                else:
                    print("Run ended with an error or without a structured output")
    except Exception as error:
        # A single-shot query() raises after yielding an error result. If the
        # failure was an error result, the error subtype branches above have
        # already run; connection or process failures yield no result message.
        print(f"Session ended with an error: {error}")


asyncio.run(main())

Советы по избежанию ошибок:

  • Держите схемы сосредоточенными. Глубоко вложенные схемы со многими обязательными полями сложнее удовлетворить. Начните с простого и добавляйте сложность по мере необходимости.
  • Соответствуйте схему задаче. Если задача может не иметь всю информацию, которую требует ваша схема, сделайте эти поля необязательными.
  • Используйте четкие промпты. Неоднозначные промпты затрудняют для агента понимание того, какой вывод создавать.

Связанные ресурсы

  • Документация JSON Schema: изучите синтаксис JSON Schema для определения сложных схем с вложенными объектами, массивами, перечислениями и ограничениями валидации
  • API Structured Outputs: используйте структурированные выводы с Claude API непосредственно для однооходовых запросов без использования инструментов
  • Custom tools: дайте вашему агенту пользовательские инструменты для вызова во время выполнения перед возвращением структурированного вывода