תיעוד 136
קבלת פלט מובנה מסוכנים
החזרת JSON שעבר אימות מתהליכי עבודה של סוכנים באמצעות JSON Schema, Zod או Pydantic. קבלת נתונים מובנים ובטוחים מבחינת טיפוסים (type-safe) לאחר שימוש רב-שלבי בכלים.
פלטים מובנים מאפשרים לך להגדיר את המבנה המדויק של הנתונים שברצונך לקבל בחזרה מסוכן. הסוכן יכול להשתמש בכל הכלים הדרושים לו להשלמת המשימה, ובסופו של דבר עדיין תקבל JSON שעבר אימות ותואם לסכמה שלך. הגדר JSON Schema עבור המבנה הנדרש לך, וה-SDK יאמת את הפלט מולו, ויבקש מהסוכן הנחיה מחדש (re-prompting) במקרה של אי-התאמה. אם האימות אינו מצליח במסגרת מגבלת הניסיונות החוזרים, התוצאה היא שגיאה במקום נתונים מובנים, ראה טיפול בשגיאות.
לבטיחות טיפוסים מלאה, השתמש ב-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 עם נתונים מאומתים התואמים לסכמה שלך.
הדוגמה שלהלן מבקשת מהסוכן לחקור על Anthropic ולהחזיר את שם החברה, שנת ההקמה והמטה כפלט מובנה.
#TypeScript
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}`);
}#Python
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 עבורך ומאפשרות לך לנתח (parse) את התגובה לאובייקט בעל טיפוסיות מלאה שתוכל להשתמש בו לאורך כל בסיס הקוד שלך עם השלמה אוטומטית ובדיקת טיפוסים.
הדוגמה שלהלן מגדירה סכמה עבור תוכנית יישום תכונה הכוללת סיכום, רשימת שלבים (כל אחד עם רמת מורכבות), וסיכונים פוטנציאליים. הסוכן מתכנן את התכונה ומחזיר אובייקט FeaturePlan בעל טיפוס מוגדר. לאחר מכן תוכל לגשת למאפיינים כמו plan.summary ולבצע איטרציה על plan.steps בבטיחות טיפוסים מלאה.
ה-SDK מאמת סכמות באמצעות JSON Schema draft-07, ולכן סכמות שמצהירות על גרסה חדשה יותר נדחות. Zod מכוון ל-draft 2020-12 כברירת מחדל, לכן העבר target: "draft-7" בעת המרת הסכמה שלך.
#TypeScript
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}`);
}#Python
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", מתקבלת כהערה (annotation) ואינה נאכפת על ידי המאמת (validator) של ה-SDK. לפני גרסה v2.1.205, כל סכמה שהכילה format נחשבה ללא תקינה.
#דוגמה: סוכן מעקב אחר TODO
דוגמה זו מדגימה כיצד פלטים מובנים עובדים עם שימוש רב-שלבי בכלים. על הסוכן למצוא הערות TODO בבסיס הקוד, ולאחר מכן לחפש מידע git blame עבור כל אחת מהן. הוא מחליט באופן עצמאי באילו כלים להשתמש (Grep כדי לחפש, Bash כדי להריץ פקודות git) ומשלב את התוצאות לתגובה מובנית אחת.
הסכמה כוללת שדות אופציונליים (author ו-date) מכיוון שייתכן שמידע git blame לא יהיה זמין עבור כל הקבצים. הסוכן ממלא את מה שהוא מצליח למצוא ומשמיט את השאר.
#TypeScript
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}`);
}#Python
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())#טיפול בשגיאות
יצירת פלט מובנה עלולה להיכשל כאשר הסוכן אינו מסוגל להפיק JSON תקין התואם לסכמה שלך. מצב זה קורה בדרך כלל כאשר הסכמה מורכבת מדי עבור המשימה, המשימה עצמה מעורפלת, או שהסוכן מגיע למגבלת הניסיונות החוזרים בניסיון לתקן שגיאות אימות. הדבר יכול לקרות גם ללא כל כשל באימות: נסיגה למודל חלופי (model fallback) עלולה לבטל פלט שכבר הושלם במהלך ההזרמה (mid-stream), ואם שום ניסיון חוזר אינו מחליף אותו, ההרצה מסתיימת עם אותה שגיאה. בדוק את רשימת ה-errors בהודעת התוצאה כדי להבדיל בין שני הגורמים לפני ניפוי שגיאות בסכמה שלך.
כאשר מתרחשת שגיאה, להודעת התוצאה יש subtype המציין מה השתבש:
| Subtype | משמעות |
|---|---|
success | הפלט נוצר ואומת בהצלחה |
error_max_structured_output_retries | לא נותר פלט תקין לאחר מספר ניסיונות (כשלי אימות, או ביטול עקב נסיגה למודל חלופי ללא ניסיון חוזר מוצלח) |
תוצאה יכולה גם להסתיים עם תת-סוג 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())טיפים למניעת שגיאות:
- שמור על סכמות ממוקדות. סכמות בעלות קינון עמוק עם שדות חובה רבים קשות יותר למילוי. התחל בפשטות והוסף מורכבות לפי הצורך.
- התאם את הסכמה למשימה. אם ייתכן שהמשימה אינה מכילה את כל המידע שהסכמה שלך דורשת, הגדר שדות אלה כאופציונליים.
- השתמש בהנחיות (prompts) ברורות. הנחיות מעורפלות מקשות על הסוכן לדעת איזה פלט להפיק.
#משאבים קשורים
- תיעוד JSON Schema: למד את תחביר JSON Schema להגדרת סכמות מורכבות עם אובייקטים מקוננים, מערכים, enums ומגבלות אימות.
- API Structured Outputs: השתמש בפלטים מובנים ישירות עם ה-API של Claude עבור בקשות של סבב בודד (single-turn) ללא שימוש בכלים.
- כלים מותאמים אישית (Custom tools): הענק לסוכן שלך כלים מותאמים אישית לקריאה במהלך הריצה לפני החזרת פלט מובנה.