Claude Batch API في TypeScript: استخراج كمي (2026)
١١ يونيو ٢٠٢٦
تُعالج واجهة برمجة تطبيقات Claude Batch ما يصل إلى 100,000 طلب Messages بشكل غير متزامن بخصم 50% من أسعار التوكنز القياسية، مع انتهاء معظم الدفعات (batches) في أقل من ساعة. يبني هذا البرنامج التعليمي خط معالجة (pipeline) بـ TypeScript يقوم بإرسال طلبات استخراج المخرجات المهيكلة، وفحص الحالة، والتحقق من صحة النتائج المتدفقة باستخدام Zod.
ملخص
ستقوم ببناء خط معالجة للاستخراج الجماعي — ثلاثة سكربتات قابلة للتشغيل بالإضافة إلى معالج نتائج مختبر — بناءً على @anthropic-ai/sdk 0.104.1: تحويل ملف JSONL لمراجعات العملاء الخام إلى طلبات دفعات يحمل كل منها مخطط output_config.format مشتق من Zod، وإرسالها كدفعة رسائل واحدة، وفحص processing_status حتى تقرأ ended، ثم دفق ملف النتائج وفرز كل إدخال إلى مستخرج / قابل لإعادة المحاولة / مرفوض باستخدام معالج مكتوب بدقة. كل كتلة برمجية تخضع لفحص النوع تحت TypeScript 6 strict، ويأتي معالج النتائج مع تسعة اختبارات أوفلاين تعمل بدون مفتاح API. ميزانية وقت البناء حوالي 30-40 دقيقة، بالإضافة إلى الوقت الذي تستغرقه دفعتك للمعالجة.
ما ستتعلمه
- كيفية عمل واجهة برمجة تطبيقات Message Batches — دورة الحياة، الحدود، ولماذا تكلف كل الأشياء نصف السعر
- كيفية تعريف مخطط Zod واحد يقود كلاً من تنسيق الطلب والتحقق من صحة النتائج
- كيفية بناء طلبات الدفعات بقيم
custom_idصالحة ومخرجات مهيكلة - كيفية إرسال دفعة وفحص حالتها مع عدادات الطلبات المباشرة
- كيفية التعامل مع جميع أنواع النتائج الأربعة — بما في ذلك غلاف الخطأ ذو الطبقتين الذي يخطئ فيه مثال TypeScript الرسمي
- كيفية دفق النتائج إلى ملفات JSONL مفهرسة بـ
custom_id - كيفية اختبار معالج النتائج أوفلاين، بدون مفتاح API
- ما هي التكلفة الفعلية لدفعة استخراج مكونة من 10,000 مراجعة
كيف تعمل واجهة برمجة تطبيقات Claude Batch؟
ترسل إلى واجهة برمجة تطبيقات Message Batches قائمة تصل إلى 100,000 طلب Messages (أو 256 ميجابايت، أيهما تصل إليه أولاً)، كل منها موسوم بـ custom_id فريد. تبدأ الدفعة في processing_status: "in_progress"، ويتم معالجة كل طلب بشكل مستقل، وتتحول الحالة إلى ended عندما ينتهي كل طلب — تكتمل معظم الدفعات في أقل من ساعة، وأي شيء لا يزال غير معالج عند مرور 24 ساعة تنتهي صلاحيته. يتم محاسبة جميع الاستخدامات بنسبة 50% من أسعار API القياسية، على كل من توكنز الإدخال والإخراج.1
أربعة أشياء تجعل الدفعات هي المسار الصحيح للاستخراج الجماعي بدلاً من حلقة for عبر messages.create():
- السعر. ينخفض سعر Claude Haiku 4.5 من 1 دولار / 5 دولارات لكل مليون توكن إدخال/إخراج إلى 0.50 دولار / 2.50 دولار. وينخفض Claude Sonnet 4.6 من 3 دولارات / 15 دولاراً إلى 1.50 دولار / 7.50 دولار.1
- الإنتاجية. تمتلك واجهة برمجة تطبيقات Batches حدود معدل خاصة بها، منفصلة عن واجهة برمجة تطبيقات Messages — في المستوى 1 (Tier 1) يمكنك الحصول على 100,000 طلب دفعة في طابور المعالجة بينما تظل حدودك المتزامنة دون مساس.2
- لا حاجة لمراقبة الاتصال. تهبط النتائج في ملف JSONL قابل للتنزيل يظل متاحاً لمدة 29 يوماً بعد إنشاء الدفعة.1
- المخرجات المهيكلة تعمل داخل الدفعات. تشير قائمة توافق الميزات الرسمية إلى ذلك مباشرة: "عالج المخرجات المهيكلة على نطاق واسع بخصم 50%."3
خط المعالجة الكامل الذي أنت على وشك بنائه:
flowchart TD
A[reviews.jsonl — raw text] --> B[buildRequest — Zod schema as output_config.format]
B --> C[messages.batches.create — status in_progress]
C --> D[poll retrieve every 60s]
D -->|processing_status == ended| E[stream results JSONL]
E --> F{result.type}
F -->|succeeded| G[stop-reason guards, then JSON.parse + Zod safeParse]
F -->|errored| H[invalid_request — rejected / server error — retryable]
F -->|canceled or expired| I[retryable]
G -->|valid| J[extracted.jsonl]
G -->|refusal, truncation, mismatch| K[failures.jsonl]
يستخدم هذا البرنامج التعليمي لـ Claude Batch نقاط النهاية GA — anthropic.messages.batches.* بدون ترويسات تجريبية (beta). لا يزال نطاق الأسماء client.beta.messages.batches في SDK موجوداً، لكنك تحتاجه فقط للميزات التجريبية مثل المخرجات الممتدة (المغطاة في قسم الملاحظات).
المتطلبات الأساسية
- Node.js 24 LTS (Active LTS، مدعوم حتى أبريل 2028)4
- مفتاح API من Anthropic في
ANTHROPIC_API_KEY(تشغيل دفعة حقيقية يكلف مالاً؛ اختبارات الأوفلاين في الخطوة 8 لا تكلف شيئاً) - الحزم المثبتة، الحالية على npm اعتباراً من 11 يونيو 2026:5
mkdir claude-batch-extraction && cd claude-batch-extraction
npm init -y
npm pkg set type=module
npm install @anthropic-ai/sdk@0.104.1 zod@4.4.3
npm install -D TypeScript@6.0.3 tsx@4.22.4 @types/node@24.13.2
الخطوة 1 — هيكلة المشروع
أنشئ ملف tsconfig.json صارم. كل كتلة TypeScript في هذا البرنامج التعليمي تخضع لفحص النوع مقابله كما هي مكتوبة:
{
"compilerOptions": {
"target": "es2023",
"module": "nodenext",
"moduleResolution": "nodenext",
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"allowImportingTsExtensions": true,
"noEmit": true,
"skipLibCheck": true,
"types": ["node"]
},
"include": ["src/**/*.ts", "test/**/*.ts"]
}
خيار "type": "module" من npm pkg set مهم: استخدام await في المستوى الأعلى في سكربتات الإرسال والفحص يتطلب وحدات ES (ES modules)، وسيرفض verbatimModuleSyntax تجميع صيغة ESM في حزمة CommonJS.
أضف بيانات الإدخال — ملف reviews.jsonl في جذر المشروع مع مراجعة واحدة لكل سطر. ثلاثة صفوف كافية لتجربة خط المعالجة بالكامل قبل توجيهه إلى بياناتك الحقيقية:
{"id": "1001", "text": "Battery dies before lunch and the proprietary charger is a pain. The screen is gorgeous though. Wish it charged over USB-C like everything else I own."}
{"id": "1002", "text": "Exactly what I wanted. Setup took five minutes and it has worked flawlessly for three months. Recommending it to my whole team."}
{"id": "1003", "text": "It's fine. Does what the box says, nothing more. Would be great if the app had an offline mode."}
الخطوة 2 — تعريف مخطط Zod واحد كمصدر وحيد للحقيقة
يقوم نفس المخطط بمهمة مزدوجة: يقوم zodOutputFormat() بتحويله إلى JSON Schema الذي يقيد مخرجات Claude على الخادم، ويقوم safeParse() بالتحقق من صحة كل نتيجة محلياً عند عودة الدفعة. أنشئ src/schema.ts:
import { z } from "zod";
export const ReviewExtraction = z.object({
sentiment: z.enum(["positive", "negative", "mixed", "neutral"]),
productIssues: z
.array(z.string())
.describe("Specific product problems the reviewer reports, empty if none"),
featureRequests: z
.array(z.string())
.describe("Features the reviewer asks for, empty if none"),
wouldRecommend: z
.boolean()
.describe("Whether the reviewer would recommend the product"),
summary: z.string().describe("One-sentence summary of the review"),
});
export type ReviewExtraction = z.infer<typeof ReviewExtraction>;
سلوك واحد يستحق المعرفة قبل الوثوق بتنسيق النقل: طباعة zodOutputFormat(ReviewExtraction) على SDK 0.104.1 تظهر أن المساعد يقوم بتخفيض قيد enum إلى نص وصفي — خاصية sentiment تنتقل عبر الشبكة كـ "type": "string" مع "description": "{enum: [\"positive\",\"negative\",\"mixed\",\"neutral\"]}" — بدلاً من إصدار كلمة مفتاحية enum في JSON Schema. تضمن قواعد الخادم أن sentiment هو سلسلة نصية؛ والتحقق المحلي من Zod هو ما يفرض أن تكون واحدة من القيم الأربع المسموح بها. هذا ليس عيباً في خط المعالجة هذا — بل هو السبب في أن فرع عدم تطابق المخطط في الخطوة 6 يمكن الوصول إليه ويستحق الاختبار.6
يغطي التحقق المحلي أيضاً فجوة الثقة التي تضيفها معالجة الدفعات: تظل النتائج في ملف قابل للتنزيل لمدة تصل إلى 29 يوماً، لذا قد تكون قديمة بأسابيع بحلول الوقت الذي يقرأها فيه المستهلك — إعادة التحقق عند نقطة الاستخدام هي تأمين رخيص.
الخطوة 3 — بناء طلبات الدفعات بـ custom_id ومخرجات مهيكلة
كل إدخال في الدفعة يقرن custom_id مع الـ params الدقيقة التي ستمررها إلى messages.create() — ناقصاً بعض المعلمات التي لا معنى لها بشكل غير متزامن (أوضحها stream: true؛ تضمينها يعيد خطأ تحقق).1 أنشئ src/requests.ts:
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
import type Anthropic from "@anthropic-ai/sdk";
import { ReviewExtraction } from "./schema.ts";
export const MODEL = "claude-haiku-4-5-20251001";
export interface Review {
id: string;
text: string;
}
const SYSTEM_PROMPT = `You extract structured data from customer product reviews.
Read the review and fill every field of the output schema.
Base every value only on what the review says. Keep arrays empty when the
review mentions no issues or no feature requests.`;
export function buildRequest(
review: Review
): Anthropic.Messages.BatchCreateParams.Request {
return {
custom_id: `review-${review.id}`,
params: {
model: MODEL,
max_tokens: 1024,
system: SYSTEM_PROMPT,
output_config: { format: zodOutputFormat(ReviewExtraction) },
messages: [{ role: "user", content: review.text }],
},
};
}
ثلاثة قيود على هذا الملف أساسية:
- يجب أن يطابق
custom_idالنمط^[a-zA-Z0-9_-]{1,64}$ويكون فريداً داخل الدفعة. يمكن أن تعود النتائج بأي ترتيب، لذا فإنcustom_idهو الطريقة الوحيدة لإعادة ربط الإجابة بمراجعتها المصدر.1 - يتم تعيين نوع
paramsكـMessageCreateParamsNonStreaming، وoutput_configهو حقل أساسي فيه — تؤكد أنواع SDK المثبتة أن المخرجات المهيكلة (structured outputs) صالحة داخل طلبات الدفعات (batch requests) بدون أي علامة تجريبية (beta flag).6 - Haiku 4.5 هو اختيار النموذج المتعمد. الاستخراج على مستوى التصنيف لا يحتاج إلى نموذج رائد (frontier model)، وبأسعار الدفعات، يكلف Haiku 4.5 حوالي 0.50 دولار / 2.50 دولار لكل مليون توكن. قم بتغيير
MODELإلىclaude-sonnet-4-6(بـ 1.50 دولار / 7.50 دولار للدفعات) إذا كانت مستنداتك تحتاج إلى تفكير منطقي حقيقي.1
الخطوة 4 — إرسال الدفعة
أنشئ src/submit.ts:
import { readFileSync } from "node:fs";
import Anthropic from "@anthropic-ai/sdk";
import { buildRequest, type Review } from "./requests.ts";
const anthropic = new Anthropic();
const reviews: Review[] = readFileSync("reviews.jsonl", "utf8")
.split("\n")
.filter((line) => line.trim().length > 0)
.map((line) => JSON.parse(line) as Review);
const batch = await anthropic.messages.batches.create({
requests: reviews.map(buildRequest),
});
console.log(`Submitted batch ${batch.id}`);
console.log(`Status: ${batch.processing_status}`);
console.log(`Requests processing: ${batch.request_counts.processing}`);
قم بتشغيله:
node --import tsx src/submit.ts
شكل المخرجات (معرف msgbatch_ هو ما يخصصه FastAPI لدفعتك):
Submitted batch msgbatch_...
Status: in_progress
Requests processing: 3
أحد تفاصيل الإرسال التي تفاجئ الناس: التحقق من صحة كل طلب يتم بشكل غير متزامن (asynchronous). تنص الوثائق الرسمية على أن التحقق من كائن params لكل طلب يحدث بشكل غير متزامن — كائن params خاطئ (اسم نموذج مكتوب بشكل غير صحيح، أو max_tokens: 0) لا يفشل عند الإرسال. بل يظهر كنتيجة errored عند انتهاء الدفعة. توصي الوثائق الرسمية بإجراء تجربة تشغيل (dry-run) لطلب واحد عبر FastAPI المتزامن أولاً لاكتشاف أخطاء الشكل قبل إرسال 10,000 نسخة منها.1
الخطوة 5 — فحص حالة دفعة الرسائل
كم تستغرق دفعة Claude؟ تنتهي معظم الدفعات في أقل من ساعة، لكن العقد الموثق هو أن النتائج تصبح متاحة عندما تكتمل جميع الرسائل أو بعد 24 ساعة، أيهما يأتي أولاً — لذا قم بالفحص بدلاً من الافتراض.1 أنشئ src/poll.ts:
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic();
const batchId = process.argv[2];
if (!batchId) {
console.error("Usage: node --import tsx src/poll.ts <batch_id>");
process.exit(1);
}
const POLL_INTERVAL_MS = 60_000;
let batch = await anthropic.messages.batches.retrieve(batchId);
while (batch.processing_status !== "ended") {
const c = batch.request_counts;
console.log(
`${new Date().toISOString() ${batch.processing_status — ` +
`processing: ${c.processing, succeeded: ${c.succeeded, ` +
`errored: ${c.errored, canceled: ${c.canceled, expired: ${c.expired`
);
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
batch = await anthropic.messages.batches.retrieve(batchId);
}
console.log(`Batch ${batch.id ended.`);
console.log(JSON.stringify(batch.request_counts, null, 2));
node --import tsx src/poll.ts msgbatch_YOUR_ID_HERE
يمنحك request_counts تقدماً حياً عبر الفئات الخمس (processing، succeeded، errored، canceled، expired)، لذا فإن الدفعات التي تستغرق وقتاً طويلاً تكون قابلة للمراقبة وليست صندوقاً أسود. فاصل زمني مدته 60 ثانية كافٍ جداً؛ كل عملية فحص هي طلب HTTP عادي يُحتسب ضمن حد طلبات الدفعات في الدقيقة لـ FastAPI (50 طلب في الدقيقة في المستوى 1)، وليس ضمن حدود رسائل FastAPI الخاصة بك.2
إذا كنت بحاجة إلى الإلغاء، فإن await anthropic.messages.batches.cancel(batchId) ينقل الدفعة إلى حالة canceling؛ وستظل تنتهي كـ ended وقد تحتوي على نتائج جزئية للطلبات التي تمت معالجتها قبل سريان الإلغاء.1
الخطوة 6 — التعامل مع جميع أنواع النتائج الأربعة
كل سطر في ملف النتائج هو نتيجة لطلب واحد، وهناك بالضبط أربعة أنواع من النتائج: succeeded، errored، canceled، و expired. يتم محاسبتك فقط على طلبات succeeded.1 المعالج أدناه هو دالة نقية (pure function) — بدون عمليات إدخال/إخراج (I/O) — وهو ما يجعل اختبارات الخطوة 8 غير المتصلة بالإنترنت ممكنة. أنشئ src/handle-result.ts:
import type Anthropic from "@anthropic-ai/sdk";
import { ReviewExtraction from "./schema.ts";
export type Outcome =
| { kind: "extracted"; customId: string; data: ReviewExtraction | { kind: "retryable"; customId: string; reason: string | { kind: "rejected"; customId: string; reason: string ;
export function handleResult(
entry: Anthropic.Messages.MessageBatchIndividualResponse
): Outcome {
const { custom_id: customId, result = entry;
switch (result.type) {
case "succeeded": {
const message = result.message;
if (message.stop_reason === "refusal") {
return { kind: "rejected", customId, reason: "model refused" ;
}
if (message.stop_reason === "max_tokens") {
return {
kind: "rejected",
customId,
reason: "output truncated at max_tokens; JSON may be incomplete",
};
}
const text = message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
let parsed: unknown;
try {
parsed = JSON.parse(text);
} catch {
return { kind: "rejected", customId, reason: "response is not valid JSON" ;
}
const validated = ReviewExtraction.safeParse(parsed);
if (!validated.success) {
return {
kind: "rejected",
customId,
reason: `schema mismatch: ${validated.error.issues
.map((issue) => issue.path.join(".") + " " + issue.message)
.join("; ")`,
};
}
return { kind: "extracted", customId, data: validated.data ;
}
case "errored": {
// result.error is an ErrorResponse envelope: { type: "error", error: {...} }.
// The error CLASS lives one level deeper, on result.error.error.type.
const errorType = result.error.error.type;
if (errorType === "invalid_request_error") {
return {
kind: "rejected",
customId,
reason: `invalid request: ${result.error.error.message`,
};
}
return { kind: "retryable", customId, reason: `server error: ${errorType` ;
}
case "canceled":
return { kind: "retryable", customId, reason: "batch canceled before processing" ;
case "expired":
return { kind: "retryable", customId, reason: "not processed within 24 hours" ;
}
}
منطق التفريع يجسد دليل التشغيل:
errored+invalid_request_error← مرفوض (rejected). جسم الطلب نفسه خاطئ؛ إعادة إرسال نفس البيانات ستفشل بنفس الطريقة. أصلح الطلب أولاً.errored+ أي شيء آخر (مثلapi_error) ← قابل لإعادة المحاولة (retryable). يمكن إعادة إرسال إخفاقات جانب الخادم كما هي في دفعة لاحقة.canceledوexpired← قابل لإعادة المحاولة (retryable). هذه الطلبات لم تصل أبداً إلى النموذج ولم يتم محاسبتك عليها.succeededلا تعني بالضرورة أنها قابلة للاستخدام. الرفض (refusal)، أو اقتطاعmax_tokens، أو قيمة خارج النطاق المحدد (out-of-enum) كلها تصل داخل نتيجة "succeeded". حواجز سبب التوقف (stop-reason) وsafeParseمن Zod هي ما يفصل بين "أعاد FastAPI رسالة" وبين "البيانات آمنة للتحميل".
لاحظ غلاف الخطأ المكون من طبقتين في فرع errored. result.error هو ErrorResponse الذي يكون حقل type الخاص به دائماً هو النص الثابت "error"؛ الفئة التي تفرع بناءً عليها — invalid_request_error، api_error، وغيرها — تعيش في مستوى أعمق عند result.error.error.type. اعتباراً من 11 يونيو 2026، يتحقق مثال TypeScript في وثائق معالجة الدفعات الرسمية من result.result.error.type الأكثر ضحالة، والذي لا يمكن أبداً أن يساوي "invalid_request_error" وفقاً للأنواع المشحونة في SDK الإصدار 0.104.1 (يستخدم مثال Python في نفس الوثيقة المسار الأعمق الصحيح).6
الخطوة 7 — بث نتائج الدفعة إلى JSONL بواسطة custom_id
تعيش النتائج في results_url بتنسيق JSONL، وتُرجع طريقة results() في SDK وحدة فك ترميز قابلة للتكرار بشكل غير متزامن (async-iterable decoder)، لذا يمكنك معالجة إدخال واحد في كل مرة بدلاً من تخزين ملف ضخم محتمل في الذاكرة المؤقتة — وهو التوجيه الرسمي للدفعات الكبيرة.1 أنشئ src/results.ts:
import { createWriteStream from "node:fs";
import Anthropic from "@anthropic-ai/sdk";
import { handleResult from "./handle-result.ts";
const anthropic = new Anthropic();
const batchId = process.argv[2];
if (!batchId) {
console.error("Usage: node --import tsx src/results.ts <batch_id>");
process.exit(1);
}
const extracted = createWriteStream("extracted.jsonl");
const failures = createWriteStream("failures.jsonl");
const counts = { extracted: 0, retryable: 0, rejected: 0 ;
for await (const entry of await anthropic.messages.batches.results(batchId)) {
const outcome = handleResult(entry);
counts[outcome.kind+= 1;
if (outcome.kind === "extracted") {
extracted.write(
JSON.stringify({ customId: outcome.customId, ...outcome.data ) + "\n"
);
else {
failures.write(JSON.stringify(outcome) + "\n");
}
extracted.end();
failures.end();
console.log(
`Done. extracted: ${counts.extracted, ` +
`retryable: ${counts.retryable, rejected: ${counts.rejected`
);
console.log("Wrote extracted.jsonl and failures.jsonl");
node --import tsx src/results.ts msgbatch_YOUR_ID_HERE
قد تصل النتائج بترتيب مختلف عن الذي أرسلتها به — الوثائق صريحة في أن الترتيب غير مضمون وأن custom_id هو مفتاح الربط.1 ولأن كل سطر مخرجات يحمل customId، يمكن للمستهلكين في المراحل اللاحقة إعادة ربط عمليات الاستخراج بالمراجعات المصدر بغض النظر عن الترتيب، ويكون ملف failures.jsonl جاهزاً لتشغيل دفعة متابعة لمدخلات retryable.
الخطوة 8 — اختبار معالج النتائج دون اتصال بالإنترنت، مجاناً
دالة handleResult نقية، لذا فإن كل فرع قابل للاختبار باستخدام كائنات وهمية (fixtures) — لا حاجة لمفتاح FastAPI، ولا يتم استهلاك أي توكنات. أنشئ test/handle-result.test.ts:
import { test from "node:test";
import assert from "node:assert/strict";
import type Anthropic from "@anthropic-ai/sdk";
import { handleResult from "../src/handle-result.ts";
type Entry = Anthropic.Messages.MessageBatchIndividualResponse;
function succeededEntry(
text: string,
stopReason: "end_turn" | "refusal" | "max_tokens" = "end_turn"
): Entry {
return {
custom_id: "review-1001",
result: {
type: "succeeded",
message: {
id: "msg_test",
type: "message",
role: "assistant",
model: "claude-haiku-4-5-20251001",
content: [{ type: "text", text, citations: null ],
container: null,
stop_details: null,
stop_reason: stopReason,
stop_sequence: null,
usage: {
input_tokens: 100,
output_tokens: 50,
cache_creation: null,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
inference_geo: null,
output_tokens_details: null,
server_tool_use: null,
service_tier: null,
,
,
,
;
const VALID = JSON.stringify({
sentiment: "negative",
productIssues: ["battery drains overnight"],
featureRequests: ["usb-c charging"],
wouldRecommend: false,
summary: "Disappointed by battery life and the proprietary charger.",
);
test("succeeded + valid JSON + valid schema -> extracted", () => {
const outcome = handleResult(succeededEntry(VALID));
assert.equal(outcome.kind, "extracted");
if (outcome.kind === "extracted") {
assert.equal(outcome.data.sentiment, "negative");
assert.equal(outcome.data.productIssues.length, 1);
);
test("refusal stop reason -> rejected", () => {
const outcome = handleResult(succeededEntry(VALID, "refusal"));
assert.equal(outcome.kind, "rejected");
);
test("max_tokens stop reason -> rejected", () => {
const outcome = handleResult(succeededEntry('{"sentiment":"neg', "max_tokens");
assert.equal(outcome.kind, "rejected");
;
test("invalid JSON -> rejected", () => {
const outcome = handleResult(succeededEntry("not json at all");
assert.equal(outcome.kind, "rejected";
if (outcomeالمدخلات (Input) المخرجات (Output) الإجمالي الرموز (10,000 مراجعة) 4.0 مليون 1.3 مليون — Haiku 4.5 المتزامن ($1/$5) $4.00 $6.50 $10.50 Batch Haiku 4.5 ($0.50/$2.50) $2.00 $3.25 $5.25
أعداد الرموز (tokens) هذه هي افتراضات لجعل الحسابات ملموسة — ستختلف مراجعاتك؛ تحقق من usage في طلب تجريبي (dry-run) للمعايرة. النسبة لا تتغير: المعالجة بالدفعة (batch) تكلف نصف ما تكلفه المعالجة المتزامنة. ملاحظتان إضافيتان من الوثائق الرسمية: المخرجات المهيكلة (structured outputs) تضيف بعض العبء على المدخلات لأن الـ API يحقن برومبت نظام لتعليمات التنسيق، وأول طلب بمخطط (schema) جديد يدفع تكلفة زمن انتقال لمرة واحدة لتجميع القواعد (grammar-compilation) (يتم تخزين القواعد المجمعة مؤقتًا لمدة 24 ساعة من آخر استخدام).3 تخزين البرومبت مؤقتًا (Prompt caching) يعمل أيضًا داخل الدفعات ويتراكم مع خصم الـ 50%، لكن الإصابات (hits) تكون على أساس "أفضل جهد" تحت المعالجة المتزامنة — تتراوح المعدلات المرصودة من 30% إلى 98% اعتمادًا على أنماط حركة المرور.1
التحقق
ثلاثة فحوصات تؤكد سلامة المسار من البداية للنهاية:
- انتهت الدفعة بنجاح. يجب أن تصل مخرجات الاستطلاع (Poll) إلى
ended مع وجود جميع الطلبات في حالة succeeded:
Batch msgbatch_... ended.
{
"processing": 0,
"succeeded": 3,
"errored": 0,
"canceled": 0,
"expired": 0
}
- الـ API الخام يتوافق مع SDK. يجب أن تظهر نقطة نهاية الاسترداد (retrieve endpoint) نفس الحالة ورابط
results_url غير فارغ:
curl "https://API.anthropic.com/v1/messages/batches/msgbatch_YOUR_ID_HERE" \
--header "x-API-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: 2023-06-01"
- كل سطر في
extracted.jsonl يعيد التحقق. يجب أن يحتوي الملف على سطر واحد لكل مراجعة ناجحة، يحمل كل منها customId يرتبط بـ reviews.jsonl، وكل منها يعاد تحليله تحت نفس مخطط Zod. قم بفحص عشوائي لبعض الصفوف مقابل مراجعاتها الأصلية — تأكد من أن sentiment ومصفوفات المشكلات/الميزات تعكس ما تقوله كل مراجعة بالفعل. الاستخراج لا يزال استنتاجًا من النموذج، لذا تختلف النصوص الدقيقة من تشغيل لآخر؛ المخطط يضمن الهيكل، وليس الحكم.
قبل التوسع إلى سجل أعمالك الحقيقي، قم بتشغيل طلب تجريبي واحد عبر messages.create() باستخدام نفس كائن params — التحقق في وقت التقديم يكون غير متزامن، وتوصي الوثائق بهذا تحديدًا لمنع تضاعف أخطاء الهيكل عبر دفعة كبيرة.1
الأخطاء الشائعة
413 request_too_large عند الإنشاء. تجاوزت الدفعة 256 ميجابايت إجمالاً. قم بتقسيم المدخلات — سقف الـ 100,000 طلب وسقف الـ 256 ميجابايت مستقلان، ومع المستندات الكبيرة ستصل إلى سقف البايتات أولاً.1
فشل التحقق من custom_id. يجب أن تطابق المعرفات (IDs) التعبير النمطي ^[a-zA-Z0-9_-]{1,64}$ وتكون فريدة داخل الدفعة. المسافات والنقاط ورسائل البريد الإلكتروني تفشل في التعبير النمطي؛ المعرفات المكررة تفشل في فحص الفرادة.1
كل شيء يعود بـ errored مع invalid_request_error. يتم تشغيل التحقق لكل طلب بشكل غير متزامن، لذا فإن الخطأ المنهجي (سلسلة نموذج خاطئة، max_tokens: 0، أو معلمة غير مدعومة مثل stream: true) يظهر كدفعة مليئة بالنتائج الخاطئة بدلاً من فشل استدعاء الإنشاء. قم بإصلاح params، وتحقق بطلب متزامن واحد، ثم أعد التقديم.1
خطأ TypeScript TS2367: الأنواع '"error"' و '"invalid_request_error"' ليس بينهما تداخل. أنت تتحقق من الغلاف، وليس الخطأ. استخدم result.error.error.type — الـ ErrorResponse.type الخارجي يكون دائمًا النص الثابت "error" في أنواع SDK المشحونة.6
تظهر الطلبات كـ expired. وصلت الدفعة إلى نافذة الـ 24 ساعة قبل معالجة تلك الطلبات — الطلب المرتفع أو الدفعات الكبيرة جدًا تجعل هذا أكثر احتمالاً. الطلبات منتهية الصلاحية لا يتم محاسبتك عليها؛ اجمعها من failures.jsonl وأعد تقديمها.1
توقف تحميل النتائج بعد أسابيع. النتائج متاحة لمدة 29 يومًا بعد تاريخ created_at للدفعة (وليس تاريخ ended_at). بعد ذلك لا يزال بإمكانك عرض كائن الدفعة، لكن نتائجها لم تعد متاحة للتحميل — قم بحفظ المخرجات على الفور.1
حدود وتنبيهات تستحق المعرفة
- حدود زمام الانتظار (Queue) تزداد مع فئتك (tier). تتيح الفئة 1 (Tier 1) وجود 100,000 طلب دفعة في زمام المعالجة؛ تتيح الفئة 4 وجود 500,000. يظل الحد الأقصى لكل دفعة 100,000 في كل فئة.2
- الدفعات محصورة في مساحة العمل (Workspace). المفاتيح من مساحة عمل أخرى لا يمكنها رؤية دفعاتك أو نتائجك.1
- غير مؤهلة لسياسة عدم الاحتفاظ بالبيانات (Zero Data Retention). تخزن معالجة الدفعات الطلبات والنتائج لمدة تصل إلى 29 يومًا؛ يمكنك استخدام
DELETE /v1/messages/batches/{id} بعد المعالجة (قم بالإلغاء أولاً إذا كانت قيد التنفيذ).1
- المخرجات الممتدة (Extended output) متاحة للدفعات فقط. رأس البيتا
output-300k-2026-03-24 يرفع max_tokens إلى 300,000 في نماذج Opus 4.8/4.7/4.6 و Sonnet 4.6 — عبر client.beta.messages.batches.create() مع مصفوفة betas — للاستخراج الشامل أو التوليد الطويل. وهي غير متاحة في API الرسائل المتزامن.1
- أدوات الخادم (Server tools) تعمل في الدفعات أيضًا، ويقوم عامل الدفعة بتشغيل تكرارات حلقة وكيل (agentic-loop) أكثر في كل دورة من الطلب المتزامن قبل إرجاع
pause_turn.1
الخطوات التالية
نمط "المخطط أولاً" هنا هو نسخة نطاق الدفعات من دليلنا مخرجات Claude المهيكلة في TypeScript باستخدام Zod — اقرأه لمسار messages.parse() المتزامن، واستخدام الأدوات الصارم، وتصنيف الحماية الكامل. لاختبار تراجع برومبت الاستخراج قبل كل تقديم كبير، قم بربطه بـ CI باستخدام promptfoo. وإذا كانت وظائف الدفعات الخاصة بك تغذي وكيلاً، فإن دليل حلقة الوكيل لاستخدام أدوات Claude Sonnet 4.6 يغطي الجانب المتزامن لنفس SDK.
Footnotes
-
Anthropic، "Batch processing" — توثيق Claude API، https://platform.claude.com/docs/en/build-with-claude/batch-processing (تم الجلب في 11 يونيو 2026). الحدود، جدول التسعير، أنواع النتائج، قواعد الفوترة، معدلات إصابة prompt-caching، النسخة التجريبية للمخرجات الممتدة (extended-output beta)، والاحتفاظ بالبيانات. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19 ↩20 ↩21 ↩22 ↩23 ↩24
-
Anthropic، "Rate limits" — توثيق Claude API، https://platform.claude.com/docs/en/API/rate-limits (تم الجلب في 11 يونيو 2026). جدول فئات Message Batches API: الـ RPM، طابور المعالجة (processing-queue)، والحدود القصوى لكل دفعة؛ والفصل عن حدود Messages API. ↩ ↩2 ↩3
-
Anthropic، "Structured outputs" — توثيق Claude API، https://platform.claude.com/docs/en/build-with-claude/structured-outputs (تم الجلب في 11 يونيو 2026). توافق الدفعات (Batch compatibility)، تجميع القواعد (grammar compilation) والتخزين المؤقت لمدة 24 ساعة، وعبء تعليمات التنسيق (format-instructions overhead). ↩ ↩2
-
جدول إصدارات Node.js، https://endoflife.date/nodejs (إصدار Node 24 Active LTS؛ الصيانة حتى أبريل 2028؛ تم التحقق في 11 يونيو 2026). ↩
-
سجل npm، تم التحقق في 11 يونيو 2026: @anthropic-ai/sdk 0.104.1، zod 4.4.3، TypeScript 6.0.3، tsx 4.22.4، @types/node 24.13.2. ↩
-
تعريفات الأنواع المرفقة ومصدر @anthropic-ai/sdk 0.104.1 (resources/messages/batches.d.ts، resources/shared.d.ts، helpers/zod.d.ts)، تم فحصها في 11 يونيو 2026، بالإضافة إلى مخرجات وقت التشغيل لـ zodOutputFormat() على مخطط البرنامج التعليمي. ↩ ↩2 ↩3 ↩4