跳到主要內容

【2026 最新】SteerDistill 教學:量化 Agent 怎麼找回走錯的一步?用 CPU Trace 分清救援與學會

最後更新: ·
SteerDistill 教學:找回走錯的一步,分清救援成功與學生學會

你讓本機 AI 幫忙寫檔案,它回覆「完成」,打開資料夾卻發現內容不對。提醒它「先建立檔案,再寫入」之後,它終於做對了。這篇 SteerDistill 教學要帶你問下一個問題:換一個全新任務、拿走提醒,它還會做嗎?

前半專為沒有機器學習背景的讀者寫;後半只需要已有的 Python 3。你會學會讀工具 Trace(逐步執行紀錄)、標出第一個偏離,再跑三種固定策略的 CPU 玩具重播。程式沒有載入語言模型或訓練權重;目標是學會辨認「有人扶著做對」與「學生獨立會做」各需要哪份證據。

本文依 2026 年 11 月 13 日查閱的論文、官方程式、案例紀錄與技術文件整理。SteerDistill 預印本的 arXiv v1 首次提交於 11 月 10 日;以下把作者的研究流程、作者公開案例與本文自製練習分開說明。

先說結論:救援成功+無提示訓練+獨立驗收,才接近「學會」

教師提示像腳踏車的扶手;當場騎穩,和拿掉扶手後仍能騎,是兩場考試。SteerDistill 先在學生犯錯的情境建立可驗證的修正,再透過蒸餾把修正內化,部署時讓學生自己走。

今天先做到一個小成果:打開一份 JSON,指出哪一回合走偏、修正後環境變了什麼,以及提示拿掉後有沒有重新測。這比只看最後一句「任務完成」更能幫你判讀量化 Agent 的研究。

一、SteerDistill 教學先認五個零件

  • ①「縮小刻度」:量化。用較低位元表示部分數值,像把精細尺換成較粗刻度的尺。Transformers 量化文件說明了不同方法與相容條件;模型檔變小後,仍要另外驗任務行為。
  • ②「帶路的人」:教師。論文使用凍結的全精度教師提供參考與指引。全精度描述數值格式,不表示它每次都答對;這個流程需要同題、同初始環境下的成功參考。
  • ③「真的動手的人」:學生。量化模型在工具環境中產生動作。教師提出修正線索後,仍由學生生成新的回應並執行,執行結果再送回核對。
  • ④「監視器」:Trace。至少留下當時看見的歷史、工具名與參數、工具回傳、執行前後狀態。Agent 會說什麼和工具實際改了什麼,要分別記錄。
  • ⑤「收進本事」:蒸餾。透過訓練改變學生的參數,讓它在較少輔助下學到目標行為。先讀 模型蒸餾入門,可以補上老師、學生與訓練目標的基本關係。

W4A16 的 W 指權重、A 指 activation(中間運算訊號);數字分別是 4 位元與 16 位元。官方精度指南另列 W2A16、W4A8 路線。這些標籤不足以說明你的模型、執行後端與整套工具鏈是否相容,別只把數字相同當成同一實驗。

二、先看一條完整 Trace:空檔案過關,任務仍在後面

設想一個特別設計的練習工具:write 只寫入已存在的檔案;create 負責建立空檔。這是本篇 Mock 的規則,不是所有檔案系統的通則。任務是讓 note.txt 內容逐字等於 Hello trace。

  • 回合 0:參考與學生都執行 inspect,看到沒有檔案;兩邊狀態相同。
  • 回合 1:參考執行 create,得到空的 note.txt;學生直接 write,收到 FILE_MISSING。第一個偏離出現。
  • 回到回合 1 之前:把歷史和環境一起還原。提示「這個工具要先有檔案」,再讓學生嘗試建立。
  • 局部驗證:空檔已存在、其他狀態正確,這一步可過關。此時檔案還沒裝入 Hello trace。
  • 後續驗證:寫入並讀回完整內容,才判整個任務成功。再另開新任務測無提示表現,才開始問是否學會。

這個拆法有官方案例可對照。作者的 Matching sentence 訓練案例記錄:教師指引後,建立空 1.txt 通過局部核對,後續才寫入第二個含指定字串的完整句子。那是作者公開的特定軌跡,並非 AlphaLab 跑出的模型成績,也不能單憑這一例推算成功率。

SteerDistill 教學:局部修正、完整任務與無提示驗收是不同關卡
空檔建立只回答第一關;寫對內容與新任務獨立完成,另外判分。圖/AlphaLab。

原論文第 3.1 節按環境狀態轉移對齊學生與參考,並把造成相同轉移的動作視為等價;不是逐字比工具名稱。局部驗證再核對狀態與回傳資訊。只讀操作尤其要看讀到了什麼,否則「狀態沒變」會把錯讀與正確讀混在一起。

閉環的意思,是教師會讀取這次嘗試的執行回饋:若局部驗證沒過,便修訂指引,再從同一段偏離前歷史與狀態重試;直到通過或用完該步的嘗試額度。通過後,學生接著在不帶指引的情況繼續任務;若後面再出現偏離,就重新定位。這個回饋迴圈,才是它和把同一句提示重貼幾次的關鍵差別。

三、SteerDistill 教學實作:三個 CPU Mock、三種固定分支

第 1 步:「要付 API 費嗎?」先用純記憶體練習場

建立新的空資料夾,存入下方 replay.py。它只使用 Python 標準函式庫,Mock 檔案存在字典裡;登入是一個布林值,不讀帳密、不打網路,也不修改你的真實 note.txt。唯一產生的檔案是當前資料夾的 toy-trace.json。

三題依序是缺檔、參數名錯誤、缺少登入。每題先固定參考與錯誤策略,再從同一快照測「重試」「有提示」「移除提示」。hint 分支由程式直接選擇預寫的正確動作,不包含真正教師生成、回饋修訂或模型推論。它像排好走位的教具,讓你檢查驗證器,不是用來比較模型能力。

第 2 步:「怎麼保證起點一樣?」複製完整狀態

程式使用 deepcopy 複製巢狀字典。Python 的深複製文件說明它和只換一個變數名稱不同:若共用內層 files,上一組建立的檔案可能留給下一組,讓「移除提示」看起來也會做。真實外部服務則需要快照或重建機制,複製 Python 物件不能撤回已送出的操作。

from copy import deepcopy
import json

TARGET = "Hello trace"
WRITE = ("write", {"path": "note.txt", "text": TARGET})
CASES = {
    "missing_file": ({"files": {}, "logged_in": True},
                     ("create", {"path": "note.txt"}), WRITE),
    "wrong_argument": ({"files": {"note.txt": ""}, "logged_in": True},
                       WRITE, ("write", {"path": "note.txt", "content": TARGET})),
    "missing_login": ({"files": {"note.txt": ""}, "logged_in": False},
                      ("login", {}), WRITE),
}

def tool(state, action):
    name, args = action
    if name == "login" and args == {}:
        state["logged_in"] = True  # Boolean mock; no real account.
        return {"ok": True}
    if name == "inspect" and args == {}:
        return deepcopy(state)
    if name == "create" and set(args) == {"path"}:
        if not state["logged_in"]:
            return {"error": "LOGIN_REQUIRED"}
        state["files"].setdefault(args["path"], "")
        return {"ok": True}
    if name != "write" or set(args) != {"path", "text"}:
        return {"error": "BAD_ARGUMENTS"}
    if not state["logged_in"]:
        return {"error": "LOGIN_REQUIRED"}
    if args["path"] not in state["files"]:
        return {"error": "FILE_MISSING"}
    state["files"][args["path"]] = args["text"]
    return {"ok": True}

def run(initial, actions):
    state, trace = deepcopy(initial), []
    for action in actions:
        before = deepcopy(state)
        response = tool(state, action)
        trace.append({"action": action, "before": before,
                      "response": response, "after": deepcopy(state)})
    return trace

def same_transition(a, b):
    return all(a[k] == b[k] for k in ("before", "after", "response"))

def policy(case, hint=False):
    # Handwritten fixed policy; this function is never trained.
    _, correction, mistake = CASES[case]
    return correction if hint else mistake

def main():
    records = {}
    for case, (initial, correction, mistake) in CASES.items():
        prefix = [("inspect", {})]
        reference = run(initial, prefix + [correction])
        original = run(initial, prefix + [mistake])
        # For this toy only, compare the two known aligned boundaries.
        divergence = next(i for i, (a, b) in enumerate(zip(original, reference))
                          if not same_transition(a, b))
        snapshot = original[divergence]["before"]
        arms = {}
        for arm, hint in (("retry", False), ("hint", True), ("hint_removed", False)):
            local = run(snapshot, [policy(case, hint)])[0]
            # Fixed branch plans, not a generated model continuation.
            continuation = run(local["after"], [WRITE if hint else mistake])[0]
            arms[arm] = {"local_pass": same_transition(local, reference[divergence]),
                         "task_pass": continuation["after"]["files"].get("note.txt") == TARGET,
                         "local": local, "continuation": continuation}
        records[case] = {"first_divergence_zero_based": divergence,
                         "original": original, "reference": reference, "arms": arms}
        print(case, {a: (v["local_pass"], v["task_pass"]) for a, v in arms.items()})
    # Exclusive creation prevents accidental overwrite of an earlier run.
    with open("toy-trace.json", "x", encoding="utf-8") as f:
        json.dump(records, f, ensure_ascii=False, indent=2)

if __name__ == "__main__":
    main()

第 3 步:「怎麼執行?」存好後只跑一次

python3 replay.py

成功執行後,每題會列出三組 (local_pass, task_pass),並保存完整 JSON。程式以 "x" 建立輸出;重跑前改用新的空資料夾,避免覆蓋先前紀錄。若系統找不到 python3,先讀 Trace 與下一節的判分,等已有可用 Python 環境再操作。

本次已執行這份固定策略程式:三題的 hint 分支均為 (True, True),retry 與 hint_removed 均為 (False, False)。這些是手寫分支的預定行為經程式核對後的結果,不是模型準確率。函式未訓練、未更改策略,移除提示後回到原先錯誤,正好示範為何「救回一次」不足以宣稱能力恢復。

四、怎麼讀 JSON,才不會把重試當成學習?

先打開 missing_file。first_divergence_zero_based 為 1,表示索引從 0 算的第二回合。逐欄比 original[1] 與 reference[1]:先看 before 是否一致,再看 after 和 response;最後才看動作字串。

  • 只換動作、不重設:起點可能已被前一次嘗試改過。先核對每個 arm 的 local.before 是否相同;在這份玩具裡,它應等於原始偏離回合的 before。
  • 只看工具回 ok:可能建立了檔案,卻沒完成內容。local_pass 看當步轉移;task_pass 看後續是否寫對完整字串。
  • 移除提示卻沿用舊歷史:歷史可能還藏著提示或答案。正式模型測試要另開乾淨歷史,保留相同任務規則與工具,不帶教師指引。
  • 只報一個總成功率:先分開本來成功、教師救回、訓練後獨立成功,以及新增失敗。題目、嘗試次數和額外推論成本一起留。

本篇只對齊兩個已知且等長的回合,並比較完整字典與回傳;它沒有實作官方跨軌跡對齊、嘗試預算、教師多輪修訂或訓練器。正式工具的唯讀回傳、時間欄位、識別碼與外部副作用都要另設核對規則。可對照 官方轉移驗證器,再讀 最小 Agent Harness補上紀錄與停止條件。

五、真正的 SteerDistill,還多了哪一堂訓練課?

玩具練習停在「看懂驗證」。論文方法第 3.2 節的下一步,是讓學習中的學生拿不到指引,卻去貼近有指引的目標預測。就像考卷不印解題提示,但訓練時仍有老師提供的學習訊號。

在修正回合,目標來自凍結的學生快照:它看到指引後對下一個 Token 的機率分布;學習中的學生只看去掉指引的上下文,以 forward KL 學習。在其他回合,仍由全精度教師提供分布,以 reverse KL 學習。KL 是衡量兩個機率分布差異的量;這裡方向不同,對應兩種訓練目標。白話說:修錯的地方學「被扶正的自己」,其餘地方繼續跟教師學。

這不是把教師答案貼進聊天記憶,也不是單純多試幾次。論文保留已通過局部驗證的修正,即使整題最後仍未完成;學到的學生再產生下一批軌跡。推論階段使用訓練後的量化學生,不攜帶教師與 steering prompts。是否真的能獨立處理你的任務,仍由保留測試題回答。

截至本次查閱,官方 README的參考訓練配置是八張 A100 80GB,並要求 Linux、NVIDIA GPU 與分開的訓練、推論、benchmark 環境。這是作者的參考配置,不是證明所有小規模實驗都必須八張卡;它也足以說明本篇 CPU 字典重播與完整訓練的工作量不同。

已有合適環境與授權 checkpoint 的讀者,先讀 官方環境與可重現性文件及 評估指南,固定模型、量化方式、工具版本、資料切分與快照方式,再走官方 preflight。缺少這些條件時,先練判讀公開案例,不必為了今天這堂課下載大型權重或購買 GPU。

常見問題:八個先講答案的判斷

1. 有提示做對,就代表蒸餾完成嗎?

不代表。提示改變當次輸入;蒸餾還包含參數訓練。本篇 hint 分支是固定策略教具,不能提供模型學會的證據。

2. 蒸餾後還要帶教師一起上線嗎?

依這篇論文的設定,不需要。推論使用已訓練的量化學生;訓練時教師和快照仍有算力成本。

3. 第一個不同的工具名,就是第一個偏離嗎?

不一定。官方方法按環境轉移對齊;不同呼叫也可能達到等價結果。讀取類動作還要在驗證時核對回傳資訊。

4. 局部過關而整題失敗,修正就沒用嗎?

仍可能是可學的局部資料。論文會保留已核對的修正;研究報告仍應分開當步與整題成績。

5. 三題都過關,可以說量化能力恢復嗎?

不能從本篇程式推出。它沒有量化模型、權重更新與保留測試集;固定分支結果只說明教具和驗證邏輯按預期運作。

6. 為什麼不能把三個 arm 接著跑?

因為後一組會繼承前一組的檔案或登入狀態。每組應從相同偏離前狀態重建,才有可比較的起點。

7. 可以直接拿真實帳戶練登入嗎?

這份範例只用布林 Mock。真正登入涉及 session、權限與副作用,需在可還原的測試環境另設流程,不能用物件複製當作登出或撤回。

8. 我今天做到哪裡算入門?

能用自己的話指出 missing_file 的第一個偏離、空檔與完成檔的差別,以及 hint_removed 為何沒有變好,就完成這次的學習目標。

給新手的三個重點

  • 先看狀態,再聽宣告:工具回傳、環境變化與最終內容各自留下證據。
  • 先還原起點,再比較救援:同一段歷史、同一份環境,提示才是有意義的變因。
  • 先拿掉扶手,再說學會:真正能力恢復要接到訓練後、無提示的新題驗收。

接著閱讀

左右滑動查看更多推薦

下一步:先把「空檔過關」那一步圈出來

現在就執行一次程式,打開 toy-trace.json,圈出 missing_file 的回合 1,再對照 hint 分支的 local.after 與 continuation.after。當場救援、無提示訓練、獨立驗收,把這三件事分開,你就能更準確地讀下一篇 Agent 研究。想繼續練系統思考,可到 AI 教學專區選下一題,或用 AlphaLab 課程安排完整的學習路線。

ALPHALAB 社群

有問題?來 Telegram 聊

和 Terry、編輯、其他網友一起討論這篇文章。提問、分享觀點,回覆更即時。

加入 Telegram 討論

📩 訂閱 AlphaLab 電子報

週日 Weekly 週報+週二、週四 Alpha Signal,每週最多三封。

我們不會 spam,隨時可退訂。已訂閱?管理訂閱偏好(會寄登入連結到你的信箱)

主題篩選與退訂說明

寄送日期以台灣時間為準。主題偏好會篩選每期精選,並非每篇新文章的即時通知;當期符合主題的文章不足時會略過寄送。

未勾選任何主題會收到所有主題的精選,並不會停止寄信。

取消勾選 Alpha Signal 可停止週二、週四短報,仍保留週日週報。若要停止所有電子報,請使用信件底部的「取消訂閱」。