תיעוד 177
קבלת פלט מובנה מסוכנים
החזרת JSON מאומת מתהליכי עבודה של סוכנים באמצעות JSON Schema, Zod או Pydantic. קבלת נתונים מובנים ובטוחים מבחינת טיפוסים אחרי שימוש בכלים לאורך כמה תורות.
פלטים מובנים מאפשרים להגדיר את הצורה המדויקת של הנתונים שרוצים לקבל מסוכן. הסוכן יכול להשתמש בכל הכלים שהוא צריך כדי להשלים את המשימה, ועדיין מקבלים בסוף JSON מאומת שתואם לסכמה. מגדירים JSON Schema למבנה הנדרש, וה-SDK מאמת את הפלט מולו, ומנחה מחדש כשיש אי התאמה. אם האימות לא מצליח בתוך מגבלת הניסיונות החוזרים, התוצאה היא שגיאה במקום נתונים מובנים. ראו טיפול בשגיאות.
לבטיחות טיפוסים מלאה, משתמשים ב-Zod (TypeScript) או ב-Pydantic (Python) כדי להגדיר את הסכמה ולקבל בחזרה אובייקטים עם טיפוסים חזקים.
#למה פלטים מובנים?
סוכנים מחזירים כברירת מחדל טקסט חופשי, וזה מתאים לצ'אט אבל לא כשצריך להשתמש בפלט באופן תכנותי. פלטים מובנים נותנים נתונים עם טיפוסים שאפשר להעביר ישירות ללוגיקה של היישום, למסד הנתונים או לרכיבי ממשק.
נניח אפליקציית מתכונים שבה סוכן מחפש באינטרנט ומביא מתכונים. בלי פלטים מובנים מקבלים טקסט חופשי שצריך לפרסר בעצמכם. עם פלטים מובנים מגדירים את הצורה הרצויה ומקבלים נתונים עם טיפוסים שאפשר להשתמש בהם ישירות באפליקציה.
#בלי פלטים מובנים
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" למספר, להפריד מצרכים מהוראות, ולטפל בעיצוב לא עקבי בין תשובות.
#עם פלטים מובנים
{
"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 לפי ה-quickstart. הדוגמה הבאה מבקשת מהסוכן לחקור את Anthropic ולהחזיר את שם החברה, שנת היסוד והמטה כפלט מובנה.
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define the shape of data you want back
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
}
}
})) {
// The result message contains structured_output with validated data
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" }
}
}
} 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
# Define the shape of data you want back
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}
),
):
# The result message contains structured_output with validated data
if isinstance(message, ResultMessage) and message.structured_output:
print(message.structured_output)
# {'company_name': 'Anthropic', 'founded_year': 2021, 'headquarters': 'San Francisco, CA'}
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())#סכמות בטוחות מבחינת טיפוסים עם 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";
// Define schema with 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>;
// Convert to JSON Schema using the draft-07 target the SDK expects
const schema = z.toJSONSchema(FeaturePlan, { target: "draft-7" });
// Use in query
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.structured_output) {
// Validate and get fully typed result
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) {
// 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 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 message.structured_output:
# Validate and get fully typed result
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:
# 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())#הגדרת פורמט הפלט
האפשרות 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 תקין מפילה את ההרצה בהפעלה, עם שגיאה שמציינת את הבעיה. לפני v2.1.205, סכמה לא תקינה התעלמה בשקט והסוכן החזיר טקסט לא מובנה.
מילת המפתח format, למשל "format": "email", מתקבלת כהערה ואינה נאכפת על ידי המאמת של ה-SDK. לפני v2.1.205, כל סכמה שהכילה format נחשבה לא תקינה.
#דוגמה: סוכן למעקב אחרי TODO
הדוגמה הזו מראה איך פלטים מובנים עובדים עם שימוש בכלים בכמה שלבים. הסוכן צריך למצוא הערות TODO בבסיס הקוד, ואז לאתר מידע git blame לכל אחת. הוא מחליט בעצמו באילו כלים להשתמש (Grep לחיפוש, Bash להרצת פקודות git) ומאחד את התוצאות לתשובה מובנית אחת.
הסכמה כוללת שדות אופציונליים (author ו-date) כי ייתכן שמידע git blame לא זמין לכל הקבצים. הסוכן ממלא את מה שהוא מצליח למצוא ומשמיט את השאר.
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define structure for TODO extraction
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"]
};
// Agent uses Grep to find TODOs, Bash to get git blame info
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.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
# Define structure for TODO extraction
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():
# Agent uses Grep to find TODOs, Bash to get git blame info
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 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 מוסיף את הכלי הזה לסשן, והסוכן קורא לו כדי להחזיר את התשובה. הודעת האתחול של המערכת מציגה אותו במערך 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 תקין שתואם לסכמה שלך. זה קורה בדרך כלל כאשר הסכמה מורכבת מדי עבור המשימה, המשימה עצמה עמומה, או שהסוכן מגיע למגבלת הניסיונות החוזרים בניסיון לתקן שגיאות אימות. זה יכול לקרות גם בלי שום כשל אימות: model fallback יכול למשוך בחזרה פלט שכבר הושלם באמצע הזרם, ואם אין ניסיון חוזר שמחליף אותו, הריצה מסתיימת באותה שגיאה. בדוק את רשימת errors בהודעת תוצאת השגיאה כדי להבחין בין שני הגורמים לפני ניפוי השגיאות בסכמה.
כאשר מתרחשת שגיאה, להודעת התוצאה יש subtype שמציין מה השתבש:
| Subtype | משמעות |
|---|---|
success | הפלט נוצר ואומת בהצלחה |
error_max_structured_output_retries | לא נותר פלט תקין אחרי כמה ניסיונות (כשלי אימות, או משיכה לאחור של model fallback בלי ניסיון חוזר מוצלח) |
תוצאה יכולה גם להסתיים עם subtype success אבל בלי ערך structured_output, למשל כאשר הריצה מסתיימת בלי שהסוכן ייצר פלט מובנה. התייחס גם למקרה הזה כאל כישלון. רשומת פתרון הבעיות structured_output is None but the result says success מכסה את המקרה הזה. הדוגמה למטה מתייחסת לתוצאה כמוצלחת רק כאשר subtype הוא success ו-structured_output קיים, ומטפלת בכל תוצאה אחרת ככישלון:
TypeScript
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.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 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}`);
}Python
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 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 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 להגדרת סכמות מורכבות עם אובייקטים מקוננים, מערכים, enums ואילוצי אימות
- API Structured Outputs: השתמש בפלטים מובנים עם Claude API ישירות לבקשות של תור יחיד בלי שימוש בכלים
- כלים מותאמים אישית: תן לסוכן כלים מותאמים אישית לקריאה במהלך הביצוע לפני החזרת פלט מובנה