你讓本機 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 跑出的模型成績,也不能單憑這一例推算成功率。

原論文第 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 課程安排完整的學習路線。





