گرفتن خروجی ساختاریافته از ایجنتها
خروجیهای ساختاریافته به تو اجازه میدهند شکل دقیق دادهای را که میخواهی از ایجنت بگیری تعریف کنی. ایجنت میتواند از هر ابزاری که برای انجام کار لازم دارد استفاده کند و تو در پایان همچنان JSONِ معتبری مطابق با اسکیمای خودت دریافت میکنی. یک JSON Schema برای ساختاری که نیاز داری تعریف کن؛ SDK خروجی را در برابر آن اعتبارسنجی میکند و در صورت ناهماهنگی دوباره پرامپت میدهد. اگر اعتبارسنجی در محدودهی تعداد تلاشهای مجاز موفق نشود، نتیجه بهجای دادهی ساختاریافته یک خطا خواهد بود؛ به مدیریت خطا نگاه کن.
برای ایمنی کاملِ نوع (type safety)، از Zod (در TypeScript) یا Pydantic (در Python) برای تعریف اسکیما و گرفتن آبجکتهای قویاً تایپشده استفاده کن.
چرا خروجیهای ساختاریافته؟
Section titled “چرا خروجیهای ساختاریافته؟”ایجنتها بهصورت پیشفرض متنِ آزاد برمیگردانند که برای چت خوب کار میکند اما وقتی بخواهی خروجی را بهصورت برنامهنویسیشده استفاده کنی، مناسب نیست. خروجیهای ساختاریافته به تو دادهی تایپشده میدهند که میتوانی مستقیم به منطق برنامه، پایگاهداده یا کامپوننتهای UI پاس بدهی.
یک اپلیکیشن دستور پخت را در نظر بگیر که در آن ایجنت در وب جستوجو میکند و دستورهای پخت را برمیگرداند. بدون خروجیهای ساختاریافته، متنِ آزادی میگیری که باید خودت آن را تجزیه کنی. با خروجیهای ساختاریافته، شکلی را که میخواهی تعریف میکنی و دادهی تایپشدهای میگیری که مستقیماً در اپلیکیشن قابلاستفاده است.
بدون خروجیهای ساختاریافته
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...برای استفاده از این در اپلیکیشنت، باید عنوان را بیرون بکشی، «۱۵ دقیقه» را به عدد تبدیل کنی، مواد اولیه را از دستورالعملها جدا کنی و قالببندیِ ناهماهنگ در پاسخهای مختلف را مدیریت کنی.
با خروجیهای ساختاریافته
{ "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" /* ... */]}دادهی تایپشدهای که مستقیماً در UI خودت قابلاستفاده است.
شروع سریع
Section titled “شروع سریع”برای استفاده از خروجیهای ساختاریافته، یک JSON Schema که شکل دادهی موردنظرت را توصیف میکند تعریف کن، سپس آن را از طریق گزینهی outputFormat (در TypeScript) یا گزینهی output_format (در Python) به query() پاس بده. وقتی ایجنت کارش تمام شد، پیامِ نتیجه شامل فیلد structured_output با دادهی اعتبارسنجیشدهی مطابق اسکیمای توست.
مثال زیر از ایجنت میخواهد دربارهی Anthropic تحقیق کند و نام شرکت، سال تأسیس و دفتر مرکزی را بهصورت خروجی ساختاریافته برگرداند.
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define the shape of data you want backconst 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: 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" } }}import asynciofrom claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
# Define the shape of data you want backschema = { "type": "object", "properties": { "company_name": {"type": "string"}, "founded_year": {"type": "number"}, "headquarters": {"type": "string"}, }, "required": ["company_name"],}
async def main(): 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'}
asyncio.run(main())اسکیماهای ایمن از نظر نوع با Zod و Pydantic
Section titled “اسکیماهای ایمن از نظر نوع با Zod و Pydantic”بهجای نوشتن دستیِ JSON Schema، میتوانی از Zod (در TypeScript) یا Pydantic (در Python) برای تعریف اسکیما استفاده کنی. این کتابخانهها JSON Schema را برایت میسازند و به تو اجازه میدهند پاسخ را به یک آبجکتِ کاملاً تایپشده تجزیه کنی که در سراسر کدبیست با تکمیلِ خودکار و بررسیِ نوع قابلاستفاده است.
مثال زیر اسکیمایی برای یک طرحِ پیادهسازیِ قابلیت تعریف میکند که شامل یک خلاصه، فهرستی از گامها (هرکدام با سطح پیچیدگی) و ریسکهای احتمالی است. ایجنت قابلیت را برنامهریزی میکند و یک آبجکتِ تایپشدهی FeaturePlan برمیگرداند. بعد میتوانی به ویژگیهایی مثل plan.summary دسترسی پیدا کنی و روی plan.steps با ایمنیِ نوعِ کامل پیمایش کنی.
import { z } from "zod";import { query } from "@anthropic-ai/claude-agent-sdk";
// Define schema with Zodconst 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 Schemaconst schema = z.toJSONSchema(FeaturePlan);
// Use in queryfor 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}`); }); } }}import asynciofrom pydantic import BaseModelfrom 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(): 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}" )
asyncio.run(main())مزایا:
- استنتاجِ کاملِ نوع (در TypeScript) و راهنمای نوع (در Python)
- اعتبارسنجی در زمان اجرا با
safeParse()یاmodel_validate() - پیامهای خطای بهتر
- اسکیماهای قابلِ ترکیب و قابلِ استفادهی مجدد
پیکربندی قالب خروجی
Section titled “پیکربندی قالب خروجی”گزینهی outputFormat (در TypeScript) یا output_format (در Python) آبجکتی میپذیرد با این موارد:
type: برای خروجیهای ساختاریافته روی"json_schema"تنظیم کنschema: یک آبجکتِ JSON Schema که ساختار خروجیات را تعریف میکند. میتوانی این را از یک اسکیمای Zod باz.toJSONSchema()یا از یک مدلِ Pydantic با.model_json_schema()بسازی
این SDK از قابلیتهای استانداردِ JSON Schema پشتیبانی میکند، از جمله همهی انواعِ پایه (object، array، string، number، boolean، null)، enum، const، required، آبجکتهای تودرتو و تعاریف $ref. برای فهرست کامل قابلیتهای پشتیبانیشده و محدودیتها، به محدودیتهای JSON Schema نگاه کن.
مثال: ایجنت ردیابی TODO
Section titled “مثال: ایجنت ردیابی TODO”این مثال نشان میدهد که خروجیهای ساختاریافته چطور با استفادهی چندمرحلهای از ابزار کار میکنند. ایجنت باید کامنتهای TODO را در کدبیس پیدا کند، سپس برای هرکدام اطلاعات git blame را جستوجو کند. ایجنت خودش بهطور مستقل تصمیم میگیرد از کدام ابزارها استفاده کند (Grep برای جستوجو، Bash برای اجرای دستورهای git) و نتایج را در یک پاسخِ ساختاریافتهی واحد ترکیب میکند.
اسکیما شامل فیلدهای اختیاری (author و date) است، چون ممکن است اطلاعات git blame برای همهی فایلها در دسترس نباشد. ایجنت هرچه را پیدا کند پر میکند و بقیه را حذف میکند.
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define structure for TODO extractionconst 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 infofor 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}`); } }); }}import asynciofrom claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
# Define structure for TODO extractiontodo_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 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']}")
asyncio.run(main())مدیریت خطا
Section titled “مدیریت خطا”تولید خروجیِ ساختاریافته میتواند شکست بخورد، وقتی ایجنت نتواند JSONِ معتبرِ مطابق با اسکیمای تو را تولید کند. این معمولاً وقتی پیش میآید که اسکیما برای کار بیشازحد پیچیده است، خودِ کار مبهم است، یا ایجنت در تلاش برای رفع خطاهای اعتبارسنجی به سقفِ تعداد تلاشها میرسد. همچنین میتواند بدون هیچ شکستِ اعتبارسنجی رخ بدهد: یک بازگشت به مدلِ جایگزین (model fallback) میتواند خروجیِ ازپیشتکمیلشدهای را در میانهی استریم پس بگیرد، و اگر هیچ تلاش مجددی جایگزین آن نشود، اجرا با همان خطا پایان مییابد. برای تشخیص این دو علت از هم پیش از اشکالزدایی اسکیما، متنِ errors در نتیجه را بررسی کن.
وقتی خطایی رخ بدهد، پیامِ نتیجه یک subtype دارد که نشان میدهد چه اشتباهی پیش آمده است:
| Subtype | معنا |
|---|---|
success | خروجی با موفقیت تولید و اعتبارسنجی شد |
error_max_structured_output_retries | پس از چند تلاش هیچ خروجیِ معتبری باقی نماند (شکستهای اعتبارسنجی، یا پسگرفتنِ خروجی بر اثر بازگشت به مدل جایگزین بدون تلاش مجددِ موفق) |
مثال زیر فیلد subtype را بررسی میکند تا مشخص شود خروجی با موفقیت تولید شده یا باید یک شکست را مدیریت کنی:
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") { // Handle the failure - retry with simpler prompt, fall back to unstructured, etc. console.error("Could not produce valid output"); } }}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": # Handle the failure print("Could not produce valid output")نکتههایی برای جلوگیری از خطا:
- اسکیماها را متمرکز نگه دار. اسکیماهای عمیقاً تودرتو با فیلدهای الزامیِ زیاد سختتر ارضا میشوند. ساده شروع کن و در صورت نیاز پیچیدگی اضافه کن.
- اسکیما را با کار هماهنگ کن. اگر ممکن است کار همهی اطلاعاتی را که اسکیمایت لازم دارد نداشته باشد، آن فیلدها را اختیاری کن.
- پرامپتهای شفاف بنویس. پرامپتهای مبهم تشخیصِ خروجیِ موردنظر را برای ایجنت سختتر میکنند.
منابع مرتبط
Section titled “منابع مرتبط”- مستندات JSON Schema: نحوِ JSON Schema را برای تعریف اسکیماهای پیچیده با آبجکتهای تودرتو، آرایهها، enumها و قیدهای اعتبارسنجی یاد بگیر
- خروجیهای ساختاریافته در API: از خروجیهای ساختاریافته مستقیماً با Claude API برای درخواستهای تکنوبتی بدون استفاده از ابزار استفاده کن
- ابزارهای سفارشی: به ایجنتت ابزارهای سفارشی بده تا پیش از برگرداندنِ خروجیِ ساختاریافته، در حین اجرا آنها را فراخوانی کند