العودة إلى المدونة

استكشاف الأخطاءدليل

400 بعد استدعاء أداة: أعد إرسال reasoning_content

لماذا يعيد الطلب الثاني لحلقة استدعاء أداة رمز 400 على نموذج تفكير، وإصلاح بـ Python يلحق الرسالة كاملة، ومتى يمكنك تخطيه.

بواسطة CheapTokens Teamنُشر في 4 دقيقة قراءة

غلاف: حلقة استدعاء أداة ينتقل فيها reasoning_content من رسالة المساعد إلى الطلب التالي
في هذه الصفحة
  1. ما يحدث بين الطلب الأول والطلب الثاني
  2. الإصلاح: ألحق الرسالة كما أُعيدت
  3. سبب شائع: رسالة أعيد بناؤها يدويًا
  4. عندما لا تحتاج إلى هذا
  5. إذا استمر إرجاع 400

حلقة الوكيل الخاصة بك تعمل في الطلب الأول وتفشل في الثاني برمز 400. في نموذج تفكير، أحد الأسباب الموثقة هو حقل مفقود: رسالة المساعد التي ترسلها مرة أخرى فقدت reasoning_content. إذا كان هذا هو السبب، فإن إلحاق الرسالة كاملة تمامًا كما أعادتها واجهة API يحل المشكلة.

دليل وضع التفكير من DeepSeek (الوثائق، تم التحقق في 8 نوفمبر 2026) يضع القاعدة في جملة واحدة: عندما يحتوي الطلب على المعامل tools، يجب إعادة تمرير reasoning_content بالكامل في جميع الطلبات اللاحقة، بما في ذلك الأدوار التي لا تتضمن استدعاء أداة. وإلا فإن واجهة API تعيد 400. وتقول نفس الصفحة إن التفكير مفعّل افتراضيًا، بجهد high. لذا فإن حلقة كتبتها لنموذج غير مفكر يمكن أن تتعطل دون تغيير سطر واحد.

ما يحدث بين الطلب الأول والطلب الثاني#

في وضع التفكير تعود سلسلة التفكير في reasoning_content، وهو حقل على نفس مستوى content. تخيل الحلقة:

  1. ترسل السؤال وقائمة tools.
  2. يجيب النموذج بـ tool_calls (وreasoning_content بجانبها).
  3. تشغّل الأداة وترسل كل شيء مرة أخرى: السجل، ورسالة المساعد، ورسالة tool بالنتيجة.

يظهر الخطأ 400 في الطلب في الخطوة 3 إذا كانت رسالة المساعد فيه لا تحتوي على reasoning_content.

ثلاث خطوات لحلقة أداة: طلب، رسالة مساعد مع reasoning_content، طلب ثانٍ
أين يجب أن ينتقل الحقل: من رسالة المساعد إلى الطلب التالي

صفحة DeepSeek توثق الخطأ 400 فقط لحالة استدعاء الأداة هذه ولا تقتبس نص خطأ، لذا انظر إلى حالة HTTP، وليس إلى رسالة محددة، عندما تتحقق مما إذا كانت لديك هذه المشكلة.

الإصلاح: ألحق الرسالة كما أُعيدت#

الخطوة الأكثر أمانًا هي عدم إعادة بناء رسالة المساعد على الإطلاق. خذها من الاستجابة وألحقها كاملة. إليك حلقة كاملة باستخدام OpenAI Python SDK موجهة إلى CheapTokens. أداة الطقس هي دالة وهمية، لذا يمكنك تشغيلها كما هي.

python
import json, os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
MODEL = "deepseek-ai/DeepSeek-V4-Flash-0731"

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

def get_weather(city):
    return {"city": city, "temp_c": 7}  # stub

messages = [{"role": "user", "content": "Do I need a jacket in Oslo today?"}]

for _ in range(5):  # hard step limit
    resp = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
    msg = resp.choices[0].message
    messages.append(msg.model_dump(exclude_none=True))  # the whole message
    if not msg.tool_calls:
        print(msg.content)
        break
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(get_weather(**args)),
        })

السطر الوحيد المهم هو messages.append(msg.model_dump(exclude_none=True)). يحتفظ SDK بالحقول التي لا يعرفها، لذا فإن أي شيء وضعته واجهة API على الرسالة، بما في ذلك reasoning_content، ينتقل معها. يتم إلحاق الإجابة النهائية أيضًا، وهذا ما تطلبه القاعدة: بعد استدعاء الأداة الأول، يحمل كل طلب لاحق تفكير أدوار المساعد السابقة.

آليات استدعاء الأداة نفسها (المخطط، tool_call_id، تحليل الوسائط) موجودة في استدعاء الدوال في Python باستخدام واجهة API متوافقة مع OpenAI. عنوان URL الأساسي والمفتاح ومعرّف النموذج موجودة في صفحة وثائق CheapTokens؛ النموذج المستخدم هنا موصوف في صفحة DeepSeek: سياق 380K وحتى 32,768 رمز إخراج لكل إجابة، مما يترك مجالًا لتفكير طويل بالإضافة إلى إجابة.

سبب شائع: رسالة أعيد بناؤها يدويًا#

طريقة شائعة لفقدان الحقل هي الكود الذي لا يلحق كائن الاستجابة بل يبني قاموسًا جديدًا. يبدو غير ضار: الدور، المحتوى، استدعاءات الأدوات. تفعل الأطر والمغلفات الشيء نفسه عندما تخزن السجل بتنسيقها الخاص. الحقل الوحيد الذي لم يسمعوا به هو الذي يضيع.

يسار: رسالة مساعد أعيد بناؤها من role وcontent وtool_calls. يمين: الرسالة كما أُعيدت، مع reasoning_content
الفرق بين رسالتي المساعد هو حقل واحد

قارن

أعيد بناؤها يدويًا مقابل إلحاقها كاملة

أعيد بناؤها يدويًا (تتعطل)

python
messages.append({
    "role": "assistant",
    "content": msg.content,
    "tool_calls": [c.model_dump() for c in msg.tool_calls],
})

ملحقة كاملة (تعمل)

python
messages.append(msg.model_dump(exclude_none=True))

إذا كنت تستخدم إطارًا ولا يمكنك لمس ذلك السطر، فتحقق من كيفية تخزينه لأدوار المساعد. السجل الذي يمر عبر مُسلسِل يحتفظ فقط بـ content وtool_calls لديه نفس الخلل، لكن على طبقة أدنى.

عندما لا تحتاج إلى هذا#

ليس كل استدعاء لنموذج تفكير يحتاج إلى العناية الإضافية. للمحادثة متعددة الأدوار بدون أدوات، يقول دليل DeepSeek إن reasoning_content لا يجب إعادة تمريره؛ إذا أرسلته على أي حال، يتم تجاهله ولا يضاف التفكير السابق إلى السياق. لذا يمكن للمحادثة العادية الاستمرار في إسقاطه ولن ينكسر شيء.

اختر حالتك

أي حالة تنطبق عليك؟

الطلب يحتوي على tools، والنموذج يفكر

ألحق رسالة المساعد كاملة في كل دور، أدوار الأدوات والإجابات النهائية على حد سواء.

محادثة عادية، بدون معامل tools

لا حاجة لإصلاح. يمكنك إسقاط reasoning_content من السجل، أو إرساله؛ يتم تجاهله.

إطار يبني السجل نيابة عنك

ابحث عن المكان الذي يسلسل فيه رسائل المساعد وتأكد من بقاء reasoning_content. إذا لم يكن ممكنًا، فاستدعِ واجهة API بحلقتك الخاصة لجزء الأداة.

أثر جانبي يجب تذكره: السجل الذي تعيد إرساله هو مدخلات في كل طلب، لذا فإن التفكير الطويل في حلقة طويلة يتراكم. كيفية حساب رموز التفكير مشمولة في جهد التفكير ورموز التفكير، وما يجب فعله عندما يتجاوز الطلب حدود النموذج موجود في الطلب طويل جدًا: 400 مقابل 413.

إذا استمر إرجاع 400#

اتبع هذا بالترتيب:

  • اطبع messages مباشرة قبل الاستدعاء الفاشل. هل كل رسالة مساعد جاءت من النموذج تحتوي على reasoning_content، إذا كانت الاستجابة تحتوي عليها؟
  • هل تعيد بناء رسالة في أي مكان: دالة مساعدة، فئة سجل، json.dumps ثم العودة بقائمة مفاتيح ثابتة؟
  • هل رسالة tool لكل tool_call_id موجودة، بنفس المعرّف؟
  • هل هو حقًا الطول؟ الطلب الذي يتجاوز حد السياق هو 400 مختلف، موصوف في المقال أعلاه.

فحص سريع

ماذا تلحق بعد أن يطلب النموذج أداة؟

إظهار الإجابة

الإجابة: رسالة المساعد كاملة كما أُعيدت، بما في ذلك reasoning_content

استدعاءات الأدوات هي ما تجيب عليه رسائل الأدوات الخاصة بك، ومع وجود الأدوات في الطلب يجب أن يرافقها التفكير.

هذا هو الإصلاح كله: إلحاق واحد، بدون إعادة بناء. أضف حد الخطوات من المثال إلى أي حلقة تنشرها، حتى يكلف الدور السيئ خمسة طلبات، وليس خمسمائة.