Day2 把 agent loop 拆成四站:看現況、想下一步、動手做、看結果。前面幾天拆的都是「看現況」(AGENTS.md、Skills)和「動手做」(工具)。今天拆中間那一站——模型願意花多少力氣想。
Pi 把這件事做成一個叫 thinking level 的旋鈕,七個檔位:
off → minimal → low → medium → high → xhigh → max
預設是 medium。可以用 --thinking high 指定,也可以用 --model luna:high 這種簡寫,互動模式裡則在 /settings 調。我們每一次實驗的 session 記錄第三行就是它:
{"type":"thinking_level_change","thinkingLevel":"medium"}
這是實作上最有意思的部分。各家模型的「思考」控制方式不一樣,Pi 用一張 thinkingLevelMap 把自己的七個檔位對應到各模型實際支援的值。這張表有三種狀態:
| 值 | 意思 |
|---|---|
| 省略 | high 以下走 provider 的預設對應,xhigh 和 max 不支援 |
| 字串 | 支援,送這個值給 provider |
null |
不支援,直接隱藏/跳過/夾到最近的等級 |
我們這系列用的 gpt-5.6-luna,它的表長這樣:
"thinkingLevelMap": { "minimal": "low", "xhigh": "xhigh", "max": "max" }
翻譯成白話:off、low、medium、high 照標準對應送出去;minimal 這個檔位在這個模型上其實就是 low——你選 minimal,送出去的是 low;而 xhigh、max 這兩個延伸檔位它有支援。
這代表一件事:同樣一個 --thinking minimal,換一個模型可能是完全不同的東西。做跨模型比較時,這是個容易踩到的陷阱。
off 不是送一個「off」另一個實作細節:把 thinking 關掉的時候,Pi 不是送 reasoning_effort: "off",而是整個欄位不送:
const reasoningEffort = options?.reasoningEffort;
if (!reasoningEffort && value.omitWhenOff) {
return undefined;
}
這是對的做法——很多 API 沒有「關閉思考」這個值,只有「不要求思考」。
在 session 記錄裡,模型的思考會以一個 thinking 型別的 content block 出現。但用 Codex provider 時,內容是加密的:
{"type":"thinking","thinking":"",
"thinkingSignature":"{\"id\":\"rs_...\",\"encrypted_content\":\"gAAAAA...\",\"summary\":[...]}"}
真正的推理過程看不到,有時候 summary 會給一行標題,例如我們某次執行裡出現的 **Fixing discount calculation and tests**。
看不到內容,但量得到用量。每一輪的 usage 裡有一個獨立的 reasoning 欄位,記錄這一輪花了多少 reasoning tokens。
把第一輪校準的 30 次執行(全部都是預設的 medium)攤開來看:
| 任務 | reasoning tokens | output tokens | reasoning 佔 output |
|---|---|---|---|
| T1 修分頁 bug | 332 | 2,366 | 14% |
| T2 新增 endpoint | 1,848 | 10,551 | 18% |
| T3 修 CI 檢查 | 628 | 4,855 | 13% |
| T4 拆常數 | 1,453 | 9,292 | 16% |
| T5 資料遷移 | 2,445 | 9,090 | 27% |
| 合計 | 6,706 | 36,154 | 19% |
兩個觀察:
第二點很反直覺,也直接影響 Day17 的假設:把 thinking 調高,直觀想是「變貴」,但如果想得更清楚能讓它少繞幾輪、少讀幾個檔案,省下來的 input 可能比多花的 output 更多。
Day17 會拿同樣的任務跑 off、low、high 三組,看三件事:
因為校準已經顯示成功率有天花板效應,我的事前預測是:成功率三組差不多,但成本和輪數會分出高下。這個預測現在先寫下來,明天再看資料打不打臉。
Day17 公布 off/low/high 的三條曲線。