ब्लॉग पर वापस जाएं

समस्या निवारणगाइड

टूल कॉल के बाद 400: reasoning_content वापस भेजें

थिंकिंग मॉडल पर टूल-कॉल लूप का दूसरा अनुरोध 400 क्यों लौटाता है, पूरा संदेश जोड़ने वाला Python समाधान, और इसे कब छोड़ सकते हैं।

CheapTokens Team द्वाराप्रकाशित 9 मिनट का पाठ

कवर: एक टूल-कॉल लूप जहाँ reasoning_content असिस्टेंट संदेश से अगले अनुरोध में यात्रा करता है
इस पृष्ठ पर
  1. अनुरोध एक और अनुरोध दो के बीच क्या होता है
  2. समाधान: संदेश को वैसे ही जोड़ें जैसे लौटाया गया
  3. एक आम दोषी: हाथ से दोबारा बनाया गया संदेश
  4. जब आपको इसकी आवश्यकता नहीं
  5. यदि फिर भी 400 लौटता है

आपका एजेंट लूप पहले अनुरोध पर काम करता है और दूसरे पर 400 के साथ विफल हो जाता है। थिंकिंग मॉडल पर, एक प्रलेखित कारण एक गायब फ़ील्ड है: आप जो असिस्टेंट संदेश वापस भेजते हैं, उसने अपना reasoning_content खो दिया है। यदि यही कारण है, तो संदेश को ठीक वैसे ही जोड़ना जैसे API ने लौटाया था, इसे ठीक कर देता है।

DeepSeek की थिंकिंग-मोड गाइड (docs, 8 नवंबर 2026 को जाँचा गया) नियम को एक वाक्य में रखती है: जब अनुरोध में tools पैरामीटर हो, तो reasoning_content को सभी बाद के अनुरोधों में पूरा वापस भेजना होता है, जिनमें बिना टूल कॉल वाले टर्न भी शामिल हैं। अन्यथा API 400 लौटाता है। वही पेज कहता है कि थिंकिंग डिफ़ॉल्ट रूप से चालू है, एफ़र्ट high के साथ। इसलिए गैर-थिंकिंग मॉडल के लिए लिखा गया आपका लूप एक भी पंक्ति बदले बिना टूट सकता है।

अनुरोध एक और अनुरोध दो के बीच क्या होता है#

थिंकिंग मोड में चेन ऑफ़ थॉट reasoning_content में वापस आता है, जो content के समान स्तर पर एक फ़ील्ड है। लूप की कल्पना करें:

  1. आप प्रश्न और tools सूची भेजते हैं।
  2. मॉडल tool_calls के साथ उत्तर देता है (और उनके बगल में reasoning_content)।
  3. आप टूल चलाते हैं और सब कुछ वापस भेजते हैं: इतिहास, असिस्टेंट संदेश, और परिणाम के साथ एक tool संदेश।

चरण 3 के अनुरोध पर 400 तब आता है जब उसमें असिस्टेंट संदेश में reasoning_content नहीं होता।

टूल लूप के तीन चरण: अनुरोध, reasoning_content वाला असिस्टेंट संदेश, दूसरा अनुरोध
फ़ील्ड को कहाँ यात्रा करनी है: असिस्टेंट संदेश से अगले अनुरोध में

DeepSeek पेज 400 को केवल इस टूल-कॉल मामले के लिए प्रलेखित करता है और कोई त्रुटि पाठ नहीं देता, इसलिए जब आप जाँचें कि आपके पास यह समस्या है या नहीं, तो विशिष्ट संदेश नहीं, HTTP स्थिति देखें।

समाधान: संदेश को वैसे ही जोड़ें जैसे लौटाया गया#

सबसे सुरक्षित कदम है असिस्टेंट संदेश को दोबारा बनाना ही नहीं। इसे प्रतिक्रिया से लें और पूरा जोड़ें। यहाँ CheapTokens की ओर इंगित OpenAI Python SDK के साथ एक पूरा लूप है। मौसम टूल एक स्टब है, इसलिए आप इसे वैसे ही चला सकते हैं।

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, आर्ग्युमेंट पार्सिंग) Function calling in Python with an OpenAI-compatible API में है। बेस URL, कुंजी और मॉडल आईडी CheapTokens docs page पर हैं; यहाँ उपयोग किया गया मॉडल DeepSeek page पर वर्णित है: 380K कॉन्टेक्स्ट और प्रति उत्तर अधिकतम 32,768 आउटपुट टोकन, जो लंबी थिंकिंग और उत्तर के लिए जगह छोड़ता है।

एक आम दोषी: हाथ से दोबारा बनाया गया संदेश#

फ़ील्ड खोने का एक आम तरीका है वह कोड जो प्रतिक्रिया ऑब्जेक्ट को जोड़ता नहीं बल्कि नया dict बनाता है। यह हानिरहित लगता है: role, content, tool calls। फ्रेमवर्क और रैपर भी ऐसा ही करते हैं जब वे इतिहास को अपने प्रारूप में संग्रहीत करते हैं। जिस एक फ़ील्ड के बारे में उन्होंने कभी सुना ही नहीं, वही गायब हो जाता है।

बाएँ: 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 कॉल करें।

ध्यान में रखने योग्य एक दुष्प्रभाव: आप जो इतिहास दोबारा भेजते हैं वह हर अनुरोध पर इनपुट होता है, इसलिए लंबे लूप में लंबी रीज़निंग जुड़ती जाती है। थिंकिंग टोकन कैसे गिने जाते हैं, यह Reasoning effort and thinking tokens में शामिल है, और जब कोई अनुरोध मॉडल की सीमाओं से बढ़ जाए तो क्या करें, यह Request too long: 400 vs 413 में है।

यदि फिर भी 400 लौटता है#

इन्हें क्रम से देखें:

  • विफल कॉल से ठीक पहले messages प्रिंट करें। क्या मॉडल से आए हर असिस्टेंट संदेश में reasoning_content है, यदि प्रतिक्रिया में वह था?
  • क्या आप कहीं संदेश दोबारा बनाते हैं: कोई हेल्पर, कोई इतिहास क्लास, json.dumps और वापस कुंजियों की निश्चित सूची के साथ?
  • क्या हर tool_call_id के लिए tool संदेश मौजूद है, उसी id के साथ?
  • क्या वास्तव में लंबाई है? कॉन्टेक्स्ट सीमा से अधिक अनुरोध एक अलग 400 है, जो ऊपर के लेख में वर्णित है।

त्वरित जाँच

मॉडल के टूल माँगने के बाद आप क्या जोड़ते हैं?

उत्तर दिखाएँ

उत्तर: पूरा असिस्टेंट संदेश जैसे लौटाया गया, reasoning_content सहित

tool_calls वही हैं जिनका उत्तर आपके टूल संदेश देते हैं, और अनुरोध में टूल होने पर रीज़निंग को उनके साथ चलना होता है।

यही पूरा समाधान है: एक append, कोई पुनर्निर्माण नहीं। उदाहरण से स्टेप सीमा को अपने हर लूप में जोड़ें, ताकि एक खराब टर्न पाँच अनुरोधों का खर्च करे, पाँच सौ का नहीं।