CMU 11-768 AI Agents 第 8 講(2026-09-17)是訓練模組的第一堂,講者是 CMU LTI 的 Yueqi Song,她和 Graham Neubig 合作了 Agent Data Protocol。前面七講都在調 harness:context、工具、記憶、規劃。這一講換一個槓桿:把錄下來的 agent 軌跡訓練進模型權重。
投影片副標是「Turning recorded trajectories into weights」。全講分六段:軌跡怎麼變成 token、挑哪些軌跡、多個資料集怎麼統一格式、訓練怎麼跑、怎麼知道有效、怎麼交棒給 RL。對象是會用 LLM API、但還沒自己訓過 agent 模型的工程師;課堂上舉手做過 agent SFT 的人比講者預期得多。
- 課程頁:cmu-agents.com 課表(第 8 講有投影片與錄影)
- 本講課表沒有指定 readings,只列 references;以下引用的技術報告數字先取自投影片,再逐條對照原始報告全文,對不上的地方會另外註明
起點:一條軌跡長什麼樣
投影片用 SWE-Gym 的一條軌跡開場:agent 在 sandbox 裡拿到一個 issue 和兩個工具(bash、檔案編輯器),依序寫出想法和指令,讀回指令的輸出,最後由 repository 自己的測試判定成功與否。角色有 system、user、assistant、observation 四種,其中真正由模型寫的 assistant 訊息只有幾則。
為什麼要改權重
講者把「更新 agent」的地方分成三處,前兩處是前面幾講的主題:
| 位置 | 優點 | 缺點 |
|---|---|---|
| Context window(第 3 講) | 最忠實記錄發生過的事 | 貴、雜訊多,自己不會判斷什麼重要 |
| 外部產物(第 4 講的 skill、記憶) | 看得到、改得了、可檢索、可搬 | 要有人歸納、挑選、維護 |
| 模型權重(本講) | 推論更快、行為改變更全面 | 更新慢、不透明、綁定特定模型 |
改一次權重,之後每次呼叫都帶著新行為,prompt 裡什麼都不用加。代價是每次改都要一輪訓練,而且你打不開權重看改了什麼,也沒辦法把這個改變搬到另一個模型上。
軌跡會教模型什麼
- 怎麼寫出這個 harness 真的能執行的工具呼叫。
- 對話變長時怎麼繼續,因為它學的軌跡本來就長。
- 工具結果長什麼樣,所以它會等結果回來,而不是自己編一個。
- 以及資料裡其他所有東西:多餘的碎念、重複的指令、多種解法中剛好被錄下的那一種。
最後一點是整講的伏筆:SFT 不分好壞,資料裡有什麼就學什麼。
SFT 在整條訓練管線的位置
投影片按 token 量畫出管線,越往下 token 越少、篩選越嚴:
| 階段 | 規模(投影片舉例) |
|---|---|
| 預訓練 | 20T tokens(Nemotron 3 Ultra) |
| Mid-training | 1.43T(投影片,K2 Horizon) |
| SFT(本講) | 332B(K2 Horizon) |
| RL(9/22 起) | 沒有固定語料,靠 rollout 加 reward |
講者提醒 mid-training 的定義很分歧,有人把 SFT 也算進去;前三段都有固定語料,只有 RL 是模型自己產生資料。
數字有一處對不上:K2 Horizon model card 的 SFT 三個階段加起來確實是 332B(81B+201B+50B),但 mid-training 四個階段加起來約 1.91T(1.09T+503B+117B+201B),不是投影片的 1.43T。另外,K2 Horizon 的實際順序是 mid-training → RL → SFT,SFT 排在 RL 後面,和上表的一般示意不同。
SFT 是 RL 的冷啟動
「Cold start」指 RL 開始時的 checkpoint。以 Kimi K3 為例,後訓練分三段:SFT 建立基本的 agent 能力 → RL 在不同推理強度下練出各領域專家 → on-policy distillation 把專家合回一個模型。工具呼叫和長任務,是在任何 RL 之前的 SFT 階段學會的。
那為什麼不直接 RL?DeepSeek-R1 的故事就是答案:R1-Zero 直接在 base model 上跑 RL,回答混雜多種語言、很難讀;下一版 R1 又把 SFT 放回來,用幾千筆精選範例起步。Kimi K3 報告的原話是:SFT 階段為接下來的 RL 建立高品質的冷啟動策略。
SFT 和 RL 並排看
| SFT | RL | |
|---|---|---|
| 從哪學 | 錄下來的軌跡,誰產生的都行 | 自己的 rollout,由 reward 打分 |
| 訊號 | 每個 token 都有目標 | 每條 rollout 一個數字 |
| 需要什麼 | 資料和 GPU | 活的環境、sandbox、可檢查的 reward |
| 擅長 | 學工具呼叫、格式、長任務 | 磨利模型已經會嘗試的事 |
| 卡住的時候 | 示範是錯的、太窄、或用完了 | 起點太弱,或 reward 沒辦法檢查 |
RL 在接下來三堂(9/22、9/29、10/1),第一堂見 L9 RL 基礎。
第一個決定:哪些 token 算 loss
軌跡變成一條 token
把軌跡丟進 chat template,會得到一條字串,例如:
<|im_start|>tool
3 failed, 41 passed
<|im_end|>
<|im_start|>assistant
The test compares floats. Fix it.
<tool_call>
str_replace(...)
</tool_call>
<|im_end|>
<|…|> 這些標記各是一個特殊 token,其餘就是 agent 寫的和看到的內容。
只對 assistant token 算 cross-entropy
$$ \mathcal{L}(\theta) = -\sum_{t:,m_t=1} \log p_\theta(x_t \mid x_{<t}) $$
$m_t$ 是 mask,每個 token 一個位元。最小化它,就是在「前面所有東西都給定」的條件下,提高每個被錄下動作的機率。觀察(工具輸出)是條件,不是預測目標。
課堂上有人問:為什麼不連工具輸出一起訓練?講者說只訓 assistant 是通則,因為觀察來自環境或使用者,不是模型該產生的東西。但確實有研究訓練模型預測執行結果,例如 Meta 的 CWM,在需要理解程式執行的任務上有幫助;代價是會佔用模型容量,小模型可能在別的能力上退步。這仍是開放問題。
Mask 怎麼產生
- Render:template 把整段對話寫成一條字串,同時記下每段 assistant 文字的起訖位置。
- Tokenize:字元位置轉成 token 位置。
- Mask:assistant 範圍內的 token 標 1,其他標 0。
訓練開始時,角色資訊已經不見了,trainer 只看到字串和位元。三個常用框架的做法:
| 框架 | 設定 | mask 怎麼來 |
|---|---|---|
| TRL | assistant_only_loss=True | template 在 render 時標出 assistant 範圍(需要 {% generation %} 標記) |
| Axolotl | roles_to_train、train_on_eos | 在 render 後的字串裡找每輪的角色標頭 |
| LLaMA-Factory | train_on_prompt、mask_history | 編碼時整輪標記為訓練或忽略 |
三者做的是同一個決定,只有 TRL 把它放進 template。講者自己做 agent SFT 時常用 LLaMA-Factory。
最容易漏的:停止 token
在 <|im_start|>assistant ... <|im_end|> 裡,角色標頭不該算 loss(推論時由 harness 提供),但結尾的 <|im_end|> 必須算,因為停下來是模型自己要做的事。沒放進 mask,模型可能學不會停。
更細一層:「結束這一輪」其實是三件事:結束這則訊息、交給工具、結束整個任務。投影片說 Kimi K3 的 template 用不同 token 把三者分開。對照 Kimi K3 報告 §4.1.1,它的 XTML template 用 [end_of_msg] 當唯一的生成停止標記,assistant 訊息內再分成 think、response、tools 三個區塊,工具呼叫放在 tools 區塊裡。所以「三件事分開」是靠區塊結構加停止 token 表達,不是三個各自獨立的停止 token。
推理區塊:兩個開關分開設
- 推理內容留不留在 context:留著,它解釋了後面的動作。
- 推理內容算不算 loss:算,它就變成模型自己寫推理的風格。
Nemotron 3 Ultra 把因預算被截斷的推理留在 context,但把人為截斷那一刀從 loss 裡遮掉。
Assistant 內部的權重,以及樣本數 vs token 數
- 長推理會主導 loss,但真正要做對的是那一小段工具呼叫。BalanceSFT(Hao et al., 2025)用可學的權重(SSB loss)重新平衡推理 token 與工具呼叫 token,並對難題重新取樣(HDR)。課表連結沒有錯,只是 arXiv 頁面的標題還是舊名 FunReason,v3 的 PDF 才改名 BalanceSFT。投影片說重新平衡「在兩個基座模型上」都是多輪幫助比單輪大,但論文表 5 只有一半支持:單獨加 SSB 時,Qwen2.5-Coder-7B 單輪 +0.5、多輪 +3.4;Llama-3.2-3B 反過來,單輪 +3.2、多輪 +1.4。
- MAI-Thinking-1 用「樣本數」配資料比例,但 loss 是按 token 算的。STEM 和程式的軌跡長很多,結果幾乎吃掉所有 token。教訓:用軌跡數配好比例後,一定要回頭看它變成 token 後長什麼樣。
還沒人做的實驗
講者直說:她找不到任何公開實驗在 agent SFT 裡改 mask 並量測效果。現有仔細的 mask 實驗都是單輪指令微調(Instruction Modelling、Weighted Instruction Tuning)。Instruction Modelling 論文發現「不遮 prompt 反而有幫助」的條件是長 prompt 配短回答,或訓練資料很少;agent 軌跡剛好都相反。在 8B 規模跑這個實驗很便宜,但一直沒人跑。
第二個決定:挑哪些軌跡
軌跡從哪來
| 全部留下 | 只留通過的 | |
|---|---|---|
| 更強的模型跑 | 單純蒸餾 | SWE-Gym、Nemotron、OpenThoughts-Agent(大多數 agent SFT 在這格) |
| 模型自己跑 | MAI 用自己的推理軌跡 | expert iteration(9/22 講) |
多數資料落在右上:強模型寫的軌跡比學生好,而任務本身的測試讓「只留好的」很便宜。下面那排是模型自己產資料,反覆跑就是 expert iteration。
左下那格的「MAI」要特別說明,因為錄影的自動字幕在這裡寫成「Anthropic 用自己的推理軌跡」。投影片寫的是 MAI,MAI-Thinking-1 報告 §3.1.4 也對得上:Microsoft AI 在 RL 過程中收集 rollout,再拿去對 mid-training 後的 checkpoint 做 SFT(他們稱為 self-distillation),當作下一輪 RL 的起點。本文採投影片與報告的說法,字幕應是誤植。
蒸餾與飽和
SWE-Gym 讓 GPT-4o 和 Claude 跑任務,491 條通過 repository 測試,只用這 491 條微調 32B 的 Qwen,兩個 SWE-Bench 分割的分數都漲了十分以上。同一組任務下增加抽樣條數,準確率在算力用完時還在上升:他們不缺任務,缺的是繼續抽樣的算力。
通過測試的軌跡也會教壞模型
投影片列了幾條都通過測試的軌跡:一條大半輪都在「改一下、跑測試」迴圈;一條把 print( 留在最後的 patch 裡;一條只讀檔、從沒編輯。loss 會照抄每個動作,不管好壞,所以模型會把亂七八糟的過程和修正一起學走。Nemotron 報告的說法是,這些軌跡「直接當 SFT 資料會教出不良行為」。
過濾、換老師、換任務來源
OpenThoughts-Agent(Raoof et al., 2026)做了一百多組受控消融,這講引了四個結論:
- 最好的過濾器很粗:數輪數,丟掉少於五輪的。輪數只代表做了多少工,不代表做得好不好;四輪乾淨解掉的會被丟掉,四十輪一團亂的會被留下。但平均下來仍然有幫助。
- 最強的模型不一定是最好的老師:GPT-5.3-Codex 在 benchmark 上是五個候選中最強的,它的軌跡卻訓出最弱的學生。講者的解釋是它的思考方式或解法可能和學生差太多。排行榜會誤導你,試兩個老師、留比較好的學生,相對便宜。
- 任務來源影響最大:不同來源幫不同 benchmark(例如 SWE-smith 對 SWE-Bench Verified 幫助大、對 Terminal-Bench 小)。先在這裡花力氣,再調別的;混最好的四到八個來源,再多就沒幫助。
- 加新任務比多抽樣有用:從同一個一萬筆資料集出發,一條曲線是同樣任務多跑幾次,很快變平;另一條是加入新任務,持續上升。
課堂討論:三條軌跡留哪條?
同一個任務跑了三次:
- A:四輪就解掉的短軌跡。
- B:改錯一次、看到測試失敗、再修回來的長軌跡。
- C:試了十一次都沒改到東西,最後才解掉。
學生多選 B,理由是它教會模型恢復。講者補上 B 的風險:模型也可能學到那個錯誤的編輯,而不是恢復能力。她的結論是 A 和 B 都收,讓資料涵蓋目標 benchmark 最需要的能力。回頭看「最少五輪」過濾器,它會丟掉 A。
第三個決定:一種格式裝所有資料集
這段是講者自己的研究 Agent Data Protocol(ADP)。
- 問題:每個 agent 資料集由不同團隊、用自己的格式做。網頁資料有的存 HTML,有的存 accessibility tree。想把 Go-Browse 和 Mind2Web 一起拿來訓 OpenHands,就得寫兩個客製轉換器;N 個資料集乘上 M 個 harness,工程量爆炸。
- 做法:每個資料集只轉一次,變成有型別的 action 與 observation;網頁同時保留 HTML 和 accessibility tree,不必二選一。之後每個 harness 只寫一次「從 ADP 轉出」。論文把 13 個現有資料集統一進 ADP。
- 轉出時要做的事:OpenHands、SWE-Agent、AgentLab 的動作集合不同,同一條軌跡轉出來長得不一樣。system prompt、context 變長時怎麼處理,也在這一步決定。轉完要檢查:工具呼叫能不能 parse、每個呼叫有沒有對應的想法、對話結尾對不對。
- 多樣性 vs 專用資料:同一個 harness、同一個模型、同一套評估,只換訓練資料組合。混合資料打敗對應的單一領域資料,連在那個領域自己的 benchmark 上也是。
第四個決定:訓練怎麼跑
真實設定
| 模型 | 序列長度 | batch | learning rate |
|---|---|---|---|
| Nemotron 3 Ultra | packed,294,912 再到 515,000 | 64 | 1.5e-5 → 1e-6(第一階段;第二階段 1e-5 → 2e-6) |
| MAI 自我蒸餾 | packed 128k | 2,048 | 1.7e-5 → 5.2e-6,2% warmup |
| K2 Horizon | 512K,三個階段 | — | 最後在高品質子集上衰減 |
有學生問這是不是全參數訓練?是的,本質上和預訓練一樣的 loss,只是資料換成 chat 格式、而且只對部分 token 算 loss。資源夠的實驗室偏好全參數微調;講者自己做過 LoRA 來省資源。
MoE 的 expert routing
講者提到的前沿模型大多是 mixture of experts,但公開論文微調的多半是 dense 模型。拿很窄的 agent 資料微調 MoE,會變成少數幾個 expert 吃掉大部分 token。MAI 的解法是 SFT 階段把 routing 平衡項調得比 RL 階段重一千倍,並把 dropout 拉到 0.15。
Packing 整段對話
訓練序列長度固定,packing 把多段對話塞進同一條序列,而不是用 padding 填滿。Nemotron 的做法:每段對話放進「剩餘空間最貼合」的那個 pack;不切斷、不截斷任何對話,這是刻意避免幻覺的選擇;相同 prompt 不放進同一個 pack;最後打亂 pack 順序。
Template drift
訓練用的 template 必須和部署用的一模一樣,連空白都算。投影片的陷阱:同一條軌跡 render 兩次,一次後面接工具訊息、一次沒有,可能得到兩條不同字串,因為某些 template 會依條件改寫前一輪 assistant 的思考區塊,前綴就對不上了。這在工具迴圈裡是致命的。TRL 的 Chat Templates 文件舉的正是這個例子:Qwen3 原版 template 依 loop.last 決定要不要輸出思考區塊,接上一則工具訊息後,前一輪 assistant 的 render 結果就變了,破壞 prefix-preservation;TRL 的 qwen3_training.jinja 改成一律輸出思考區塊。HF 的〈Writing a chat template〉文件原話是:chat template 應該永遠和模型訓練時的格式一致。
去讀公開的訓練產物
K2 Horizon 的 model card 有逐階段的訓練表(步數、token 數、序列長度、每階段目的),上面那張設定表的數字就出自這裡;它也連到 Weights & Biases 上的 loss 曲線,並釋出每個階段的中間 checkpoint。Nemotron 則把後訓練 checkpoint、訓練資料和配方一起釋出。講者建議動手做 SFT 前至少讀一份。
第五個決定:怎麼知道真的有效
訓練和部署的落差
訓練時永遠接著錄下來的軌跡預測下一步。部署時沒有錄影:模型每個動作都會改變它下一步看到的東西。只要一次編輯和示範不同,它就進入一個訓練從沒見過的狀態,低 loss 在那裡什麼都不保證。這就是 DAgger 處理的問題,也是為什麼剛才說恢復能力重要。loss 曲線可以用來監控訓練,但不能取代評估。
評估清單
- 先確認能 overfit 20 筆:loss 降不下來,bug 在上游(mask、template、資料)。
- 在目標 harness 裡跑,用結果可檢查的任務評估。
- 讀公開數字要小心:有些是多個 harness 中挑最好的。
- 切分要對得上宣稱:同一任務的所有軌跡放同一邊;想說能推廣到新 repo,就按 repo 切。
- 跨 harness 的穩健性:在一個系統收的軌跡,會連那個系統的慣例一起教。只用 OpenHands 收的資料,模型可能換到別的 harness 就不行。實驗室的做法是跨多個 harness 收資料。
- 看目標領域以外的退步:訓一種能力就會動到其他能力,不管你有沒有量。你的資料組合就是控制變因,而按樣本數平衡不等於按 token 數平衡。所以也要評估你沒打算改進的東西。
最後:交棒給 RL
OpenThoughts-Agent 的表 11 給了三個結論:
- 完全沒 SFT 就上 RL,base model 幾乎不動(R1-Zero 的教訓)。
- 只做 SFT,離 SFT 加 RL 的成績還差很遠。
- 贏的配方是從一個刻意沒訓到自身最佳分數的 checkpoint 開始 RL。
投影片引用的說法是:當 SFT 模型是為 RL 而挑選時,RL 帶來的增益最大。講者的整體看法是,SFT 給 agent 最通用的能力,RL 用來練特定領域。
課堂問答
- 沒有更強的老師怎麼辦? 從自己蒸餾,或者直接靠 RL。講者認為 OpenAI、Anthropic 這類前沿實驗室更多是靠 RL 取得 agent 能力,因為 SFT 從根本上受限於老師的能力。
- 要不要監督錯誤的中間步驟? 要,因為模型需要學怎麼從錯誤中恢復;但最後結果必須正確。
- 怎麼決定資料配比? 大多靠實驗:先在小模型、小資料上試。MAI 選 56% 程式與 STEM 也是實驗出來的。另外,RL 會隱式地重新加權:reward 越高的軌跡梯度權重越大。
- 有沒有不用 teacher forcing 的模仿學習? SFT 用的是 teacher forcing,context 固定、訓練時不從模型取樣。會讓模型自己產生訓練資料的方法,留到接下來的 RL 課。
今晚能做的事
拿一條你自己 agent 的軌跡,用目標模型的 tokenizer render,然後把 mask 為 1 的 token 印出來看:
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("your-model")
out = tok.apply_chat_template(
messages, tools=tools, tokenize=True,
return_dict=True, return_assistant_tokens_mask=True,
)
ids, mask = out["input_ids"], out["assistant_masks"]
print(tok.decode([i for i, m in zip(ids, mask) if m]))
檢查三件事:工具輸出有沒有混進來、結尾的停止 token 有沒有在裡面、角色標頭有沒有被誤算。如果印出來是空的,代表這個 template 沒有 {% generation %} 標記:Transformers tokenizer 文件說 return_assistant_tokens_mask 只對用 {% generation %} 關鍵字支援它的 template 有效。TRL 的 Chat Templates 文件列出它內建訓練用 template 的模型家族(Qwen3、Llama 3、DeepSeek-V3、GPT-OSS 等),在 assistant_only_loss=True 時自動替換;其他模型就得自己補上標記,否則 assistant_only_loss 不會正常運作。接著用 20 筆資料確認能 overfit,再談其他。
延伸閱讀
- 同系列:L7 Computer Use Agents(CUA 的 SFT 資料)、L9 RL 基礎
- SFT 與 RLHF 的基礎觀念:CS336 Lecture 15:SFT 教模型模仿,RLHF 才開始直接最佳化偏好
- 下一步的 RL:CS336 Lecture 16:RLVR 與 GRPO
- 另一門課的訓練全貌:CME295 第 4 講:預訓練、SFT 與 LoRA 的成本
- 其他團隊怎麼做後訓練 RL:後訓練 RL 三條路線:Ornith、Nous Research、MiniMax
課表的 references 另列 Kimi K2 技術報告。已打開全文,本文未引用其內容;文中談 template 與後訓練流程時用的是後繼的 Kimi K3 報告。
參考資料
- 課程:CMU 11-768 AI Agents 官網與課表、第 8 講錄影、講者 Yueqi Song 個人頁
- 技術報告:Kimi K3、DeepSeek-R1、Nemotron 3 Ultra、MAI-Thinking-1、K2 Horizon model card
- 資料與方法:OpenThoughts-Agent、SWE-Gym、Agent Data Protocol、BalanceSFT(arXiv:2505.20192)、Instruction Modelling、Weighted Instruction Tuning、DAgger、CWM
- 工具:Transformers chat templating、Transformers:Writing a chat template、Transformers
apply_chat_template原始碼、TRL SFTTrainer、TRL Chat Templates({% generation %}標記、prefix-preserving、內建訓練用 template 與支援的模型家族)、Transformers tokenizer 文件(apply_chat_template的return_assistant_tokens_mask參數)、Axolotl conversation datasets、LLaMA-Factory
Loading...