Kembali ke blog

Pemecahan masalahPanduan

400 setelah tool call: kirim kembali reasoning_content

Mengapa permintaan kedua dari loop tool-call mengembalikan 400 pada model thinking, perbaikan Python yang menambahkan pesan lengkap, dan kapan Anda bisa melewatinya.

Oleh CheapTokens TeamDiterbitkan 4 menit baca

Sampul: loop tool-call di mana reasoning_content berpindah dari pesan assistant ke permintaan berikutnya
Di halaman ini
  1. Apa yang terjadi antara permintaan satu dan permintaan dua
  2. Perbaikannya: tambahkan pesan sebagaimana dikembalikan
  3. Tersangka umum: pesan yang dibangun ulang secara manual
  4. Kapan Anda tidak memerlukan ini
  5. Jika masih mengembalikan 400

Loop agen Anda berfungsi pada permintaan pertama dan gagal pada permintaan kedua dengan 400. Pada model thinking, salah satu penyebab yang terdokumentasi adalah field yang hilang: pesan assistant yang Anda kirim kembali telah kehilangan reasoning_content-nya. Jika itu penyebabnya, menambahkan seluruh pesan persis seperti yang dikembalikan API akan memperbaikinya.

Panduan mode thinking DeepSeek (docs, diperiksa 8 November 2026) merangkum aturannya dalam satu kalimat: ketika permintaan memiliki parameter tools, reasoning_content harus dikirim kembali secara utuh di semua permintaan berikutnya, termasuk giliran tanpa tool call. Jika tidak, API mengembalikan 400. Halaman yang sama menyebut thinking aktif secara default, dengan effort high. Jadi loop yang Anda tulis untuk model non-thinking bisa rusak tanpa satu baris pun berubah.

Apa yang terjadi antara permintaan satu dan permintaan dua#

Dalam mode thinking, chain of thought dikembalikan dalam reasoning_content, field pada level yang sama dengan content. Bayangkan loop-nya:

  1. Anda mengirim pertanyaan dan daftar tools.
  2. Model menjawab dengan tool_calls (dan reasoning_content di sampingnya).
  3. Anda menjalankan tool dan mengirim semuanya kembali: riwayat, pesan assistant, dan pesan tool dengan hasilnya.

400 muncul pada permintaan di langkah 3 jika pesan assistant di dalamnya tidak memiliki reasoning_content.

Tiga langkah loop tool: permintaan, pesan assistant dengan reasoning_content, permintaan kedua
Ke mana field harus berpindah: dari pesan assistant ke permintaan berikutnya

Halaman DeepSeek mendokumentasikan 400 hanya untuk kasus tool-call ini dan tidak mengutip teks error, jadi perhatikan status HTTP, bukan pesan spesifik, saat Anda memeriksa apakah Anda memiliki masalah ini.

Perbaikannya: tambahkan pesan sebagaimana dikembalikan#

Langkah teraman adalah tidak membangun ulang pesan assistant sama sekali. Ambil dari respons dan tambahkan utuh. Berikut adalah loop lengkap dengan OpenAI Python SDK yang diarahkan ke CheapTokens. Tool cuaca adalah stub, jadi Anda bisa menjalankannya apa adanya.

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)),
        })

Satu baris yang penting adalah messages.append(msg.model_dump(exclude_none=True)). SDK menyimpan field yang tidak dikenalnya, jadi apa pun yang API letakkan pada pesan, termasuk reasoning_content, ikut terkirim kembali. Jawaban akhir juga ditambahkan, sesuai aturan: setelah tool call pertama, setiap permintaan berikutnya membawa reasoning dari giliran assistant sebelumnya.

Mekanisme tool-call itu sendiri (schema, tool_call_id, parsing argumen) ada di Function calling in Python with an OpenAI-compatible API. Base URL, key, dan model id ada di halaman docs CheapTokens; model yang digunakan di sini dijelaskan di halaman DeepSeek: konteks 380K dan hingga 32.768 token output per jawaban, yang menyisakan ruang untuk thinking panjang plus jawaban.

Tersangka umum: pesan yang dibangun ulang secara manual#

Cara umum kehilangan field ini adalah kode yang tidak menambahkan objek respons tetapi membangun dict baru. Terlihat tidak berbahaya: role, content, tool calls. Framework dan wrapper melakukan hal yang sama ketika menyimpan riwayat dalam format mereka sendiri. Satu field yang tidak pernah mereka kenal adalah yang hilang.

Kiri: pesan assistant dibangun ulang dari role, content, tool_calls. Kanan: pesan sebagaimana dikembalikan, dengan reasoning_content
Perbedaan antara kedua pesan assistant adalah satu field

Bandingkan

Dibangun ulang manual vs ditambahkan utuh

Dibangun ulang manual (rusak)

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

Ditambahkan utuh (berfungsi)

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

Jika Anda menggunakan framework dan tidak bisa menyentuh baris itu, periksa bagaimana ia menyimpan giliran assistant. Riwayat yang melewati serializer yang hanya menyimpan content dan tool_calls memiliki bug yang sama, hanya satu lapisan lebih rendah.

Kapan Anda tidak memerlukan ini#

Tidak setiap panggilan model thinking memerlukan perhatian ekstra. Untuk chat multi-turn tanpa tool, panduan DeepSeek menyebut reasoning_content tidak harus dikirim kembali; jika Anda tetap mengirimnya, ia diabaikan dan reasoning sebelumnya tidak ditambahkan ke konteks. Jadi chat biasa bisa terus membuangnya dan tidak ada yang rusak.

Pilih kasus Anda

Kasus mana milik Anda?

Permintaan memiliki tools, model sedang thinking

Tambahkan seluruh pesan assistant setiap giliran, baik giliran tool maupun jawaban akhir.

Chat biasa, tanpa parameter tools

Tidak perlu perbaikan. Anda boleh membuang reasoning_content dari riwayat, atau mengirimnya; ia diabaikan.

Framework membangun riwayat untuk Anda

Temukan tempat ia menyerialisasi pesan assistant dan pastikan reasoning_content bertahan. Jika tidak bisa, panggil API dengan loop Anda sendiri untuk bagian tool.

Efek samping yang perlu diingat: riwayat yang Anda kirim ulang adalah input pada setiap permintaan, jadi reasoning panjang dalam loop panjang akan terakumulasi. Cara token thinking dihitung dibahas di Reasoning effort and thinking tokens, dan apa yang harus dilakukan ketika permintaan melebihi batas model ada di Request too long: 400 vs 413.

Jika masih mengembalikan 400#

Periksa secara berurutan:

  • Cetak messages tepat sebelum panggilan yang gagal. Apakah setiap pesan assistant yang berasal dari model memiliki reasoning_content, jika respons memilikinya?
  • Apakah Anda membangun ulang pesan di suatu tempat: helper, kelas riwayat, json.dumps dan kembali dengan daftar key tetap?
  • Apakah pesan tool untuk setiap tool_call_id ada, dengan id yang sama?
  • Apakah benar-benar masalah panjang? Permintaan yang melebihi batas konteks adalah 400 yang berbeda, dijelaskan di artikel di atas.

Cek cepat

Apa yang Anda tambahkan setelah model meminta tool?

Tampilkan jawaban

Jawaban: Seluruh pesan assistant sebagaimana dikembalikan, termasuk reasoning_content

tool_calls adalah yang dijawab oleh pesan tool Anda, dan dengan tool dalam permintaan, reasoning harus ikut bersama mereka.

Itulah seluruh perbaikannya: satu append, tanpa membangun ulang. Tambahkan batas langkah dari contoh ke setiap loop yang Anda rilis, agar giliran yang buruk memakan lima permintaan, bukan lima ratus.