غيّرت النموذج الذي تستدعيه عبر Claude API إلى إصدار أحدث، فبدأ يظهر الخطأ التالي. أو صار نص الرد يعود فارغًا. السبب في الغالب واحد: الكتلة الأولى في الرد (content[0]) لم تعد نصًا، بل صارت كتلة تفكير (thinking).

AttributeError: 'ThinkingBlock' object has no attribute 'text'

إذا كنت تقرأ response.content[0].text في Python SDK، فستحصل على هذا الخطأ. وفي JavaScript تُرجع الشيفرة نفسها undefined، فلا يحدث خطأ، بل يمر نص فارغ عبر تطبيقك. والجزء المخادع: مع المدخلات البسيطة تبقى الكتلة الأولى نصًا، لذا تنجح الاختبارات التي تستخدم مدخلات خفيفة.

المشكلة في ثلاثة أسطر

ما الذي يحدث

الكتلة الأولى لم تعد نصًا

مع المدخلات الثقيلة في الغالب، تأتي كتلة تفكير أولًا في الرد (وأحيانًا مع المدخلات الخفيفة أيضًا). فتتعطل content[0].text.

السبب

يفكر دون أن يُطلب منه

نماذج Claude 5 وما بعدها تفكر عند الحاجة حتى إن لم تضبط thinking. أما نماذج Claude 4 فلا تفكر ما لم تضبطه.

الحل

ابحث عن الكتل حسب نوعها

لا تفترض الكتلة الأولى. اجمع الكتل التي type فيها "text"، وتحقق من stop_reason أيضًا.

1. متى يحدث

الحالة النموذجية هي ترحيل لا تغيّر فيه إلا اسم النموذج. مثلًا، تنتقل من Claude Sonnet 4.6 إلى Claude Sonnet 5.5 بتعديل قيمة model وحدها. وبافتراض أن شكل الرد لم يتغير، تترك بقية الشيفرة كما هي. فيحدث ما يلي (والحالة الثالثة تحدث حتى مع شيفرة مكتوبة بشكل صحيح).

طريقة القراءةما الذي يحدث
content[0].text في Python SDKيتوقف بالخطأ AttributeError: 'ThinkingBlock' object has no attribute 'text'
content[0].text في JavaScriptلا يحدث خطأ؛ تصبح القيمة undefined، وينتهي نص فارغ على الشاشة أو في التخزين
البحث عن كتلة النصإذا فكّر النموذج كثيرًا وبلغ max_tokens، فقد لا توجد كتلة نص على الإطلاق

الحالة الثالثة تحدث حتى مع شيفرة مكتوبة بشكل صحيح. فرموز التفكير تُحتسب ضمن max_tokens، لذا إذا كان الحد صغيرًا، يُقطع الرد قبل أن يكتب النموذج أي نص.

2. السبب: Claude 5 وما بعده يفكر دون أن يُطلب منه

تسرد وثائق Anthropic «Thinking»، نموذجًا نموذجًا، ما يحدث عندما لا يتضمن الطلب thinking. والخط الفاصل هو Claude 5.

النموذجدون thinkingمحتوى التفكير
Claude 5 وما بعده
(Opus 5.5 وSonnet 5.5 وHaiku 5.5 وFable 5.1 وغيرها)
يفكر (adaptive thinking)فارغ افتراضيًا (display: "omitted")
Opus 4.8، Opus 4.7لا يفكريفكر فقط عندما تضبط thinking: {type: "adaptive"}، والمحتوى فارغ افتراضيًا ("omitted")
Opus 4.6، Sonnet 4.6لا يفكريفكر فقط عندما تضبط thinking: {type: "adaptive"}، ويُرجع ملخصًا افتراضيًا ("summarized")

المصدر: الجدول الخاص بكل نموذج ووصف display في وثائق Anthropic «Thinking» (رُوجعت في 1 نوفمبر 2026). لا تقبل Opus 4.5 وSonnet 4.5 وHaiku 4.5 القيمة {type: "adaptive"}؛ ولجعلها تفكر، استخدم {type: "enabled"} مع budget_tokens.

النموذج هو من يقرر لكل مدخل هل يفكر أم لا. وتوضح وثائق «Steering thinking» أن النموذج قد يجيب عن الأسئلة الواقعية البسيطة دون كتلة تفكير. ثم تحذّرك من بناء منطق تطبيقك على افتراض أن كل رد يبدأ بكتلة تفكير. بعبارة أخرى، تكون الكتلة الأولى نصًا أحيانًا وتفكيرًا أحيانًا أخرى. والشيفرة التي تفترض أحدهما ستتعطل في مكان ما.

يُرتَّب محتوى الرد كما يلي. محتوى التفكير فارغ افتراضيًا، لكن الكتلة نفسها تُرجَع رغم ذلك.

3. ما وجدته في الاختبار

اختبرت ذلك في بيئتي باستخدام Python SDK (anthropic 1.14.0) في 1 نوفمبر 2026. أرسلت إلى أربعة نماذج مدخلًا خفيفًا ومدخلًا ثقيلًا مرة واحدة لكل منهما، دون thinking ومع {type: "adaptive"}. وضبطت max_tokens على 8000. كان المدخل الخفيف سؤالًا واقعيًا بسيطًا يطلب عاصمة اليابان بكلمة واحدة (طرحناه باللغة اليابانية)، والمدخل الثقيل مسألة تطلب كل الأعداد الصحيحة المكوّنة من أربعة أرقام التي تحقق خمسة شروط.

النموذجإعداد thinkingcontent (مدخل خفيف)content (مدخل ثقيل)
Sonnet 4.6غير مضبوطtexttext
Sonnet 4.6adaptivetextthinking فقط (بلغ الحد)
Haiku 5.5غير مضبوطtextthinking فقط (بلغ الحد)
Haiku 5.5adaptivetextthinking فقط (بلغ الحد)
Sonnet 5.5غير مضبوطtextthinking → text
Sonnet 5.5adaptivetextthinking → text
Opus 5.5غير مضبوطtextthinking → text
Opus 5.5adaptivethinking → textthinking → text

يشير اللون الأحمر إلى التركيبات التي أطلقت فيها content[0].text الخطأ AttributeError: 'ThinkingBlock' object has no attribute 'text'. كل نتيجة من تشغيل واحد؛ وقد يختلف تفكير النموذج من تشغيل إلى آخر، حتى مع المدخل نفسه.

ثلاث خلاصات.

أولًا، فكّرت نماذج Claude 5 وما بعدها في المدخل الثقيل حتى دون thinking. ومع Sonnet 4.6، أبقى حذفه النص كتلةً أولى. وهذا يؤكد ما تقوله الوثائق: تغيير اسم النموذج وحده يغيّر شكل الرد.

ثانيًا، مع المدخل الخفيف، كانت الكتلة الأولى نصًا في كل التركيبات تقريبًا. إذا كانت اختباراتك تستخدم مدخلات خفيفة فقط، فلن تكتشف هذه المشكلة. في المقابل، فكّر Opus 5.5 مع adaptive حتى في المدخل الخفيف. وفي Claude 5 وما بعده، حذف thinking وضبط adaptive هما الإعداد نفسه، لذا فهذا الفرق على الأرجح تفاوت بين التشغيلات. لا يمكنك القول إن «المدخلات الخفيفة لا تستدعي التفكير».

ثالثًا، عند بلوغ الحد، لم يُرجَع أي نص على الإطلاق. في المدخل الثقيل، استهلك Haiku 5.5 كل الرموز الـ8000 في التفكير، دون thinking ومع adaptive كليهما، ولم يُرجع أي كتلة نص. وبلغ Sonnet 4.6 الحد أيضًا مع adaptive. وفي Claude 5 وما بعده يكون محتوى التفكير فارغًا افتراضيًا كذلك، لذا لا يرى المستخدمون شيئًا، ومع ذلك تدفع ثمن رموز المخرجات.

4. كيف تصلحه

الحل هو التوقف عن افتراض الكتلة التي تأتي أولًا، وجمع الكتل التي نوعها نص (type فيها "text"). وتحقق أيضًا من stop_reason حتى لا تعامل ردًا قُطع عند الحد كأنه إجابة عادية.

Python

import anthropic

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=16000,
    messages=[{"role": "user", "content": prompt}],
)

# لا تفترض الكتلة الأولى: اجمع كتل النص فقط
text = "".join(b.text for b in response.content if b.type == "text")

if response.stop_reason == "max_tokens":
    # قُطع عند الحد. قد يكون text فارغًا
    raise RuntimeError("Hit max_tokens. Raise the limit or reduce thinking")
if response.stop_reason == "end_turn" and not text:
    # في تدفقات استخدام الأدوات، يكون stop_reason هو "tool_use" والنص الفارغ أمر طبيعي
    raise RuntimeError("No text block")

TypeScript

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();
const response = await client.messages.create({
  model: "claude-sonnet-5-5",
  max_tokens: 16000,
  messages: [{ role: "user", content: prompt }],
});

// ضيّق حسب النوع قبل قراءة text
const text = response.content
  .filter((b): b is Anthropic.TextBlock => b.type === "text")
  .map((b) => b.text)
  .join("");

if (response.stop_reason === "max_tokens") {
  throw new Error("Hit max_tokens. Raise the limit or reduce thinking");
}
// في تدفقات استخدام الأدوات، يكون stop_reason هو "tool_use" والنص الفارغ أمر طبيعي
if (response.stop_reason === "end_turn" && text === "") {
  throw new Error("No text block");
}

في TypeScript SDK، عناصر content اتحاد (union) من أنواع مثل النص والتفكير واستخدام الأدوات. وكتابة content[0].text مباشرة يُفترض أن تفشل في فحص الأنواع عادةً. لكن إذا فرضت النوع باستخدام as، فسيمر الخطأ دون أن يُلاحَظ.

اضبط max_tokens مع احتساب التفكير

توضح وثائق «Steering thinking» أن max_tokens حد مشترك للتفكير ونص الإجابة. وتشير أيضًا إلى أن الحد الذي يُختار على افتراض أن النموذج لن يفكر يكون غالبًا أصغر مما ينبغي للمدخلات الصعبة. وعندما تحصل على stop_reason: "max_tokens"، فهناك حلّان.

القيمة 16000 في الأمثلة أعلاه تقديرية. ففي اختباراتي، استُهلكت حتى 8000 كلها في التفكير أحيانًا. وللإنتاج، شغّل مدخلات ثقيلة واضبط القيمة بالنظر إلى usage.output_tokens_details.thinking_tokens (عدد الرموز المستهلكة في التفكير) في الرد.

  • ارفع الحد: عندما يحسّن التفكير جودة الإجابة على المدخلات التي قُطعت
  • اخفض الجهد (effort): عندما يكون النموذج يفرط في التفكير فحسب ولا تتغير جودة الإجابة (اضبط الجهد بـoutput_config.effort)

عند مواصلة المحادثة، أعِد كتل التفكير أيضًا

عندما تضيف ردًا إلى سجل المحادثة، لا تستخرج النص وحده؛ بل أعِد content كما هو. وخاصة في تدفقات استخدام الأدوات، تحمل كتلة التفكير المنطق الذي يقف وراء استدعاء الأداة. وتطلب الوثائق أن تُعيد الكتل التي تلقيتها دون تعديل، والكتل المعدّلة تُرفض بخطأ 400. والأسلم أن تحتفظ بالنص الذي تعرضه منفصلًا عن content الذي تعيده في السجل.

5. هل يكفي إيقاف التفكير؟

من المغري أن تقول «إذا كان التفكير يعترض الطريق، فلأوقفه»، لكن إمكان ذلك يتوقف على النموذج. وإليك كيف يُعامَل thinking: {type: "disabled"}.

النموذج{type: "disabled"}
Opus 5.5، Fable 5.1، Fable 5خطأ 400 (لا يمكن إيقافه)
Sonnet 5.5خطأ 400 (بدلًا من ذلك، يتخطى {type: "between_tools"} التفكير الأولي عندما يكون الجهد high أو أقل)
Haiku 5.5، Opus 5يمكن إيقافه (فقط عندما يكون الجهد high أو أقل؛ وخطأ 400 عند xhigh وmax)
Sonnet 5يمكن إيقافه

المصدر: وثائق Anthropic «Thinking» (رُوجعت في 1 نوفمبر 2026). ويمكنك أيضًا التحقق مما إذا كان النموذج يقبله عبر capabilities.thinking.types.disabled.supported في Models API. الجهد الافتراضي هو medium في Opus 5.5 وHaiku 5.5، وhigh في معظم النماذج الأخرى، لذا فالقيم الافتراضية تستوفي شرط «high أو أقل».

حتى في النماذج التي يمكن إيقافه فيها، قد يخفض ذلك جودة الإجابة على المدخلات الصعبة. وتعود المشكلة نفسها في كل مرة تغيّر فيها النموذج. الحل الحقيقي هو شيفرة تستطيع قراءة الرد بأي ترتيب. وإذا أردت تقليل التفكير، فاخفض الجهد أولًا. ووفقًا لوثائق Anthropic، فإن مطالبة Sonnet 5.5 في موجّه النظام بأن يفكر أقل لا تقلل التفكير بشكل موثوق، وظل Haiku 5.5 يفكر حتى عندما طُلب منه أن يجيب مباشرة (في اختبارات Anthropic). وفي كليهما، اضبط ذلك بالجهد.

6. كيف لا تفوّته في الاختبار

هذه المشكلة تفلت من الاختبارات ذات المدخلات الخفيفة. وعند تغيير النماذج، يصعب أن تفوّتها إذا تحققت بالترتيب التالي.

  1. ابحث عن content[0] في شيفرتك: اسرد كل موضع يقرأ حسب الموقع، مثل content[0] وcontent[-1] و.content[0].text، لا في شيفرة الإنتاج وحدها، بل في الاختبارات والتسجيل أيضًا
  2. أضف مدخلات ثقيلة إلى اختباراتك: أدرج أطول مدخلات الإنتاج وأصعب المهام ضمن مدخلات الاختبار (فمع المدخلات الخفيفة وحدها تبقى الكتلة الأولى نصًا ويبقى العطل مخفيًا)
  3. مرّر ردودًا مصطنعة تبدأ بكتلة تفكير: في اختبارات الوحدة التي لا تستدعي API، جهّز ردًا بالترتيب [thinking, text] وردًا من thinking فقط يكون فيه stop_reason هو "max_tokens"، حتى تختبر بصرف النظر عن تفكير النموذج
  4. انشر تدريجيًا: لا تغيّر كل شيء دفعة واحدة؛ انقل بعض الأحمال إلى النموذج الجديد أولًا، وراقب أعداد النصوص الفارغة والاستثناءات، ثم وسّع النطاق

⚠️ عندما تغيّر عدة خدمات أو فرق النماذج معًا، أضف سطرًا واحدًا إلى طلب الترحيل: «الكتلة الأولى في الرد ليست نصًا دائمًا. ابحث عنها حسب النوع». فالتغيير يبدو مجرد إعادة تسمية، وإذا انتظرت أن يلاحظ كل فريق ذلك بنفسه، فإن الشيفرة التي نجحت في اختبارات المدخلات الخفيفة ستتعطل في الإنتاج.

وليس هذا هو الشيء الوحيد الذي يجب التحقق منه عند تغيير النماذج. لمعرفة ما تغيّر عند الترحيل من الأجيال السابقة، راجع «دليل ترحيل Claude Opus 4.7» و«التغييرات الجذرية الثلاثة في Claude Fable 5.1 وكيفية الانتقال». والجهد في API مشروح في وثائق Anthropic «Effort»، واختيار الجهد في Claude Code في «ضبط الجهد (Effort) في Claude Code».

الخلاصة

  • يحدث AttributeError: 'ThinkingBlock' object has no attribute 'text' عندما تكون الكتلة الأولى في الرد كتلة تفكير وتحاول قراءة النص بـcontent[0].text
  • نماذج Claude 5 وما بعدها تفكر عند الحاجة حتى دون thinking، لذا فإن مجرد تغيير اسم النموذج من أحد نماذج Claude 4 يغيّر شكل الرد
  • مع المدخلات الخفيفة تبقى الكتلة الأولى نصًا في الغالب، لذا تنجح الاختبارات الخفيفة
  • الحل هو جمع الكتل التي type فيها "text"، والتحقق مما إذا كان stop_reason هو "max_tokens"
  • بعض النماذج لا يمكن إيقاف التفكير فيها، لذا فالحل الحقيقي ليس إيقافه، بل كتابة شيفرة تقرأ الرد بأي ترتيب

الأسئلة الشائعة

س. لم أضبط thinking قط. فلماذا أحصل على كتلة تفكير؟

ج. في نماذج Claude 5 وما بعدها، يكون adaptive thinking مفعّلًا حتى دون thinking، ويفكر النموذج في المدخلات التي يقرر أنها تحتاج إلى ذلك. محتوى التفكير فارغ افتراضيًا، لكن الكتلة نفسها تُدرج في الرد. أما نماذج Claude 4 مثل Claude Opus 4.8 وSonnet 4.6 فلا تفكر ما لم تضبطه.

س. لماذا أحصل على رد لا توجد فيه كتلة نص على الإطلاق؟

ج. لأن رموز التفكير تُحتسب ضمن max_tokens، وقد بُلغ الحد قبل أن يكتب النموذج أي نص. ينبغي أن يكون stop_reason هو "max_tokens". ارفع الحد، أو اخفض الجهد لتقليل التفكير.

س. ماذا لو أردت قراءة محتوى التفكير؟

ج. اضبط thinking: {type: "adaptive", display: "summarized"} لتحصل على ملخص للتفكير. والقيمة الافتراضية في Claude 5 وما بعده هي "omitted" (يكون المحتوى فارغًا ولا يُدرج إلا التوقيع). وفي الحالتين، تُحاسَب على كل رموز التفكير.

سجل التحديثات

  • 1 نوفمبر 2026 النسخة الأولى. كُتبت بعد مراجعة وثائق Anthropic «Thinking» و«Steering thinking»، واختبار ترتيب الرد في أربعة نماذج في بيئتي.

المصادر