# 叫它先自我審查，它把審查過程直接寫進答案裡

- URL: https://justfly.idv.tw/%e5%8f%ab%e5%ae%83%e5%85%88%e8%87%aa%e6%88%91%e5%af%a9%e6%9f%a5%ef%bc%8c%e5%ae%83%e6%8a%8a%e5%af%a9%e6%9f%a5%e9%81%8e%e7%a8%8b%e7%9b%b4%e6%8e%a5%e5%af%ab%e9%80%b2%e7%ad%94%e6%a1%88%e8%a3%a1/
- 日期: 2026-04-26
- 分類: 技術筆記
- 標籤: api, claude, 神島

![叫它先自我審查，它把審查過程直接寫進答案裡]

早餐店點完餐，阿姨在小紙條上記了「少糖去冰」，結果把那張便條紙直接塞進袋子裡。打開袋子看到的是備註本身，飲料壓在下面。叫 LLM「先自我檢查再輸出 JSON」，很容易就變成這樣。先講結論：檢查步驟要嘛明講只能在思考區做，要嘛 parser 自己扛得住 JSON 前面多出來的字，最穩是兩層一起做。

##### 症狀長這樣

常見寫法是 prompt 最後要求「Return ONLY valid JSON」，程式端拿到回應直接 `JSON.parse(text)`，中間沒有任何前處理。平常相安無事，直到 prompt 多加一條「輸出前先做 self-check」。然後 parse 開始一半一半地炸，錯誤訊息很直接：`Unexpected token '#', "## Pre-Wri"...`。把原始回應拉出來看，開頭是一段 markdown 的自我檢查筆記，後面才接你要的 JSON。

我碰到的那次，加這條指令之前失敗率接近零，加了之後跳到 50%。品管步驟本身變成了失敗來源。

##### 為什麼會這樣

模型對「先做 X 再做 Y」的解讀不是固定的。有時候它把 X 放進 thinking，有時候直接寫進回應本文。開了 extended thinking 也不保證，模型只在自己判斷需要時才走那條路。Prompt 寫「先做 X」，它就可能把 X 當成輸出的一部分。

還有一個容易忽略的推手：prompt 本身用了一堆 markdown 標題。上下文一直在示範 markdown 格式，最後那句「ONLY valid JSON」就輸給了示範效果。

##### 為什麼一開始看不出來

不是每次都失敗。成功的那一半讓問題看起來像偶發抽風，不像 prompt 設計的缺陷；加上 prompt 裡明明寫了只能回 JSON，直覺上會以為有寫就有保障。沒有。

##### 怎麼確認

把失敗跟成功的原始回應並排比：失敗的開頭是 `## Pre-Write Self-Check`，成功的開頭是 `{`。分佈有規律、不是隨機，就可以排除 API 抖動，問題在 prompt 沒有壓住輸出格式。

##### 兩層防法

上游是 prompt。自我檢查那段的標題直接寫死「MANDATORY — perform this INSIDE your thinking/reasoning block only; NEVER include this section text in the JSON response body」，輸出格式那段再加一條 CRITICAL OUTPUT RULE，把後果講清楚：「if ANY markdown appears before the opening `{`, parsing fails」。

下游是 parser。`JSON.parse()` 之前先剝前綴：找第一個 `{` 到最後一個 `}`，開頭有沒有殘留文字都抓得到真正的 JSON。

```
// 去掉 JSON 前後的雜訊（markdown 筆記、```json 圍欄），只取第一個 { 到最後一個 }
function extractJson(text) {
  const start = text.indexOf('{');
  const end = text.lastIndexOf('}');
  if (start === -1 || end
