AI Development

2026 DeepSeek V4 第二輪 400:請求日誌怎麼查?

2026 DeepSeek V4 第二輪 400:請求日誌怎麼查?

獲勝者:逐跳比對同一會話的請求資料,而不是先重試。 如果首輪 tool call 正常、加入工具結果後第二輪才報 400,先檢查歷史訊息是否遺失 reasoning_content,再確認 vLLM 端點是否應使用 reasoning;只有在欄位與端點契約都正確後,才排查訊息順序和額外參數。

這篇適合維護多輪 function calling 循環、負責 API 閘道、會話資料庫或 SDK 封裝的工程師,也適合同時接入官方 API 與 vLLM、需要保留端點切換能力的推理平台團隊。

最後更新於 2026 年 8 月 15 日;資料核實自官方 thinking mode 說明官方 tool calls 文件Chat Completion 欄位定義vLLM reasoning outputs 文件

先看故障簽名:首輪成功,第二輪才失敗

最小脫敏日誌應保留同一個 conversation_id 下的三段內容:

位置 必須保留的欄位 用途
首個 assistant 回應 rolecontentreasoning_contentreasoningtool_calls 確認模型是否真的產生工具呼叫
工具回應 roletool_call_idcontent 確認工具結果是否回到正確的呼叫
下一次請求 完整訊息索引、欄位集合、端點類型、HTTP 狀態 定位欄位在哪一跳消失或改名

如果首輪連工具定義都無法解析,方向才是 schema、toolstool_choice。但首輪已經成功產生 tool_calls,而第二輪才出現 400,故障範圍通常已縮小到「歷史訊息如何被重新組裝」。

官方文件明確指出,thinking mode 中一旦發生工具呼叫,後續請求必須完整回傳該 assistant 輪次的 reasoning_content;缺少這個欄位可能直接得到 400。

常見限制有三個:

  • SDK 回應物件只保存 contenttool_calls,額外欄位在轉成字典時被忽略。
  • API 閘道只允許白名單欄位,未知的 reasoning_contentreasoning 被刪除。
  • 資料庫 schema 沒有推理欄位,消費者重新建立 assistant 訊息時只取固定欄位。

因此,重試通常沒有幫助。重試只會把同一份缺欄位的歷史訊息再次送出。

沒有推理欄位:先查保存與序列化,不要改工具定義

若最終失敗請求中的 assistant 訊息完全沒有推理欄位,先不要調整工具 JSON Schema。排查重點是資料流:

assistant = {
    "role": response["role"],
    "content": response.get("content"),
    "tool_calls": response.get("tool_calls"),
    "reasoning_content": response.get("reasoning_content"),
}

上段只代表「應保存哪些欄位」,不代表兩類端點可以共用同一個出站物件。對官方 API 的 thinking tool loop,發生工具呼叫的 assistant 訊息需要保留 reasoning_content。官方範例也直接把模型回應中的完整 assistant 訊息加入 messages,其中包含 contentreasoning_contenttool_calls

建議在四個位置列印「欄位集合」,而不是列印完整推理內容:

取樣節點 建議記錄 看到甚麼才算正常
SDK 原始回應 sorted(message.keys()) 有工具呼叫時包含推理欄位
內部會話物件 欄位集合與訊息索引 assistant 索引沒有被重建遺失
資料庫或佇列輸出 schema 欄位、序列化後欄位 推理欄位仍存在,空值策略一致
最終 HTTP body 端點、版本、欄位集合 與目標端點契約一致

實務上,以下幾種程式碼最容易製造第二輪 400:

messages.append({
    "role": "assistant",
    "content": message.content,
    "tool_calls": message.tool_calls,
})

這段程式主動丟掉了 reasoning_content。若上游是官方 thinking API,下一輪就不再是原本的會話上下文。

另外,串流模式需要注意增量合併。reasoning_content 可能分散在多個 delta;若合併器只累積 content,首輪畫面看似正常,實際保存的 assistant 訊息卻沒有完整推理欄位。

有推理內容但送錯欄位:官方 API 與 vLLM 必須分開處理

這是 reasoning_contentvLLM reasoning 最容易混淆的地方。

端點 主要推理欄位 排查重點 不應直接假設
DeepSeek 官方 API reasoning_content thinking tool loop 是否完整回傳 vLLM 的 reasoning 可直接代替
當前 vLLM 文件與介面 reasoning 實際部署版本、reasoning parser、chat template 所有版本都接受舊欄位
內部訊息模型 建議使用中立欄位,例如 reasoning_text 進入出站適配器前保持一致 內部物件可直接當 HTTP body

vLLM 文件已說明,推理輸出欄位由舊名稱 reasoning_content 改為 reasoning,遷移方式是替換欄位名稱。文件同時展示了工具呼叫與 reasoning 並存的回應形式。(vLLM reasoning outputs 文件)

但這不代表 reasoning 可以不經轉換地發給官方 API。較穩妥的做法是保留一個內部欄位,再依端點映射:

def to_outbound_message(message, endpoint):
    out = {
        "role": message["role"],
        "content": message.get("content"),
    }

    if message.get("tool_calls") is not None:
        out["tool_calls"] = message["tool_calls"]

    if endpoint == "deepseek-api":
        out["reasoning_content"] = message.get("reasoning_text")
    elif endpoint == "vllm":
        out["reasoning"] = message.get("reasoning_text")

    return out

這個適配層有兩個優點:

  • 不需要在全域訊息物件同時複製兩個欄位。
  • 端點切換時,欄位轉換集中在一處,容易配合版本測試。

vLLM 的實際輸入兼容行為仍應按部署版本驗證。文件中的欄位名稱、parser 和 chat template 是協議線索,不是所有歷史版本的永久保證。社群 issue 可用來找復現方向,但不能直接推論所有 vLLM 版本都會丟棄舊欄位。(vLLM 社群 issue 復現報告)

欄位在記憶體裡,經過閘道或資料庫後消失

若 SDK 原始回應有推理欄位,內部物件也有,但最終 body 沒有,故障已不在模型端。可按以下順序逐跳取證:

1. 檢查 SDK 物件轉換

部分 SDK 的訊息物件不是普通字典。使用 .dict()model_dump() 或自訂 JSON encoder 時,未宣告欄位可能被排除。日誌只記錄欄位名稱即可:

assert "tool_calls" in assistant
assert "reasoning_content" in assistant or "reasoning" in assistant

2. 檢查閘道白名單

閘道常見設定包括:

風險位置 可能造成的結果 證據
Request schema 未知欄位被拒絕或刪除 閘道前後欄位集合不同
空值清理器 空字串或 null 被移除 原始欄位存在,出站欄位消失
JSON 重建器 只複製固定欄位 欄位順序或索引重新生成
流式合併器 只合併 content reasoning 增量沒有進入完整訊息

3. 檢查資料庫與佇列

若 schema 只有 rolecontenttool_calls,即使應用程式暫時保存了推理內容,寫入資料庫後仍會遺失。消費者服務也可能使用舊版資料模型,把新增欄位當成未知欄位丟棄。

最有價值的證據不是完整 payload,而是同一個訊息索引在各節點的差異:

message[3] sdk:      role, content, reasoning_content, tool_calls
message[3] memory:   role, content, reasoning_content, tool_calls
message[3] database:  role, content, tool_calls
message[3] outbound: role, content, tool_calls

此時應修資料庫 schema 或消費者映射,不應透過重試掩蓋問題。若需要固定環境保存失敗會話,可先參考 ProxyMac 的說明資源,將日誌欄位、端點版本和回放方式固定下來。

推理欄位正確:再查訊息鏈與額外參數

當最終請求已含正確推理欄位,400 仍可能來自訊息鏈,而不是欄位名稱。

應逐項核對:

  • assistant 工具呼叫是否排在對應的 tool 訊息之前。
  • tool_call_id 是否與 assistant 的呼叫 ID 完全一致。
  • tool 訊息是否只回應實際存在的工具呼叫。
  • assistant 的 content 是否被框架改成不符合端點要求的空值型別。
  • 是否在 thinking mode 中帶入端點不支援的額外參數。

官方 thinking mode 文件列出,思考模式不支援 temperaturetop_ppresence_penaltyfrequency_penalty;相容行為可能讓參數不一定立即報錯,但不應把所有 400 都歸因於推理欄位。

tool_choice 也要單獨核對。某些整合文件已提示,DeepSeek V4 thinking mode 對 tool_choice 有限制;若錯誤訊息明確指向參數,就應依該端點文件移除或改寫,而不是繼續複製推理欄位。(DeepSeek thinking mode 參數說明)

按條件選擇修復層:不要用全域雙欄位掩蓋問題

可用以下分支決定修復位置:

  • 若 SDK 原始回應已缺欄位:先檢查 SDK、串流合併器和模型回應解析器;不要修改資料庫。
  • 若記憶體有欄位、資料庫沒有:修 schema、序列化規則或佇列消費者。
  • 若資料庫有欄位、官方 API 最終請求沒有:修官方 API 出站適配器,映射到 reasoning_content
  • 若 vLLM 回應只有 reasoning:內部先統一成中立欄位,再按版本和端點映射。
  • 若欄位、順序和 ID 都正確但仍 400:檢查 tool_choice、thinking mode 參數、chat template 和明確錯誤訊息。
  • 若無法判斷是 API 還是 vLLM 問題:用同一份最小會話分別直連兩個端點,再回放完整業務鏈路。

不建議在所有訊息中同時加入 reasoning_contentreasoning。這可能暫時讓某些鏈路「看似恢復」,卻把端點契約錯誤藏在資料模型內,之後升級 parser 或閘道時更難定位。

用同一會話回放,確認修復真正生效

最小回放應固定 3 個測試入口

回放入口 目的 必須記錄
直連官方 API 確認 reasoning_content 與 tool loop 契約 端點、模型、thinking 設定、HTTP 回應
直連目標 vLLM 確認 reasoning、parser 與版本行為 vLLM 版本、啟動參數、chat template
經完整業務鏈路 確認閘道、資料庫、佇列沒有丟欄位 每跳欄位集合、訊息索引、前後差異

回放時不要只測新的單輪請求。應保留:

  1. 使用者訊息。
  2. 首個 assistant 工具呼叫。
  3. 對應 tool 結果。
  4. 下一次請求。
  5. 最終回應或 400 body。

修復完成後,版本控制中至少留下以下紀錄:

  • 修改前後的出站 JSON 欄位差異。
  • 端點類型:官方 API 或 vLLM。
  • 實際 vLLM 版本與 parser 設定。
  • tool_call_id 和訊息索引是否保持一致。
  • 明確錯誤訊息及回放結果。

這份基線能在 DeepSeek 或 vLLM 更改欄位名稱、兼容策略或工具參數限制後,快速判斷是端點變更,還是自家資料鏈路再次丟欄位。

常見問題

DeepSeek V4 為甚麼第一次工具呼叫成功,第二次才報 400?

首輪成功表示工具定義和初次解析可能沒有問題。第二輪加入 tool 結果後,框架必須重送先前的 assistant 訊息;若 reasoning_content 在保存、序列化或出站轉換時消失,官方 API 便可能拒絕該歷史上下文。

reasoning_content 已經保存,為甚麼多輪請求仍然失敗?

保存位置不一定是出錯位置。需要比較 SDK、記憶體物件、資料庫、閘道和最終 body 的欄位集合。即使 reasoning_content 存在,tool_call_id 不匹配、訊息順序錯誤、空值型別不相容或額外參數受限,也可能造成 400。

vLLM 返回 reasoning 後能否直接發給 DeepSeek 官方 API?

不應直接發送。當前 vLLM 文件使用 reasoning,官方 thinking tool loop 要求 reasoning_content。兩者應由適配層轉換,並以實際 vLLM 部署版本做直連回放;社群報告只能作為特定版本的排查線索。

怎麼抓取多輪 tool call 的最小請求日誌定位丟欄位環節?

只保留同一會話的 assistant 工具呼叫、tool 結果和下一次失敗請求,逐跳記錄欄位集合、訊息索引、端點與狀態碼。推理內容、工具結果和金鑰全部脫敏。若前後欄位集合不同,差異所在的節點就是第一個應修復的位置。

如果目前方案是把兩類端點共用一份訊息 JSON,常見缺點是欄位契約混雜、閘道白名單難以維護、vLLM 升級後兼容行為不易驗證,還會讓失敗會話難以穩定重播。先把適配層和回放環境固定,比單純增加伺服器或重試次數更可靠。需要持續保留固定測試環境時,可進一步查看 ProxyMac 的雲端環境說明;若專案還依賴 macOS 客戶端、Xcode 自動化或長時間在線任務,再評估 ProxyMac 的方案頁面,讓同一套回歸流程有可重複的 Mac 執行環境。

為第二輪 400 建立可重現的遠端測試環境

使用 ProxyMac 遠端 Mac,重播相同會話並逐跳比對請求與回應,協助縮短問題定位時間。
透過 ProxyMac 彈性租用 Mac 資源,分開驗證本機、閘道與後端環境差異,避免盲目重試或擴容。