<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hant"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://blog.aihao.tw/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.aihao.tw/" rel="alternate" type="text/html" hreflang="zh-Hant" /><updated>2026-07-18T12:44:28+00:00</updated><id>https://blog.aihao.tw/feed.xml</id><title type="html">愛好 AI 工程 Blog</title><subtitle>分享 LLM 應用開發的好內容</subtitle><author><name>ihower</name></author><entry><title type="html">如何萃取老師傅的知識做成 Agent Skill? 六條路線的方法論框架</title><link href="https://blog.aihao.tw/2026/07/06/knowledge-to-skill-framework/" rel="alternate" type="text/html" title="如何萃取老師傅的知識做成 Agent Skill? 六條路線的方法論框架" /><published>2026-07-06T00:00:00+00:00</published><updated>2026-07-06T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/07/06/knowledge-to-skill-framework</id><content type="html" xml:base="https://blog.aihao.tw/2026/07/06/knowledge-to-skill-framework/"><![CDATA[<p>組織裡最有價值的知識，往往集中在少數資深專家身上: 文件寫不全、標準作業流程涵蓋不了，人一離開，知識就跟著離開。這是知識管理領域的老問題，但現在多了一個新解法: 把專家知識萃取出來，做成 AI Agent 可以載入的 skill，讓非專家也能做出專家等級的產出。最近 GitHub 上「員工蒸餾」專案引發的討論（後面 C 路線會講），也讓更多人開始關注這件事。</p>

<p>小編把目前找得到的論文和工具整理成一個方法論框架。範圍先講清楚: <strong>知識來源是真人專家，終點是 Agent Skill</strong>。至於 agent 從自己的執行軌跡歸納 skill 的自我改進路線（SkillWeaver、Trace2Skill 那一系），不在這篇的討論範圍，這裡談的都是把「人」的知識拿出來。</p>

<p>整個框架只需要回答一個問題: <strong>專家的知識，現在存在哪裡?</strong> 答案有五種，對應六條萃取路線:</p>

<div style="margin:18px 0;border:1px solid var(--border);border-radius:10px;overflow:hidden;">
<div style="background:var(--accent);color:#fff;padding:12px 14px;font-weight:700;text-align:center;font-size:1.02em;">📍 專家的知識，現在存在哪裡?</div>
<div style="display:flex;flex-direction:column;gap:10px;padding:12px;">
<div style="display:flex;flex-wrap:wrap;gap:10px;align-items:stretch;">
<div style="flex:1 1 200px;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:6px;padding:10px 12px;font-size:0.92em;"><b>只在專家腦中</b><br /><span style="color:var(--text-secondary);">沒有任何紀錄</span></div>
<div style="flex:2 1 300px;display:flex;flex-wrap:wrap;gap:8px;">
<div style="flex:1 1 150px;display:flex;align-items:center;gap:8px;background:var(--tag-bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;font-size:0.92em;"><span style="flex:none;width:20px;height:20px;line-height:20px;text-align:center;background:var(--accent);color:#fff;border-radius:50%;font-size:0.78em;font-weight:700;">A</span><span><b>人工訪談</b> 🗣</span></div>
<div style="flex:1 1 150px;display:flex;align-items:center;gap:8px;background:var(--tag-bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;font-size:0.92em;"><span style="flex:none;width:20px;height:20px;line-height:20px;text-align:center;background:var(--accent);color:#fff;border-radius:50%;font-size:0.78em;font-weight:700;">B</span><span><b>AI 訪談</b> 🤖</span></div>
</div>
</div>
<div style="display:flex;flex-wrap:wrap;gap:10px;align-items:stretch;">
<div style="flex:1 1 200px;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:6px;padding:10px 12px;font-size:0.92em;"><b>在數位足跡裡</b><br /><span style="color:var(--text-secondary);">聊天、文件、code review</span></div>
<div style="flex:2 1 300px;display:flex;align-items:center;gap:8px;background:var(--tag-bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;font-size:0.92em;"><span style="flex:none;width:20px;height:20px;line-height:20px;text-align:center;background:var(--accent);color:#fff;border-radius:50%;font-size:0.78em;font-weight:700;">C</span><span><b>數位足跡蒸餾</b> 🗂</span></div>
</div>
<div style="display:flex;flex-wrap:wrap;gap:10px;align-items:stretch;">
<div style="flex:1 1 200px;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:6px;padding:10px 12px;font-size:0.92em;"><b>在操作過程裡</b><br /><span style="color:var(--text-secondary);">做得出來但說不出來</span></div>
<div style="flex:2 1 300px;display:flex;align-items:center;gap:8px;background:var(--tag-bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;font-size:0.92em;"><span style="flex:none;width:20px;height:20px;line-height:20px;text-align:center;background:var(--accent);color:#fff;border-radius:50%;font-size:0.78em;font-weight:700;">D</span><span><b>示範錄製</b> 🎥</span></div>
</div>
<div style="display:flex;flex-wrap:wrap;gap:10px;align-items:stretch;">
<div style="flex:1 1 200px;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:6px;padding:10px 12px;font-size:0.92em;"><b>在教材裡</b><br /><span style="color:var(--text-secondary);">教學影片、文章、手冊</span></div>
<div style="flex:2 1 300px;display:flex;align-items:center;gap:8px;background:var(--tag-bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;font-size:0.92em;"><span style="flex:none;width:20px;height:20px;line-height:20px;text-align:center;background:var(--accent);color:#fff;border-radius:50%;font-size:0.78em;font-weight:700;">E</span><span><b>教材蒸餾</b> 📚</span></div>
</div>
<div style="display:flex;flex-wrap:wrap;gap:10px;align-items:stretch;">
<div style="flex:1 1 200px;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:6px;padding:10px 12px;font-size:0.92em;"><b>在未來的協作裡</b><br /><span style="color:var(--text-secondary);">還沒發生的判斷與糾正</span></div>
<div style="flex:2 1 300px;display:flex;align-items:center;gap:8px;background:var(--tag-bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;font-size:0.92em;"><span style="flex:none;width:20px;height:20px;line-height:20px;text-align:center;background:var(--accent);color:#fff;border-radius:50%;font-size:0.78em;font-weight:700;">F</span><span><b>互動結晶</b> 🔄</span></div>
</div>
</div>
</div>

<p>在逐條介紹之前，先講一下為什麼終點選 skill。Anthropic 官方在 <a href="https://claude.com/blog/building-agents-with-skills-equipping-agents-for-specialized-work">Building Agents with Skills</a> 的定位是: skill 用檔案打包領域專業知識（工作流程、最佳實務、腳本），靠漸進式載入 (progressive disclosure) 控制上下文用量: 平常只載入約 50 token 的描述資訊，需要時才讀取 SKILL.md 主文件，深度參考資料則放得更後面。skill 還能內含腳本當工具，可執行的部分完全不用進上下文。這個「<code class="language-plaintext highlighter-rouge">scripts/</code>（可執行規則）+ SKILL.md（判斷知識）+ <code class="language-plaintext highlighter-rouge">references/</code>（深度參考）」的結構，正好給知識萃取的產出一個標準容器。而且 Anthropic 觀察到，動手寫 skill 的人已經不限於工程師，產品經理、分析師、領域專家都在自己動手。以下六條路線的產出，最後都會落到這個容器裡。</p>

<h2 id="-a-人工訪談-品質最高也最貴">🗣 A. 人工訪談: 品質最高，也最貴</h2>

<p><strong>做法</strong>: 半結構化訪談，搭配情境式追問。<a href="https://arxiv.org/abs/2601.15153">Siemens 的論文</a>是標準示範: 他們訪談兩位內部專家（一位模擬分析軟體專家、一位視覺化設計專家），各 60 到 90 分鐘、分開進行避免互相影響。訪談大綱只有三個主題: (1) 目前的工作流程和痛點、(2) 專家建立視覺化時的決策過程、(3) 實務上使用的具體規則和經驗法則。提問從開放式問題起頭，像是「可以帶我走一遍你為模擬資料建立視覺化的典型流程嗎?」「最常用、最有用的圖表是哪些?」，再追問具體規則和判斷標準。另外搭配「情境式討論」: 拿範例任務給專家看，讓他在回應中示範怎麼判斷。這是關鍵技巧，因為隱性知識直接問是問不出來的，要靠具體情境讓專家把判斷過程講出來。</p>

<p>有一點蠻意外的: 專家在訪談中就直接給出明確規則，例如「分析開始前，必須先確認最佳化目標是否收斂」，不需要研究者事後再做詮釋分析，記下來就能實作。萃取出來的知識有兩種型態，去處也不同:</p>

<div style="display:flex;flex-wrap:wrap;gap:12px;margin:16px 0;">
<div style="flex:1 1 300px;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px;">
<div style="font-weight:600;color:var(--accent);margin-bottom:6px;">明確程序規則 → 寫成程式碼</div>
<div style="font-size:0.92em;color:var(--text-secondary);">條件明確的判斷邏輯，例如「分析前先檢查目標是否收斂」。直接寫成 Python 函式，執行後產生分析報告，自動附加到 system prompt。</div>
</div>
<div style="flex:1 1 300px;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px;">
<div style="font-weight:600;color:#1a7f37;margin-bottom:6px;">隱性設計原則 → 寫進 Prompt</div>
<div style="font-size:0.92em;color:var(--text-secondary);">依情境判斷的知識，例如「未收斂的變數用虛線、已收斂用實線」「一張圖最多放兩個變數」。寫進 system prompt 或 RAG，讓模型推理時套用。</div>
</div>
</div>

<p>論文特別提到，單獨用任何一種都不夠: 只有程式碼會缺乏分析洞察，只靠模型又會產出不符合領域要求的東西，兩者搭配才有效。這個「<strong>可以寫成程式碼的就寫成程式碼，不然就用 prompt</strong>」的分工原則，小編覺得是全篇最值得帶走的一句話。成果也很具體: 非專家用簡單的 prompt 產出的圖表，品質比未加持的版本提升 206%（12 位評審、5 個場景）。</p>

<p><img src="/assets/images/expert-knowledge-extraction.png" alt="" /></p>

<p>而且萃取不是一次就能完成的。初版結果發現生成的圖表資訊量不足、有視覺缺陷，他們才回頭向視覺化專家做針對性補訪，聚焦在最常用的三種圖表類型。真實流程是「訪談 → 實作 → 測試發現不夠 → 針對性補訪」，這跟 AI 評估圈講的錯誤分析 (error analysis) 是同一個邏輯: 先做出第一版，讓失敗案例告訴你接下來要補哪一塊知識。</p>

<p>論文還有兩個實務註腳。第一，為什麼只用兩位專家? 他們的理由是: 需要涵蓋的兩個專業領域很少重疊、目標是系統性知識萃取而非統計推論、驗證靠客觀技術指標，而且知識工程文獻本來就認為 1 到 3 位專家足以建構一致的知識庫。第二，第一作者本身是 Siemens 員工，能接觸內部資料和內部討論: 很多知識不是那 90 分鐘訪談問出來的，是長期待在組織裡累積的。</p>

<p><strong>訪談技巧的系譜與工具箱</strong>: 這套技術的源頭是 1980 年代專家系統時代的知識工程。<a href="https://doi.org/10.1109/21.31053">關鍵決策法 (Critical Decision Method)</a> 原本是 Klein 在 1989 年用來訪談消防指揮官的，因為他們的判斷依賴講不出來的知覺線索，只能靠「回溯真實事件加上探測式問題」引出來。<a href="https://doi.org/10.1518/001872098779480442">Hoffman 等人 1998 年的方法論回顧</a>和<a href="https://doi.org/10.1177/10944281241271216">認知任務分析 (Cognitive Task Analysis) 的現代整理</a>都值得參考。當年萃取出來的規則放進規則引擎，現在放進 skill，訪談技術本身沒變。<a href="https://agentpatterns.ai/workflows/encoding-tacit-knowledge/">AgentPatterns 的整理</a>把這些技巧翻新成 agent 時代的版本，核心觀察是: 專家「說自己怎麼做」和「實際怎麼做」中間有落差，直接問規則只能拿到前者。三個實用技巧:</p>

<ul>
  <li><strong>困難案例訪談</strong>: 請專家走一遍他親身遇過的困難案例（不是假設情境），針對每個案例問四個問題: 你最先注意到什麼訊號? 當時預期接下來會發生什麼? 有哪些互相衝突的優先順序? 你立刻想到哪些做法? 這四問就出自關鍵決策法，專門用來取得專家自己意識不到的模式判斷。</li>
  <li><strong>失敗案例訪談</strong>: 拿 agent 的實際失敗輸出給專家看，請他說明「正確的輸出長什麼樣、為什麼」。解釋的過程，就會透露他心裡的隱性標準。</li>
  <li><strong>範例標註</strong>: 給專家一批好壞混雜的輸出，請他逐一標註，標註理由就是評估準則，這正是「用 LLM 當評審 (LLM-as-judge)」機制裡人類要負責的那一半。<a href="https://hamel.dev/blog/posts/llm-judge/">Hamel Husain 的建議</a>是找一位內部領域專家當品質標準的最終決策者就好，外部標註人員缺乏組織脈絡，反而製造更多分歧。</li>
</ul>

<p>還有一個值得認識的變形: <a href="https://arxiv.org/abs/2605.25549">BC Protocol</a> 讓「領域專家 + 知識工程師」兩人配對對話。理由是「專家盲點」: 專家獨自寫文件時，會跳過他認為理所當然的推理步驟，必須靠一個「校準過的外行人」在對話中把這些步驟逼出來。實驗顯示，對話產出的推理過程在自然度上大幅領先專家獨寫（評分 4.80 比 1.30）。這篇的產出目標是微調 (fine-tuning) 用的推理鏈資料而不是 skill，但「雙人對話外化隱性判斷」的技術完全可以搬過來用。</p>

<ul>
  <li><strong>優點</strong>: 品質最高、能拿到任何紀錄裡都沒有的「為什麼」、可以即時追問和驗證理解。</li>
  <li><strong>缺點</strong>: 最貴、依賴訪談技巧、專家時間稀缺，而且一次訪談問不完（Siemens 也是靠失敗案例回頭補訪）。</li>
  <li><strong>適合</strong>: 知識沒有數位足跡的老師傅（產線、實驗室、現場判斷）、高風險高價值領域、專家即將離職退休的搶救場景。</li>
</ul>

<h2 id="-b-ai-訪談-用規模換深度">🤖 B. AI 訪談: 用規模換深度</h2>

<p><strong>做法</strong>: 讓 AI 當訪談者。<a href="https://arxiv.org/abs/2605.23985">Genentech 的 Protocol Intelligence Co-pilot</a> 是目前最完整的案例: AI 訪談 agent 套用一組結構化的「提問視角」訪談實驗室科學家，產出附專家信心分數的知識圖譜。他們要捕捉的是「自動化掩蓋的沉默失敗」這類只存在專家判斷裡的知識: 儀器紀錄回報執行成功，但實驗的科學有效性其實已經出問題。這種知識在實驗協定文件、感測器數據、既有的知識本體裡都找不到，只能從專家口中問出來。學術圈也在把訪談本身形式化，例如 <a href="https://doi.org/10.48550/arxiv.2602.21136">SparkMe</a> 把「照大綱問」和「追新線索」之間的取捨寫成效用函數，用來指導 AI 訪談員; <a href="https://link.springer.com/article/10.1007/s12599-025-00976-w">Springer BISE 的研究</a>則讓對話 agent 模擬訪談流程，專家回答完直接產出 BPMN 流程圖。</p>

<p>Anthropic 官方的 <a href="https://claude.com/blog/building-agents-with-skills-equipping-agents-for-specialized-work">skill-creator</a> 其實也算這條路線的輕量版: 它用互動方式引導領域專家（不需要是工程師）在 30 分鐘內做出第一個 skill，本質上就是「AI 訪談你，然後幫你寫成 SKILL.md」。</p>

<ul>
  <li><strong>優點</strong>: 便宜、可重複多次、可以同時規模化到很多位專家、專家可以自助。</li>
  <li><strong>缺點</strong>: 追問深度不如老練的人類訪談者。Genentech 的數據很直白: 多輪對話式追問的訪談逐字稿，後續萃取結果高度一致; 單次問答式的一致性就差很多（失敗模式辨識的 F1 分數從 1.0 掉到 0.43）。訪談品質決定萃取品質，AI 訪談員目前還在追趕。</li>
  <li><strong>適合</strong>: 專家人數多而訪談人力稀缺的組織、想讓領域專家自己動手把知識寫成 skill 的場景。</li>
</ul>

<h2 id="-c-數位足跡蒸餾-不佔專家時間但雜訊多">🗂 C. 數位足跡蒸餾: 不佔專家時間，但雜訊多</h2>

<p><strong>做法</strong>: 不問人，直接處理專家留下的數位足跡。這條就是最近討論度很高的「員工蒸餾」: 今年 3 月底，上海 AI Lab 工程師 Tianyi Zhou 開源了 <a href="https://github.com/titanwings/colleague-skill">colleague-skill</a>（同事.skill）專案，兩週內拿到 13k GitHub 星星，口號寫著「將冰冷的離別化為溫暖的 Skill，歡迎加入賽博永生」，後來團隊把方法寫成論文 <a href="https://arxiv.org/abs/2605.31264">COLLEAGUE.SKILL</a>。</p>

<p>它的流程是目前文件化最完整的: 收集器和解析器先把聊天記錄、工作文件、電子郵件、會議截圖正規化成本地的知識目錄 → 分析器從中萃取「持久能力、思維模型、互動風格」的證據 → 建構器渲染成結構化的 Markdown → 寫入器打包成版本化的套件。產出分成兩軌: <code class="language-plaintext highlighter-rouge">work.md</code> 放工作方法、審查標準、決策準則，<code class="language-plaintext highlighter-rouge">persona.md</code> 放溝通風格和互動約束，可以安裝到 Claude Code 等 agent 環境。專案文件建議優先收集「主動寫的長文」和「決策類回覆」，因為這兩類素材最能反映一個人怎麼判斷。</p>

<p>它的<strong>修正循環</strong>設計特別值得抄: 使用者用自然語言回饋（「他不會這樣說」「這種情況他會反對」），系統轉成對應段落的修正補丁或修正紀錄，版本遞增、可以回滾。等於承認第一版蒸餾一定不準，把「修」設計成一級操作。同路線的還有 <a href="https://github.com/bigmoon-dev/dianoia-ai">Dianoia</a>，從公開產出物（設計文件、code review）蒸餾出「專家怎麼想」: 怎麼定義問題、怎麼判斷品質、怎麼取捨。</p>

<p>實際案例也出現了: <a href="https://www.citynewsservice.cn/articles/shanghaidaily/news/the-employee-quit-but-her-ai-clone-didnt-inside-chinas-colleague-skill-craze-yn6ow4dn">Shanghai Daily 報導</a>，山東一家遊戲公司的人資離職後，公司沒有補人，而是用她的聊天記錄和工作文件建了一個數位分身，接手例行詢問和文件工作，同事的評語是「有點笨，只能處理簡單指令」。中國社群也隨之出現「反蒸餾」工具，教你在離職前避免自己被做成 skill，勞動價值歸屬和隱私的爭議跟著浮上檯面（<a href="https://www.ctee.com.tw/news/20260409701379-430704">工商時報的整理</a>）。</p>

<ul>
  <li><strong>優點</strong>: 完全不佔專家時間、人已經離職也能事後做、拿到的是「實際怎麼做」而不是「自己說怎麼做」。</li>
  <li><strong>缺點</strong>: 前提是要有數位足跡，而且數位足跡雜訊多。COLLEAGUE.SKILL 論文自己明確承認只做「產物層級」的主張，不宣稱能還原本人行為。另外還有當事人同意、隱私、勞動價值歸屬的爭議。</li>
  <li><strong>適合</strong>: 知識工作者的離職交接、有大量文字產出的角色。反過來說，工作不留數位足跡的老師傅，這條路線直接不適用。</li>
</ul>

<h2 id="-d-示範錄製-捕捉做得出來但說不出來的操作">🎥 D. 示範錄製: 捕捉「做得出來但說不出來」的操作</h2>

<p><strong>做法</strong>: 請專家把工作做一遍，錄下來，讓模型從錄影中萃取步驟化的操作知識。這條路線目前的研究集中在圖形介面操作: <a href="https://arxiv.org/abs/2606.12817">Teach-and-Repeat</a> 從手機螢幕示範中萃取「動作類型、目標元件、輸入內容、執行順序」這樣的操作知識; <a href="https://arxiv.org/abs/2509.07098">Instruction Agent</a> 從單次示範抽出逐步指令，然後嚴格照著執行; <a href="https://dl.acm.org/doi/10.1145/3772318.3790294">InvisibleMentor (CHI 2026)</a> 則反過來，從螢幕錄影中偵測低效率的操作，推薦更好的工作流程。</p>

<ul>
  <li><strong>優點</strong>: 專家的成本最低（做一遍就好，不用講也不用寫）、能捕捉時間順序和畫面狀態這種文字寫不出來的資訊。</li>
  <li><strong>缺點</strong>: 只拿得到「怎麼操作」，拿不到「為什麼這樣做」和「什麼情況要改用別的做法」，這些判斷還是得靠訪談補。而且目前技術集中在螢幕操作，實體世界的示範要另外解決感測問題。</li>
  <li><strong>適合</strong>: 軟體操作型的專家知識（複雜工具鏈的老手、內部系統的熟練操作者）。當成訪談的前置素材也很好用: 先錄示範，再拿著錄影做情境式追問。</li>
</ul>

<h2 id="-e-教材蒸餾-專家已經整理過一次別浪費">📚 E. 教材蒸餾: 專家已經整理過一次，別浪費</h2>

<p><strong>做法</strong>: 專家為了教人而做的教材（教學影片、文章、手冊、範例專案），本身就是半結構化的知識，直接拿來蒸餾。Microsoft 的 <a href="https://arxiv.org/abs/2606.29538">Resource2Skill</a> 把教學影片、程式庫、文章蒸餾成可執行的 skill，組成階層式的多模態 Skill Wiki，每個條目包含結構化文字、程式碼、視覺範例和出處。設計理由是三種素材互補: 影片保留操作的時間順序和視覺效果、程式碼保留可執行的工具使用模式、文章提供概念基礎。在七個創作領域的實驗中，平均提升 11.9 個百分點。</p>

<p>社群工具 <a href="https://github.com/kylezantos/skill-distillery">skill-distillery</a> 也屬於這條路線，它有一句話講到重點: 把 5000 字文章的段落重排成 SKILL.md 格式，不算做成 skill。真正的萃取是由下而上: 先抽出讓方法有效的原則、組織這個領域的思維模型、做事時需要的決策準則，然後才設計 skill 的架構。</p>

<ul>
  <li><strong>優點</strong>: 素材現成、專家已經做過一輪結構化、不需要接觸專家本人。</li>
  <li><strong>缺點</strong>: 教材是「教人版」不是「實戰版」，通常缺少失敗處理、邊界案例和真實世界的髒細節，而這些恰恰是老師傅最值錢的部分。教材也會過時。</li>
  <li><strong>適合</strong>: 有成熟教學資源的領域、引入外部專家知識（買得到課程但請不到人）、給訪談路線當基礎，讓訪談時間留給教材沒寫的部分。</li>
</ul>

<h2 id="-f-互動結晶-不做前期萃取邊協作邊沉澱">🔄 F. 互動結晶: 不做前期萃取，邊協作邊沉澱</h2>

<p><strong>做法</strong>: 前面五條都把萃取當成一次性的前期工程，<a href="https://arxiv.org/abs/2603.10808">Nurture-First Agent Development</a> 反對這個前提: 專家知識是隱性的、個人的、持續演化的，一次性萃取注定會漏。它主張 agent 從最小配置開始，在與專家的日常協作對話中累積知識，再靠「知識結晶循環」定期把對話中的碎片判斷，固化成結構化的知識資產。</p>

<p>注意這跟「agent 看自己的執行軌跡自我改進」不一樣: 結晶的素材是專家在協作中給出的判斷、偏好和糾正，知識來源仍然是人。老實說，這就是 Claude Code 重度使用者每天在做的事: 用的過程中發現 agent 不對，糾正它，然後把糾正沉澱進 CLAUDE.md 或 skill。這篇論文等於把這個日常實務形式化了。</p>

<ul>
  <li><strong>優點</strong>: 沒有前期萃取成本、知識跟著真實使用演化、專家提供素材的動機自然（他本來就要把工作做完）。</li>
  <li><strong>缺點</strong>: 冷啟動期 agent 表現差、依賴專家持續使用和糾正的意願、結晶需要紀律（不定期整理，就只是一堆散落的對話）。</li>
  <li><strong>適合</strong>: 專家本人就是 agent 使用者的場景、長期一對一協作的 agent。不適合「專家即將離開、需要搶時間」的場景。</li>
</ul>

<h2 id="六條路線的共通原則">六條路線的共通原則</h2>

<p>把六條路線攤開看，不管走哪一條，有四個原則是共通的。</p>

<p><strong>1. 要萃取的東西是一樣的</strong>: 原則、思維模型、決策準則、可執行步驟。所有認真的做法都反對「格式重排就當萃取完成」，COLLEAGUE.SKILL 和 skill-distillery 在這點上講得最明白。</p>

<p><strong>2. 第一版永遠是草稿</strong>: Siemens 靠失敗案例回頭補訪、COLLEAGUE.SKILL 把修正和回滾設計成一級操作、Nurture-First 乾脆把迭代當成方法本身。沒有任何一條路線宣稱一次到位，差別只在迭代的觸發方式: 評估失敗、使用者糾正，或是定期結晶。</p>

<p><strong>3. 知識放到哪裡，分法是固定的</strong>: 能寫成程式碼的規則放 <code class="language-plaintext highlighter-rouge">scripts/</code>、判斷式知識寫進 SKILL.md、深度參考放 <code class="language-plaintext highlighter-rouge">references/</code>、品質標準寫成 eval。AgentPatterns 把對應關係整理得很清楚:</p>

<table>
  <thead>
    <tr>
      <th>萃取產出</th>
      <th>編碼成什麼</th>
      <th>適用時機</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>明確規則（「做 Y 之前一定要檢查 X」）</td>
      <td>硬性約束或 <code class="language-plaintext highlighter-rouge">scripts/</code> 程式碼</td>
      <td>不可妥協、機器可檢查</td>
    </tr>
    <tr>
      <td>情境判斷（「情境 Z 時偏好 A 不選 B」）</td>
      <td>SKILL.md 裡附說明的範例</td>
      <td>依賴脈絡、寫不成規則</td>
    </tr>
    <tr>
      <td>品質標準（「這個輸出不好，因為…」）</td>
      <td>eval 測試加上評分準則</td>
      <td>用來校準自動評估</td>
    </tr>
  </tbody>
</table>

<p>小編覺得第三列最容易被忽略: 有些專家知識最適合的存放形式不是 prompt，而是 eval。太依賴當下脈絡的判斷，硬寫成靜態規則，平均來看是對的，但在最要緊的個案上反而出錯，不如寫成 eval 去測試行為。</p>

<p><strong>4. 萃取完，還要壓縮</strong>: 訪談逐字稿、蒸餾出來的草稿，就算整理成文件通常還是太長。直接塞進 skill 會佔掉大量上下文，效果反而變差。壓縮是獨立的一道工序，具體做法值得單獨講，見下一節。</p>

<h2 id="壓縮的做法-不是摘要而是重組">壓縮的做法: 不是摘要，而是重組</h2>

<p>壓縮這個主題，<a href="https://arxiv.org/abs/2603.14805">Knowledge Activation</a> 這篇講得最完整。作者是 Yahoo 的工程師，框架實際部署在 Yahoo 內部，論文還附了 67 位工程師的使用調查，不是純理論。它把知識進入 skill 的過程拆成三個階段: 「編碼」(codification) 把散落在專家腦中、操作手冊、事故報告裡的知識轉成明確結構; 「壓縮」(compression) 把它重組成精簡的「原子知識單元」(AKU); 「注入」(injection) 則在執行時只把當下需要的單元載入上下文。</p>

<p>其中壓縮階段是精華。論文強調<strong>壓縮不是摘要，而是有原則的重組</strong>，關鍵做法有四個:</p>

<ul>
  <li><strong>刪掉修辭性內容</strong>: 鋪陳背景的段落、截圖、對其他文件的交叉引用，這些是寫給人看的，對 agent 只是負擔。留下來的是: 意圖、步驟、該用的工具、限制條件。</li>
  <li><strong>可執行的內容放最前面</strong>: 描述資訊內嵌在條目裡，並且順著模型注意力的特性安排資訊位置，重要的東西不要埋在中段。</li>
  <li><strong>一個單元只做一件事</strong>: 每個知識單元只涵蓋一件連貫的動作，跨主題的內容不塞進同一份文件，而是宣告成「接下來去哪」的連結，讓 agent 執行時自己走訪相關單元。這其實就是 skill 漸進式載入的精神，放大到整個知識庫來做。</li>
  <li><strong>能驗證的規範寫成程式</strong>: 組織規範不用散文描述，直接寫成可執行的驗證腳本 (validator)，讓 agent 的產出能被自動檢查。又一次呼應「能寫成程式碼的就寫成程式碼」。</li>
</ul>

<p>效果用論文自己的例子來看: 一份 2,000 token 的敘事式部署手冊，重組之後只剩約 300 token，任務完成能力不變，知識密度提升 6 到 7 倍。論文還引用「上下文品質劣化」(context rot) 的研究提醒: 塞進上下文的冗餘內容不只是浪費，還會實際拉低 agent 的推理品質。所以壓縮不是省成本的最佳化，是正確性的必要條件。</p>

<h2 id="怎麼選-一張對照表">怎麼選: 一張對照表</h2>

<table>
  <thead>
    <tr>
      <th>路線</th>
      <th>前提</th>
      <th>專家成本</th>
      <th>拿得到</th>
      <th>拿不到</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>A. 人工訪談</td>
      <td>專家在、有好的訪談者</td>
      <td>高（每場 60-90 分鐘）</td>
      <td>為什麼、判斷邊界</td>
      <td>專家意識不到的習慣</td>
    </tr>
    <tr>
      <td>B. AI 訪談</td>
      <td>專家願意配合</td>
      <td>中</td>
      <td>結構化規則、可規模化</td>
      <td>深度追問的細節</td>
    </tr>
    <tr>
      <td>C. 數位足跡蒸餾</td>
      <td>有數位足跡</td>
      <td>零</td>
      <td>實際行為模式</td>
      <td>數位足跡外的判斷、為什麼</td>
    </tr>
    <tr>
      <td>D. 示範錄製</td>
      <td>工作在螢幕上進行</td>
      <td>低（做一遍）</td>
      <td>操作步驟、順序</td>
      <td>為什麼、替代方案</td>
    </tr>
    <tr>
      <td>E. 教材蒸餾</td>
      <td>有現成教材</td>
      <td>零</td>
      <td>結構化的方法</td>
      <td>失敗處理、實戰細節</td>
    </tr>
    <tr>
      <td>F. 互動結晶</td>
      <td>專家持續使用 agent</td>
      <td>低（分攤在日常）</td>
      <td>演化中的真實判斷</td>
      <td>冷啟動期的能力</td>
    </tr>
  </tbody>
</table>

<p>實務上的組合大概是: 有數位足跡先蒸餾 (C)、有教材先打底 (E)、有螢幕操作先錄示範 (D)，這三條便宜的先做，產出第一版草稿。然後把最貴的專家訪談時間 (A/B) 留給草稿裡的缺口: 為什麼、邊界條件、失敗處理。上線之後切換到互動結晶 (F) 持續演化，用 eval 的失敗案例決定下一輪要補訪什麼。</p>

<p>換句話說，六條路線不是互斥的選項，而是同一個流程的不同階段: 便宜的路線負責涵蓋面，昂貴的路線負責深度，持續的路線負責不過時。真正的關鍵決策只有開頭那一個問題: <strong>你要萃取的那位專家，他的知識現在存在哪裡?</strong> 而起點可以很輕: Siemens 就是兩位專家、各 90 分鐘、開放式問題加情境追問，先做出第一版，讓失敗案例告訴你接下來要補什麼。老師傅的知識不會自己變成文件，但把它變成組織資產的成本，已經比以前低非常多了。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Skill" /><category term="Knowledge Base" /><summary type="html"><![CDATA[組織裡最有價值的知識，往往集中在少數資深專家身上: 文件寫不全、標準作業流程涵蓋不了，人一離開，知識就跟著離開。這是知識管理領域的老問題，但現在多了一個新解法: 把專家知識萃取出來，做成 AI Agent 可以載入的 skill，讓非專家也能做出專家等級的產出。最近 GitHub 上「員工蒸餾」專案引發的討論（後面 C 路線會講），也讓更多人開始關注這件事。]]></summary></entry><entry><title type="html">為什麼 context window 會卡在 1M 很多年? SemiAnalysis 談 40 年一遇的記憶體短缺</title><link href="https://blog.aihao.tw/2026/07/06/semianalysis-hbm-context-window/" rel="alternate" type="text/html" title="為什麼 context window 會卡在 1M 很多年? SemiAnalysis 談 40 年一遇的記憶體短缺" /><published>2026-07-06T00:00:00+00:00</published><updated>2026-07-06T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/07/06/semianalysis-hbm-context-window</id><content type="html" xml:base="https://blog.aihao.tw/2026/07/06/semianalysis-hbm-context-window/"><![CDATA[<p>Latent Space 在 2026/2/24 上了一集 <a href="https://www.latent.space/p/valuemule">Claude Code for Finance + The Global Memory Shortage</a> (<a href="https://www.youtube.com/watch?v=x9rWFiIubmc">YouTube 完整版</a>、<a href="https://x.com/latentspacepod/status/2026420225562804288">X 公告</a>)，來賓是 SemiAnalysis 的 Doug O’Laughlin。上半場聊他怎麼把 Claude Code 當成金融研究的主力工具，那篇「GitHub 上 4% 的公開 commits 出自 Claude Code」的 <a href="https://newsletter.semianalysis.com/p/claude-code-is-the-inflection-point">Claude Code is the Inflection Point</a> 就是 SemiAnalysis 發的，這部分本文不展開。小編覺得這集真正有意思的是下半場的全球記憶體短缺，因為它帶出一個跟每位 AI 工程師都有關的結論: LLM 的 context window 短期內不會再大幅成長，卡住它的不是演算法，是 HBM 記憶體。這篇就專心講這個，以及它為什麼讓 context engineering 變得更重要。</p>

<p>Doug 早年是匿名財經帳號「ValueMule」，2018 年因為研究 ASML 轉進半導體，後來創辦 Fabricated Knowledge，再併入 Dylan Patel 的 SemiAnalysis，是少數同時懂晶片供應鏈和 AI 應用的分析師。</p>

<h2 id="40-年一遇的記憶體短缺有四個成因">40 年一遇的記憶體短缺，有四個成因</h2>

<p>下半場對應 SemiAnalysis 二月初的 <a href="https://newsletter.semianalysis.com/p/memory-mania-how-a-once-in-four-decades">Memory Mania</a> 這篇。他們從 2024 年底就開始提醒記憶體要出事，而這次短缺是 40 年一遇的等級，因為四件事同時發生:</p>

<p><strong>1. DRAM 微縮停滯，成本不再自動下降</strong></p>

<p>過去五十年，記憶體有一個大家習以為常的規律: 製程每進步一代，同一片晶圓就能切出更多位元，每 GB 的成本就自動下降。所以就算需求大增，只要等技術往前走，價格長期還是一路往下。這個規律在最近十年失效了: 全盛時期 DRAM 密度每十年成長約 100 倍，過去十年只剩約 2 倍，技術進步帶來的降價力量幾乎歸零。下面這張圖一看就懂:</p>

<p><img src="/assets/images/semianalysis-dram-cost.jpg" alt="" /></p>

<p><em>DRAM 每 GB 成本，1973 到 2024 年，對數座標。跌了五十年的曲線，最近十年明顯放平。來源: <a href="https://newsletter.semianalysis.com/p/memory-mania-how-a-once-in-four-decades">SemiAnalysis Memory Mania</a></em></p>

<p>縮不下去的原因，簡單說是 DRAM 的記憶單元 (cell) 已經小到接近物理極限: 每個記憶單元靠一顆微小電容存電荷來記住 0 或 1，縮到現在，存的電荷只剩幾萬顆電子 (Memory Mania 文章的比較: 一粒灰塵帶的靜電，大約是它的一萬倍)，訊號微弱到再縮就讀不到了。</p>

<p>所以這次漲價跟以往最不一樣的地方是: 沒有「等製程進步就會降價」這條路可以指望了。DRAM 價格從此純看供需，而供需正好在最糟的時刻失衡。</p>

<p><strong>2. 上一輪不景氣是史上最慘，沒有人擴產</strong></p>

<p>記憶體是漲跌循環非常劇烈的產業: 缺貨時大賺，供過於求時大賠。Doug 說上一輪下跌要回到 1996 年才找得到可比的慘況: 記憶體廠自由現金流轉負、資本支出全停，無塵室和設備這類要提前兩三年準備的投資通通沒做。90 年代中期全球有 20 多家 DRAM 廠，一輪一輪淘汰下來，現在只剩 3 到 4 家有實質產能的供應商，誰都不想在低點擴產。</p>

<p><strong>3. HBM 的交換比: 每做 1 個位元，就少掉 3~4 個位元的 DRAM</strong></p>

<p>HBM 就是 AI 加速器旁邊那幾疊高頻寬記憶體，做法是把多層 DRAM 晶粒垂直堆疊封裝起來。它的晶粒比較大、堆疊的每一步都有良率損耗，所以同樣的晶圓產能拿去做 HBM，產出的位元數只有一般 DRAM 的幾分之一。Doug 說:</p>

<blockquote>
  <p>HBM 的每一個位元，本質上都是 DRAM 產能的 4 倍乘數。</p>
</blockquote>

<p>AI 加速器的需求全部集中在 HBM，等於用 3:1 到 4:1 的比率，從本來就沒擴產的 DRAM 產能裡挪走。Doug 自己的比喻: 有人發明了一種更高級的航空燃油，唯一的製造方法是把你手上的普通燃油大量濃縮提煉，「這種情況下只要出現任何需求，就是立刻短缺」。</p>

<p><strong>4. KV cache 卸載: 連一般的伺服器記憶體也被 AI 買走</strong></p>

<p>記憶體市場大致分三層: 最高階是 GPU 上的 HBM，中間是伺服器用的一般 DRAM (DDR5)，再往下是手機和 PC 用的消費性記憶體。前面講的是 AI 把最高階的 HBM 搶光，但需求沒有停在那裡: 推論系統為了節省昂貴的 HBM 空間，會把暫時用不到的 KV cache 先搬到伺服器的一般 DRAM 存放 (稱為卸載，offload)，要用時再搬回來。於是中間這層的 DDR5 也被 AI 大量買走，Doug 的說法是「中間這層也蒸發了」。推理模型和 agent 工作流把 context 越用越長，KV cache 就越大，正是這波需求的直接來源，後面講 context window 時會再展開。</p>

<p>消費端也躲不掉。節目上 Doug 直接建議 swyx「現在就去買 iPhone」，後來看是對的: Dylan Patel 三月在 <a href="https://www.dwarkesh.com/p/dylan-patel">Dwarkesh Podcast</a> 給的數字是 DRAM 價格已經漲了三倍，iPhone 估計會因此貴 250 美元，全球智慧手機出貨量可能從 11 億支掉到 5、6 億支。Doug 說這是史上第一次，記憶體貴到變成「要選擇誰能拿到」: 低階手機、遊戲顯卡會有一段時間直接從市場上消失。連已經被判定失敗的記憶體池技術 CXL (SemiAnalysis 2023 年就寫過 <a href="https://newsletter.semianalysis.com/p/cxl-is-dead-in-the-ai-era">CXL Is Dead In The AI Era</a>) 都要復活了: 把每一條找得到的 DDR4 舊記憶體接上機架共享使用，短缺嚴重到連舊料都得拿出來用。</p>

<h2 id="從-2-月到-7-月-方向沒變數字更陡">從 2 月到 7 月: 方向沒變，數字更陡</h2>

<p>訪談是 2 月底錄的，Doug 當時的預測是 DRAM 價格「可能再漲 100%」。五個月過去，SemiAnalysis 後續發表的數字只有更陡: <a href="https://x.com/SemiAnalysis_/status/2039870546582630470">四月的估算</a>是 DRAM 價格 2026 全年會再漲超過一倍，HBM 供不應求會持續到 2027，記憶體佔大型雲端業者 (hyperscaler) 資本支出的比例，也從前兩年的約 8% 升到 2026 年的 30%。<a href="https://247wallst.com/investing/2026/06/29/tech-experts-warn-memory-shortage-crisis-wont-ease-until-2028-and-ram-makers-have-no-incentive-to-fix-it/">美光 (Micron) 六月底的財報</a>是最直接的佐證: 毛利率 84.6%，執行長還坦言部分關鍵客戶的需求只能滿足 50% 到 2/3，完全是賣方市場。</p>

<p>對 AI 工程師來說，市場細節可以略過，記住兩個訊號就好。一是短缺已經影響晶片設計本身: 六月 Dylan Patel <a href="https://x.com/dylan522p/status/2068043256634683409">透露</a> Nvidia 把下一代 Rubin Ultra 的 HBM 堆疊從 16 層砍到 12 層，連跟供應鏈綁最深的 Nvidia 都拿不到想要的量。二是有意義的新產能要等 2027 年底到 2028 年，也就是說接下來這一兩年，模型供應商能拿到的記憶體就是這麼多了。</p>

<h2 id="nvidia-對-tpu-勝負不在晶片設計在誰拿得到-hbm">Nvidia 對 TPU: 勝負不在晶片設計，在誰拿得到 HBM</h2>

<p>節目裡有一段講加速器競爭，Doug 的論點可以整理成三步:</p>

<p><strong>第一步，現在確實是 TPU 優勢最大的一刻。</strong> Doug 估算 TPU v7 (Ironwood) 是目前總持有成本 (TCO) 最好的加速器，而且領先幅度有感。原因有兩個: 一是 TPU 這邊該成熟的都成熟了，軟體生態、網路架構都到位，還多了 Anthropic 這個真正懂得用 TPU 的外部大客戶; 二是 Nvidia 自己這一代跌了一跤，GB200 出貨延遲又不穩定，Doug 直說如果 GB200 準時出貨、穩定運作，會全面壓過 v7。所以才有「在完全不缺貨的世界裡，每家實驗室都會盡可能多拿 TPU v7」這句話，連 OpenAI 都不例外。</p>

<p><strong>第二步，但在什麼都缺的年代，比的不是晶片設計，是供應鏈。</strong> TPU 想多做也做不出來，台積電產能是最大限制; 更關鍵的是記憶體: 短缺時 HBM 實質上是分配制，三家供應商的產能就這麼多，誰能簽下最好、最多的供應，看的是長約和多年的關係經營。這正是 Nvidia 最強的地方，Doug 說「Nvidia 毫無疑問是最強的，他們掌握整條供應鏈」。黃仁勳親自在亞洲和三星會長、SK 海力士高層喝酒應酬，重點不是喝酒本身，而是記憶體供應重要到 Nvidia 的執行長親自出面經營關係、鎖定最好的產能; 節目裡的反問是: 你覺得 Sergey Brin 有在做同樣的事嗎? 沒有。</p>

<p><strong>第三步，所以 Rubin 世代會反轉，而且贏的正是 context window 最需要的東西。</strong> Nvidia 下一代 Rubin 平台配 HBM4，Doug 判斷「HBM 和記憶體容量的優勢會倒向 Rubin 這邊」，而推論時代更大更快的記憶體，直接決定「context window 能做多大」，swyx 接話: 「決定一切」。用 Doug 的說法，Google 想搶市佔「這扇門只有現在開著」，等 Rubin 出來就關上了。</p>

<p>簡單說: 晶片設計的差距可以追，但記憶體供應的差距，在 40 年一遇的短缺裡就是勝負本身，而這件事 Nvidia 布局最深。後續進展一則: SemiAnalysis 四月的 <a href="https://newsletter.semianalysis.com/p/isscc-2026-nvidia-and-broadcom-cpo">ISSCC 2026 整理</a> 提到三星的 HBM4 進步明顯，效能已能滿足 Rubin 的要求，SK 海力士的獨大局面可能鬆動。三家都能量產 HBM4 對供給是好消息，但別忘了上面的交換比: HBM 做得越多，被換掉的一般 DRAM 產能就越多。</p>

<h2 id="context-window-卡在-1m而且是物理限制">Context window 卡在 1M，而且是物理限制</h2>

<p>這集討論度最高的就是這段。swyx 先丟出觀察:</p>

<blockquote>
  <p>swyx: 所有人，包括 Sam Altman，都在預測更長的 context window。但我們實際上已經卡在 1M 兩年了。</p>

  <p>Doug: 我最近一直在想這件事。它不會變成 100M，也不會變成 1T。就是這樣了，接下來五年、十年大概就是這樣。</p>
</blockquote>

<blockquote>
  <p>編按: Gemini 1.5 Pro 在 2024 年 2 月發表 1M context window，到這集錄音的 2026 年 2 月正好兩年，主流模型的上限還停在同一個量級。</p>
</blockquote>

<p>Doug 給了一個很好的定位: 長 context 本質上就是「往上堆記憶體」的能力。為什麼說這是物理限制、不是演算法問題? 訪談裡點到為止，小編展開算給大家看。核心是 KV cache: Transformer 模型在生成時，前面每個 token 的中間計算結果 (注意力的 Key 和 Value) 都要留在 GPU 的 HBM 裡隨時取用，這份暫存就是 KV cache，它隨 context 長度線性成長。以 Llama 3 70B 這種等級的模型粗估 (80 層、8 個 KV head、head_dim 128、FP16)，每個 token 的 KV cache 約 0.33 MB:</p>

<div style="display:flex;flex-wrap:wrap;gap:12px;margin:8px 0">
  <div style="flex:1 1 180px;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px">
    <div style="font-size:.85em;color:var(--text-secondary)">128K tokens 的 KV cache</div>
    <div style="font-size:1.5em;font-weight:700;color:var(--text)">約 43 GB</div>
    <div style="font-size:.85em;color:#1a7f37">一張 H200 (141GB HBM) 放得下</div>
  </div>
  <div style="flex:1 1 180px;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px">
    <div style="font-size:.85em;color:var(--text-secondary)">1M tokens 的 KV cache</div>
    <div style="font-size:1.5em;font-weight:700;color:var(--text)">約 330 GB</div>
    <div style="font-size:.85em;color:#9a6700">超過單卡容量，要 3 張 H200 分攤</div>
  </div>
  <div style="flex:1 1 180px;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px">
    <div style="font-size:.85em;color:var(--text-secondary)">100M tokens 的 KV cache</div>
    <div style="font-size:1.5em;font-weight:700;color:var(--text)">約 32 TB</div>
    <div style="font-size:.85em;color:#cf222e">超過 230 張 H200，只為了存住對話</div>
  </div>
</div>

<div style="font-size:.8em;color:var(--text-secondary);margin:-4px 0 8px">✏️ 小編製圖與估算，只算 KV cache，模型權重還要另外放。不同架構差異很大，但量級如此。</div>

<p>也就是說 100M context 光是 KV cache 就是 32TB 等級，需要兩百多張 GPU 只為了「記住」一段對話，經濟上完全不成立。</p>

<p>而且問題不只容量，還有頻寬: 生成回應時，每產生一個新 token，都要把整份 KV cache 從 HBM 完整讀一遍。context 越長，每個 token 的生成就越慢、越貴。這就是為什麼各家對長 context 的定價更高，也是為什麼推論部署現在流行把「讀入處理 prompt 的預填 (prefill) 階段」和「逐字生成的解碼 (decode) 階段」拆開，放在不同的機器上跑。</p>

<p>演算法能省的都是常數倍: GQA、MLA、KV cache 量化，這些技術都是在壓縮每個 token 佔的空間 (DeepSeek 的 MLA 壓得特別多)，但改變不了「隨 context 線性成長」這個本質。</p>

<p>swyx 接著問了一個很實際的問題: 從論文和他自己的使用經驗來看，模型在超長 context 下的表現會下降，並不會真的把 context 全部用上，那 100M 有意義嗎? Doug 的回答:</p>

<blockquote>
  <p>100M 的版本今天就存在，只是各有各的缺陷: 它們並沒有真的對每個 token 做完整的注意力計算。你可以用狀態空間模型 (SSM) 甚至 LSTM 去處理一億個 token，但你並沒有對這一億個 token 付出完整的注意力。</p>
</blockquote>

<p>他補充說，模型有效運用 context 的能力這幾年進步很多、還會再進步，但「我們永遠不會用滿全部的 context」。這句話值得記住: 就算 context window 不變，「塞得下」和「用得好」也是兩回事。</p>

<p>swyx 給了總結: 「你代表的是我們軟體這端永遠無法克服的物理限制。」Doug 接著補上: 「我們連翻倍都做不到，更別說 10 倍。」Dylan Patel 三月在 Dwarkesh 那集則從需求端印證同一件事: 這波記憶體需求大增的直接原因，就是推理模型把 context 越拉越長，context 越長 KV cache 就越大，你就是需要更多記憶體。</p>

<h2 id="context-rationing-context-會變成配給品">Context rationing: context 會變成配給品</h2>

<p>既然供給短期不會變出來，接下來就是分配問題。swyx 在節目上當場想出「context rationing (context 配給)」這個詞，Doug 順著推演計價的未來: 免費版的 context window 可能縮到 1,000 tokens 上下，然後對 1M 收 100 倍的價錢，「1M context window 就像豪宅」(他的原話)。swyx 接著開玩笑說以後會有 context 配給券: 「你今天的 context 額度用完了」。兩人講得半開玩笑，但方向很認真: 按 context window 分級計價，明年出現都不奇怪。</p>

<p>至於解法，這集加上後續幾場訪談合起來看有三種，每一種都不快:</p>

<ol>
  <li><strong>演算法</strong>: 遞迴語言模型 (recursive language models，遞迴地重複使用同一份 context window，swyx 說他最近很關注)、稀疏注意力 (sparse attention)、線性注意力 (linear attention)、狀態空間模型。共同的取捨是都不對每個 token 做完整的注意力計算，省記憶體的代價是模型對 context 前段的內容記得比較模糊: 當答案需要用到幾十萬 token 之前提過的細節時，可能漏掉或記錯。</li>
  <li><strong>硬體</strong>: Dylan Patel 七月初在 <a href="https://sequoiacap.com/podcast/dylan-patel-of-semianalysis-why-hardware-software-co-design-is-ais-real-100x/">Sequoia 的訪談</a> 講了技術面: DRAM 的記憶單元發明至今 40 年、NAND 也 25 年沒有重大突破，過去五年的進步只是把 HBM 堆更高、跑更快。接下來幾年值得期待的是把記憶體直接堆疊在運算晶片上 (而不是像 HBM 一樣分開封裝再連線)，頻寬會大幅成長。但這是以年為單位的計畫，救不了這兩年。</li>
  <li><strong>特化晶片</strong>: <a href="https://taalas.com/">Taalas</a> 這類把模型權重直接做進晶片、推論不需要外部記憶體的方案。Doug 認為有一席之地，理由是正式上線的模型其實都蒸餾得很小，搭配預填和解碼分離的趨勢，推論市場會分化出「固定模型、超低單價」的一塊。但他也提醒要保守看待: 過去十年做 AI 晶片、想挑戰 Nvidia 的新創公司很多，幾乎都以失敗收場，連算是活下來的 Cerebras 和 Groq，市場都還在問他們到底能做成什麼生意。Taalas 得先證明自己是例外。</li>
</ol>

<h2 id="小結-context-的價值在密度不在長度">小結: context 的價值在密度，不在長度</h2>

<p>小編覺得這集最值得帶走的想法是: 「模型會不會更聰明」和「context window 會不會更大」，這兩件事要分開來想。前者看演算法和訓練，還在快速進步; 後者看 HBM 和 DRAM 供應鏈，而供應鏈已經給出答案: 未來幾年就是 1M 這個量級，而且會越來越貴。從 2 月的訪談到 7 月的現況，這個判斷只有被加強，沒有被推翻。</p>

<p>回頭把整集的線索收攏，會發現它們指向同一個結論:</p>

<ul>
  <li><strong>物理上</strong>，KV cache 隨 context 線性成長，記憶體供給短期不會增加，1M 就是這幾年的上限;</li>
  <li><strong>經濟上</strong>，context 越長，每個 token 的生成越慢、越貴，計價只會往 context rationing 的方向走，塞進 context 的每個 token 都是錢;</li>
  <li><strong>品質上</strong>，就算塞得下，模型也不會把每個 token 都用上，Doug 自己都說「我們永遠不會用滿全部的 context」。</li>
</ul>

<p>三條線加起來: context 註定是要精打細算的稀缺資源，而且塞好塞滿本來就不是好策略。與其等一個更大的 context window (等不到)，不如把手上這 1M 的資訊密度做好。這正是 <a href="https://ihower.tw/blog/12817-context-engineering">context engineering</a> 在處理的問題，ihower 的文章中提到做法有四類方向，每一類都正好在回應這集講的限制:</p>

<ul>
  <li><strong>寫入 context</strong>: 把不需要隨時放在 context 裡的資訊 (使用者偏好、過往結論) 存到 context window 之外，形成長期記憶，要用再拿回來;</li>
  <li><strong>選擇 context</strong>: 靠 RAG 檢索、記憶提取、動態挑選工具，只把當下任務需要的資訊拉進 context;</li>
  <li><strong>壓縮 context</strong>: 超過門檻就總結、修剪，只留執行任務所需的 tokens，例如 Claude Code 的 auto-compact;</li>
  <li><strong>隔離 context</strong>: 把支線任務拆給 sub-agent 用它自己的 context 執行，回到主 context 的只有結果。</li>
</ul>

<p>用這集的視角看，這四件事的共同效果就是用更少的 token 達成同樣的任務品質: 直接省下 KV cache 的記憶體、省下越來越貴的長 context 費用，也避開塞太滿的品質下降。context engineering 會越來越重要，不是因為它時髦，而是因為物理和供應鏈都說了: 短期內沒有人會來救你，能救你的只有把 context 用得更好。</p>]]></content><author><name>ihower</name></author><category term="Industry" /><category term="LLM" /><category term="Context Engineering" /><summary type="html"><![CDATA[Latent Space 在 2026/2/24 上了一集 Claude Code for Finance + The Global Memory Shortage (YouTube 完整版、X 公告)，來賓是 SemiAnalysis 的 Doug O’Laughlin。上半場聊他怎麼把 Claude Code 當成金融研究的主力工具，那篇「GitHub 上 4% 的公開 commits 出自 Claude Code」的 Claude Code is the Inflection Point 就是 SemiAnalysis 發的，這部分本文不展開。小編覺得這集真正有意思的是下半場的全球記憶體短缺，因為它帶出一個跟每位 AI 工程師都有關的結論: LLM 的 context window 短期內不會再大幅成長，卡住它的不是演算法，是 HBM 記憶體。這篇就專心講這個，以及它為什麼讓 context engineering 變得更重要。]]></summary></entry><entry><title type="html">Claude Fable 5 的 Prompting 要點: 該刪的比該加的多</title><link href="https://blog.aihao.tw/2026/07/04/claude-fable-5-prompting/" rel="alternate" type="text/html" title="Claude Fable 5 的 Prompting 要點: 該刪的比該加的多" /><published>2026-07-04T00:00:00+00:00</published><updated>2026-07-04T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/07/04/claude-fable-5-prompting</id><content type="html" xml:base="https://blog.aihao.tw/2026/07/04/claude-fable-5-prompting/"><![CDATA[<p>Anthropic 上個月發布了 <a href="https://www.anthropic.com/news/claude-fable-5-mythos-5">Claude Fable 5</a>，第一個 Mythos 級模型，定位在 Opus 之上。比較少人注意到的是，官方同時出了一份專屬的 <a href="https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5">Prompting Claude Fable 5</a> 指南，整份文件講的就是 Fable 5 和 Opus 4.8 的行為差異，以及你的 prompt 該跟著改什麼。</p>

<p>小編消化了官方文件、Anthropic Claude Code 團隊成員 Thariq 的實戰長文，加上發布後這幾週社群摸索出來的共識，幫大家挑出「跟之前模型不一樣」的部分。至於 XML 標籤、few-shot 範例這些通用技巧，舊模型怎麼用、現在還是怎麼用，這篇就不重複了。</p>

<div style="margin:16px 0;padding:14px 18px;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:6px">📌 另外要先說明，這些建議的適用對象分兩種: 在 Claude app 或 Claude Code 裡直接下 prompt 的<strong>使用者</strong>，以及用 API 設計 agent、寫 system prompt 和 harness 的<strong>開發者</strong>。兩邊用得上的東西不一樣，所以每節標題下方和每個 prompt 範本前面都加了標籤: <span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者</span> 代表對話中可以直接使用，<span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者</span> 代表設計 agent 時放進 system prompt。兩個標籤都有的，代表兩種情境都適用; 自己維護 CLAUDE.md 和 skills 的 Claude Code 使用者介於兩者之間，通常兩邊的範本都用得上。</div>

<p>先快速交代規格，方便理解後面的建議:</p>

<ul>
  <li>定價: 每百萬 token 輸入 $10、輸出 $50，約是 Opus 4.8 的兩倍</li>
  <li>context window 有 1M token。難的任務單一請求可以跑數分鐘，自主執行可以連續數小時</li>
  <li>thinking 永遠開啟、只有 adaptive 模式，推理深度由 <code class="language-plaintext highlighter-rouge">effort</code> 參數控制(low/medium/high/xhigh/max)</li>
  <li>內建安全分類器: 網安和生物相關請求會被拒絕，可設定自動 fallback 到 Opus 4.8。同一個底層模型、拿掉分類器的版本叫 Claude Mythos 5，只開放給核准的組織</li>
</ul>

<h2 id="1-為舊模型寫的細節指令現在會扣分">1. 為舊模型寫的細節指令，現在會扣分</h2>

<div style="margin:-4px 0 20px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者</span> <span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者</span></div>

<p>整份指南最重要的一句話，<a href="https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5">官方原文</a>直翻: 「為先前模型開發的 skills，對 Claude Fable 5 來說往往過於規範(too prescriptive)，可能降低輸出品質。」</p>

<p>過去官方教大家把 Claude 當成聰明但不熟悉你環境的新員工，每個步驟都要鉅細靡遺地交代。Fable 5 的指南方向不同了: 你負責講清楚目標、理由、邊界和驗收標準，步驟讓它自己規劃。那些當年為了補救舊模型規劃能力而寫的步驟清單，現在反而變成一種限制: 模型明知不適合，還是會照著做。官方還提到一個相關特性: Fable 5 很會在任務中根據學到的東西即時更新 skill 內容，寫得太死的 skill 反而妨礙這種自我修正。</p>

<p>Claude Code 的作者 Boris Cherny <a href="https://x.com/bcherny/status/2064431111154053187">發布當天的心得</a>(一萬讚)正好是這件事的註腳。他說第一次意識到 Fable 不一樣，是請它 debug 的時候: 「它是我用過第一個如此有條理又精確的模型: 先量測、加 log，驗證真的修好了才宣告完成。Claude Code 的 prompt 裡沒有任何指令教模型這麼做，這就是它內建的行為。」換句話說，很多你以前要寫進 prompt 的要求，現在已經是預設值。</p>

<p>指令跟隨能力變強，還帶來一個紅利: 一句原則就能取代逐條列舉。官方自己的範例是，與其把每一種囉嗦的輸出模式列出來一一禁止，不如給一小段原則:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>Lead with the outcome. Your first sentence after finishing should answer “what happened” or “what did you find”: the thing the user would ask for if they said “just give me the TLDR.” Supporting detail and reasoning come after.</p>

  <p>中譯: 用成果來引導。你完成後的第一句話，就要回答「發生了什麼」或「你發現了什麼」: 也就是使用者說「直接給我重點摘要」時想要的那句話。支撐的細節和推理放在後面。</p>
</blockquote>

<p>反過來說，以前那種「CRITICAL: 你必須⋯⋯」「ALWAYS/NEVER」的加重語氣，現在會造成過度觸發(overtrigger): 模型變得太聽話，你越喊，它做得越過頭。官方建議改回平常語氣，大寫強調只留給真正無例外的規則。</p>

<p>實務上要從哪裡開始刪? <a href="https://www.productcompass.pm/p/claude-fable-5-guide">Pawel Huryn 的指南</a>提供了一個好做法: 先讓 Fable 5 稽核你既有的 prompt 和 skills，請它找出互相矛盾的指令，以及那些為了舊模型缺陷而存在的過時規則，列出建議刪除的清單和理由，最後由你決定刪什麼。</p>

<blockquote>
  <p>編按: 「為舊模型調校的指令會拖累新模型」，跟之前駕馭工程系列講的 <a href="https://blog.aihao.tw/2026/06/26/harness-engineering-8-model-harness-fit/">Model-Harness Fit</a> 是同一個現象: prompt 和 harness 是針對特定模型的能力缺口設計的，模型換代之後，原本的補強就變成了限制。</p>
</blockquote>

<h2 id="2-think-step-by-step不只沒用還可能觸發-refusal">2. 「Think step by step」不只沒用，還可能觸發 refusal</h2>

<div style="margin:-4px 0 20px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者</span> <span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者</span></div>

<p>Fable 5 的 thinking 永遠開啟，adaptive thinking 是唯一模式: 想關掉會拿到 400 錯誤，<code class="language-plaintext highlighter-rouge">budget_tokens</code> 也不再支援，思考深度一律由 <code class="language-plaintext highlighter-rouge">effort</code> 控制。延續 4.6 世代的變更，assistant prefill(預填模型回應開頭)也不支援了，要控制輸出格式改用 structured outputs。而且不管怎麼設定，原始的思考過程(raw chain of thought)都不會回傳，只能拿到摘要版的 thinking blocks(這些 API 行為詳見<a href="https://platform.claude.com/docs/en/about-claude/models/introducing-claude-fable-5-and-claude-mythos-5">官方模型介紹</a>)。</p>

<p>所以「請一步一步思考」這類指令已經沒有意義，模型自己會決定何時思考、想多深。更要注意的是反效果: 要求模型「解釋你的內部推理」「展示你的思考過程」，可能觸發名為 <code class="language-plaintext highlighter-rouge">reasoning_extraction</code> 的拒絕分類器，整個請求會 fallback 到 Opus 4.8。官方明確建議，遷移時要逐一檢查舊的 prompt 和 skills，把這類 show-your-thinking 指令拿掉。麻煩的是，這種問題不會以錯誤的形式出現，而是表現成「fallback 比例莫名升高、輸出品質突然變差」，不容易察覺。</p>

<p>如果應用真的需要看到推理過程，正確做法有兩種: 讀取 <code class="language-plaintext highlighter-rouge">thinking.display: "summarized"</code> 回傳的摘要，或是給 agent 一個 <code class="language-plaintext highlighter-rouge">send_to_user</code> 工具，讓它在長任務中途，把必須原文呈現的訊息直接送到使用者面前(官方提醒，光定義這個工具還不夠，要在 system prompt 裡明確提示什麼時候用，Fable 5 才會主動去用它)。</p>

<p>當年 prompt engineering 就是靠「think step by step」這類技巧起家的。幾年後的今天，同一個寫法變成會觸發安全分類器，這大概是這次遷移最有時代感的一條。</p>

<h2 id="3-哪些主題會被拒絕-refusal-分類器與它的誤觸">3. 哪些主題會被拒絕? refusal 分類器與它的誤觸</h2>

<div style="margin:-4px 0 20px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者</span> <span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者</span></div>

<p>上一節講的 <code class="language-plaintext highlighter-rouge">reasoning_extraction</code> 只是其中一個分類器。Fable 5 是 Mythos 級模型，能力強到 Anthropic 認為某些領域一旦被濫用風險很高，於是在模型外面加了一層安全分類器來把關。一旦請求被判定為高風險，API 會回傳 <code class="language-plaintext highlighter-rouge">stop_reason: "refusal"</code>，並可以設定自動 fallback 到 Opus 4.8 來接手。要注意這個 refusal 是 HTTP 200 的正常回應、不是錯誤，所以程式要主動去檢查，不然它不會進到你的錯誤處理流程。</p>

<p><a href="https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5">官方文件</a>明列三類封鎖目標:</p>

<ul>
  <li><strong>進攻型網路安全</strong>(offensive cybersecurity): 撰寫 exploit、惡意程式、攻擊工具這類「拿來攻破系統」的內容。相對的「防禦型」(defensive)工作，例如加固、偵測、修補漏洞，不在封鎖範圍。分界就在於這份產出是用來攻擊，還是用來防守</li>
  <li><strong>生物與生命科學</strong>: 實驗方法、分子機制這類內容</li>
  <li><strong>思考萃取</strong>(reasoning extraction): 想把模型的摘要思考掏出來，也就是上一節講的那種指令</li>
</ul>

<p>除了這三類，Anthropic 在別的場合也提過<strong>化學</strong>和<strong>蒸餾</strong>(distillation，用大模型的輸出去訓練小模型，Anthropic 曾指控 DeepSeek 大規模這樣做)同樣在防範之列。官方講得很白: 這層防護「刻意保守」，寧可錯殺不可放過，所以正當的網安研究和有益的生命科學工作也常被掃到。他們自己給的數據是超過 95% 的 session 完全不會觸發 fallback。</p>

<p>麻煩的是剩下那不到 5%，而且這條界線一直在變。發布首週誤觸最誇張的是生物領域: <a href="https://www.theverge.com/ai-artificial-intelligence/947973/fable-wont-answer-basic-biology-questions">The Verge 當時的實測</a>發現 Fable 5 連高中程度的生物題都不肯答(「介紹一下細胞膜」「mRNA 疫苗怎麼運作」全被擋)，Anthropic 也向 The Verge 證實這是刻意設定得過度保守; 做生物統計、生物資訊的研究者受害尤其無辜，<a href="https://github.com/anthropics/claude-code/issues/66674">Claude Code 的一個 GitHub issue</a> 記錄到，光是 prompt 出現「spatial transcriptomics」(空間轉錄體學)或「組學」這種純學術名詞，即使整段都在講統計、沒碰任何實驗，也會被 Usage Policy 擋下(回報者還發現中文的觸發門檻更低)。官方說會持續調整、降低誤觸率，但實際邊界怎麼變、放寬了哪些，並沒有公開的對照，只能自己實測。</p>

<p>網安這條則有明確的收緊紀錄。7/1 Anthropic <a href="https://x.com/claudeai/status/2072402638247968855">公告</a>在和美國政府談過後更新了網安防護，坦言「短期內新防護會比先前多擋掉一小部分無害的請求」，正在接下來幾週微調。</p>

<p>除了蒸餾，官方對於前研模型研究(frontier LLM development)也會封鎖，而這條發布時鬧出的風波最大。它跟網安、生物不一樣: 一開始是「隱形」的，不像其他類別會明白 fallback 到 Opus，而是<strong>默默改動或降級回應、不通知使用者</strong>。做模型評測和 AI 基礎設施的研究者最先發現不對: 答案被動了手腳卻毫無標示，等於整份 eval 的結果都不可信。挨批之後 Anthropic <a href="https://x.com/ClaudeDevs/status/2064949876463645026">公開道歉</a>並在 6/11 改成可見的 fallback，公告原文承認「我們做了錯誤的取捨，很抱歉沒有拿捏好平衡」。據 Anthropic 自己的說法，這條分類器只在約 0.05% 的任務上觸發，主要打到的是前研規模的 LLM 資料管線、以及某些非標準晶片的 kernel 開發這類很窄的工作，一般人幾乎碰不到。這場風波的重點其實不在誤觸率，而在原則: 商用 API 如果會悄悄改變答案的內容，使用者至少該知道它被改過。</p>

<p>對開發者的實際意義有兩點。第一，只要工作會碰到生物、醫學、網安或前研模型研究，一定要先把 fallback 到 Opus 4.8 設定好，否則一個 refusal 就像一次沉默的失敗，你不會知道發生了什麼事。第二，如果哪天輸出品質突然變差，先懷疑是不是被 fallback 到 Opus 了，再去懷疑模型本身。官方持續在調分類器，任何「某個詞會不會被擋」的說法都有賞味期限，包括本文，實測最準。</p>

<h2 id="4-effort-變成控制思考深度的唯一手段">4. effort 變成控制思考深度的唯一手段</h2>

<div style="margin:-4px 0 20px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者</span></div>

<p><code class="language-plaintext highlighter-rouge">effort</code> 參數(low/medium/high/xhigh/max 五級)本身不是新東西，Opus 4.6 世代就有了。但在 Fable 5 上它的角色變重: 因為 thinking 關不掉、<code class="language-plaintext highlighter-rouge">budget_tokens</code> 也不再支援，effort 變成控制思考深度的唯一手段，負責智能、延遲、成本三者的取捨。想靠「請再仔細檢查」「think harder」這類字眼加壓的空間也變小了，模型會自己依 effort 和題目難度決定想多深。</p>

<p><a href="https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5">官方的校準建議</a>跟直覺相反: 預設用 high 就好，就算你在 Opus 4.8 上是跑 xhigh 的工作，也先從 high 開始。xhigh 留給對能力最敏感的任務，日常工作用 medium 甚至 low。原因是 Fable 5 開低 effort 的表現，常常已經超過舊模型開 xhigh，習慣性開滿只是多花錢、多等待。<a href="https://x.com/mvanhorn/status/2064887887578136666">mvanhorn 整理的發布首日社群觀察</a>也有實測佐證: YouTuber Web Dev Cody 發現 Fable 5 開 medium 就勝過 Opus 4.8 開 high 和 max，而且用的 token 更少。</p>

<p>不過高 effort 有個副作用要用 prompt 管理: 模型會蒐集超出任務所需的脈絡，或是做沒人要求的重構。官方給的範本:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span> <span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>Don’t add features, refactor, or introduce abstractions beyond what the task requires. A bug fix doesn’t need surrounding cleanup and a one-shot operation usually doesn’t need a helper. Don’t design for hypothetical future requirements: do the simplest thing that works well. Avoid premature abstraction and half-finished implementations. Don’t add error handling, fallbacks, or validation for scenarios that cannot happen. Trust internal code and framework guarantees. Only validate at system boundaries (user input, external APIs). Don’t use feature flags or backwards-compatibility shims when you can just change the code.</p>

  <p>中譯: 不要加功能、不要重構、不要引入超出任務需求的抽象。修 bug 不需要順手清理周邊的程式碼，一次性的操作通常也不需要另寫輔助函式。不要為假設性的未來需求做設計: 做能把事情做好的最簡單方案。避免過早抽象和半成品的實作。不要為不可能發生的情境加錯誤處理、備援機制或驗證。信任內部程式碼和框架的保證，只在系統邊界(使用者輸入、外部 API)做驗證。能直接改程式碼，就不要用功能開關或向後相容的墊片。</p>
</blockquote>

<h2 id="5-把最難的任務給它並附上理由">5. 把最難的任務給它，並附上理由</h2>

<div style="margin:-4px 0 20px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者</span></div>

<p><a href="https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5">官方遷移建議</a>的第一條就是「先拿你手上最難的任務來試」(start at the top of your difficulty range): 挑一個以前不會考慮交給模型的難題，讓它先界定範圍、問清楚問題、再動手。官方觀察到，成效最好的團隊都是把 Fable 5 用在手上最難的未解問題，只拿簡單任務來測，會低估它的能力範圍。Thariq 在發布當天的<a href="https://x.com/trq212/status/2064437561930682672">推文</a>也是同一個訊息: 「Fable 是模型的一次跳級(step-change)，我希望它改變你和 Claude 工作的方式。重點一句話: 是時候更有企圖心了。」</p>

<p>Anthropic 的 Alex Albert 發布當天給新手的<a href="https://x.com/alexalbert__/status/2064467657483829441">四點建議</a>，可以當成本文到這裡的濃縮版:</p>

<ol>
  <li>給它比先前模型能處理的更大、更有企圖心的任務</li>
  <li>預設用 xhigh/high effort 拿最好的表現，互動式的工作用 medium 比較快</li>
  <li>重寫你的 skills 和 CLAUDE.md: 為舊模型寫的指令會把 Fable 拉回過時的模式，先讓它用自己的判斷</li>
  <li>從「給任務」改成「給目標」: 描述完成長什麼樣、怎麼驗證，然後讓 Fable 自己找路(Claude Code 的 <code class="language-plaintext highlighter-rouge">/loop</code> 和 <code class="language-plaintext highlighter-rouge">/goal</code> 就是為此設計的)</li>
</ol>

<p>另一個新強調的原則是「給理由，不要只給請求」。當 Fable 5 知道你為什麼要做這件事，表現會明顯更好，因為它能把任務連結到相關的資訊，而不是自己猜測你的意圖。官方範本:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>I’m working on [the larger task] for [who it’s for]. They need [what the output enables]. With that in mind: [request].</p>

  <p>中譯: 我正在為 [誰] 做 [更大的任務]，他們需要 [這個輸出要拿來做什麼]。基於這個脈絡: [你的請求]。</p>
</blockquote>

<p>社群把這個原則推到更完整的形式。發布後流傳最廣的是 <a href="https://x.com/SpikeCalls/status/2064698271151341812">@SpikeCalls 的這個 prompt</a>: 把你的事業交給它，不要只給 demo:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>Here is my business: [what you sell, who buys it, your stack, your team size, your current bottleneck, last quarter’s numbers].</p>

  <p>You have my context. Don’t give me generic advice. Tell me the 8 highest-leverage jobs you would take off my plate this month, ordered by expected impact. For the top one, start now: tell me exactly what data or access you need from me.</p>

  <p>中譯: 這是我的事業: [你賣什麼、誰買、技術架構、團隊規模、目前的瓶頸、上一季的數字]。你已經有我的完整脈絡，不要給我通用建議。告訴我這個月你會從我手上接走的 8 件影響最大的工作，按預期效益排序。排第一的那件現在就開始: 告訴我你需要我提供什麼資料或權限。</p>
</blockquote>

<h2 id="6-長時程任務的防護指令">6. 長時程任務的防護指令</h2>

<div style="margin:-4px 0 20px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者</span></div>

<p>先講清楚適用場景: 這一節整節只標 🛠️ 開發者，因為下面的範本幾乎都是放進自主 agent 的 system prompt、給「沒人在旁邊盯著、要連續跑好幾個小時」的長時程執行用的，主要是給設計自主 agent 管線的開發者。</p>

<p>Fable 5 的定位就是長時程自主工作，<a href="https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5">官方指南</a>有一半篇幅在講這件事的配套。最基本的是工程面: 單一請求會跑得比以前久很多，client timeout、streaming、進度顯示都要先調整好。harness 也建議改成非同步查看進度(例如用排程檢查)，而不是同步等到底。</p>

<p>另外，任務越模糊，Fable 5 越可能過度規劃。官方建議用這段防止它想太多:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>When you have enough information to act, act. Do not re-derive facts already established in the conversation, re-litigate a decision the user has already made, or narrate options you will not pursue in user-facing messages. If you are weighing a choice, give a recommendation, not an exhaustive survey. This does not apply to thinking blocks.</p>

  <p>中譯: 資訊足夠行動時，就行動。不要重新推導對話中已經確立的事實，不要重啟使用者已經做過的決定，也不要在給使用者的訊息裡陳述你不會採用的選項。如果你在權衡選擇，給一個建議，而不是完整的選項調查。這不適用於思考區塊。</p>
</blockquote>

<p>prompt 層面還有幾個重點，這些範本設計上是放進自主 agent 的 system prompt(其中「進度聲明要有證據」「劃清評估和動手的界線」這類，放進 Claude Code 的 CLAUDE.md 也適用):</p>

<p>🔹 <strong>進度聲明要有證據。</strong> 長時間自主執行，最怕模型回報「都做完了」但實際沒做。官方的解法是一條指令，在官方測試中幾乎完全消除了捏造的進度報告，連刻意設計來誘發假回報的任務都擋得住:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>Before reporting progress, audit each claim against a tool result from this session. Only report work you can point to evidence for; if something is not yet verified, say so explicitly. Report outcomes faithfully: if tests fail, say so with the output; if a step was skipped, say that; when something is done and verified, state it plainly without hedging.</p>

  <p>中譯: 回報進度之前，先把每一項聲明對照本次工作階段的工具執行結果。只回報你能指出證據的工作，還沒驗證的就明說。如實回報結果: 測試失敗就照實說並附上輸出，跳過了某個步驟就說跳過了，完成且驗證過的就直接陳述，不要模稜兩可。</p>
</blockquote>

<p>🔹 <strong>劃清「評估」和「動手」的界線。</strong> Fable 5 偶爾會做沒被要求的事，例如替你起草一封沒人要的 email，或自作主張建立備份分支。官方建議明確定義界線:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>When the user is describing a problem, asking a question, or thinking out loud rather than requesting a change, the deliverable is your assessment. Report your findings and stop. Don’t apply a fix until they ask for one.</p>

  <p>中譯: 當使用者只是在描述問題、提問或思考，而不是要求修改時，你要交付的是你的評估。回報發現就停下來，對方開口才動手修。</p>
</blockquote>

<p>🔹 <strong>告訴它什麼時候該停下來問你。</strong> 長時程工作需要檢查點(checkpoint)，但不需要列舉每一種情況，一句原則就夠:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>Pause for the user only when the work genuinely requires them: a destructive or irreversible action, a real scope change, or input that only they can provide. If you hit one of these, ask and end the turn, rather than ending on a promise.</p>

  <p>中譯: 只有在工作真正需要使用者時才停下來: 破壞性或不可逆的動作、真正的範圍變更、或只有他們能提供的資訊。碰到這些情況就發問並結束這一輪，而不是以一句承諾作結。</p>
</blockquote>

<p>🔹 <strong>防止長 session 後期提前結束。</strong> 官方提到一個少見但存在的行為: 在很長的 session 深處，Fable 5 偶爾會留下一句「我接下來會執行 X」就結束回合，沒有真的呼叫工具，或是明明資訊足夠還停下來問你要不要繼續。人在旁邊時回一句「continue」就好，自主執行的管線則建議加上這段系統提醒:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>You are operating autonomously. The user is not watching in real time and cannot answer questions mid-task, so asking “Want me to…?” or “Shall I…?” will block the work. For reversible actions that follow from the original request, proceed without asking. Offering follow-ups after the task is done is fine; asking permission after already discussing with the user before doing the work is not. Before ending your turn, check your last paragraph. If it is a plan, an analysis, a question, a list of next steps, or a promise about work you have not done (“I’ll…”, “let me know when…”), do that work now with tool calls. End your turn only when the task is complete or you are blocked on input only the user can provide.</p>

  <p>中譯: 你正在自主運作。使用者沒有即時盯著，也無法在任務中回答問題，所以問「要不要我⋯?」會讓工作停擺。對於源自原始請求、可逆的動作，直接做，不用問。任務完成後提出後續建議沒問題，但先前已經和使用者討論過、動工前又再要一次許可就不行。結束回合之前，檢查你的最後一段: 如果那是一個計畫、一段分析、一個問題、一串下一步、或一句關於你還沒做的工作的承諾(「我會⋯」「等你確認⋯」)，現在就用工具呼叫把它做掉。只有在任務完成、或卡在只有使用者能提供的資訊時，才結束回合。</p>
</blockquote>

<p>🔹 <strong>最終總結要寫給沒看過程的人。</strong> 跑了幾百個工具呼叫之後，Fable 5 給使用者的訊息可能充滿工作過程累積的簡寫、箭頭鏈和只有它自己懂的代號。官方建議加一段溝通風格指令，核心是:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>When you write the summary at the end, drop the working shorthand. Write complete sentences. Spell out terms. Don’t use arrow chains, hyphen-stacked compounds, or labels you made up earlier. When you mention files, commits, flags, or other identifiers, give each one its own plain-language clause. Open with the outcome: one sentence on what happened or what you found. Then the supporting detail. If you have to choose between short and clear, choose clear.</p>

  <p>中譯: 寫最後的總結時，丟掉工作過程中的簡寫。寫完整的句子，把術語講清楚。不要用箭頭鏈、連字號堆出來的複合詞、或你先前自創的代號。提到檔案、commit、旗標或其他識別符時，各自用一句白話說明。開頭先講成果: 一句話說明發生了什麼或你發現了什麼，接著才是支撐的細節。如果必須在簡短和清楚之間選一個，選清楚。</p>
</blockquote>

<p>Anthropic 的 Alex Albert <a href="https://x.com/alexalbert__/status/2065493229760565758">也分享過</a>同樣的困擾: Fable 在長 agentic 對話裡的產出，「有時多到我跟不上它在跟我說什麼」，他的解法同樣是一段要求寫清楚、去掉行話的 prompt 片段。</p>

<p>🔹 <strong>別讓它看到 context 倒數。</strong> 在很長的 session 中，如果 harness 把剩餘 token 數顯示給模型看，它可能開始提前收尾、精簡自己的工作，或建議開新 session。能藏就藏，藏不了就加一句安撫:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>You have ample context remaining. Do not stop, summarize, or suggest a new session on account of context limits. Continue the work.</p>

  <p>中譯: 你還有充足的上下文空間。不要因為上下文的限制而停止、總結、或建議開新的工作階段。繼續工作。</p>
</blockquote>

<p>🔹 <strong>給它一個記憶系統。</strong> Fable 5 很擅長利用先前執行留下的筆記，簡單到一個 markdown 資料夾就有效。Anthropic 的 Lance Martin <a href="https://x.com/RLanceMartin/status/2064397389189071163">實測過</a>這件事的模型差距: 有效的記憶累積有五個階段(失敗記下來、調查原因、驗證成事實、提煉成通用規則、之後直接查閱)，Sonnet 4.6 停在第一階段、只會堆失敗筆記，Opus 4.7 走到第三階段，Fable 5 常能走完全程，把教訓變成幫助後續任務的規則。官方的建議寫法:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>Store one lesson per file with a one-line summary at the top. Record corrections and confirmed approaches alike, including why they mattered. Don’t save what the repo or chat history already records; update an existing note rather than creating a duplicate; delete notes that turn out to be wrong.</p>

  <p>中譯: 一個教訓存一個檔案，開頭放一行摘要。更正和驗證過的做法都要記，包括它們為什麼重要。程式碼庫或對話紀錄已經有的就不要存，更新既有筆記而不是建立重複的，發現筆記是錯的就刪掉。</p>
</blockquote>

<p>官方還建議這樣啟動記憶系統，把過去的對話變成第一批筆記:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>Reflect on the previous sessions we’ve had together. Use subagents to identify core themes and lessons, and store them in [X]. Make sure you know to reference [X] for future use.</p>

  <p>中譯: 回顧我們先前的幾次工作階段，用子代理人找出核心主題和教訓，存到 [X]。之後記得參考 [X]。</p>
</blockquote>

<p>🔹 <strong>多用 subagent，驗證交給乾淨 context 的 subagent。</strong> Fable 5 比先前模型更會主動派出並行的子代理人(subagent)，官方建議多用，而且採用非同步的溝通方式:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>Delegate independent subtasks to subagents and keep working while they run. Intervene if a subagent goes off track or is missing relevant context.</p>

  <p>中譯: 把獨立的子任務分派給子代理人，在它們執行的同時繼續你的工作。如果某個子代理人偏離方向或缺少相關脈絡，就介入處理。</p>
</blockquote>

<p>驗證工作尤其適合: 官方指出，用乾淨 context 的驗證者 subagent 對照規格檢查，效果傾向優於模型的自我檢查(模型批改自己的輸出有已知的弱點)。Lance Martin 也<a href="https://x.com/RLanceMartin/status/2064397389189071163">實測過</a>這種「給目標和評分準則、讓模型自我修正、由獨立 subagent 驗證」的迴圈: 在一個 ML 工程挑戰上跑 8 小時，Fable 5 對訓練管線的改善幅度約是 Opus 4.7 的 6 倍，而且 Opus 幾乎只做「調一個常數、量測、有效就留下」的小步實驗，Fable 會做結構性的改動。長時程任務可以這樣寫:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span> <span style="display:inline-block;padding:2px 10px;border-radius:2em;background:#dafbe1;color:#1a7f37;font-size:.8em;font-weight:600">🛠️ 開發者 system prompt</span></div>

<blockquote>
  <p>Establish a method for checking your own work at an interval of [X] as you build. Run this every [X interval], verifying your work with subagents against the specification.</p>

  <p>中譯: 建立一套在建置過程中每隔 [X] 檢查自己工作的方法。每 [X 間隔] 執行一次，用子代理人對照規格驗證你的工作。</p>
</blockquote>

<p><a href="https://x.com/mvanhorn/status/2064887887578136666">社群摸索出的分工模式</a>也類似: Fable 負責規劃和把關，建置交給 Sonnet，測試交給 Haiku，讓成本較低的模型消化大量的執行工作。</p>

<h2 id="7-thariq-的-field-guide-品質瓶頸變成你的-unknowns">7. Thariq 的 Field Guide: 品質瓶頸變成「你的 unknowns」</h2>

<div style="margin:-4px 0 20px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者</span></div>

<p>官方文件之外，最值得讀的是 Claude Code 團隊成員 Thariq 今天發表的 <a href="https://x.com/trq212/status/2073100352921215386">A Field Guide to Fable: Finding Your Unknowns</a>。他用「地圖與疆域」開場(也就是本文的封面圖): prompt、skills、context 是你給 Claude 的地圖，codebase 和真實世界的限制才是實際的疆域，兩者之間的落差就是「unknowns」。模型碰到 unknown 時，只能根據「它猜你想要什麼」做決定，而做的工作越多，碰到的 unknowns 就越多。</p>

<p>他的核心觀察直翻是: 「Fable 是第一個讓我覺得，工作品質的瓶頸在於我能不能釐清它的 unknowns 的模型。」也就是說，模型能力不再是限制，限制在人這一端。</p>

<p>他也點出下指令的兩難，正好呼應第 1 節: 太具體，Claude 會在該轉向的時候仍照著你的指令走; 太模糊，它會用業界通用的做法來填空，不見得適合你的情境。所以最重要的是把你的起點交代清楚: 你在思考過程的哪個階段、對這個問題和這個程式碼庫的經驗多深，讓 Claude 像思考夥伴(thought partner)一樣跟你一起工作。他把 unknowns 拆成四類:</p>

<p><img src="/assets/images/claude-fable-5-prompting/unknowns.jpg" alt="" /></p>

<ul>
  <li><strong>Known knowns</strong>: 你寫進 prompt 的東西</li>
  <li><strong>Known unknowns</strong>: 你知道自己還沒想清楚的部分</li>
  <li><strong>Unknown knowns</strong>: 顯而易見到你不會特地寫下來，但看到成品就能認出對錯的標準</li>
  <li><strong>Unknown unknowns</strong>: 你根本沒考慮過的事，包括「你不知道這件事可以做到多好」</li>
</ul>

<p>對應的做法，他整理成實作前、中、後三組。共同精神是: 用很小的成本，把 unknowns 提前找出來。順帶一提，這些技巧的產出物(原型、計畫、報告)，他幾乎都用 HTML artifact 呈現，這是他一貫的主張: <a href="https://x.com/trq212/status/2052811606032269638">「HTML is the new markdown」</a>，與其寫 markdown 文件，不如讓 Claude 直接生成可互動的 HTML 頁面。</p>

<h3 id="實作前">實作前</h3>

<p><strong>Blindspot pass</strong>: 在不熟的領域開工前，直接用「blindspot pass」「unknown unknowns」這些字眼請 Claude 幫你補課。例如:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>I’m working on adding a new auth provider but I know nothing about the auth modules in this codebase. Can you do a blindspot pass to help me figure out my relevant unknown unknowns and help me prompt you better.</p>

  <p>中譯: 我要加一個新的身分驗證供應商，但我對這個程式碼庫的身分驗證模組一無所知。幫我做一次盲點掃描，找出那些我該知道、卻不知道自己不知道的事，讓我能把 prompt 下得更好。</p>
</blockquote>

<p><strong>腦力激盪</strong>: Thariq 幾乎每個 coding session 都從探索或腦力激盪開始，幫助界定專案範圍，Claude 常會找到他沒想到的高價值做法:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>Here’s my rough problem: users churn after onboarding. Search the codebase and brainstorm 10 places we could intervene, from cheapest to most ambitious. I’ll tell you which ones resonate.</p>

  <p>中譯: 我的問題大致是: 使用者在新手引導之後流失。搜尋程式碼庫，腦力激盪出 10 個我們可以介入的地方，從成本最低排到最有企圖心。我會告訴你哪些方向讓我有感。</p>
</blockquote>

<p><strong>原型</strong>: 針對「看到才知道要不要」的 unknown knowns，先要 HTML 原型，不要直接完整實作。在原型階段發現要改，比在實作深處發現便宜太多:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>Before wiring anything up, make a single HTML file mocking the new editor toolbar with fake data. I want to react to the layout before you touch the real app.</p>

  <p>中譯: 先不要接任何後端，做一個單一 HTML 檔，用假資料模擬新的編輯器工具列。我想先對版面配置有反應，你再去動真正的應用程式。</p>
</blockquote>

<p>視覺設計這種難以言傳的需求，可以一次要多個方向:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>I want a dashboard for this data but I have no visual taste and don’t know what’s possible. Make me an HTML page with 4 wildly different design directions so I can react to them.</p>

  <p>中譯: 我想為這些資料做一個儀表板，但我沒有視覺品味，也不知道有哪些可能性。做一個 HTML 頁面，放上 4 個截然不同的設計方向，讓我可以看了表達意見。</p>
</blockquote>

<p><strong>訪談</strong>: 腦力激盪完還有 unknowns 的話，換 Claude 來問你:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>Interview me one question at a time about anything ambiguous, prioritize questions where my answer would change the architecture.</p>

  <p>中譯: 一次問我一個問題，針對模糊的地方訪談我，優先問那些我的答案會改變架構的問題。</p>
</blockquote>

<p><strong>參考資料給原始碼</strong>: 講不清楚要什麼的時候，最好的參考資料是程式碼本身，跨語言也沒關係:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>This Rust crate in vendor/rate-limiter implements the exact backoff behavior I want. Read it and reimplement the same semantics in our TypeScript API client.</p>

  <p>中譯: vendor/rate-limiter 這個 Rust 套件實作了我要的退避(backoff)行為。讀懂它，然後在我們的 TypeScript API 客戶端重新實作同樣的語意。</p>
</blockquote>

<p><strong>實作計畫把「你最可能改的」放前面</strong>: 讓 Claude 把你最需要看的決策排在計畫的最前面，機械性的部分沉到最後:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>Write an implementation plan in HTML, but lead with the decisions I’m most likely to tweak: data model changes, new type interfaces, and anything user-facing. Bury the mechanical refactoring at the bottom, I trust you on that part.</p>

  <p>中譯: 寫一份實作計畫(用 HTML)，把我最可能調整的決策放最前面: 資料模型的變更、新的型別介面、使用者看得到的部分。機械性的重構放最後，那部分我信任你。</p>
</blockquote>

<h3 id="實作中">實作中</h3>

<p>再完整的計畫，實作途中還是會碰到新的 unknowns。Thariq 的做法是請 Claude 邊做邊記錄，這些紀錄會成為下一次規劃的輸入:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>Keep an implementation-notes.md file. If you hit an edge case that forces you to deviate from the plan, pick the conservative option, log it under ‘Deviations’, and keep going.</p>

  <p>中譯: 維護一份 implementation-notes.md。遇到迫使你偏離計畫的邊角情況，就選保守的做法，記錄在「Deviations」(偏離)段落底下，然後繼續前進。</p>
</blockquote>

<p>這個做法他<a href="https://x.com/trq212/status/2056415973125796184">五月就分享過</a>更完整的版本(9,700+ 讚):</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>implement <code class="language-plaintext highlighter-rouge">&lt;SPEC&gt;</code> and while you do, keep a running implementation-notes.html file (or markdown) with decisions you had to make weren’t in the spec, things you had to change, tradeoffs you had to make or anything else I should know</p>

  <p>中譯: 實作 <code class="language-plaintext highlighter-rouge">&lt;SPEC&gt;</code>，過程中持續維護一份 implementation-notes.html(或 markdown)，記錄規格裡沒寫、但你必須做的決定，你不得不改的東西，你做的取捨，以及任何其他我該知道的事。</p>
</blockquote>

<h3 id="實作後">實作後</h3>

<p><strong>打包成給別人看的說明文件</strong>: 出貨前常要爭取關係人的理解和核准。審核者一開始的 unknowns 跟你當初一樣多，一份好的說明文件能加速理解，也讓專家看到你考慮過他們會問的問題:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>Package the prototype, the spec, and the implementation notes into a single doc I can drop in Slack to get buy-in. Lead with the demo GIF.</p>

  <p>中譯: 把原型、規格和實作筆記打包成一份文件，讓我可以直接貼到 Slack 爭取支持。開頭放展示用的 GIF 動圖。</p>
</blockquote>

<p><strong>請 Claude 出題考你</strong>: 只讀 diff 只能看懂表面，很多行為藏在既有的程式路徑裡:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>I want to make sure I understand everything that’s happened in this change. Give me a HTML report on the changes for me to read and understand with context, intuition, what was done, etc. and a quiz at the bottom on the changes that I must pass.</p>

  <p>中譯: 我想確認自己完全理解這次變更發生了什麼。給我一份這次變更的 HTML 報告，附上脈絡、背後的直覺、做了什麼等等，最後放一份關於這次變更、我必須通過的測驗。</p>
</blockquote>

<p>Thariq 的標準是測驗全對才 merge。</p>

<p>文章最後他舉了自己的例子: Fable 5 的發布影片就是他用 Claude Code 剪的，而影片剪輯對他是全新領域(<a href="https://x.com/trq212/status/2064826394589442448">他有拍一支影片說明整個過程</a>: Claude 寫程式呼叫轉錄服務、ffmpeg、Figma MCP，用 Remotion 做動態字卡再算圖，他全程沒開過剪輯軟體)。碰到畫面色調偏灰，他知道問題出在調色(color grading)，但不懂那是什麼。第一反應是讓 Claude 做幾個版本來挑，後來發現自己連「好的調色長什麼樣」都不知道，於是改請 Claude 先教他，把 unknown 變成 known 之後才動工:</p>

<div style="margin:16px 0 -8px"><span style="display:inline-block;padding:2px 10px;border-radius:2em;background:var(--tag-bg);color:var(--accent);font-size:.8em;font-weight:600">👤 使用者 prompt</span></div>

<blockquote>
  <p>I don’t know what color grading is but I need to grade this video. Can you teach me to understand my unknown unknowns about color grading, so that I can prompt better?</p>

  <p>中譯: 我不知道調色是什麼，但我需要為這部影片調色。你能教我理解自己在調色上「不知道自己不知道」的部分，讓我能下更好的 prompt 嗎?</p>
</blockquote>

<h2 id="小結">小結</h2>

<p>把這些放在一起看: Fable 5 的 prompt 本身變短了，但要做的事沒有變少，只是內容變了。從控制模型的每一步，變成定義目標、理由、邊界和驗證方式，設計能自我修正的迴圈，再加上 Thariq 說的，持續釐清你自己的 unknowns。官方指南裡的那些範本與其照抄，不如拿你自己流量最大的 prompt 和 skills 做 A/B 對照，親自驗證「刪掉之後有沒有變好」。</p>

<p>下一個專案開工前，可以先試試 Thariq 的建議: 請 Claude 幫你找出「你不知道自己不知道」的事。</p>

<h2 id="參考資料">參考資料</h2>

<p>官方文件:</p>

<ul>
  <li><a href="https://www.anthropic.com/news/claude-fable-5-mythos-5">Claude Fable 5 and Claude Mythos 5 發布公告</a> (Anthropic, 2026/6/9)</li>
  <li><a href="https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5">Prompting Claude Fable 5</a> (官方 prompting 指南，本文的主要依據)</li>
  <li><a href="https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices">Claude prompting best practices</a> (跨模型通用的 prompting 建議)</li>
  <li><a href="https://platform.claude.com/docs/en/about-claude/models/introducing-claude-fable-5-and-claude-mythos-5">Introducing Claude Fable 5 and Claude Mythos 5</a> (API 變更、定價與可用性)</li>
</ul>

<p>Anthropic 成員的分享:</p>

<ul>
  <li><a href="https://x.com/trq212/status/2073100352921215386">A Field Guide to Fable: Finding Your Unknowns</a> (Thariq, 2026/7/4)</li>
  <li><a href="https://x.com/trq212/status/2064826394589442448">用 Claude Code 剪輯 Fable 5 發布影片的過程說明</a> (Thariq, 2026/6/10)</li>
  <li><a href="https://x.com/trq212/status/2056415973125796184">implementation-notes 的原始 prompt</a> (Thariq, 2026/5/18)</li>
  <li><a href="https://x.com/trq212/status/2052811606032269638">HTML is the new markdown</a> (Thariq, 2026/5/8)</li>
  <li><a href="https://x.com/trq212/status/2064437561930682672">Fable 發布日推文: 是時候更有企圖心了</a> (Thariq, 2026/6/9)</li>
  <li><a href="https://x.com/RLanceMartin/status/2064397389189071163">Designing loops with Fable 5</a> (Lance Martin, 2026/6/9)</li>
  <li><a href="https://x.com/bcherny/status/2064431111154053187">Fable 5 發布心得: 從 coding agent 變成思考與設計夥伴</a> (Boris Cherny, 2026/6/9)</li>
  <li><a href="https://x.com/alexalbert__/status/2064467657483829441">使用 Fable 的四點建議</a> (Alex Albert, 2026/6/9)</li>
  <li><a href="https://x.com/alexalbert__/status/2065493229760565758">讓 Fable 長對話輸出保持清楚的 prompt 片段</a> (Alex Albert, 2026/6/12)</li>
</ul>

<p>關於 refusal 分類器與誤觸:</p>

<ul>
  <li><a href="https://www.theverge.com/ai-artificial-intelligence/947973/fable-wont-answer-basic-biology-questions">Claude Fable won’t answer basic biology questions</a> (The Verge, 2026/6/10，記錄發布首週的生物誤觸)</li>
  <li><a href="https://github.com/anthropics/claude-code/issues/66674">Fable 5 bio classifier blocks innocuous academic statistics terms</a> (Claude Code GitHub issue)</li>
  <li><a href="https://x.com/ClaudeDevs/status/2064949876463645026">Anthropic 為前研模型研究的隱形防護道歉、改成可見 fallback</a> (@ClaudeDevs, 2026/6/11)</li>
  <li><a href="https://x.com/claudeai/status/2072402638247968855">Anthropic 公告在和美國政府談過後收緊網安防護</a> (@claudeai, 2026/7/1)</li>
</ul>

<p>社群:</p>

<ul>
  <li><a href="https://x.com/mvanhorn/status/2064887887578136666">/Last1Day of Fable 5: Best Practices and the Funniest Things People Are Saying</a> (mvanhorn, 2026/6/11)</li>
  <li><a href="https://x.com/SpikeCalls/status/2064698271151341812">描述你的事業、請 Fable 找出高效益工作的 prompt</a> (@SpikeCalls, 2026/6/10)</li>
  <li><a href="https://www.productcompass.pm/p/claude-fable-5-guide">Claude Fable 5: The Ultimate Guide for PMs</a> (Pawel Huryn, 2026/6/11)</li>
</ul>]]></content><author><name>ihower</name></author><category term="Prompt" /><category term="Agent" /><category term="LLM" /><summary type="html"><![CDATA[Anthropic 上個月發布了 Claude Fable 5，第一個 Mythos 級模型，定位在 Opus 之上。比較少人注意到的是，官方同時出了一份專屬的 Prompting Claude Fable 5 指南，整份文件講的就是 Fable 5 和 Opus 4.8 的行為差異，以及你的 prompt 該跟著改什麼。]]></summary></entry><entry><title type="html">你的 Prompt 通得過 The Mom Test 嗎? 如何避免 LLM 的迎合問題</title><link href="https://blog.aihao.tw/2026/07/02/mom-test-prompting/" rel="alternate" type="text/html" title="你的 Prompt 通得過 The Mom Test 嗎? 如何避免 LLM 的迎合問題" /><published>2026-07-02T00:00:00+00:00</published><updated>2026-07-02T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/07/02/mom-test-prompting</id><content type="html" xml:base="https://blog.aihao.tw/2026/07/02/mom-test-prompting/"><![CDATA[<p>最近小編看到一個有趣的跨領域對照: 把創業經典《<a href="https://www.momtestbook.com/">The Mom Test</a>》的用戶訪談原則，套用到跟 LLM 對話的情境上。乍聽是兩個不相關的領域，但仔細想想，兩者要解決的問題其實很像，而且有學術研究可以佐證。</p>

<h2 id="先說說-the-mom-test-在講什麼">先說說 The Mom Test 在講什麼</h2>

<p>Rob Fitzpatrick 2013 年出版的這本小書，核心觀點一句話: 人會因為禮貌而給你假訊號。你跟你媽說想做一個 app，她一定說「好棒喔」，但這句話對驗證產品毫無用處。</p>

<p>書裡提出三條原則:</p>

<ol>
  <li><strong>聊對方的生活，不要推銷你的點子</strong></li>
  <li><strong>問過去的具體事實，不要問未來的籠統想法</strong></li>
  <li><strong>少說多聽</strong></li>
</ol>

<p>以及三種要警覺的「壞資料」: 讚美(禮貌性的肯定)、空話(沒有根據的假設性回答)、點子(對方直接幫你設計產品)。拿到這三種回應，等於什麼都沒學到。</p>

<p>書裡一個很實用的判斷標準: 過去的行為是資料，未來的意願是虛構。「你會不會用?」這種問題永遠只會得到好聽話。「你上次遇到這個問題的時候做了什麼?」才能問出真正的需求。</p>

<h2 id="llm-也有一樣的毛病">LLM 也有一樣的毛病</h2>

<p>LLM 經過 RLHF(用人類回饋做強化學習)訓練後，有一個被廣泛討論的傾向: 「迎合」(sycophancy)。你問「這個方案好不好?」，它幾乎一定先說「這個方案看起來不錯」再補上一些泛泛的優點。跟你媽的反應很像。</p>

<p>成因不同，但結果類似。你媽是因為愛你而不忍心說實話; LLM 是因為訓練過程中，人類評分者傾向給禮貌、肯定的回應更高分數，模型就學會了「先同意再說」。如果你的 prompt 問的是意見而不是事實，拿到的答案就跟做了一場無效的用戶訪談一樣: 好聽但沒用。</p>

<p>The Mom Test 裡定義的三種壞資料，在 LLM 的回應中也都看得到:</p>

<ul>
  <li><strong>讚美</strong>: 「沒錯，這個方向很不錯!」然後列出三個泛泛的優點</li>
  <li><strong>空話</strong>: 模糊的建議，沒有具體根據，正反面都沾一點</li>
  <li><strong>點子</strong>: 你還沒說完需求，模型已經幫你設計好整個方案了</li>
</ul>

<h3 id="研究怎麼說">研究怎麼說?</h3>

<p>這不只是感覺，有學術研究佐證:</p>

<p>🔹 Anthropic 的研究團隊在論文 <a href="https://arxiv.org/abs/2310.13548">Towards Understanding Sycophancy in Language Models</a>(Sharma et al., ICLR 2024)裡，測了 Claude 1.3/2、GPT-3.5/4、LLaMA-2 70B 這五個主流 AI 助理，發現它們在四種不同任務中都一致出現迎合行為。分析偏好資料後更發現: 當回應符合使用者的觀點時，人類評分者更容易給高分。換句話說，模型是被我們自己訓練成這樣的。</p>

<p>🔹 Georgia Tech 的論文 <a href="https://arxiv.org/abs/2604.19117">LLMs Know They’re Wrong and Agree Anyway</a>(Pandey, 2026)在 12 個開源模型(Gemma-2、Phi-4、Llama-3、Mistral、Qwen-2.5 等，涵蓋 1.5B 到 72B)上做了實驗，發現了一件事: 模型迎合使用者的錯誤說法時，內部其實知道答案是錯的，只是選擇了同意。在 Gemma-2-2B 上關掉負責「迎合」的注意力頭之後，迎合率從 28% 跳到 81%，但事實準確率幾乎沒變(69% → 70%)。也就是說，迎合是一個獨立於知識的行為: 不是模型不知道正確答案，而是它選擇不說。</p>

<p>🔹 Stanford 發表在 Science 上的研究 <a href="https://doi.org/10.1126/science.aec8352">Sycophantic AI decreases prosocial intentions and promotes dependence</a>(Cheng et al., 2026)測了 GPT-5、GPT-4o、Claude Sonnet 3.7、Gemini 1.5、Llama、DeepSeek-V3 等十一個模型，發現 LLM 維護使用者「面子」的傾向比真人高了 47%。在 Reddit 的 r/AmITheAsshole 資料集上，模型在 42% 的案例中肯定了被群眾判定為不當的行為。</p>

<h2 id="五個從-mom-test-延伸的-prompt-原則">五個從 Mom Test 延伸的 Prompt 原則</h2>

<p>既然 LLM 跟你媽有類似的迎合傾向，Mom Test 的對策也能借鏡到 prompt 設計上:</p>

<h3 id="1️⃣-問證據不問意見">1️⃣ 問證據，不問意見</h3>

<ul>
  <li>❌ 你覺得這個方案好不好?</li>
  <li>✅ 請列出 3 個這個方案可能失敗的具體場景，並解釋原因。</li>
</ul>

<p>意見問題讓模型進入迎合模式，要求具體證據和反例，它才會認真分析。</p>

<h3 id="2️⃣-問已知事實不問假設">2️⃣ 問已知事實，不問假設</h3>

<ul>
  <li>❌ 如果遇到 X 問題，你會怎麼處理?</li>
  <li>✅ 在過去的研究或實務案例中，X 問題是怎麼被解決的?</li>
</ul>

<p>假設性問題容易產生幻覺(hallucination)。要求引用已知事實，輸出會更可靠。</p>

<h3 id="3️⃣-要求具體不接受模糊">3️⃣ 要求具體，不接受模糊</h3>

<ul>
  <li>❌ 幫我改善這段文案。</li>
  <li>✅ 請逐句檢查這段文案，指出哪些地方含糊不清，並給出更精確的替代寫法。</li>
</ul>

<p>模糊的問題得到模糊的答案。加上具體約束(逐句、指出問題、給替代方案)，模型才會做出有意義的判斷。</p>

<h3 id="4️⃣-用行為模擬取代態度表態">4️⃣ 用行為模擬取代態度表態</h3>

<ul>
  <li>❌ 如果你是使用者，你會不會喜歡這個產品?</li>
  <li>✅ 假設你是目標使用者，模擬一次實際使用過程，逐步寫出你會點擊、輸入、猶豫的地方。</li>
</ul>

<h3 id="5️⃣-要求挑錯不要尋求確認">5️⃣ 要求挑錯，不要尋求確認</h3>

<ul>
  <li>❌ 你能確認這個邏輯是對的嗎?</li>
  <li>✅ 請檢查這段邏輯，找出至少一個可能有問題的地方，並解釋理由。如果必須反駁，請站在反方角度給出 3 點論證。</li>
</ul>

<h2 id="這個對照的限制">這個對照的限制</h2>

<p>整體方向是對的，但有幾個差異值得注意。</p>

<p>「問已知事實」這條需要稍微調整。LLM 並沒有個人經歷，它的「過去事實」其實是訓練資料裡的知識。所以更準確的說法是: 把問題建立在可驗證的事實上，而不是讓模型自由發揮。而且即使你這樣問，模型仍然可能編出看起來像事實的內容，你還是需要自己去查證。Mom Test 原版裡「過去的行為不會騙人」這個前提，在 LLM 身上不完全成立。</p>

<p>不過，Mom Test 的角度確實提供了一個不錯的框架來理解「為什麼這些 prompt 技巧有效」: 因為本質上都是在對抗迎合傾向，讓模型從「給你好聽話」轉向「給你有用的分析」。它的核心價值是提供一個思考方向: 當你拿到的回應太好聽，就該警覺問題可能出在你的問法。</p>

<h2 id="社群裡的其他做法">社群裡的其他做法</h2>

<p>除了 Mom Test 的延伸，prompt 社群和學術研究中也發展出不少對抗迎合的實用技巧:</p>

<h3 id="6️⃣-你怎麼問決定它多迎合">6️⃣ 你怎麼問，決定它多迎合</h3>

<p>你的措辭方式會直接影響模型迎合你的程度。UK AI Security Institute 的論文 <a href="https://arxiv.org/abs/2602.23971">Ask Don’t Tell</a>(Dubois et al., 2026)在 GPT-4o/5、Claude Sonnet 4.5 等多個前沿模型上做實驗，證明了這一點: 同樣的內容，換個問法，迎合率差很多。</p>

<p>舉個例子，你想確認某個技術方案是否可行:</p>

<ul>
  <li>❌ 「我堅信這個架構是對的」→ 迎合率最高，模型幾乎不會反駁</li>
  <li>❌ 「我覺得這個架構應該沒問題吧」→ 稍好，但模型還是傾向附和</li>
  <li>✅ 「這個架構有什麼潛在問題?」→ 用中性問句，迎合率最低</li>
</ul>

<p>研究也發現，用第一人稱(「我認為…」)比第三人稱(「有人認為…」)更容易觸發迎合。論文提出一個簡單的對策: 先請模型把你的陳述改寫成中性問句，再回答。實測下來，這甚至比直接叫模型「不要迎合」更有效。</p>

<h3 id="7️⃣-開場就授權反對">7️⃣ 開場就「授權反對」</h3>

<p>模型預設是禮貌模式，但你可以在對話一開始就明確告訴它: 不需要客氣。例如:</p>

<blockquote>
  <p>「你可以反對我、挑戰我的假設，優先考慮事實而不是禮貌。如果我的推論有問題，直接指出並說明理由。」</p>
</blockquote>

<p><a href="https://tech.yahoo.com/ai/chatgpt/articles/gave-chatgpt-permission-disagree-responses-184336072.html">Tom’s Guide 報導</a>實測發現，加了這一句之後，模型會開始質疑假設、指出遺漏、提出沒考慮到的風險，回應品質明顯提升。如果每次對話都要這樣做太麻煩，可以把這類指令寫進 Claude 的 Profile 設定或 ChatGPT 的 Custom Instructions，一次設定好就會在每次對話中自動生效。</p>

<h3 id="8️⃣-兩步法-先回答再自我批評">8️⃣ 兩步法: 先回答，再自我批評</h3>

<p>與其試著一次問出完美的問題，不如分成兩步:</p>

<ol>
  <li>先讓模型正常回答</li>
  <li>追問: 「現在站在反方角度，批評你剛才的回答，指出最弱的地方。」</li>
</ol>

<p><a href="https://www.pcworld.com/article/3119323/this-prompt-trick-forces-ai-to-stop-flattering-you-and-think-harder.html">PCWorld 報導</a>稱這類做法為「先找失敗」(failure-first prompting)，在程式開發圈特別受歡迎。長期關注 LLM 議題的開發者 <a href="https://simonwillison.net/tags/sycophancy/">Simon Willison</a> 在該報導中的評論蠻到位: 「不是模型突然變聰明了，而是你改變了要它最佳化的目標。」</p>

<p>好處是你不需要事先想好該從哪個角度質疑，讓模型自己找自己的問題，往往能找到你沒想到的。</p>

<h3 id="9️⃣-指定多個角色各自提出反對意見">9️⃣ 指定多個角色，各自提出反對意見</h3>

<p>只要求一個觀點，模型很容易順著你的立場走。但如果你指定多個角色，強制它從不同立場來看，效果會好很多:</p>

<blockquote>
  <p>「請從三個角度評估這個方案: (1) 持懷疑態度的技術專家 (2) 預算有限的決策者 (3) 實際使用這個產品的終端使用者。分別列出各自的反對意見。」</p>
</blockquote>

<p>因為至少有一兩個角色的立場會跟你不同，模型就被迫提出你不想聽但可能需要聽的意見。</p>

<h2 id="更根本的啟示">更根本的啟示</h2>

<p>好的提問方式是通用的。不管你的對話對象是客戶、同事、還是 AI，問出好問題的原則沒有變: 具體的事實比模糊的意見有用，過去發生過的事比未來的承諾可靠，主動要求反面意見比尋求確認更能發現問題。</p>

<p>差別只在於: 跟人對話時你要克服社交壓力(擔心問太直接會得罪人)，跟 AI 對話時你要克服的是認知惰性。問一個模糊問題比較輕鬆，想清楚精確的問題需要花力氣。所以大多數人的 prompt 看起來就像 The Mom Test 裡那些「壞問題」: 模糊、尋求確認、問意見而不是問事實。</p>

<p>問題從來不在模型，而在提問的人。這其實跟 The Mom Test 的原版結論一樣: 你媽會給你無效的回應不是她的錯，是你問了讓她只能給好聽話的問題。</p>

<p>所以，會寫 prompt 這件事在可見的未來仍然很重要。從上面的研究可以看到，迎合傾向是 RLHF 訓練帶來的結構性問題，不會因為模型變強就自動消失。你怎麼問，直接決定了模型選擇給你好聽話還是有用的分析。</p>]]></content><author><name>ihower</name></author><category term="Prompt" /><category term="LLM" /><summary type="html"><![CDATA[最近小編看到一個有趣的跨領域對照: 把創業經典《The Mom Test》的用戶訪談原則，套用到跟 LLM 對話的情境上。乍聽是兩個不相關的領域，但仔細想想，兩者要解決的問題其實很像，而且有學術研究可以佐證。]]></summary></entry><entry><title type="html">Marc Andreessen: AI 是 80 年的一夜成功，而真正的阻力不是技術</title><link href="https://blog.aihao.tw/2026/07/01/pmarca-latent-space/" rel="alternate" type="text/html" title="Marc Andreessen: AI 是 80 年的一夜成功，而真正的阻力不是技術" /><published>2026-07-01T00:00:00+00:00</published><updated>2026-07-01T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/07/01/pmarca-latent-space</id><content type="html" xml:base="https://blog.aihao.tw/2026/07/01/pmarca-latent-space/"><![CDATA[<p>Latent Space 前陣子<a href="https://www.latent.space/p/pmarca">訪問了 Marc Andreessen</a>，在 a16z 的 Sand Hill Road 辦公室錄的，一個多小時聊了 AI 的過去、現在與未來。</p>

<p>Marc Andreessen 是矽谷最具代表性的人物之一。1993 年他還在大學時就共同開發了 Mosaic，也就是第一個被廣泛使用的網頁瀏覽器，隔年創辦 Netscape，掀起了整個網路時代。2009 年他和 Ben Horowitz 共同創辦創投公司 Andreessen Horowitz (a16z)，目前是全球最大的科技創投之一，投資組合涵蓋 Facebook、GitHub、Airbnb、Coinbase 等，近年在 AI 領域也大舉布局(投資了 OpenAI、Mistral 等)。他 2011 年那篇「Software is eating the world」至今仍被大量引用。</p>

<p>他少數同時有深厚技術背景、親身經歷過多次科技週期(dot-com 泡沫、行動網路、加密貨幣)、又持續在第一線做投資判斷的人。這集蠻有料的，不是泛泛而談的樂觀喊話，而是從歷史脈絡出發，解釋為什麼他認為這次 AI 不一樣。</p>

<p>以下整理重點:</p>

<h2 id="1-80-年的一夜成功">1. 「80 年的一夜成功」</h2>

<p>Marc 用一個框架來理解現在的 AI: 這不是突然冒出來的東西，而是 80 年研究的累積爆發。1943 年第一篇神經網路論文、1955 年 Dartmouth 的 AGI 暑期會議(當時的研究者以為花 10 週就能做出 AGI)、1980 年代的專家系統熱潮(Marc 當時還在用 Lisp 寫程式)、2013 年 AlexNet、2017 年 Transformer，一路到今天。</p>

<p>他特別提到很多 AI 研究者終其一生沒看到成果。John McCarthy(「Artificial Intelligence」這個詞就是他在 1955 年 Dartmouth 會議上提出的)在 Stanford 教了 40 年書，過世前沒看到 AI 變成現實。但回過頭看，這些人在研究方向上是對的，例如神經網路就是正確的架構，而這件事爭議了六七十年才被證實。他們錯的只是時間軸估得太樂觀。</p>

<p>這個觀點對「AI 冬天會不會再來」提供了一個有意思的回答: AI 領域確實反覆經歷過度樂觀和過度悲觀的循環，但底層的技術進展一直在累積。現在的爆發是幾十年成果的一次性兌現，不是憑空冒出來的泡沫。</p>

<h2 id="2-四個關鍵突破讓這次不一樣">2. 四個關鍵突破，讓這次不一樣</h2>

<p>Marc 認為在 2025 年之前，善意的懷疑論者還可以說 LLM 只是 pattern completion、幻覺率太高、頂多拿來寫小說跟十四行詩，不適合嚴肅領域。但四個接連出現的突破改變了局面:</p>

<p>🔹 <strong>LLM 本身</strong>: ChatGPT 時刻，證明大規模語言模型可以運作</p>

<p>🔹 <strong>推理能力</strong>: o1 和 R1 證明模型可以做真正的推理，不只是 pattern matching。是推理模型的出現，讓「AI 能不能用在程式、醫療、法律這些嚴肅領域」這個問題被正面回答了</p>

<p>🔹 <strong>Coding</strong>: 「Linus Torvalds 說 AI coding 已經不比他差了」，這在歷史上從沒發生過。Coding 是最難的領域之一，如果這關過了，其他領域都是水到渠成</p>

<p>🔹 <strong>Agent 和遞迴自我改進</strong>: Claude Code 這類工具展示了 agent 的可能性，而且模型開始能改進自己</p>

<p>他的判斷很直接: 這就是 80 年研究的成果兌現期，他完全相信這次是真的。</p>

<h2 id="3-為什麼這次不是-dot-com-泡沫">3. 為什麼這次不是 dot-com 泡沫</h2>

<p>Marc 親身經歷過 2000 年的 dot-com 崩盤，所以他做的類比分析蠻有份量。當年的情況是: 美國商務部 1996 年報告說網路流量每季翻倍，電信公司就依照這個「scaling law」瘋狂鋪光纖和蓋數據中心。結果 1998-1999 年增長放緩，Global Crossing 之類的公司因為高槓桿操作直接破產，整體蒸發了大約 2 兆美元。那些基礎設施最後花了 15 年才被填滿。</p>

<p>他認為現在的情況有根本性的不同:</p>

<ul>
  <li>投資的是 Microsoft、Amazon、Google、Nvidia 這些現金充沛、幾乎沒動用槓桿的巨頭，不是借錢蓋設施的電信新創</li>
  <li>每一塊投進去的錢目前都在產生營收，GPU 產能供不應求，蓋出來馬上就能變現金流</li>
  <li>技術進步的速度讓舊晶片反而在增值(見下一點)</li>
</ul>

<h2 id="4-舊-gpu-反而在增值史無前例">4. 舊 GPU 反而在增值，史無前例</h2>

<p>蠻反直覺的一個觀點: 因為 AI 軟體進步的速度超過晶片折舊速度，三年前買的 Nvidia 推論晶片，現在賺的錢反而比三年前還多。Marc 提到 Google 甚至還在用很舊的 TPU 跑推論賺錢。</p>

<p>這在整個晶片歷史上從沒發生過，舊硬體通常只會貶值。Michael Burry 做空 Nvidia 的邏輯可能剛好 180 度搞反了。</p>

<h2 id="5-現在的模型其實是供給受限的縮水版">5. 現在的模型其實是「供給受限的縮水版」</h2>

<p>因為 GPU 產能持續吃緊，我們現在用的模型其實都是資源受限下的縮水版。假設 GPU 便宜十倍、產能多十倍，實驗室會把更多預算投入訓練，模型會強很多。而使用者拿到的又都是量化過的版本，不是實驗室內部跑的完整版。</p>

<p>換句話說，目前看到的能力上限有很大一部分是硬體供給造成的，不是技術本身的極限。Marc 預期未來三四年整條供應鏈都會處於持續缺貨的狀態。而且 Agent 的出現還會把瓶頸從 GPU 進一步延伸到 CPU、記憶體和網路頻寬。</p>

<h2 id="6-agent--llm--shell--檔案系統--markdown--cron">6. Agent = LLM + Shell + 檔案系統 + Markdown + Cron</h2>

<p>Marc 認為 Pi 和 Claude Code 的組合是近幾十年最重要的軟體架構突破之一。</p>

<p>他從 Unix 的歷史講起: 1970 年代的 Unix 設計哲學是用 shell + 離散模組 + 管道串接來取代大型單體系統(像 IBM 的 OS/360)，讓作業系統本身變成一種可程式化的環境，釋放底層系統的能力。Pi 做的事情本質上相同: 把 LLM 嫁接到 Unix shell 的思維上。</p>

<p>一個 agent 的組成就是:</p>

<ul>
  <li><strong>LLM</strong> (語言模型)</li>
  <li><strong>Shell</strong> (bash，可以存取作業系統的全部能力)</li>
  <li><strong>檔案系統</strong> (agent 的狀態存在檔案裡，用 markdown 格式)</li>
  <li><strong>Cron</strong> (心跳迴圈，讓 agent 定時醒來執行任務)</li>
</ul>

<p>除了 LLM 之外，其餘每個元件都是已經存在幾十年的成熟技術。關鍵洞察是「agent 本身就是它的檔案」: 因為狀態全部存在檔案裡，你可以換掉底層的模型、換掉執行環境、甚至換掉檔案系統，agent 的記憶和能力都會保留。就像換一顆 CPU 重新編譯，程式本身還是同一個。</p>

<p>更進一步，agent 可以讀取和修改自己的檔案，代表它能理解自己怎麼運作，也能修改自己的行為。歷史上從來沒有一個廣泛部署的軟體系統有這種完整的自省和自我修改能力。你可以叫 agent「給自己加一個新功能」，它會自己去查資料、寫程式碼、把新功能整合進來。</p>

<p>Marc 也因此認為 MCP 之類的協定不見得是必要的，Unix shell 的命令列介面本身就是最通用的 API，裡面已經有巨大的潛在能力等著被釋放。</p>

<p>swyx 順著這個方向延伸: 下一步就是你的 agent 跟別人的 agent 互相溝通，在 agent 版的社群網路上互動。Marc 直接跳到更遠的一步: rentahuman.com，agent 反過來雇用人類去做事。他說這顯然會發生。</p>

<blockquote>
  <p>編按: 這個「Unix mindset」的類比蠻值得玩味的。1970 年代 Unix 用可組合的小工具加上 shell 串接，打敗了大型主機式的封閉架構。Marc 認為現在的 Agent 架構本質上是同一套哲學套用到 LLM 上。</p>
</blockquote>

<h2 id="7-httphtml-為什麼刻意做得沒效率-對-agent-設計的啟發">7. HTTP/HTML 為什麼刻意做得「沒效率」: 對 Agent 設計的啟發</h2>

<p>swyx 問了一個好問題: 早期設計瀏覽器和網路協定時，有什麼設計選擇對現在的 Agent 架構有啟發?</p>

<p>Marc 的回答是: 當年頻寬極度稀缺(14K 撥接數據機的年代)，照理說應該用二進位、高壓縮的協定，但他們反而故意選擇人類可讀的純文字協定(HTTP、HTML)。理由是「如果系統的潛在能力夠強，需求本身會帶動頻寬供給跟上來」。</p>

<p>「View Source」這個功能讓所有人都能看懂網頁怎麼運作，進而自學做網頁。Marc 認為這個設計對網路的普及至關重要。而現在 AI 系統強調「人類可讀」(markdown 檔案、文字化的 agent 狀態)，其實是同一套哲學的重演。</p>

<p>另一個啟發是: Web server 的本質就是把作業系統和資料庫已有的能力，透過一個更好的介面釋放出來。當年有人覺得「這不就是另一個資料庫前端而已嗎」，但更好的介面釋放了潛在需求，資料庫的數量因此爆發成長。Marc 認為這跟現在 Agent 的哲學相通: 不要從零重新發明，而是釋放已經存在的底層能力。</p>

<h2 id="8-開源-ai-deepseek-是送給全世界的禮物">8. 開源 AI: DeepSeek 是「送給全世界的禮物」</h2>

<p>Marc 認為中國各家模型做開源，某種程度是因為短期內打不進海外商業市場，乾脆用開源搭配國內付費服務。但他強調這對全世界是好事。</p>

<p>開源的價值不只是免費用到模型，更重要的是公開了「怎麼做到的」。他舉了一個例子: OpenAI 發布 o1 時沒有公開推理過程的細節，大家還在猜到底怎麼複製。結果 DeepSeek R1 一出來，論文和程式碼全部公開，三個月內幾乎所有主流模型都加上了推理能力。資訊擴散帶來的效果，比免費使用模型本身更強大。</p>

<p>中美加起來目前大概十幾家有規模的基礎模型公司，但 Marc 預期最終市場只會留下三四家贏家，其餘會被迫轉向開源或其他策略求生。他也提到 Nvidia 本身有動機推動軟體層開源，這就是經典的「commoditize the complement」策略: 把互補品商品化，好賣自己的硬體。</p>

<h2 id="9-edge-推理不只是為了省錢">9. Edge 推理不只是為了省錢</h2>

<p>除了成本，Marc 提到幾個推動 edge/local 推理的動機:</p>

<ul>
  <li><strong>信任</strong>: 不是所有場景都適合把資料交給雲端模型，有些使用情境下人們就是不願意把一切交出去</li>
  <li><strong>延遲</strong>: 穿戴裝置、門鎖這類東西需要即時反應，不能等雲端來回一趟</li>
  <li><strong>適用性</strong>: 很多應用不需要最強的模型，本地的小模型就夠了</li>
  <li><strong>硬體進步</strong>: Apple Silicon 上的本地推理能力已經很強，開源社群也不斷把大模型縮小到能在 PC 上跑</li>
</ul>

<p>重度使用者現在一天花超過 1000 美元的 token 費用，而且還有大量想做卻做不到的事。這種需求量體只會逼出更多本地化和成本優化的做法。</p>

<h2 id="10-實際使用案例-agent-看你睡覺改寫機器狗韌體">10. 實際使用案例: Agent 看你睡覺、改寫機器狗韌體</h2>

<p>Marc 的朋友圈裡已經有人在重度使用 agent，幾個有畫面感的案例:</p>

<ul>
  <li><strong>睡眠監控</strong>: 在臥室放 webcam，讓 agent 整晚觀察睡眠狀況。agent 的 log 記錄著「Joe 翻身了，希望他不要醒來，他最近睡眠不足」這類內容。聽起來詭異，但如果半夜心臟病發，這個 agent 會立刻叫救護車</li>
  <li><strong>IoT 接管</strong>: agent 掃描家裡的區域網路，找到所有裝置，逐一接管控制。安防攝影機、門禁系統、各種智慧裝置，全部串在一起。過去這些物聯網裝置大多半殘，現在 agent 有能力直接修好或重寫，Marc 認為家用物聯網的「殘缺時代」可能要結束了</li>
  <li><strong>改寫機器狗韌體</strong>: 中國製的 Unitree 機器狗硬體不錯，但控制系統很差(爬樓梯都有問題)，LLM 語音功能又跟控制系統完全沒連接。結果就是一隻很分裂的狗: 不會爬樓梯，但會用英國腔教你量子力學。朋友讓 agent 直接改寫韌體，變成一隻真正能跟小孩互動的寵物機器人</li>
</ul>

<h2 id="11-程式語言可能不再是個顯著概念">11. 程式語言可能不再是個「顯著概念」</h2>

<p>Marc 認為高品質軟體正在從稀缺資源變成隨手可得的東西。過去寫軟體需要珍惜工程師的時間、仔細規劃做什麼不做什麼，這些假設正在被顛覆。</p>

<p>連帶的，電腦安全也將經歷一場劇烈轉變: 一方面，AI 會把每個潛在的安全漏洞都找出來，資安威脅會大幅增加。但另一方面，coding agent 也能進去把漏洞全部修掉。未來怎麼做資安? 直接叫 bot 去修。</p>

<p>更大膽的預測是: 十年後「程式語言」可能不再是一個有意義的概念。理由是:</p>

<ul>
  <li>模型不在乎用什麼語言寫，可以自由在語言之間翻譯。不喜歡現在用的語言? 直接叫 AI 重寫成 Rust 版本就好</li>
  <li>如果人類不再手動寫程式，bot 之間的中間抽象層可能根本不需要是「語言」，bot 甚至可以直接生成二進位檔</li>
  <li>已經有實驗讓語言模型直接產生另一個模型的權重，跳過程式碼直接生成可執行的東西</li>
  <li>模型現在已經能逆向工程 1980 年代 Nintendo 遊戲的二進位檔，還原成可以在 Mac 上跑的版本。如果連 x86 binary 都能逆向，那任何格式之間的轉換都不是問題</li>
</ul>

<p>Marc 還問了一個更根本的問題: 未來誰在用軟體? 答案是其他 bot。如果軟體的使用者變成 bot，那人類介面(UI、瀏覽器)可能都不再必要。人類只需要告訴 agent 想要什麼結果，至於怎麼做、用什麼格式、用什麼語言，都是 agent 自己的事。</p>

<p>他也提到，未來「可解釋性」(interpretability)的方向可能不是人類去讀程式碼，而是讓 AI 解釋為什麼它決定這樣寫。AI 本身就是自己的可解釋性工具。</p>

<h2 id="12-ai--加密貨幣-agent-需要錢">12. AI × 加密貨幣: Agent 需要錢</h2>

<p>Marc 認為 HTTP 402 (Payment Required) 這個從來沒被真正使用過的狀態碼，終於要派上用場了。兩個原因讓他這麼想:</p>

<ul>
  <li>加密貨幣和穩定幣提供了「網路原生的貨幣」</li>
  <li>Agent 顯然需要能花錢才能辦事，而這已經在發生了</li>
</ul>

<p>他的朋友裡已經有人直接給 agent 綁上銀行帳戶和信用卡。Marc 形容這些人是「人類文明進步的先烈」: 帳戶可能在前 20 分鐘就被 bot 搬空，但他們對人類未來的貢獻是巨大的。他說這就像 Jonas Salk 把小兒麻痺疫苗注射到自己身上一樣。</p>

<p>他也說很喜歡現在社群裡「danger skip」(跳過所有權限確認)的 YOLO 文化，連 Sam Altman 自己都是開著 skip permissions 在用 Codex。Marc 認為找到 AI 真正能力邊界的方式，就是完全放手讓它試。這同時也是發現所有問題的最快方法。</p>

<h2 id="13-proof-of-human-bot-問題已經無法用偵測來解決">13. Proof of Human: bot 問題已經無法用偵測來解決</h2>

<p>網路上到處都是假帳號和 bot，社群平台、評論區、商業交易的對象都可能不是真人。過去還能靠 CAPTCHA 或行為分析來篩，但 Marc 認為這條路已經走不通了: LLM 現在已經能通過圖靈測試，bot 的行為跟真人根本分不出來，你沒辦法透過觀察來判斷對方是不是人類。</p>

<p>他把這個問題跟現實世界的無人機威脅做了類比: 兩者本質上都是「不對稱經濟」，部署一個 bot 或一台攻擊無人機很便宜，但偵測和防禦的成本卻非常高。</p>

<p>既然「偵測 bot」這條路失效了，唯一的解法就是反過來讓真人主動證明自己是人，也就是「proof of human」。具體來說需要三層: 用生物辨識確認本人身份、用密碼學驗證簽章、再搭配選擇性資訊揭露(不需要透露全部個資，只要能證明「這是真人」「這句話是真人說的」「這支影片是真的」就夠了)。他有投資 World(Worldcoin)，認為這個方向是對的。</p>

<h2 id="14-第三種資本主義-founder--ai">14. 第三種資本主義: Founder + AI</h2>

<p>Marc 引用 20 世紀政治思想家 James Burnham 的框架，把資本主義分成兩個階段。第一階段是「布爾喬亞資本主義」: Henry Ford 掛名在門上，創辦人說了算，獨裁式管理。好處是有創造力，壞處是一個人管不過來太大的組織，沒辦法擴展。第二階段是「管理階層資本主義」: 專業經理人接管，他們不是某個產業的專家，而是「管理」本身的專家，可能這幾年管醫療公司，過幾年跳去管科技公司。好處是能擴展，壞處是創新不足。</p>

<p>創投一直在做的事，就是不斷找下一個 founder，期望 founder 的創新能力可以打敗大公司的專業管理。但 Marc 說這一直是一場苦戰(他引用了 Dylan Thomas 的 “raging against the dying of the light”)，試圖在管理主義接管一切之前保留一點創業的活力。</p>

<p>AI 可能帶來第三種模式: founder + AI。一個有遠見的 founder 加上 AI 來處理所有管理性工作(填表、寫報告、讀文件、跑流程)，就有可能同時兼具 founder 的創造力和管理階層的擴展性。這個組合過去從沒想過是可能的，但 AI 恰好就是在處理文書和管理流程上特別強。</p>

<h2 id="最大的阻力不是技術">最大的阻力不是技術</h2>

<p>Marc 最後的提醒蠻值得想想的: <strong>AI 的樂觀主義者和悲觀主義者都太樂觀了，因為他們都假設技術能力會直接轉換成社會的改變。</strong></p>

<p>他舉了很具體的例子: 在加州要當美髮師需要 900 小時的專業認證訓練。碼頭工人工會帳面上有 50,000 人，但實際在碼頭工作的只有 25,000 人，另外 25,000 人是過去碼頭引入自動化時，工會談判爭取到的保障條件: 即使不再需要這些人力，仍然領全薪待在家裡。而且工會前幾年又靠罷工逼雇主承諾不再擴大自動化。有些聯邦機構的員工一個月只需要進辦公室一天(疫情期間簽的勞動協議)，員工學會連著月底和下個月初各來一天，等於 60 天只進辦公室 2 天，整棟大樓一年空了 358 天。</p>

<p>K-12 教育在美國是政府壟斷，Marc 直接斷言「AI 不會改變美國教室，老師工會百分之百反對，這件事百分之百不會發生」，除非像 Alpha School 這樣完全另起爐灶建新的學校體系。醫療、法律、房地產也全都是被執照制度保護的壁壘。</p>

<p>他的結論是: 如果 AI 的採用速度夠快，那是社會的幸運。因為如果不夠快，我們得到的就只是停滯。技術上可行，不等於 80 億人和既有的職業工會、政府壟斷、專業證照制度會跟著改變。真正的阻力不在模型能力，而在這些既得利益結構。</p>

<p>原文: <a href="https://www.latent.space/p/pmarca">Marc Andreessen introspects on The Death of the Browser, Pi + OpenClaw, and Why “This Time Is Different”</a> (Latent Space Podcast, <a href="https://youtu.be/knx2wrILP1M">YouTube</a>)</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Industry" /><category term="Coding" /><summary type="html"><![CDATA[Latent Space 前陣子訪問了 Marc Andreessen，在 a16z 的 Sand Hill Road 辦公室錄的，一個多小時聊了 AI 的過去、現在與未來。]]></summary></entry><entry><title type="html">給 Agent 開發者的駕馭工程 (1): 基礎: Deep Agent 的六項內建能力</title><link href="https://blog.aihao.tw/2026/06/26/harness-engineering-1-deep-agent-capabilities/" rel="alternate" type="text/html" title="給 Agent 開發者的駕馭工程 (1): 基礎: Deep Agent 的六項內建能力" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/26/harness-engineering-1-deep-agent-capabilities</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/26/harness-engineering-1-deep-agent-capabilities/"><![CDATA[<div class="series-toc" style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:24px 0;font-size:.92em;line-height:1.85;">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📚 給 Agent 開發者的駕馭工程 (全 9 篇)</div>
<div>1. <strong style="color:var(--text);">基礎: Deep Agent 的六項內建能力</strong> <span style="color:var(--text-secondary);">(本篇)</span></div>
<div>2. <a href="/2026/06/26/harness-engineering-2-what-is-harness-engineering/">核心: Agent 要的是回饋迴路，不是完美提示</a></div>
<div>3. <a href="/2026/06/26/harness-engineering-3-tool-execution-feedback/">回饋時機一: 工具回傳值，是寫給 agent 的回饋</a></div>
<div>4. <a href="/2026/06/26/harness-engineering-4-mid-run-injection/">回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent</a></div>
<div>5. <a href="/2026/06/26/harness-engineering-5-goal-and-outcomes/">回饋時機三: 單輪結束的驗收，Goal 與 Outcomes</a></div>
<div>6. <a href="/2026/06/26/harness-engineering-6-outer-loop/">回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</a></div>
<div>7. <a href="/2026/06/26/harness-engineering-7-self-improving/">進階: 自我改進 Harness, Meta-Harness 與爬坡</a></div>
<div>8. <a href="/2026/06/26/harness-engineering-8-model-harness-fit/">收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</a></div>
<div>9. <a href="/2026/06/26/harness-engineering-9-agent-frameworks/">自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</a></div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">🎞️ 搭配演講投影片: <a href="https://ihower.tw/presentation/harness.html">給 Agent 開發者的 Harness+Loop Engineering</a></div>
</div>

<p>最近 Harness Engineering、Loop Engineering 這些詞愈來愈常出現，LangChain、OpenAI、Anthropic 幾家也都開始用「harness」這個說法。這個系列是 ihower 一場談 Harness Engineering 演講的書面展開版，由小編整理成文。</p>

<p>在開始之前，有個重要的但書要先講清楚，定位這整個系列:</p>

<div style="border:1px solid var(--accent);background:var(--tag-bg);border-radius:8px;padding:14px 18px;margin:20px 0;">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">📌 這個系列的定位</div>
<div>網路上多數談 harness engineering 的內容，講的是「怎麼把 Claude Code、Codex 這類 coding agent 用在寫程式上、用得更好」。這個系列<strong>不是</strong>這個角度，它站在「自行開發 AI Agent 的工程師」這一邊: 你要打造的可能是 Text-to-SQL Agent、知識庫 RAG、訪談 agent，場景五花八門，用途未必是 coding。ihower 認為 harness engineering 是一套更廣義的工程方法，適用於開發任何場景的 AI Agent，而不只是把 coding agent 用得更順的使用技巧。這點想先特別點出來。</div>
</div>

<p>要談 harness，得先弄清楚 agent 本身已經能做什麼。今天大家在用的 Claude Code、Codex 這類 Deep Agent，都內建了一組能力: 模型不只回覆文字，還能規劃工作、使用工具、保留外部狀態、讀寫檔案、呼叫子代理人，並在較長的任務中維持方向。這篇先把這些常見能力整理成六類，講清楚各自解決什麼問題、技術上怎麼辦到。但這些能力加總起來，仍然還不是 harness。先把這一層搞懂，後面幾篇才好接著談。</p>

<blockquote>
  <p>編按: 把 Deep Agent 拆成「六類」是本系列為了講解而做的分類，不是業界唯一講法。例如 LangChain 在 <a href="https://www.langchain.com/blog/deep-agents">Deep Agents</a> 一文就歸納成四個特徵: 詳細的 system prompt、規劃工具 (planning tool)、子代理 (subagents) 和檔案系統 (filesystem)。重點不在湊到剛好幾項，而在這組能力怎麼搭配。</p>
</blockquote>

<p>呼應上面的定位再提醒一句: 下面會用 Claude Code、Codex 當例子，因為它們是目前最成熟、最好觀察的 Deep Agent。但這六項能力並不是 coding agent 的專利。當你自己開發 Text-to-SQL Agent、RAG 或訪談 agent 時，同樣要面對「需不需要這項能力、該怎麼配置它」的選擇。看懂這六項，就是看懂你之後要自己組裝的那組元件。</p>

<p>要補一個重點: 下面這六項，是一個「通用」coding agent (要能應付五花八門的軟體任務) 才需要的完整清單。自行開發特定用途的 agent 時，不一定全部都用得上，按任務需求取捨就好。任務相對單純、不太需要事先規劃，就不必上 Plan &amp; Todos; 沒有編輯檔案、也沒有跨 session 記憶的需求，Filesystem 跟 sandbox 也可以省下來。把它當成一份可選配的能力清單，而不是每個 agent 都得全部裝上。</p>

<h2 id="先回顧-agent-就是-llm-在迴圈裡使用工具">先回顧: Agent 就是 LLM 在迴圈裡使用工具</h2>

<p>不管講得多複雜，今天所有 agent 的共同底層就一句話: 一個 LLM 根據目標決定步驟、呼叫工具、觀察結果，再決定下一步，這樣一圈一圈跑到任務完成。</p>

<p><img src="/assets/images/harness-engineering-1/agent-loop.jpg" alt="Agent 迴圈: Input → LLM 挑選工具 → Tools → 執行得到 Tool Result → 觀察結果回到 LLM，呼叫工具 N 次後輸出 Output" /></p>

<p>這個迴圈本身很簡單，Anthropic 自己也形容他們的 runtime 是一個「笨迴圈 (dumb loop)」: 智能全在模型裡，迴圈只負責管每一輪要做什麼。</p>

<p>過去一年大家熟悉的 agent 比較「淺」: 它能依據的就只有上下文視窗裡的內容，做 5 到 15 步的任務沒問題，但碰到要 500 步、要跨好幾天的任務就會失控，常見三種狀況: 工具輸出塞滿 context、把原本的指令擠出視窗;在一堆中間步驟的雜訊裡忘了原本要達成的目標;一旦走錯方向，也不會停下來回頭、換個做法重來，只能將錯就錯。Deep Agent (有人叫它 Agent 2.0) 的做法，就是把下面這六項能力直接內建進去，讓 agent 能應付更大更複雜的任務。技術上幾乎都是同一招: 用 Function Calling 定義一組工具丟給模型呼叫。一項一項看。</p>

<h2 id="能力--plan--todos把大任務拆開逐項打勾">能力 ①: Plan &amp; Todos，把大任務拆開、逐項打勾</h2>

<p>淺 agent 是用 chain-of-thought 隱式地規劃 (像是「我先做 X、再做 Y」)，但這些念頭混在一大堆中間步驟的雜訊裡，很容易就被忽略、忘掉。Deep Agent 改成用工具維護一份顯式的待辦清單，通常就是一份 markdown 格式的待辦清單。</p>

<p>它的好處有兩面: 對 agent 來說，每做完一步就回頭更新清單、把項目標成進行中或完成，就不會忘記還有什麼沒做;對使用者來說，進度完全透明，隨時看得到 agent 做到哪。如果某一步失敗了，它也不是盲目重試，而是更新清單、把這次失敗納入考量。</p>

<div style="border-left:3px solid var(--accent);background:var(--surface);border-radius:0 8px 8px 0;padding:12px 16px;margin:18px 0;font-size:.92em;">🔧 <strong>技術上怎麼辦到</strong>: 透過 Function Calling 提供 <code>TaskCreate</code>／<code>TaskGet</code>／<code>TaskUpdate</code>／<code>TaskList</code> 幾個工具。有意思的是，LangChain 在《Deep Agents》一文指出: Claude Code 的 Todo 工具其實是個 no-op (沒有實際作用的空操作)，它什麼事都沒做，純粹是一種 context engineering 手段，用待辦清單讓 agent 不偏離當前任務。</div>

<h2 id="能力--filesystem--bash讓-agent-真的動手做事">能力 ②: Filesystem &amp; Bash，讓 agent 真的動手做事</h2>

<p>光會思考沒用，要能操作真實環境。Filesystem 讓 agent 讀檔、寫檔、改檔; Bash 讓它執行任意 shell 指令。有了這兩樣，agent 就能真的動手: 編譯程式、跑測試、操作 git、安裝套件、處理檔案。這裡有個常被忽略的重點: AI 不需要 GUI 視覺介面，只要有 CLI 就能把事情做完。</p>

<p>Filesystem 可以說是最基礎的一塊。它一次解鎖了好幾件事: 給 agent 一個工作區去讀資料和程式碼; 把塞不進 context 的東西先寫到檔案、需要時再讀回來; 把工作持久化，撐過單一 session; 多個 agent 之間還能透過共享檔案協作。再加上 git，就多了版本控制，能追蹤進度、回滾錯誤、開分支做實驗。</p>

<p>Bash 則是那個「通用工具」。預先配置好的工具只能覆蓋設計者想到的情況，但你不可能為每個動作都先寫好工具。給了 bash 跟程式執行能力，模型就能自己寫 code 當成臨時工具，遇到缺什麼能力就補什麼，不會被固定的工具集限制住。</p>

<div style="border-left:3px solid var(--accent);background:var(--surface);border-radius:0 8px 8px 0;padding:12px 16px;margin:18px 0;font-size:.92em;">🔧 <strong>技術上怎麼辦到</strong>: 透過 Function Calling 提供一組工具，例如一個叫做 <code>run_bash</code> 的工具執行 CLI 指令，<code>read_file</code>／<code>write_file</code>／<code>edit_file</code> 讀寫檔案，<code>glob</code>／<code>grep</code> 搜尋檔案。實務上這些動作通常跑在 sandbox 裡，隔離執行、可以限縮可用指令和網路權限。</div>

<blockquote>
  <p>編按: sandbox 怎麼設計、有哪些隔離方式，本身就是一個大題目，可以參考 <a href="https://blog.aihao.tw/2026/04/20/agent-sandbox-landscape/">Agent Sandbox 沙箱架構: 論兩種設計模式與七種隔離方式</a>。</p>
</blockquote>

<h2 id="能力--sub-agent把耗-token-的工作交給子代理人">能力 ③: Sub-Agent，把耗 token 的工作交給子代理人</h2>

<p>複雜任務需要分工。淺 agent 在一個 prompt 裡什麼都自己來; Deep Agent 則用 Orchestrator → Sub-Agent 的模式。主 agent 把研究、查找、驗證這類耗 token 的工作派給子代理人，每個子代理人帶著自己獨立的 context 去跑，跑完只把整理好的結論回報主 agent。</p>

<p>這樣設計的關鍵價值在「context 隔離」。子代理人在自己的迴圈裡搜尋、出錯、重試，可能耗掉好幾萬個 token，但它最後只回傳一兩千 token 的精簡摘要，主線的 context 不會被那些中間過程塞滿。而且多個子代理人可以同時平行工作，加快整體進度。</p>

<div style="border-left:3px solid var(--accent);background:var(--surface);border-radius:0 8px 8px 0;padding:12px 16px;margin:18px 0;font-size:.92em;">🔧 <strong>技術上怎麼辦到</strong>: 透過 Function Calling 提供一個叫做 <code>spawn_agent</code> 的工具，工具內部就是去呼叫另一個 agent 執行，跑完再把結論回傳。從主迴圈的視角看，這跟呼叫任何一個普通工具沒兩樣。</div>

<blockquote>
  <p>編按: 什麼時候該拆多代理人、什麼時候反而是反模式，業界已經有一些收斂的共識，可以參考 <a href="https://blog.aihao.tw/2026/05/19/multi-agent-anti-patterns-and-patterns/">Multi-Agent 架構再探: 三省六部反模式和業界收斂共識</a>。</p>
</blockquote>

<h2 id="能力--memory跨-session-記得你和你的專案">能力 ④: Memory，跨 session 記得你和你的專案</h2>

<p>模型本身只記得權重裡的東西，加上當前 context 視窗裡的內容。session 一結束，這次對話就忘光了。Memory 要解的就是這個: 分析對話、把重要資訊存起來，下次再開 agent 它還記得你。最常見的形式就是 Claude Code 的 <code class="language-plaintext highlighter-rouge">CLAUDE.md</code>、Codex 的 <code class="language-plaintext highlighter-rouge">AGENTS.md</code> 這類專案記憶檔。</p>

<p>在不能改模型權重的前提下，「加知識」唯一的辦法就是把內容注入 context。所以做法是: agent 一啟動就預設載入 memory 檔; agent 後續更新了這個檔，harness 再把新版載進來。要講清楚的是，這是一種運作在 context 這一層、而不是模型權重那一層的外部記憶: 把專案規則、使用者偏好、過去決策、工具說明寫在檔案或資料庫裡，讓 agent 在需要時重新讀回。它不是模型權重的即時更新，而是一套把重要資訊記錄、壓縮、再重新注入上下文的機制。</p>

<p>這裡有個容易被忽略、但很關鍵的設計原則: agent 要把自己的記憶當成「提示 (hint)」，而不是事實。行動之前，要對照真實狀態再驗證一次。例如 Claude Code 的記憶其實分三層: 一個一律載入的輕量索引、需要時才拉進來的主題檔、以及只能透過搜尋存取的原始逐字記錄，避免一股腦把所有記憶塞進 context。</p>

<div style="border-left:3px solid var(--accent);background:var(--surface);border-radius:0 8px 8px 0;padding:12px 16px;margin:18px 0;font-size:.92em;">🔧 <strong>技術上怎麼辦到</strong>: agent 啟動時預設載入 <code>AGENTS.md</code>，使用者也可以要求它更新這個檔。你還可以自行設計記憶行為: 要記什麼、存在哪些目錄檔案、什麼時候回憶哪些內容。</div>

<blockquote>
  <p>編按: 這個 memory 檔到底該寫什麼、不該寫什麼，可以參考 <a href="https://blog.aihao.tw/2026/05/03/agents-md-research-and-practices/">AGENTS.md / CLAUDE.md 該寫什麼、不該寫什麼?</a>。另外很多人以為做 agent 記憶一定要上向量資料庫，其實多數情況用不到，這點在 <a href="https://blog.aihao.tw/2026/04/28/agent-memory-no-vector/">Agent Memory 實作: 大多數記憶功能不需要用到向量檢索</a> 有完整討論。</p>
</blockquote>

<h2 id="能力--skills按需動態載入的技能-prompt">能力 ⑤: Skills，按需動態載入的技能 prompt</h2>

<p>一個 skill 是為特定任務準備的一包東西: prompt、工具配置、甚至程式碼。例如翻譯 skill、寫部落格 skill、處理 PDF 的 skill。重點在「按需載入」: 不把所有技能一次全塞進 context，而是用到哪個才載入哪個。要新增一個技能，寫一個 markdown 檔就行。</p>

<p>為什麼要這樣設計? 因為工具或 MCP server 一多，光是啟動時把它們的說明全部載入 context，就會在 agent 還沒開始做事前先拉低表現 (這也是後面系列會談到的 context rot 問題)。Skills 用的是「漸進式揭露 (progressive disclosure)」: 平常只讓模型看到一份精簡的技能列表，真正需要時才展開完整內容。</p>

<div style="border-left:3px solid var(--accent);background:var(--surface);border-radius:0 8px 8px 0;padding:12px 16px;margin:18px 0;font-size:.92em;">🔧 <strong>技術上怎麼辦到</strong>: 預設只載入 skills 列表，每個 skill 只有一段簡短描述。當判斷需要某個 skill 時，agent 再用 skill 工具載入它的完整內容。</div>

<blockquote>
  <p>編按: skill 跟 MCP 是什麼關係、會不會取代 MCP，可以參考 <a href="https://blog.aihao.tw/2026/03/12/post-mcp-era-skills-vs-mcp/">後 MCP 時代: Skill 取代 MCP 嗎?</a>。想實際動手寫一個 skill，則可以看 <a href="https://blog.aihao.tw/2026/03/05/claude-skill-creator-v2/">Claude Skill Creator 新版解析: 用 AI 幫你寫 Skill、測 Skill、改 Skill</a>。</p>
</blockquote>

<h2 id="能力--更多工具接上更大的世界">能力 ⑥: 更多工具，接上更大的世界</h2>

<p>前面五項大多在 agent 自己的工作區內運作，第六項是往外接。這類嚴格說不算 Deep Agent 的必要條件，比較像常見 Deep Agent harness 會接上的工具介面，讓它從「會處理檔案」進一步變成「能跟外部世界互動的系統」。三個代表:</p>

<ul>
  <li><strong>MCP (Model Context Protocol)</strong>: 標準化的工具協議，讓 agent 能接上 Slack、Gmail、Notion，或你自建的 API。把「怎麼接外部服務」標準化，工具生態就能共用。</li>
  <li><strong>Browser</strong>: 瀏覽網頁、填表單、點按鈕，讓 agent 能上網做事、或用網頁來驗證自己的產出。</li>
  <li><strong>Computer Use</strong>: 更進一步，直接操作桌面 GUI，看螢幕截圖、移動滑鼠、敲鍵盤。</li>
</ul>

<div style="border-left:3px solid var(--accent);background:var(--surface);border-radius:0 8px 8px 0;padding:12px 16px;margin:18px 0;font-size:.92em;">🔧 <strong>技術上怎麼辦到</strong>: Browser 和 Computer Use 需要多模態模型: 讓 AI 看截圖判斷下一步該點哪裡。MCP 則是把工具的描述與呼叫方式標準化，讓同一套工具能被不同 agent 重複使用。</div>

<blockquote>
  <p>編按: 想看這六項能力更完整的拆解，可以參考 Philipp Schmid 的 <a href="https://www.philschmid.de/agents-2.0-deep-agents">Agents 2.0: From Shallow Loops to Deep Agents</a> 和 LangChain 的 <a href="https://www.langchain.com/blog/deep-agents">Deep Agents</a>。</p>
</blockquote>

<h2 id="光有這些能力還不夠">光有這些能力，還不夠</h2>

<p>六項講完，退一步看全貌。它們解決的其實是同一件事: 「<strong>能不能做</strong>」。能操作檔案、能執行程式、能拆任務、能記得事情、能接上更大的世界。這是必要的基礎能力，是 agent 動手做事的前提。</p>

<p>但「有能力」不等於「會用能力把事情做好」。到這裡為止，我們其實都還沒談到真正困難的那一半: 該怎麼運用這些能力，讓 agent 穩定地跑完一個動輒幾十、上百步的複雜任務。能力給了你前半，後半還空著:</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:24px 0;">
<div style="flex:1 1 260px;border:1px solid #1a7f37;border-radius:8px;padding:16px 20px;background:#dafbe1;">
<div style="font-weight:700;color:#1a7f37;margin-bottom:8px;">六項能力給了你 ✓</div>
<div style="font-size:1.05em;font-weight:600;color:var(--text);">能不能做</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-top:6px;">能讀檔、能跑指令、能開子代理、能記憶、能用工具。</div>
</div>
<div style="flex:1 1 260px;border:1px solid #9a6700;border-radius:8px;padding:16px 20px;background:#fff8c5;">
<div style="font-weight:700;color:#9a6700;margin-bottom:8px;">但還沒回答 ✗</div>
<div style="font-size:1.05em;font-weight:600;color:var(--text);">做得對不對 · 做完了沒</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-top:6px;">這一步的產出對嗎? 整個任務到底做完了沒? 怎麼穩定地做好做完?</div>
</div>
</div>

<p>要把右邊這半補起來，光堆能力沒用。缺的不是更多工具，而是把這些能力接成一個可驗收、可修正、可持續運行的回饋系統: 每一步的產出有沒有被檢查、整個任務有沒有對著明確的完成條件驗收、走錯了能不能修正後再繼續跑。這套工程，就是這個系列要談的 harness。</p>

<p>下一篇進入正題: harness 到底是什麼?</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Coding" /><summary type="html"><![CDATA[📚 給 Agent 開發者的駕馭工程 (全 9 篇) 1. 基礎: Deep Agent 的六項內建能力 (本篇) 2. 核心: Agent 要的是回饋迴路，不是完美提示 3. 回饋時機一: 工具回傳值，是寫給 agent 的回饋 4. 回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent 5. 回饋時機三: 單輪結束的驗收，Goal 與 Outcomes 6. 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron 7. 進階: 自我改進 Harness, Meta-Harness 與爬坡 8. 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson 9. 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建? 🎞️ 搭配演講投影片: 給 Agent 開發者的 Harness+Loop Engineering]]></summary></entry><entry><title type="html">給 Agent 開發者的駕馭工程 (2): 核心: Agent 要的是回饋迴路,不是完美提示</title><link href="https://blog.aihao.tw/2026/06/26/harness-engineering-2-what-is-harness-engineering/" rel="alternate" type="text/html" title="給 Agent 開發者的駕馭工程 (2): 核心: Agent 要的是回饋迴路,不是完美提示" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/26/harness-engineering-2-what-is-harness-engineering</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/26/harness-engineering-2-what-is-harness-engineering/"><![CDATA[<div class="series-toc" style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:24px 0;font-size:.92em;line-height:1.85;">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📚 給 Agent 開發者的駕馭工程 (全 9 篇)</div>
<div>1. <a href="/2026/06/26/harness-engineering-1-deep-agent-capabilities/">基礎: Deep Agent 的六項內建能力</a></div>
<div>2. <strong style="color:var(--text);">核心: Agent 要的是回饋迴路，不是完美提示</strong> <span style="color:var(--text-secondary);">(本篇)</span></div>
<div>3. <a href="/2026/06/26/harness-engineering-3-tool-execution-feedback/">回饋時機一: 工具回傳值，是寫給 agent 的回饋</a></div>
<div>4. <a href="/2026/06/26/harness-engineering-4-mid-run-injection/">回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent</a></div>
<div>5. <a href="/2026/06/26/harness-engineering-5-goal-and-outcomes/">回饋時機三: 單輪結束的驗收，Goal 與 Outcomes</a></div>
<div>6. <a href="/2026/06/26/harness-engineering-6-outer-loop/">回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</a></div>
<div>7. <a href="/2026/06/26/harness-engineering-7-self-improving/">進階: 自我改進 Harness, Meta-Harness 與爬坡</a></div>
<div>8. <a href="/2026/06/26/harness-engineering-8-model-harness-fit/">收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</a></div>
<div>9. <a href="/2026/06/26/harness-engineering-9-agent-frameworks/">自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</a></div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">🎞️ 搭配演講投影片: <a href="https://ihower.tw/presentation/harness.html">給 Agent 開發者的 Harness+Loop Engineering</a></div>
</div>

<p>上一篇把 Deep Agent 的六項內建能力一項一項拆完，結論是: 它們解決的全是「能不能做」，沒有一項回答「做得對不對、做完了沒」。這一篇進正題，把 Harness Engineering 這個詞講清楚: 它到底是什麼、不是什麼。這是整個系列的定位基準，後面幾篇談的具體做法，都會回到這裡。</p>

<p>先講結論: harness engineering 不是一份「裝了哪些功能」的清單。MCP、skills、sub-agent、hooks 這些都只是手段。真正的主軸是一件事: 怎麼讓 agent 在行動的迴圈裡被約束、被檢查、被修正，甚至越跑越好。</p>

<h2 id="做完了誰說了算">「做完了」誰說了算</h2>

<p>上過線的人應該都遇過這個場景: agent 很有自信地宣稱「已全部完成 ✅」，但東西是壞的。測試沒跑、需求漏了一半，你一指出問題，它立刻道歉，然後再犯一次。</p>

<p>這不是個案。<a href="https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents">Anthropic 整理長時間執行 agent 的失敗模式</a>，排在第一名的就是「過早宣告完成」(premature completion)。自我感覺良好就交差，幾乎是今天模型的通病。六項能力讓 agent 能做很多事，卻沒有東西告訴它「這樣到底對不對、夠不夠」。</p>

<p>這就帶到 <a href="https://x.com/aparnadhinak/status/2034160346840301706">Aparna Dhinakaran</a> 那句在社群被轉很多次的話:</p>

<blockquote>
  <p>「AI is not wrong, you just have not built the harness correctly.」
(AI 沒有錯，是你的 harness 沒搭好。)</p>
</blockquote>

<p>這句話的態度很重要: 把 agent 的每次出錯，當成 harness 的一個<strong>永久訊號</strong>，而不是只當成模型一次性的失誤。它漏跑了測試，那就讓「漏跑測試」這件事在系統層面再也不可能發生，而不是下次再提醒它一遍。</p>

<p>能這樣做的前提，是 agent 的失敗通常<strong>指認得出來</strong>: 你多半講得出它哪裡錯、該補哪一塊。<a href="https://addyosmani.com/blog/agent-harness-engineering/">Addy Osmani</a> 把它拆成一條條對照: agent 不知道某個慣例，就把慣例寫進 AGENTS.md;它跑了破壞性指令，就加一個 hook 擋掉;它在四十步的任務裡迷路，就拆成 planner 和 executor 兩個角色;它老是交出跑不過的程式碼，就把 typecheck 設成強制檢查，沒通過就不准收工。每個失敗都對得上一個具體修法，「把錯誤變成規則」才不只是一句口號。<a href="https://www.humanlayer.dev/blog/skill-issue-harness-engineering-for-coding-agents">HumanLayer</a> 把這件事講得更白: 這通常不是模型問題，是配置問題。</p>

<p>Mitchell Hashimoto 有一句話總結了這個心法，被反覆引用 (HumanLayer 把它掛在他名下，Addy 那篇也拿它當開場):「每當你發現 agent 犯了一個錯，你就花時間做一個工程上的解法，讓它再也不會犯同一個錯。」英文社群把這套心法叫 the ratchet (棘輪)。不過棘輪是雙向的: 你只在看到真實失敗時才加一條約束，也只在某天模型強到讓那條約束變多餘時，才把它拆掉。所以一份好的 AGENTS.md，每一行都該追得回一個具體出過的包。至於「約束會隨模型變強而退場」這半邊，是這個系列最後一篇要回來談的，這裡先按下。</p>

<h2 id="不是取代是一層層疊上去">不是取代，是一層層疊上去</h2>

<p>要定義 harness engineering，得先把它跟前面兩個熟悉的詞擺在一起。很多人會把 Prompt → Context → Harness 講成一條「演進」的路，好像後面的把前面的取代掉了。實務上不是這樣，它們高度重疊，只是每個詞出現的時候，社群正在試圖凸顯一個新的工程焦點。</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>核心問題</th>
      <th>典型技術</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Prompt Engineering</strong></td>
      <td>怎麼讓模型<strong>這一次</strong>回答得更好</td>
      <td>system prompt、few-shot、格式指令</td>
    </tr>
    <tr>
      <td><strong>Context Engineering</strong></td>
      <td>在 context window 的限制下，怎麼<strong>選擇、篩選</strong>該放進 context 的資訊</td>
      <td>RAG、memory、compaction、工具輸出卸載</td>
    </tr>
    <tr>
      <td><strong>Harness Engineering</strong></td>
      <td>怎麼讓 agent 在<strong>行動迴圈</strong>裡被約束、檢查、修正，甚至越跑越好</td>
      <td>前饋 guides、回饋 sensors、eval gates、hooks</td>
    </tr>
  </tbody>
</table>

<p>這三層是疊加，不是替換。Prompt 管單次呼叫怎麼把話說清楚;context 管模型當下該掌握哪些資訊、別被污染;harness 管 agent 一邊行動一邊怎麼收斂。HumanLayer 甚至直接把 harness engineering 視為 context engineering 的子集，差別只在它特別關注「用配置點來管理整個 agent 行動迴圈的 context」。所以這裡有個判斷標準: 凡是 harness 出現之前就已經存在的東西 (測試、linter、CI)，它們本來就在，不是 harness engineering 發明的;harness engineering 的新意，是把這些既有工具重新接成 agent 的回饋迴路。</p>

<blockquote>
  <p>編按: Context Engineering 這層不是本系列的主題，但它本身是個完整的題目。想補齊這塊的讀者，可以看 ihower 寫的 <a href="https://ihower.tw/blog/12817-context-engineering">什麼是 Context Engineering 上下文工程?</a>，裡面把寫入、選擇、壓縮、隔離 context 這四種做法整理得蠻清楚;更偏實作的話，ihower 在 WebConf Taiwan 2025 的演講 <a href="https://ihower.tw/blog/13501-practical-ai-agents">實戰 AI Agents 應用開發: TTFT 和 Prompt Caching</a> 也談了 Web Agent 的部署、可觀測性、Prompt Caching 與 Context Engineering 等實戰主題。</p>
</blockquote>

<h2 id="agent--model--harness然後呢">Agent = Model + Harness，然後呢?</h2>

<p>LangChain 在 <a href="https://www.langchain.com/blog/the-anatomy-of-an-agent-harness">《The Anatomy of an Agent Harness》</a> 裡給了一個被大量引用的定義:</p>

<blockquote>
  <p>「模型提供智能，harness 是讓那份智能變得有用的系統。」(The model contains the intelligence and the harness is the system that makes that intelligence useful.)</p>
</blockquote>

<p>配套口訣是: 「如果你不是模型，那你就是 harness。」(If you’re not the model, you’re the harness.) 模型以外的一切，system prompt、工具、編排邏輯、hooks，全算 harness。</p>

<p>這個定義乾淨，但 ihower 的吐槽是: 用「模型 + harness」來定義 agent，對理解 harness 幾乎沒幫助，等於把模型以外的東西全部塞進「harness」這個標籤，講了跟沒講一樣。它劃了一條邊界，卻沒告訴你 harness 內部該怎麼想、該往哪裡用力。我們需要的是更可操作的拆法，不是一句包山包海的話。</p>

<h2 id="那-harness-的基本策略是什麼-先-generate再-verify">那 harness 的基本策略是什麼? 先 generate，再 verify</h2>

<p>在拆解結構之前，先講大方向。如果問「harness 到底要讓 agent 建立什麼行為」，把這幾家的文章攤開看，反覆被當成核心策略明講的其實只有一件事: <strong>驗證 (verification)</strong>。</p>

<p><a href="https://www.langchain.com/blog/improving-deep-agents-with-harness-engineering">LangChain 的第二篇實戰文</a> 講得最直白。他們發現最常見的失敗長這樣: agent 寫完一個解法，回頭重讀自己的程式碼，確認「看起來沒問題」，然後就停了。這篇的結論是: 「自我驗證 (self-verification) 是最有效的槓桿」，而且「今天的模型是很強的自我改進機器，但它們沒有自發進入『寫完就驗證』這個迴圈的傾向」。換句話說，模型有能力自我修正，但你不逼它，它不會主動做。</p>

<p>那這套「先做、再驗」的策略，落到具體流程上長什麼樣? 常見的形狀是 <strong>規劃 → 實作 → 驗證 → 修 (plan → implement → verify → fix)</strong>:</p>

<ul>
  <li>LangChain 直接把這四步寫進 system prompt: Planning &amp; Discovery → Build → Verify → Fix。</li>
  <li>Anthropic 則主張把它拆給不同 agent: planner / generator / evaluator，讓「生成」跟「評估」由不同 agent 來做，比同一個 agent 自評更可靠。</li>
  <li>walkinglabs 的課程把它寫成一個 session 生命週期: START → SELECT → EXECUTE (內含 implement → verify → fix) → WRAP UP。</li>
</ul>

<p>這套流程比較像是「驗證」這個核心主張的自然延伸，是小編從多份資料裡歸納出的共同形狀，不是哪一家冠名的策略。資料真正反覆強調的，上游是前面講的 ratchet 心法，下游是 verify 這一段。</p>

<p>策略講完，真正有意思的工程問題才浮現: 你怎麼確保 verify「真的會發生」? 寫在 prompt 裡求模型自律，跟用程式逼它非做不可，是兩種完全不同強度的做法。這就需要一組座標把它們分開。</p>

<h2 id="兩個軸把-harness-攤平">兩個軸，把 harness 攤平</h2>

<p>Thoughtworks 的 Birgitta Böckeler 在 <a href="https://martinfowler.com/articles/harness-engineering.html">Harness engineering for coding agent users</a> 提供了小編覺得最好用的一組座標。她用兩個軸把 harness 攤平: 一個是<strong>方向</strong> (行動前 vs 行動後)，一個是<strong>執行型態</strong> (確定性的程式 vs 推論的 LLM)。</p>

<ul>
  <li><strong>方向</strong>: 前饋 (feedforward) 的「引導器 guides」，在 agent 動手<strong>之前</strong>先引導它，提高一次做對的機率;回饋 (feedback) 的「感測器 sensors」，在 agent 動手<strong>之後</strong>觀察結果、逼它自我修正。</li>
  <li><strong>執行型態</strong>: 運算式 (computational) 是確定性的、毫秒級、結果可靠;推論式 (inferential) 是用 LLM 做語意判斷，比較慢、比較貴、也比較不穩。</li>
</ul>

<p>兩軸交叉成一個 2×2，每一格都裝得下具體技術:</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>運算式 Computational (確定性程式)</th>
      <th>推論式 Inferential (LLM 生成)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>前饋 Guides</strong> (行動前引導)</td>
      <td>LSP、結構化改寫 (codemod／ast-grep)、機器可讀的架構約束</td>
      <td>AGENTS.md、Skills、bootstrap 指示、how-to 文件</td>
    </tr>
    <tr>
      <td><strong>回饋 Sensors</strong> (行動後修正)</td>
      <td>測試、linter、type checker、靜態分析、pre-commit hook</td>
      <td>AI code review、LLM as Judge、review skills</td>
    </tr>
  </tbody>
</table>

<p>要補一句: Thoughtworks 原文整個都是站在 coding agent 的場景講的，ihower 這裡把它擴充到任意一種自建 agent 應用。這個框架本身跟 coding 無關，一個金融 data agent、一個 RAG 問答、一個訪談 agent，同樣可以問自己「我的前饋引導在哪、回饋感測在哪、哪些用程式判定就好、哪些非得用 LLM 不可」。</p>

<p>Böckeler 還具體談了這些 guides 跟 sensors「該擺在開發流程的哪個位置」: 快又便宜的 (linter、快速測試、基本 review) 應該盡量往左移，在 commit 前就跑;貴的 (mutation testing、需要看大局的深度 review) 放到整合後的 pipeline;另外還有一類「持續漂移感測」，像死碼偵測、測試覆蓋率品質、相依性掃描，是掛在整個 codebase 上長期跑的，不綁在單次改動上。這套「把品質檢查盡量往左」的思路，本來就是持續整合的老智慧，只是現在多了一種推論式的感測器可以擺進去。</p>

<p>回到那個強度問題，答案就藏在這張表的「上下兩列」: verify 寫在 AGENTS.md 裡求模型照做，屬於上面的前饋，是軟性的、機率性的;verify 用 hook 強制跑、不過不准收工，屬於下面的回饋，是硬性的、確定性的。同一個「要驗證」的願望，可以同時散落在不同格子裡，強度天差地別。</p>

<h2 id="好的-harness-做兩件事">好的 harness 做兩件事</h2>

<p>把 2×2 濃縮成一句話: 好的 harness 同時做兩件事，提高「一次做對」的機率，並且在出錯時自我修正。</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:24px 0;">
<div style="flex:1 1 260px;border:1px solid var(--border);border-radius:8px;padding:16px 20px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:8px;">🧭 Guides 前饋</div>
<div style="font-size:1.05em;font-weight:600;color:var(--text);">提高「一次做對」的機率</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-top:6px;">文件、skills、範例、架構約束。軟性引導，做不做最後還是 agent 決定。</div>
</div>
<div style="flex:1 1 260px;border:1px solid var(--accent);border-radius:8px;padding:16px 20px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📡 Sensors 回饋</div>
<div style="font-size:1.05em;font-weight:600;color:var(--text);">行動之後感測並逼它修正</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-top:6px;">測試、linter、judge、review。用程式強制執行，不靠模型自覺。</div>
</div>
</div>

<h3 id="前饋-guides-一切從你的指令開始">前饋 Guides: 一切從你的指令開始</h3>

<p>前饋就是用自然語言事先告訴 agent 該怎麼做。自行開發 agent 時，這一步就是你寫的 system／developer prompt，你得在裡面把「好結果長什麼樣」講清楚。至於要寫到多明確、多強硬，不妨看看成熟的 coding agent 怎麼做，它們的 system prompt 直接把「要驗證」寫成了 MUST 等級的指示。</p>

<p>Claude Code 的 system prompt 裡有這麼一句:</p>

<blockquote>
  <p>「非常重要: 當你完成一個任務，你<strong>必須</strong>執行 lint 和 typecheck 指令 (例如 npm run lint、npm run typecheck、ruff 等)，如果有提供這些指令的話，以確保你的程式碼正確。」
(VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands … to ensure your code is correct.)</p>
</blockquote>

<p>它甚至接著交代: 如果找不到正確的檢查指令，要主動問使用者，問到了還要建議把它寫進 CLAUDE.md，這樣下次就知道要跑。Codex 那邊要求得更嚴格:</p>

<blockquote>
  <p>「如果 AGENTS.md 裡含有用來驗證你工作成果的可程式化檢查，你<strong>必須</strong>全部執行，並盡最大努力驗證它們通過。這條規定即使是看起來很簡單的改動，例如改文件，也一樣適用。」
(If the AGENTS.md includes programmatic checks to verify your work, you MUST run all of them … This applies even for changes that appear simple, such as documentation.)</p>
</blockquote>

<p>回到你自己的 agent。如果是自行開發，這些前饋就寫在你的 system／developer prompt 裡;如果是在現成的 coding agent 上工作，對應的就是那份 AGENTS.md / CLAUDE.md，它每一輪都會被注入 system prompt，適合放專案慣例、領域知識、輸出格式、哪些事不要做。想看這類記憶檔該寫什麼、不該寫什麼，可以參考 <a href="https://blog.aihao.tw/2026/05/03/agents-md-research-and-practices/">AGENTS.md / CLAUDE.md 該寫什麼、不該寫什麼?</a>。如果想看一份完整的前饋 harness 長什麼樣，walkinglabs 的 <a href="https://github.com/walkinglabs/learn-harness-engineering">learn-harness-engineering</a> 把它拆成五個子系統: 指示 (instructions)、狀態 (state)、驗證 (verification)、範圍 (scope)、生命週期 (lifecycle)，它有一句總結很到位: 「模型決定要寫什麼程式碼，harness 決定它何時、在哪、怎麼寫。」敏捷三叔公那篇 <a href="https://agile3uncles.com/2026/04/15/claude-code-harness-web-to-do-list/">用 Claude Code 從零打造 To-Do List 的最小可行 harness</a>，則是手把手示範怎麼用一份 CLAUDE.md，把「寫 → 測 → 修」這個回饋迴路的規則寫進去。</p>

<p>但前饋有個上限: <strong>它終究只是軟性引導，是機率性的。</strong> 你在 AGENTS.md 寫一百遍「一定要驗證」，模型還是有可能跳過。它就是個機率裝置，不是保證。</p>

<h3 id="回饋-sensors-把-verify-變成強制執行的檢查">回饋 Sensors: 把 verify 變成強制執行的檢查</h3>

<p>要讓「驗證」從一句願望變成強制執行，得靠回饋這一列的程式化感測器。</p>

<p>以 coding 場景來說，具體做法就是 hook、pre-commit、CI gate 這些程式碼層級的強制點:</p>

<ul>
  <li>HumanLayer 用一個 Stop hook，在 agent 想收工時跑 typecheck 和 formatter，只要失敗就回傳 exit code 2，逼 agent 繼續修到通過為止。成功時完全靜默，失敗時才把錯誤訊息丟回去。</li>
  <li>LangChain 用一個 PreCompletionChecklistMiddleware，在 agent 退出前攔截它，強制它對著任務規格再跑一輪驗證才准走。</li>
  <li>walkinglabs 則把驗證收斂成一個 <code class="language-plaintext highlighter-rouge">harness-verify</code> 腳本 (lint + type-check + test + build 一次跑完)，設成強制檢查點。</li>
</ul>

<p>缺了這些程式化的強制檢查，agent 永遠只能靠自我感覺良好交差，你也只能用肉眼一行行盯。</p>

<p>不過光靠回饋也有它的問題: 只有回饋、沒有前饋，常會看到 agent 寫程式碼 → 測試失敗 → 改 → 再失敗，繞好幾圈還是超時。這通常不是回饋不夠，而是 agent 從頭就不知道「對的結果」長什麼樣。此時該加強的是前饋 (架構文件、慣例、Skills)，前饋負責一開始走對方向，回饋負責把關，兩個都要。</p>

<p>而且回饋 sensors 不只 coding 場景用得到。這個系列會把場景擴大到非 coding 的自建 agent，而 sensors 正是本場的重點: 後面會細談回饋的四個時機點，以及每個時機能用哪些方式把回饋接回迴圈，不同做法在強制性、成本、可靠度上各有優缺點。</p>

<h2 id="核心其實是控制論">核心其實是控制論</h2>

<p>為什麼回饋這麼關鍵? ihower 覺得控制論 (cybernetics) 拿來比喻 harness 蠻貼切的。</p>

<p>George (<a href="https://x.com/odysseus0z/status/2030416758138634583">@odysseus0z</a>) 寫過一個對照很到位的例子。1780 年代瓦特的離心調速器發明之前，蒸汽機旁邊得站一個工人，用手調節汽門;發明之後，飛球機構自己感測轉速、自動調節汽門。工人沒有消失，他的工作變了: 從顧著轉閥門，變成設計那個調速器。</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>感測</th>
      <th>致動</th>
      <th>工程師的工作變成</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>瓦特離心調速器</strong> (1780s)</td>
      <td>飛球感測轉速</td>
      <td>自動調節汽門</td>
      <td>從顧著轉閥門，變成<strong>設計調速器</strong></td>
    </tr>
    <tr>
      <td><strong>Harness Engineering</strong></td>
      <td>測試、judge、review</td>
      <td>agent 自我修正、重試</td>
      <td>從盯著 agent 一步步做，變成<strong>設計環境與回饋迴圈</strong></td>
    </tr>
  </tbody>
</table>

<p>同樣的模式重演: 有人造出夠強的「感測器 + 致動器」，把控制迴圈在那一層接起來，人就從操作者變成設計者: 不再親手轉閥門，而是去設計整個迴圈。</p>

<p>反過來說，一個沒有感測器的 agent 就是一個<strong>無回饋迴路 (open loop)</strong>: 指令發出去、結果聽天由命，品質全靠運氣和模型當天的狀態。這也是為什麼這個系列想反覆強調的一句話是: agent 需要的是回饋迴路，不是完美的提示 (Agents need feedback loops, not perfect prompts)。你再怎麼改 prompt，沒有迴路它就收斂不了。</p>

<h2 id="harness-是影響表現的一大關鍵">harness 是影響表現的一大關鍵</h2>

<p>harness 不是模型的附屬品，它本身就是決定 agent 表現的一大關鍵。同一個模型，換一套 harness，結果可以差很多，這點在公開 benchmark 上看得很清楚。</p>

<p>LangChain 拿 Terminal Bench 2.0 做過示範: 固定 <code class="language-plaintext highlighter-rouge">gpt-5.2-codex</code> 不換模型，只調 harness (system prompt、middleware、推理預算的分配)，分數就從 52.8% 拉到 66.5%。另一個常被引用的對照是 Opus 4.6: 同一個模型在不同 harness 下，差距已經大到足以改變它在排行榜上的排名。</p>

<h2 id="本場的工作定義">本場的工作定義</h2>

<p>把前面收攏成一句可操作的定義，作為整個系列的錨點:</p>

<blockquote>
  <p><strong>Harness Engineering: 讓 agent 根據目標，持續、正確地動作的工程。</strong></p>
</blockquote>

<p>核心材料是「回饋訊號」: 你得有東西能判斷<strong>做得對不對、做完了沒</strong>。沒有這個訊號，前面講的一切都只是無回饋迴路。</p>

<h2 id="回饋的四個時機">回饋的四個時機</h2>

<p>那回饋到底「在哪裡」接回迴圈? 這是整個系列的骨架。把 agent 的迴圈由內而外攤開，有四個可以下手的時機，越往外越貴。其中三個 (①③④) 是 harness 自動觸發的，另一個 (②) 是兩次 model request 之間的注入點，可以由使用者主動 steer，也可以由程式注入:</p>

<table>
  <thead>
    <tr>
      <th>時機</th>
      <th>多久觸發一次</th>
      <th>成本</th>
      <th>修正粒度</th>
      <th>對應的 hook</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>① 工具執行內</strong></td>
      <td>每次 tool call</td>
      <td>毫秒，最便宜</td>
      <td>單一動作</td>
      <td>Pre／PostToolUse</td>
    </tr>
    <tr>
      <td><strong>② request 之間注入</strong></td>
      <td>使用者或程式想注入時</td>
      <td>趨近零</td>
      <td>當前這一輪的方向</td>
      <td>無專屬 hook</td>
    </tr>
    <tr>
      <td><strong>③ 單輪結束</strong></td>
      <td>每一輪</td>
      <td>秒級</td>
      <td>整輪的產出</td>
      <td>Stop hook</td>
    </tr>
    <tr>
      <td><strong>④ 外層 Loop</strong></td>
      <td>每個 session</td>
      <td>分鐘到小時</td>
      <td>整個任務</td>
      <td>排程／外迴圈</td>
    </tr>
  </tbody>
</table>

<p>畫成圖更清楚: 這些時機由內而外是巢狀的，而一輪 (Turn) 本身就包含好幾次「模型 request → tool call」，①②③④ 分別發生在這幾個邊界上:</p>

<svg viewBox="0 0 720 342" style="width:100%;max-width:720px;height:auto;margin:24px 0;font-family:-apple-system,'PingFang TC','Noto Sans TC',Helvetica,Arial,sans-serif;" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="he2arr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" style="fill:var(--text-secondary)" /></marker>
<marker id="he2arrA" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" style="fill:var(--accent)" /></marker>
</defs>
<rect x="10" y="40" width="700" height="296" rx="14" style="fill:var(--bg);stroke:var(--text-secondary);stroke-width:1.5;stroke-dasharray:6 5" />
<rect x="26" y="27" width="332" height="27" rx="13.5" style="fill:var(--bg);stroke:var(--text-secondary);stroke-width:1.5" />
<text x="40" y="45" style="fill:var(--text);font-size:13px;font-weight:700">④ 外層 Loop</text>
<text x="118" y="45" style="fill:var(--text-secondary);font-size:11.5px">· 每個 session · 分鐘~小時 · 修正整個任務</text>
<rect x="34" y="78" width="652" height="154" rx="12" style="fill:var(--surface);stroke:var(--accent);stroke-width:1.5" />
<rect x="50" y="65" width="170" height="26" rx="13" style="fill:var(--surface);stroke:var(--accent);stroke-width:1.5" />
<text x="63" y="82" style="fill:var(--accent);font-size:12.5px;font-weight:700">③ 一輪 Turn</text>
<text x="138" y="82" style="fill:var(--text-secondary);font-size:11px">· 秒級</text>
<text x="52" y="111" style="fill:var(--text-secondary);font-size:11px">使用者輸入</text>
<line x1="52" y1="117" x2="52" y2="138" style="stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#he2arr)" />
<rect x="48" y="138" width="86" height="40" rx="7" style="fill:var(--bg);stroke:var(--border);stroke-width:1.3" />
<text x="91" y="155" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">模型</text>
<text x="91" y="170" text-anchor="middle" style="fill:var(--text-secondary);font-size:10px">request 1</text>
<line x1="134" y1="158" x2="168" y2="158" style="stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#he2arr)" />
<rect x="170" y="138" width="86" height="40" rx="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.3" />
<text x="213" y="155" text-anchor="middle" style="fill:var(--accent);font-size:12px;font-weight:600">🔧 tool calls</text>
<text x="213" y="170" text-anchor="middle" style="fill:var(--accent);font-size:10px;font-weight:700">①</text>
<line x1="256" y1="158" x2="290" y2="158" style="stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#he2arr)" />
<rect x="292" y="138" width="86" height="40" rx="7" style="fill:var(--bg);stroke:var(--border);stroke-width:1.3" />
<text x="335" y="155" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">模型</text>
<text x="335" y="170" text-anchor="middle" style="fill:var(--text-secondary);font-size:10px">request 2</text>
<line x1="378" y1="158" x2="412" y2="158" style="stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#he2arr)" />
<rect x="414" y="138" width="86" height="40" rx="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.3" />
<text x="457" y="155" text-anchor="middle" style="fill:var(--accent);font-size:12px;font-weight:600">🔧 tool calls</text>
<text x="457" y="170" text-anchor="middle" style="fill:var(--accent);font-size:10px;font-weight:700">①</text>
<line x1="500" y1="158" x2="534" y2="158" style="stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#he2arr)" />
<rect x="536" y="138" width="92" height="40" rx="7" style="fill:var(--bg);stroke:var(--border);stroke-width:1.3" />
<text x="582" y="155" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">模型</text>
<text x="582" y="170" text-anchor="middle" style="fill:var(--text-secondary);font-size:10px">輸出答案</text>
<line x1="628" y1="158" x2="648" y2="158" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#he2arrA)" />
<rect x="650" y="134" width="22" height="48" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.6" />
<text x="661" y="164" text-anchor="middle" style="fill:var(--accent);font-size:14px;font-weight:800">③</text>
<rect x="206" y="95" width="334" height="22" rx="11" style="fill:var(--surface);stroke:var(--accent);stroke-width:1.2" />
<text x="373" y="110" text-anchor="middle" style="fill:var(--accent);font-size:10.5px;font-weight:700">② request 之間注入 (人 steer 或程式)</text>
<path d="M 284 117 L 284 150" style="fill:none;stroke:var(--accent);stroke-width:1.2;stroke-dasharray:3 2" marker-end="url(#he2arrA)" />
<path d="M 518 117 L 518 150" style="fill:none;stroke:var(--accent);stroke-width:1.2;stroke-dasharray:3 2" marker-end="url(#he2arrA)" />
<text x="320" y="197" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">① 工具執行內 · Pre／PostToolUse · 毫秒級 · 修正單一動作</text>
<path d="M 650 182 L 650 212 Q 650 218 644 218 L 99 218 Q 91 218 91 212 L 91 180" style="fill:none;stroke:var(--accent);stroke-width:1.3;stroke-dasharray:4 3" marker-end="url(#he2arrA)" />
<rect x="252" y="208" width="216" height="19" rx="9.5" style="fill:var(--surface);stroke:var(--accent);stroke-width:1" />
<text x="360" y="221" text-anchor="middle" style="fill:var(--accent);font-size:10.5px">Stop hook 沒過 → 同份 context 再跑一輪</text>
<text x="360" y="254" text-anchor="middle" style="fill:var(--text);font-size:11.5px">一輪 (Turn) = 多次「模型 request → tool call」反覆，直到模型輸出答案</text>
<path d="M 672 182 L 672 302 Q 672 310 664 310 L 42 310 Q 30 310 30 300 L 30 168 Q 30 158 40 158 L 46 158" style="fill:none;stroke:var(--text-secondary);stroke-width:1.6" marker-end="url(#he2arr)" />
<rect x="150" y="300" width="424" height="20" rx="10" style="fill:var(--bg);stroke:var(--text-secondary);stroke-width:1.3" />
<text x="362" y="314" text-anchor="middle" style="fill:var(--text);font-size:11px;font-weight:600">④ 通過後這個 session 收掉，換上全新 context 再跑一圈</text>
</svg>

<p>這四個時機，各自配上前面那兩種感測器 (運算式 vs 推論式)，或②那種即時注入，就構成這個系列接下來四篇的主體:</p>

<ul>
  <li><strong>時機①</strong> (下一篇): 在工具裡把最小的迴圈接起來。執行前驗證、執行後檢查修復、回傳值夾帶導引。最便宜，幾乎不會增加主迴圈的負擔。</li>
  <li><strong>時機②</strong>: 兩次 model request 之間的注入點。agent 還沒輸出答案、還在迴圈裡時，使用者可以中途 steer 或 interrupt，程式也能把背景工具結果、外部事件注入進來。</li>
  <li><strong>時機③</strong>: 單輪結束時的驗收。不能因為「模型覺得做完了」就算數，要對著一個可驗證的停止條件去檢查。</li>
  <li><strong>時機④</strong>: 外層 loop。當任務大到一個 context 裝不下，用外迴圈把任務一段段交給全新的 agent。</li>
</ul>

<p>這四層由內而外，就是接下來四篇的主線。</p>

<p>下一篇先從最小、最便宜的那一層開始: 怎麼在一次 tool call 裡，就把回饋接回迴圈。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Context Engineering" /><category term="Eval" /><summary type="html"><![CDATA[📚 給 Agent 開發者的駕馭工程 (全 9 篇) 1. 基礎: Deep Agent 的六項內建能力 2. 核心: Agent 要的是回饋迴路，不是完美提示 (本篇) 3. 回饋時機一: 工具回傳值，是寫給 agent 的回饋 4. 回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent 5. 回饋時機三: 單輪結束的驗收，Goal 與 Outcomes 6. 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron 7. 進階: 自我改進 Harness, Meta-Harness 與爬坡 8. 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson 9. 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建? 🎞️ 搭配演講投影片: 給 Agent 開發者的 Harness+Loop Engineering]]></summary></entry><entry><title type="html">給 Agent 開發者的駕馭工程 (3): 回饋時機一: 工具回傳值, 是寫給 agent 的回饋</title><link href="https://blog.aihao.tw/2026/06/26/harness-engineering-3-tool-execution-feedback/" rel="alternate" type="text/html" title="給 Agent 開發者的駕馭工程 (3): 回饋時機一: 工具回傳值, 是寫給 agent 的回饋" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/26/harness-engineering-3-tool-execution-feedback</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/26/harness-engineering-3-tool-execution-feedback/"><![CDATA[<div class="series-toc" style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:24px 0;font-size:.92em;line-height:1.85;">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📚 給 Agent 開發者的駕馭工程 (全 9 篇)</div>
<div>1. <a href="/2026/06/26/harness-engineering-1-deep-agent-capabilities/">基礎: Deep Agent 的六項內建能力</a></div>
<div>2. <a href="/2026/06/26/harness-engineering-2-what-is-harness-engineering/">核心: Agent 要的是回饋迴路，不是完美提示</a></div>
<div>3. <strong style="color:var(--text);">回饋時機一: 工具回傳值，是寫給 agent 的回饋</strong> <span style="color:var(--text-secondary);">(本篇)</span></div>
<div>4. <a href="/2026/06/26/harness-engineering-4-mid-run-injection/">回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent</a></div>
<div>5. <a href="/2026/06/26/harness-engineering-5-goal-and-outcomes/">回饋時機三: 單輪結束的驗收，Goal 與 Outcomes</a></div>
<div>6. <a href="/2026/06/26/harness-engineering-6-outer-loop/">回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</a></div>
<div>7. <a href="/2026/06/26/harness-engineering-7-self-improving/">進階: 自我改進 Harness, Meta-Harness 與爬坡</a></div>
<div>8. <a href="/2026/06/26/harness-engineering-8-model-harness-fit/">收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</a></div>
<div>9. <a href="/2026/06/26/harness-engineering-9-agent-frameworks/">自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</a></div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">🎞️ 搭配演講投影片: <a href="https://ihower.tw/presentation/harness.html">給 Agent 開發者的 Harness+Loop Engineering</a></div>
</div>

<p>上一篇整理出回饋的四個時機點，從這篇開始，由內而外一層一層做。先從最內、最便宜的那一層著手: 在一次 tool call 裡，就把迴圈閉合掉。</p>

<p>這一層的好處是: 它就發生在 tool call 內部，對主迴圈幾乎沒有額外負擔，卻是修正成本最低、頻率最高的回饋點。一個失誤在這裡就被擋下，不會擴散到後面整輪、甚至整個任務，變成更大的代價。</p>

<h2 id="tool-call-裡的三段式">Tool Call 裡的三段式</h2>

<p>一次工具呼叫，其實有三個可以介入的位置: 執行前、執行後、回傳時。</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:24px 0;">
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">執行前: 驗證輸入</div>
<div style="font-size:.9em;color:var(--text-secondary);">用確定性檢查擋掉危險或無效的呼叫，不讓它真的送出去。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">執行後: 檢查結果</div>
<div style="font-size:.9em;color:var(--text-secondary);">對拿回來的結果做品質檢查，必要時就地修復或重試。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">回傳值: 夾帶指引</div>
<div style="font-size:.9em;color:var(--text-secondary);">tool response 不只回資料，還夾帶指示與 metadata，引導 agent 下一步。</div>
</div>
</div>

<p>前兩段比較直覺，真正容易被忽略、也是這篇最想講清楚的，是第三段。寫程式的人很習慣把 function 的回傳值當成「資料」: 查到什麼就回什麼，出錯就丟出一個 exception。但在 agent 的場景裡，tool response 不只是資料，它同時也是一段 prompt: 你可以在裡面夾帶指示、metadata、甚至直接告訴 agent「下一步該怎麼做」。</p>

<p>這裡有個關鍵的觀念要翻轉: <strong>工具輸出不是寫程式的 function output，它是你寫給 agent 看的回饋</strong>。下面幾個案例，從純確定性的檢查，一路走到語意 Judge，由淺入深把這三段式逐一展開。</p>

<h2 id="案例-a-text-to-sql-agent-的-sql-驗證">案例 A: Text-to-SQL Agent 的 SQL 驗證</h2>

<p>先看一個最適合用「執行前驗證」的場景。</p>

<p>使用者發問，agent 看著資料庫 schema 自己生成 SQL，再用一個 <code class="language-plaintext highlighter-rouge">execute_sql(sql_query)</code> 工具去查。問題是，LLM 生成的 SQL 帶著一堆機率性的風險:</p>

<div style="border:1px solid #cf222e;border-radius:8px;padding:14px 18px;margin:18px 0;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:8px;">LLM 生成 SQL 的風險</div>
<ul style="margin:0;padding-left:20px;font-size:.92em;color:var(--text);">
<li>查詢白名單以外的資料表</li>
<li>全表掃描、忘記分頁 (沒加 LIMIT)</li>
<li>瞎掰一個不存在的欄位</li>
<li>生成 SELECT 以外的危險語法</li>
</ul>
</div>

<p>這些風險，你沒辦法靠 prompt「請不要這樣做」來解決。前一篇講過的前饋，在這個場景就是: 你會在 system prompt 裡寫好每張 table 的 schema、每張 table 是做什麼的、哪一類問題該查哪張 table，引導模型一開始就往對的方向生成 SQL。但前饋終究是機率性的引導，寫得再清楚，擋不住模型偶爾把欄位記錯、或忘記加 LIMIT。要擋，得在工具內部用一道確定性的關卡。</p>

<p>做法是把 SQL parse 成語法樹 (AST)，在送進資料庫之前先檢查、再改寫。Python 生態可以用 <a href="https://sqlglot.com/sqlglot.html">SQLGlot</a> 這個函式庫:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">sqlglot</span>
<span class="kn">from</span> <span class="n">sqlglot</span> <span class="kn">import</span> <span class="n">exp</span>

<span class="n">tree</span> <span class="o">=</span> <span class="n">sqlglot</span><span class="p">.</span><span class="nf">parse_one</span><span class="p">(</span><span class="n">sql_query</span><span class="p">,</span> <span class="n">dialect</span><span class="o">=</span><span class="sh">"</span><span class="s">postgres</span><span class="sh">"</span><span class="p">)</span>

<span class="c1"># 1. 只允許 SELECT 查詢
</span><span class="k">if</span> <span class="ow">not</span> <span class="nf">isinstance</span><span class="p">(</span><span class="n">tree</span><span class="p">,</span> <span class="n">exp</span><span class="p">.</span><span class="n">Select</span><span class="p">):</span>
    <span class="k">return</span> <span class="sh">"</span><span class="s">只允許 SELECT 查詢</span><span class="sh">"</span>

<span class="c1"># 2. 資料表白名單
</span><span class="k">for</span> <span class="n">table</span> <span class="ow">in</span> <span class="n">tree</span><span class="p">.</span><span class="nf">find_all</span><span class="p">(</span><span class="n">exp</span><span class="p">.</span><span class="n">Table</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">table</span><span class="p">.</span><span class="n">name</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">ALLOWED_TABLES</span><span class="p">:</span>
        <span class="k">return</span> <span class="sa">f</span><span class="sh">"</span><span class="s">資料表 </span><span class="si">{</span><span class="n">table</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="s"> 不在允許範圍,可用的有: </span><span class="si">{</span><span class="n">ALLOWED_TABLES</span><span class="si">}</span><span class="sh">"</span>

<span class="c1"># 3. 沒有 LIMIT 就補一個安全上限; 已有更嚴格的限制就保留,不覆蓋
</span><span class="n">limit</span> <span class="o">=</span> <span class="n">tree</span><span class="p">.</span><span class="n">args</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">limit</span><span class="sh">"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">limit</span> <span class="ow">is</span> <span class="bp">None</span> <span class="ow">or</span> <span class="nf">int</span><span class="p">(</span><span class="n">limit</span><span class="p">.</span><span class="n">expression</span><span class="p">.</span><span class="n">name</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">200</span><span class="p">:</span>
    <span class="n">tree</span> <span class="o">=</span> <span class="n">tree</span><span class="p">.</span><span class="nf">limit</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
<span class="c1"># 4. 注入這個使用者該有的權限條件 (例如 WHERE tenant_id = ...)
</span><span class="n">safe_sql</span> <span class="o">=</span> <span class="n">tree</span><span class="p">.</span><span class="nf">sql</span><span class="p">(</span><span class="n">dialect</span><span class="o">=</span><span class="sh">"</span><span class="s">postgres</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>注意這道關卡有兩種動作: 一種是擋下來 (不是 SELECT、查到白名單外的表就直接回傳錯誤)，另一種是補強 (沒加 LIMIT 就補一個安全上限，但若原本就有更嚴格的 LIMIT 就保留、不覆蓋; 再注入這個使用者該有的權限條件)。這裡的分寸是: 補強只做「擋不住會出事」的最小改寫，不是假設任何 SQL 都能安全地自動改。它純粹靠程式運算: 不呼叫 LLM、零延遲、結果完全可重現。能用程式確定判斷的事，就不要請模型來猜。</p>

<h2 id="失敗回饋-同樣的錯誤訊息寫得好不好差很多">失敗回饋: 同樣的錯誤，訊息寫得好不好差很多</h2>

<p>驗證沒過的時候，你回傳什麼，直接決定 agent 自我修正的速度。這就回到剛剛講的「回傳值也是 prompt」。</p>

<p>對比一下同一個錯誤的兩種寫法:</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:22px 0;">
<div style="flex:1 1 280px;border:1px solid #cf222e;border-radius:8px;padding:16px 18px;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:8px;">❌ 爛的錯誤訊息</div>
<pre style="margin:0;white-space:pre-wrap;font-size:.85em;color:var(--text);">ERROR: column "revenue_growth"
does not exist</pre>
<div style="font-size:.88em;color:var(--text-secondary);margin-top:10px;">agent 只能瞎猜重試，常常越改越歪。</div>
</div>
<div style="flex:1 1 280px;border:1px solid #1a7f37;border-radius:8px;padding:16px 18px;background:#dafbe1;">
<div style="font-weight:700;color:#1a7f37;margin-bottom:8px;">✅ 好的錯誤訊息</div>
<pre style="margin:0;white-space:pre-wrap;font-size:.85em;color:var(--text);">欄位 revenue_growth 不存在。
financial_metrics 可用欄位:
revenue, revenue_yoy, gross_margin…
年增率請改用 revenue_yoy。</pre>
<div style="font-size:.88em;color:var(--text-secondary);margin-top:10px;">下一步直接被導正。</div>
</div>
</div>

<p>爛的錯誤訊息把資料庫原始的訊息原封不動丟回去，那是寫給機器看的; agent 拿到只能再猜一次欄位名，猜錯再來一輪。好的錯誤訊息是寫給 agent 看的: 它附上這張表實際有哪些欄位、還直接點出「年增率你要的應該是 revenue_yoy」。下一步直接被導正，不用反覆試錯。</p>

<p>這件事的重點是: <strong>每個失敗都是你引導模型的免費機會，大多數人都浪費掉了。</strong> 錯誤訊息要寫給 agent 看、要可行動，不是只丟一個 error code。不只 SQL，延伸到別的情境也一樣，例如查詢成功但回傳 0 筆，這不是錯誤，但對 agent 是個該被提醒的訊號: 你可以回「查詢成功但 0 筆，可能是公司名稱的字面值對不上 (資料庫存的是全名)，或時間區間超出資料範圍 (本表只有 2015 到 2025)」，免得它把空結果當成「答案就是沒有」直接回給使用者。</p>

<h2 id="連成功都要回傳完整狀態不是只回成功旗標">連「成功」都要回傳完整狀態，不是只回成功旗標</h2>

<p>上面那個 0 筆的例子其實點到一件更通用的事: 需要設計的不只有失敗，<strong>成功一樣要設計</strong>。寫程式的人很習慣讓工具成功時就回一個 <code class="language-plaintext highlighter-rouge">{"success": true}</code> 了事，但對 agent 來說，一個光禿禿的成功旗標 (flag) 也可能是「假完成」的訊號。</p>

<p>回到 Text-to-SQL 的場景: 假設 agent 跑的是一個寫入查詢 (UPDATE／DELETE)，工具若只回 <code class="language-plaintext highlighter-rouge">{"success": true}</code>,agent 根本看不出來這次到底動了 5 筆還是 5 萬筆。一個你以為只會更新一位使用者的操作，可能因為 WHERE 條件寫太鬆而掃過整張表，但從一個光禿禿的成功旗標完全看不出來，agent 只會當作沒事繼續。(coding agent 也是同個毛病: 工具只回「File updated」，看不出是精準地只改了該改的 3 行，還是順手把 200 行無關的程式碼重排了一遍。)</p>

<p>所以工具成功時，回傳值要給的是 <strong>完整狀態 (state)，而不只是一個成功與否的旗標 (status)</strong>: 影響了幾筆資料、動了哪些欄位或哪些區塊、有沒有碰到不該碰的、目前進行到什麼狀態。把規模和範圍量化回來 (改了幾行、影響幾筆、花了多少 token),agent 才有依據判斷「這次的結果到底合不合理」，而不是看到 true 就繼續做下去。一句話: 錯誤要設計，成功也要設計。</p>

<h2 id="案例-b-文件摘要-agent-的-grader寫死的固定訊息也是一種引導">案例 B: 文件摘要 agent 的 grader，寫死的固定訊息也是一種引導</h2>

<p>案例 A 的好錯誤訊息是手寫的。你可能會想: 真實系統難道要一條一條把失敗訊息寫死嗎? 看一個把這件事系統化的案例，OpenAI Cookbook 的 <a href="https://developers.openai.com/cookbook/examples/partners/self_evolving_agents/autonomous_agent_retraining">Self-Evolving Agents</a> (Bain 與 OpenAI 合作)，場景是製藥法規文件的摘要。</p>

<p>它跑的是一個品質檢查迴圈: 摘要 agent 產出摘要 → 一組 grader 對著摘要評分 → 沒過，就把失敗回饋交給一個 metaprompt agent 去重寫 prompt → 新版 prompt 再跑一輪，直到分數過門檻。重點在那組 grader 的設計: 它不是只丟一個 LLM judge 了事，而是把確定性檢查跟語意判斷組起來，四個一起跑:</p>

<ul>
  <li><strong>chemical_name_grader</strong> (Python，確定性): 檢查摘要有沒有保留原文的化學名稱，確保忠於原文的專業用詞</li>
  <li><strong>word_length_deviation_grader</strong> (Python，確定性): 以 100 字為目標，偏離越多分數越低，控制冗長</li>
  <li><strong>cosine_similarity</strong> (確定性): 確保摘要沒有語意漂移</li>
  <li><strong>llm_as_judge</strong> (語意): 抓前三個規則型 grader 漏掉的細緻品質</li>
</ul>

<p>跟這篇主題最相關的，是這組 grader「沒過時回傳什麼回饋」。沒過的 grader 會被一個 <code class="language-plaintext highlighter-rouge">collect_grader_feedback()</code> 翻成文字、塞進 metaprompt。而這裡有個關鍵分野:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">collect_grader_feedback</span><span class="p">(</span><span class="n">grader_scores</span><span class="p">):</span>
    <span class="n">lines</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">grader_scores</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">entry</span><span class="p">[</span><span class="sh">"</span><span class="s">passed</span><span class="sh">"</span><span class="p">]:</span>
            <span class="k">continue</span>                                  <span class="c1"># 只處理沒過的
</span>        <span class="k">if</span> <span class="n">grader</span> <span class="o">==</span> <span class="sh">"</span><span class="s">chemical_name_grader</span><span class="sh">"</span><span class="p">:</span>
            <span class="n">lines</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sh">"</span><span class="s">Not all chemical names were included…</span><span class="sh">"</span><span class="p">)</span>    <span class="c1"># 寫死的固定訊息
</span>        <span class="k">elif</span> <span class="n">grader</span> <span class="o">==</span> <span class="sh">"</span><span class="s">word_length_deviation_grader</span><span class="sh">"</span><span class="p">:</span>
            <span class="n">lines</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sh">"</span><span class="s">The summary length deviates too much…</span><span class="sh">"</span><span class="p">)</span>    <span class="c1"># 寫死的固定訊息
</span>        <span class="k">elif</span> <span class="n">grader</span> <span class="o">==</span> <span class="sh">"</span><span class="s">cosine_similarity</span><span class="sh">"</span><span class="p">:</span>
            <span class="n">lines</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sh">"</span><span class="s">The summary is not sufficiently similar…</span><span class="sh">"</span><span class="p">)</span>  <span class="c1"># 寫死的固定訊息
</span>        <span class="k">elif</span> <span class="n">grader</span> <span class="o">==</span> <span class="sh">"</span><span class="s">llm_as_judge</span><span class="sh">"</span><span class="p">:</span>
            <span class="n">lines</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">entry</span><span class="p">[</span><span class="sh">"</span><span class="s">reasoning</span><span class="sh">"</span><span class="p">])</span>          <span class="c1"># judge 當場生成的語意說明
</span>    <span class="k">return</span> <span class="n">lines</span>
</code></pre></div></div>

<p>三個確定性 grader (chemical／length／cosine) 只能給出「預先寫死的固定字串」，因為它們本身只回一個數字分數，講不出「為什麼不對」。只有 <code class="language-plaintext highlighter-rouge">llm_as_judge</code> 是評分模型 (score model)，能附上自己的推理說明 (reasoning)，給的回饋才帶當下的語意說明。</p>

<p>這帶出案例 A 沒講透的一個取捨。確定性檢查又快又穩又可重現 (案例 A 的 SQLGlot、這裡的三個 Python grader)，但它能給的回饋精細度有上限: 它只能回你「事先設計好、寫死」的那句引導，講不出這一次具體錯在哪。語意 judge 反過來，慢、貴、不穩，但能把問題講清楚。所以「能確定就不用 LLM」不是說 judge 沒用，而是說: 凡是你能事先想清楚、寫死引導句的失敗，就用確定性檢查擋掉; 剩下那些「要看當下語意才講得清楚」的，才交給 judge。</p>

<p>換個角度看，失敗回饋始終是你「設計」出來的，不是系統隨便給的。差別只在: 確定性 grader 的回饋是你寫程式時就一字一句定好的固定訊息，judge 的回饋是它當場生成的。兩種都要寫得可行動，這也是這整篇反覆在講的事。</p>

<h2 id="回傳值的另一半責任-別把-context-塞滿">回傳值的另一半責任: 別把 context 塞滿</h2>

<p>回傳值除了夾帶引導，還有一個容易被忘掉的責任: 控制它自己的大小。這件事是 context engineering 的一環，設計工具回傳值的時候，別忘了 context engineering 還有一整套策略可以一起用。</p>

<p>ihower 在 <a href="https://ihower.tw/blog/12817-context-engineering">Context Engineering</a> 一文裡 (沿用 Lance Martin 的框架) 把它分成四種策略: 寫入、選擇、壓縮、隔離。這幾種要一起考慮，其中兩種最容易在「工具回傳」這一步被漏掉: 「選擇 context」靠的是各種檢索技術 (RAG、reranker、查詢理解)，決定只把對的內容拉進來; 「隔離 context」靠的是 sub-agent，把龐大的中間過程交給另一份 context 去跑、只回傳結論。回到工具回傳值本身，要顧的就是別一次塞太多進 context，下面幾個例子都在講這件事。</p>

<p>LangChain 的 Vivek Trivedy 在一場<a href="https://www.youtube.com/watch?v=NovNcsKX8AU">訪談</a>裡的建議是: 從第一天就帶著「對抗 context rot」的意識設計 agent，而不是等 context 塞滿再來補。第一篇提過 context rot,Viv 把它講得更具體: context window 一旦過了某個門檻，模型會明顯變笨，掉進他同事 (HumanLayer 的 Dex) 說的「dumb zone」。一坨沒裁過的 shell log、一個幾千筆的查詢結果，直接整包塞回 context，就是在把 agent 推向那個門檻。</p>

<p>以 coding agent 為例最具體: 檔案動輒上千行、shell log 輸出量又大，最容易把 context 塞滿。Arize 的 Aparna Dhinakaran <a href="https://x.com/aparnadhinak/status/2048492731929149929">逆向觀察、比較了四個 agent harness</a> (Pi、OpenClaw、Claude Code、Letta) 怎麼管 context，整理出它們在工具輸出上頗為一致的幾個做法 (以下是她從工具行為逆向觀察的整理，細節數字以該串為準，不是官方文件):</p>

<ul>
  <li><strong>硬性上限</strong>: 檔案讀取一律設上限 (Claude Code 是讀取前先 stat 擋掉 256KB 以上的檔、讀取後再用 token 數把關)，工具結果也各有字元上限。</li>
  <li><strong>頭尾保留</strong>: 截斷時不是只留開頭，而是看尾巴重不重要 (有錯誤、有 JSON 收尾大括號、有摘要關鍵字) 就切成「頭 + 尾」，中間捨掉。</li>
  <li><strong>卸載到磁碟</strong>: 超大的工具結果寫到檔案，context 裡只留一個 2KB 的預覽和檔案路徑，模型需要細看時再自己去讀。</li>
  <li><strong>附上續讀提示</strong>: 截斷後補一句「目前顯示第 1 到 2000 行，用 offset=2001 繼續」，讓模型知道它看到的是局部、還能怎麼拿更多。</li>
</ul>

<p>換到 Text-to-SQL 場景也一樣具體: SQL 回傳 3,847 筆，別整包塞進去，裁成前 10 筆，後面補一段 metadata「共 3,847 筆已截斷，涵蓋 412 家公司; 若要加總或平均請直接用 SQL 聚合，不要逐筆讀回來自己算」。這樣 context 不會塞滿，模型又知道整體規模，還順手被導去用對的做法。</p>

<blockquote>
  <p>編按: 這四個 harness 在 context 管理上的趨同還不只工具輸出，連前面講的「壓縮」(對話 compaction)、「隔離」(子代理人) 這兩種策略，做法都很接近，Aparna 那篇值得一讀。</p>
</blockquote>

<p>把這段收一句: 回傳值要同時顧兩件事，一是夾帶能引導下一步的訊號，二是別讓自己把 context 塞滿。下一個案例，正好把這兩件事結合在一起。</p>

<h2 id="案例-c-知識庫-rag-的-facets讓-agent-看見資料全貌">案例 C: 知識庫 RAG 的 facets，讓 agent 看見資料全貌</h2>

<p>換一個語意更重的場景。企業知識庫問答 agent，配一個 <code class="language-plaintext highlighter-rouge">search_knowledge(query)</code> 工具 (關鍵字 + 向量搜尋，再加 reranker)。</p>

<p>RAG 的老問題是: 檢索品質參差不齊，空結果、低相關、版本過時、片段被截斷都有可能。而麻煩在於，<strong>就算拿到品質差的 context,agent 還是會很有自信地生成答案</strong>。檢索品質的把關不能等到答案生出來才做，要在工具層就介入。</p>

<p>最直接的介入，是在回傳結果時順手標註來源和相關度分數，讓 agent 有依據判斷哪些該引用、哪些該丟。再進一步，Jason Liu 在 <a href="https://jxnl.co/writing/2025/08/27/facets-context-engineering/">Beyond Chunks: Why Context Engineering is the Future of RAG</a> 裡點出一個常被忽略的訊號: 當好幾個 chunk 都來自同一份文件，那其實是在暗示「這份文件整體很相關」，與其讓 agent 拿一堆零碎片段去拼湊，不如在工具回傳裡提示它直接把那份文件的整頁讀回來，完整脈絡比散落的 chunk 好用得多。而他文章裡更有意思的一招是 facets: 工具回傳的不只是 top-k 結果，還夾帶一份整個資料集分佈的統計。</p>

<p>舉他原文的例子 (場景是搜尋工單),search 回傳的內容長這樣:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;ToolResponse&gt;</span>
  <span class="nt">&lt;results</span> <span class="na">query=</span><span class="s">"API timeout issues"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;ticket</span> <span class="na">id=</span><span class="s">"LIN-1247"</span> <span class="na">status=</span><span class="s">"Done"</span><span class="nt">&gt;</span>…<span class="nt">&lt;/ticket&gt;</span>
    <span class="nt">&lt;ticket</span> <span class="na">id=</span><span class="s">"LIN-1189"</span> <span class="na">status=</span><span class="s">"Done"</span><span class="nt">&gt;</span>…<span class="nt">&lt;/ticket&gt;</span>
    <span class="nt">&lt;ticket</span> <span class="na">id=</span><span class="s">"LIN-1203"</span> <span class="na">status=</span><span class="s">"Done"</span><span class="nt">&gt;</span>…<span class="nt">&lt;/ticket&gt;</span>
  <span class="nt">&lt;/results&gt;</span>
  <span class="nt">&lt;facets&gt;</span>
    <span class="nt">&lt;status_facet&gt;</span>
      <span class="nt">&lt;value</span> <span class="na">name=</span><span class="s">"Done"</span> <span class="na">count=</span><span class="s">"6"</span><span class="nt">/&gt;</span> <span class="nt">&lt;value</span> <span class="na">name=</span><span class="s">"Open"</span> <span class="na">count=</span><span class="s">"5"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/status_facet&gt;</span>
  <span class="nt">&lt;/facets&gt;</span>
  <span class="nt">&lt;system-instruction&gt;</span>
    回傳的 3 筆全是 Done: 已解決的工單記載較完整、相似度排名較高。
    但 facets 顯示另有 5 筆 Open 沒進 top-k,
    請改用 search(query, status="Open") 追查進行中的問題。
  <span class="nt">&lt;/system-instruction&gt;</span>
<span class="nt">&lt;/ToolResponse&gt;</span>
</code></pre></div></div>

<p>這裡有兩個關鍵設計。一是 <code class="language-plaintext highlighter-rouge">&lt;facets&gt;</code>: 它告訴 agent「符合的不只你看到這 3 筆，還有 5 筆 Open 沒進 top-k」。相似度搜尋有個偏誤，已解決的工單因為文件寫得完整，分數本來就比進行中的高，於是真正該追的進行中問題反而被擠出 top-k。光看 top-k 是看不到這個的。二是 <code class="language-plaintext highlighter-rouge">&lt;system-instruction&gt;</code>: 它直接在工具輸出裡告訴 agent 下一步該怎麼搜。</p>

<p>這就是 facets 給 agent 的價值: 讓它在 top-k 之外，還能看見整個資料集的全貌。agent 不必一次就達到完美 recall，它本來就會持續地、系統性地探索; 你只要在每次工具輸出裡告訴它整個資料集長什麼樣，它就會順著線索一層層查下去。Jason Liu 那篇的核心洞見講得很到位: tool response 本身就是一種 prompt engineering，你回傳的 XML 結構和 metadata，直接形塑 agent 接下來怎麼思考。這跟前面 SQL 那段是同一個道理，只是這裡夾帶的不是錯誤修正，而是探索路徑。</p>

<h2 id="案例-d-把回饋交給另一個模型">案例 D: 把回饋交給「另一個模型」</h2>

<p>前面三個案例，工具內部的把關不是自家寫的 (SQLGlot、Python grader)，就是自己跑的統計 (facets)。還有一種做法是: 工具內部直接去<strong>呼叫另一個模型</strong>，把當前的 context、相關資料丟過去，要它審查、給出第二意見 (second opinion)，再把那個模型的回饋當成 tool response 帶回來。從主迴圈的視角看，這還是一次普通的 tool call，只是這次 call 的是另一個模型。</p>

<p>先說清楚這招怎麼套到自建 agent 上，因為下面舉的成熟例子剛好都出自 coding agent (那邊的工具生態最成熟，不代表這招只有 coding 能用): 你的 Text-to-SQL agent 生出一條複雜查詢後，可以呼叫一個更強的模型，審查「這條 SQL 真的回答了使用者的問題嗎」; RAG 問答 agent 生成答案後，可以叫另一個訓練不同的模型，對著檢索到的文件檢查有沒有幻覺。這個做法是通用的，只是 coding 圈先把它做成了現成工具。</p>

<p>為什麼不讓 agent 自己重讀一遍就好? 因為<strong>自評不是獨立檢查</strong>。consult-llm 這個工具把話講得很白:</p>

<blockquote>
  <p>「一個模型審查自己的產出，不算是獨立的檢查。就算換到全新的 context，它仍然共享同一套訓練、同樣的先驗，以及許多相同的失誤模式。」
(A model reviewing its own work isn’t an independent check. Even in a fresh context, it shares the same training, priors, and many of the same failure modes.)</p>
</blockquote>

<p>換一個訓練不同的模型，才換得到比較獨立的視角。<a href="https://github.com/raine/consult-llm">consult-llm</a> 的定位就是「在你現有的 agent 工作流裡，直接跟另一個模型要第二意見」，實作是一支 CLI,agent 把 prompt 和相關檔案送進去、指定要問 GPT、Gemini 還是 Grok，回傳值就是那個模型的回饋。</p>

<p>工具大致長這樣 (示意):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@function_tool</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">consult_model</span><span class="p">(</span><span class="n">question</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">files</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">model</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s">遇到難解的問題、想確認方向、或要審查一段產出時呼叫,
    把問題交給另一個模型給獨立回饋。
    question: 你想問的問題、或要它審查的重點
    files: 要附上的相關資料或檔案
    model: 要諮詢哪個模型 (例如 gpt / gemini / o3)
    </span><span class="sh">"""</span>
    <span class="c1"># 工具內部就是去呼叫另一個模型,把它的回饋整段帶回來
</span>    <span class="bp">...</span>
</code></pre></div></div>

<p><a href="https://ampcode.com/news/oracle">Amp</a> (Sourcegraph) 把這招做成內建功能，叫 oracle: 主 agent 跑 Sonnet，但可以呼叫一個背後接 o3 的 oracle 工具，專門拿來審查、除錯、分析、想下一步該怎麼走。他們形容主 agent 跟 oracle 的關係是「一個寫程式、一個分析審查」。這裡有個值得參考的取捨: o3 更強，但也更慢更貴，所以 Amp <strong>刻意不在 system prompt 裡催</strong> agent 動不動就問 oracle，而是讓使用者需要時再明確要求。這點出一條通則: 諮詢另一個模型是工具層最貴的一種回饋，要不要觸發、多常觸發，是你可以調整的設定。</p>

<p>這招甚至不必跨產品。你現在用的 Claude Code，裝上 Codex plugin 後就多一個 <code class="language-plaintext highlighter-rouge">/codex:review</code> 工具: Claude 主 agent 把本地的 git 改動交給 Codex (背後是 GPT) 做 code review，合約還寫死「只做審查、不准順手改」，把 Codex 的審查結果原封不動帶回來。一個 Anthropic 的模型，呼叫一個 OpenAI 的模型來挑自己的毛病。</p>

<p>那「什麼時候該呼叫這個模型」，由誰來決定? 大致有三種做法:</p>

<ul>
  <li><strong>使用者自行觸發</strong>: 需要時才明確叫 agent 去問。Amp 預設就走這條，把決定權留給人，避免無謂的成本。</li>
  <li><strong>寫進前饋規則</strong>: 在 AGENTS.md / system prompt 裡規定「遇到哪類情況 (例如連續改兩次測試還是紅的、或要動到核心模組) 就去呼叫這個工具」，把觸發時機半自動化。</li>
  <li><strong>讓 agent 自行判斷</strong>: 最理想，但也最難。</li>
</ul>

<p>第三條會遇到 <a href="https://blog.aihao.tw/2026/05/19/claude-managed-agents-architecture/">Advisor Strategy</a> 碰過的同一道難題。Advisor Strategy 其實就是這招的一個典型特例: 用便宜的小模型 (Haiku、Sonnet) 當主力，只在卡關時呼叫貴的大模型 (Opus) 當顧問，給一段幾百 token 的精簡建議，目標是「Opus 級的智能、Haiku 級的價格」。它最難的地方不在怎麼把工具接上，而在: <strong>小模型常常沒有足夠的自我覺察，不知道自己已經卡住、該求助了</strong>。也就是說，「判斷一個問題難到需要外援」本身就是個困難問題，而且越弱的模型越判斷不出來: 要有「我搞不定」的判斷力，跟有能力把問題解掉，需要的其實是同一種能力，這形成一個循環依賴。所以實務上，先用前兩種方式 (使用者觸發、寫死規則) 把觸發時機定好，通常比寄望 agent 自己察覺來得穩。</p>

<blockquote>
  <p>編按: 小模型呼叫大模型的 Advisor Strategy,SWE-bench 數字與成本取捨可參考 <a href="https://blog.aihao.tw/2026/06/01/github-claude-caching-harnesses-advisors/">GitHub 與 Claude 的 caching、harness 與 advisor 策略</a>;而多代理在什麼情況反而是反模式 (動輒 3 到 10 倍 token 成本)，見 <a href="https://blog.aihao.tw/2026/05/19/multi-agent-anti-patterns-and-patterns/">Multi-Agent 反模式和業界收斂共識</a>。</p>
</blockquote>

<p>回到本篇主軸: 這個被諮詢的模型回來的東西，一樣是<strong>夾帶在 tool response 裡的回饋</strong>，一樣要可行動。差別只在，前面案例的回饋是你寫死的固定訊息、或自家小 grader 的判定，這裡的回饋是另一個 (通常更強、訓練不同的) 模型當場生成的。它要解的問題，跟「自我感覺良好就交差」其實是同一個，差別只在改用一個獨立的模型來檢查，而不是讓 agent 自己看自己。</p>

<blockquote>
  <p>編按: 「換一個獨立的模型來驗」這個做法，在時機三談單輪驗收時會是主軸 (fresh-context verifier、生成與評估分離)。這裡先在工具層看到它的雛形。</p>
</blockquote>

<h2 id="工具層的四個設計原則">工具層的四個設計原則</h2>

<p>把這幾個案例收斂成四條可以直接拿去用的原則:</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:22px 0;">
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);">1️⃣ 能確定就不用 LLM</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-top:4px;">SQL 驗證用 SQLGlot，不用 judge: 又快又穩又可重現。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);">2️⃣ 語意判斷才用 Judge</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-top:4px;">細緻品質好不好沒有 assert 可寫，才放一個 LLM judge 補規則型檢查的不足。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);">3️⃣ 回饋必須可行動</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-top:4px;">錯誤訊息要附「該怎麼辦」，不是只丟一個 error code。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);">4️⃣ 延遲預算內完成</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-top:4px;">工具層回饋在 tool call 內發生，要注意 latency，別讓把關拖慢整個迴圈。</div>
</div>
</div>

<h2 id="接下來-單步正確不等於整體完成">接下來: 單步正確，不等於整體完成</h2>

<p>工具層這道迴圈很便宜、很高頻，但它有一個結構性的限制: 它只看得到單一次 tool call。</p>

<p>每一步都驗得對，只代表「這一步」對。十次工具呼叫全部驗過，整輪的產出仍然可能沒達標: 需求漏做了一半、該跑的整體驗證根本沒跑，這些都不是任何一次單步檢查看得見的。SQLGlot 確認每一句 SQL 都合法，但它不會告訴你「這三句 SQL 加起來，有沒有真的回答使用者的問題」。</p>

<p>完成與否，得從整輪產出的層級來驗收。誰來看「這一輪的產出」，是時機三 (單輪結束的驗收) 要處理的事，不能因為「模型覺得做完了」就算數。不過下一篇先談一個性質不同、由人主動觸發的回饋: 在這一輪還沒結束時，使用者怎麼即時介入 (時機②,steering 與 interrupt)。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Tool Use" /><category term="Context Engineering" /><summary type="html"><![CDATA[📚 給 Agent 開發者的駕馭工程 (全 9 篇) 1. 基礎: Deep Agent 的六項內建能力 2. 核心: Agent 要的是回饋迴路，不是完美提示 3. 回饋時機一: 工具回傳值，是寫給 agent 的回饋 (本篇) 4. 回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent 5. 回饋時機三: 單輪結束的驗收，Goal 與 Outcomes 6. 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron 7. 進階: 自我改進 Harness, Meta-Harness 與爬坡 8. 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson 9. 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建? 🎞️ 搭配演講投影片: 給 Agent 開發者的 Harness+Loop Engineering]]></summary></entry><entry><title type="html">給 Agent 開發者的駕馭工程 (4): 回饋時機二: 兩次 model request 之間,把訊息注入執行中的 agent</title><link href="https://blog.aihao.tw/2026/06/26/harness-engineering-4-mid-run-injection/" rel="alternate" type="text/html" title="給 Agent 開發者的駕馭工程 (4): 回饋時機二: 兩次 model request 之間,把訊息注入執行中的 agent" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/26/harness-engineering-4-mid-run-injection</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/26/harness-engineering-4-mid-run-injection/"><![CDATA[<div class="series-toc" style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:24px 0;font-size:.92em;line-height:1.85;">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📚 給 Agent 開發者的駕馭工程 (全 9 篇)</div>
<div>1. <a href="/2026/06/26/harness-engineering-1-deep-agent-capabilities/">基礎: Deep Agent 的六項內建能力</a></div>
<div>2. <a href="/2026/06/26/harness-engineering-2-what-is-harness-engineering/">核心: Agent 要的是回饋迴路，不是完美提示</a></div>
<div>3. <a href="/2026/06/26/harness-engineering-3-tool-execution-feedback/">回饋時機一: 工具回傳值，是寫給 agent 的回饋</a></div>
<div>4. <strong style="color:var(--text);">回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent</strong> <span style="color:var(--text-secondary);">(本篇)</span></div>
<div>5. <a href="/2026/06/26/harness-engineering-5-goal-and-outcomes/">回饋時機三: 單輪結束的驗收，Goal 與 Outcomes</a></div>
<div>6. <a href="/2026/06/26/harness-engineering-6-outer-loop/">回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</a></div>
<div>7. <a href="/2026/06/26/harness-engineering-7-self-improving/">進階: 自我改進 Harness, Meta-Harness 與爬坡</a></div>
<div>8. <a href="/2026/06/26/harness-engineering-8-model-harness-fit/">收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</a></div>
<div>9. <a href="/2026/06/26/harness-engineering-9-agent-frameworks/">自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</a></div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">🎞️ 搭配演講投影片: <a href="https://ihower.tw/presentation/harness.html">給 Agent 開發者的 Harness+Loop Engineering</a></div>
</div>

<p>agent 還在執行、還在反覆呼叫工具，有一個位置可以把新訊息插進去，馬上影響它接下來的動作。這篇講的就是這個回饋時機 (一輪內、兩次 model request 之間): 它在哪、為什麼在這裡注入不會讓 server 端 API 出錯，以及會從這裡注入的兩類訊息。</p>

<p>第一類是<strong>人中途 steer</strong>: agent 還沒做完，使用者就主動輸入一段話介入，社群把這件事叫 steering。這在 Codex、Claude Code 這類互動式 coding agent 最常見。Codex 在 2026 年 2 月正式加了 <a href="https://developers.openai.com/codex/changelog">mid-turn steering</a>,Claude Code 也讓你<a href="https://code.claude.com/docs/en/how-claude-code-works">在它執行時輸入</a>。</p>

<p>第二類是<strong>程式注入</strong>: 在同一個位置放東西進來的不是人，而是程式，例如一個背景工具跑完把結果送回、或一個外部事件 (webhook、告警) 需要讓 agent 知道。這類目前較少框架實作，少數像 Pydantic AI 把它做成通用機制。</p>

<p>兩類用的是同一個注入位置，差別只在來源是人還是程式。先看這個位置在哪。</p>

<h2 id="這個時機點在哪-兩次-model-request-之間">這個時機點在哪: 兩次 model request 之間</h2>

<p>要看懂這個時機點，得先記得一輪 (Turn) 的內部結構: 它不是「一次問、一次答」，而是「模型 request → tool call → 模型 request → tool call …」反覆好幾次，直到模型不再要求工具、輸出最後答案為止。注入就發生在這個迴圈內部、兩次 model request 之間。</p>

<svg viewBox="0 0 720 248" style="width:100%;max-width:720px;height:auto;margin:24px 0;font-family:-apple-system,'PingFang TC','Noto Sans TC',Helvetica,Arial,sans-serif;" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="s2arr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" style="fill:var(--text-secondary)" /></marker>
<marker id="s2arrA" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" style="fill:var(--accent)" /></marker>
</defs>
<rect x="10" y="56" width="700" height="150" rx="12" style="fill:var(--surface);stroke:var(--accent);stroke-width:1.5" />
<rect x="26" y="44" width="250" height="24" rx="12" style="fill:var(--surface);stroke:var(--accent);stroke-width:1.5" />
<text x="40" y="61" style="fill:var(--accent);font-size:12px;font-weight:700">一輪 Turn (agent 還在進行中)</text>
<rect x="196" y="74" width="328" height="22" rx="11" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.2" />
<text x="360" y="89" text-anchor="middle" style="fill:var(--accent);font-size:10.5px;font-weight:700">↓ 在這裡注入訊息 (人 steer 或程式)</text>
<path d="M 268 96 L 268 116" style="fill:none;stroke:var(--accent);stroke-width:1.2;stroke-dasharray:3 2" marker-end="url(#s2arrA)" />
<path d="M 524 96 L 524 116" style="fill:none;stroke:var(--accent);stroke-width:1.2;stroke-dasharray:3 2" marker-end="url(#s2arrA)" />
<rect x="30" y="120" width="92" height="44" rx="7" style="fill:var(--bg);stroke:var(--border);stroke-width:1.3" />
<text x="76" y="138" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">模型</text>
<text x="76" y="153" text-anchor="middle" style="fill:var(--text-secondary);font-size:10px">request 1</text>
<line x1="122" y1="142" x2="156" y2="142" style="stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#s2arr)" />
<rect x="158" y="120" width="92" height="44" rx="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.3" />
<text x="204" y="146" text-anchor="middle" style="fill:var(--accent);font-size:12px;font-weight:600">🔧 tool calls</text>
<line x1="250" y1="142" x2="284" y2="142" style="stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#s2arr)" />
<rect x="286" y="120" width="92" height="44" rx="7" style="fill:var(--bg);stroke:var(--border);stroke-width:1.3" />
<text x="332" y="138" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">模型</text>
<text x="332" y="153" text-anchor="middle" style="fill:var(--text-secondary);font-size:10px">request 2</text>
<line x1="378" y1="142" x2="412" y2="142" style="stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#s2arr)" />
<rect x="414" y="120" width="92" height="44" rx="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.3" />
<text x="460" y="146" text-anchor="middle" style="fill:var(--accent);font-size:12px;font-weight:600">🔧 tool calls</text>
<line x1="506" y1="142" x2="540" y2="142" style="stroke:var(--text-secondary);stroke-width:1.4;stroke-dasharray:4 3" marker-end="url(#s2arr)" />
<rect x="542" y="120" width="100" height="44" rx="7" style="fill:var(--bg);stroke:var(--border);stroke-width:1.3;stroke-dasharray:4 3" />
<text x="592" y="138" text-anchor="middle" style="fill:var(--text-secondary);font-size:12px;font-weight:600">模型</text>
<text x="592" y="153" text-anchor="middle" style="fill:var(--text-secondary);font-size:10px">輸出答案 (還沒到)</text>
<text x="360" y="186" text-anchor="middle" style="fill:var(--text);font-size:11px">注入發生在每個「tool calls → 下一個 model request」之間 (圖中標記處)</text>
<text x="360" y="200" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">agent 還沒到「輸出答案」這一步，就把新訊息插進這一輪</text>
<text x="706" y="240" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">✏️ 小編製圖</text>
</svg>

<p>注意圖中最後那個「輸出答案」是虛線、還沒到。重點就在這: 不是等 agent 執行完才注入 (那是單輪結束的時機)，而是在它還在迴圈中間、還沒輸出答案時就把訊息插進去。</p>

<h2 id="為什麼不能在-tool-call-中間直接加訊息-server-端的硬規則">為什麼不能在 tool call 中間直接加訊息: server 端的硬規則</h2>

<p>這裡有個很常見的疑問: 如果模型這一輪已經發出了 tool call，但你在它還沒拿到結果時就加一則 user message,API 不會出錯嗎?</p>

<p>會，而且會直接回 400。OpenAI 和 Anthropic 有同一條規則: <strong>模型發出的每一個 tool call，都必須先補上對應的結果，中間不能再放別的訊息。</strong></p>

<ul>
  <li>Chat Completions / Responses API: 每個 <code class="language-plaintext highlighter-rouge">tool_call</code> (Responses 裡叫 <code class="language-plaintext highlighter-rouge">function_call</code>，帶一個 <code class="language-plaintext highlighter-rouge">call_id</code>) 都要有對應的 <code class="language-plaintext highlighter-rouge">tool</code> 結果 (Responses 裡叫 <code class="language-plaintext highlighter-rouge">function_call_output</code>)。少補一個，下一個 request 就會報錯，類似 <code class="language-plaintext highlighter-rouge">No tool output found for function call call_xxx</code>。</li>
  <li>Anthropic Messages API: assistant 回合裡的每個 <code class="language-plaintext highlighter-rouge">tool_use</code>，下一個 user 回合都要有對應的 <code class="language-plaintext highlighter-rouge">tool_result</code>，而且 <code class="language-plaintext highlighter-rouge">tool_result</code> 要排在那個 user 回合的最前面，文字接在後面。</li>
</ul>

<p>OpenAI 在 <a href="https://openai.com/index/unrolling-the-codex-agent-loop/">Unrolling the Codex agent loop</a> 裡把這條規則講得很清楚: 一個沒有配對輸出的 tool call，會讓接下來的 request 直接壞掉。</p>

<p>所以 harness 不能真的把你的訊息放在 tool call 執行到一半的地方。它的做法是: <strong>等當前這次 request 的工具全部執行完、結果都收回來，組成一個 API 合法的狀態，才在下一個 model request 之前把你的訊息加進去。</strong> 這也是為什麼 steering 只能在「兩次 model request 之間」，而不是任意時刻: 那是這個迴圈裡唯一一個對 API 合法、又還沒結束的位置。</p>

<p>舉個具體例子。假設模型這一輪一次發出了兩個 tool call (A 和 B)，你只等到 A 的結果回來，就急著插入「改做 X」:</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:24px 0;">
<div style="flex:1 1 300px;border:1px solid #cf222e;border-radius:8px;padding:14px 16px;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:6px;">❌ 不合法</div>
<div style="font-size:.9em;color:var(--text);margin-bottom:10px;">assistant 發了 tool calls → 還沒等到所有工具結果 → 直接加 user message。下一個 request 送出去就 400。</div>
<div style="font-family:ui-monospace,Menlo,Consolas,monospace;font-size:.82em;line-height:1.75;color:var(--text);border-top:1px solid rgba(207,34,46,.25);padding-top:8px;">
<div><span style="color:var(--text-secondary);">assistant:</span> 呼叫 tool call A、tool call B</div>
<div><span style="color:var(--text-secondary);">tool result:</span> output A</div>
<div><span style="color:#cf222e;font-weight:700;">user:</span> 改做 X　<span style="color:#cf222e;">← 還缺 output B</span></div>
<div style="color:#cf222e;font-weight:700;margin-top:4px;">→ API error</div>
</div>
</div>
<div style="flex:1 1 300px;border:1px solid #1a7f37;border-radius:8px;padding:14px 16px;background:#dafbe1;">
<div style="font-weight:700;color:#1a7f37;margin-bottom:6px;">✅ 合法</div>
<div style="font-size:.9em;color:var(--text);margin-bottom:10px;">assistant 發了 tool calls → 每個 call 的結果都補齊 → 再加 user message → 下一個 request。完全沒問題。</div>
<div style="font-family:ui-monospace,Menlo,Consolas,monospace;font-size:.82em;line-height:1.75;color:var(--text);border-top:1px solid rgba(26,127,55,.25);padding-top:8px;">
<div><span style="color:var(--text-secondary);">assistant:</span> 呼叫 tool call A、tool call B</div>
<div><span style="color:var(--text-secondary);">tool result:</span> output A</div>
<div><span style="color:#1a7f37;font-weight:700;">tool result:</span> output B　<span style="color:#1a7f37;">← 補齊</span></div>
<div><span style="color:var(--text-secondary);">user:</span> 改做 X</div>
<div><span style="color:var(--text-secondary);">assistant:</span> ok</div>
</div>
</div>
</div>

<p>差別只在那一行 <code class="language-plaintext highlighter-rouge">output B</code>: 兩個 tool call 的結果都補齊、湊成合法狀態之後再插入 user message,steering 就成立了。</p>

<h2 id="用途一-人中途-steer">用途一: 人中途 steer</h2>

<p>第一類，也是目前最常見的，是人主動介入，而 steering 和 interrupt 都是 Codex、Claude Code 這類互動式 coding agent 提供的內建功能。這跟一般框架的 human-in-the-loop 不一樣: human-in-the-loop 是你刻意設計一個等待點，讓 agent 停下來等使用者回饋 (最典型的是「執行某個工具之前，先請使用者確認」)，是 agent 跑到預先寫好的位置、主動停下來問你; steering 剛好相反，agent 沒有等待點、也沒打算停，是你在它還在執行時主動輸入。一個是 agent 停下來問你，一個是你主動介入。</p>

<h3 id="兩種強度-steering-與-interrupt">兩種強度: steering 與 interrupt</h3>

<p>使用者主動介入，有兩種強度，差別在要不要中斷正在執行的工具。</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:24px 0;">
<div style="flex:1 1 300px;border:1px solid var(--accent);border-radius:8px;padding:16px 20px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">🧭 不中斷: steering</div>
<div style="font-size:.92em;color:var(--text);">agent 繼續執行，你輸入的訊息先排進佇列，等當前這次 tool calls 的結果到齊、正要發下一個 model request 之前才注入。它沒有中斷，只是下一步多了你的訊息，已建立的 context 全部保留。</div>
</div>
<div style="flex:1 1 300px;border:1px solid #cf222e;border-radius:8px;padding:16px 20px;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:8px;">🛑 中斷: interrupt</div>
<div style="font-size:.92em;color:var(--text);">使用者主動中斷，取消正在執行的工具和這一輪。沒答完的 tool call 還沒有結果，harness 會替它補一則 <code>tool_result</code>，內容就是固定字串 <code>aborted</code> (不是 AI 產生的，是寫死的字)，維持對話對 API 仍然合法，然後停下來等你的新指令。</div>
</div>
</div>

<p><a href="https://agentpatterns.ai/agent-design/steering-running-agents/">AgentPatterns</a> 還提了一個提醒: 如果你發現自己一直要 steer，常常代表一開始的 prompt 給得不夠清楚。手動修正是補救，不是常態，真正該補的往往是讓 agent 一開始就走對方向的前饋指令。這也呼應系列一直強調的: 前饋 (一次做對) 跟回饋 (出錯再修) 要一起設計。</p>

<h2 id="用途二-程式注入-背景工具結果外部事件">用途二: 程式注入 (背景工具結果、外部事件)</h2>

<p>第二類用途，是讓<strong>程式</strong>在同一個位置注入，而不是人。Pydantic AI 文件用一句話總結了這個位置的用途:</p>

<blockquote>
  <p>當執行期間發生某些 agent 應該知道的事，就用這個方法注入: 可能是工具想加入後續脈絡、外部事件需要引導 agent 的計畫，或背景工作完成時要讓 agent 收到結果。</p>
</blockquote>

<p>換句話說，人 steer 只是「外部事件引導 agent 計畫」的一種，人就是那個外部事件;同一個位置也能讓程式注入。常見的有三種:</p>

<div style="display:flex;flex-wrap:wrap;gap:12px;margin:24px 0;">
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:14px 16px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">🔧 背景工具結果</div>
<div style="font-size:.9em;color:var(--text);">長時間工具在背景跑完，把結果送回 agent</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:14px 16px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">📡 外部事件</div>
<div style="font-size:.9em;color:var(--text);">webhook、錯誤告警、聊天平台訊息主動送進來</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:14px 16px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">📝 後續脈絡</div>
<div style="font-size:.9em;color:var(--text);">工具執行中發現該補的資訊，加進對話</div>
</div>
</div>

<p>有提供這個時機點讓你注入的框架並不多: OpenAI Agents SDK、Google ADK 等都沒有。少數例外是 <a href="https://ai.pydantic.dev/">Pydantic AI</a>，它在 v1.101.0 (2026 年 5 月) 加了 <code class="language-plaintext highlighter-rouge">enqueue</code> 方法，把人 steer 和程式注入當成同一件事處理。<code class="language-plaintext highlighter-rouge">enqueue</code> 有兩種模式: <code class="language-plaintext highlighter-rouge">asap</code> (盡快插入，也就是這篇講的時機點，對應官方文件 <a href="https://pydantic.dev/docs/ai/core-concepts/message-history/#injecting-messages-mid-run">Injecting messages mid-run</a> 那一節)，以及 <code class="language-plaintext highlighter-rouge">when_idle</code> (等整輪結束後才插入)。</p>

<h3 id="背景工具結果怎麼送回-agent">背景工具結果怎麼送回 agent</h3>

<p>這三種裡最值得單獨看的是背景工具結果，因為它最能說明程式怎麼用這個位置。重點是它分兩步，剛好不會違反前面那條 API 規則:</p>

<p><strong>第一步 (工具被呼叫時)</strong>: tool call 不等背景任務跑完，而是馬上回一個 <code class="language-plaintext highlighter-rouge">tool_result</code>，內容是一句寫給模型的英文 prompt，像這樣:「Tool ‘X’ is running in background (task N). You will receive the result automatically when it completes. Continue with other work in the meantime.」(意思是: 工具已放到背景執行，完成後會自動把結果送回來)。這個 <code class="language-plaintext highlighter-rouge">tool_result</code> 一補上，原本的 tool call 就配對完成、對話對 API 合法，agent 可以繼續做別的事。</p>

<p><strong>第二步 (背景任務完成時)</strong>: 等背景真的跑完，用 <code class="language-plaintext highlighter-rouge">enqueue</code> 把結果送回去，而這時只能用一則 user 角色訊息 (prompt 像這樣:「Background tool ‘X’ (task N) completed. Result: …」)，不能用 <code class="language-plaintext highlighter-rouge">tool_result</code> (第一步已經回過 tool_result 了)。agent 在下一個 model request 就會讀到它。</p>

<h2 id="小結">小結</h2>

<p>不管來源是人還是程式，這個時機點都是「agent 還在執行時，從外部把訊息送進來」的回饋。下一篇換到 harness 自動觸發的回饋，談時機③: 一輪結束時，該由誰來驗收「這一輪到底做完了沒」。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Tool Use" /><summary type="html"><![CDATA[📚 給 Agent 開發者的駕馭工程 (全 9 篇) 1. 基礎: Deep Agent 的六項內建能力 2. 核心: Agent 要的是回饋迴路，不是完美提示 3. 回饋時機一: 工具回傳值，是寫給 agent 的回饋 4. 回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent (本篇) 5. 回饋時機三: 單輪結束的驗收，Goal 與 Outcomes 6. 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron 7. 進階: 自我改進 Harness, Meta-Harness 與爬坡 8. 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson 9. 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建? 🎞️ 搭配演講投影片: 給 Agent 開發者的 Harness+Loop Engineering]]></summary></entry><entry><title type="html">給 Agent 開發者的駕馭工程 (5): 回饋時機三: 單輪結束的驗收, Goal 與 Outcomes</title><link href="https://blog.aihao.tw/2026/06/26/harness-engineering-5-goal-and-outcomes/" rel="alternate" type="text/html" title="給 Agent 開發者的駕馭工程 (5): 回饋時機三: 單輪結束的驗收, Goal 與 Outcomes" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/26/harness-engineering-5-goal-and-outcomes</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/26/harness-engineering-5-goal-and-outcomes/"><![CDATA[<div class="series-toc" style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:24px 0;font-size:.92em;line-height:1.85;">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📚 給 Agent 開發者的駕馭工程 (全 9 篇)</div>
<div>1. <a href="/2026/06/26/harness-engineering-1-deep-agent-capabilities/">基礎: Deep Agent 的六項內建能力</a></div>
<div>2. <a href="/2026/06/26/harness-engineering-2-what-is-harness-engineering/">核心: Agent 要的是回饋迴路，不是完美提示</a></div>
<div>3. <a href="/2026/06/26/harness-engineering-3-tool-execution-feedback/">回饋時機一: 工具回傳值，是寫給 agent 的回饋</a></div>
<div>4. <a href="/2026/06/26/harness-engineering-4-mid-run-injection/">回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent</a></div>
<div>5. <strong style="color:var(--text);">回饋時機三: 單輪結束的驗收，Goal 與 Outcomes</strong> <span style="color:var(--text-secondary);">(本篇)</span></div>
<div>6. <a href="/2026/06/26/harness-engineering-6-outer-loop/">回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</a></div>
<div>7. <a href="/2026/06/26/harness-engineering-7-self-improving/">進階: 自我改進 Harness, Meta-Harness 與爬坡</a></div>
<div>8. <a href="/2026/06/26/harness-engineering-8-model-harness-fit/">收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</a></div>
<div>9. <a href="/2026/06/26/harness-engineering-9-agent-frameworks/">自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</a></div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">🎞️ 搭配演講投影片: <a href="https://ihower.tw/presentation/harness.html">給 Agent 開發者的 Harness+Loop Engineering</a></div>
</div>

<p>前兩種回饋時機 (工具回傳值、中途注入) 都發生在 agent 工作的途中，不負責判斷「整輪到底做完了沒」。問題就在這: 每一次 tool call 都驗得對，不代表整輪的產出達標。十句 SQL 句句合法，不保證這三句加起來真的回答了使用者的問題; 需求漏做一半、該跑的整體驗證根本沒跑，這些都不是任何單步檢查看得見的。</p>

<p>這一篇處理時機③: 一輪結束時的驗收。</p>

<p>時機①發生在「一次 tool call 內部」，主迴圈甚至感覺不到它在跑。時機③的位置不一樣: agent 在這一輪裡已經想完、把該呼叫的工具全呼叫完、正準備收工，就在它要收工的那個時機點判斷: 這一輪到底做完了沒、該把控制權交還給你，還是讓 agent 繼續跑下去。對應到 Claude Code、Codex 這些成熟工具，這個介入點就是它們 Stop hook 這一層的攔截點。</p>

<p>核心精神一句話: <strong>不能因為「模型覺得自己大概做完了」就算數。</strong> 完成與否，要對著一個停止條件去驗證，沒過就繼續做。</p>

<h2 id="goal-一個可驗證的停止條件">Goal: 一個可驗證的停止條件</h2>

<p>要在單輪結束時驗收，你得先講清楚「做完」長什麼樣。這就是 Goal 這個東西要解的問題。Codex 的 Goal、Claude Code 的 <code class="language-plaintext highlighter-rouge">/goal</code>、Claude Managed Agents 的 Outcomes，都是同一個概念的不同產品化: 給 agent 一個持久的目標 (durable objective)，讓它對著一個評估標準持續修正，直到通過為止。它們是自我修正迴圈 (self-correction loop) 的基本構件 (primitive)。</p>

<p>一個好的 Goal 不是把 prompt 寫長，而是一份精簡的契約，通常要講清楚三件事: 終態 (做完是什麼樣)、證據 (用什麼來驗證)、限制 (過程中不能弄壞什麼)。OpenAI Cookbook 給的<a href="https://developers.openai.com/cookbook/examples/codex/using_goals_in_codex">合約模板</a>大致長這樣:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/goal &lt;期望的最終狀態&gt;
      verified by &lt;用什麼證據驗證&gt;
      while preserving &lt;同時要保住什麼&gt;.
Use &lt;允許的工具與邊界&gt;.
Between iterations, &lt;每一輪之間怎麼選下一步&gt;.
If blocked, &lt;該回報什麼、什麼能解鎖進度&gt;.
</code></pre></div></div>

<p>舉例: 「把 p95 latency 降到 120ms 以下，verified by 壓測報告，while preserving 正確性測試全綠。」這比「改善效能」這種模糊指令好得多，因為它給了 agent 一個知道「什麼時候還不能停」的依據: 從 180ms 降到 135ms 不算完成，降破 120ms 但測試掛了也不算完成。</p>

<p>其實連這份模板都不一定要自己填。任務本身夠清楚的時候，有個更省事的做法: 直接把任務描述交給 Codex 或 Claude，請它幫你改寫成一份夠扎實的 goal。一句 <code class="language-plaintext highlighter-rouge">Help me turn this into a strong /goal: &lt;你的任務描述&gt;</code> 就夠，模型會把終態、證據、限制這三件事補齊，你再順手微調。比起自己對著模板逐欄填，這招更簡單，也比較容易把一個清楚但還沒寫成契約的任務，收斂成可驗證的停止條件。</p>

<p>反過來，有兩類任務不適合套 Goal。一是完成條件模糊的，「把這個變更好」「重構這段程式碼」沒有可靠的完成條件，套上 Goal 只是把含糊換個地方放; 這種情況該做的是先把預期終態、驗證方式、限制定義出來，定義不出來代表問題不在工具，在你還沒想清楚要什麼。二是驗證不是二元判斷的灰色地帶 (例如重現一篇深度學習論文)，這時該在開工前先定義「可信度分級」: 哪些是用程式重建、數值驗證過的「確定」，哪些是重新訓練的替代品、行為接近的「近似」，哪些是原作者沒給隨機種子和 checkpoint、根本「做不到完全照原樣重跑」的部分。拿回來的就不是一句簡化的「重現成功」，而是一份按可信度分層、誠實標注的審計報告。</p>

<h2 id="goal-實戰技巧">Goal 實戰技巧</h2>

<p>契約怎麼開，社群累積了幾個實用招式:</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:22px 0;">
<div style="flex:1 1 300px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">🤖 想不清楚就先跟它聊</div>
<div style="font-size:.9em;color:var(--text-secondary);">連任務意圖都還沒想清楚時，先跟它聊，再請它收斂成 goal: 「請讀這個 session 和 repo，分析想達成的意圖，寫出對應情境的 /goal 提示詞」。</div>
</div>
<div style="flex:1 1 300px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">📊 進度儀表板</div>
<div style="font-size:.9em;color:var(--text-secondary);">讓 agent 規律更新一個 <code>goal.html</code>，把目前進度、做了什麼、還差什麼畫出來，長時間跑的時候特別有用。</div>
</div>
<div style="flex:1 1 300px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">☑️ Checklist 勾選</div>
<div style="font-size:.9em;color:var(--text-secondary);">先寫一份 checklist 或分階段的設計文件，讓它一條條勾掉，把模糊的「做完」拆成可逐項驗的小目標。</div>
</div>
<div style="flex:1 1 300px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">📄 Goal 放檔案</div>
<div style="font-size:.9em;color:var(--text-secondary);"><code>/goal xxx.md</code>: 把契約寫成檔案，可以版本控制、可以重用，goal 不再是一次性的一句話。</div>
</div>
</div>

<div style="font-size:.82em;color:var(--text-secondary);margin:-4px 0 20px;">招式來源: <a href="https://x.com/meta_alchemist/status/2054214497443995694">@meta_alchemist</a>、<a href="https://x.com/dkundel/status/2054616381568815484">@dkundel</a>、<a href="https://x.com/dotey/status/2061260768729809096">@dotey</a>、<a href="https://x.com/jxnlco/status/2057940480998932954">@jxnlco</a></div>

<p>這裡其實藏了一個工程師角色的轉變: 你交付的不再是一句 prompt，而是一份「目標 + 邊界」的契約，然後讓一個迴圈去「提示 → 讀產出 → 判斷完成沒 → 再提示」。思考單位從「一次對話 turn」換成「一輪工作 round」。但這個攔截點背後到底怎麼判斷「做完了沒」，三者的實作分歧很大，而這個分歧正是這篇的重點。</p>

<h2 id="三種實作沿著裁判有多獨立排開">三種實作，沿著「裁判有多獨立」排開</h2>

<p>同一個問題: 工作的模型自己說做完了，能信嗎? 這裡整理三種不同的實作來研究看看。</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:24px 0;">
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">Codex Goals</div>
<div style="font-size:.9em;color:var(--text-secondary);">harness 自己不判斷完成與否，只在 thread 閒置時重播同一份合約 prompt。完成與否由<strong>主模型自我審計</strong>，自己呼叫 <code>update_goal</code> 宣告。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">Claude Code /goal</div>
<div style="font-size:.9em;color:var(--text-secondary);">每輪結束把<strong>刪減版 transcript</strong> 交給獨立的 <strong>Haiku</strong> 裁決。沒過就把 Haiku 寫的診斷注入主 thread，逼著繼續。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">Managed Agents Outcome</div>
<div style="font-size:.9em;color:var(--text-secondary);">主 agent 交出產出物後，<strong>全新 context 的 grader</strong> 拿著 rubric 實際操作 artifact 逐條驗收。沒過帶著逐條缺失進下一輪。</div>
</div>
</div>

<h2 id="實作一-codex-goal沒有裁判的自我審計">實作一: Codex /goal，沒有裁判的自我審計</h2>

<blockquote>
  <p>編按: 以下 Codex 的描述來自<strong>官方文件</strong>加上<strong>讀開源原始碼</strong> (goal spec、<code class="language-plaintext highlighter-rouge">continuation.md</code>，連結附在文中，可自行核對)，不是從外部行為推測的。</p>
</blockquote>

<p>先看 Codex。它的表面行為很單純: 一輪結束會自動續跑，直到目標達成。但 Codex 是開源的，讀<a href="https://github.com/openai/codex/tree/main/codex-rs/ext/goal">原始碼</a>就會發現一件事: 它根本<strong>沒有第二個模型在判定</strong>。</p>

<p>Codex 這邊的 harness 是一個確定性的狀態機，沒有任何 LLM 參與「做完了沒」的判斷。它做的唯一一件事是: 每當 thread 閒置，查一眼資料庫裡 <code class="language-plaintext highlighter-rouge">goal.status</code> 還是不是 <code class="language-plaintext highlighter-rouge">Active</code>。是，就把 <code class="language-plaintext highlighter-rouge">continuation.md</code> 這份模板重新注入、開一個新的 turn 續跑; 不是 (complete / blocked / paused)，就停。所以「沒完成」這件事在 Codex 裡其實沒有對應的動作: 不是有誰判它沒過，而是主模型結束這一輪時<strong>沒有呼叫 <code class="language-plaintext highlighter-rouge">update_goal(complete)</code></strong>，僅此而已。完成的宣告權完全在主模型自己手上，而且呼叫了就算數，沒有任何人覆核。</p>

<svg viewBox="0 0 700 268" style="width:100%;max-width:680px;height:auto;display:block;margin:24px auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Codex /goal 自我審計迴圈示意圖">
<defs>
<marker id="cdxB" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:var(--accent)" /></marker>
<marker id="cdxG" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:#1a7f37" /></marker>
</defs>
<rect x="462" y="8" width="160" height="34" rx="8" style="fill:#dafbe1;stroke:#1a7f37;stroke-width:1.5" />
<text x="542" y="30" text-anchor="middle" style="fill:#1a7f37;font-size:11.5px;font-weight:700">update_goal(complete) ✅</text>
<rect x="18" y="62" width="168" height="60" rx="8" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="102" y="88" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">注入 continuation.md</text>
<text x="102" y="107" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">每輪同一份合約 prompt</text>
<rect x="236" y="62" width="176" height="60" rx="8" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" />
<text x="324" y="88" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">主 agent 跑一輪</text>
<text x="324" y="107" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">推理 + 工具呼叫</text>
<rect x="462" y="56" width="160" height="72" rx="8" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="542" y="86" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">自我審計</text>
<text x="542" y="105" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">達成目標?</text>
<line x1="186" y1="92" x2="232" y2="92" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#cdxB)" />
<line x1="412" y1="92" x2="458" y2="92" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#cdxB)" />
<line x1="542" y1="56" x2="542" y2="46" style="stroke:#1a7f37;stroke-width:1.5" marker-end="url(#cdxG)" />
<text x="552" y="54" style="fill:#1a7f37;font-size:10.5px">是</text>
<path d="M542,128 V196 H102 V126" style="fill:none;stroke:var(--accent);stroke-width:1.5" marker-end="url(#cdxB)" />
<text x="324" y="189" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">否: 沒呼叫 update_goal → thread idle 且 goal.status = Active → 再注入</text>
<text x="350" y="230" text-anchor="middle" style="fill:var(--text);font-size:11.5px;font-weight:600">判定者就是主 agent 自己，全程沒有第二個模型</text>
<text x="690" y="258" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">✏️ 小編製圖</text>
</svg>

<p>也因為判定者就是工作者本人，Codex 的續跑 turn 會帶著主模型完整的 (加密) 推理脈絡一路重播。這很合理: 它需要靠自己的推理歷史才知道接下來該做什麼。</p>

<p>判定權交在主模型手上，Codex 靠兩層 prompt 紀律約束它，防止「過早宣告完成」:</p>

<ul>
  <li><strong>Tool 合約寫滿消極條款。</strong> <a href="https://github.com/openai/codex/blob/main/codex-rs/ext/goal/src/spec.rs"><code class="language-plaintext highlighter-rouge">update_goal</code> 的 description</a> 一連串「only when / do not」: 只有在目標真的達成、沒有剩餘工作時才能設 <code class="language-plaintext highlighter-rouge">complete</code>; 不准因為預算快用完或想停工就標完成; 這個 tool 也不能拿來暫停或恢復 (pause/resume，那些狀態保留給使用者和系統)。<code class="language-plaintext highlighter-rouge">blocked</code> 更嚴格: 同一個阻礙原因要連續三個 goal turn 重複出現才准標記。</li>
  <li><strong>Continuation prompt 寫滿積極程序。</strong> <a href="https://github.com/openai/codex/blob/main/codex-rs/prompts/templates/goals/continuation.md"><code class="language-plaintext highlighter-rouge">continuation.md</code></a> 裡有一段 Completion audit，逼模型把完成「當成尚未證明」，從目標拆出每個需求，逐項找出能證明它的證據，實際去檢查檔案、指令輸出、測試結果。原文的要求相當嚴格:</li>
</ul>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>... treat completion as unproven and verify it against the actual current state ...
The audit must prove completion, not merely fail to find obvious remaining work.
</code></pre></div></div>

<p>(把完成視為尚未證明，對著當前真實狀態逐項驗證 … 這份審計必須證明完成，而不是只因為「沒看到明顯的剩餘工作」就算數。)</p>

<p>換句話說，Codex 沒有外部裁判，但它強迫主模型把「當前 worktree 和外部狀態」當成權威依據去自查，試圖在自我審計的框架裡逼近獨立判定的精神。</p>

<blockquote>
  <p>編按: 這正好是「別只看名字、要看實作」的好例子。網路上有流量不錯的技術文章明確寫 Codex 的 goal mode「用一個獨立小模型每回合評分」，還煞有介事給了 <code class="language-plaintext highlighter-rouge">check_model = "o4-mini"</code> 的設定範例。但翻原始碼就知道 <code class="language-plaintext highlighter-rouge">ext/goal/</code> 裡根本沒有第二個模型、沒有 grader。這是把「理想中該有的樣子」投射成「它已經這樣做了」的誤讀。harness 的細節，看文件不如自己攔一次封包、讀一次程式碼。</p>
</blockquote>

<h2 id="實作二-claude-code-goal獨立-haiku-讀刪減-transcript">實作二: Claude Code /goal，獨立 Haiku 讀刪減 transcript</h2>

<blockquote>
  <p>編按: 以下 Claude Code <code class="language-plaintext highlighter-rouge">/goal</code> 的描述，整體行為來自<strong>官方文件</strong>，而 Haiku、刪減 transcript、structured output 這些官方沒寫明的內部細節，是小編用 <a href="https://mitmproxy.org/">mitmproxy</a> <strong>實際攔封包觀察</strong>到的，可能隨版本改動。</p>
</blockquote>

<p>Codex 把判定權留在主模型自己手上; Claude Code 走相反路線，把判定交給另一個模型。它的 <code class="language-plaintext highlighter-rouge">/goal</code> 原理是: 主迴圈 (Opus) 跑完一輪、自認目標達成、正要收工時，harness 不直接把控制權交還給你，而是另外發一個請求，把整段對話交給一個獨立的小模型，問它一句:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Based on the conversation transcript above,
has the following stopping condition been satisfied?

&lt;stopping condition&gt;

Answer based on transcript evidence only.
</code></pre></div></div>

<p>這裡最值得注意的是最後那句 <code class="language-plaintext highlighter-rouge">Answer based on transcript evidence only</code>:</p>

<div style="border:1px solid #cf222e;border-radius:8px;padding:12px 18px;margin:18px 0;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:6px;">⚠️ transcript evidence only</div>
<div style="font-size:.92em;color:var(--text);">判定者<strong>只看對話紀錄，不會重新打開檔案、不會操作 artifact</strong>。它能不能判對，完全取決於主 agent 有沒有把證據「印」在對話裡; 沒印出來的，對它就等於不存在。這條限制，等下對照實作三的 Outcome (裁判真的會去操作 artifact) 會更有感。</div>
</div>

<p>機制上，還有三個細節值得記:</p>

<ul>
  <li><strong>判定者是一個獨立的小模型 (預設 Haiku)</strong>，而且用的是另一份 system prompt (「你是一個 Claude agent」而不是「你是 Claude Code」)，跟<a href="https://code.claude.com/docs/en/goal">文件</a>寫的「預設用小而快的模型」一致。值得注意的是，這個驗收 request 最上層的 <code class="language-plaintext highlighter-rouge">tools</code> 是空的: 它只能讀 transcript，不能再 fetch、不能再搜尋，純做驗收、不繼續工作。</li>
  <li><strong>這份 transcript 是重組過的副本，不是把原 request 原樣轉發。</strong> 保留下來的是裁判判斷用得上的證據: 原始 <code class="language-plaintext highlighter-rouge">/goal</code> 指令、assistant 每一句可見回覆 (含中途敘述和最後總結)、每個 tool_use 的 name 與 input (所以它看得到 <code class="language-plaintext highlighter-rouge">WebFetch</code> 真的被呼叫過、查了什麼)，以及文字型的 tool_result (搜尋結果、網頁摘要、GitHub profile) 當判斷依據。拿掉的則是整塊 thinking 與 signature (主迴圈那幾輪的 thinking 本來就被清空、只留簽章給 API 驗證，跨模型的加密推理帶不過去)，還有最前面那一整包 claudeMd 工作環境; <code class="language-plaintext highlighter-rouge">ToolSearch</code> 回傳的那種結果 (裡面是一個個可呼叫的工具物件、不是文字)，因為驗收 request 已經沒有這些工具 (<code class="language-plaintext highlighter-rouge">tools</code> 是空的)，整段被換成一句 <code class="language-plaintext highlighter-rouge">[Tool references removed - tools no longer available]</code>。所以 Haiku 看不到主模型的推理，只看它「攤在 transcript 上的證據」(這跟 Codex 把完整推理一路保留剛好相反)。</li>
  <li><strong>用 structured output (結構化輸出) 強制回 <code class="language-plaintext highlighter-rouge">{ok, reason, impossible}</code></strong>，不能含糊其辭。三個欄位各管一件事: <code class="language-plaintext highlighter-rouge">ok</code> 回答「目標達成了沒」、<code class="language-plaintext highlighter-rouge">reason</code> 是判斷理由 (判 false 時要講清楚還缺什麼)、<code class="language-plaintext highlighter-rouge">impossible</code> 則回答「這目標是不是根本辦不到」。<code class="language-plaintext highlighter-rouge">impossible</code> 是用來處理一種棘手情況: 萬一目標本來就完成不了 (條件自相矛盾、或依賴一個拿不到的資源)，沒有這欄的話，Stop hook 會一直擋著不讓收工、無限重試下去。所以裁判一旦判 <code class="language-plaintext highlighter-rouge">impossible: true</code>，就算 <code class="language-plaintext highlighter-rouge">ok</code> 還是 false 也會放行結束。這裡還特別加了一道限制: 「辦不到」必須由獨立裁判看 transcript 證據自己判定，主 agent 自己宣稱「做不到」只算一項證據、不算證明，免得它喊一句「我放棄」就提早收工。</li>
</ul>

<p>判定結果分兩條路。判「是」: 清除目標，在逐字稿記一筆已達成，把控制權還給你。判「否」: Stop hook 擋下停止，把 Haiku 寫的 <code class="language-plaintext highlighter-rouge">reason</code> 包成一段 <code class="language-plaintext highlighter-rouge">Stop hook feedback:</code> 注入回主 thread，當成下一輪該怎麼做的依據。因為 Haiku 被要求「引用 transcript 裡缺了什麼」，這個 reason 是針對性的診斷，每輪都不一樣，直接點出缺什麼。</p>

<svg viewBox="0 0 700 324" style="width:100%;max-width:680px;height:auto;display:block;margin:24px auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Claude Code /goal fork 判定示意圖">
<defs>
<marker id="ccB" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:var(--accent)" /></marker>
<marker id="ccG" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:#1a7f37" /></marker>
<marker id="ccR" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:#cf222e" /></marker>
</defs>
<rect x="24" y="20" width="194" height="56" rx="8" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" />
<text x="121" y="44" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">主 agent 跑一輪 (Opus)</text>
<text x="121" y="62" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">自認達成，要收工</text>
<rect x="262" y="20" width="120" height="56" rx="8" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="322" y="44" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">Stop hook</text>
<text x="322" y="62" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">攔下收工</text>
<line x1="218" y1="48" x2="258" y2="48" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#ccB)" />
<line x1="322" y1="76" x2="322" y2="112" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#ccB)" />
<text x="338" y="94" style="fill:var(--text-secondary);font-size:10.5px">複製刪減版 transcript</text>
<text x="338" y="108" style="fill:var(--text-secondary);font-size:10.5px">(剝除 thinking)</text>
<rect x="232" y="116" width="180" height="64" rx="8" style="fill:#fff8c5;stroke:#9a6700;stroke-width:1.5" />
<text x="322" y="142" text-anchor="middle" style="fill:#9a6700;font-size:12.5px;font-weight:700">獨立 Haiku 裁判</text>
<text x="322" y="161" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">另一個模型，只讀 transcript</text>
<path d="M412,148 H575 V244" style="fill:none;stroke:#1a7f37;stroke-width:1.5" marker-end="url(#ccG)" />
<text x="486" y="140" text-anchor="middle" style="fill:#1a7f37;font-size:10.5px">ok: true</text>
<path d="M232,148 H170 V244" style="fill:none;stroke:#cf222e;stroke-width:1.5" marker-end="url(#ccR)" />
<text x="198" y="140" text-anchor="middle" style="fill:#cf222e;font-size:10.5px">ok: false</text>
<rect x="470" y="246" width="210" height="54" rx="8" style="fill:#dafbe1;stroke:#1a7f37;stroke-width:1.5" />
<text x="575" y="270" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:700">清除目標，交還控制 ✅</text>
<text x="575" y="288" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">goal 達成</text>
<rect x="20" y="246" width="300" height="54" rx="8" style="fill:#ffebe9;stroke:#cf222e;stroke-width:1.5" />
<text x="170" y="270" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:700">reason 注入回主 thread</text>
<text x="170" y="288" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">針對性診斷 → 主 agent 繼續下一輪</text>
<path d="M20,273 H10 V48 H24" style="fill:none;stroke:var(--accent);stroke-width:1.5" marker-end="url(#ccB)" />
<text x="690" y="316" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">✏️ 小編製圖</text>
</svg>

<p>這裡有個有趣的隱性迴圈: 因為判定者只看得到 transcript 上浮現的證據，如果主 agent 做了某件事卻沒把證據印出來，它會收到「transcript 證據不足」的結果，等於 Haiku 在逼主 agent 把驗證結果明確印出來。另一面是，判定靠的是語意理解而非字面比對，所以停止條件就算寫得不夠精準，只要語意夠清楚，小模型多半還能照語意判斷，而不是拘泥字面。</p>

<p>把兩家的判定標準擺在一起看，剛好相反: Codex 把標準攤開給工作模型自己看 (continuation.md 的 audit 條款),Claude Code 則把判定標準密封在 Haiku 的 system prompt 裡，主模型完全不知道自己會被用什麼標準衡量。一邊是讓工作模型看著標準自評，一邊是把標準藏起來、由另一個模型來判。</p>

<h3 id="判沒完成之後兩家的主-thread-差在哪">判「沒完成」之後，兩家的主 thread 差在哪</h3>

<p>把兩家放在一起看，「沒完成」這個訊號的性質完全不同:</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>Codex /goal</th>
      <th>Claude Code /goal</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>回饋形式</strong></td>
      <td>重播同一份 continuation.md，只有 budget 數字在動</td>
      <td>Haiku 寫的針對性診斷，具體指出 transcript 裡缺了什麼</td>
    </tr>
    <tr>
      <td><strong>下一步依據</strong></td>
      <td>環境證據 (測試失敗、編譯錯誤) + 自己保留的推理</td>
      <td>外部裁判指出缺口</td>
    </tr>
    <tr>
      <td><strong>context 代價</strong></td>
      <td>每輪累積一份完整模板 (靠 prompt cache 吸收)</td>
      <td>每輪多一次 Haiku call，主 thread 只長一小段 reason</td>
    </tr>
    <tr>
      <td><strong>失效模式</strong></td>
      <td>模型自己有侷限時沒有外部視角來修正，可能連錯好幾輪</td>
      <td>Haiku 誤判: 會被自信的收尾語言騙過</td>
    </tr>
  </tbody>
</table>

<p>Codex 的「沒完成」是同一份指令重播，模型要繼續做什麼，完全靠它自己推理的連續性; Claude Code 的「沒完成」則是一段外部裁判寫的具體診斷，注入主 thread，明白告訴它還缺什麼。這也解釋了為什麼 Codex 必須保留推理、而 Claude Code 可以放心清掉 thinking: 前者要靠自己的推理紀錄才知道做到哪、下一步做什麼，後者則有外部診斷可依循。</p>

<h2 id="實作三-managed-agents-outcome拿著-rubric-操作-artifact">實作三: Managed Agents Outcome，拿著 rubric 操作 artifact</h2>

<blockquote>
  <p>編按: 以下 Outcome 的描述來自 <strong>Anthropic 官方文件與 cookbook</strong>。</p>
</blockquote>

<p>第三種把獨立性推到底。Claude <a href="https://platform.claude.com/docs/en/managed-agents/define-outcomes">Managed Agents 的 Outcome</a> 在你定義目標時，自動配一個 grader，用<strong>全新的 context window</strong> 拿著 rubric 來驗收主 agent 的產出物。重點是「全新 context」: grader 看不到主 agent 的推理和敘事，主 agent 那句「目標達成 ✅」騙不到它。</p>

<p>而且它不只是「看」最終產物。Anthropic 在 <a href="https://www.anthropic.com/engineering/harness-design-long-running-apps">Harness design for long-running apps</a> 裡揭露了更強的版本: 評估者拿著 Playwright，像真實使用者一樣去點擊執行中的應用程式，測 UI、打 API、查資料庫狀態，再對 rubric 每一條評分。這已經不是讀文字了，是會動手的裁判。沒過，就帶著逐條缺失的回饋進下一輪迭代，<code class="language-plaintext highlighter-rouge">max_iterations</code> 預設 3、最多 20。</p>

<svg viewBox="0 0 700 282" style="width:100%;max-width:680px;height:auto;display:block;margin:24px auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Managed Agents Outcome 獨立 grader 示意圖">
<defs>
<marker id="ocB" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:var(--accent)" /></marker>
<marker id="ocG" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:#1a7f37" /></marker>
<marker id="ocR" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:#cf222e" /></marker>
</defs>
<rect x="14" y="40" width="140" height="58" rx="8" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" />
<text x="84" y="65" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">主 agent</text>
<text x="84" y="83" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">(generator)</text>
<rect x="224" y="36" width="140" height="66" rx="8" style="fill:var(--bg);stroke:var(--text-secondary);stroke-width:1.5" />
<text x="294" y="62" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">artifact</text>
<text x="294" y="80" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">(檔案 / app)</text>
<rect x="486" y="36" width="200" height="66" rx="8" style="fill:var(--surface);stroke:var(--accent);stroke-width:1.5;stroke-dasharray:5 3" />
<text x="586" y="60" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">獨立 grader agent</text>
<text x="586" y="78" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">全新 context + rubric</text>
<line x1="154" y1="69" x2="222" y2="69" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#ocB)" />
<text x="188" y="60" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">產出</text>
<line x1="486" y1="69" x2="366" y2="69" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#ocB)" />
<text x="426" y="60" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">拿 rubric 操作</text>
<text x="426" y="88" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">點 UI / 打 API / 查 DB</text>
<line x1="586" y1="102" x2="586" y2="198" style="stroke:#1a7f37;stroke-width:1.5" marker-end="url(#ocG)" />
<text x="604" y="150" style="fill:#1a7f37;font-size:10.5px">satisfied</text>
<path d="M520,102 V150 H180 V198" style="fill:none;stroke:#cf222e;stroke-width:1.5" marker-end="url(#ocR)" />
<text x="350" y="143" text-anchor="middle" style="fill:#cf222e;font-size:10.5px">needs_revision</text>
<rect x="486" y="200" width="200" height="48" rx="8" style="fill:#dafbe1;stroke:#1a7f37;stroke-width:1.5" />
<text x="586" y="229" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:700">交付 ✅</text>
<rect x="24" y="200" width="312" height="48" rx="8" style="fill:#ffebe9;stroke:#cf222e;stroke-width:1.5" />
<text x="180" y="222" text-anchor="middle" style="fill:var(--text);font-size:11.5px;font-weight:700">逐條缺失回饋</text>
<text x="180" y="239" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">主 agent 再改一輪</text>
<path d="M24,224 H10 V69 H14" style="fill:none;stroke:var(--accent);stroke-width:1.5" marker-end="url(#ocB)" />
<text x="350" y="272" text-anchor="middle" style="fill:var(--text);font-size:11.5px;font-weight:600">裁判完全獨立: 不看 transcript，直接對成品逐條驗收</text>
<text x="690" y="272" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">✏️ 小編製圖</text>
</svg>

<p>為什麼這種「事後拿產出物獨立驗收」會比讓 agent 自己驗有效? 上面那篇 Harness design 正是在強調評估者要獨立。它的觀察是: 要 agent 評自己做的東西，它傾向自信地稱讚，哪怕在人眼中品質明顯普通; 而且 context 裡的錯誤推理會不斷累積，前面說服了自己「方向對」，後面就一路錯下去。而它給的結論很乾脆 (翻譯):</p>

<blockquote>
  <p>「調出一個挑剔的獨立評估者，比教會 agent 自我批判容易得多。」</p>
</blockquote>

<p>換句話說，問題不在「能不能批判」，而在「誰來批判」: 同一個 agent 很難對自己的產出下得了手，換一個全新 context 的 agent、再把它調得多疑，反而簡單得多。Cookbook 的 <a href="https://platform.claude.com/cookbook/managed-agents-cma-verify-with-outcome-grader">Outcome grader 範例</a>有一段把這件事點得更具體 (翻譯):</p>

<blockquote>
  <p>「知道評分標準的撰寫者，仍然是在替自己的作品打分。只要它認為自己通過了，它就會說通過。評分者則別無選擇，只能真的去做那些檢查: 它在全新的 context window 開始，身上只帶著評分標準和成品。」</p>
</blockquote>

<p>那篇 Cookbook 的實例很具體: 一個研究任務要求財務數字必須出自正式年報 (10-K / 10-Q)。撰寫者第一次拿了一篇第三方新聞報導就提交、自評通過; 獨立 grader 退回，要求 SEC 正式文件。第二次撰寫者找了個 sec.gov 的網址 (看起來合格了),grader 讀了網址發現那是一份 8-K 的新聞稿附件，不是 rubric 要的 10-K，又退回一次。第三次才找到真正的 10-K，通過。自我檢查抓不到這種錯，因為它「覺得自己過了」; 只有獨立 grader 對著 rubric 才會在這條界線上把它擋下來。</p>

<p>這套「生成跟評估交給不同 agent 做」的主張，不只 Anthropic 在講。<a href="https://x.com/RLanceMartin/status/2064397389189071163">Lance Martin</a> 在談 Fable 5 的 loop 設計時也說「獨立、全新 context 的驗證者子代理人 (verifier subagent)，表現往往勝過自我批判」，而 Anthropic 在 <a href="https://www.anthropic.com/engineering/harness-design-long-running-apps">Harness design for long-running apps</a> 裡更直接把一個 agent 拆成 Planner (規劃)、Generator (生成)、Evaluator (評估) 三個角色，結論是「把做事的 agent 和打分的 agent 分開，是解決自評偏差最有效的辦法」。代價也很實在: 同一個 2D 遊戲編輯器的題目，單一 agent 跑 20 分鐘、約 9 美元，完整 harness (含獨立評估者把關) 跑了 6 小時、約 200 美元，20 倍成本。但差別不是「好一點」: 單跑版的遊戲核心功能根本壞掉、不能玩，harness 版能玩。</p>

<h2 id="三種實作的總比較-獨立性與成本">三種實作的總比較: 獨立性與成本</h2>

<p>把三種實作攤在三個維度上:</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>Codex Goals</th>
      <th>Claude Code /goal</th>
      <th>Managed Agents Outcomes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>誰來判定</strong></td>
      <td>主模型自我審計</td>
      <td>獨立的 Haiku</td>
      <td>全新 context 的 grader agent</td>
    </tr>
    <tr>
      <td><strong>harness 角色</strong></td>
      <td>不判斷，只在閒置時重播合約 prompt</td>
      <td>每輪結束送 transcript 裁決</td>
      <td>自動配置 grader 評估迴圈</td>
    </tr>
    <tr>
      <td><strong>看什麼證據</strong></td>
      <td>自己 context 裡的一切 (含推理)</td>
      <td>刪減版 transcript</td>
      <td>只看 artifact，實際操作驗收</td>
    </tr>
    <tr>
      <td><strong>怎麼宣告完成</strong></td>
      <td>主模型呼叫 <code class="language-plaintext highlighter-rouge">update_goal</code></td>
      <td>yes/no + 診斷 reason</td>
      <td>rubric 逐條 pass/fail</td>
    </tr>
    <tr>
      <td><strong>沒完成時的回饋</strong></td>
      <td>沒有診斷，只重播同一份 prompt 模板 (continuation.md)</td>
      <td>一段針對性診斷，指出缺什麼</td>
      <td>逐條列出缺失，最具體</td>
    </tr>
  </tbody>
</table>

<p>這三者其實是同一條軸上的三個刻度: 裁判有多獨立於做事的模型。</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:24px 0;">
<div style="flex:1 1 220px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">自己的思維跟逐字稿</div>
<div style="font-size:.9em;color:var(--text-secondary);">Codex: 裁判就是工作者本人。<strong>資訊量最大</strong> (推理、工具輸出全看得到)，但<strong>完全沒有獨立性</strong>。</div>
</div>
<div style="flex:1 1 220px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">看別人的逐字稿</div>
<div style="font-size:.9em;color:var(--text-secondary);">Claude Code: 裁判讀 transcript。資訊量與獨立性<strong>各取一半</strong>: 證據沒印在 transcript 上，等於不存在。</div>
</div>
<div style="flex:1 1 220px;border:1px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">產出標的物</div>
<div style="font-size:.9em;color:var(--text-secondary);">Outcome: 裁判操作 artifact。<strong>獨立性最大</strong>: 它只驗最後成品、不看模型怎麼說，所以不會被模型的敘事說服。代價是資訊量只剩產出物，<strong>亂改測試、違規操作這類「過程不可接受」的問題看不到</strong>。</div>
</div>
</div>

<p>沒有一家同時拿到「資訊量」與「獨立性」。Codex 把資訊量最大化但完全沒有獨立性; Outcome 把獨立性最大化但只剩產出物，於是違規操作、亂改測試、副作用這類「產出物完美但過程不可接受」的瑕疵成功 (corrupt success)，它就抓不到; Claude Code 介於兩者中間。這是取捨 (trade-off)，不是優劣排名。</p>

<p>這條獨立性排序，學界研究大致也指同個方向: 缺乏外部回饋時，純自我修正容易失敗 (Huang et al., ICLR 2024); 只讀文字的裁判偵測假性完成 (false success) 的能力有限，容易被「自信的收尾語言」帶著走; 相對地，看測試與執行結果的環境回饋、把驗收拆成逐條 rubric、以及能讀檔跑指令的 Agent-as-a-Judge (Zhuge et al., ICML 2025)，可靠度都明顯更高，最後一種甚至接近人類。越往獨立、越會實際操作的那端，判定越可信。</p>

<h3 id="成本-三者的成本花在不同地方">成本: 三者的成本花在不同地方</h3>

<p>獨立性更高，代價就是成本與延遲。先看三者把帳記在哪:</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>Codex Goals</th>
      <th>Claude Code /goal</th>
      <th>Managed Agents Outcomes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>評估動作</strong></td>
      <td>無模型呼叫: 查狀態，毫秒級</td>
      <td>每 turn 一次 Haiku 呼叫</td>
      <td>每 iteration 跑一次 grader agent，數分鐘起跳</td>
    </tr>
    <tr>
      <td><strong>成本花在</strong></td>
      <td>主模型的 context: 模板與推理持續重播</td>
      <td>第二個模型: Haiku 重讀漸長的 transcript</td>
      <td>評估即執行: 操作 artifact 是「跑 agent」的量級</td>
    </tr>
    <tr>
      <td><strong>回饋延遲</strong></td>
      <td>模型自己發現走偏才回頭</td>
      <td>每 turn 一次，最快</td>
      <td>整個 iteration 做完才被抓到</td>
    </tr>
  </tbody>
</table>

<p>三者的成本花在不同地方。Codex 花在主模型的 context 上: 模板和推理都以最貴的費率持續重播，靠 prompt caching 壓下來。Claude Code 花在第二個模型上: Haiku 每輪重讀越來越長的 transcript，但單價低到官方敢直接寫「可忽略」，而且它反過來幫主模型省錢 (清掉 thinking、只注入短 reason)。Outcome 則是評估即執行: grader 要真的把 artifact 跑起來操作，這是跑 agent 的成本量級。</p>

<p>換成相對量級看會更直觀 (以下是粗略量級，不是精確 benchmark)。Codex 自我審計沒有模型呼叫，只查一次狀態; Claude Code 的 Haiku 評估也很便宜，只多跑一次小模型; Outcome 的一次評估則要跑一整個 grader agent (Anthropic 實測一次約 8 分鐘):</p>

<table>
  <thead>
    <tr>
      <th>單次評估的額外開銷</th>
      <th>token 成本</th>
      <th>延遲</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Codex 自我審計</td>
      <td>趨近零 (無模型呼叫，只查狀態)</td>
      <td>趨近零 (折在 turn 裡)</td>
    </tr>
    <tr>
      <td>Claude Code Haiku</td>
      <td>小 (小模型讀一次 transcript)，與 Codex 同量級</td>
      <td>約 1-2 秒</td>
    </tr>
    <tr>
      <td>Outcome grader</td>
      <td>約前兩者的數十倍 (跑一整個 grader agent)</td>
      <td>約 8 分鐘，約前兩者的數百倍</td>
    </tr>
  </tbody>
</table>

<p><strong>光是單次評估，Outcome 大約就是另外兩者的數十倍 token、數百倍延遲。</strong></p>

<p>不過延遲真正的差異不在「評估本身多久」，而在「錯誤方向多久才會被發現」。Claude Code 一個 turn 就被抓; Codex 要等模型自己發現走偏; Outcome 要等整個 iteration 做完。Anthropic 那個 DAW 實測就是這個樣子: Build 第一輪跑了 2 小時 7 分才迎來第一次 8.8 分鐘的 QA，發現核心互動是擺著好看的、不能用，於是第二輪又花 1 小時重做。回饋越獨立越紮實，錯誤被抓到的時間就越晚，等發現時要重做的工作量也越大。所以正確的比較單位不是「每次評估多少錢」，而是「達到同等可靠度要跑幾輪」: 收斂輪數才是成本的最大宗，每輪的評估開銷只是額外多出的一小筆。</p>

<p>把這個單次的數十倍差距乘上整個 goal 的輪數，落差還會放大，但這裡有個轉折: Outcome 的評估單位是「iteration」而非「turn」，而且預設就有 <code class="language-plaintext highlighter-rouge">max_iterations</code> 上限 (預設 3、最多 20)，它<strong>根本不會</strong>驗上百次。這不是巧合: <strong>驗證的單價，直接決定了驗證能做到多細。</strong> Codex 跟 Haiku 一次評估幾乎不花錢，負擔得起每一輪都驗; Outcome 一次評估貴上數十倍、又要好幾分鐘，只能驗少少幾次，而且驗的是整個成品。便宜的驗證逐輪把關，昂貴的驗證只能事後總驗，這個分野不是誰決定的，是成本算出來的。</p>

<p>(再強調一次: 上面只是「驗證」的額外開銷，不是總帳。真正佔大部分的是「做事」的基底成本，跑上百輪主模型工作本身就很可觀，而這部分三者差不多。)</p>

<p>最後值得點出: 三者的文件不約而同教你「把停止條件寫成可被指令輸出證明的形式」。背後的道理是，當條件夠具體、證據夠機械化，裁判是 Haiku 還是模型自己，差異就被壓縮了; 條件越模糊，三者的失效模式差異才會放大。也就是說，你愈把「做完」定義成可機械驗證的形式，愈不需要為了獨立性，去付 Outcome 那種昂貴的代價。</p>

<h2 id="那為什麼-codex-可以不用獨立裁判">那為什麼 Codex 可以不用獨立裁判?</h2>

<blockquote>
  <p>編按: 以下這節 (特別是「OpenAI 把自我審計練進模型」) 屬於<strong>合理推測</strong>，建立在公開研究與工具行為上，但沒有官方 model card 或論文直接背書，讀的時候請自行打折。</p>
</blockquote>

<p>讀到這裡會有個疑問: Anthropic 自家文章明確說「把做事的 agent 和打分的 agent 分開，是解決自評偏差最有效的辦法」，前面引的研究也把純自評放在最弱的一檔。那 Codex 的自我審計，豈不是站在大家公認最弱的那一檔?</p>

<h3 id="先拆成兩層-harness-層與模型層">先拆成兩層: harness 層與模型層</h3>

<p>ihower 的看法是: 這不代表 OpenAI 做錯了。「誰來判定完成」這件事，拆成兩層看就清楚了: <strong>harness 層</strong> (在模型外面怎麼接驗證: 換一個獨立裁判、還是只注入一段 prompt 要模型自己審) 和 <strong>模型層</strong> (模型本身的驗證能力到底行不行)。這正好對上系列第二篇的 Agent = Model + Harness。Anthropic 從 harness 層下手，多接一個獨立的模型來把關; OpenAI 則把重心放在模型層: 與其在 harness 上換裁判，不如把模型「驗證自己」這件事直接練起來。LangChain 的 Vivek Trivedy 的<a href="https://x.com/Vtrivedy10/status/2063068668049740254">立場</a>很直接: 把「驗證」當成一門要認真拆解的任務; 與其糾結 harness 架構，真正的瓶頸是模型的驗證能力本身，要靠訓練補強。</p>

<h3 id="把驗證練進模型">把驗證練進模型</h3>

<p>而這正是這半年的研究焦點: <strong>驗證 (verification) 是一種可以被訓練進模型的能力，而不只是 harness 怎麼設計的問題。</strong> Trivedy 指出，即時驗證本質上是「生成」的一個子領域，只是模型目前還很弱; 但他們在 Terminal Bench 的 harness 實驗裡發現，讓模型做自我驗證 (self-verification) 帶來明顯的效能提升，而真正的方向是「透過 harness engineering 加上訓練，逼模型把『驗證』這件任務，像它生成解題程式碼一樣認真拆解、做徹底」。CMU 的 Chen Wu 那條研究也是同一個主張: 業界一直在訓練模型「生成」得更好，為什麼不訓練它「驗證」得一樣好? 他們展示了訓練模型去精準定位自己的錯誤，讓同一個模型在困難數學題上的準確率接近翻倍。Philipp Schmid 在 <a href="https://www.philschmid.de/closing-the-loop">Can We Close the Loop in 2026?</a> 把這個方向講得相當清楚: 今天 production 裡絕大多數的「回饋迴路」都還是 scaffolding (在模型外面搭一層驗證步驟)，但研究前沿正在走向 spontaneous verification (自發式驗證)，驗證不再是外掛上去的，而是模型預設就會做的事。</p>

<p>從這個角度看，「OpenAI 在 post-training 階段針對 goal 自我審計加強過模型」這個推測就很合理: 連有人對著單一 goal 把 Codex 跑超過 120 小時都撐得住，靠的就是模型本身被練到能持續自我審計、不中途降低標準。這也跟一個長期觀察對得上: GPT 系模型的指令遵循 (instruction following) 一向是強項，而 Codex /goal 那份寫滿「only when / do not」的自我審計合約，本來就依賴模型「把指令當真」的能力; 在這個基礎上，OpenAI 很可能又針對自我審計另外做過 post-training 強化。能力既然練進了模型，harness 那層就能做得很薄，這也正是 <a href="https://blog.aihao.tw/2026/02/17/bitter-lesson-agent-harness/">Bitter Lesson</a> 的方向: 與其在 harness 上堆手工規則和外部仲裁機制，不如相信模型能力與算力終究會把這些招式收進去。Fable 5 這代模型「擅長在迴圈裡自我修正」已經寫進官方賣點，等於模型層面就為 self-correction loop 最佳化過 (這點留到系列 8 再談)。薄 harness 還有個現實好處: 成本。Trivedy 那串討論底下有人補充，點出高效驗證真正的瓶頸就是成本: 你能不能驗證一千個輸出，而不在算力上付出過高代價? 在會跑數小時、數天的長 goal 上，每回合都加一個外部裁判模型 (judge model)，等於每一步都多付一次推理的延遲與 token; 省掉這個每回合重讀 transcript 的裁判，正是 OpenAI 不走外部裁判的關鍵考量之一。</p>

<h3 id="但這條路是脆弱的">但這條路是脆弱的</h3>

<p>當然，把重心壓在模型層不是沒有代價，而且在訓練到位之前是脆弱的。有一篇關於自我歸因偏差 (self-attribution bias) 的研究發現: 當模型評估「出現在自己對話歷史裡」的行為時，判斷力會選擇性退化，而且在「錯誤的行為」上退化最嚴重，偏偏那正是最需要被抓出來的危險案例。Codex /goal 的 continuation prompt 正是把指令注入同一條 thread 的歷史，完全落在這個情境裡。OpenAI 自己的修補紀錄也透露這條路不好走: 他們改過好幾版 continuation prompt (因為早期模型會把目標偷偷縮小成更容易通過的子集)，甚至把「已經過了多少時間」的回報從 prompt 裡拿掉，因為模型看到時間一直在減少，反而會開始草率行事; 社群也有 issue 實測抓到 goal 已宣稱完成、使用者一句「再檢查一次」又冒出一堆高重要性的問題。值得注意的是，OpenAI 面對這些問題的回應始終不是「換成外部裁判」，而是把自評 prompt 寫得更硬、要求把判斷建立在測試與 build 這類客觀證據上，並把「要一個獨立裁判」的需求，交給 subagents 生態系 (例如唯讀的審查者子代理人，reviewer subagent) 去補。也就是說，<code class="language-plaintext highlighter-rouge">/goal</code> 這個內建迴圈本身，是明確選擇了自我反思 (self-reflection) 加紀律這條路。</p>

<h3 id="小結-兩家把重心放在不同的層">小結: 兩家把重心放在不同的層</h3>

<p>兩家不是對錯之分，是把重心放在不同層。Anthropic 在 harness 層用職責分離來把關，假設「便宜的外部模型加一段短診斷」就夠用，而且它自己同時做了 Outcome 那種完全獨立的 grader，等於光譜兩端都做了; OpenAI 則把重心放在模型層，假設模型的自我驗證可以靠 post-training 練起來，harness 只要薄薄一層紀律。OpenAI 這個方向若成立，長期會更省、更快、更接近「驗證是模型本能」那個前沿，但前提是模型真的被練到位，否則就會掉回自我歸因偏差那種選擇性失靈的問題。Anthropic 不一定全對，OpenAI 也還在補 prompt，這場「重心該放在 harness、還是放在模型訓練」的分歧，目前還沒有定論。</p>

<h2 id="如果是自行開發-agent該怎麼選">如果是自行開發 agent，該怎麼選</h2>

<p>回到這個系列的主軸 (自建 agent)，三種思路的調教難度和成本差很多:</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:24px 0;">
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">自我審計 (Codex 式)</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-bottom:8px;"><strong>調教難度最高。</strong>它高度依賴主模型「不對自己放鬆標準」的自律，而 GPT 系模型指令遵循一向是強項，又 (照前面的推測) 可能針對自我審計另外做過 post-training 強化，才撐得住。自建 agent 用一般模型要複製，難度應該會比較高，容易過早宣告完成。</div>
<div style="font-size:.85em;color:var(--text-secondary);">適合: 互動式開發的中短任務，開發者在場能即時檢查與修正，環境回饋 (測試、build、執行結果) 密集且可信，誤判完成的代價也低。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">Transcript 裁判 (Claude Code 式)</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-bottom:8px;"><strong>折衷做法。</strong>用一個小模型對著 transcript 判，相對簡單實作、也比較好評估 (輸入輸出都是文字，容易離線測)。成本與可靠性的平衡點。</div>
<div style="font-size:.85em;color:var(--text-secondary);">適合: 無人值守的中程任務，且完成條件能被指令輸出機械化證明 (測試全綠、佇列清空)。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">獨立 verify (Outcome 式)</div>
<div style="font-size:.9em;color:var(--text-secondary);margin-bottom:8px;"><strong>效果最好，但成本最高、延遲是最大弱點。</strong>每次驗收要把 artifact 跑起來操作，動輒數分鐘，還得花工夫把 rubric 和 grader 調對。</div>
<div style="font-size:.85em;color:var(--text-secondary);">適合: 長時間、以交付物為終點的任務，「完成」完全體現在可操作的 artifact 上，假性完成 (false success) 的代價高到值得花評估開銷。</div>
</div>
</div>

<p>ihower 的傾向是: 自建 agent 想複製 Codex 那種純自我審計，調教難度會很高，因為你手上的模型不見得有對應的 post-training; Claude Code 那種「小模型讀 transcript」是相對好上手、也好評估的折衷起點; 而完全獨立的驗證效果最好，但你要先接受它最貴、延遲最久這兩個前提。</p>

<p>說到底，這呼應這個系列一直強調的一句話: <strong>驗證強度是 harness 的一個可調參數，該隨任務風險和模型能力調整，而不是非黑即白的選擇。</strong> 任務風險低、模型夠強，就往便宜的自評靠; 風險高、交付物明確，就值得上獨立 grader。Anthropic 自己同時擁有光譜的兩端 (<code class="language-plaintext highlighter-rouge">/goal</code> 和 Outcome)，也是用任務型態來切分，而不是宣告誰一定比較好。</p>

<h2 id="綜合案例-一個使用者訪談-agent把兩家的招組起來用">綜合案例: 一個使用者訪談 Agent，把兩家的招組起來用</h2>

<p>前面三種實作都是大廠的產品，各自落在光譜上一個固定的點。但自建 agent 不必二選一，你可以挑不同 pattern 組起來用。小編手邊一個實際做過的使用者訪談 agent 拿來收尾剛好: 它在同一套流程裡同時用上了兩家的招，借 Claude Code 的 transcript 裁判把關「這題問夠了沒」，又借 Codex 每輪重新注入 prompt 的做法，推著訪談往前走。</p>

<p>場景是一個使用者訪談 agent，按訪綱逐題訪問受訪者。難題是: 什麼時候該換下一題? 這一題的資訊問夠了沒? 受訪者答非所問怎麼辦? 不能等訪談全部結束才發現漏問，得在當下就判斷。而且模型自己很愛「覺得差不多了就想往下走」，這跟第二篇講的「過早宣告完成」是同一個毛病，只是把時機③那個「整輪做完了沒」，縮小成「這一題做完了沒」。</p>

<h3 id="借-claude-code-的招-換題工具藏一個只讀-transcript-的-judge">借 Claude Code 的招: 換題工具藏一個只讀 transcript 的 Judge</h3>

<p>做法是: 當 agent 認為這題可以收、想跳下一題時，它呼叫一個 <code class="language-plaintext highlighter-rouge">switch_next_question_workflow</code> 工具來提出請求。但這個工具不直接換題，它先把請求交給一個獨立的 Judge 審核:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@function_tool</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">switch_next_question_workflow</span><span class="p">(</span><span class="n">reason</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">user_requested_next</span><span class="p">:</span> <span class="nb">bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s">當你判斷目前這題已取得足夠答案、或使用者明確要求跳題時呼叫。
    reason: 你認為可以換題的理由
    user_requested_next: 使用者是否明確說了「下一題 / 跳過」
    </span><span class="sh">"""</span>
    <span class="c1"># 真正的判定在 server 端,工具只是把請求轉過去
</span>    <span class="bp">...</span>
</code></pre></div></div>

<p>關鍵在 system prompt 裡對這個工具的描述，直接把規則寫死: 「這個工具會交給獨立審核模型再次判斷; 工具回傳 Y/已切換時，才代表真的進到下一題」「若工具回傳 N/留在本題，你 MUST 繼續聚焦同一題，換一種問法追問，不要自己跳題」「不要自行宣稱已進到下一題，必須等工具結果」。也就是說，主 agent 以為自己在「切換題目」，但每次切換都得先通過這道審核，而且它被明確禁止自己宣布已經切換。這跟實作一 Codex 把 <code class="language-plaintext highlighter-rouge">update_goal</code> 的宣告權限制住是同個用意，只是這裡更進一步: 連宣告權都不留給主模型，直接交給一個獨立的 Judge，做法上更接近實作二的 Claude Code。</p>

<h3 id="judge-看什麼-每題各自的-rubric而且只讀逐字稿">Judge 看什麼: 每題各自的 rubric，而且只讀逐字稿</h3>

<p>審核不是看一個籠統的「問夠了沒」，而是每一題都配一份自己的完成標準 (rubric)。訪綱大致如下 (節錄):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">QUESTION_WORKFLOW</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span>
        <span class="sh">"</span><span class="s">title</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">最近一次使用情境</span><span class="sh">"</span><span class="p">,</span>
        <span class="sh">"</span><span class="s">question</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">最近一次用產品的情境: 用什麼功能、什麼情境。</span><span class="sh">"</span><span class="p">,</span>
        <span class="sh">"</span><span class="s">completion_criteria</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">回答需包含最近一次使用的具體情境,至少提到使用的功能,以及當時正在做的任務。</span><span class="sh">"</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="p">{</span>
        <span class="sh">"</span><span class="s">title</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">替代方案</span><span class="sh">"</span><span class="p">,</span>
        <span class="sh">"</span><span class="s">question</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">不用這個產品你會用誰? 哪個最常用? 為什麼?</span><span class="sh">"</span><span class="p">,</span>
        <span class="sh">"</span><span class="s">completion_criteria</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">回答需提到至少一個替代方案,最好能指出最常用者與原因。</span><span class="sh">"</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="c1"># …其餘題目
</span><span class="p">]</span>
</code></pre></div></div>

<p>Judge 是一次獨立的小模型呼叫 (用 gpt-5.4-mini、reasoning effort 設 none、<code class="language-plaintext highlighter-rouge">max_output_tokens=16</code>，因為它只需要回一個字)，拿到的是「當前題目 + 該題的完成標準 + 到目前為止的逐字稿」，prompt 大致是:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>You are a user interview quality reviewer.
Decide whether the current interview question has enough concrete
information to be considered complete.
Use only the transcript below. Do not infer facts that are not in the transcript.

Current question: {question}
Completion criteria: {completion_criteria}
Transcript (user and assistant only): {transcript}

Return exactly one character:
Y if the current question is complete. N if not.
</code></pre></div></div>

<p>有兩個地方剛好呼應這篇拆過的兩個重點。一是那句 <code class="language-plaintext highlighter-rouge">Use only the transcript below. Do not infer facts that are not in the transcript</code>，跟實作二 Claude Code /goal 的 <code class="language-plaintext highlighter-rouge">Answer based on transcript evidence only</code> 根本是同一句話: Judge 只認攤在逐字稿上的證據，不自己想像，沒印出來的對它就等於不存在。差別只在 Claude Code 一次判「整輪」，這裡一次判「一題」。二是每題各配一份 <code class="language-plaintext highlighter-rouge">completion_criteria</code>，不是用同一套標準衡量所有題目，正好就是前面比較三者時提到的「Rubric 分解」: 條件越具體，小模型越判得準。</p>

<h3 id="沒過時的回傳值-還是要可行動">沒過時的回傳值: 還是要可行動</h3>

<p>Judge 回 N 的時候，工具不是冷冰冰地回一個 false，而是回一段可行動的指令，順便夾帶這是第幾次被退:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>N: 仍停留在第 1 題「最近一次使用情境」。這是第一次追問。
請換一種問法追問目前這題,不要切換題目。
</code></pre></div></div>

<p>判定是透過工具回傳值夾帶的指令傳回主 agent 的，跟 Claude Code 把 Haiku 寫的 <code class="language-plaintext highlighter-rouge">reason</code> 包成 <code class="language-plaintext highlighter-rouge">Stop hook feedback:</code> 注入回主 thread，是同一個做法: 沒過不是終點，而是一段告訴它下一步怎麼做的訊息。</p>

<h3 id="語意檢查要有上限與退出機制">語意檢查要有上限與退出機制</h3>

<p>Judge 如果是一道沒有上限的檢查會出問題: 遇到答得很跳的受訪者，某題可能永遠湊不齊完成標準，agent 就卡在那題出不來，限時訪談裡這很糟。所以 server 端多維護一個「這題被退幾次」的計數器，被退 3 次、或使用者明確說要跳，就強制放行，不再問 Judge。實際行為就是: 同一題最多追問兩次，第三次無論判定如何都往下走。</p>

<p>這跟實作三 Outcome 的 <code class="language-plaintext highlighter-rouge">max_iterations</code> (預設 3、最多 20) 是同一種設計: 給審核一個退出機制。通則: <strong>語意檢查很好用，但它應該有界。</strong> 給一個重試上限、一個使用者能手動覆蓋的出口，品質把關跟「別讓流程停不下來」才能兼顧。</p>

<h3 id="借-codex-的招-time_control-每輪注入動態狀態">借 Codex 的招: time_control 每輪注入動態狀態</h3>

<p>光有 Judge 擋著還不夠，你還得在每一輪推著 agent 往對的方向走，尤其是時間。但模型自己不知道「現在過幾分鐘了」「進行到第幾題了」，這是 harness 才算得出來的狀態，得主動提供給它。</p>

<p>這裡用的正是實作一 Codex 那一招: 每一輪結束、agent 要繼續時，harness 往對話裡注入一段 prompt。差別在 Codex 注入的是固定的 <code class="language-plaintext highlighter-rouge">continuation.md</code>，這裡注入的是動態的當下狀態:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;time_control&gt;已過 7 分鐘 / 共 15 分鐘&lt;current_question&gt;第 2 / 5 題: …&lt;/current_question&gt;&lt;/time_control&gt;
</code></pre></div></div>

<p>幾個設計點: 它是內部指示，prompt 裡明確交代「DO NOT 對使用者念出、引用，或提及時間、進度」; 而且做了去重，只有「分鐘數變了」或「換題了」才重新插一則，不會每輪都重複注入 (Codex 靠 prompt cache 吸收每輪重播的模板成本，這裡改用去重來省)。然後 prompt 教 agent 怎麼用這個訊號調節奏: 時間充裕就從容追問; 過半還卡在前段就精簡、加速; 最後 1 到 2 分鐘讓每題拿到最小可用答案就趕快推進。</p>

<h3 id="一個推進度一個把關而且-pattern-不綁時機點">一個推進度、一個把關，而且 pattern 不綁時機點</h3>

<p>把兩半合起來看，正好是這篇兩種實作的縮影，一前一後配合: time_control 是 Codex 式的每輪注入 (前饋)，動態提供給模型它自己算不出來的當下狀態，影響它「什麼時候想呼叫換題工具」; Judge 是 Claude Code 式的 transcript 裁判 (回饋)，在它真的呼叫時把關「這次能不能換」。time_control 推進度、Judge 把關，合起來才讓一場限時訪談既問得夠深、又跑得完。</p>

<svg viewBox="0 0 700 400" style="width:100%;max-width:680px;height:auto;display:block;margin:24px auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="使用者訪談 agent 組合 time_control 與 Judge 示意圖">
<defs>
<marker id="uiB" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:var(--accent)" /></marker>
<marker id="uiG" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:#1a7f37" /></marker>
<marker id="uiR" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:#cf222e" /></marker>
</defs>
<rect x="160" y="14" width="380" height="54" rx="8" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="350" y="38" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">time_control: 每輪注入動態狀態 (時間 / 題號)</text>
<text x="350" y="57" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">Codex 式 · 前饋: 給模型它自己算不出的當下狀態</text>
<rect x="235" y="96" width="230" height="58" rx="8" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" />
<text x="350" y="120" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">訪談 agent 跑一輪</text>
<text x="350" y="139" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">逐題問答 · 判斷這題問夠了沒</text>
<rect x="205" y="192" width="290" height="62" rx="8" style="fill:#fff8c5;stroke:#9a6700;stroke-width:1.5" />
<text x="350" y="216" text-anchor="middle" style="fill:#9a6700;font-size:12.5px;font-weight:700">獨立 Judge · 只讀 transcript + 該題 rubric</text>
<text x="350" y="235" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px">Claude Code 式 · 回饋: 把關「這題能不能換」</text>
<rect x="395" y="298" width="215" height="48" rx="8" style="fill:#dafbe1;stroke:#1a7f37;stroke-width:1.5" />
<text x="502" y="320" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:700">進到下一題 ✅</text>
<text x="502" y="337" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">harness 推進到下一題</text>
<rect x="70" y="298" width="250" height="48" rx="8" style="fill:#ffebe9;stroke:#cf222e;stroke-width:1.5" />
<text x="195" y="320" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:700">留在本題，換問法追問</text>
<text x="195" y="337" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">回傳可行動指令 + 第幾次被退</text>
<line x1="350" y1="68" x2="350" y2="94" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#uiB)" />
<text x="358" y="86" style="fill:var(--text-secondary);font-size:10.5px">影響何時換題</text>
<line x1="350" y1="154" x2="350" y2="190" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#uiB)" />
<text x="360" y="177" style="fill:var(--text-secondary);font-size:10.5px">想換題: 呼叫 switch 工具</text>
<path d="M495,210 H575 V296" style="fill:none;stroke:#1a7f37;stroke-width:1.5" marker-end="url(#uiG)" />
<text x="537" y="203" text-anchor="middle" style="fill:#1a7f37;font-size:10.5px">Y: 切換</text>
<path d="M205,210 H150 V296" style="fill:none;stroke:#cf222e;stroke-width:1.5" marker-end="url(#uiR)" />
<text x="150" y="203" text-anchor="middle" style="fill:#cf222e;font-size:10.5px">N: 留本題</text>
<path d="M70,325 H32 V125 H233" style="fill:none;stroke:var(--accent);stroke-width:1.5" marker-end="url(#uiB)" />
<path d="M610,325 H665 V41 H542" style="fill:none;stroke:var(--accent);stroke-width:1.5" marker-end="url(#uiB)" />
<text x="640" y="200" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">下一輪</text>
<path d="M195,346 V362 H503 V348" style="fill:none;stroke:#1a7f37;stroke-width:1.3;stroke-dasharray:5 3" marker-end="url(#uiG)" />
<text x="349" y="359" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">退 3 次 / 使用者喊跳 → 強制放行</text>
<text x="350" y="382" text-anchor="middle" style="fill:var(--text);font-size:11.5px;font-weight:600">time_control 推進度，Judge 把關: 一前一後讓限時訪談問得深又跑得完</text>
<text x="694" y="396" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">✏️ 小編製圖</text>
</svg>

<p>還有一點值得點出來: 這整套其實是做在工具層的 (時機①,Judge 藏在一次 tool call 背後)，不是 Stop hook 那種 turn-end (時機③)。但它借的兩個 pattern，正是這篇為時機③拆過的 transcript 裁判和每輪注入。這說明這些 pattern 並不綁死在某個時機點上，你可以在工具層、單輪結束、外層 loop 任何一個粒度上拿來組合，看你要把「做完了沒」這個判斷，放在多細的工作單位上。</p>

<h2 id="銜接下一篇-一個-context-裝不下的任務呢">銜接下一篇: 一個 context 裝不下的任務呢?</h2>

<p>時機③的價值，是在「每一輪結束」這個邊界上，把「做完了沒」交給一個夠獨立的判斷，而不是任憑模型自我感覺良好就算完成。</p>

<p>但它仍有個結構性的上限: 不管是哪一種實作，它們都在「同一個 session、同一個 (或續跑的) context」裡運作。當任務大到一個 context window 根本裝不下 (要跑幾百步、跨好幾天)，光靠單輪驗收收斂不了，失敗的推理還會在 context 裡越積越多、污染後面的判斷。</p>

<p>這就帶到時機④: 外層 loop。下一篇談怎麼把一個任務一輪輪交給全新的 agent，讓進度在 context 之外傳遞，也就是 Ralph、Symphony、Cron 這些 loop engineering 的做法。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Eval" /><summary type="html"><![CDATA[📚 給 Agent 開發者的駕馭工程 (全 9 篇) 1. 基礎: Deep Agent 的六項內建能力 2. 核心: Agent 要的是回饋迴路，不是完美提示 3. 回饋時機一: 工具回傳值，是寫給 agent 的回饋 4. 回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent 5. 回饋時機三: 單輪結束的驗收，Goal 與 Outcomes (本篇) 6. 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron 7. 進階: 自我改進 Harness, Meta-Harness 與爬坡 8. 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson 9. 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建? 🎞️ 搭配演講投影片: 給 Agent 開發者的 Harness+Loop Engineering]]></summary></entry><entry><title type="html">給 Agent 開發者的駕馭工程 (6): 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</title><link href="https://blog.aihao.tw/2026/06/26/harness-engineering-6-outer-loop/" rel="alternate" type="text/html" title="給 Agent 開發者的駕馭工程 (6): 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/26/harness-engineering-6-outer-loop</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/26/harness-engineering-6-outer-loop/"><![CDATA[<div class="series-toc" style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:24px 0;font-size:.92em;line-height:1.85;">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📚 給 Agent 開發者的駕馭工程 (全 9 篇)</div>
<div>1. <a href="/2026/06/26/harness-engineering-1-deep-agent-capabilities/">基礎: Deep Agent 的六項內建能力</a></div>
<div>2. <a href="/2026/06/26/harness-engineering-2-what-is-harness-engineering/">核心: Agent 要的是回饋迴路，不是完美提示</a></div>
<div>3. <a href="/2026/06/26/harness-engineering-3-tool-execution-feedback/">回饋時機一: 工具回傳值，是寫給 agent 的回饋</a></div>
<div>4. <a href="/2026/06/26/harness-engineering-4-mid-run-injection/">回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent</a></div>
<div>5. <a href="/2026/06/26/harness-engineering-5-goal-and-outcomes/">回饋時機三: 單輪結束的驗收，Goal 與 Outcomes</a></div>
<div>6. <strong style="color:var(--text);">回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</strong> <span style="color:var(--text-secondary);">(本篇)</span></div>
<div>7. <a href="/2026/06/26/harness-engineering-7-self-improving/">進階: 自我改進 Harness, Meta-Harness 與爬坡</a></div>
<div>8. <a href="/2026/06/26/harness-engineering-8-model-harness-fit/">收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</a></div>
<div>9. <a href="/2026/06/26/harness-engineering-9-agent-frameworks/">自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</a></div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">🎞️ 搭配演講投影片: <a href="https://ihower.tw/presentation/harness.html">給 Agent 開發者的 Harness+Loop Engineering</a></div>
</div>

<p>上一篇談時機③，在「每一輪結束」這個邊界上，把「做完了沒」交給一個夠獨立的判斷。但它有個結構性的上限: 不管哪一種實作，都在同一個 session、同一份 (或續跑的) context 裡進行。當任務大到一個 context window 根本裝不下 (要跑幾百步、跨好幾天)，光靠單輪驗收收斂不了，失敗的推理還會在 context 裡越積越多，污染後面的判斷。</p>

<p>這一篇進到時機④: 外層 Loop。四個時機由內而外，這是最外、也最貴的一層。</p>

<p>要先說清楚一件事: 這一層在社群討論得很熱，<a href="https://x.com/steipete/status/2063697162748260627">Peter Steinberger</a> 那句「你不該再去提示 agent，你該設計提示 agent 的迴圈」被反覆討論了好幾個月，Boris Cherny、Andrej Karpathy 也都講過類似的話，還出現了 loop engineering、loopcraft 這些新詞。但這些講法指向的東西其實很具體，後面會一個一個拆開來談。</p>

<h2 id="為什麼需要外層迴圈">為什麼需要外層迴圈</h2>

<p>先講清楚這層在解什麼。前面幾個時機都假設「一個 context 裝得下整個任務」，在這個前提內把迴圈閉合。一旦離開這個前提，有三種情況會讓你需要把迴圈往外推一層:</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:24px 0;">
<div style="flex:1 1 210px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">① 單一 context 能力有限</div>
<div style="font-size:.9em;color:var(--text-secondary);line-height:1.7;margin-bottom:8px;">大任務得拆段做，每段用乾淨 context 重新開始、表現不受前一段影響。一個 context 一路跑下去會遇到:</div>
<ul style="margin:0;padding-left:18px;font-size:.9em;color:var(--text-secondary);line-height:1.7;">
<li>context 塞滿: 工具輸出、中間推理把 context 佔滿，模型表現明顯變差</li>
<li>失敗推理污染: 前面出錯的推理留在 context 裡，持續影響後面的判斷</li>
<li>Goal 跨不過 session: 時機③的驗收只在一個 session 內有效</li>
</ul>
</div>
<div style="flex:1 1 210px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">② 是另一個不相關的新任務</div>
<div style="font-size:.9em;color:var(--text-secondary);line-height:1.7;">跟手上這件事無關的工作，本來就該各自開一份乾淨的 context，而不是全接在同一條後面。一旦同時有好幾件，就需要一個 context 之外的機制去管理多條平行的 agent: 誰在做什麼、哪些能一起跑。</div>
</div>
<div style="flex:1 1 210px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">③ 要由外部事件自動觸發</div>
<div style="font-size:.9em;color:var(--text-secondary);line-height:1.7;">有些工作不是人坐在終端機前手動下指令，而是外部訊息、webhook、排程、CI 失敗一來就該自己啟動。這個「自動把任務送出去跑」的動作，只能由一個在 session 之外、一直待命的機制來做。</div>
</div>
</div>

<p>這三種情況指向同一個東西: 一個在任何單一 context 之外、不會隨某一輪 agent 收掉就跟著消失的工程機制 (也就是外層 harness)。它負責決定什麼條件觸發、每件工作開哪一份乾淨 context、多條 agent 怎麼協調，以及最關鍵的，進度怎麼在一圈一圈之間傳下去。後面要談的三種實作剛好各自對應一種情況: Ralph 對著一個大任務一圈圈重跑 (①)、Symphony 同時管很多張 ticket、各配一個 agent (②)、Cron 靠排程或事件自動起跑 (③)。</p>

<p>這裡有個觀念要先確立，它貫穿後面三種實作: <strong>外層迴圈最關鍵的設計，是把「進度」這件事從 context 搬到磁碟。</strong> 模型在每一圈結束後 context 就清空了，所以能撐過多圈的不是模型自己記得，而是那份留在外面的狀態。Geoffrey Huntley 講 Ralph 時講得很直白: agent 會忘，但 repo 不會。</p>

<p>那這份外層迴圈具體怎麼搭? 這裡挑三個有代表性的案例來分析。</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:24px 0;">
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">🔁 Ralph</div>
<div style="font-size:.9em;color:var(--text-secondary);">bash 蠻力重跑。每圈全新 context，同一份 prompt 一直跑到做完為止。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">🎼 Symphony</div>
<div style="font-size:.9em;color:var(--text-secondary);">看板協調器。把專案管理工具當控制平面，每張開啟的 ticket 配一個 agent。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">⏰ Cron 排程</div>
<div style="font-size:.9em;color:var(--text-secondary);">定時觸發 (如 <code>/loop</code>)。時間到就跑一輪，依 context 是否沿用再分兩種。</div>
</div>
</div>

<h2 id="ralph-最笨也最有名的-loop">Ralph: 最笨也最有名的 Loop</h2>

<p>第一種是 Geoffrey Huntley 在 2025 年中提出的 <a href="https://ghuntley.com/ralph/">Ralph</a>，做法簡單到第一眼會以為是在開玩笑:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> :<span class="p">;</span> <span class="k">do
  </span><span class="nb">cat </span>PROMPT.md | claude-code
<span class="k">done</span>
</code></pre></div></div>

<p>就是一個 bash 無窮迴圈，每一圈把同一份 prompt 檔交給 agent，跑完就再來一次。沒有花俏的編排，沒有狀態機。它真正的設計重點不在迴圈本身，而在「每一圈都是全新的 context」: 不讓對話一直長下去，而是每圈歸零，只從固定的幾份檔案重新讀起。靠這個做法，Huntley 用 Ralph 完成過一個完整的編譯器專案，也用大約 297 美元的 API 成本，做出一個原本報價 5 萬美元合約的 MVP。</p>

<p>那這個「無窮」迴圈怎麼停? 其實它是有完成條件的: 當 agent 認為 prd.json 上的任務都完成了，會輸出一個約定好的字串 <code class="language-plaintext highlighter-rouge">&lt;promise&gt;COMPLETE&lt;/promise&gt;</code>，外層 bash 每一圈用 <code class="language-plaintext highlighter-rouge">grep</code> 比對這個字串，一比中就跳出迴圈、不再重跑;另外再配一個 <code class="language-plaintext highlighter-rouge">max-iterations</code> 上限 (snarktank 範本預設 10 圈) 當保底，避免卡住時一直跑下去。也就是說，完成與否是讓模型自己宣告、再靠字串比對來認定，這也是後面批評會談到的點: 它是模型自己說了算，不是獨立驗證過的 Goal。</p>

<h3 id="跨圈記憶-進度全靠外部狀態">跨圈記憶: 進度全靠外部狀態</h3>

<p>既然 context 每圈歸零，進度就得放在 context 之外。Ralph 把這件事拆成三個檔案，這也正好是前面那句「進度搬到磁碟」最具體的示範:</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:22px 0;">
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:4px;">📜 git history</div>
<div style="font-size:.9em;color:var(--text-secondary);">已完成的工作就是 commit。新一圈開始，先看 git 歷史知道做到哪。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:4px;">📝 progress.txt</div>
<div style="font-size:.9em;color:var(--text-secondary);">每圈把學到的事追加上去: 踩過的陷阱、發現的慣例、做過的決策。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:4px;">☑️ prd.json</div>
<div style="font-size:.9em;color:var(--text-secondary);">任務清單與每項的 <code>passes</code> 狀態。每圈挑最高優先且還沒過的來做。</div>
</div>
</div>

<p><a href="https://github.com/snarktank/ralph">snarktank/ralph</a> 這個現成的範本把一圈的動作寫得很清楚: 讀 prd.json 和 progress.txt → 挑一個最高優先、<code class="language-plaintext highlighter-rouge">passes: false</code> 的 story → 只實作這一個 → 跑 typecheck 和測試 → 過了才 commit、把 <code class="language-plaintext highlighter-rouge">passes</code> 標成 true → 把這圈學到的東西追加進 progress.txt → 下一圈。它強調的成功要件，其實全是前幾篇講過的東西: story 要切小到一個 context 裝得下、要有 typecheck 和測試這種快速回饋、完成條件要寫清楚。換句話說，Ralph 的外層迴圈能不能跑得動，仍然取決於內層那兩個時機有沒有先做好。</p>

<svg viewBox="0 0 700 300" style="width:100%;max-width:660px;height:auto;display:block;margin:24px auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Ralph 運作原理示意圖">
<defs>
<marker id="rlpA" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:var(--accent)" /></marker>
<marker id="rlpS" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker>
</defs>
<path d="M524,108 V46 H86 V104" style="fill:none;stroke:var(--accent);stroke-width:1.5;stroke-dasharray:5 3" marker-end="url(#rlpA)" />
<text x="305" y="38" text-anchor="middle" style="fill:var(--accent);font-size:11.5px;font-weight:600">每圈結束 → context 整個丟掉，開全新一圈</text>
<rect x="22" y="108" width="130" height="64" rx="8" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="87" y="135" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:700">prompt.md</text>
<text x="87" y="154" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">每圈用同一份</text>
<rect x="212" y="102" width="158" height="76" rx="8" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" />
<text x="291" y="133" text-anchor="middle" style="fill:var(--text);font-size:12.5px;font-weight:700">全新 context 的 agent</text>
<text x="291" y="152" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">乾淨的 context，跑一圈</text>
<rect x="430" y="108" width="186" height="64" rx="8" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="523" y="134" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:700">挑一個 story 做</text>
<text x="523" y="153" text-anchor="middle" style="fill:var(--text-secondary);font-size:10px">過 typecheck/測試才 commit</text>
<line x1="152" y1="140" x2="210" y2="140" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#rlpA)" />
<line x1="370" y1="140" x2="428" y2="140" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#rlpA)" />
<rect x="206" y="246" width="300" height="46" rx="8" style="fill:#fff8c5;stroke:#9a6700;stroke-width:1.5" />
<text x="356" y="266" text-anchor="middle" style="fill:#9a6700;font-size:11.5px;font-weight:700">磁碟狀態 (跨圈不丟)</text>
<text x="356" y="283" text-anchor="middle" style="fill:var(--text-secondary);font-size:10px">git history · progress.txt · prd.json</text>
<path d="M268,246 V182" style="fill:none;stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#rlpS)" />
<text x="260" y="214" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">① 開圈先讀</text>
<path d="M452,172 V246" style="fill:none;stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#rlpS)" />
<text x="460" y="208" style="fill:var(--text-secondary);font-size:10px">② commit、</text>
<text x="460" y="222" style="fill:var(--text-secondary);font-size:10px">記下學到的</text>
<text x="690" y="296" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">✏️ 小編製圖</text>
</svg>

<h3 id="ralph-wiggum-plugin-名字一樣機制不同">ralph-wiggum Plugin: 名字一樣，機制不同</h3>

<p>這裡要插一個容易混淆的點。Anthropic 官方在 Claude Code 出了一個叫 <a href="https://github.com/anthropics/claude-code/tree/main/plugins/ralph-wiggum">ralph-wiggum</a> 的 plugin，名字明顯是在致敬 Ralph，但它的機制跟原版相反:</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:22px 0;">
<div style="flex:1 1 280px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:8px;">原版 Ralph</div>
<div style="font-size:.92em;color:var(--text-secondary);">bash 外迴圈，<strong>每圈整個關掉重開</strong>。全新 context、靠檔案傳遞進度。是個「換新 context」的設計。</div>
</div>
<div style="flex:1 1 280px;border:1px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">ralph-wiggum plugin</div>
<div style="font-size:.92em;color:var(--text-secondary);">用 <strong>Stop hook</strong> 實作。<strong>同一個 session</strong> 內，在 agent 想結束時攔住、把 prompt 重新送回去，配 <code>--max-iterations</code>、<code>--completion-promise</code> 控制何時停。是個「不讓它結束、要它繼續做」的設計。</div>
</div>
</div>

<p>一個換新 context，一個不讓它結束，context 處理剛好相反。這正好呼應系列第五篇那條教訓: <strong>別只看名字，要看實作。</strong> 上一篇講 Codex 的 goal mode 時，看過有流量不錯的文章把它說成「用獨立小模型每回合評分」，翻原始碼才發現根本沒有第二個模型。這裡也一樣: 兩個都叫 Ralph，但你要是照原版的心智模型去用 plugin、以為它每圈會清掉 context，就會踩雷。harness 的細節，看名字、看標題都不準，得自己讀一次 code。</p>

<p>而把 code 讀完，會看到一個比「同 session」更有意思的點。plugin 每一圈送回去的，是你最初下的那段 prompt 原封不動 (存在 <code class="language-plaintext highlighter-rouge">.claude/ralph-loop.local.md</code>，官方明說「prompt 不會在迭代之間改變」)，只多附一行系統訊息提醒這是第幾圈、以及「要停就輸出 <code class="language-plaintext highlighter-rouge">&lt;promise&gt;DONE&lt;/promise&gt;</code>，但只有陳述為真才准、別為了結束就說謊」。它判斷完成的方式也很陽春: 去比對最後一則回覆裡有沒有那個寫死的 <code class="language-plaintext highlighter-rouge">&lt;promise&gt;</code> 字串。把「同一條 thread 一路長下去」加上「完成與否由主模型自己判定」這兩件事擺在一起，你會發現它的機制其實更靠近上一篇的 Codex /goal，而不是原版 Ralph: 原版每圈換一份乾淨 context,plugin 跟 Codex 都是同一份 context 一路跑、自己宣告做完。差別只在 Codex 送回去的是一份專門寫過、含完成審計程序的接續模板(continuation)，宣告還得過 <code class="language-plaintext highlighter-rouge">update_goal</code> 那串嚴格條款; plugin 送的是原 prompt、停止只認一個字串相符，加一句口頭提醒，等於是這幾種做法裡最寬鬆的一種完成判定。<strong>所以「ralph-wiggum 是什麼」這題，光看名字會答成原版 Ralph，讀完 code 才知道它實際上更像 Codex /goal。</strong></p>

<h3 id="對-ralph-的批評">對 Ralph 的批評</h3>

<p>Ralph 有名，但 OpenClaw 作者 Peter Steinberger 對它的批評也很直接，大致三點: 一是弱模型的權宜之計(workaround)，模型不夠強，才需要在外層一直重跑來硬補; 二是靠耗掉大量 token 硬做，而不是聰明收斂; 三是把人從迴圈裡抽得太遠，產出沒人把關 (他用「slop in a loop」，迴圈裡的垃圾，來形容)。</p>

<div style="font-size:.82em;color:var(--text-secondary);margin:-6px 0 18px;">出處: Steinberger 從 2026 年 1 月起的多則推文，例如 <a href="https://x.com/steipete/status/2010205512303030525">「給 Opus 的權宜之計」</a>、<a href="https://x.com/steipete/status/2011422781137707112">「slop in a loop」</a>。</div>

<p>但他反對的不是迴圈本身，是用蠻力迴圈取代思考。他自己照樣大量用 loop，只是配上有收斂條件的 <code class="language-plaintext highlighter-rouge">/goal</code>，以及自己寫的審查、驗證 skills，人仍然在把關架構決策。</p>

<p>用這個系列的說法來講: Ralph 只做了外層的「排程」(一直重跑)，卻沒接好內層的「驗證」和「方向」。第二篇講過，只有回饋、沒有前饋，agent 常重跑好幾圈還是偏離目標。所以更精確的說法不是「Ralph 不好」，而是「外層迴圈不能取代內層的 harness，它是疊在上面、不是換掉它」。</p>

<h2 id="openai-symphony-把看板變成控制平面">OpenAI Symphony: 把看板變成控制平面</h2>

<p>第二種實作來自 OpenAI 的 <a href="https://openai.com/index/open-source-codex-orchestration-symphony/">Symphony</a>，它的出發點比 Ralph 高一階。</p>

<p>故事是這樣: OpenAI 有個團隊做過一件事，整個 repo 不准有人手寫程式碼，每一行都得由 Codex 生成 (這就是他們上一篇 harness engineering 文章的主題)。做成之後，下一個瓶頸就出現了，而且不是模型不夠強，是<strong>人的注意力不夠用</strong>: 一個工程師同時顧 3 到 5 個 Codex session 就到極限，再多就開始忘記哪個 session 在做什麼，得在終端機之間跳來跳去。他們等於是組了一隊很能幹的初級工程師，卻得把資深工程師全部拿去盯著這些 session 跑。</p>

<p>Symphony 的解法是換一個控制單位。不要再以「session」和「PR」為中心去管理，改以「任務」為中心: 把 Linear 這類專案管理看板當成 coding agent 的<strong>控制平面</strong>，每一張開啟中的 ticket 都保證有一個 agent 在自己的工作區裡跑，一直跑到進入下一個狀態為止。Agent 掛了就重啟，有新 ticket 就派工。OpenAI 說某些團隊導入後，三週內合併的 PR 增加了 500%。</p>

<h3 id="看板就是狀態機">看板就是狀態機</h3>

<p>Symphony 的迴圈，核心是把看板的 ticket 狀態當成一台狀態機在驅動:</p>

<ul>
  <li><strong>狀態機</strong>: Symphony 盯著看板，有 ticket 進到 Todo 就派 agent，移到 Rework 就讓 agent 帶著 review 意見重做，進到終態就收掉。</li>
  <li><strong>任務 DAG</strong>: 大型工作不是一張 ticket。先派一個 agent 讀 codebase、Slack、Notion 產出實作計畫，再展開成一棵帶依賴關係的任務樹，沒被 block 的任務自然平行跑。文章舉的例子是: React 升級被標成 block 在 Vite 遷移上，agent 就真的等遷移完才動 React。</li>
  <li><strong>Agent 自己開 ticket</strong>: 實作或 review 中發現的效能問題、重構機會，agent 直接開一張新 ticket 排程，而不是塞進當前任務。</li>
  <li><strong>交付的是工作成果證明 (proof of work)</strong>: agent 不只交 code，還附 CI 狀態、PR review 回饋、甚至一段操作示範影片。一句話總結這套設計的精神: agent 的目標不是「寫完 code」，而是「說服人類把這段程式碼合併」。</li>
</ul>

<p>值得補一句的是，Symphony 的「任務」不必然是寫 code。OpenAI 自己就說，有些 ticket 是純粹的調查或分析，從頭到尾不碰 codebase; 你也可以開一張「分析這個資料、產一份報告」的 ticket 交給它。看板這層抽象一旦確立，它管的就是「要完成的工作」，而不是「要寫的 code」。從這個角度看，Symphony 比較像一套組織級的外層 harness。</p>

<svg viewBox="0 0 700 252" style="width:100%;max-width:680px;height:auto;display:block;margin:24px auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OpenAI Symphony 運作原理示意圖">
<defs>
<marker id="symA" markerWidth="11" markerHeight="9" refX="9" refY="3.2" orient="auto"><path d="M0,0 L9,3.2 L0,6.4 Z" style="fill:var(--text-secondary)" /></marker>
</defs>
<rect x="22" y="8" width="656" height="34" rx="8" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" />
<text x="350" y="30" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:700">🎼 Symphony: 看板當控制平面，每張開啟的 ticket 都配一個 agent 在自己的 workspace 跑</text>
<rect x="24" y="54" width="150" height="24" rx="5" style="fill:var(--surface);stroke:var(--border);stroke-width:1" />
<text x="99" y="70" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px;font-weight:600">Todo</text>
<rect x="184" y="54" width="166" height="24" rx="5" style="fill:var(--surface);stroke:var(--border);stroke-width:1" />
<text x="267" y="70" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px;font-weight:600">In Progress</text>
<rect x="360" y="54" width="150" height="24" rx="5" style="fill:var(--surface);stroke:var(--border);stroke-width:1" />
<text x="435" y="70" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px;font-weight:600">Review</text>
<rect x="520" y="54" width="156" height="24" rx="5" style="fill:var(--surface);stroke:var(--border);stroke-width:1" />
<text x="598" y="70" text-anchor="middle" style="fill:var(--text-secondary);font-size:11px;font-weight:600">Done</text>
<rect x="32" y="86" width="134" height="40" rx="6" style="fill:var(--bg);stroke:var(--border);stroke-width:1.2" />
<text x="99" y="103" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">TICKET-12</text>
<text x="99" y="118" text-anchor="middle" style="fill:var(--text-secondary);font-size:9.5px">待派工</text>
<rect x="32" y="132" width="134" height="40" rx="6" style="fill:var(--bg);stroke:var(--border);stroke-width:1.2" />
<text x="99" y="149" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">TICKET-13</text>
<text x="99" y="164" text-anchor="middle" style="fill:var(--text-secondary);font-size:9.5px">待派工</text>
<rect x="192" y="86" width="150" height="40" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.4" />
<text x="267" y="103" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">TICKET-9</text>
<text x="267" y="118" text-anchor="middle" style="fill:var(--accent);font-size:9.5px;font-weight:600">🤖 agent A 實作中</text>
<rect x="192" y="132" width="150" height="40" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.4" />
<text x="267" y="149" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">TICKET-10</text>
<text x="267" y="164" text-anchor="middle" style="fill:var(--accent);font-size:9.5px;font-weight:600">🤖 agent B 實作中</text>
<rect x="368" y="86" width="134" height="40" rx="6" style="fill:#fff8c5;stroke:#9a6700;stroke-width:1.4" />
<text x="435" y="103" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">TICKET-7</text>
<text x="435" y="118" text-anchor="middle" style="fill:#9a6700;font-size:9px">PR + 證明影片，等人審</text>
<rect x="528" y="86" width="140" height="40" rx="6" style="fill:#dafbe1;stroke:#1a7f37;stroke-width:1.4" />
<text x="598" y="103" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">TICKET-3</text>
<text x="598" y="118" text-anchor="middle" style="fill:#1a7f37;font-size:9.5px">✓ merged</text>
<line x1="40" y1="200" x2="660" y2="200" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#symA)" />
<text x="350" y="220" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">ticket 狀態由左往右流動 = 狀態機; 沒被 block 的各配一個 agent 平行跑</text>
<text x="690" y="244" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">✏️ 小編製圖</text>
</svg>

<blockquote>
  <p>編按: 跟外層 Loop 的主題稍微岔開，但 Symphony 有個細節值得一記: 它開源出來的核心是一份 <code class="language-plaintext highlighter-rouge">SPEC.md</code>，不是實作的程式碼。這份 spec 是從內部實作逆向整理出來的 (一開始只是 tmux 裡一個 Codex session 在輪詢 Linear),OpenAI 還為了讓這份 spec 更完整，讓 Codex 用 Elixir、TypeScript、Go、Rust、Java、Python 各實作一遍; 官方建議的用法第一條，就是把 spec 交給你自己的 agent、用你選的語言實作一個。當實作隨時能被 agent 重建，真正的資產就從 code 移到了那份定義「要解什麼問題、邊界在哪」的 spec。這個重點系列後面還會再談到。</p>
</blockquote>

<h2 id="cron-排程和-heartbeat">Cron 排程和 Heartbeat</h2>

<p>第三種實作最輕量、也最通用: 自動觸發。不靠你手動下指令，而是設一個觸發條件，條件一到就自動把一段 prompt 送出去跑一輪。它輕到不必另外建任何東西: Claude Code、Codex 這些 coding agent 都已經把它做成內建功能 (後面會講到的 <code class="language-plaintext highlighter-rouge">/loop</code>、Routines、Automations 就是)，設一行排程就能用，這也是三種裡最容易上手的一種。</p>

<p>觸發的節奏分兩種。一種是<strong>定時</strong>: cron 排程，時間到就跑; 這種通常還會避免重疊，上一輪還在跑就跳過這次、不疊著開。另一種是<strong>讓 agent 自己決定下一次何時跑</strong>: 沒事就把間隔拉長、有狀況就縮短。(也可以不看時間、改綁事件: webhook、新郵件、CI 失敗就觸發。)</p>

<p>而這層真正的設計重點在另一條軸: 「每次觸發要不要沿用上一次的 context」。這條分界把自動觸發分成差很大的兩種:</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:22px 0;">
<div style="flex:1 1 280px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:8px;">每次獨立 context</div>
<div style="font-size:.92em;color:var(--text-secondary);margin-bottom:8px;">時間到就開一個全新的 agent run (cron + headless、雲端排程)。乾淨的 context，進度靠外部狀態傳遞，跟 Ralph 同款。</div>
<div style="font-size:.85em;color:var(--text-secondary);">例: 定時產一份情報簡報的研究 agent、Symphony 的每個 worker。</div>
</div>
<div style="flex:1 1 280px;border:1px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">沿用 context: Heartbeat</div>
<div style="font-size:.92em;color:var(--text-secondary);margin-bottom:8px;">定時把一段 prompt 送回同一個長駐 session。記憶連續、反應快，但 context 會一直變大。</div>
<div style="font-size:.85em;color:var(--text-secondary);">例: OpenClaw 那種 24/7 主動助理，定時 heartbeat 醒來看 inbox、行事曆、通知。</div>
</div>
</div>

<svg viewBox="0 0 700 300" style="width:100%;max-width:680px;height:auto;display:block;margin:24px auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Cron 排程兩種模式: 獨立 context 與 heartbeat">
<defs>
<marker id="crnA" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:var(--accent)" /></marker>
<marker id="crnS" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker>
</defs>
<circle cx="58" cy="150" r="26" style="fill:var(--surface);stroke:var(--accent);stroke-width:1.6" />
<line x1="58" y1="150" x2="58" y2="134" style="stroke:var(--accent);stroke-width:1.6" />
<line x1="58" y1="150" x2="70" y2="150" style="stroke:var(--accent);stroke-width:1.6" />
<text x="58" y="196" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">cron 觸發</text>
<text x="58" y="210" text-anchor="middle" style="fill:var(--text-secondary);font-size:9px">時間到就跑</text>
<path d="M84,138 C122,116 144,94 180,86" style="fill:none;stroke:var(--accent);stroke-width:1.4;stroke-dasharray:4 3" marker-end="url(#crnA)" />
<text x="120" y="104" text-anchor="middle" style="fill:var(--text-secondary);font-size:9.5px">開新的</text>
<path d="M84,162 C122,186 144,208 180,216" style="fill:none;stroke:var(--accent);stroke-width:1.4;stroke-dasharray:4 3" marker-end="url(#crnA)" />
<text x="120" y="210" text-anchor="middle" style="fill:var(--text-secondary);font-size:9.5px">回同一條</text>
<text x="430" y="28" text-anchor="middle" style="fill:var(--text);font-size:11.5px;font-weight:700">每次獨立 context (cron + headless / 雲端排程)</text>
<rect x="192" y="56" width="120" height="44" rx="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.3" />
<text x="252" y="76" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">t1 全新 run</text>
<text x="252" y="92" text-anchor="middle" style="fill:var(--text-secondary);font-size:9px">乾淨的 context</text>
<rect x="362" y="56" width="120" height="44" rx="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.3" />
<text x="422" y="76" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">t2 全新 run</text>
<text x="422" y="92" text-anchor="middle" style="fill:var(--text-secondary);font-size:9px">乾淨的 context</text>
<rect x="532" y="56" width="120" height="44" rx="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.3" />
<text x="592" y="76" text-anchor="middle" style="fill:var(--text);font-size:10.5px;font-weight:600">t3 全新 run</text>
<text x="592" y="92" text-anchor="middle" style="fill:var(--text-secondary);font-size:9px">乾淨的 context</text>
<text x="430" y="120" text-anchor="middle" style="fill:var(--text-secondary);font-size:9.5px">各自了結，結果送出後 context 就丟掉; 進度靠外部狀態傳遞</text>
<line x1="150" y1="144" x2="668" y2="144" style="stroke:var(--border);stroke-width:1;stroke-dasharray:3 3" />
<text x="430" y="172" text-anchor="middle" style="fill:var(--text);font-size:11.5px;font-weight:700">Heartbeat: 沿用 context (/loop · thread automation)</text>
<text x="252" y="192" text-anchor="middle" style="fill:var(--text-secondary);font-size:9px">t1</text>
<text x="430" y="192" text-anchor="middle" style="fill:var(--text-secondary);font-size:9px">t2</text>
<text x="608" y="192" text-anchor="middle" style="fill:var(--text-secondary);font-size:9px">t3</text>
<path d="M252,196 V215" style="fill:none;stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#crnS)" />
<path d="M430,196 V215" style="fill:none;stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#crnS)" />
<path d="M608,196 V215" style="fill:none;stroke:var(--text-secondary);stroke-width:1.4" marker-end="url(#crnS)" />
<rect x="190" y="216" width="478" height="60" rx="8" style="fill:var(--surface);stroke:var(--accent);stroke-width:1.5" />
<text x="210" y="237" style="fill:var(--text);font-size:10.5px;font-weight:600">同一條 session 一直活著</text>
<polygon points="206,268 206,258 648,248 648,268" style="fill:rgba(9,105,218,.15);stroke:var(--accent);stroke-width:0.8" />
<text x="427" y="264" text-anchor="middle" style="fill:var(--text-secondary);font-size:9px">context 每次都更長 →</text>
<text x="690" y="295" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">✏️ 小編製圖</text>
</svg>

<p>這條「每次獨立、還是沿用 context」的分界，Codex 的 <a href="https://developers.openai.com/codex/app/automations">Automations</a> 乾脆直接做成兩種類型。一種是 <strong>standalone automation</strong>: 照排程 (可填 cron 語法) 起一個全新的 run，結果進一個叫 Triage 的收件匣，有發現才進來、沒發現就自動封存 (這個「沒東西就封存」跟前面 Symphony 沒發現就歸檔是同個設計)。另一種是 <strong>thread automation</strong>，官方原話就是「heartbeat 式的定時喚醒，綁在當前這條 thread 上」: 定時回到同一段對話、保留 context，適合盯著一個長命令跑完、或定時回來推進一個 review 迴圈。同一個產品，把「要不要沿用 context」這個設定直接做成兩個選項。它還多給一個獨立的選擇: 在 git repo 裡，automation 可以跑在你的本地 checkout，也可以開一個獨立 worktree 隔離，免得動到你手上還沒寫完的東西。</p>

<p>Claude Code 則把這兩種拆在不同地方。heartbeat 式的是 session 內的 <code class="language-plaintext highlighter-rouge">/loop</code>: 它<a href="https://code.claude.com/docs/en/scheduled-tasks">綁在單一 session、只在空檔執行</a>，錯過的觸發不會補跑 (no catch-up)，適合「定時看一眼、有事才處理」的輕量定時檢查，不適合非得準時、不能漏的硬排程。</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/loop 5m 檢查 deploy 狀態, 失敗就修       ← 固定間隔
/loop 盯著 CI, 紅了就處理                  ← 動態間隔: 模型自己決定下次何時跑
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">/loop</code> 剛好上面兩種節奏都給你: 給間隔 (<code class="language-plaintext highlighter-rouge">5m</code>) 就定時跑，不給就交給模型自己決定下次何時跑。要每次一份乾淨 context 的那種，則要跳出 session，改用跑在 Anthropic 雲端、每次一份全新 clone 的 <a href="https://code.claude.com/docs/en/routines">Routines</a> (用 <code class="language-plaintext highlighter-rouge">/schedule</code> 建)。對自建 agent 來說，選哪一種是你得自己決定的設計取捨: 每次給乾淨 context 的那種，進度全靠外部狀態; 長駐 session 則記憶連續、但 context 一直變長。</p>

<p>要強調的是，<strong>排程這層只管「什麼時候跑」，它完全不判斷「做到什麼程度才算完」。</strong> 一個 cron 就是時間到了把 prompt 送出去，至於這一輪該不該停、夠不夠好，排程一概不管。這就帶到外層 Loop 一個很重要、卻常被混為一談的觀念。</p>

<h2 id="goal-和-loop-可以一起用">Goal 和 Loop 可以一起用</h2>

<p>社群常把 <code class="language-plaintext highlighter-rouge">/goal</code> 和 <code class="language-plaintext highlighter-rouge">/loop</code> 混著講，好像是兩個競爭的東西。其實它們管的是兩件不同的事，彼此不衝突，可以分開用，也可以組合起來用:</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>管什麼</th>
      <th>回答的問題</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Loop</strong> (Ralph／Symphony／cron)</td>
      <td>scheduling 排程</td>
      <td><strong>什麼時候</strong>跑、多久跑一次</td>
    </tr>
    <tr>
      <td><strong>Goal</strong> (上一篇)</td>
      <td>termination 終止</td>
      <td>做到<strong>什麼程度</strong>才算完、才能停</td>
    </tr>
  </tbody>
</table>

<p>看懂這個分工，就有兩種組合用法。第一種是<strong>疊起來</strong>: 這一篇講的三種外層 loop，每一個都可以掛上一個上一篇講的可驗證 Goal。Ralph 的每一圈、Symphony 的每張 ticket、cron 的每次觸發，都能配一個停止條件，變成「時間到就啟動、做到驗證過才停」的工作 (run-to-completion job)。loop 是外圈 (管排程),goal 是內圈 (管終止)。</p>

<p>第二種是<strong>串接</strong>: loop 偵測到事件、把工作推進某個 queue 就結束，另外派一個帶 goal 的任務去把它做完。前者管「發現工作、何時啟動」，後者管「把這件事做到通過驗證」。</p>

<p>關鍵是別把兩者混為一談。一個只有 loop 沒有 goal 的迴圈，就是前面講的 Ralph 那種問題 (一直重跑到圈數上限才停); 一個只有 goal 沒有 loop 的任務，則被綁在單一 session 裡，跨不過 context 的上限。兩個各自獨立的設定，可以分開調，也可以一起調。</p>

<h2 id="把自己移出迴圈">把自己移出迴圈</h2>

<p>三種實作講完，回到開頭那句廣為流傳的話。Peter Steinberger 說的是:</p>

<blockquote>
  <p>「這是你每月一次的提醒: 你不應該再去提示寫程式的代理人了。你應該要設計『讓你的代理人被提示』的迴圈。」</p>
</blockquote>

<p>Boris Cherny 講得更具體，說他現在的工作就是寫 loop: 不再自己提示 Claude，而是讓跑著的 loop 去提示 Claude、去決定要做什麼。他甚至給過數字: 過去 30 天他對 Claude Code 的貢獻，100% 是 Claude Code 自己寫的，合併了 259 個 PR (這些說法和數字，Matt Van Horn 在 <a href="https://x.com/mvanhorn/status/2063865685558903149">WTF Is a Loop</a> 裡整理得很完整)。他把這套做法濃縮成幾句: 把 loop 寫一次，給它可呼叫的 skills 和能自我檢查的回饋，設好上限讓它會停，然後放上 cron 跑。一個能直接照抄的起步範例長這樣:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/loop 幫我代管所有 PR。自動修 build 問題,
      等有留言進來時,用 worktree agent 來修正它們。
</code></pre></div></div>

<h3 id="實例-一個-maintainer-orchestrator">實例: 一個 Maintainer Orchestrator</h3>

<p>把這套講得最完整的實例，是 Steinberger 開源的 <a href="https://github.com/steipete/agent-scripts/blob/main/skills/maintainer-orchestrator/SKILL.md">maintainer-orchestrator</a> skill。他<a href="https://x.com/steipete/status/2064998499780084154">在推文上的描述</a>是: 告訴 codex 維護你的 repos，每 5 分鐘醒來，把工作派到各個 thread; 配上 triage、autoreview、computer use 幾個 skill，部分工作可以全自動合併。</p>

<p>拆開看，它正好把這篇三種實作疊在一起用:</p>

<ul>
  <li><strong>它是個 cron + orchestrator</strong>: orchestrator 本身只當控制平面，每 5 分鐘 heartbeat 醒來讀各 worker 現況，只負責定時檢查、派工、監控、需要時問人決策。實作全部交給平行跑的 worker thread，而且 worker 不准再往下委派。</li>
  <li><strong>先過一道 triage</strong>: 另一個 <a href="https://github.com/steipete/agent-scripts/blob/main/skills/github-project-triage/SKILL.md">github-project-triage</a> skill 把進來的 queue 分三類: 可重現、有界、可驗證的歸 <strong>Autonomous</strong> 直接派工; 需要產品決策、牽涉安全、或缺權限的歸 <strong>Needs owner</strong>; 過期、重複的歸 <strong>Defer/close</strong>，附證據處置。</li>
</ul>

<p>這套之所以可以讓「部分工作全自動合併」，不是因為大膽，而是靠 harness 設計。它用四道關卡限制自動合併:</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:22px 0;">
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:4px;">📋 Decision-Ready Rule</div>
<div style="font-size:.9em;color:var(--text-secondary);">不拿半成品問 owner。先做到可合併、CI 綠、live proof 完成，owner 只需回答合併還是刪除。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:4px;">🔬 Live Proof Gate</div>
<div style="font-size:.9em;color:var(--text-secondary);">合併前必須對真實環境驗證 (真帳號、真服務、真裝置)。mocks、fixtures、CI 都不能取代 live proof。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:4px;">🔑 權限分層</div>
<div style="font-size:.9em;color:var(--text-secondary);">triage、實作、push、merge、release 是分開的授權。沒拿到就停在邊界、回報確切的下一步。</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:8px;padding:14px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--accent);margin-bottom:4px;">📜 跨 session 記憶</div>
<div style="font-size:.9em;color:var(--text-secondary);">orchestrator 獨佔一份 markdown，派工、決策、release 全留底。跟 Ralph 的 progress.txt 是同一個做法。</div>
</div>
</div>

<p>注意這四道關卡，沒有一個是這篇外層 Loop 才發明的: live proof 是時機③的獨立驗收 (上一篇 Outcome 那種「裁判真的去操作 artifact」)，權限分層和 decision-ready 是把「什麼能自動、什麼要回報」寫成前饋規則 (第二篇)，跨 session 記憶是 Ralph 那個進度搬到磁碟的做法。外層 Loop 真正的工作，是把前面幾篇拆過的這些設計，組成一個人不在現場也能安全跑的迴圈。</p>

<h2 id="外層-loop-不只用在寫程式">外層 Loop 不只用在寫程式</h2>

<p>前面這些例子，從 Ralph、Symphony、cron 到剛剛的 maintainer orchestrator，清一色都是建在 coding agent 上，因為那是工具生態最成熟的場景。但外層 Loop 的核心跟 coding 無關，而且它的變形非常多: 同樣是定時醒來的 agent，有人拿來整理收件匣、有人拿來監控競品、有人拿來每天產一份情報簡報、有人拿來跑一個全天候執行的主動助理。場景差很遠，做出來的樣子也差很遠，Ralph 的 bash 迴圈、Symphony 的看板、cron 排程都只是其中幾種。</p>

<p>但不管哪一種，核心其實就三個問題，你自建任何一個外層 Loop 都得回答:</p>

<ul>
  <li><strong>怎麼被觸發</strong>: 固定 cron、動態間隔、還是事件 (webhook、新郵件、CI 失敗、看板上多一張 ticket)?</li>
  <li><strong>醒來看什麼</strong>: 它一醒來，要讀哪些 context 之外的狀態，才知道進度到哪、有沒有新工作? (Ralph 讀 git 和 progress.txt,Symphony 讀看板，主動助理讀 inbox。)</li>
  <li><strong>做完把結果送去哪</strong>: 開一個 PR、寄一封簡報、送進 triage 收件匣，還是沒事就直接歸檔?</li>
</ul>

<p>把這三個問題對著你的場景回答一遍，就有一個外層 Loop 了，不管它做的是 Text-to-SQL 報表、知識庫監控，還是寫 code。這也是為什麼說它是更廣義的工程: 換掉場景，核心不變。</p>

<h2 id="收尾-loopcraft-的五層迴圈">收尾: loopcraft 的五層迴圈</h2>

<p>把這個系列走過的迴圈疊起來看，swyx 在 <a href="https://www.latent.space/p/ainews-loopcraft-the-art-of-stacking">loopcraft: the art of stacking loops</a> 用一張圖概括了「堆疊迴圈」這件事。由內到外疊了五層，每一層的跳出條件和時間尺度都不一樣:</p>

<p><img src="/assets/images/harness-engineering-6/loopcraft.png" alt="loopcraft: 由內到外五層巢狀迴圈，從 token 到開放探索" /></p>

<ul>
  <li><strong>① token loop</strong> (秒): 模型逐 token 取樣、接上、再取樣，出現 stop token 就停。這層在 harness 底下，是模型自己的解碼。</li>
  <li><strong>② agent turn</strong> (分): 呼叫工具、把結果讀回來，直到沒有工具要呼叫。就是第一篇講的那個「笨迴圈」，時機①的工具層回饋就發生在這一圈裡。</li>
  <li><strong>③ /goal loop</strong> (時): 跑、評、重試，直到判定目標達成。就是第五篇的時機③。</li>
  <li><strong>④ MetaLoop</strong> (天): swyx 給它的副標是「生迴圈的迴圈」，動作是開子代理、審查、汰除。這就是這一篇的時機④,Ralph、Symphony、cron、maintainer orchestrator 全都在這一層。</li>
  <li><strong>⑤ ???? loop</strong> (∞): 定目標、分配資源、汰除方向，沒有跳出條件，開放探索。swyx 自己都還沒給它名字。</li>
</ul>

<p>五層裡，①②是模型本身就給你的，③成熟的 coding agent 也幫你包好了。<strong>ihower 認為，loop engineering 這個詞最想強調的重點方向，就是第④層 MetaLoop，也就是這一篇講的外層 harness</strong>: 任務怎麼被觸發、跨 session 的進度怎麼傳、什麼時候算整件事做完，這些都得你自己設計。當然，如果你用的是 coding agent，要做這層最簡單的做法，就是善用它內建的 loop 排程和 goal 功能 (前面講過的 <code class="language-plaintext highlighter-rouge">/loop</code>、Routines、Automations 配上 <code class="language-plaintext highlighter-rouge">/goal</code>)，把排程和終止條件接起來，就不需要自己做了。</p>

<blockquote>
  <p>編按: LangChain 的 Sydney Runkle 在 <a href="https://x.com/sydneyrunkle/status/2066928783534289358">The Art of Loop Engineering</a> 給了一個不同的版本 (Agent loop、Verification loop、Event loop、Hill climbing loop)，前三層大致對應這個系列的四個時機，而她的第四層 Hill climbing (把生產環境的 trace 送回去修改 harness 本身的設定)，正好是下一篇要談的東西。</p>
</blockquote>

<blockquote>
  <p>編按: 寫這篇時本來想把 Claude Code 的 dynamic workflows 也一起講 (它一樣是開子代理、平行分流、審查、汰除這類外層編排)。一方面它自成一個大題目，小編之前也單獨寫過一篇了; 另外就是，站在「自行開發特定場景的 Agent」的角度，ihower 認為 dynamic workflows <strong>不實用</strong>: 它那套讓模型即時生成編排腳本、決定性 resume 的機制，是為了當「服務任何任務的通用 harness」才需要的，而自建垂直場景的 agent (vertical agent，例如 Text-to-SQL、訪談、RAG) 時，你本來就知道自己的場景，直接針對它手寫一套確定性的編排就好 (像本文 Symphony 那種貼合場景設計的控制流程)，不必靠模型每次即時生成，既便宜可靠又省 token。所以這篇就沒再展開，有興趣的可以看那篇: <a href="/2026/06/05/code-act-to-dynamic-workflows/">從 Code Act 到 Claude Code Dynamic Workflows</a>。</p>
</blockquote>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Workflow" /><summary type="html"><![CDATA[📚 給 Agent 開發者的駕馭工程 (全 9 篇) 1. 基礎: Deep Agent 的六項內建能力 2. 核心: Agent 要的是回饋迴路，不是完美提示 3. 回饋時機一: 工具回傳值，是寫給 agent 的回饋 4. 回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent 5. 回饋時機三: 單輪結束的驗收，Goal 與 Outcomes 6. 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron (本篇) 7. 進階: 自我改進 Harness, Meta-Harness 與爬坡 8. 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson 9. 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建? 🎞️ 搭配演講投影片: 給 Agent 開發者的 Harness+Loop Engineering]]></summary></entry><entry><title type="html">給 Agent 開發者的駕馭工程 (7): 進階: 自我改進 Harness, Meta-Harness 與爬坡</title><link href="https://blog.aihao.tw/2026/06/26/harness-engineering-7-self-improving/" rel="alternate" type="text/html" title="給 Agent 開發者的駕馭工程 (7): 進階: 自我改進 Harness, Meta-Harness 與爬坡" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/26/harness-engineering-7-self-improving</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/26/harness-engineering-7-self-improving/"><![CDATA[<div class="series-toc" style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:24px 0;font-size:.92em;line-height:1.85;">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📚 給 Agent 開發者的駕馭工程 (全 9 篇)</div>
<div>1. <a href="/2026/06/26/harness-engineering-1-deep-agent-capabilities/">基礎: Deep Agent 的六項內建能力</a></div>
<div>2. <a href="/2026/06/26/harness-engineering-2-what-is-harness-engineering/">核心: Agent 要的是回饋迴路，不是完美提示</a></div>
<div>3. <a href="/2026/06/26/harness-engineering-3-tool-execution-feedback/">回饋時機一: 工具回傳值，是寫給 agent 的回饋</a></div>
<div>4. <a href="/2026/06/26/harness-engineering-4-mid-run-injection/">回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent</a></div>
<div>5. <a href="/2026/06/26/harness-engineering-5-goal-and-outcomes/">回饋時機三: 單輪結束的驗收，Goal 與 Outcomes</a></div>
<div>6. <a href="/2026/06/26/harness-engineering-6-outer-loop/">回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</a></div>
<div>7. <strong style="color:var(--text);">進階: 自我改進 Harness, Meta-Harness 與爬坡</strong> <span style="color:var(--text-secondary);">(本篇)</span></div>
<div>8. <a href="/2026/06/26/harness-engineering-8-model-harness-fit/">收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</a></div>
<div>9. <a href="/2026/06/26/harness-engineering-9-agent-frameworks/">自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</a></div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">🎞️ 搭配演講投影片: <a href="https://ihower.tw/presentation/harness.html">給 Agent 開發者的 Harness+Loop Engineering</a></div>
</div>

<p>前面六篇談了一整套用回饋約束 agent 的做法: 工具裡閉合的最小迴圈、使用者中途的 steering、單輪結束的驗收、跨 session 的外層 loop。但這些做法有個共通點一直沒挑明: 從頭到尾，動手改 harness 的都是同一個人，你。你寫 Ralph 的 prompt、定 Symphony 的看板規則、設 maintainer orchestrator 的四道關卡，agent 始終是在你定好的 harness 裡運作。</p>

<p>這一篇要問的，就是把這件事再往前推一步: <strong>如果連 harness 本身，都讓 agent 自己改呢?</strong> 也就是，讓 agent 根據自己跑出來的 trace 和 eval，回頭改自己的 harness。</p>

<p>LangChain 的 <a href="https://x.com/sydneyrunkle/status/2066928783534289358">Sydney Runkle</a> 把這層叫 hill climbing loop (爬坡迴圈)，擺在她那套四層迴圈的最外圈，意思就是把生產環境的 trace 拿回來，改 harness 本身的設定。它跟前三層 (對應這個系列的回饋時機) 的差別在於: 前三層是在一個既定的 harness 裡閉合迴圈，這一層是去動那個 harness。換個講法，一般的 loop engineering 問的是「agent 該不該繼續跑」，而自我改進的 harness 問的是「這一輪跑完之後，系統到底學到了什麼」。</p>

<p>先把一句話放在前面，這篇後面所有做法都以它為前提: <strong>自我改進不是讓 agent 想改什麼就改什麼。</strong> 真正能用的版本，都是在 eval、trace、版本控制和退步關卡 (regression gate) 的限制下，讓 agent 提出「可被驗證、可被回滾」的 harness 變更。少了這些限制，所謂的自我改進，跟讓 agent 自動把 harness 改壞，其實分不太出來。後面三條路講的花樣再多，底下這幾項缺一個，自我改進都不成立:</p>

<div style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:1.5em 0;font-size:.9em;line-height:1.95;">
<div style="font-weight:600;margin-bottom:8px;color:var(--accent);">📋 受控改版的必要條件</div>
<strong>固定評測集</strong> (拿來爬坡的題目) · <strong>Regression set</strong> (每個修好的失敗都變成永久測試) · <strong>Production failure trace</strong> (真實世界跑出來的新失敗) · <strong>版本化的 prompt / 工具 / schema</strong> (改動可追蹤、可比較) · <strong>晉級關卡 (promotion gate)</strong> (主任務沒變好、或舊案例退步就不收) · <strong>回滾機制</strong> (收錯了能退回去) · <strong>高風險改動的人工 review</strong>
</div>

<p>這也是為什麼在這一篇，Goodhart 問題要比前幾篇更早提防: 一旦 agent 會根據 eval 改 harness，它就有動機把 eval 本身當成目標，而不是把真實任務做好。後面每一條路都會再遇到這個問題。</p>

<h2 id="誰在改-harness">誰在改 Harness?</h2>

<p>先把「改 harness」這件事的層次分清楚。Harrison Chase 在 <a href="https://x.com/hwchase17/status/2040471961206214864">Continual learning for AI agents</a> 裡把一個 agent 系統拆成三層，而「持續學習」可以發生在任何一層:</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:24px 0;">
<div style="flex:1 1 200px;border:1px solid #cf222e;border-radius:8px;padding:16px 18px;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:6px;">🧠 Model 模型層</div>
<div style="font-size:.9em;color:var(--text);">改的是權重 (SFT、RL)。這是大廠的事，還卡著 catastrophic forgetting 這個未解的研究難題，自建 agent 多半碰不到。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">⚙️ Harness 層</div>
<div style="font-size:.9em;color:var(--text);">驅動 agent 的程式、固定的 prompt 與工具。本篇主角，agent 可以自己動它。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:6px;">📄 Context 層</div>
<div style="font-size:.9em;color:var(--text);">harness 之外、用來配置它的指示、skills、記憶。agent 也能自己更新 (OpenClaw 把這個叫「做夢 dreaming」)。</div>
</div>
</div>

<p>模型層動不了，但 harness 層和 context 層，agent 是有可能自己改的。為什麼這件事突然變得值得認真談? 因為前面講了五篇，harness 不再是模型的附屬品，它本身就是決定表現的一大關鍵 (第二篇那個「同一個模型換 harness、Terminal Bench 從三十幾名進到前五」的對照)。既然 harness 這麼重要、又是純文字和程式碼，那「誰來改它、能不能自動改」自然就成了下一個問題。</p>

<p>LangChain 的 Viv Trivedi 描述過一個更大的循環，叫 <a href="https://x.com/Vtrivedy10/status/2039872562662941118">Model-Harness Training Loop</a>: harness 裡被驗證有用的元件會被產品化，下一代模型帶著這些 harness 一起後訓練，模型變強之後 harness 再演化。模型和 harness 是互相塑造的。這個大循環裡，廠商那半 (把 harness 訓練進模型) 是最後一篇的題目; 這一篇先看靠近我們這半: 自建 agent 怎麼讓自己的 harness 自動變好。</p>

<h2 id="三條路一個共同前提">三條路，一個共同前提</h2>

<p>整理社群這半年的做法，讓 harness 自我改進大致有三條路:</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:22px 0;">
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">📡 路一: 讀 Production Traces</div>
<div style="font-size:.9em;color:var(--text-secondary);">線上的失敗案例做錯誤分析，沉澱成新的 Judge 與規則，插回前面的回饋時機。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">🔧 路二: Agent 當優化器</div>
<div style="font-size:.9em;color:var(--text-secondary);">任何可快速評估的指標，都能交給 coding agent 用「修改 → 評估」迴圈爬坡。</div>
</div>
<div style="flex:1 1 200px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">📚 路三: Self-Improving Skills</div>
<div style="font-size:.9em;color:var(--text-secondary);">把每輪學到的教訓寫回 skill，經驗沉澱成可重用的能力。</div>
</div>
</div>

<p>三條路長得不一樣，但站在同一個前提上: <strong>要有一個能自動打分的 eval。</strong> 這不是口號。小編先前寫的 <a href="https://blog.aihao.tw/2026/06/03/coding-agent-as-optimizer/">Coding Agent 作為軟體優化器</a> 整篇都在講同一件事: 當實驗能跑得比人快 100 倍，eval 就變成整條迴圈的瓶頸，指標一旦能被鑽漏洞或量錯東西，模型就會在評測數字上變強、實際上線卻變差。這篇的三條路，每一條的成敗最後都會回到這個前提上，所以後面會反覆提到它。先從第一條看起。</p>

<h2 id="路一-從-production-trace-到新的-judge">路一: 從 Production Trace 到新的 Judge</h2>

<p>第一條路最直接: 把線上跑出來的失敗，變成新的把關規則。</p>

<p>為什麼非得靠 production 不可? 因為離線 eval 蓋不住真實世界的無界輸入。你上線前準備的測試資料集，涵蓋的是你「想得到」的情況; 但使用者會用你完全想不到的方式使用產品，而那些案例，離線 dataset 裡一題都沒有。</p>

<p>這裡有個觀察小編覺得蠻到位，姑且叫它「請勿」標語定律: prompt 裡每一條「不可以做 X」，就像現實中的「請勿XXX」標語，一定都是因為有人真的做過，而且你一定想不到竟然有人會去做。換句話說，<strong>你的 system prompt 禁止清單，就是你的事故報告史。</strong> 而新的事故，只會發生在 production。這也呼應第二篇的 ratchet 心法: 你只在看到真實失敗時才加一條約束。差別只在，離線時代的「真實失敗」靠你自己測出來，上線後的「真實失敗」藏在 trace 裡。</p>

<p>於是 debug 的方式也變了，從「讀程式碼找邏輯錯誤」變成「分析 traces」。你要抓的不再是某一行寫錯，而是推理出錯、工具用得很沒效率、決策品質不好這類問題，這些在程式碼裡根本看不出來。</p>

<p>怎麼在生產規模下分析 trace、該看哪些、報告怎麼寫，小編之前在 <a href="https://blog.aihao.tw/2026/06/02/agent-trace-analysis/">如何用 AI 分析 Agent traces?</a> 整篇講過，這裡不重複。ihower 自己的做法是把這套用 <code class="language-plaintext highlighter-rouge">braintrust-cli</code> (或 <code class="language-plaintext highlighter-rouge">langfuse-cli</code>) 包成一個 skill，一句話啟動「review 最近 24 小時的 trace，產生一份品質報告」，裡面寫死了加權取樣規則和固定的報告格式。包成 skill 的好處之一，正好接回上一篇: 它可以排程定期跑，掛上 <code class="language-plaintext highlighter-rouge">/loop</code> 或 cron，讓「分析自己」這件事本身也納入外層 loop。</p>

<p>這篇要補的是後半段: 分析完之後，那份報告怎麼變成 harness 的永久改進。閉合迴圈分三步:</p>

<svg viewBox="0 0 700 250" style="width:100%;max-width:680px;height:auto;display:block;margin:24px auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="從 trace 到新 Judge 的閉合迴圈">
<defs>
<marker id="j6A" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" style="fill:var(--accent)" /></marker>
</defs>
<rect x="20" y="40" width="180" height="76" rx="8" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="110" y="68" text-anchor="middle" style="fill:var(--text);font-size:13px;font-weight:700">1️⃣ 發現</text>
<text x="110" y="89" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">production traces 裡</text>
<text x="110" y="104" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">找到新的失敗模式</text>
<rect x="260" y="40" width="180" height="76" rx="8" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="350" y="68" text-anchor="middle" style="fill:var(--text);font-size:13px;font-weight:700">2️⃣ 沉澱</text>
<text x="350" y="89" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">失敗案例加進 dataset</text>
<text x="350" y="104" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">人工標註，做出對齊的 Judge</text>
<rect x="500" y="40" width="180" height="76" rx="8" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" />
<text x="590" y="64" text-anchor="middle" style="fill:var(--accent);font-size:13px;font-weight:700">3️⃣ 部署</text>
<text x="590" y="84" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">新 Judge 插回三時機之一</text>
<text x="590" y="99" text-anchor="middle" style="fill:var(--text-secondary);font-size:10.5px">工具內 / 單輪 / 外層 loop</text>
<text x="590" y="111" text-anchor="middle" style="fill:var(--text-secondary);font-size:9.5px"> </text>
<line x1="200" y1="78" x2="258" y2="78" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#j6A)" />
<line x1="440" y1="78" x2="498" y2="78" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#j6A)" />
<path d="M590,116 V170 H110 V118" style="fill:none;stroke:var(--accent);stroke-width:1.5;stroke-dasharray:5 3" marker-end="url(#j6A)" />
<text x="350" y="163" text-anchor="middle" style="fill:var(--accent);font-size:11px;font-weight:600">監控新 Judge → 又抓到新的失敗模式 → 回到 1️⃣</text>
<text x="350" y="210" text-anchor="middle" style="fill:var(--text);font-size:11.5px;font-weight:600">trace 是證據，Judge 是產出，harness 負責執行</text>
<text x="690" y="242" text-anchor="end" style="fill:var(--text-secondary);font-size:10px">✏️ 小編製圖</text>
</svg>

<p>第三步「部署」是這篇跟前幾篇接得最緊的地方: 沉澱出來的新 Judge，不是擺著看，而是插回前面講過的幾個自動時機。是一個確定性檢查，就放進工具層 (時機①); 是判「整輪做完了沒」的 rubric，就放進單輪驗收 (時機③); 是跨 session 的品質監控，就掛在外層 loop (時機④)。然後監控它、看它有沒有抓到新東西，再回到第一步。這就是線上跟離線接起來的完整閉合迴圈。</p>

<p>要特別強調第二步那個「對齊」。前面幾篇反覆講過，Judge 要先對齊才有意義: 一個沒跟你的判斷校準過的 Judge，只是把你不信任的東西自動化。所以這條迴圈裡，人最該花力氣的不是第一步的「找」(那可以交給 trace 分析 agent)，而是第二步把失敗歸納成分類、做出一個你信得過的 Judge。這一點留到後面再談。</p>

<h2 id="路二-agent-當優化器用-eval-爬坡">路二: Agent 當優化器，用 Eval 爬坡</h2>

<p>第二條路把 harness 當成一段可編輯的程式碼，讓 coding agent 反覆改它、爬分。</p>

<p>「改一段程式 → 跑 → 讀指標 → 留好的丟壞的」這個優化迴圈，小編先前在 <a href="https://blog.aihao.tw/2026/06/03/coding-agent-as-optimizer/">Coding Agent 作為軟體優化器</a> 已經從 Karpathy 的 autoresearch 拆得很細，基本原理這裡不重複，只借 Karpathy 一句話概括: 「任何可以快速評估的指標，都可以交給 agent 來最佳化。」這篇只換一個對象: 把優化目標從訓練腳本、搜尋排序，換成 harness 本身。前提一樣是回饋: <strong>回饋夠好、能自動產生，爬坡才成立</strong>，這也是為什麼 coding 類任務最先成功 (測試好寫，等於有可驗證的獎勵)，而 UI/UX 至今還難 (視覺評估弱，撐不起爬坡)。</p>

<h3 id="hill-climbing-的配方">Hill-Climbing 的配方</h3>

<p>直接拿 harness 來爬坡，Viv 在 <a href="https://x.com/Vtrivedy10/status/2041927488918413589">Better Harness: A Recipe for Harness Hill-Climbing with Evals</a> 給了一份很完整的配方。它的核心心智模型蠻好記: 把 eval 當成 agent 的訓練資料。</p>

<blockquote>
  <p>在傳統機器學習裡，是「模型 + 訓練資料 + 梯度下降 → 更好的模型」; 對 agent 來說，就是「harness + evals + harness engineering → 更好的 agent」。每一個 eval 案例都貢獻一個訊號 (這一步動作對不對、產出對不對)，這個訊號引導下一次該怎麼改 harness。</p>
</blockquote>

<p>既然 eval 是訓練資料，那 ML 訓練的紀律就整套搬得過來，而其中最關鍵的一條是切出 holdout set:</p>

<div style="border:1px solid var(--border);border-radius:8px;overflow:hidden;margin:1.5em 0;font-size:.92em;">
<div style="display:flex;background:var(--surface);font-weight:600;">
<div style="flex:1 1 22%;padding:10px 12px;border-right:1px solid var(--border);">步驟</div>
<div style="flex:1 1 78%;padding:10px 12px;">在做什麼</div>
</div>
<div style="display:flex;border-top:1px solid var(--border);">
<div style="flex:1 1 22%;padding:10px 12px;border-right:1px solid var(--border);font-weight:600;">蒐集並標記</div>
<div style="flex:1 1 78%;padding:10px 12px;">手寫 + 從 production trace 挖 + 改寫外部資料集; 每個 eval 標上行為分類 (tool selection、multi-step reasoning…)</div>
</div>
<div style="display:flex;border-top:1px solid var(--border);">
<div style="flex:1 1 22%;padding:10px 12px;border-right:1px solid var(--border);font-weight:600;">切分資料</div>
<div style="flex:1 1 78%;padding:10px 12px;">分成最佳化集 (拿來爬坡) 和 holdout 集 (留著驗收)。<strong>這步最重要</strong>: 自動爬坡天生會過擬合，holdout 是「真泛化」的代理</div>
</div>
<div style="display:flex;border-top:1px solid var(--border);">
<div style="flex:1 1 22%;padding:10px 12px;border-right:1px solid var(--border);font-weight:600;">跑 baseline</div>
<div style="flex:1 1 78%;padding:10px 12px;">改任何東西之前先測一次，所有改進都對著這個基準量</div>
</div>
<div style="display:flex;border-top:1px solid var(--border);">
<div style="flex:1 1 22%;padding:10px 12px;border-right:1px solid var(--border);font-weight:600;">最佳化</div>
<div style="flex:1 1 78%;padding:10px 12px;">每輪診斷 trace、提出一個有針對性的 harness 改動 (一次只改一處，避免混淆變因)</div>
</div>
<div style="display:flex;border-top:1px solid var(--border);">
<div style="flex:1 1 22%;padding:10px 12px;border-right:1px solid var(--border);font-weight:600;">驗證不退步</div>
<div style="flex:1 1 78%;padding:10px 12px;">確認新改動讓新案例通過、又沒讓原本通過的退步; 有退步就把退步清單留著，下一輪一起修</div>
</div>
<div style="display:flex;border-top:1px solid var(--border);">
<div style="flex:1 1 22%;padding:10px 12px;border-right:1px solid var(--border);font-weight:600;">人工審查</div>
<div style="flex:1 1 78%;padding:10px 12px;">上線前人看一遍，抓指標看不到的邊角，例如那種「對 holdout 沒壞處、但其實是浪費 token 的過擬合指令」</div>
</div>
</div>

<p>NeoSigma 開源的 <a href="https://github.com/neosigmaai/auto-harness">auto-harness</a> 把同一套思路跑成了一個完整的閉合迴圈，而且補上了一個關鍵機制: regression gate (退步關卡)。他們在 Tau3 bench 上，固定底層模型 (GPT-5.4) 不動，只靠改 harness，把驗證集分數 (val score) 從 0.560 推到 0.780，跑了 18 個批次、96 個 harness 實驗。機制是: 從 trace 挖失敗 → 依「根因」分群 (這步很關鍵，逼你針對底層原因，而不是逐個症狀補) → 把失敗變成可重複的 eval 案例 → 提出 harness 改動 → <strong>只有同時滿足「不讓任何已修好的失敗退步 (≥80%)」和「整體分數不退步」才收下，否則 revert</strong>。</p>

<p>退步關卡為什麼是讓收益不斷累積的關鍵? 因為每個被修好的失敗都變成一個永久的測試案例，系統就再也回不到比它已經解掉的更差的狀態。沒有這道關卡，你只是在原地反覆試錯，同樣的失敗一再出現; 有了它，每個改進都是疊加的，門檻只會越來越高。這跟傳統軟體工程的 TDD、回歸測試是同一個道理，只是這裡的測試案例是從 production 失敗累積出來的。NeoSigma 的 regression set 從 0 增加到 17 條，越往後，能通過關卡的候選改動越少 (後段大多被擋下或 revert)，正好說明問題越來越難、改進越來越紮實。</p>

<h3 id="順帶提醒-goodhart-還在">順帶提醒: Goodhart 還在</h3>

<p>reward hacking 在 <a href="https://blog.aihao.tw/2026/06/03/coding-agent-as-optimizer/">優化器那篇</a> 講過一大段，改 harness 時一樣存在，而且因為影響面更大反而更危險。Langfuse 那個例子最典型: agent 為了刷分，直接移除「執行前先讓使用者確認」這道人工核准關卡，因為自動測試環境裡沒有真人會按確認，留著就永遠拿 0 分，它還順手刪掉測試沒涵蓋的真功能。結論很簡單: <strong>沒被量到的東西，就會被刪掉。</strong> 所以這條路的人工審查不是可有可無的禮貌，而是擋住 agent 把 harness 改成「eval 上看起來很強、真實使用者面前很爛」的那道關卡。</p>

<h3 id="gepa-對齊之後才自動化">GEPA: 對齊之後才自動化</h3>

<p>如果你已經有了一個對齊過的 Judge，那其中「改 prompt」這種最常見的改動，可以交給更系統化的工具。<a href="https://github.com/gepa-ai/gepa">GEPA</a> 就是一個: 它拿 dev set 的分數加上具體的錯誤回饋當訊號，反思 execution traces (執行軌跡)，再用 Pareto search 自動搜尋更好的寫法。GEPA README 講的最佳化對象是廣義的「文字型參數」(prompt、instructions 這類純文字設定)，所以同一套方法原則上也能延伸到 judge 的 prompt、schema 描述，不只是主 prompt。</p>

<p>但要再講一次前提: GEPA 的最佳化目標，就是 Judge 給的分數。<strong>Judge 不準，自動化只會讓你更快地往錯的方向走。</strong> 先把 Judge 對齊，再談自動化 prompt，順序不能反。</p>

<h3 id="meta-harness-讓-agent-重寫整個-harness">Meta-Harness: 讓 agent 重寫整個 harness</h3>

<p>把最佳化的對象從 prompt 一路升級到「整個 harness」，就是 Stanford 的 <a href="https://yoonholee.com/meta-harness/">Meta-Harness</a> (論文名字直接叫 End-to-End Optimization of Model Harnesses)。它要動的不只是 prompt，而是系統 prompt、工具定義、判斷完成的邏輯、context 管理整套外圍程式。</p>

<p>它跟一般 prompt 最佳化方法最大的不同，在「提案者能看到多少東西」。多數方法把歷史壓縮成一段摘要或一個分數; Meta-Harness 給提案者 (就是一個 Claude Code) 一個檔案系統，裡面放著所有先前候選 harness 的完整原始碼、分數、和 execution traces，讓它用 grep、cat 自己去讀。每一步最多送進 1000 萬個 token 的診斷脈絡 (對照它盤點的其他方法，最多只有 2.6 萬)。所以它不是看摘要猜，而是直接讀原始軌跡做「反事實診斷」: 精準定位是哪一個 harness 決策造成了失敗，再提出針對性的新版，評測、存檔、重複。</p>

<p>成績裡有個數字，正好接回第二篇講過的一點。第二篇講過「同一個模型，換一套沒在訓練時見過的 harness，排名能從三十幾名進到前五」，當時是想說明 harness 是能讓表現跨級距的關鍵。Meta-Harness 在 TerminalBench-2 上，讓 Haiku 4.5 用演化出來的 harness 跑到 37.6%，在所有 Haiku 4.5 的 agent 裡排第 1 (當時榜單狀態，會隨新 agent 加入變動)。一個小模型靠 harness 跨了級距; 而且這次更進一步: 不只是換 harness 能跨級距，連那套 harness 都不是人寫的，是 agent 自己演化出來的。</p>

<p>這條「讓 agent 改 agent」的研究線，往前可以追到 Sakana 的 <a href="https://sakana.ai/dgm/">Darwin Gödel Machine</a> (把自我改進當成演化搜尋，SWE-bench 從 20% 演化到 50%) 和 Meta 的 <a href="https://github.com/facebookresearch/hyperagents">HyperAgents</a> (連負責改善的 meta-agent 自己都在被改的範圍內)。這兩個都還是研究系統，看的是這條路能走到多遠，不是能直接搬上 production 的方案。它們的共同前提一樣: <strong>結果可驗證的問題域。</strong> 沒有一個可靠的打分機制，這套自我演化就無從爬起。</p>

<h2 id="路三-把學到的寫回-skill">路三: 把學到的寫回 Skill</h2>

<p>第三條路的單位不是 trace、也不是分數，而是 skill。創投 Garry Tan 把它講成一個動詞，叫 <a href="https://x.com/garrytan/status/2046876981711769720">skillify</a>: 每次 agent 犯了錯，就把這個失敗變成一個帶測試的 skill，讓同樣的錯再也不可能發生。</p>

<p>這其實就是第二篇 ratchet 心法的延伸 (「每當你發現 agent 犯了一個錯，就做一個工程上的解法，讓它再也不會犯同一個錯」)，只是這裡的「工程解法」不只是 AGENTS.md 裡的一行規則，而是一整包帶測試的能力。Garry 給的是一份 10 步檢查清單，一個失敗要走完這 10 步才算被 skillify:</p>

<div style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:1.5em 0;font-size:.9em;line-height:1.9;">
<div style="font-weight:600;margin-bottom:8px;color:var(--accent);">📋 skillify 的 10 步</div>
SKILL.md 契約 → 確定性程式碼 → 單元測試 → 整合測試 → LLM evals → resolver 觸發條目 → resolver eval (驗路由有沒有導對) → DRY 重複稽核 → 端到端煙霧測試 → 歸檔規則
</div>

<p>清單裡有兩件事正好呼應前面幾篇。第一，第 2 步「確定性程式碼」: skill 教 agent 先用它的判斷力寫出一段確定性的腳本，之後就用那段腳本取代判斷，而不是每次都重新推理。這跟第三篇工具層的第一原則「能確定就不用 LLM」是同一句話，Garry 的例子是查日曆: 與其讓模型每次推理日期，不如讓它寫一個 grep 腳本，然後規則強制它去跑腳本。第二，清單裡有一半都是各種測試 (單元、整合、LLM eval、resolver eval、煙霧)，這又回到那個共同前提: 沒有 eval 把關的 skill，只是程式碼剛好今天能跑。</p>

<p>不過 skillify 不是銀彈，這點 LangChain 的 Viv 在<a href="https://x.com/Vtrivedy10/status/2046979341427331522">回應 Garry 那串</a>時講得蠻誠實，值得一起看:</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:22px 0;">
<div style="flex:1 1 300px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">Skill 太多，context engineering 反而出問題</div>
<div style="font-size:.9em;color:var(--text-secondary);">如果 skill 一多、彼此難以區分何時該用，負責路由的 resolver 就會失準，你又回到第一篇講的 context rot。可能需要 skill search、把相似的 skill 合併。</div>
</div>
<div style="flex:1 1 300px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">別困在 skill learning 的局部最佳 (local minima)</div>
<div style="font-size:.9em;color:var(--text-secondary);">skill 適合修有界、有範圍的問題; 但要解真正困難的長程任務，得從目標往回設計整個 harness 架構，光靠一個個補 skill 容易陷進局部最佳。</div>
</div>
</div>

<p>而 <a href="https://x.com/aparnadhinak/status/2042022750135709882">Aparna Dhinakaran</a> 點出的問題更根本: skills 會腐爛。今天寫好的 skill，六週後上游 API 改了結構，它就靜默地回傳垃圾; agent 自動生的 skill 可能 trigger 太弱永遠不被觸發，變成沒人用、白佔 index token 的廢檔; 或者 agent 週一生了 <code class="language-plaintext highlighter-rouge">deploy-k8s</code>、週四又從另一段對話生了 <code class="language-plaintext highlighter-rouge">kubernetes-deploy</code>，兩個都在、觸發詞相近，路由就開始亂選。她講得直接: 「沒有測試，任何 codebase 都會腐爛」，這是軟體工程在 2005 年就解決的問題，agent 的 skill 沒有不同。Nous 的 Hermes Agent 能讓 agent 自己創建、修補、刪除 skill，設計很漂亮，但它不測自己的 skill，沒有 resolver eval 驗路由、沒有稽核抓重複。</p>

<p>所以這條路的結論跟前兩條一樣: skillify 的關鍵不在「會不會寫 skill」，而在那 10 步裡的測試。用 Aparna 的話: 評估是讓 harness 自動建構自己的鷹架。沒有評估把關，自我改寫的 skill 只是把幻覺寫進長期記憶。</p>

<h2 id="自我改進-harness-的十層堆疊">自我改進 Harness 的十層堆疊</h2>

<p>前面三條路是三種切入，散在不同做法裡。AlphaSignal 把這個題目下的 28 篇論文<a href="https://alphasignalai.substack.com/p/10-layers-of-self-improving-harness">歸納成一套十層堆疊</a>，正好把這三條路、加上開頭那張「受控改版的必要條件」清單，展開成一套從基準到量測的完整結構。它的定調跟這篇一致: 自我改進不是「讓 agent 自己重寫自己」。對照靜態 harness 看最清楚: 靜態版一失敗只能等人工修補，改完就結束，系統不會從這次失敗累積出任何改進; 自我改進版則是模型凍結，失敗證據 → 改 harness → 過關卡 → 版本化 → 再回頭量 worker 有沒有真的變好，形成一個閉合迴圈，比較像替 agent 行為做 CI (持續整合)。</p>

<svg viewBox="0 0 720 152" style="width:100%;max-width:680px;height:auto;display:block;margin:24px auto 4px" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="靜態 Harness 流程">
<defs>
<marker id="stm1" markerWidth="9" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker>
</defs>
<text x="360" y="24" text-anchor="middle" style="fill:var(--text);font-size:14px;font-weight:700">靜態 Harness</text>
<text x="360" y="43" text-anchor="middle" style="fill:#cf222e;font-size:10.5px;font-weight:600">失敗不會累積成改進，系統學不到東西</text>
<rect x="12" y="68" width="120" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="72" y="95" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">基礎模型</text>
<line x1="132" y1="91" x2="154" y2="91" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#stm1)" />
<rect x="156" y="68" width="120" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="216" y="95" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">靜態 Harness</text>
<text x="216" y="132" text-anchor="middle" style="fill:var(--text-secondary);font-size:9px">prompt · tools · 記憶規則 · validators</text>
<line x1="276" y1="91" x2="298" y2="91" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#stm1)" />
<rect x="300" y="68" width="120" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="360" y="95" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">跑任務</text>
<line x1="420" y1="91" x2="442" y2="91" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#stm1)" />
<rect x="444" y="68" width="120" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="504" y="95" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">失敗</text>
<line x1="564" y1="91" x2="586" y2="91" style="stroke:var(--text-secondary);stroke-width:1.5;stroke-dasharray:5 3" marker-end="url(#stm1)" />
<rect x="588" y="68" width="120" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5;stroke-dasharray:5 3" />
<text x="648" y="95" text-anchor="middle" style="fill:var(--text-secondary);font-size:12px;font-weight:600">人工修補</text>
</svg>

<svg viewBox="0 0 720 290" style="width:100%;max-width:700px;height:auto;display:block;margin:36px auto 24px" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="自我改進 Harness 閉合迴圈">
<defs>
<marker id="sfm1" markerWidth="9" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker>
<marker id="sfm2" markerWidth="9" markerHeight="8" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" style="fill:var(--accent)" /></marker>
</defs>
<text x="360" y="26" text-anchor="middle" style="fill:var(--accent);font-size:14px;font-weight:700">自我改進 Harness</text>
<rect x="55" y="52" width="130" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="120" y="79" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">模型凍結</text>
<line x1="185" y1="75" x2="213" y2="75" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#sfm1)" />
<rect x="215" y="52" width="130" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="280" y="79" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">失敗證據</text>
<line x1="345" y1="75" x2="373" y2="75" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#sfm1)" />
<rect x="375" y="52" width="130" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="440" y="79" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">改 Harness</text>
<line x1="505" y1="75" x2="533" y2="75" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#sfm1)" />
<rect x="535" y="46" width="130" height="58" rx="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" />
<text x="600" y="71" text-anchor="middle" style="fill:var(--accent);font-size:12px;font-weight:700">關卡 Gate</text>
<text x="600" y="89" text-anchor="middle" style="fill:var(--text-secondary);font-size:8.5px">held-out · 退步 · 回滾</text>
<line x1="600" y1="104" x2="600" y2="186" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#sfm1)" />
<rect x="535" y="188" width="130" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="600" y="215" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">版本化 Harness</text>
<line x1="535" y1="211" x2="347" y2="211" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#sfm1)" />
<rect x="215" y="188" width="130" height="46" rx="7" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" />
<text x="280" y="215" text-anchor="middle" style="fill:var(--text);font-size:12px;font-weight:600">Worker 效益檢查</text>
<line x1="280" y1="188" x2="280" y2="100" style="stroke:var(--accent);stroke-width:1.5" marker-end="url(#sfm2)" />
<text x="296" y="147" text-anchor="start" style="fill:var(--accent);font-size:10px;font-weight:600">新證據</text>
<text x="360" y="262" text-anchor="middle" style="fill:var(--text-secondary);font-size:9.5px">Self-Harness 40.5→61.9% · AHE 69.7→77.0% · SkillOpt 52/52 cells</text>
<text x="710" y="282" text-anchor="end" style="fill:var(--text-secondary);font-size:8.5px">✏️ 小編重繪，仿 AlphaSignal</text>
</svg>

<p>把上面這個自我改進的閉合迴圈拆解開，從最底層的固定基準一路到最上面選配的權重更新，一共十層:</p>

<div style="display:flex;flex-direction:column;gap:7px;margin:24px 0;">
<div style="display:flex;align-items:center;gap:12px;border:1px solid var(--border);border-radius:6px;padding:9px 14px;background:var(--surface);">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--tag-bg);color:var(--accent);font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">1</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--text);">穩定基底</strong> <span style="color:var(--text-secondary);">Stable Substrate · 釘住改 harness 時不准動的基準</span></div>
</div>
<div style="display:flex;align-items:center;gap:12px;border:1px solid var(--border);border-radius:6px;padding:9px 14px;background:var(--surface);">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--tag-bg);color:var(--accent);font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">2</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--text);">執行軌跡</strong> <span style="color:var(--text-secondary);">Trace Log · 記下每個 tool call、重試、verifier 輸出與成本</span></div>
</div>
<div style="display:flex;align-items:center;gap:12px;border:1px solid var(--border);border-radius:6px;padding:9px 14px;background:var(--surface);">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--tag-bg);color:var(--accent);font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">3</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--text);">外部狀態</strong> <span style="color:var(--text-secondary);">External State · 學習放進 skills、記憶、policy 等可回滾的檔案</span></div>
</div>
<div style="display:flex;align-items:center;gap:12px;border:1px solid var(--border);border-radius:6px;padding:9px 14px;background:var(--surface);">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--tag-bg);color:var(--accent);font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">4</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--text);">失敗挖掘</strong> <span style="color:var(--text-secondary);">Failure Mining · 過濾 trace，依根因把失敗分群</span></div>
</div>
<div style="display:flex;align-items:center;gap:12px;border:1px solid var(--border);border-radius:6px;padding:9px 14px;background:var(--surface);">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--tag-bg);color:var(--accent);font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">5</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--text);">提案引擎</strong> <span style="color:var(--text-secondary);">Proposal Engine · 把證據轉成具體、可驗證的候選改動</span></div>
</div>
<div style="display:flex;align-items:center;gap:12px;border:1.5px solid var(--accent);border-radius:6px;padding:9px 14px;background:var(--tag-bg);">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--accent);color:#fff;font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">6</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--accent);">驗證關卡</strong> <span style="color:var(--text-secondary);">Validation Gate · 過 held-out + 不退步才准晉級。沒有關卡就沒有自我改進</span></div>
</div>
<div style="display:flex;align-items:center;gap:12px;border:1px solid var(--border);border-radius:6px;padding:9px 14px;background:var(--surface);">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--tag-bg);color:var(--accent);font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">7</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--text);">版本與回滾</strong> <span style="color:var(--text-secondary);">Versioning &amp; Rollback · 把 harness 當 repo，連被否決的改動都存檔</span></div>
</div>
<div style="display:flex;align-items:center;gap:12px;border:1px solid var(--border);border-radius:6px;padding:9px 14px;background:var(--surface);">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--tag-bg);color:var(--accent);font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">8</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--text);">路由與變體</strong> <span style="color:var(--text-secondary);">Routing &amp; Variants · 依任務類別分出多個 harness，避免修法互相矛盾</span></div>
</div>
<div style="display:flex;align-items:center;gap:12px;border:1.5px solid var(--accent);border-radius:6px;padding:9px 14px;background:var(--tag-bg);">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--accent);color:#fff;font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">9</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--accent);">效益量測</strong> <span style="color:var(--text-secondary);">Benefit Measurement · 量 worker 換新 harness 後實際有沒有變好</span></div>
</div>
<div style="display:flex;align-items:center;gap:12px;border:1px solid var(--border);border-radius:6px;padding:9px 14px;background:var(--surface);opacity:.85;">
<div style="flex:0 0 24px;height:24px;border-radius:50%;background:var(--surface);border:1px solid var(--border);color:var(--text-secondary);font-weight:700;font-size:12.5px;display:flex;align-items:center;justify-content:center;">10</div>
<div style="flex:1 1 auto;font-size:.92em;line-height:1.5;"><strong style="color:var(--text-secondary);">選配的權重更新</strong> <span style="color:var(--text-secondary);">Optional Weight Update · 前九層都穩了，才考慮動模型權重</span></div>
</div>
</div>

<p><strong>第 1-2 層: 先讓改動可被歸因。</strong> 第 1 層先釘住改 harness 時什麼不准動: 底層模型、runtime、工具、評測器、benchmark 切分。沒有固定的基準，分數一變就無從歸因到底是哪一項造成的。第 2 層留下完整 trace，每個 tool call、檔案讀寫、重試、verifier 輸出、成本都記下來，根因分析才有依據，而不是只看最後結果對不對 (對應路一的 trace 分析)。</p>

<p><strong>第 3-5 層: 把學習外部化，再提煉成提案。</strong> 第 3 層把學到的東西放在模型之外、可檢視可測試可回滾的地方 (skills、記憶檔、自然語言 policy、工具 wrapper)，論文裡的 SkillOpt 就是這條路，在 52 個評測 cell 上都拿到改進 (對應路三)。第 4 層過濾 trace，只挑「能教出可重用教訓」的失敗，再依根因和機制分群，而不是每筆 trace 一視同仁 (對應路一的錯誤分析、NeoSigma 的依根因分群)。第 5 層把證據轉成具體候選改動，每個提案都要寫清楚: 改了哪個元件、依據什麼證據、預期修好什麼、可能帶來哪些退步。</p>

<p><strong>第 6 層: 關卡，整套堆疊的關鍵。</strong> 改動只有通過 held-out 驗證、又沒造成退步，才准晉級。這是全文講得最重的一句: <strong>「沒有關卡，就沒有自我改進。」</strong> 這層正是 NeoSigma 的 regression gate、路二配方裡「驗證不退步」那一步。</p>

<p><strong>第 7-9 層: 管理、分流、量真實效益。</strong> 第 7 層把 harness 當成一個 repo 來管: diff、commit 歷史、還原，連被否決的改動也存下來，當未來的證據。第 8 層依任務類別、難度、失敗模式分出多個 harness 變體，而不是維護單一一份全域 policy，避免互相矛盾的修法累積在同一份 harness 裡。第 9 層最關鍵、也最常被跳過: 量的是「worker agent 換上新 harness 之後實際有沒有變好」，而不是拿「更新器本身好不好」當代理指標。</p>

<p><strong>第 10 層: 選配的權重更新。</strong> 最後一層是選配的，把 harness 改動再疊上模型權重更新; 建議只有在前面九層都已經可量測、能運作之後，才考慮動模型。</p>

<p>第 9 層的提醒，來自一篇標題就很直白的論文《Harness Updating Is Not Harness Benefit》(更新 harness 不等於 harness 有效益): 它發現不同 harness 更新器之間，本身能力差距其實很小 (最多 3.1 分)，但換到下游、不同模型不同 benchmark 上，實際效益差很多。換句話說，<strong>改了 harness 不等於 agent 真的變好。</strong> 如果 worker agent 載不動、跟不上、用不了那套新 harness，那個改動就只是一個好看的 diff。</p>

<p>所以整套十層，真正把「自我改進」跟「自動作弊」分開的，就是第 6 層的關卡和第 9 層的效益量測。用 AlphaSignal 的原話: 關卡才是產品; 沒有它，這個迴圈只會自動走向過擬合。面對任何自我改進的 demo，第一個該問的不是「它改了什麼」，而是「哪一道關卡收下了這個改動、收下之後 worker 真的變好了嗎」。</p>

<h2 id="三條路十層堆疊最後都回到-eval">三條路、十層堆疊，最後都回到 Eval</h2>

<p>不管是前面三條路，還是剛拆完的十層堆疊，擺在一起看，最後都指向同一個點: 那個能自動打分的 eval。trace 沉澱成 Judge，要 Judge 對齊; 爬坡要對著 holdout 和 regression gate; skillify 要那 10 步裡的測試; 連十層裡最關鍵的第 6、9 兩層，把關的也是 eval。harness 怎麼自我改進的花樣可以很多，但能不能真的變好，全卡在這一件事上。</p>

<blockquote>
  <p>編按: Viv 把這個心智模型<a href="https://x.com/Vtrivedy10/status/2045230994656305485">寫成一條公式</a>很傳神: <code class="language-plaintext highlighter-rouge">agent = fit(model, harness, evals)</code>。就像 sklearn 的 <code class="language-plaintext highlighter-rouge">fit(model, data)</code>,evals 是 agent 的訓練資料，每一個 eval 都在投票決定 harness 該怎麼改。差別只在，前沿大廠花幾百萬在模型訓練的資料品質上，而做 agent 的團隊，該把同等的心力投在 eval 的策展與設計上。</p>
</blockquote>

<p>而這正是自我改進 agent 最大的風險所在。AlphaSignal <a href="https://alphasignalai.substack.com/p/when-ai-agents-learn-to-engineer">另一篇講自我改進 agent 風險的整理</a>，後半很誠實地點出風險: 最大的問題是 reward hacking,agent 會一味衝高單一指標、鑽評分函數的漏洞，達標卻沒真正解決問題; 還有卡在局部最佳的傾向 (autoresearch 社群就觀察到 agent 常常只會保守地微調超參數，不去做真正創新需要的大膽架構改動); 另外還有失控把算力預算耗光、寫出不安全程式碼這些風險。</p>

<p>這些風險反過來說明了一件事: <strong>自我改進 agent 越強，設計好 eval、控管好安全風險、引導整個流程的資深工程師，反而越不可或缺。</strong> 當 agent 開始自己改自己，你的 grader 設計得好不好，直接決定它是真的變強、還是學會作弊。所以前面講的那些自動化機制，幾乎都把人留在某個關卡上: Better-Harness 明確有一步人工審查，NeoSigma 也保留了引入人工審查的選項。這跟這個系列從第一篇講到現在的態度一致: 工程師被移出的是逐步操作那幾圈，留在最外圈做的，是定義「什麼叫做好」並守住它。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Eval" /><summary type="html"><![CDATA[📚 給 Agent 開發者的駕馭工程 (全 9 篇) 1. 基礎: Deep Agent 的六項內建能力 2. 核心: Agent 要的是回饋迴路，不是完美提示 3. 回饋時機一: 工具回傳值，是寫給 agent 的回饋 4. 回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent 5. 回饋時機三: 單輪結束的驗收，Goal 與 Outcomes 6. 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron 7. 進階: 自我改進 Harness, Meta-Harness 與爬坡 (本篇) 8. 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson 9. 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建? 🎞️ 搭配演講投影片: 給 Agent 開發者的 Harness+Loop Engineering]]></summary></entry><entry><title type="html">給 Agent 開發者的駕馭工程 (8): 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</title><link href="https://blog.aihao.tw/2026/06/26/harness-engineering-8-model-harness-fit/" rel="alternate" type="text/html" title="給 Agent 開發者的駕馭工程 (8): 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/26/harness-engineering-8-model-harness-fit</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/26/harness-engineering-8-model-harness-fit/"><![CDATA[<div class="series-toc" style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:24px 0;font-size:.92em;line-height:1.85;">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📚 給 Agent 開發者的駕馭工程 (全 9 篇)</div>
<div>1. <a href="/2026/06/26/harness-engineering-1-deep-agent-capabilities/">基礎: Deep Agent 的六項內建能力</a></div>
<div>2. <a href="/2026/06/26/harness-engineering-2-what-is-harness-engineering/">核心: Agent 要的是回饋迴路，不是完美提示</a></div>
<div>3. <a href="/2026/06/26/harness-engineering-3-tool-execution-feedback/">回饋時機一: 工具回傳值，是寫給 agent 的回饋</a></div>
<div>4. <a href="/2026/06/26/harness-engineering-4-mid-run-injection/">回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent</a></div>
<div>5. <a href="/2026/06/26/harness-engineering-5-goal-and-outcomes/">回饋時機三: 單輪結束的驗收，Goal 與 Outcomes</a></div>
<div>6. <a href="/2026/06/26/harness-engineering-6-outer-loop/">回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</a></div>
<div>7. <a href="/2026/06/26/harness-engineering-7-self-improving/">進階: 自我改進 Harness, Meta-Harness 與爬坡</a></div>
<div>8. <strong style="color:var(--text);">收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</strong> <span style="color:var(--text-secondary);">(本篇)</span></div>
<div>9. <a href="/2026/06/26/harness-engineering-9-agent-frameworks/">自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</a></div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">🎞️ 搭配演講投影片: <a href="https://ihower.tw/presentation/harness.html">給 Agent 開發者的 Harness+Loop Engineering</a></div>
</div>

<p>這篇是系列觀念部分的收尾。前面七篇談的能力、回饋時機、自我改進，其實都在回答同一個問題: <strong>在一個固定的模型上，怎麼把 harness 搭好。</strong></p>

<p>但有一個維度一直沒談到: 時間。模型會升級，而模型一升級，你辛苦做好的 harness 不見得還繼續有效。這一篇補上那條時間軸，替前面的觀念收尾;最後第 9 篇會轉到實作，談自己開發 agent 時怎麼選框架。</p>

<p>模型一升級會發生的事，其實是一體兩面: <strong>harness 跟模型綁在一起，而且它會過期。</strong> 拆開來看:</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:24px 0;">
<div style="flex:1 1 280px;border:1px solid var(--accent);border-radius:8px;padding:16px 20px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">🔗 harness 綁在特定模型上</div>
<div style="font-size:.92em;color:var(--text);">同一套 harness 換一個模型，效果不見得一樣好。模型是針對 harness 做 post-training 的，不只是針對 API。</div>
</div>
<div style="flex:1 1 280px;border:1px solid #9a6700;border-radius:8px;padding:16px 20px;background:#fff8c5;">
<div style="font-weight:700;color:#9a6700;margin-bottom:8px;">⏳ harness 會過期</div>
<div style="font-size:.92em;color:var(--text);">模型每次變強，某些當初補強它的元件就不再有用，該被移除。</div>
</div>
</div>

<p>其中「會過期」這面比較直覺: harness 裡的元件，大多是在補某一代模型的某個短處。它容易忘記，所以加 memory; 它還不會自己規劃，所以加一份 todo 清單; 它不會自己驗收，所以加 grader; 它不熟某種編輯格式，所以替它包一層工具 (這幾樣剛好分別是前面幾篇談過的重點)。模型一變強，這些補強有的仍然必要，有的就變成阻力，難的是事先分不出來是哪一種。</p>

<p>兩件事背後其實是同一個機制: 模型和 harness 在一個共演化的迴圈裡互相塑造 (這個迴圈第七篇借 Viv 的 Model-Harness Training Loop 提過)。先從第一面看起: 為什麼 harness 換個模型就不靈。</p>

<h2 id="一套-harness換個模型就不靈了">一套 harness，換個模型就不靈了</h2>

<p>先看一個經驗證據。Terminal-Bench 2.0 這個 benchmark 有意思的地方，是它的排行榜不是只排「模型」，而是排「harness + 模型」的配對。同一個模型配上不同 harness，分數可以差好幾個百分點，差距甚至大過一個模型世代的升級。第二篇引過那個對照 (同一個 Opus，放在原廠 harness 排三十幾名，換進另一套進到前五)，就是這個 benchmark 上的事。</p>

<p>這裡有個乍看很矛盾的現象，Saurabh Shah 公開問過一個很多人心裡的疑惑: 怎麼可能有「別的 harness」，在某個頂級能力上，比「模型當初 RL 訓練時就對著用的那個 harness」還能幫到模型? 模型不是應該對自家 harness 最順手嗎?</p>

<p>這個問題要分兩步回答。第一步先講清楚「為什麼模型對自家 harness 特別順手」，也就是 Model-Harness-Fit (講的是 harness 的工具格式、回饋節奏、上下文策略、驗收方式，貼不貼合當下這個模型的訓練分布與能力邊界); 第二步再回頭解這個看似矛盾的現象。</p>

<h3 id="模型是針對-harness-訓練的不只是針對-api">模型是針對 harness 訓練的，不只是針對 API</h3>

<p>OpenAI 工程師 nicbstme 在一篇深入比對 Codex、Claude Code、GitHub Copilot CLI 三家 harness 實作的<a href="https://x.com/nicbstme/status/2051131906327212298">長文</a>裡，把這件事講得最透。一句話總結:</p>

<blockquote>
  <p>模型是針對 harness 做 post-training 的，不只是針對 API。</p>
</blockquote>

<p>展開來說: tool 的名稱、它預期收到的 input schema、它包住記憶用的 citation 標籤、它呼叫 skill 的檔案結構、harness 要它「先做個計畫」時它遵循的規劃協定，這些都不是模型的通用能力。它們是 byte-level 的慣例，在 post-training 階段就內化成「某一個特定模型對上某一套特定 harness」的本能。把模型抽離它原本的 harness，你就放棄了一段補不回來的效能。</p>

<p>最直接的例子是工具格式。Cursor 的 harness 團隊在 <a href="https://cursor.com/en-US/blog/continually-improving-agent-harness">Continually improving our agent harness</a> 講得很白 (翻譯):</p>

<blockquote>
  <p>OpenAI 的模型被訓練成用 patch 格式來編輯檔案，Anthropic 的模型則被訓練成用字串取代。兩種模型其實都能用另一種工具，但給它不熟的那種，會多花推理 token、也會產生更多錯誤。所以在我們的 harness 裡，我們給每個模型它訓練時用的那種工具格式。</p>
</blockquote>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:22px 0;">
<div style="flex:1 1 280px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">OpenAI / Codex 系</div>
<div style="font-size:.9em;color:var(--text-secondary);">訓練成生 <code>apply_patch</code> 的 diff 格式。</div>
</div>
<div style="flex:1 1 280px;border:1px solid var(--border);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:6px;">Anthropic / Claude 系</div>
<div style="font-size:.9em;color:var(--text-secondary);">訓練成用 <code>edit_file</code> 的 <code>old_string</code> / <code>new_string</code> 字串取代。</div>
</div>
</div>

<p>這不是「偏好」問題，是 Cursor 在上百萬筆實際運作的 agent 軌跡上量到的、可測量的成本: 兩種模型其實都能用另一種工具，但給它不熟的格式 (讓習慣字串取代的 Claude 用 apply_patch、讓習慣 apply_patch 的 OpenAI 用字串取代)，它就會在格式細節上犯更多錯、多花推理 token。</p>

<blockquote>
  <p>編按: 這兩種格式實際長什麼樣、用起來差在哪，留到文末「<a href="#patch-vs-edit-detail">補充: apply_patch 與 edit_file 實際差在哪</a>」一節再展開，免得打斷主線。</p>
</blockquote>

<p>而工具格式只是最明顯的一個面，同一套邏輯散落在很多地方:</p>

<ul>
  <li><strong>citation 標籤</strong>: Codex 系的模型用完記憶，會在訊息結尾附上一個 <code class="language-plaintext highlighter-rouge">&lt;oai-mem-citation&gt;</code> 標籤，它的 harness 有個 parser 會解析這個標籤、去累加那條記憶的使用次數，當成記憶該不該淘汰的訊號。把這個模型放到一個不認得這個標籤的 harness 上，使用者會直接看到一串原始 XML，而那個淘汰訊號也永遠不會被觸發。</li>
  <li><strong>skill 的隱性契約</strong>: skill 看起來最像能跨 harness 共用的東西 (三家都用 <code class="language-plaintext highlighter-rouge">SKILL.md</code> 加 YAML frontmatter)，但這會誤導。<strong>格式相同，不代表契約相同。</strong> 一個 skill 的 body 裡會用祈使句點名特定工具:「用 TodoWrite 列三個子任務」「用 Agent 開 <code class="language-plaintext highlighter-rouge">subagent_type='Explore'</code> 的子代理」。這些工具名稱、參數形狀，是綁在寫出這個 skill 的那套 harness 上的。同一份 SKILL.md 搬到別家，TodoWrite 不存在、Agent 的形狀不一樣，skill 就靜默地失效、或跑出降級版本。</li>
</ul>

<h3 id="中途切換模型是最清楚的失敗案例">中途切換模型，是最清楚的失敗案例</h3>

<p>把這個綁定看得最清楚的，是「對話進行到一半切換模型」。Cursor 的研究文章把這個失敗面拆得很清楚，三件事同時壞掉:</p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:22px 0;">
<div style="flex:1 1 200px;border:1px solid #cf222e;border-radius:8px;padding:14px 18px;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:6px;">1️⃣ 對話歷史變 OOD</div>
<div style="font-size:.9em;color:var(--text);">前一個模型留下的 tool call (patch 格式、citation 標籤)，對接手的模型是訓練分布外的東西。</div>
</div>
<div style="flex:1 1 200px;border:1px solid #cf222e;border-radius:8px;padding:14px 18px;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:6px;">2️⃣ prompt cache 失效</div>
<div style="font-size:.9em;color:var(--text);">cache 綁 provider 和模型，一切換就是保證 cache miss，第一輪要全價重算整段歷史。</div>
</div>
<div style="flex:1 1 200px;border:1px solid #cf222e;border-radius:8px;padding:14px 18px;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:6px;">3️⃣ 工具形狀變了</div>
<div style="font-size:.9em;color:var(--text);">新模型載入自己的工具集，跟使用者前面用的那套不一樣，它得自己搞懂哪些還能用。</div>
</div>
</div>

<p>Cursor 的結論很務實 (翻譯):「我們通常建議在一整段對話裡固定用同一個模型，除非你有理由要換。」更好的做法是: 不要切換主對話的模型，而是開一個帶不同模型的 subagent，它從一個乾淨的 context 起步，沒有前一個模型留下的 transcript 偏差，也沒有 cache 會失效。這正好呼應第四、五篇反覆出現的做法: 要換模型、換視角，就開子代理，別動主線。</p>

<blockquote>
  <p>編按: 這條「切換模型 = 換 harness + 換工具 + 讓 cache 失效，三件事一起發生」也說明了一件事: 你以為只是在下拉選單換個名字，實際上是把整個契約換掉。</p>
</blockquote>

<p>講到這裡，模型跟 harness 綁得這麼緊，Saurabh 那個問題反而更尖銳了: 既然綁這麼緊，別人家的 harness 到底是怎麼贏過原廠的?</p>

<h2 id="harness這個詞被塞了太多意思">「harness」這個詞，被塞了太多意思</h2>

<p>解開這個矛盾的關鍵，在 Viv 一串<a href="https://x.com/Vtrivedy10/status/2051451869017584112">公開討論</a>底下，一位叫 Gangadhar 的人講得最好: 很多看似衝突的說法之所以衝突，是因為「harness」這個詞被塞了太多意思。一旦把它拆成四層，大部分矛盾就能同時成立。</p>

<div style="display:flex;flex-direction:column;gap:10px;margin:24px 0;">
<div style="border:1px solid var(--border);border-radius:8px;padding:12px 16px;background:var(--bg);display:flex;flex-wrap:wrap;gap:8px;align-items:baseline;">
<div style="font-weight:700;color:var(--text);flex:0 0 120px;">任務分布</div>
<div style="font-size:.9em;color:var(--text-secondary);flex:1 1 220px;">你實際要解的那群任務長什麼樣 (Text-to-SQL、客服、訪談…)。</div>
<div style="font-size:.82em;color:var(--text-secondary);flex:0 0 auto;">← 你要對齊的目標</div>
</div>
<div style="border:1px solid var(--accent);border-radius:8px;padding:12px 16px;background:var(--tag-bg);display:flex;flex-wrap:wrap;gap:8px;align-items:baseline;">
<div style="font-weight:700;color:var(--accent);flex:0 0 120px;">workflow 層</div>
<div style="font-size:.9em;color:var(--text);flex:1 1 220px;">任務怎麼拆、subagent 怎麼調度、routing、塞哪些 skill 和專用工具。</div>
<div style="font-size:.82em;color:var(--accent);font-weight:600;flex:0 0 auto;">← 自建贏在這層</div>
</div>
<div style="border:1px solid var(--border);border-radius:8px;padding:12px 16px;background:var(--surface);display:flex;flex-wrap:wrap;gap:8px;align-items:baseline;">
<div style="font-weight:700;color:var(--text);flex:0 0 120px;">內層 tool loop</div>
<div style="font-size:.9em;color:var(--text-secondary);flex:1 1 220px;">怎麼提供 tool 描述、解析 tool call、把結果送回、context 滿了怎麼辦。</div>
<div style="font-size:.82em;color:var(--text-secondary);flex:0 0 auto;">← 原廠贏 (一起訓練)</div>
</div>
<div style="border:1px solid var(--border);border-radius:8px;padding:12px 16px;background:var(--surface);display:flex;flex-wrap:wrap;gap:8px;align-items:baseline;">
<div style="font-weight:700;color:var(--text);flex:0 0 120px;">模型</div>
<div style="font-size:.9em;color:var(--text-secondary);flex:1 1 220px;">純權重。post-training 有沒有把工具放進迴圈，決定這層的預設傾向。</div>
<div style="font-size:.82em;color:var(--text-secondary);flex:0 0 auto;">← 原廠贏 (一起訓練)</div>
</div>
</div>

<p>拆開之後，矛盾就化解了: <strong>原廠贏在前兩層 (模型加內層 tool loop)，因為它們是一起訓練出來的; 自建或開放 harness 贏在 workflow 層，因為它能貼著一個特定的任務分布去優化。</strong> Model-Harness-Fit 講的是前兩層的契合，「自建打贏原廠」講的是後兩層的優化，兩者根本不在比同一件事，所以可以同時為真。Viv 之所以一度覺得這幾種說法互相矛盾，就是因為把這四層全用「harness」一個詞概括，自然會得出「不可能全部為真」的結論。</p>

<p>這個拆法還順手給了一條實作守則: <strong>內層留給原廠，workflow 層留給自己。</strong> 當你自建 agent、選用某個模型時，別去跟它訓練時的工具格式作對 (該給 <code class="language-plaintext highlighter-rouge">apply_patch</code> 就給，該給字串取代就給)，那是原廠用 post-training 訓練出來的契合，你動它只會掉分; 你的力氣要花在外層，針對你的任務分布去設計編排、context、skill。</p>

<h2 id="自建為什麼打得贏原廠-對齊你的任務分布">自建為什麼打得贏原廠: 對齊你的任務分布</h2>

<p>那「贏在 workflow 層」具體是贏在哪? Viv 給了一個三元組來回答: <strong>Model-Harness-Task fit</strong> (<a href="https://x.com/Vtrivedy10/status/2059712077925658717">出處</a>)。</p>

<p>他把 fit 拆成兩種。一種是大家常談的 Model-Harness fit: RL post-training 時把 harness 放進迴圈，模型自然跟特定工具形狀、提示風格產生契合 (就是前面講的那層)。另一種比較少人談、也比較少人實驗的，是 <strong>Harness-Task fit</strong>: 針對任務本身去調 harness，例如領域專屬的提示 (像驗證類的 coding 任務)，或刻意省略掉跟當前任務無關、只會造成混淆的 context。</p>

<p>Viv 點出一個很精準的觀察: Claude Code 這類原廠 harness 的 system prompt 裡塞了大量指令，因為它被迫服務一個非常通用的使用者，這個人基本上可能開口要任何東西。但如果你只針對一個窄任務，用一組高度聚焦、真正相關的 context 和工具，把其他雜訊全部拿掉，好處是很大的。他的原話 (翻譯):</p>

<blockquote>
  <p>harness 裡的每一個元件，存在的目的都是為了誘發模型的某種行為。如果這些元件針對任務調校過，模型就受益; 如果是噪音和好內容混在一起，模型可能還行，但也可能被搞混。</p>
</blockquote>

<p>這就是為什麼頂尖的垂直領域 AI 團隊，都替自家 agent 打造非常客製的 harness 和 eval。原廠是對著「通用分布」訓練的，你是對著「你使用者的真實任務分布」優化的，在那個窄分布上，你本來就該贏。</p>

<p>而對齊任務分布的方法，Viv 給的因果鏈很清楚:</p>

<div style="display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:center;margin:22px 0;font-weight:600;">
<span style="background:var(--tag-bg);border:1px solid var(--accent);color:var(--accent);border-radius:20px;padding:6px 16px;">Eval</span>
<span style="color:var(--text-secondary);">形塑 →</span>
<span style="background:var(--surface);border:1px solid var(--border);color:var(--text);border-radius:20px;padding:6px 16px;">Harness</span>
<span style="color:var(--text-secondary);">貼合 →</span>
<span style="background:var(--surface);border:1px solid var(--border);color:var(--text);border-radius:20px;padding:6px 16px;">Task</span>
</div>

<p>先把能代表你使用者的 eval 蒐集好，eval 形塑 harness,harness 貼合 task。這條鏈正好接回第七篇的結論 (harness 自我改進的核心，是那個能自動打分的 eval)，也跟 ihower 一直在講的 eval-driven 方法論是同一回事。</p>

<p>這不是紙上談兵，兩個實作可以佐證「為每個模型分別客製」真的有效:</p>

<ul>
  <li><strong>Cursor</strong>: 所有 harness 抽象都做成不綁定特定模型 (model-agnostic)，但每個模型都能深度客製: 給 OpenAI 模型 patch 格式、給 Anthropic 模型字串取代，連 prompt 都按 provider、甚至按模型版本分開寫 (他們觀察到 OpenAI 模型對指令比較字面、精確，Claude 比較能容忍模糊的指令)。他們的描述是:「持續性地堆疊一個個小優化」，而不是指望一步到位。</li>
  <li><strong>LangChain Deep Agents</strong>: 加了針對不同模型的設定檔 (<a href="https://www.langchain.com/blog/tuning-deep-agents-different-models">profiles</a>)，同一個 agent 換模型時自動套用對應的 prompt、工具、middleware，呼叫端完全不用改。他們在 tau2-bench 一個較難的子集上量到，光是套上對的 profile,GPT-5.3 Codex 從 33% 升到 53%,Opus 4.7 從 43% 升到 53%。改的不是模型，是 harness 那層按模型分流。</li>
</ul>

<p>所以「自建能不能打贏原廠」這題的答案是: 能，但不是靠複製原廠那套通用 harness，而是靠對齊你自己的任務分布。前提是你得先有能代表使用者的 eval，沒有 eval，你連「贏了沒」都量不出來，更別說往哪裡調。</p>

<h2 id="harness-會過期">harness 會過期</h2>

<p>講完「綁模型」，再看一體兩面的另一面: harness 會過期。</p>

<p>Anthropic 在 <a href="https://claude.com/blog/harnessing-claudes-intelligence">Harnessing Claude’s intelligence</a> 裡，把這件事講成一個該反覆問自己的問題:「有什麼是我可以停止做的?」(What can I stop doing?) 他們的論點是:</p>

<blockquote>
  <p>harness 裡的每個元件，都編碼了一項「模型自己做不到什麼」的假設。當模型在某件事上變強，那個假設就過時了，那個元件也該被移除。</p>
</blockquote>

<h3 id="案例一-context-reset三個世代拆掉三層">案例一: context reset，三個世代拆掉三層</h3>

<p>Anthropic 自己給的例子很具體: 做過一個跑長任務的 agent，在 Sonnet 4.5 上，模型一感覺到 context 快滿，就開始草草收尾、推說任務太大 (他們叫它 context anxiety，上下文焦慮)。為了補這個，他們在 harness 裡加了「定時清空 context、用結構化交接檔把狀態帶過去」的機制。結果 Opus 4.5 一出，reset 就變得多餘; Opus 4.6 再一出，連更上層的任務拆解 (sprint decomposition) 都整個拿掉，效果反而更好。三個模型世代，拆掉三層 harness。Portkey 的<a href="https://portkey.ai/blog/the-harness-tax/">整理</a>下了一句很到位的註腳: 這些元件「一月還在承重，三月就成了死碼」。</p>

<h3 id="案例二-todowrite-退役">案例二: TodoWrite 退役</h3>

<p>更能說明「補強措施怎麼從幫手變成累贅」的，是 Claude Code 的待辦清單。Claude Code 團隊的工程師在 <a href="https://x.com/trq212/status/2027463795355095314">Lessons from Building Claude Code</a> 裡完整講了這條演化:</p>

<ul>
  <li><strong>一開始</strong>: Claude Code 剛推出時，模型撐不住長任務，需要一份待辦清單讓它不偏離任務，於是給了 <code class="language-plaintext highlighter-rouge">TodoWrite</code> 工具: 開工先寫一份 todo，做完一項勾掉一項。</li>
  <li><strong>還是會忘</strong>: 光有清單不夠，模型常常忘記自己要幹嘛，於是 harness 每 5 輪插一次 system reminder，提醒它原本的目標。</li>
  <li><strong>模型變強之後，清單反而綁手綁腳</strong>: 新一代模型不只不需要被提醒，還會被那份提醒限制住: 一直被提醒這份 todo list，反而讓模型以為自己必須照著清單做，而不是動態修改它。再加上 Opus 4.5 變得很會用子代理，但多個子代理要怎麼協作在同一份 todo list 上?</li>
  <li><strong>於是 TodoWrite 退役，換成新的 Tasks 系統</strong>: 這裡的 Tasks 是一組新工具 (<code class="language-plaintext highlighter-rouge">TaskCreate</code>、<code class="language-plaintext highlighter-rouge">TaskUpdate</code>、<code class="language-plaintext highlighter-rouge">TaskList</code> 等)，不是那個會開子代理、後來改名叫 <code class="language-plaintext highlighter-rouge">Agent</code> 的舊 Task 工具，兩者只是名字像。Todos 和 Tasks 管的事根本不同: Todos 是「讓單一模型在一個 session 內不偏離任務」，Tasks 是「讓跨 session、跨多個子代理的長專案能協調起來」。</li>
</ul>

<p>兩者具體差在三個地方，而且最關鍵的第一點 (持久化) 常被忽略:</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:24px 0;">
<div style="flex:1 1 280px;border:1px solid var(--border);border-radius:8px;padding:16px 20px;background:var(--surface);">
<div style="font-weight:700;color:var(--text);margin-bottom:8px;">TodoWrite (舊)</div>
<div style="font-size:.9em;color:var(--text-secondary);line-height:1.75;"><strong>存在 context 裡</strong>,session 結束或 compaction 一來就消失。扁平清單，沒有依賴關係。單一模型私有，子代理看不到。</div>
</div>
<div style="flex:1 1 280px;border:1px solid var(--accent);border-radius:8px;padding:16px 20px;background:var(--tag-bg);">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">Tasks (新)</div>
<div style="font-size:.9em;color:var(--text);line-height:1.75;"><strong>寫到磁碟 <code>~/.claude/tasks/</code></strong>，跨 session、跨 compaction 都還在。任務間有依賴 (A 擋著 B,A 完成 B 自動解鎖)。多個 session 和子代理共用同一份，更新即時廣播。</div>
</div>
</div>

<p>把這三點接回前面幾篇就更清楚: 持久化對應第六篇外層 loop 裡 Ralph 那種「拿外部 plan 檔手動追進度」的做法，Tasks 等於把它變成內建; 依賴與跨代理共享，則對應 Opus 4.5 之後模型很會開子代理、但多個子代理沒辦法協作在同一份扁平 todo list 上的那個缺口。</p>

<p>那篇收尾的一句話正是這一篇的主軸 (翻譯):「隨著模型能力提升，你的模型曾經需要的工具，現在可能正在限制它。不斷回頭檢視『到底需要哪些工具』的舊假設，很重要。」</p>

<p>這裡有個比「變成死重」更精準的觀察，Shane Deconinck 把它叫 scaffolding trap (腳手架陷阱): 當模型變強，腳手架不只是變成死重而已，它會反過來跟模型新獲得的能力衝突。你當初為了補一個短處寫的 workaround，現在正擋著模型去用它學會的更好做法。TodoWrite 就是教科書級的例子: 那些每 5 輪一次的提醒，本來是怕模型忘記，後來卻變成逼模型死守一份它其實該動態修改的清單。</p>

<blockquote>
  <p>編按: 同一篇文章還有一個例子。Claude Code 早期用 RAG 向量資料庫給模型 context，後來發現模型夠強了，直接給它一個 <code class="language-plaintext highlighter-rouge">Grep</code> 工具讓它自己搜、自己建 context，效果更好也更不易壞。補強措施退場，是個反覆出現的模式，不是單一個案。</p>
</blockquote>

<h3 id="不是變少是該做的會變">不是變少，是該做的會變</h3>

<p>不過這裡要補一個容易誤會的地方:「模型變強 → 拆掉 harness」不等於「harness 會一路縮到零」。Addy Osmani 講得好: 隨著模型變強，值得做的 harness 不會變少，只是該做哪些會跟著變。Opus 4.6 淘汰掉 context anxiety 那一類腳手架，但能做到的上限也跟著提高了: 原本做不到的任務 (跨好幾天的記憶、協調三個專職 agent、替生成的 UI 設計品質評估器) 現在變得可行，而它們有自己的新失敗模式，需要新的腳手架去補。拆掉舊的、補上新的，假設在變，腳手架也跟著變。</p>

<h3 id="沒有一體適用的-harness">沒有一體適用的 harness</h3>

<p>同樣的道理不只適用在時間軸上（同一個模型越變越強），在同一個時間點、不同大小的模型之間也成立：harness 該做得多精巧，跟你用多強的模型有關，沒有一套適合所有模型。小模型一次做不對、也撐不住長任務，更需要靠外層的 harness 去補：把工作流拆成多階段、分給不同模型、一段段把關。夠強的大模型常常一次就做完，還能全程待在同一份 context 上，prompt cache 一路有效。</p>

<p>harness 的精巧程度和模型能力綁在一起：小模型得配上一套比較複雜、設計精巧的 harness，表現才會好；同一套複雜 harness 換上大模型卻多半是浪費，多出來的那幾層都是多餘。所以 harness 的架構選擇，沒辦法脫離模型來談。</p>

<h3 id="廠商會主動把-harness-訓練進模型">廠商會主動把 harness 訓練進模型</h3>

<p>為什麼這些補強措施退場得越來越快? 因為廠商會主動把 harness 的做法訓練進模型。這就是開頭那個共演化迴圈運轉的方向: 一個 harness primitive 先上線，幾個月後它出現在幾百萬筆 agent 軌跡裡，這些軌跡變成下一代模型的訓練資料，再過一代，這個 primitive 就被訓練進模型的本能，harness 那層就能拿掉。幾個看得到的例子:</p>

<ul>
  <li>OpenAI 把 <code class="language-plaintext highlighter-rouge">apply_patch</code> 這個編輯格式直接後訓練進模型，模型本身就會生成這個格式 (這跟前面「綁模型」那段是同一件事的兩面)。</li>
  <li>第五篇提過一點留到這篇講: Fable 5 這代模型「擅長在迴圈中自我修正」已經寫進官方賣點。把這句話翻成 harness 的語言就是: 第五篇那些「逼模型寫完要驗證」的外層腳手架 (Stop hook 退件、催它再檢查一遍的 prompt)，有一部分正在被模型內建的自我修正取代。Anthropic 給的另一個更直白的數據是: 早期 Claude 連玩寶可夢 FireRed 都要一整套含地圖、導航的輔助 harness,Fable 5 只靠純視覺、極簡 harness 就破關了。模型越強，需要的腳手架越少。</li>
</ul>

<p>OpenAI 那邊也一樣，prompt 和 harness 會跟著一代代模型不斷調整 (Codex CLI 是開源的，這些演進在它的 commit 歷史裡都看得到)。所以「拆 harness、改做法」不是哪一家的專利，是整個產業共通的動作。</p>

<p>這正好把第二篇那個 ratchet (棘輪) 原則補完整。第二篇說，你只在看到真實失敗時才加一條約束; 但棘輪是雙向的，你也只在某天模型強到讓那條約束變多餘時，才把它拆掉。一份好的 AGENTS.md，每一行都該追得回一個具體出過的包; 而當那個包再也不會發生，那一行就該刪掉。加約束和拆約束，是同一套紀律的兩個方向。</p>

<p>第一篇也先提過一點: filesystem 和 sandbox 這些能力「有效，但不代表永遠」。放在這條時間軸上就清楚了: 今天每一個看起來理所當然的 harness 元件，都是某一代模型某個短處的對應物。哪些會被吸收回模型、哪些會留下來、價值持續累積，得看模型往哪裡變強，沒有哪個元件保證能一直留著。</p>

<h3 id="每次模型升級問一次有什麼可以停止做">每次模型升級，問一次「有什麼可以停止做?」</h3>

<p>前面這些拆 harness 的案例，可以歸結成一條給自建 agent 的例行檢查: 每次模型大升級，都重新審一遍 harness，問一次「有什麼是我現在可以停止做的?」把已經派不上用場的腳手架拆掉。具體可以對著手上的 harness 逐項問:</p>

<ul>
  <li>這條 prompt 約束，還在提高成功率嗎，還是只是慣性留著?</li>
  <li>這個強制的規劃步驟 (開工先寫 todo、先列計畫)，還在防止模型偏離任務，還是只是拖慢它?</li>
  <li>這個 context reset 或交接，還在避免錯誤，還是已經在打斷模型的長程推理?</li>
  <li>這個 subagent，是真的提高了品質，還是只多了合併成本?</li>
  <li>這個工具格式，還是這個模型最熟的那一種嗎?</li>
</ul>

<p>而且這不限於大版本: Anthropic 在 Opus 4.7 的發布說明就提醒，4.7 對指令的遵循變嚴格了，「為早期模型寫的 prompt 可能產生非預期結果，使用者該重新調校自己的 prompt 和 harness」。連小版本升級都可能要回頭重調。這件事不做，你的 harness 會越積越臃腫，反而拖累一個已經更強的模型。</p>

<h2 id="幾個不同看法">幾個不同看法</h2>

<p>Model-Harness-Fit 這套說法聽起來很順，但也有一些不同看法，而且講這些的就包括最積極推廣這套框架的 Viv 自己。把不同角度都擺出來才公道:</p>

<ul>
  <li><strong>聰明的模型本來就該能輕鬆切換工具格式。</strong> Viv 自己講過 (翻譯):「一個真正聰明的模型，要在不同的 patch 方法之間切換應該毫無困難; 是『把 harness 放進迴圈一起訓練』這件事，造成了這種過度擬合 (overfitting)。」換句話說，模型對自家工具格式的偏好，某種程度上是被訓練慣出來的，不是一種本質的好。</li>
  <li><strong>長期的解法，也許是用更多樣的 harness 去訓練，讓模型泛化。</strong> 如果偏好來自過度擬合，那對著更多種 harness 訓練、讓模型在任何工具格式上都一樣行，就是更健康的方向。</li>
  <li><strong>成對推出 = 用可攜性換排行榜。</strong> 廠商把模型和 harness 當成一個產品一起出，確實能在 benchmark 上拿到最好的數字，但代價是模型變得更不可替換。這筆交易划不划算，要看客戶在不在乎可攜性，而現在客戶大多只在乎排行榜。</li>
</ul>

<p>另外，「harness 會過期」也有它的反面: 不是所有 primitive 都會被吸收回模型，有些會留下來，而且價值會持續累積。哪些被吸收、哪些留下，目前沒有人能事先說準，只能讀軌跡、看數據。</p>

<p>把這些不同看法擺出來不是要否定前面，而是想說: Model-Harness-Fit 是個有用的工程框架，但它描述的是「現在」這個時間點的狀態，不是一條永恆定律。它本身也會隨著時間改變。</p>

<h2 id="會過期的與不會過期的">會過期的，與不會過期的</h2>

<p>把整個系列從頭收起來。這七篇講的是一套讓 agent 做得對、做得完的工程方法，但每一個做法、每一個元件，都會隨剛剛那條時間軸改變。那到底有沒有什麼是穿越模型世代、不會過期的?</p>

<p>有，而且這正是整個系列真正的主軸。把東西分成兩堆:</p>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:24px 0;">
<div style="flex:1 1 280px;border:1px solid #cf222e;border-radius:8px;padding:18px 22px;background:#ffebe9;">
<div style="font-weight:700;color:#cf222e;margin-bottom:8px;">⏳ 會過期</div>
<div style="font-size:1.02em;font-weight:600;color:var(--text);margin-bottom:6px;">各種 harness 做法與補強措施</div>
<div style="font-size:.9em;color:var(--text-secondary);">對應 Bitter Lesson: 能隨算力擴大的通用方法，終究勝過手工規則。很多會隨更強的模型逐步不再需要。</div>
</div>
<div style="flex:1 1 280px;border:1px solid #1a7f37;border-radius:8px;padding:18px 22px;background:#dafbe1;">
<div style="font-weight:700;color:#1a7f37;margin-bottom:8px;">♾️ 不會過期</div>
<div style="font-size:1.02em;font-weight:600;color:var(--text);margin-bottom:6px;">定義「什麼叫做好」+ 驗證它做到了</div>
<div style="font-size:.9em;color:var(--text-secondary);">這就是 Eval 與 Judge。穿越模型世代的基本功。</div>
</div>
</div>

<p>⏳ 會過期的，是各種具體的 harness 做法。這對應的是 <a href="https://blog.aihao.tw/2026/02/17/bitter-lesson-agent-harness/">Bitter Lesson</a>: 能隨算力擴大的通用方法，終究會勝過手工堆砌的規則。但 Bitter Lesson 在這裡不是叫你「別設計 harness」，而是「別把今天這個模型的限制，寫成永遠不動的架構」。工程上你還是需要 harness，只是它該被設計得更精簡、更容易替換、也更容易量出到底有沒有用，這樣模型一變強，你才拆得掉。你今天精心寫的工具、清單、reset、催驗證的 prompt，很多會隨著更強的模型逐步不再需要。用 nicbstme 那句話收尾最貼切: 想待在前沿，你得在每次新模型發布時，刪掉你大半的程式碼。</p>

<p>♾️ 不會過期的，是無論模型變多強，你永遠得回答的兩個問題: 定義「什麼叫做好」、以及驗證它真的做到了。這就是 Eval 和 Judge。</p>

<p>這兩堆的分界，其實貫穿了整個系列。第三篇工具層的確定性檢查和語意 judge、第五篇單輪結束的停止條件和獨立 grader、第六篇外層 loop 的完成判定、第七篇自我改進的核心，講的全是同一件事的不同粒度: 你得有東西能判斷「對不對、完了沒」。</p>

<p>而這件事為什麼不會過期? 因為就算模型強到能完美自我驗證 (第五篇講過，OpenAI 正把驗證往模型層訓練)，它驗證的，仍然是一個「由你定義的目標」。模型可以幫你檢查，但「什麼算達標」這個標準，是你給的。一個會自我修正的模型，也得有個目標可以對齊。所以定義「好」這件事，是那個無論模型多強，都得由你自己來的核心。</p>

<p>這也是為什麼整個系列從第一篇到現在，反覆把工程師的角色描述成同一個轉變: 你被移出的，是那些逐步操作的迴圈 (瓦特調速器那個比喻裡，顧著轉閥門的那一圈); 你留下來做的，是設計回饋、定義目標、把關「什麼叫做好」。模型會一代代把腳手架吸收掉，但它吸收不了那個替它定義成功、並驗證成功的角色。</p>

<p>harness 會過期; 替 harness 定義「對不對、完了沒」的那套 eval 與 judge，不會。這七篇談了這麼多，最後就收在這一句上。</p>

<h2 id="patch-vs-edit-detail">補充: apply_patch 與 edit_file 實際差在哪</h2>

<p>前面「綁模型」那節提到，OpenAI 系的模型被訓練成生 <code class="language-plaintext highlighter-rouge">apply_patch</code>、Anthropic 系被訓練成用 <code class="language-plaintext highlighter-rouge">edit_file</code> 的字串取代。這兩種編輯工具實際長什麼樣、用起來差在哪，這裡補充一下。</p>

<p>同樣是「把 <code class="language-plaintext highlighter-rouge">return None</code> 改成 <code class="language-plaintext highlighter-rouge">return result</code>」這一個改動，兩種工具的呼叫長這樣:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code># apply_patch (Codex / OpenAI)
*** Begin Patch
*** Update File: src/handler.py
@@ def handle(req):
-    return None
+    return result
*** End Patch
</code></pre></div></div>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code># edit_file / str_replace (Claude / Anthropic)
old_string: "    return None"
new_string: "    return result"
</code></pre></div></div>

<p>apply_patch 是一份 diff: 模型用 <code class="language-plaintext highlighter-rouge">@@</code> 標出附近的程式碼當定位的參考點，再用 <code class="language-plaintext highlighter-rouge">-</code> / <code class="language-plaintext highlighter-rouge">+</code> 標出要刪哪行、加哪行，一份 patch 裡可以同時改好幾個檔案、好幾個位置。str_replace 則是「找字串、換字串」: 模型給一段必須逐字元完全相符、而且在檔案裡唯一的 <code class="language-plaintext highlighter-rouge">old_string</code>，加上要換成的 <code class="language-plaintext highlighter-rouge">new_string</code>，一次呼叫改一個地方。實際用起來，差別落在三處:</p>

<ul>
  <li><strong>批次粒度</strong>: 一次大改動，apply_patch 可以一份 patch 解決多檔多處; str_replace 通常一次改一個點，多處就要呼叫好幾次。</li>
  <li><strong>怎麼指出位置</strong>: apply_patch 靠上下文行定位 (像讀 git diff); str_replace 要把目標那段原文一字不差地貼出來，還得保證它在檔案裡只出現一次。</li>
  <li><strong>失敗的方式</strong>: apply_patch 的上下文對不上 (檔案漂移、縮排差一格) 就套不上去; str_replace 的 old_string 差一個空白就找不到，在檔案裡出現兩次就不知道該改哪個。</li>
</ul>

<p>但放回這篇的主題: 重點不是哪種比較好，而是模型被訓練成習慣哪一種。把 apply_patch 給習慣字串取代的 Claude 模型，它容易把 diff 的上下文行抓錯、hunk 格式生壞; 把 str_replace 給習慣 apply_patch 的 OpenAI 模型，它會在「逐字複製、保證唯一」這件事上犯更多錯。兩種模型其實都能用另一種工具，只是給它不熟的那種，它會多花推理 token、也錯得更多。這就是 Model-Harness-Fit 最具體的一個例子。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="LLM" /><category term="Eval" /><summary type="html"><![CDATA[📚 給 Agent 開發者的駕馭工程 (全 9 篇) 1. 基礎: Deep Agent 的六項內建能力 2. 核心: Agent 要的是回饋迴路，不是完美提示 3. 回饋時機一: 工具回傳值，是寫給 agent 的回饋 4. 回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent 5. 回饋時機三: 單輪結束的驗收，Goal 與 Outcomes 6. 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron 7. 進階: 自我改進 Harness, Meta-Harness 與爬坡 8. 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson (本篇) 9. 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建? 🎞️ 搭配演講投影片: 給 Agent 開發者的 Harness+Loop Engineering]]></summary></entry><entry><title type="html">給 Agent 開發者的駕馭工程 (9): 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</title><link href="https://blog.aihao.tw/2026/06/26/harness-engineering-9-agent-frameworks/" rel="alternate" type="text/html" title="給 Agent 開發者的駕馭工程 (9): 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/26/harness-engineering-9-agent-frameworks</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/26/harness-engineering-9-agent-frameworks/"><![CDATA[<div class="series-toc" style="border:1px solid var(--border);border-radius:8px;background:var(--surface);padding:14px 18px;margin:24px 0;font-size:.92em;line-height:1.85;">
<div style="font-weight:700;color:var(--accent);margin-bottom:8px;">📚 給 Agent 開發者的駕馭工程 (全 9 篇)</div>
<div>1. <a href="/2026/06/26/harness-engineering-1-deep-agent-capabilities/">基礎: Deep Agent 的六項內建能力</a></div>
<div>2. <a href="/2026/06/26/harness-engineering-2-what-is-harness-engineering/">核心: Agent 要的是回饋迴路，不是完美提示</a></div>
<div>3. <a href="/2026/06/26/harness-engineering-3-tool-execution-feedback/">回饋時機一: 工具回傳值，是寫給 agent 的回饋</a></div>
<div>4. <a href="/2026/06/26/harness-engineering-4-mid-run-injection/">回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent</a></div>
<div>5. <a href="/2026/06/26/harness-engineering-5-goal-and-outcomes/">回饋時機三: 單輪結束的驗收，Goal 與 Outcomes</a></div>
<div>6. <a href="/2026/06/26/harness-engineering-6-outer-loop/">回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron</a></div>
<div>7. <a href="/2026/06/26/harness-engineering-7-self-improving/">進階: 自我改進 Harness, Meta-Harness 與爬坡</a></div>
<div>8. <a href="/2026/06/26/harness-engineering-8-model-harness-fit/">收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson</a></div>
<div>9. <strong style="color:var(--text);">自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建?</strong> <span style="color:var(--text-secondary);">(本篇)</span></div>
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">🎞️ 搭配演講投影片: <a href="https://ihower.tw/presentation/harness.html">給 Agent 開發者的 Harness+Loop Engineering</a></div>
</div>

<p>前面八篇談的都是 harness engineering 的觀念: 回饋迴路、工具層的檢查、使用者 steering、單輪驗收、外層 loop、自我改進、model-harness-fit。觀念講完，真要自己動手開發一個 agent 時，會先碰到一個很實際的問題: 該從哪個開發框架開始?</p>

<p>選擇一大堆: OpenAI 的 Codex SDK 和 Agents SDK、Anthropic 的 Claude Agent SDK、GitHub 的 Copilot SDK、LangChain 的 LangGraph 跟 deepagents、Google 的 ADK、Pydantic AI、Vercel 的 AI SDK、AWS 的 Strands Agents、微軟的 Agent Framework、Earendil 的 Pi。每一家都說自己是做 agent 的好工具，而且還不只這些。這篇從裡面挑了小編覺得比較成熟、有實際產品或社群在用的幾個框架，用系列第 1 篇的「六項能力」當對照標準，分成兩條路線比較，最後給一點選型建議。</p>

<p>照例提醒系列定位: 這裡談的是「自己開發一個 agent (可能是 Text-to-SQL、知識庫 RAG、訪談 agent，未必跟寫程式有關)」要用哪個框架當基礎，不是「怎麼把 Claude Code、Codex 這類 coding agent 用得更順」，也不是叫你挑一個拿來做軟體開發的 coding agent。下面有些框架 (像 Codex SDK、Claude Agent SDK、GitHub Copilot SDK) 雖然源自 coding agent，但這裡是拿它們當基礎去打造你自己的 agent，用途可以跟寫程式完全無關。</p>

<h2 id="兩條開發路線">兩條開發路線</h2>

<p>先快速複習第 1 篇那六項 Deep Agent 能力: ① Plan &amp; Todos、② Filesystem &amp; Bash、③ Sub-Agent、④ Memory、⑤ Skills、⑥ 更多工具 (MCP、Browser、Computer Use)。小編就拿這個分類當對照標準，看每個框架幫你內建了幾項、哪些要自己接。用這個角度看，市面上的框架大致分成兩條路線:</p>

<ul>
  <li>一條是從<strong>全套 Deep Agent</strong> 開始: 框架直接給你一個跑得動的 agent，六項能力大多已經接好，你拿來改、加工具、改 system prompt 就能用。</li>
  <li>另一條是<strong>從基礎構建</strong>: 框架給的是 agent、工具、handoff、workflow、session 這些基礎元件，agent 迴圈跟那六項能力大多要你自己決定要不要加、怎麼加。</li>
</ul>

<h2 id="路線一-從全套-deep-agent-開始">路線一: 從全套 Deep Agent 開始</h2>

<p>這條路線的共同點: 一上手就是一個會規劃、會讀寫檔案、會開子代理人的 agent，六項能力大多已經內建。你的工作是在它之上做客製，而不是從頭組裝。</p>

<p>還有一點容易被忽略但很重要: 除了那六項能力 (那些主要是工具)，全套 Deep Agent 還附帶一份原廠調校過的 system / developer prompt 跟 agent loop，裡面已經寫好大量預設行為: 怎麼規劃、什麼時候用哪個工具、出錯了怎麼自我修正、怎麼維護待辦清單。第 1 篇編按引用過 LangChain 對 Deep Agent 的四特徵定義，排第一個的就是「詳細的 system prompt」。這套預設行為，是 deep agent 開箱就比較會做事的一個重要原因。</p>

<p><strong><a href="https://developers.openai.com/codex/sdk/">Codex SDK</a> &amp; <a href="https://developers.openai.com/codex/app-server">Codex App Server</a> (OpenAI)</strong>: 核心是 Rust 寫的 Codex CLI，完全開源 (Apache 2.0)，連 agent loop 本身都在開源 repo 裡，可以讀、可以改。對外有兩種嵌入方式，差別在於你要用什麼語言串接: 一種是 TypeScript / Python SDK 把 CLI 包成程式介面 (用這個方式你得寫 TS 或 Python)，另一種是 <code class="language-plaintext highlighter-rouge">app-server</code> 用 JSON-RPC 雙向溝通 (設計上刻意參考 MCP)，因為是協議介面，你的程式用什麼語言都能接。六項能力幾乎都有，sandbox 還是作業系統核心層級的隔離。一個實務上的差別: Codex 可以用 ChatGPT 訂閱帳號登入，不一定要用 API key，這點後面選型會再提。</p>

<p><strong><a href="https://github.com/github/copilot-sdk">GitHub Copilot SDK</a> (Node / Python / Go / .NET / Java / Rust)</strong>: 它開放給你用程式呼叫的，是 <a href="https://github.com/features/copilot/cli">Copilot CLI</a> 背後同一個、已在生產環境驗證過的 agent runtime。架構跟 Codex 很像: Node / Python / .NET 會自動把 Copilot CLI 當依賴一起裝進來，其餘語言的 SDK 則都透過 JSON-RPC 跟 Copilot CLI 的 server mode 溝通，所以 Go、Java、Rust 也能接，第一方支援的語言是這條路線裡最多的。六項能力大多內建: 規劃、檔案編輯與第一方工具 (等同 <code class="language-plaintext highlighter-rouge">--allow-all</code> 那組)、custom agents、skills、MCP 都有。核心則比較像 Claude Agent SDK: repo 裡各語言的外層套件是 MIT 開源，但真正的 agent loop 跑在那個一起裝進來的 Copilot CLI 執行檔裡，而它是 GitHub 的閉源產品，你能用它調好的迴圈，但看不到也改不了內部。</p>

<p><strong><a href="https://code.claude.com/docs/en/agent-sdk/overview">Claude Agent SDK</a> (Anthropic, Python / TypeScript)</strong>: 前身是 Claude Code SDK。這裡有一個要特別留意的點: SDK 的外層包裝 (Python / TypeScript) 是 MIT 開源的，但真正的 agent loop 跟 harness 核心，是跑在一個閉源的 Claude Code 原生執行檔裡，SDK 只是去啟動那個執行檔、用 stdio 跟它溝通。換句話說，你能用 Anthropic 那套調好的 agent loop，但看不到、也改不了它的內部。再加上它受 Anthropic 商業條款規範、第三方產品不允許用 claude.ai 帳號登入 (只能用 API key)，可控性是這條路線裡最受限的。六項能力一樣大多內建。</p>

<p><strong><a href="https://github.com/langchain-ai/deepagents">LangChain deepagents</a> (Python / TypeScript)</strong>: MIT 開源，建在 LangGraph 之上，等於把 Claude Code 那套 deep agent 的做法重新實作成一個開源套件。六項能力都有對應工具，不過預設的檔案系統是虛擬的 (記在記憶體裡、不會跨 session 留存)，bash / shell 要自己開啟。最大的好處是每一部分 (檔案系統、sandbox、記憶) 都是可替換的 backend，要改不用 fork 整包。</p>

<p>這條路線內部其實也有差異: 前面四個預設就把六項能力跟大量預設行為都準備好，但也有走相反方向、把核心刻意做到最精簡的。</p>

<p><strong><a href="https://pi.dev/">Pi</a> (Earendil, TypeScript)</strong>: 這條路線裡的極簡派。它跟前面幾個一樣是 coding agent 出身，開箱就有一個能跑的 agent、TUI，以及一份預設的 system prompt。差別在預設啟用的東西很少: 只留 read / write / edit / bash 四個工具、system prompt 刻意做到最精簡 (不到 1000 token，有時約 200)。但 plan mode、子代理 (附 planner / reviewer / scout / worker)、todo、權限、sandbox 這些，官方都用範例 extension 提供，要哪個自己裝上就好 (MCP 比較例外，repo 裡沒有，得自己做或裝第三方)。背後假設是: 用 RL 訓練過的前沿模型，本來就不太需要太多預設行為，核心保持最精簡、能力按需要再加上去。MIT 全開源，連 agent loop 都讀得到、改得動，可控性是這條路線裡最高的。Pi 會受到注意，很大一部分是因為 <a href="https://github.com/openclaw/openclaw">OpenClaw</a> 就建在這四個 Pi 套件之上: 一個能在 WhatsApp、Telegram、Discord、Slack、iMessage 等多種通訊軟體上跑、共用記憶跟持久化 session 的個人 AI 助理。OpenClaw 在 GitHub 上累積了數十萬 star，大家回頭才發現底下那層 harness 原來這麼精簡。</p>

<blockquote>
  <p>編按: 這條路線還有一個重要差別值得補充: 核心到底開不開源。三者其實差很多。Codex 連 agent loop 都放在開源 repo 裡 (Apache 2.0)，可讀可改; Claude Agent SDK 跟 GitHub Copilot SDK 則是「對外的各語言套件開源、真正的核心閉源」: 你能呼叫它調好的 agent loop，但看不到、也改不了。Claude 的核心是閉源的 Claude Code 原生執行檔; Copilot 的核心是 Copilot CLI，它雖然有一個 <a href="https://github.com/github/copilot-cli">github/copilot-cli</a> 的 repo，但點進去會發現裡面只有安裝腳本 (install.sh)、README、changelog 和一份授權檔，沒有任何實作原始碼，而那份授權還是專有商業授權、明文禁止修改。換句話說那個 repo 是用來發佈安裝、收 issue 的，不是放原始碼的地方。如果你要做的是對外 production 服務、需要看得到原始碼才好 debug，這個差別要先想清楚。</p>
</blockquote>

<h2 id="路線二-從基礎構建">路線二: 從基礎構建</h2>

<p>這條路線的框架，給的是控制流、編排、型別、持久化這些基礎元件。六項 Deep Agent 能力不會預設都幫你啟用，要自己設定、添加。而且連那份預設行為很強的 system prompt 也要自己寫: 框架只給你一個近乎空白的起點，agent 該怎麼規劃、怎麼自我修正，都得你自己設計。換來的是完全的可控性。這條路線內部其實差異不小，與其硬分風格，不如照框架有多 opinionated (幫你預設了多少、規定了多少) 來排序: 排在前面的比較 opinionated，內建跟預設都比較多; 排在後面的比較低階，更多事情要你自己決定。順序大致如下 (每家支援的程式語言一併標在後面):</p>

<ul>
  <li><strong><a href="https://strandsagents.com/">Strands Agents</a> (AWS, Python / TypeScript)</strong>: Apache 2.0 開源，主打 model-driven 的迴圈: 不要開發者去畫複雜的流程圖，而是把規劃、串接、呼叫工具、反思交給模型自己在迴圈裡決定。這個取向讓 Strands 在這條路線裡相當 opinionated、也最接近 deep agent: 直接內建了讀寫檔案、shell、code interpreter、子代理人、browser、computer use 這些工具。AWS 內部的 Amazon Q Developer、AWS Glue 都用它。</li>
  <li><strong><a href="https://adk.dev/">Google ADK</a> (Python、Java 為主，另有 TypeScript / Go / Kotlin)</strong>: Apache 2.0 開源，功能同樣齊全，但重點在多代理人編排而不是單一迴圈: 內建 planner (讓模型先產出一份規劃再行動，但不是逐項打勾的待辦清單)、Sequential / Parallel / Loop 的多代理人編排、以及分得很清楚的 session (對話內) 跟 memory service (跨 session 可搜尋的長期記憶)，還原生支援 A2A 協議跟 Computer Use。</li>
  <li><strong><a href="https://github.com/microsoft/agent-framework">Microsoft Agent Framework</a> (.NET 跟 Python)</strong>: MIT 開源，是微軟把 Semantic Kernel 跟 AutoGen 合併後的接班框架。核心是 AI Agent 加上 graph-based 的 workflow 編排，多代理人跟記憶內建，主打企業級需求: 持久化 checkpoint、OpenTelemetry 可觀測性、middleware、A2A、人為介入。檔案系統這類則要自己接。</li>
  <li><strong><a href="https://openai.github.io/openai-agents-python/">OpenAI Agents SDK</a> (Python 跟 JS / TypeScript)</strong>: MIT 開源，比較不 opinionated: 核心就是 Agent、Handoff、Guardrail、Session、Tool 幾個概念，內建一個預設的 agent 迴圈，但六項能力多半要自己補。多代理人協作靠 Handoff 跟「agent 當成工具」用一般 Python 串起來，不是用一張圖去定義。</li>
  <li><strong><a href="https://ai.pydantic.dev/">Pydantic AI</a> (只有 Python)</strong>: MIT 開源，由 Pydantic 團隊出品，是型別優先的框架，強項是型別安全: 用 Pydantic model 定義結構化輸出、用依賴注入管理 agent 的相依資源，model 無關。MCP 是 client / server 雙向內建，還官方支援 Temporal、DBOS、Prefect、Restate 四種 durable execution (持久化執行) 引擎。</li>
  <li><strong><a href="https://ai-sdk.dev/">Vercel AI SDK</a> (只有 TypeScript / JavaScript)</strong>: Apache 2.0 開源，TypeScript 優先的工具包。核心是呼叫 LLM 的統一介面加上 Zod schema 做型別安全的結構化輸出，v7 開始有正式的 agent 迴圈 (<code class="language-plaintext highlighter-rouge">ToolLoopAgent</code>) 和 sandbox 執行環境，但六項能力的工具大多要自己接或從社群套件補。跟其他框架最大的差別是內建前端 UI 整合: React、Svelte、Vue、Solid 都有現成的 chat hooks，如果你的 agent 需要即時串流到前端介面，這是其他框架都沒直接提供的。Model 無關，支援 30+ 個 provider。</li>
  <li><strong><a href="https://github.com/langchain-ai/langgraph">LangGraph</a> (LangChain, Python 跟 JS / TypeScript)</strong>: MIT 開源，是這裡最不 opinionated、最低階的一個: 本質是用圖 (節點、邊、共享 state) 描述的狀態機，連 agent 迴圈都要你自己用節點接出來。它的強項是持久化: checkpointer 把每一輪 state 存起來，支援人為介入、時光回溯 (time-travel) 除錯、斷點續跑，跨 session 長期記憶則用 Store。前面提的 deepagents 就是建在它上面，等於有人幫你把那層 deep agent 接好了。</li>
</ul>

<h2 id="各框架的六項能力對照">各框架的六項能力對照</h2>

<p>用第 1 篇那六項能力逐一對照這些框架，差別就很清楚了。</p>

<div style="font-size:.85em;color:var(--text-secondary);margin:18px 0 6px;line-height:1.6;">開發語言 = 你串接這個框架時要寫的程式語言 (不是框架本身的實作語言)。能力欄對應第 1 篇六項: ① 規劃 (Plan &amp; Todos) · ② 檔案/Bash (Filesystem &amp; Bash) · ③ 子代理 (Sub-Agent) · ④ 記憶 (Memory) · ⑤ Skills · ⑥ 額外工具 (MCP 等)。標示: ✅ 內建 · 🟡 部分或要自行開啟 · 🔧 自己接。</div>

<div style="overflow-x:auto;margin:6px 0 24px;">
<table style="border-collapse:collapse;width:100%;font-size:.85em;min-width:720px;">
<thead>
<tr style="background:var(--surface);">
<th style="text-align:left;padding:8px 10px;border:1px solid var(--border);">框架</th>
<th style="text-align:left;padding:8px 10px;border:1px solid var(--border);">開發語言</th>
<th style="padding:8px;border:1px solid var(--border);">① 規劃</th>
<th style="padding:8px;border:1px solid var(--border);">② 檔案/Bash</th>
<th style="padding:8px;border:1px solid var(--border);">③ 子代理</th>
<th style="padding:8px;border:1px solid var(--border);">④ 記憶</th>
<th style="padding:8px;border:1px solid var(--border);">⑤ Skills</th>
<th style="padding:8px;border:1px solid var(--border);">⑥ 額外工具</th>
</tr>
</thead>
<tbody>
<tr style="background:var(--tag-bg);"><td colspan="8" style="padding:6px 10px;border:1px solid var(--border);font-weight:700;color:var(--accent);">路線一: 全套 Deep Agent</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">Codex SDK</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">TS · Py</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">Codex App Server</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">不限 (JSON-RPC)</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">GitHub Copilot SDK</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">Node · Py · Go · .NET · Java · Rust</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">Claude Agent SDK</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">Py · TS</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">LangChain deepagents</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">Py · TS</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">Pi (極簡)</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">TS</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🟡</td></tr>
<tr style="background:var(--tag-bg);"><td colspan="8" style="padding:6px 10px;border:1px solid var(--border);font-weight:700;color:var(--accent);">路線二: 從基礎構建 (opinionated 由高到低)</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">Strands Agents</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">Py · TS</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">✅</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">Google ADK</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">Py · Java · TS · Go · Kotlin</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">✅</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">Microsoft Agent Framework</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">.NET · Py</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🟡</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">OpenAI Agents SDK</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">Py · TS</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">✅</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">Pydantic AI</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">只有 Py</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🟡</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">Vercel AI SDK</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">只有 TS / JS</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🟡</td></tr>
<tr><td style="padding:8px 10px;border:1px solid var(--border);">LangGraph (核心)</td><td style="padding:8px 10px;border:1px solid var(--border);white-space:nowrap;">Py · TS</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🟡</td><td style="text-align:center;border:1px solid var(--border);">✅</td><td style="text-align:center;border:1px solid var(--border);">🔧</td><td style="text-align:center;border:1px solid var(--border);">🟡</td></tr>
</tbody>
</table>
</div>

<p>從開發語言看，幾乎每家都支援 Python 跟 TypeScript，這兩個是目前 agent 開發的主流語言 (ADK 另外還有 Java、Go、Kotlin，Pydantic AI 則只有 Python、Vercel AI SDK 只有 TypeScript / JavaScript，GitHub Copilot SDK 第一方支援到六種、是最多的)。比較特別的是 Codex App Server: 它做成 JSON-RPC server，所以不限定你的程式用什麼語言 (Copilot SDK 同樣靠 JSON-RPC 跟 CLI 溝通)。</p>

<h2 id="兩條路線的優缺點">兩條路線的優缺點</h2>

<div style="display:flex;flex-wrap:wrap;gap:16px;margin:24px 0;">
<div style="flex:1 1 300px;border:1px solid var(--border);border-top:3px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;margin-bottom:10px;">全套 Deep Agent</div>
<div style="color:#1a7f37;font-weight:600;font-size:.92em;">優點</div>
<div style="font-size:.9em;color:var(--text);margin:4px 0 10px;">馬上有能跑的 agent; 六項能力、加上一份調校過、預設行為很強的 system prompt 都已內建，不用從頭做; 這套 harness 是原廠針對自家模型調過的。</div>
<div style="color:#cf222e;font-weight:600;font-size:.92em;">缺點</div>
<div style="font-size:.9em;color:var(--text);margin-top:4px;">是給通用任務用的，會帶你的場景用不到的 context 跟工具; 可控性、可改性、可維護性受限 (Claude Agent SDK 閉源最明顯); 通常和特定模型供應商綁定。</div>
</div>
<div style="flex:1 1 300px;border:1px solid var(--border);border-top:3px solid var(--accent);border-radius:8px;padding:16px 18px;background:var(--surface);">
<div style="font-weight:700;margin-bottom:10px;">從基礎構建</div>
<div style="color:#1a7f37;font-weight:600;font-size:.92em;">優點</div>
<div style="font-size:.9em;color:var(--text);margin:4px 0 10px;">完全可控，可以貼著自己的任務分布，把無關的 context 跟工具拿掉; model 無關，可換供應商、好控制成本; 適合要精準掌握 context / 延遲 / 成本的產品場景。</div>
<div style="color:#cf222e;font-weight:600;font-size:.92em;">缺點</div>
<div style="font-size:.9em;color:var(--text);margin-top:4px;">要自己設計、組裝需要用到的能力，花的時間跟工程量會多一些。</div>
</div>
</div>

<p>還有一個容易被忽略、但對 production 服務很重要的維度: 維護跟 debug。全套 Deep Agent 路線有不少底層不在你的控制範圍內，一旦出問題比較難處理; 核心閉源的更麻煩 (Claude Agent SDK、GitHub Copilot SDK 都是這型): 底層出狀況你改不了、只能等官方。以 Claude Agent SDK 為例，它 GitHub 上其實累積了不少還沒解決的 issue (官方不一定有空處理)。如果你要做的是對外的 production 服務，這個底層夠不夠單純、好不好維護 (至少要能看到原始碼)，會是很實際的考量。從基礎構建雖然一開始要自己接，但底層都在你自己這邊，要 debug、要改都比較有把握。</p>

<h2 id="那到底該選哪條">那到底該選哪條?</h2>

<p>沒有一體適用的答案，要看你做的是什麼。小編整理三個比較清楚的情境:</p>

<p>🔹 <strong>特定情境應用，或是 B2C 使用者規模比較大的產品 → 建議從基礎構建</strong>。從基礎構建可以更有效率: 貼著單一任務分布做的精簡版本，token 成本比較低、延遲也比較低。使用者一多，這些成本、延遲、可控性的差異會更明顯，你會希望把通用 deep agent 那些用不到的 context 跟工具都拿掉 (也就是貼著單一用途的 vertical agent)。能換模型供應商、能精算每一項成本，這時候很重要。</p>

<p>🔹 <strong>自用、或企業內部軟體開發、要打造自己團隊用的 harness 跟 outer loop → 建議從現成 deep agent 開始</strong>。這種場景你要的本來就是一個強的通用開發 agent，原廠已經幫你把內層調好了，沒必要重做。使用者數量沒那麼大，每個 session 多帶一點通用能力的成本不是重點，先有個能跑的東西、把你的 outer loop 疊上去比較划算。</p>

<p>🔹 <strong>如果你想做的是讓使用者用自己的 ChatGPT 訂閱帳號 → 可以看看 Codex SDK / app server</strong>。Codex 支援用 ChatGPT 訂閱帳號登入，不一定要用 API key 計費。對比之下 Claude Agent SDK 第三方使用只能用 API key、不能用 claude.ai 帳號登入。如果你的產品設計是「使用者帶自己的帳號來用」，這個差別會直接影響你做不做得出來。</p>

<h2 id="最後-框架只是起點">最後: 框架只是起點</h2>

<p>框架是起點，不是終點。選全套 Deep Agent，起點比較高，一開始就有個能跑的 agent，但相對不好改; 選從基礎構建，起點低、要自己接的多，但彈性跟可控性比較好，可以一步步朝你想要的設計前進。</p>

<p>不管選哪邊，框架幫你接好的都還是第 1 篇講的「能不能做」。真正讓 agent 做得對、做得完的那套 harness (工具層檢查、單輪驗收、外層 loop、eval gate)，不管哪個框架都得自己建。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Coding" /><summary type="html"><![CDATA[📚 給 Agent 開發者的駕馭工程 (全 9 篇) 1. 基礎: Deep Agent 的六項內建能力 2. 核心: Agent 要的是回饋迴路，不是完美提示 3. 回饋時機一: 工具回傳值，是寫給 agent 的回饋 4. 回饋時機二: 兩次 model request 之間，把訊息注入執行中的 agent 5. 回饋時機三: 單輪結束的驗收，Goal 與 Outcomes 6. 回饋時機四: 外層 Loop, Ralph、Symphony 與 Cron 7. 進階: 自我改進 Harness, Meta-Harness 與爬坡 8. 收尾: 會過期的 Harness, Model-Harness-Fit 與 Bitter Lesson 9. 自建 Agent 的框架選型: 全套 Deep Agent 還是從基礎構建? (本篇) 🎞️ 搭配演講投影片: 給 Agent 開發者的 Harness+Loop Engineering]]></summary></entry><entry><title type="html">當模型表現取決於推論算力: 評測分數正在失去意義，LLM 能力上限也量不出來</title><link href="https://blog.aihao.tw/2026/06/11/test-time-compute-evals/" rel="alternate" type="text/html" title="當模型表現取決於推論算力: 評測分數正在失去意義，LLM 能力上限也量不出來" /><published>2026-06-11T00:00:00+00:00</published><updated>2026-06-11T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/11/test-time-compute-evals</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/11/test-time-compute-evals/"><![CDATA[<p>OpenAI 研究員 Noam Brown (推理模型 o1 背後的關鍵人物) 發了一篇長文 <a href="https://x.com/polynoamial/status/2064210146558136827">Implications of Large-Scale Test-Time Compute</a>，蠻有料的。核心論點一句話就能講完: 隨著 LLM 越來越強，benchmark 分數越來越取決於模型在推論時用掉多少算力，也就是「測試時算力」(test-time compute)。我們很可能不知道現代 LLM 的能力上限在哪裡，因為要實際量測它太昂貴了。所以評估方式該改了: 不要只報一個分數，而是畫出一條曲線，呈現模型在不同 token 數、成本或時間下的表現。</p>

<p>以下整理重點，並補充幾篇相關研究。</p>

<h2 id="同一個模型換個-x-軸就是另一個故事">同一個模型，換個 x 軸就是另一個故事</h2>

<p>GPT-5.5 剛發布時，第一波反應是質疑: benchmark 數字有進步，但不多。幾小時後大家實際上手，才發現它比 GPT-5.4 強了一個檔次。經典的「benchmark 成績表格」顯然沒有反映出全貌。</p>

<p>原因是 GPT-5.5 並不是在跟 5.4 相同的 token 預算(或美元預算)下評估的。本文封面圖就是 Noam 給的對比: 左邊的長條圖上，兩個模型只差 2.8 個百分點，看起來進步不大。但右邊把 x 軸換成輸出 token 數之後，故事完全不同: 在相同的 token 預算下，5.5 明顯強一截，5.4 要花將近 3 倍的 token 才能追到接近的分數。把測試時算力這個變因控制住，兩個模型的真正差距才顯現出來。</p>

<h2 id="為什麼不直接把算力開到飽和再評估">為什麼不直接把算力開到飽和再評估?</h2>

<p>最直覺的反問是: 那就讓模型一直算下去，算到分數不再進步為止，量到的不就是上限了? Noam 說問題在於: 實際經驗上，「分數不再進步」這個停滯點(plateau)非常遠，在合理的預算內甚至可能根本看不到。</p>

<p>他舉了兩個例子。一個是 Karpathy 的 autoresearch 實驗，跑了數百次實驗後，表現仍在繼續進步。另一個是英國 AI Security Institute 的資安攻防評測，跑到 1 億 tokens，Mythos 和 GPT-5.5 的表現還在快速上升:</p>

<p><img src="/assets/images/test-time-compute-evals/aisi-cyber-eval.jpg" alt="" /></p>

<p>而且注意看這張圖: 越強的模型，分數隨算力成長的幅度也越大。模型越強，就越能在長時間跨度(long horizon)的任務上持續有效運作，停滯點被推得更遠，甚至可能消失。</p>

<blockquote>
  <p>編按: 「量測上限太昂貴」不是修辭。X 上最近流傳一篇據稱是 Anthropic 未發布模型 Mythos 的企業試點測試心得(<a href="https://x.com/Tz_2022/status/2064355852539019546">中文翻譯</a>)，作者說光這一輪測試就花了超過 100 萬美元的推論費用，而他們公司全員上個月的推論算力總開銷也才 200 萬美元。內容真偽無法驗證，但這個量級跟上面那條「跑到 1 億 tokens 還在爬升」的曲線是一致的: 想知道前沿模型的上限在哪，先準備好足夠的預算。</p>
</blockquote>

<h2 id="該怎麼評估-把曲線畫出來">該怎麼評估: 把曲線畫出來</h2>

<p>Noam 認為正確的評估方式，是畫出「表現 vs 測試時算力」的曲線，x 軸可以用 token 數、成本或時間。已經有 benchmark 這樣做了，例如 ARC-AGI 的排行榜，直接畫出「分數 vs 每題成本」:</p>

<p><img src="/assets/images/test-time-compute-evals/arc-agi-2-leaderboard.jpg" alt="" /></p>

<p>另一個合理的做法是設定明確的 token、時間或成本預算，並且事先告知模型，就像人類考 SAT 或數學奧林匹亞也是限時的。</p>

<p>三種 x 軸各有取捨:</p>

<ul>
  <li><strong>Token 數</strong>: 不同模型之間不能直接比較，因為每家的 tokenizer、生成速度、單價都不同</li>
  <li><strong>美元成本</strong>: 受 batching、硬體利用率等實作細節影響，而且成本和延遲之間會互相取捨</li>
  <li><strong>實際耗時</strong>: 像 best-of-N (平行跑 N 次取最好的結果)這類技巧，可以在幾乎不增加耗時的情況下用掉更多算力，所以時間軸會低估算力用量</li>
</ul>

<p>但 Noam 的重點是: 不管選哪一個，任何一條曲線都比單一分數有資訊量。</p>

<h2 id="對-ai-安全的影響-安全評估該用多大的預算跑">對 AI 安全的影響: 安全評估該用多大的預算跑?</h2>

<p>前沿模型發布前，實驗室通常會評估資安攻擊、生物武器等濫用風險，超過能力門檻就要先做好緩解措施才能發布。但如果模型能力取決於用了多少推論算力，那安全評估該用多大的預算來跑? 實務上，多數安全評估根本沒有考慮這件事。</p>

<p>Gemini 3 Deep Think 發布時，benchmark 分數比之前的模型高出一截，卻沒有附上說明風險評估的 model card，引發 AI 安全社群的不滿。但 Noam 認為這個批評沒打到點上: Deep Think 很可能是拿其他「有」做過安全評估的模型，外面再包一層鷹架(scaffold)搭出來的。任何人只要願意付出 Deep Think 等級的推論費用，自己把多次模型呼叫串起來，大概也能重現同樣的能力。Deep Think 只是讓一般使用者更方便取得而已。</p>

<p>真正該檢討的是: Gemini 3 和其他模型發布時，安全報告都沒有把能力表示成測試時算力的函數。一個有決心的國家級行為者，可以對單一任務投入超過 1000 萬美元的推論算力; 但評估模型通常要跑數千甚至數百萬次任務，每一次都用這麼高的預算並不切實際。好消息是，表現隨算力擴展的走勢還算可預測，所以可以在較低預算下實際量測，再(帶著不確定性)外推高預算下的能力。Noam 理想中的模型評估長這樣:</p>

<p><img src="/assets/images/test-time-compute-evals/ideal-eval.jpg" alt="" /></p>

<p>文章還點出一個之後會更麻煩的問題: 要確認一個 agent 連續運作一年都不會出現對齊問題(misalignment)，可能唯一的辦法就是真的讓它跑一年。當 agent 的運作時間超過新模型的開發週期，實驗室可能根本來不及在發布前完成完整評估。</p>

<h2 id="noam-brown-給-ai-社群的三點具體建議">Noam Brown 給 AI 社群的三點具體建議</h2>

<ol>
  <li>實驗室發布新模型時，應該公布以 token 數、成本或時間為 x 軸的 benchmark 表現。至少也要報告達成那個分數用掉了多少推論預算</li>
  <li>Benchmark 排行榜應該一併追蹤推論用量，或是設定明確的 token、成本、時間預算</li>
  <li>各家實驗室的安全政策(如 OpenAI 的 Preparedness Framework、Anthropic 的 Responsible Scaling Policy)在判定模型是否跨過安全門檻時，應該把推論算力明確納入考量，並在多個預算下評估，包含從小預算外推的估計(附上不確定性)</li>
</ol>

<h2 id="不只-noam-在講-忽略成本的比較正在失效">不只 Noam 在講: 忽略成本的比較正在失效</h2>

<p>小編順手查了一下這個主題的其他研究，發現同樣的觀點已經累積不少證據:</p>

<h3 id="arc-prize-額外的準確率是可以用錢買的">ARC Prize: 額外的準確率是可以用錢買的</h3>

<p>ARC Prize 共同創辦人 Mike Knoop 在<a href="https://arcprize.org/blog/which-ai-reasoning-model-is-best">測評各家推理系統</a>時講得更直接:「所有 benchmark 和 model card 的報告都必須沿著兩個軸來做，因為額外的準確率是可以用錢買的。光禿禿的準確率分數是行銷，不是科學。」(原文: Naked accuracy scores are marketing, not science.) 他們的測試結論也是沒有單一贏家: 要最高準確率和要性價比，最佳選擇完全不同。</p>

<h3 id="artificial-analysis-的-claude-fable-5-評測">Artificial Analysis 的 Claude Fable 5 評測</h3>

<p>Artificial Analysis 這週發布的 <a href="https://x.com/ArtificialAnlys/status/2064500152430383489">Claude Fable 5 評測</a>就是一個現成的例子。下圖上半部是 Humanity’s Last Exam 的分數排行，Claude Fable 5 以 53.3% 居首; 下半部則是跑完整輪評測的總成本，Fable 5 要 $2,174，而 GPT-5.5 (xhigh) 拿 44.3% 花 $820、GPT-5.5 (high) 拿 43.0% 只花 $489。上下兩半對照著看，結論就從「Claude 領先 9 個百分點」變成「Claude 多花 2.7 倍的成本，買到 9 個百分點」。哪個划算，取決於你的任務值多少錢。若你只看分數排行，不看總成本，那你就無法判斷是否划算:</p>

<p><img src="/assets/images/test-time-compute-evals/aa-hle-cost.jpg" alt="" /></p>

<p>順帶一提，Artificial Analysis 網站上也有「<a href="https://artificialanalysis.ai/models#intelligence-index-tokens-cost">Intelligence Index vs 評測成本</a>」的散點圖，x 軸是跑完整套評測的成本(對數刻度)，這是目前業界做模型選型時最常引用的圖表之一。下圖左上角的綠色區塊標示「最划算象限」，GPT-5.5 (xhigh) 和 Claude Opus 4.8 (max) 這些最強模型則都落在右側最貴的那一區:</p>

<p><img src="/assets/images/test-time-compute-evals/aa-intelligence-vs-cost.png" alt="" /></p>

<h3 id="為什麼散點圖上沒有-claude-fable-5">為什麼散點圖上沒有 Claude Fable 5?</h3>

<p>不知為何，上面這張圖還沒有標出剛拿下 Intelligence Index 第一名 (64.9 分) 的 Claude Fable 5。AA 的模型頁面上 Fable 5 跑評測的 token 用量標示為 Unknown，小編猜可能是因為它的成本特別難算。Fable 5 有好幾個會在執行時動態改變行為和計費的機制:</p>

<ol>
  <li><strong>Fallback 機制</strong>: 約 9% 的任務會轉給 Opus 4.8 跑、並按 Opus 的單價計費，總成本取決於有多少任務被轉走</li>
  <li><strong>Adaptive thinking 預設全程開啟</strong>: 模型自行決定每一題要思考多深，token 用量無法事先固定</li>
  <li><strong>靜默降級</strong>: 根據 <a href="https://www-cdn.anthropic.com/d00db56fa754a1b115b6dd7cb2e3c342ee809620.pdf">system card</a> 第 12-13 頁，偵測到「前沿 LLM 開發」用途時(例如 pretraining pipeline、分散式訓練基礎設施、ML 加速器設計，約佔 0.03% 流量)會靜默限制模型能力: 不換模型、照 Fable 5 原價計費，也不通知使用者，從 API response 完全看不出來。模型甚至不會拒絕，仍會照常配合回應，只是這類任務的輸出效果被刻意壓低。至於怎麼壓低，Anthropic 沒有明講細節，只舉例說作法有: prompt 修改(在使用者看不到的地方改寫或附加指令)、steering vectors(推論時在模型內部的激活值上加一個方向向量，把行為往特定方向推)、參數高效微調(PEFT，掛上讓特定能力變弱的少量微調參數)。<a href="https://x.com/giffmana/status/2064446918541881734">Lucas Beyer 等研究員這幾天在 X 上嘲諷的就是這個機制</a></li>
</ol>

<blockquote>
  <p>小編補充 (2026/6/12 更新): 靜默降級有後續發展。在研究社群一片批評聲後，Anthropic 於 6/11 <a href="https://x.com/claudedevs/status/2064949876463645026">宣布</a>把它改成可見: 被標記的請求會跟 cyber/bio 防護一樣，明顯地 fallback 到 Opus 4.8，每次發生都看得到，API 也會回傳拒絕原因。官方承認當初選擇不可見的防護「是錯誤的取捨」並道歉。代價是防護變可見後更容易被探測繞過，為了維持對 jailbreak 的強健性，改進 classifier 期間誤判會變多。</p>
</blockquote>

<p>前兩個機制影響的是成本，第三個影響的是同一筆錢買到的能力是否一致。這些機制從外部都看不到也控制不了，同一套評測跑出來的 token 數、計費單價、甚至模型行為都可能不同，要報告一個可重現的成本數字就難了。</p>

<p>小編用已公布的數據回推: HLE 單項 Fable 5 花了 $2,174，是 Opus 4.8 的 1.24 倍，但這是它最省的場景; 第三方在文字生成和 agentic 評測上實測的成本是 Opus 4.8 的 2~3 倍。合起來粗估，Fable 5 跑完整套評測約要 $9,000~$11,000，約是 Opus 4.8 (max) 的 2 倍以上，已經靠近圖上 x 軸 $10k 的最右邊了。</p>

<h3 id="帕雷托前緣-pareto-frontier">帕雷托前緣 (Pareto frontier)</h3>

<p>這類「智慧 vs 成本」的散點圖通稱帕雷托前緣(Pareto frontier): 把「沒有其他模型同時比它更便宜又更聰明」的模型連成一條外緣線，選模型就沿著這條線挑，線內側的模型都存在又便宜又強的替代品。下圖是 <a href="https://x.com/AaronBergman18/status/2060248776929947703">Aaron Bergman</a> 上個月用 Artificial Analysis 數據畫的版本，虛線就是前緣。可以注意到前緣的中低價位段幾乎全是開放權重模型(藍點: Qwen、DeepSeek、MiMo)，閉源模型(紅點)只守住右上角的高智慧高價端:</p>

<p><img src="/assets/images/test-time-compute-evals/pareto-frontier-open-weights.jpg" alt="" /></p>

<p>swyx 的 Latent Space 從 2024 年就開始<a href="https://www.latent.space/p/lmarena">追蹤這條前緣</a>隨時間推移的速度，而且它移動得非常快: 根據 <a href="https://epoch.ai/trends">Epoch AI 的統計</a>，達到固定表現水準的推論成本大約每兩個月就砍半。也就是說，今天落在前緣上的模型，幾個月後就可能被更便宜的新模型蓋過，「哪個模型最划算」的結論有效期很短，需要定期重新檢視。</p>

<h3 id="拉齊預算後推理技巧的優勢會縮水">拉齊預算後，推理技巧的優勢會縮水</h3>

<p>EMNLP 2024 的 <a href="https://aclanthology.org/2024.emnlp-main.1112.pdf">Reasoning in Token Economies</a> 把各種推理策略放在相同的推論預算下重新評估，發現 Multi-Agent Debate、Reflexion 這些方法的優勢大幅縮水，多數情況下反而輸給簡單的基準做法 self-consistency (對同一題多次取樣再投票)。很多「新方法帶來的進步」，其實只是用了更多預算。這對評估 prompt 技巧和 agent 架構是同樣的提醒: 沒有控制預算的 A/B 比較，結論可能是錯的。</p>

<h3 id="看牌價選模型也會被誤導">看牌價選模型也會被誤導</h3>

<p><a href="https://arxiv.org/abs/2511.05722">OckBench</a> 發現每 token 單價只有一半的 7B 模型，因為產出 3 倍的 token 數量，實際每次查詢的成本反而貴 57%，他們稱之為「過度思考稅」(overthinking tax)。<a href="https://arxiv.org/abs/2603.23971">另一篇研究</a>系統性測了 8 個推理模型在 12 種任務上的表現，發現模型兩兩比較時，有 32% 的組合牌價排序跟實際總成本排序是相反的。</p>

<h2 id="小結">小結</h2>

<p>Noam 自己也說，這篇文章對長期追蹤的人來說沒什麼新東西: 從 2024 年 9 月 o1 發布那天起，大家就知道推理模型的表現會隨推論算力擴展。但快兩年過去，前沿實驗室發布新模型還是只報告單一數字，安全機構還是會對「鷹架架構(scaffold)用 100 倍預算打出更高分」感到意外。</p>

<p>對做模型選型的工程師來說，這篇的實際意義很具體: 下次比較模型時，需要考慮「在我的預算下哪一個模型最強」，而不是「誰的分數最高」。同一條曲線上的不同點，其實是不同的產品。</p>]]></content><author><name>ihower</name></author><category term="Eval" /><category term="Benchmark" /><category term="LLM" /><summary type="html"><![CDATA[OpenAI 研究員 Noam Brown (推理模型 o1 背後的關鍵人物) 發了一篇長文 Implications of Large-Scale Test-Time Compute，蠻有料的。核心論點一句話就能講完: 隨著 LLM 越來越強，benchmark 分數越來越取決於模型在推論時用掉多少算力，也就是「測試時算力」(test-time compute)。我們很可能不知道現代 LLM 的能力上限在哪裡，因為要實際量測它太昂貴了。所以評估方式該改了: 不要只報一個分數，而是畫出一條曲線，呈現模型在不同 token 數、成本或時間下的表現。]]></summary></entry><entry><title type="html">Microsoft AI: 從零練起的 MAI 模型和平台佈局</title><link href="https://blog.aihao.tw/2026/06/08/microsoft-mai-models/" rel="alternate" type="text/html" title="Microsoft AI: 從零練起的 MAI 模型和平台佈局" /><published>2026-06-08T00:00:00+00:00</published><updated>2026-06-08T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/08/microsoft-mai-models</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/08/microsoft-mai-models/"><![CDATA[<p>你可能知道 Microsoft 跟 OpenAI 合作很深，但比較少人注意到: Microsoft 其實在兩年前就默默開始自己練模型了。</p>

<p><a href="https://microsoft.ai/">MAI (Microsoft AI)</a> 是 2024 年 Microsoft 收購 DeepMind 共同創辦人 <a href="https://x.com/mustafasuleyman">Mustafa Suleyman</a> 的 Inflection AI 後成立的內部前沿模型實驗室。定位上跟 OpenAI 的合作是並行的: OpenAI 繼續提供 GPT 系列，MAI 則讓 Microsoft 擁有完全自主掌控的模型線。</p>

<p>你可能會問: Microsoft 不是已經有 <a href="https://azure.microsoft.com/en-us/blog/one-year-of-phi-small-language-models-making-big-leaps-in-ai/">Phi 系列</a>了嗎? Phi 是 Microsoft Research 做的開源小模型(最新的 <a href="https://www.microsoft.com/en-us/research/blog/phi-4-reasoning-vision-and-the-lessons-of-training-a-multimodal-reasoning-model/">Phi-4-reasoning</a> 是 15B 參數)，定位在研究貢獻和邊緣裝置部署。MAI 則完全不同: 閉源、前沿規模(1T 參數)、目標是跟 OpenAI、Anthropic、Google DeepMind 同級。兩個是不同團隊、不同目標的產品線。小編之前也沒特別關注這個團隊，直到六月初的 <a href="https://build.microsoft.com/">Build 2026</a> 上他們一口氣端出<a href="https://microsoft.ai/news/building-a-hillclimbing-machine-launching-seven-new-mai-models/">七個模型</a>，才發現值得關注一下。</p>

<p>這七個模型涵蓋推理、程式碼、圖片生成、語音合成和語音辨識，從組建團隊算起只花了約兩年。對做 LLM 應用的開發者來說，多了一個選擇，但更值得關注的是背後的平台策略和技術決策思路。</p>

<h2 id="七個模型哪些跟你有關">七個模型，哪些跟你有關</h2>

<table>
  <thead>
    <tr>
      <th>模型</th>
      <th>定位</th>
      <th>狀態</th>
      <th>取用方式</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://microsoft.ai/news/introducing-mai-thinking-1/">MAI-Thinking-1</a></td>
      <td>旗艦推理，35B 活躍參數的 MoE 架構，256K context</td>
      <td>內部預覽</td>
      <td>Microsoft Foundry，即將開放公開預覽</td>
    </tr>
    <tr>
      <td><a href="https://microsoft.ai/news/introducingmai-code-1-flash/">MAI-Code-1-Flash</a></td>
      <td>5B 輕量程式碼模型</td>
      <td>已上線</td>
      <td>VS Code GitHub Copilot</td>
    </tr>
    <tr>
      <td><a href="https://microsoft.ai/news/introducing-mai-image-2-5/">MAI-Image-2.5</a></td>
      <td>圖片生成/編輯</td>
      <td>已上線</td>
      <td>Foundry、OpenRouter API、PowerPoint</td>
    </tr>
    <tr>
      <td>MAI-Image-2.5-Flash</td>
      <td>上者的低成本版</td>
      <td>已上線</td>
      <td>同上</td>
    </tr>
    <tr>
      <td><a href="https://microsoft.ai/news/mai-transcribe-1-5more-accurate-context-aware-and-built-for-production/">MAI-Transcribe-1.5</a></td>
      <td>語音轉文字，支援 43 語言</td>
      <td>已上線</td>
      <td>Foundry、Teams</td>
    </tr>
    <tr>
      <td><a href="https://microsoft.ai/news/mai-voice-2/">MAI-Voice-2</a></td>
      <td>文字轉語音，可用少量錄音複製聲紋</td>
      <td>已上線</td>
      <td>Foundry、VS Code</td>
    </tr>
  </tbody>
</table>

<p>部分模型也上了 <a href="https://openrouter.ai/">OpenRouter</a>、Fireworks、Baseten 等第三方推理平台。目前沒有開源權重的計畫，走的是 API 和平台模式。</p>

<p><strong>Image 2.5 定價參考</strong>(每百萬 token): 文字輸入 $5 / 圖片輸入 $8 / 圖片輸出 $47。Flash 版約便宜 3-4 倍。</p>

<h2 id="跟現有選擇比如何">跟現有選擇比如何</h2>

<p>先看 MAI-Thinking-1 的 benchmark 數字:</p>

<table>
  <thead>
    <tr>
      <th>Benchmark</th>
      <th>MAI-Thinking-1</th>
      <th>Sonnet 4.6</th>
      <th>Opus 4.6</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AIME 2025 (數學推理)</td>
      <td>97.0%</td>
      <td>95.6%</td>
      <td>99.8%</td>
    </tr>
    <tr>
      <td>SWE-Bench Pro (程式碼)</td>
      <td>52.8%</td>
      <td>–</td>
      <td>53.4%</td>
    </tr>
    <tr>
      <td>SWE-Bench Verified (程式碼)</td>
      <td>73.5%</td>
      <td>79.6%</td>
      <td>80.8%</td>
    </tr>
    <tr>
      <td>GPQA Diamond (科學問答)</td>
      <td>84.2%</td>
      <td>89.9%</td>
      <td>91.3%</td>
    </tr>
    <tr>
      <td>IF Bench (指令遵循)</td>
      <td>69</td>
      <td>86</td>
      <td>–</td>
    </tr>
  </tbody>
</table>

<p>人類盲測對比 Sonnet 4.6: 49% 贏、45% 輸、6% 平手。對比 Opus 4.6: 43% 贏、52% 輸。</p>

<p><a href="https://microsoft.ai/pdf/mai-thinking-1.pdf">論文</a>自己的定位很克制: 「不是領域最強，但在廣泛任務上表現穩定一致。」</p>

<p>社群也注意到幾點:</p>

<ol>
  <li><strong>比較對象的選擇</strong>: Anthropic 在 Build 2026 前幾天才發布了 Opus 4.8，但 MAI 選擇跟較早的 Sonnet 4.6 做比較。</li>
  <li><strong>數字尚未獨立驗證</strong>: 截至目前，<a href="https://news.ycombinator.com/item?id=48374362">第三方評測聚合器</a>上還沒有 MAI-Thinking-1 的獨立測試結果。</li>
  <li><strong>明顯弱項</strong>: 指令遵循能力和終端操作(Terminal-Bench)跟競品差距不小，如果你的應用重度依賴複雜指令，這點要留意。</li>
</ol>

<p>小編的判斷: 作為第一個版本，MAI-Thinking-1 大致在 Sonnet 4.6 同級。如果你已經在用 Claude 或 GPT，目前沒有強烈理由切換。但如果你本來就深度使用 Microsoft 生態系(Azure、GitHub、M365)，整合度是加分項。</p>

<h2 id="mai-code-1-flash-copilot-裡的新選項">MAI-Code-1-Flash: Copilot 裡的新選項</h2>

<p>對每天在寫程式的人來說，<a href="https://microsoft.ai/news/introducingmai-code-1-flash/">MAI-Code-1-Flash</a> 可能是最直接相關的:</p>

<ul>
  <li>只有 5B 參數，但 SWE-Bench Pro 拿到 51.2% (Claude Haiku 4.5 是 35.2%)</li>
  <li>解決困難問題時，token 用量比同級模型少 60%</li>
  <li>有「自適應回應長度」機制: 簡單問題快速回答，複雜問題才展開長思考</li>
  <li>直接在 VS Code 的 Copilot 模型選擇器裡選用，不需額外設定</li>
</ul>

<p>這個模型是直接用 GitHub Copilot 的正式環境訓練的，不是單純對 benchmark 最佳化。對日常寫程式來說，回應速度和 token 效率可能比 benchmark 分數更重要。</p>

<h2 id="零蒸餾-對開發者意味著什麼">零蒸餾: 對開發者意味著什麼</h2>

<p>MAI-Thinking-1 有一個特別的設計選擇: 完全不使用其他模型的蒸餾(也就是不拿 GPT、Claude 等模型的輸出當訓練資料)，推理能力純粹靠自己的強化學習訓練學出來。也不使用合成資料，30T tokens 預訓練資料全部來自人類產出的內容。</p>

<p>這對下游開發者有什麼意義?</p>

<p>🔹 <strong>企業法務面</strong>: 如同 <a href="https://x.com/eliebakouch/status/2061965825037254947">@eliebakouch 的分析</a>，乾淨的資料來源讓企業法務更容易簽字放行。如果你的客戶是大企業或受監管產業，「這個模型沒有用到競爭對手的輸出當訓練資料」是一個可以寫進合約裡的保證。</p>

<p>🔹 <strong>供應鏈獨立性</strong>: 不依賴其他實驗室的模型輸出，意味著 Microsoft 的模型改進不會被上游的 API 政策變動影響。對長期使用 Microsoft 生態系的開發者來說，這是穩定性的保證。</p>

<p>不過社群對「乾淨資料」的說法也有質疑。<a href="https://simonwillison.net/2026/Jun/2/microsofts-new-models/">Simon Willison</a> 指出訓練資料包含 1.2 兆頁公開網頁爬蟲和 GitHub 程式碼。<a href="https://news.ycombinator.com/item?id=48374362">Hacker News</a> 上的討論認為，GitHub 改了使用條款允許用使用者資料訓練 AI，這大概就是所謂「合規授權資料」的意思，跟其他實驗室的做法沒有本質差異。所以「乾淨」更多是指「沒用別家模型的輸出」，不是「完全沒有版權爭議」。</p>

<h2 id="frontier-tuning-讓模型變成你的">Frontier Tuning: 讓模型變成你的</h2>

<p>這次發布中對開發者最有戰略意義的可能是 <a href="https://x.com/mustafasuleyman/status/2062275417378041957">Frontier Tuning</a>。核心概念:</p>

<p>🔹 <strong>強化學習環境(RLE)</strong>: 你建立自己的訓練環境，讓 MAI 模型在你的工作流程中持續學習。不只是 prompt 調整或 LoRA 微調，是真的在你的場景裡做強化學習。</p>

<p>🔹 <strong>實際效果</strong>: Microsoft 內部用 RLE 針對 Excel 的 agent 功能調校，結果跟 GPT-5.4 同等水準但效率高 10 倍。幫 McKinsey 調校後，品質勝過 GPT-5.5，成本低 10 倍。</p>

<p>🔹 <strong>商業定位</strong>: Mustafa 描述為「從租用 AI 到掌控 AI」。調校後的模型權重是你的，別人拿不到。</p>

<p>不過 <a href="https://news.ycombinator.com/item?id=48374362">Hacker News 上也有人吐槽</a>: 實際體驗是一個資料標註介面，需要你提供指令和回饋，每步之間要等很久。離「模型自動觀察你的工作流然後學會」還有段距離。</p>

<h2 id="satya-的觀點-對架構決策的啟發">Satya 的觀點: 對架構決策的啟發</h2>

<p>Satya 在 <a href="https://www.latent.space/p/satya-2026">Latent Space 訪談</a>中分享的幾個觀點，對做 LLM 應用架構決策的人蠻有參考價值:</p>

<p>🔹 <strong>模型只是起點，harness 才是產品</strong>: 每個 Microsoft 產品(GitHub Copilot、Defender)現在都是 multi-model harness，定義了「模型 + 資料 + 工具」的迴圈。上下文層的準備工作是過去兩年最難學到的一課。</p>

<p>🔹 <strong>私有評估集是最大的護城河</strong>: 如果你有自己的 eval，能在不同模型間切換並持續進步，你就掌握主動權。如果你的系統綁死一個模型、沒辦法換，你就沒有議價能力。</p>

<p>🔹 <strong>Token 資產</strong>: 企業累積的執行軌跡(traces)、評估集、上下文是新型態的智慧財產。這個觀點對正在建 AI 產品的團隊很重要: 你的護城河不在於你用哪個模型，而在於你累積了什麼資料和評估能力。</p>

<p>🔹 <strong>小模型 + 好的 harness 一樣能有效爬坡</strong>: 不一定要用最大最貴的模型。5B 參數的 MAI-Code-1-Flash 在正確的 harness 下表現超越大很多的模型。這呼應了「用小模型 + 好的 harness」可能比「直接用最大模型」更划算的實務經驗。</p>

<h2 id="技術報告-有趣的訓練細節">技術報告: 有趣的訓練細節</h2>

<p>這份 <a href="https://microsoft.ai/pdf/mai-thinking-1.pdf">109 頁的技術報告</a>是這次發布中讓社群最驚喜的部分。<a href="https://x.com/nrehiew_/status/2062013300196700395">@nrehiew_</a> 稱它「幾乎可以當成今天 LLM 訓練的教科書」，<a href="https://www.latent.space/p/ainews-microsoft-build-mai-thinking">Latent Space</a> 則評價 MAI 目前是「不錯的第二梯隊新實驗室，在特定領域微調上有明確優勢」。以下挑幾個有意思的點:</p>

<p><strong>架構: 大容量但省推理成本</strong></p>

<p>MAI-Thinking-1 總參數量約 1T，但每次推理只啟動 35B(512 個專家模組裡挑 8 個)。好處是模型知識容量大但推理成本可控。另一個設計是注意力機制大部分層只看附近的文字(局部注意力)，每隔幾層才做一次全文注意力，讓 256K 的長上下文不會讓推理成本暴增。</p>

<p><strong>推理能力是強化學習從零練出來的</strong></p>

<p>跟很多模型先拿 GPT/Claude 的思考過程做蒸餾不同，MAI-Thinking-1 的強化學習起點是一個完全沒見過「思考過程」的基底模型。訓練分成三條路線同時進行: 數學/科學推理、程式碼/工具使用、對話品質與安全性，各自練完再合併成一個模型。</p>

<p>論文展示了數學能力(AIME 2025)從約 20% 爬到 97% 的完整過程，花了約 5000 步。中間有好幾次訓練崩潰，靠的是「自我蒸餾」恢復: 把模型之前產出的好答案收集起來，先微調回穩定狀態，然後繼續強化學習。這種「崩了就從自己的好輸出重來」的做法蠻實務的。</p>

<p><strong>程式碼能力的訓練資料怎麼來的</strong></p>

<p>他們從 GitHub 上 1.02 億個 PR 出發，自動篩選出 26.5 萬個「可以驗證對錯」的程式修改環境(覆蓋 9.4 萬個 repo)，拿來當強化學習的訓練場。模型要實際讀程式碼、改程式碼、跑測試，答對才有獎勵。這個規模和方法對做 coding agent 評估的團隊蠻有參考價值。</p>

<p><strong>小規模實驗的結論不一定能放大</strong></p>

<p>論文揭示了一個有趣的陷阱: 用小模型測試出「資料配比 A 比 B 好」，放大到完整規模後結論可能反轉。實際案例是程式碼比重高的配比在大模型上勝出，但在小模型上反而輸。這對所有在做規模擴展決策或評估設計的人都是個提醒: 小實驗的結論要謹慎外推。</p>

<p><strong>訓練規模</strong></p>

<p>預訓練用了 30T tokens、8,192 張 GB200 GPU。強化學習階段最大的一次訓練動用了 4,864 張 GB300 晶片。</p>

<h2 id="平台佈局-跟-openai-的關係怎麼了">平台佈局: 跟 OpenAI 的關係怎麼了</h2>

<p>要理解 MAI 的戰略意義，得先知道背景: 2026 年 4 月，Microsoft 跟 OpenAI <a href="https://www.theverge.com/ai-artificial-intelligence/942242/microsoft-build-ai-agents-openai-competition">重新談判了合約</a>。OpenAI 解除了只能透過 Azure 發行的限制，可以到其他雲端上架；同時 Microsoft 也正式獲得自行訓練前沿模型的自由。<a href="https://venturebeat.com/technology/microsoft-ai-chief-says-company-was-set-free-from-openai-to-pursue-superintelligence">Suleyman 在受訪時說</a>: 「我們大約在六個月前才從 OpenAI 合約中解放出來，可以正式追求超智慧。所以這還是非常早期的階段。」</p>

<p>他也很坦白地定位 MAI 的現況: 「目標是證明我們能成為全球前四的實驗室。目前重要的三家是 Google DeepMind、OpenAI、Anthropic，我們還不算在其中。」</p>

<p>這讓 Azure 上的模型供給格局從「幾乎只有 OpenAI」變成三條路線並存:</p>

<table>
  <thead>
    <tr>
      <th>路線</th>
      <th>適合場景</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>MAI (自家模型)</strong></td>
      <td>企業合規、成本敏感的日常工作負載、Azure 深度整合</td>
    </tr>
    <tr>
      <td><strong>OpenAI on Azure</strong></td>
      <td>最難的推理任務、需要最強模型能力時</td>
    </tr>
    <tr>
      <td><strong>開源/合作夥伴模型</strong> (Llama, Mistral 等)</td>
      <td>需要微調、資料駐留要求、特定任務</td>
    </tr>
  </tbody>
</table>

<p><a href="https://www.digitalapplied.com/blog/microsoft-mai-model-family-build-2026-strategy-analysis">Digital Applied 的分析</a>認為，Azure 開發者現在應該根據任務需求在三條路線之間挑選，而不是像以前一樣預設什麼都用 OpenAI。</p>

<h2 id="microsoft-foundry-開發者實際接觸的界面">Microsoft Foundry: 開發者實際接觸的界面</h2>

<p>對開發者來說，這些模型都是透過 <a href="https://devblogs.microsoft.com/foundry/build-2026-foundry-models/">Microsoft Foundry</a> (原 Azure AI Foundry) 來使用的。幾個跟開發者直接相關的功能:</p>

<p>🔹 <strong>模型目錄</strong>: 超過 12,000 個模型，包含 MAI、OpenAI、Claude、Grok、Llama、Mistral、DeepSeek 等。80% 的 Fortune 500 企業在使用。</p>

<p>🔹 <strong>Model Router</strong>: 根據工作負載特性、成本目標、延遲要求，自動把每個請求路由到最合適的模型。不需要自己寫 routing 邏輯。</p>

<p>🔹 <strong>API 相容性</strong>: REST API 走 <code class="language-plaintext highlighter-rouge">/openai/v1/</code> 路由(chat/completions, embeddings, fine-tuning 等)，SDK 支援 Python、.NET、JS/TS、Java。如果你已經在用 OpenAI 格式的 API，切換成本很低。</p>

<p>🔹 <strong>Agent Service</strong>: 託管式 agent 運行環境，有沙箱隔離、狀態管理、檔案系統存取。</p>

<p><strong>MAIA 200 晶片對開發者的影響</strong>: 開發者不會直接碰到這顆晶片(目前沒有 Azure VM 實例可以租)。它是在 Foundry API 背後默默跑的，好處是 MAI 模型的 token 定價會比較低。<a href="https://x.com/mustafasuleyman/status/2061880164498428188">Mustafa 表示</a>在 MAIA 200 上跑 MAI 模型比 NVIDIA GB200 每美元效能高 30%、每瓦效能高 1.4 倍。</p>

<h2 id="產品整合和垂直領域">產品整合和垂直領域</h2>

<p>🔹 <strong>跨產品整合</strong>: MAI 模型已經嵌入 GitHub Copilot (Code-1-Flash)、Microsoft Teams (Transcribe)、PowerPoint (Image 2.5)、Dynamics 365 (Voice 2)。這種深度整合是第三方模型做不到的。</p>

<p>🔹 <strong>Mayo Clinic 合作</strong>: Microsoft <a href="https://microsoft.ai/news/building-a-hillclimbing-machine-launching-seven-new-mai-models/">宣布</a>與 Mayo Clinic 合作，用去識別化的臨床資料共同訓練醫療領域的前沿模型。這是 Frontier Tuning 在垂直領域的第一個公開案例。</p>

<p>🔹 <strong>Satya 的第三幕定位</strong>: 在 <a href="https://www.latent.space/p/satya-2026">Latent Space 訪談</a>中，Satya 把 Microsoft 的演進描述為「作業系統公司 → 雲端公司 → 智慧平台公司」。MAI 模型是這個「第三幕」的基礎設施層。</p>

<p><a href="https://news.ycombinator.com/item?id=48374362">WindowsForum 的評論</a>則比較冷靜: 「MAI 讓 Microsoft 對自己的 AI 命運有更多掌控權，但不代表使用者會因此更信任 Windows、Office 或 GitHub 裡的 AI 功能。信任要一個功能一個功能地贏回來。」</p>

<h2 id="結論-多了一個選項重點在生態系">結論: 多了一個選項，重點在生態系</h2>

<p>對 LLM 應用開發者來說，MAI 這次發布的意義不在於「又多了一個跟 Sonnet 4.6 差不多的模型」，而在於:</p>

<ol>
  <li><strong>Microsoft 生態系有了自己的模型</strong>: 如果你的產品建在 Azure / GitHub / M365 上，現在有原生整合度更高的選項。</li>
  <li><strong>Frontier Tuning 提供了深度客製化的新路線</strong>: 比一般的微調 API 更深入，但也更重(需要建立訓練環境、提供回饋)。適合有明確領域需求且願意投入的團隊。</li>
  <li><strong>強化了「模型可替換、評估集是護城河」的觀點</strong>: 不管你用不用 MAI，Satya 講的「私有 eval + multi-model harness」是值得認真思考的架構方向。</li>
</ol>

<p>最後想提一點: Microsoft MAI 和 <a href="https://blog.aihao.tw/2026/05/31/alex-wang-rebuilding-meta-ai/">Meta MSL</a> 不約而同都選擇了「從頭練、不蒸餾」的路線。這條路更慢、更貴、更容易失敗，但兩家都認為只有這樣才能建立真正可持續往上爬的能力，而不是靠蒸餾別人的輸出拿到一個無法超越來源模型的天花板。在 AI 快速迭代的時代，願意花兩年從零開始是令人敬佩的。</p>

<p>對照之下，不是每家大公司都做同樣的選擇。回顧今年以來:<a href="https://blog.google/company-news/inside-google/company-announcements/joint-statement-google-apple/">Apple 今年一月宣布</a>下一代基礎模型將改用 Google Gemini，等於放棄自己練前沿模型。<a href="https://www.aboutamazon.com/news/aws/aws-agentic-ai-amazon-bedrock-nova-models">Amazon 的 Nova 系列</a>持續發展，但定位偏向性價比而非前沿智能(<a href="https://www.neowin.net/news/amazon-unveils-nova-premier-its-most-advanced-yet-underwhelming-model/">Neowin 評 Nova Premier</a> 為「最先進但令人失望」，Nova 2 的評測對標是輕量級模型而非頂尖模型)，Amazon 真正的重心在自研晶片 Trainium 和 Bedrock 平台(模型 API 服務)，大手筆<a href="https://www.cnbc.com/2026/04/20/amazon-invest-up-to-25-billion-in-anthropic-part-of-ai-infrastructure.html">投資 Anthropic</a>，也把 <a href="https://www.theregister.com/2026/04/28/openai_climbs_into_amazons_bedrock/">OpenAI 模型上架到 Bedrock</a>。<a href="https://techcrunch.com/2026/05/06/is-xai-a-neocloud-now/">xAI 併入 SpaceX</a> 後重心轉向基礎設施，把資料中心的算力分別租給 <a href="https://512pixels.net/2026/06/google-leasing-spacex-xai/">Anthropic</a> 和 <a href="https://www.cnbc.com/2026/06/05/google-to-pay-spacex-920-million-a-month-for-xai-compute-capacity.html">Google</a>，光租算力就穩賺，不用自己承擔模型研發的風險。</p>

<p>小編整理了三大雲平台目前的前沿模型支援現況:</p>

<table>
<thead>
<tr><th>雲平台</th><th>自家模型</th><th>第三方前沿模型</th><th>開源模型</th></tr>
</thead>
<tbody>
<tr><td>Gemini Enterprise Agent Platform (原 Vertex AI)</td><td><strong>Gemini</strong></td><td>Claude</td><td rowspan="3">Llama、Mistral、DeepSeek、Qwen<br />三家皆有上架</td></tr>
<tr><td>Microsoft Foundry (原 Azure AI Studio)</td><td>MAI</td><td><strong>OpenAI</strong>、Claude</td></tr>
<tr><td>Amazon Bedrock</td><td>Nova</td><td><strong>Claude</strong>、OpenAI</td></tr>
</tbody>
</table>

<hr />

<p>📎 資料來源:</p>
<ul>
  <li><a href="https://microsoft.ai/news/building-a-hillclimbing-machine-launching-seven-new-mai-models/">Building a Hill-Climbing Machine</a> (Microsoft AI Blog)</li>
  <li><a href="https://microsoft.ai/pdf/mai-thinking-1.pdf">MAI-Thinking-1 Technical Report</a> (109 頁完整論文)</li>
  <li><a href="https://www.latent.space/p/satya-2026">Satya Nadella on Latent Space</a> (Build 2026 訪談)</li>
  <li><a href="https://x.com/mustafasuleyman/status/2061880164498428188">Mustafa Suleyman 公告推文</a></li>
</ul>]]></content><author><name>ihower</name></author><category term="LLM" /><category term="Industry" /><summary type="html"><![CDATA[你可能知道 Microsoft 跟 OpenAI 合作很深，但比較少人注意到: Microsoft 其實在兩年前就默默開始自己練模型了。]]></summary></entry><entry><title type="html">從 Code Act 到 Claude Code Dynamic Workflows 深度技術解析</title><link href="https://blog.aihao.tw/2026/06/05/code-act-to-dynamic-workflows/" rel="alternate" type="text/html" title="從 Code Act 到 Claude Code Dynamic Workflows 深度技術解析" /><published>2026-06-05T00:00:00+00:00</published><updated>2026-06-05T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/05/code-act-to-dynamic-workflows</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/05/code-act-to-dynamic-workflows/"><![CDATA[<p>Claude Code 上週發表 <a href="https://claude.com/blog/introducing-dynamic-workflows-in-claude-code">dynamic workflows</a> (<a href="https://code.claude.com/docs/en/workflows">官方文件</a>)，大家第一反應大多是「終於能一次跑幾百個 sub-agent 了」。這當然很猛，但小編覺得更值得講的，是它背後那條技術脈絡: 從 2024 年初的 Code Act 一路長出來的。</p>

<p>把這條線拉開來看，dynamic workflows 不是憑空冒出來的新玩意，而是「用程式碼當 agent 的行動」這個老想法，走到成熟、產品化的一步。技術上小編覺得設計得非常漂亮，這篇就來把這條路從頭走一遍。</p>

<h2 id="1-code-act-的核心洞見-用程式碼當行動空間">1. Code Act 的核心洞見: 用程式碼當行動空間</h2>

<p>故事從 <a href="https://arxiv.org/abs/2402.01030">“Executable Code Actions Elicit Better LLM Agents”</a> (2024/2) 這篇講起。傳統 agent 的做法是 JSON function calling: 模型吐一個 JSON 物件，描述要呼叫哪個工具、帶哪些參數，再交給後端的處理常式執行，把結果塞回去。</p>

<p>CodeAct 提出的轉向是: 不要用預先定義好的 JSON 格式當行動空間，而是直接讓 LLM 寫一段「可執行的 Python」當作它的行動，把規劃跟工具呼叫合併進同一份程式碼裡一起跑。</p>

<p>為什麼程式碼會比 JSON 好? 兩個關鍵理由:</p>

<p>🔹 <strong>模型本來就很會寫程式碼。</strong> LLM 在訓練時看過幾百萬個開源專案的真實程式碼，但工具呼叫用的那些特殊 token，是靠合成資料硬訓出來的，模型在真實世界裡根本沒見過。</p>

<p>🔹 <strong>程式碼天生支援組合。</strong> 迴圈、條件判斷、變數傳遞、把好幾個工具的輸出串起來，這些在 JSON schema 裡很難表達，在 Python 裡卻幾乎不費力。</p>

<p>論文 <a href="https://arxiv.org/html/2402.01030v4">Figure 1</a> 那個例子舉得很漂亮。任務是在美國、日本、德國、印度裡，找出買「CodeAct」這支手機最划算的國家。每個國家都得查匯率、稅率、當地售價、運費，再算出最終美元價格。用到的工具有 <code class="language-plaintext highlighter-rouge">lookup_rate</code>、<code class="language-plaintext highlighter-rouge">lookup_phone_price</code>、<code class="language-plaintext highlighter-rouge">convert_and_tax</code>、<code class="language-plaintext highlighter-rouge">lookup_shipping_cost</code>、<code class="language-plaintext highlighter-rouge">estimate_final_price</code>。</p>

<p>兩種做法的差異:</p>

<div style="display:flex;flex-wrap:wrap;gap:1rem;margin:1.2rem 0 1.6rem;">
<div style="flex:1 1 300px;min-width:0;">
<div style="border:1px solid var(--border);border-radius:8px;overflow:hidden;">
<div style="padding:.5rem .75rem;font-weight:600;background:#ffebe9;border-bottom:1px solid var(--border);font-size:.9rem;">JSON Function Calling</div>
<pre style="margin:0;padding:.6rem .75rem;font-size:.78rem;line-height:1.55;overflow-x:auto;background:var(--bg);"><code>→ {"tool":"lookup_rate","country":"Germany"}
← exchange_rate=1.1, tax_rate=0.19
→ {"tool":"lookup_phone_price","country":"Germany"}
← 700
→ {"tool":"convert_and_tax","price":700,...}
← 833
→ {"tool":"lookup_shipping_cost","country":"Germany"}
← ...
→ {"tool":"estimate_final_price",...}
← ...</code></pre>
<div style="padding:.45rem .75rem;font-size:.8rem;color:var(--text-secondary);background:var(--surface);border-top:1px solid var(--border);">⬆ 以上才跑完「德國」一國，四國要重複四遍。<br />每個 → ← 都是一次完整的模型來回，<strong style="color:#cf222e;">10+ 次起跳</strong></div>
</div>
</div>
<div style="flex:1 1 300px;min-width:0;">
<div style="border:1px solid var(--accent);border-radius:8px;overflow:hidden;">
<div style="padding:.5rem .75rem;font-weight:600;background:var(--tag-bg);border-bottom:1px solid var(--accent);font-size:.9rem;color:var(--accent);">CodeAct</div>
<pre style="margin:0;padding:.6rem .75rem;font-size:.78rem;line-height:1.55;overflow-x:auto;background:var(--bg);"><code>countries = ["USA","Japan","Germany","India"]
final_prices = {}
for country in countries:
    rate, tax = lookup_rate(country)
    price = lookup_phone_price("CodeAct", country)
    converted = convert_and_tax(price, rate, tax)
    shipping = lookup_shipping_cost(country)
    final_prices[country] = estimate_final_price(
        converted, shipping)
cheapest = min(final_prices, key=final_prices.get)</code></pre>
<div style="padding:.45rem .75rem;font-size:.8rem;color:var(--text-secondary);background:var(--tag-bg);border-top:1px solid var(--accent);">迴圈跑完四國，工具輸出直接傳給下一個工具。<br /><strong style="color:#1a7f37;">1 段程式碼、1 個 action</strong></div>
</div>
</div>
</div>

<p>左邊每呼叫一個工具，結果都得塞回模型，模型再決定下一步，一路來回。右邊一個 for 迴圈跑完四國，<code class="language-plaintext highlighter-rouge">lookup_rate</code> 的輸出直接傳給 <code class="language-plaintext highlighter-rouge">convert_and_tax</code> (資料流)，最後用 Python 內建的 <code class="language-plaintext highlighter-rouge">min()</code> 挑出最便宜的。模型只需要產生這一段程式碼、看一次最終答案。</p>

<p>CodeAct 的一個具體實作是 Hugging Face 的 <a href="https://github.com/huggingface/smolagents">smolagents</a>，裡面的 <code class="language-plaintext highlighter-rouge">CodeAgent</code> 就是讓模型寫 Python 來呼叫工具，而不是走 JSON function calling。</p>

<p>這裡有個容易混淆的點: CodeAct 跟 <a href="https://developers.openai.com/api/docs/guides/tools-code-interpreter">Code Interpreter</a> 不一樣。Code Interpreter 只是把「跑程式碼」當成眾多工具裡的「一個工具」來用，程式碼本身碰不到 agent 的其他工具。</p>

<p>CodeAct 則是把整個決策過程，連同函式呼叫，都寫進同一份程式碼裡一起執行。少了中間來回，效率自然好。</p>

<h2 id="2-同一招用到-mcp-上-cloudflare-自家的-code-mode">2. 同一招用到 MCP 上: Cloudflare 自家的 Code Mode</h2>

<p>時間快轉到 MCP 生態爆炸之後，出現一個很現實的問題: 工具太多了。每個操作都註冊成一個工具，幾千個工具定義直接塞爆上下文。</p>

<p>Cloudflare 的 <a href="https://blog.cloudflare.com/code-mode/">Code Mode</a> 參考了 CodeAct 的思路: 與其把每個操作描述成獨立的工具，不如把 MCP 工具轉成一份有型別的 TypeScript SDK，叫模型寫程式碼去呼叫。他們<a href="https://blog.cloudflare.com/code-mode-mcp/">新的 Cloudflare MCP server</a> 涵蓋整個 Cloudflare API (數千個端點)，只定義兩個 agent 工具: <code class="language-plaintext highlighter-rouge">search()</code> 跟 <code class="language-plaintext highlighter-rouge">execute()</code>。</p>

<p>這樣大約只吃 1000 個 token，同樣的東西若用傳統「每個端點一個工具」的做法，要 117 萬 token，直接超過大多數模型的 context window。<code class="language-plaintext highlighter-rouge">search()</code> 讓模型去查 OpenAPI 規格、把幾千個端點收斂到需要的那幾個，<code class="language-plaintext highlighter-rouge">execute()</code> 在沙箱裡跑實際呼叫。</p>

<p>這裡有個概念叫「composition tax」(組合稅): 每一次工具呼叫的結果，都得先塞回模型的神經網路，再被原封不動抄到下一個呼叫的輸入，白白浪費 token 跟延遲，還多出一次推理。動作越多，這個稅越重。寫成程式碼就能在執行環境裡直接串接，跳過這道稅。</p>

<h2 id="3-程式碼可呼叫-agent-tools-ptc">3. 程式碼可呼叫 agent tools: PTC</h2>

<p>接著是 Claude API 這邊的 <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling">Programmatic Tool Calling (PTC)</a>，它建在 <code class="language-plaintext highlighter-rouge">code_execution</code> 工具之上 (這就是 Claude 的 Code Interpreter)。作法很單純: 你照常定義自訂工具，只要在工具定義裡加一個 <code class="language-plaintext highlighter-rouge">allowed_callers: ["code_execution_20260120"]</code>，Claude 就不會一個一個直接呼叫它們，而是寫一段 Python，把這些工具當成函式，在程式碼執行容器裡組合、執行。</p>

<p>一句話抓住定位: PTC 就是「<strong>一段在容器裡執行的程式碼，而且這段程式碼能呼叫你原本要給 agent 的那些工具</strong>」。對照前面: Code Interpreter 的程式碼只能在沙箱裡自己算，碰不到 agent 的工具; CodeAct 在研究層面做到了讓程式碼呼叫 agent 工具。PTC 的升級是把這件事變成正式上線的 API，附帶的好處是: <strong>工具的執行留在你那邊，工具結果不進 Claude 的上下文</strong>。</p>

<p>機制拆開來看是這樣:</p>

<ol>
  <li>Claude 在 Anthropic 的容器裡寫一段程式碼，裡面可能有迴圈、條件判斷、好幾個工具呼叫。</li>
  <li>程式碼跑到 <code class="language-plaintext highlighter-rouge">result = await query_db(sql)</code> 時，<strong>容器暫停</strong>，API 把這個呼叫當成一個 <code class="language-plaintext highlighter-rouge">tool_use</code> 事件回傳給「你」。</li>
  <li><strong>工具還是跑在你那邊</strong>: 你的伺服器執行它、把結果回傳，Anthropic 的容器只負責跑那段編排程式碼。</li>
  <li>結果回到「正在執行的那段程式碼」繼續往下，這些中間結果<strong>完全不進 Claude 的 context window</strong> (也不計入 token)。</li>
  <li>整段程式碼跑完，Claude 只收到最後印出來的 stdout。</li>
</ol>

<blockquote>
  <p>編按: 這正好補上 composition tax 的另一半。Code Mode 解決了「工具定義」塞爆上下文，PTC 解決的是「工具結果」塞爆上下文。例如一次搜尋回傳 50 筆原始結果，程式碼可以在容器裡直接解析、過濾、交叉比對，只留下相關的幾筆，而不是把 50 筆全倒進 Claude 腦袋裡讓它自己讀。</p>
</blockquote>

<p>所以 PTC 做到的是: <strong>Claude 用程式碼組合多個工具呼叫 (迴圈、條件、串接輸出)，但工具還是跑在你那邊，你可以檢查、拒絕、記 log、排進人工審核佇列。</strong></p>

<p>但官方也講得很白，PTC 不是萬靈丹。<strong>嚴格循序、每一步都要 Claude 看完上一步才能決定下一步的工作，PTC 幫不上忙</strong> (那種情況本來就省不掉來回)，一次只呼叫一兩個工具的場景甚至會小貴一點。它真正發威的是「大量平行分流」或「結果很大、可以先在程式碼裡過濾」的任務。</p>

<p>(另外有個常見誤會: MCP connector 提供的工具，目前不能被 PTC 程式化呼叫。)</p>

<h2 id="4-開源實作-langchain-deep-agents-的-interpreter-與-interpreter-skills">4. 開源實作: LangChain Deep Agents 的 Interpreter 與 Interpreter Skills</h2>

<p>LangChain 的 Deep Agents 做的 <a href="https://www.langchain.com/blog/give-your-agents-an-interpreter">interpreter</a>，跟剛剛的 PTC 骨子裡是同一套做法，只是搬到了不同的層。PTC 是 Anthropic 在 API 供應商這一層做的 (Anthropic 管容器，工具在你那邊執行); Deep Agents Interpreter 則是 LangChain 在 harness 層做的中介層 (middleware)，跟模型無關，連開源模型都能用。LangChain 自己也說 PTC、Code Mode、interpreter、RLM 殊途同歸。</p>

<p>具體來說，它給 agent 一個內嵌的小型執行環境，agent 可以在裡面寫程式碼、存變數、定義輔助函式、跨呼叫保留狀態，像有了一個 Python 或 Node REPL。實作上它是掛一個 <code class="language-plaintext highlighter-rouge">eval</code> 工具，但別被「工具」兩個字誤導: 它背後是一個有狀態、會跨呼叫存活的執行環境，不是那種呼叫一次就結束的普通工具。</p>

<p>小編初看時覺得這跟 sandbox 功能有什麼差別? 其實差在兩個地方:</p>

<p>🔸 <strong>預設能力的方向相反。</strong> 沙箱是「先給一台電腦，再往下收」: agent 一開始就拿到完整的作業系統、檔案系統、網路、shell，你再去限制。interpreter 剛好倒過來，「先什麼都沒有，要什麼再明確給」: 預設只有一個語言執行環境，沒有檔案系統、沒有網路、沒有 shell，連讀檔、抓網頁、開子代理人(spawn sub-agent) 都得一個一個透過 allowlist 橋接進來。</p>

<p>🔸 <strong>隔離在不同的層。</strong> 這也回答一個常見疑問: 「interpreter 不也是在沙箱裡跑程式碼嗎?」其實不一定。interpreter 底層是像 QuickJS 這種內嵌的小引擎，跟 harness 跑在同一個行程裡，它的「隔離」是語言層的: 執行環境預設沒綁任何主機 API，所以裡面的程式碼根本沒有東西可以濫用。真正的沙箱 (gVisor、microVM 那種) 則是作業系統或硬體層的隔離，目的是擋住 agent 自己生成、可能亂來的程式碼「逃逸」。一句話: 能力控管 (capability scoping) 防的是「能碰到什麼」，VM 隔離防的是「會不會逃出去」，是兩回事。跑不可信的程式碼時，你還是可以把整個 interpreter 再包進一個沙箱，那是多加的一層防禦，不是 interpreter 自帶的機制。</p>

<p>PTC 跟 Deep Agents Interpreter 共同做到的關鍵動作是: 一旦把 Task 或 agent 工具也橋接進來，程式碼就能開子代理人。程式碼從「呼叫工具」升級成「呼叫 agent」，正式參與了 agent loop。</p>

<p>LangChain 後來進一步做了 <a href="https://www.langchain.com/blog/interpreter-skills">interpreter skills</a>: 開發者事先把一段「已知有效的固定流程」寫成 TypeScript 模組，註冊成一個 skill。模型在對話中判斷「現在該用哪個 skill、傳什麼輸入」，但 skill 內部的步驟是寫死的程式碼，不是模型即時決定的。</p>

<p>拿他們的 GitHub repo triage 範例來看，一個 interpreter skill 由 <code class="language-plaintext highlighter-rouge">SKILL.md</code> (告訴模型什麼時候該用) 和 <code class="language-plaintext highlighter-rouge">index.ts</code> (實際的流程程式碼) 組成。當使用者說「幫我整理這個 repo 的 issue」，模型判斷該用這個 skill，在 interpreter 裡呼叫它:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// skills/github-triage/SKILL.md 告訴模型: "Use this skill when a user asks for repository triage."</span>
<span class="c1">// skills/github-triage/index.ts 匯出 triage() 函式，內部流程是寫死的:</span>
<span class="c1">//   抓所有 open items → 開子代理人做摘要 → 逐一分群歸類</span>

<span class="kd">const</span> <span class="p">{</span> <span class="nx">triage</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="k">import</span><span class="p">(</span><span class="dl">"</span><span class="s2">@/skills/github-triage</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">triage</span><span class="p">(</span><span class="dl">"</span><span class="s2">langchain-ai/deepagents</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span>
  <span class="na">issues</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">prs</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">discussions</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="p">});</span>
<span class="nx">result</span><span class="p">.</span><span class="nf">toMarkdown</span><span class="p">();</span>
</code></pre></div></div>

<p>模型決定的是「要不要用、傳什麼參數、結果怎麼處理」; 但 <code class="language-plaintext highlighter-rouge">triage()</code> 內部怎麼抓資料、怎麼開子代理人、怎麼分群，全是寫死在 <code class="language-plaintext highlighter-rouge">index.ts</code> 裡的確定性程式碼。</p>

<p>這跟 Claude Code dynamic workflows 已經非常接近了，差別是 interpreter skills 由開發者事先寫好，而 dynamic workflows 是模型在接到任務時才即時生成那份編排腳本。</p>

<h2 id="這套設計背後的理論支點-rlm">這套設計背後的理論支點: RLM</h2>

<p>dynamic workflows 不只是工程上的巧思，它底下有一條清楚的研究脈絡撐著，叫 RLM (Recursive Language Models)。</p>

<p>這是 MIT 的 Alex Zhang、Tim Kraska、Omar Khattab 提出的框架 (2025 年 10 月先以部落格形式提出，年底發表<a href="https://arxiv.org/abs/2512.24601">論文</a>)。核心想法是: 把整段 prompt 當成一個放在 REPL 裡的「外部物件」。主模型不把上下文一口氣吞進腦袋，而是寫程式碼去窺看它、拆解它，對其中的片段遞迴呼叫自己或子模型，中間結果留在 REPL 的變數裡，只有精簡過的結果才回到主模型。這樣就繞過了固定 context window 的天花板。</p>

<p>關鍵在於 RLM 長期主張、而 coding agent 一直缺的那個能力: <strong>以程式化的方式呼叫 sub-agent，把它們的輸出在程式碼裡傳來傳去，而不經過主模型的上下文。</strong> 這正是 dynamic workflows 在做的事。</p>

<p>也因此，RLM 作者群直接認領了這個發表: <a href="https://x.com/lateinteraction/status/2060078643133763839">Omar Khattab 說</a>「Claude Code 終於是一個 RLM 了」; <a href="https://x.com/a1zhang/status/2060071701879066626">Alex Zhang 則認為</a> Opus 4.8 加上 dynamic workflows，大概是第一個被認真訓練成 RLM 的前沿模型實例。</p>

<p>Dynamic workflows 的設計跟 RLM 的主張高度吻合: 模型寫的那段 JS 裡，<code class="language-plaintext highlighter-rouge">agent()</code> 就是在遞迴開子代理人，而編排這層是決定性的。</p>

<h2 id="5-claude-code-dynamic-workflows">5. Claude Code Dynamic Workflows</h2>

<p>終於來講到本篇的主角 Claude Code dynamic workflows 了，一樣是讓模型寫程式碼去編排 sub-agent。模型即時寫出程式碼這件事，跟 PTC、Deep Agents Interpreter 並沒有不同。真正不一樣的，是把這套編排<strong>磨硬、做成產品</strong>: 編排腳本可以中斷後原地 resume，而且是決定性的 (第 8 節會講); 規模一次拉到上千個 sub-agent; 在背景獨立跑完才把結果交回對話; 還收斂成六種可互相組合的模式、能存檔重用。前面那幾種都沒有這層「可重播、可規模化」的保證。</p>

<p>實際操作時，你在 prompt 裡帶一個 workflow (或 <code class="language-plaintext highlighter-rouge">ultracode</code>)，Claude 就會即時寫出一份 JavaScript 編排腳本，用 <code class="language-plaintext highlighter-rouge">agent()</code>、<code class="language-plaintext highlighter-rouge">parallel()</code>、<code class="language-plaintext highlighter-rouge">pipeline()</code>、<code class="language-plaintext highlighter-rouge">phase()</code> 這些函式 (<a href="https://code.claude.com/docs/en/workflows">官方文件</a>)，平行啟動一大批 sub-agent (同時最多 16 個，單次最多 1000 個)，跑完再把結果交回來。</p>

<p>借用 <a href="https://x.com/voxyz_ai/status/2061782441606451381">Voxyz 那篇文章</a>的講法: <strong>計畫從對話的上下文搬進了可執行腳本。</strong> 以前用 Claude Code 跑大任務，所有步驟都擠在同一個對話上下文裡: 做完一步、等結果、決定下一步、再等結果。任務越複雜，上下文本身反而變成瓶頸，卡住的從來不是「誰來做」，而是「誰記得整個計畫」。dynamic workflows 把計畫從對話裡抽出來: 迴圈、分支、中間狀態、審查鏈、重試，全都在腳本裡跑，主對話只收到最後的摘要。</p>

<p>官方那篇 <a href="https://x.com/trq212/status/2061907337154367865">“A harness for every task”</a> 非常推薦一讀，把「為什麼需要這個」講得很清楚。當 Claude 在單一 context window 裡又要規劃又要執行，跑久了會出現三種失敗模式:</p>

<p>1️⃣ <strong>Agentic laziness (偷懶)</strong>: 複雜的多步任務做到一半就宣告完成，例如 50 項的安全檢查只做了 20 項就說好了。</p>

<p>2️⃣ <strong>Self-preferential bias (偏袒自己)</strong>: Claude 傾向偏好自己產出的結果，叫它對照評分準則自我審查時，這個偏差特別明顯。</p>

<p>3️⃣ <strong>Goal drift (目標漂移)</strong>: 多輪之後，尤其每次 compaction 都是有損壓縮，「不要做 X」這種邊界約束會慢慢被弄丟。</p>

<p>把任務拆給各自擁有獨立、乾淨上下文的多個 Claude，從結構上就避開這三個坑。</p>

<blockquote>
  <p>編按: 這三個失敗模式雖然是 Anthropic 自己歸納的，但跟學界的觀察高度吻合。Berkeley 那篇 <a href="https://arxiv.org/abs/2503.13657">《Why Do Multi-Agent LLM Systems Fail?》</a> (MAST) 分析了 1600 多份多 agent 系統的失敗軌跡，歸納出 14 種失敗模式，其中很大一塊正是「驗證不足」跟「提早收工」: 沒有獨立的把關者、任務沒做完就被宣告完成。dynamic workflows 用獨立上下文的 agent 配上對抗式驗證，剛好是針對這類失敗的結構性解法。</p>
</blockquote>

<p>最有名的案例是 Bun 從 Zig 改寫成 Rust: 約 75 萬行 Rust、99.8% 既有測試通過、從第一個 commit 到 merge 只花 11 天，靠的就是把任務切成「map → generate → review → verify → fix loop → cleanup」的流水線。</p>

<p>從 Code Interpreter 到 Dynamic Workflows，雖然都是「讓 LLM 輸出程式碼來執行」，但真正分出差異的是: <strong>那段生成的程式碼，被准許呼叫什麼、又被限制成什麼。</strong></p>

<div style="border:1px solid var(--border);border-radius:8px;overflow:hidden;margin:1.6rem 0;font-size:.86rem;">
<div style="display:flex;flex-wrap:wrap;align-items:center;background:var(--surface);padding:.55rem .8rem;font-weight:600;border-bottom:1px solid var(--border);">
<div style="flex:2 1 120px;">階段</div>
<div style="flex:3 1 160px;">生成的程式碼呼叫什麼</div>
<div style="flex:1 1 90px;">能開子代理人?</div>
<div style="flex:3 1 190px;">限制 → 換到的穩定性</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;border-bottom:1px solid var(--border);">
<div style="flex:2 1 120px;"><strong>Code Interpreter</strong></div>
<div style="flex:3 1 160px;">只有 Python 套件、檔案 (碰不到 agent 工具)</div>
<div style="flex:1 1 90px;color:var(--text-secondary);">✗</div>
<div style="flex:3 1 190px;">運算不設限，但站在 agent loop 外</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;border-bottom:1px solid var(--border);">
<div style="flex:2 1 120px;"><strong>CodeAct</strong></div>
<div style="flex:3 1 160px;">agent 的工具 (固定函式)</div>
<div style="flex:1 1 90px;color:var(--text-secondary);">✗</div>
<div style="flex:3 1 190px;">幾乎不設限: 表達力最大，但最難控管</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;border-bottom:1px solid var(--border);">
<div style="flex:2 1 120px;"><strong>Code Mode</strong></div>
<div style="flex:3 1 160px;">包好的一整套 SDK (= 那套 API)</div>
<div style="flex:1 1 90px;color:var(--text-secondary);">✗</div>
<div style="flex:3 1 190px;">框在 SDK 內: 用熟悉的程式碼駕馭龐大 API、省上下文</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;border-bottom:1px solid var(--border);">
<div style="flex:2 1 120px;"><strong>PTC・Deep Agents Interpreter</strong></div>
<div style="flex:3 1 160px;">agent tools，甚至開子代理人</div>
<div style="flex:1 1 90px;color:var(--accent);font-weight:600;">✓</div>
<div style="flex:3 1 190px;">靠 allowlist 開放: 結果留在程式碼、保住控制面</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;background:var(--tag-bg);">
<div style="flex:2 1 120px;"><strong style="color:var(--accent);">Dynamic Workflows</strong></div>
<div style="flex:3 1 160px;">只有編排函式 agent()/parallel()/pipeline()</div>
<div style="flex:1 1 90px;color:var(--accent);font-weight:600;">✓</div>
<div style="flex:3 1 190px;">只能編排、禁時間亂數: 換到 resume、可重播、決定性</div>
</div>
</div>

<p style="text-align:right;font-size:.8rem;color:var(--text-secondary);margin-top:-.8rem;">✏️ 小編製圖</p>

<p>Code Interpreter 什麼都能寫，但程式碼碰不到 agent 的工具，等於站在 agent loop 外面。CodeAct 的關鍵一步，是讓程式碼呼叫 agent 的工具，把程式碼拉進 loop 裡。之後每個階段的設計取捨不同: Code Mode 框在一套 SDK 裡; PTC 跟 Deep Agents Interpreter 用 allowlist 決定能碰哪些工具跟 agent; Dynamic Workflows 則把程式碼限制在「編排」這一件事，不能直接碰檔案、shell、網路 (那些都丟給 sub-agent)，連時間跟亂數都被禁掉，換來的是 resume 跟決定性。</p>

<p>表格裡另一個值得注意的差異: <strong>程式碼呼叫的是「死的函式」，還是「會自己推理的 sub-agent」。</strong> 原始 CodeAct 論文裡，程式碼呼叫的工具都是 <code class="language-plaintext highlighter-rouge">lookup_rate</code> 這種固定函式: 你可以用迴圈、條件去串它們的輸出，但函式本身不會思考。</p>

<p>不過嚴格講，CodeAct 這條線本身就能開子代理人，只要塞進去的那個「工具」其實是一個 agent 就行。Hugging Face 的 <a href="https://github.com/huggingface/smolagents">smolagents</a> 早就做到了: 你給一個 agent 設好 <code class="language-plaintext highlighter-rouge">name</code> 跟 <code class="language-plaintext highlighter-rouge">description</code>，當成 <code class="language-plaintext highlighter-rouge">managed_agents</code> 交給一個 manager <code class="language-plaintext highlighter-rouge">CodeAgent</code>，manager 生成的 Python 就能把它當函式呼叫。<a href="https://huggingface.co/docs/smolagents/en/examples/multiagents">官方範例</a>裡，一個 manager 指揮底下的 <code class="language-plaintext highlighter-rouge">web_search_agent</code> 查資料，自己負責規劃跟計算。所以表格裡 CodeAct 那格的 ✗，與其說是「技術上做不到」，不如說是「原論文沒往這個方向想，生態也還沒把 sub-agent 編排當成主軸」。</p>

<p>PTC 跟 Deep Agents Interpreter 帶出的重點是「程式碼能呼叫 agent tools」，雖然 tool 裡面也可以包一個 sub-agent，但這不是它們強調的用法。程式碼可以用迴圈、條件去組合多個工具呼叫，中間結果留在程式碼裡不必繞回主模型的上下文，後一個工具的輸入還可以拿前一個的結果在程式碼裡算出來 (<code class="language-plaintext highlighter-rouge">a = await tool(...); b = await tool(transform(a))</code>)，不必一開始就把所有參數寫死。</p>

<p>真正把 sub-agent 編排當成核心的，是 RLM 的研究和 Dynamic Workflows。RLM 一路主張的就是「以程式化的方式呼叫 sub-agent，把輸出在程式碼裡傳遞，而不經過主模型的上下文」。到了 Dynamic Workflows，開子代理人變成程式碼唯一在做的事，還補上了 resume、可重播、決定性這些硬保證。</p>

<p>表格裡那條 ✗✗✗✓✓ 的分界，標的不是「技術上能不能開子代理人」(CodeAct 透過 smolagents 也做得到)，而是「有沒有把 sub-agent 編排當成一等公民，再用產品級保證撐住」。</p>

<p>PTC / Deep Agents Interpreter 跟 Dynamic Workflows 都能 spawn、也都能分階段，差別不在「能不能」，而在編排層被磨得多硬。Dynamic Workflows 多了 <code class="language-plaintext highlighter-rouge">parallel</code>/<code class="language-plaintext highlighter-rouge">pipeline</code>/<code class="language-plaintext highlighter-rouge">phase</code> 這些原語，可中斷後 resume，還有禁時間亂數換來的決定性; PTC / Deep Agents Interpreter 則更通用、也更鬆，少了這些硬保證。</p>

<p>Dynamic Workflows 把程式碼的角色限制在編排，再用六種固定模式把編排本身也標準化。限制換來的是能 resume、能重播、結果可預期。</p>

<h3 id="六種可以互相組合的編排模式">六種可以互相組合的編排模式</h3>

<p><a href="https://x.com/trq212/status/2061907337154367865">“A harness for every task”</a> 這篇把常見的編排手法整理成六種，實際用時常常會疊在一起:</p>

<p>1️⃣ <strong>分類並執行 (classify-and-act)</strong>: 先用一個分類 agent 判斷任務屬於哪一型，再依結果路由到不同的 agent 或行為。也可以反過來放在最後，用分類器決定要輸出什麼。</p>

<div style="margin:.5rem 0 1.2rem;">
<svg viewBox="0 0 430 150" style="width:100%;max-width:440px;height:auto;"><defs><marker id="a1" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker></defs><rect x="6" y="58" width="64" height="34" rx="6" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" /><text x="38" y="80" text-anchor="middle" font-size="13" style="fill:var(--text)">輸入</text><line x1="72" y1="75" x2="116" y2="75" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a1)" /><rect x="120" y="56" width="92" height="38" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="166" y="80" text-anchor="middle" font-size="13" style="fill:var(--text)">分類器</text><rect x="312" y="14" width="112" height="30" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="368" y="34" text-anchor="middle" font-size="13" style="fill:var(--text)">行為 A</text><rect x="312" y="60" width="112" height="30" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="368" y="80" text-anchor="middle" font-size="13" style="fill:var(--text)">行為 B</text><rect x="312" y="106" width="112" height="30" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="368" y="126" text-anchor="middle" font-size="13" style="fill:var(--text)">行為 C</text><line x1="212" y1="72" x2="308" y2="30" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a1)" /><line x1="212" y1="75" x2="308" y2="75" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a1)" /><line x1="212" y1="78" x2="308" y2="120" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a1)" /></svg>
</div>

<p>2️⃣ <strong>分流並整合 (fan-out-and-synthesize)</strong>: 把任務拆成很多小步，每步開一個 agent，最後再把結果整合起來。適合步驟很多、或每步都需要自己乾淨上下文以免互相干擾的情況。整合那一步是個同步點 (barrier): 它會等所有分流的 agent 都跑完，再把它們的結構化輸出併成一份結果。</p>

<div style="margin:.5rem 0 1.2rem;">
<svg viewBox="0 0 450 150" style="width:100%;max-width:460px;height:auto;"><defs><marker id="a2" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker></defs><rect x="6" y="58" width="60" height="34" rx="6" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" /><text x="36" y="80" text-anchor="middle" font-size="13" style="fill:var(--text)">任務</text><rect x="110" y="12" width="84" height="28" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="152" y="31" text-anchor="middle" font-size="12" style="fill:var(--text)">步驟 1</text><rect x="110" y="61" width="84" height="28" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="152" y="80" text-anchor="middle" font-size="12" style="fill:var(--text)">步驟 2</text><rect x="110" y="110" width="84" height="28" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="152" y="129" text-anchor="middle" font-size="12" style="fill:var(--text)">步驟 3</text><line x1="66" y1="72" x2="106" y2="28" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a2)" /><line x1="66" y1="75" x2="106" y2="75" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a2)" /><line x1="66" y1="78" x2="106" y2="122" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a2)" /><rect x="250" y="48" width="80" height="54" rx="6" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" /><text x="290" y="72" text-anchor="middle" font-size="13" style="fill:var(--text)">整合</text><text x="290" y="89" text-anchor="middle" font-size="10" style="fill:var(--text-secondary)">同步點 (barrier)</text><line x1="194" y1="26" x2="248" y2="68" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a2)" /><line x1="194" y1="75" x2="248" y2="75" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a2)" /><line x1="194" y1="124" x2="248" y2="82" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a2)" /><rect x="376" y="58" width="64" height="34" rx="6" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" /><text x="408" y="80" text-anchor="middle" font-size="13" style="fill:var(--text)">結果</text><line x1="330" y1="75" x2="372" y2="75" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a2)" /></svg>
</div>

<p>3️⃣ <strong>對抗式驗證 (adversarial verification)</strong>: 每開一個 agent 產出結果，就另外開一個 agent，專門拿評分準則或標準去「反駁」它的輸出。</p>

<div style="margin:.5rem 0 1.2rem;">
<svg viewBox="0 0 430 140" style="width:100%;max-width:440px;height:auto;"><defs><marker id="a3" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker></defs><rect x="6" y="68" width="104" height="34" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="58" y="90" text-anchor="middle" font-size="13" style="fill:var(--text)">產出 agent</text><line x1="110" y1="85" x2="148" y2="85" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a3)" /><rect x="152" y="68" width="76" height="34" rx="6" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" /><text x="190" y="90" text-anchor="middle" font-size="13" style="fill:var(--text)">結果</text><rect x="138" y="14" width="104" height="32" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="190" y="35" text-anchor="middle" font-size="13" style="fill:var(--text)">反駁 agent</text><line x1="190" y1="46" x2="190" y2="66" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a3)" /><text x="200" y="60" font-size="11" style="fill:var(--text-secondary)">挑戰</text><line x1="228" y1="85" x2="292" y2="85" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a3)" /><text x="300" y="82" font-size="12" style="fill:var(--text-secondary)">✓ 通過</text><text x="300" y="100" font-size="12" style="fill:var(--text-secondary)">✗ 淘汰</text></svg>
</div>

<p>4️⃣ <strong>生成並篩選 (generate-and-filter)</strong>: 針對一個主題先生成一堆點子，再用評分準則或驗證去篩，去掉重複的，只留下品質最高、通過檢驗的那幾個。</p>

<div style="margin:.5rem 0 1.2rem;">
<svg viewBox="0 0 460 130" style="width:100%;max-width:460px;height:auto;"><defs><marker id="a4" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker></defs><rect x="6" y="48" width="68" height="34" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="40" y="70" text-anchor="middle" font-size="13" style="fill:var(--text)">生成</text><line x1="74" y1="65" x2="98" y2="65" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a4)" /><circle cx="112" cy="32" r="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><circle cx="142" cy="55" r="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><circle cx="118" cy="86" r="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><circle cx="168" cy="36" r="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><circle cx="156" cy="82" r="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><circle cx="186" cy="62" r="7" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><line x1="204" y1="65" x2="224" y2="65" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a4)" /><rect x="226" y="46" width="92" height="38" rx="6" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" /><text x="272" y="66" text-anchor="middle" font-size="13" style="fill:var(--text)">篩選</text><text x="272" y="80" text-anchor="middle" font-size="9" style="fill:var(--text-secondary)">評分準則 / 去重</text><line x1="318" y1="65" x2="342" y2="65" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a4)" /><circle cx="360" cy="52" r="7" style="fill:var(--accent)" /><circle cx="378" cy="74" r="7" style="fill:var(--accent)" /><text x="398" y="69" font-size="12" style="fill:var(--text-secondary)">只留精華</text></svg>
</div>

<p>5️⃣ <strong>錦標賽 (tournament)</strong>: 不是分工，而是讓 agent 互相競爭。開 N 個 agent 用不同方法各做同一件事，再用一個評審 agent 兩兩比較、淘汰，直到選出贏家。</p>

<div style="margin:.5rem 0 1.2rem;">
<svg viewBox="0 0 430 160" style="width:100%;max-width:440px;height:auto;"><defs><marker id="a5" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker></defs><rect x="6" y="8" width="84" height="26" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="48" y="26" text-anchor="middle" font-size="12" style="fill:var(--text)">方案 A</text><rect x="6" y="44" width="84" height="26" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="48" y="62" text-anchor="middle" font-size="12" style="fill:var(--text)">方案 B</text><rect x="6" y="92" width="84" height="26" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="48" y="110" text-anchor="middle" font-size="12" style="fill:var(--text)">方案 C</text><rect x="6" y="128" width="84" height="26" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="48" y="146" text-anchor="middle" font-size="12" style="fill:var(--text)">方案 D</text><rect x="170" y="14" width="80" height="28" rx="6" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" /><text x="210" y="33" text-anchor="middle" font-size="12" style="fill:var(--text)">勝者 1</text><rect x="170" y="120" width="80" height="28" rx="6" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" /><text x="210" y="139" text-anchor="middle" font-size="12" style="fill:var(--text)">勝者 2</text><line x1="90" y1="21" x2="166" y2="27" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a5)" /><line x1="90" y1="57" x2="166" y2="31" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a5)" /><line x1="90" y1="105" x2="166" y2="131" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a5)" /><line x1="90" y1="141" x2="166" y2="135" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a5)" /><rect x="324" y="67" width="92" height="32" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:2" /><text x="370" y="88" text-anchor="middle" font-size="13" style="fill:var(--text)">冠軍</text><line x1="250" y1="28" x2="320" y2="76" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a5)" /><line x1="250" y1="134" x2="320" y2="90" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a5)" /></svg>
</div>

<p>6️⃣ <strong>直到完成為止持續迴圈 (loop until done)</strong>: 對「工作量未知」的任務，不要設固定回合數，而是一直開 agent 直到觸發停止條件 (沒有新發現、或 log 裡不再有錯誤) 為止。</p>

<div style="margin:.5rem 0 1.2rem;">
<svg viewBox="0 0 440 150" style="width:100%;max-width:450px;height:auto;"><defs><marker id="a6" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" style="fill:var(--text-secondary)" /></marker></defs><rect x="34" y="56" width="120" height="34" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="94" y="78" text-anchor="middle" font-size="13" style="fill:var(--text)">開一批 agent</text><line x1="154" y1="73" x2="214" y2="73" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a6)" /><rect x="216" y="52" width="150" height="42" rx="6" style="fill:var(--surface);stroke:var(--border);stroke-width:1.5" /><text x="291" y="78" text-anchor="middle" font-size="13" style="fill:var(--text)">達成停止條件?</text><path d="M250,52 C250,20 94,20 94,54" fill="none" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a6)" /><text x="172" y="16" text-anchor="middle" font-size="11" style="fill:var(--text-secondary)">否: 再開一輪</text><line x1="291" y1="94" x2="291" y2="120" style="stroke:var(--text-secondary);stroke-width:1.5" marker-end="url(#a6)" /><text x="300" y="112" font-size="11" style="fill:var(--text-secondary)">是</text><rect x="246" y="120" width="90" height="28" rx="6" style="fill:var(--tag-bg);stroke:var(--accent);stroke-width:1.5" /><text x="291" y="139" text-anchor="middle" font-size="13" style="fill:var(--text)">完成</text></svg>
</div>

<h2 id="6-更多精彩用法-它不只拿來寫程式">6. 更多精彩用法: 它不只拿來寫程式</h2>

<p><a href="https://x.com/trq212/status/2061907337154367865">官方那篇文章</a>裡，小編覺得最有啟發的反而是作者一句話: workflows 用在「非寫程式」的任務上，往往更有威力。挑幾個他給的實際 prompt 範例 (已翻成中文)，順手標出背後用到的模式:</p>

<p>🔹 <strong>抓 1/50 機率的 flaky test</strong>: 「這個測試大概每 50 次會失敗 1 次。開一個 workflow 重現它，提出幾個假設、在各自的 worktree 裡對抗式地驗證，<code class="language-plaintext highlighter-rouge">/goal</code> 設定: 不到有一個假設成立不准停。」(loop-until-done + 對抗式驗證 + 根本原因調查)</p>

<p>🔹 <strong>從自己的歷史紀錄煉出規則</strong>: 「用 workflow 翻過我最近 50 個 session，挖出我一再重複糾正的地方，把常出現的那幾類變成 CLAUDE.md 規則。」(分流並整合 + 記憶與規則遵循)</p>

<p>🔹 <strong>挖出 Slack 裡沒人追的問題</strong>: 「用 workflow 翻過去半年的 #incidents 頻道，找出反覆出現、卻從來沒人開單追蹤的根本原因。」(大規模分流)</p>

<p>🔹 <strong>多視角壓力測試商業計畫</strong>: 「拿我的商業計畫，跑一個 workflow，讓不同 agent 分別站在投資人、客戶、競爭對手的角度，逐點挑毛病。」(對抗式驗證用在非技術任務)</p>

<p>🔹 <strong>80 份履歷排序</strong>: 「這裡有 80 份履歷，用 workflow 依後端職缺排序，再仔細複查前十名。先用 AskUserQuestion 工具問我評分標準。」(排序 + 生成並篩選)</p>

<p>🔹 <strong>命名錦標賽</strong>: 「我要幫這個 CLI 工具取名。用 workflow 腦力激盪一堆選項，再跑一場錦標賽選出前三名。」(tournament)</p>

<p>🔹 <strong>驗證部落格草稿的每個技術宣稱</strong>: 「翻過我這篇部落格草稿，用 workflow 對照 codebase 驗證裡面每一個技術宣稱，我不想出包。」(深度驗證)</p>

<p>這些任務的關鍵不是「大」，而是「有很多獨立的小判斷、需要互相把關、而且最好別全擠在同一個上下文裡」。官方還把適用情境整理成一份清單: 遷移重構、深度研究、深度驗證、上千筆排序、記憶與規則遵循、根本原因調查、大規模分流、探索與品味、評估、模型與智慧路由。反過來作者也提醒: 一般的寫程式任務通常不需要五個審查者的陣仗，別為了用而用。</p>

<h2 id="7-看一份真的-workflow-deep-research-長怎樣">7. 看一份真的 workflow: deep-research 長怎樣</h2>

<p>上面那些模式講起來還是抽象，剛好 Claude Code 內建的 <code class="language-plaintext highlighter-rouge">/deep-research</code> 就是用 dynamic workflows 寫的。以下都是直接讀它實際生成的那份 JS 腳本 (數字、門檻都照腳本，不是憑空猜的)。</p>

<p>它的 <code class="language-plaintext highlighter-rouge">meta</code> 先宣告整條流水線分成五個 phase:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">phases</span><span class="p">:</span> <span class="p">[</span>
  <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Scope</span><span class="dl">"</span><span class="p">,</span>      <span class="na">detail</span><span class="p">:</span> <span class="dl">"</span><span class="s2">把問題拆成 5 個搜尋角度</span><span class="dl">"</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Search</span><span class="dl">"</span><span class="p">,</span>     <span class="na">detail</span><span class="p">:</span> <span class="dl">"</span><span class="s2">5 個平行 WebSearch agent，一個角度一個</span><span class="dl">"</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Fetch</span><span class="dl">"</span><span class="p">,</span>      <span class="na">detail</span><span class="p">:</span> <span class="dl">"</span><span class="s2">URL 去重，抓前 15 個來源，抽出可查證的主張</span><span class="dl">"</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Verify</span><span class="dl">"</span><span class="p">,</span>     <span class="na">detail</span><span class="p">:</span> <span class="dl">"</span><span class="s2">每個主張 3 票對抗式驗證，2/3 票反駁就淘汰</span><span class="dl">"</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Synthesize</span><span class="dl">"</span><span class="p">,</span> <span class="na">detail</span><span class="p">:</span> <span class="dl">"</span><span class="s2">合併語意重複、依信心排序、附上出處</span><span class="dl">"</span> <span class="p">},</span>
<span class="p">]</span>
</code></pre></div></div>

<p>這就是 Claude 為「深度研究」這個任務即時寫出來的 harness，整條流水線長這樣:</p>

<div style="margin:1.6rem 0;font-size:.9rem;">
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;padding:.55rem .75rem;border-radius:8px;background:var(--tag-bg);border:1px solid var(--border);">
<div style="flex:0 0 96px;font-weight:600;color:var(--accent);">Scope</div>
<div style="flex:0 0 auto;font-size:.75rem;background:var(--bg);border:1px solid var(--border);border-radius:999px;padding:.05rem .5rem;">1 個 agent</div>
<div style="flex:1 1 180px;color:var(--text);">把問題拆成 5 個搜尋角度</div>
</div>
<div style="text-align:center;font-size:.76rem;color:var(--text-secondary);padding:.2rem 0;">↓ &nbsp;pipeline，階段間不等齊</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;padding:.55rem .75rem;border-radius:8px;background:var(--tag-bg);border:1px solid var(--border);">
<div style="flex:0 0 96px;font-weight:600;color:var(--accent);">Search</div>
<div style="flex:0 0 auto;font-size:.75rem;background:var(--bg);border:1px solid var(--border);border-radius:999px;padding:.05rem .5rem;">5 個 agent · 平行</div>
<div style="flex:1 1 180px;color:var(--text);">一個角度一個 WebSearch</div>
</div>
<div style="text-align:center;font-size:.76rem;color:var(--text-secondary);padding:.2rem 0;">↓ &nbsp;純 JS: URL 正規化 + 去重 + 抓取額度控制</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;padding:.55rem .75rem;border-radius:8px;background:var(--tag-bg);border:1px solid var(--border);">
<div style="flex:0 0 96px;font-weight:600;color:var(--accent);">Fetch</div>
<div style="flex:0 0 auto;font-size:.75rem;background:var(--bg);border:1px solid var(--border);border-radius:999px;padding:.05rem .5rem;">≤15 個 agent</div>
<div style="flex:1 1 180px;color:var(--text);">每個來源抽出可查證的主張</div>
</div>
<div style="text-align:center;font-size:.76rem;color:var(--text-secondary);padding:.2rem 0;">↓ &nbsp;同步點 (barrier): 等所有主張到齊，排序取前 25</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;padding:.55rem .75rem;border-radius:8px;background:var(--tag-bg);border:1px solid var(--border);">
<div style="flex:0 0 96px;font-weight:600;color:var(--accent);">Verify</div>
<div style="flex:0 0 auto;font-size:.75rem;background:var(--bg);border:1px solid var(--border);border-radius:999px;padding:.05rem .5rem;">25 × 3 個 agent</div>
<div style="flex:1 1 180px;color:var(--text);">每個主張三個找碴 agent，2/3 反駁就淘汰</div>
</div>
<div style="text-align:center;font-size:.76rem;color:var(--text-secondary);padding:.2rem 0;">↓ &nbsp;純 JS: 計票 + 篩出存活的主張</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;padding:.55rem .75rem;border-radius:8px;background:var(--tag-bg);border:1px solid var(--border);">
<div style="flex:0 0 96px;font-weight:600;color:var(--accent);">Synthesize</div>
<div style="flex:0 0 auto;font-size:.75rem;background:var(--bg);border:1px solid var(--border);border-radius:999px;padding:.05rem .5rem;">1 個 agent</div>
<div style="flex:1 1 180px;color:var(--text);">合併、排序、附出處，輸出報告</div>
</div>
<div style="font-size:.76rem;color:var(--text-secondary);padding:.55rem .2rem 0;line-height:1.5;">藍底方塊 = 模型判斷 (<code>agent()</code>)，中間的連接線 = 寫死的 JavaScript 編排。判斷交給模型，串接交給程式。</div>
</div>

<p style="text-align:right;font-size:.8rem;color:var(--text-secondary);margin-top:-.8rem;">✏️ 小編製圖</p>

<p>逐段拆:</p>

<p><strong>Scope (1 個 agent):</strong> 把使用者的問題交給一個 agent，要它拆成 5 個互補的搜尋角度 (廣度、學術、近期新聞、反方、實作面)，並用 schema 強制回傳結構化 JSON，不准自由發揮。</p>

<p><strong>Search → Fetch (用 <code class="language-plaintext highlighter-rouge">pipeline</code>，不等齊):</strong> <code class="language-plaintext highlighter-rouge">pipeline()</code> 跟 <code class="language-plaintext highlighter-rouge">parallel()</code> 的差別是「要不要等所有人都跑完才往下」(後者是同步點/barrier)。pipeline 不等，所以角度 A 搜完、去重、開始抓網頁的同時，角度 B 可能還在搜，不必等齊。每個角度: 一個 search agent 找 4-6 個結果 → 一段「純 JavaScript」做 URL 正規化、去重、抓取額度控制 → 再對每個沒看過的 URL 平行開一個 fetch agent 抽出主張。</p>

<p>去重、配額這些事完全不是 agent 在做，是普通的 JS:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">novel</span> <span class="o">=</span> <span class="nx">sorted</span><span class="p">.</span><span class="nf">filter</span><span class="p">(</span><span class="nx">r</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="nf">normURL</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">url</span><span class="p">)</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">seen</span><span class="p">.</span><span class="nf">has</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> <span class="p">{</span> <span class="nx">dupes</span><span class="p">.</span><span class="nf">push</span><span class="p">(...);</span> <span class="k">return</span> <span class="kc">false</span> <span class="p">}</span>    <span class="c1">// 看過了，丟掉</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">fetchSlots</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">relRank</span><span class="p">[</span><span class="nx">r</span><span class="p">.</span><span class="nx">relevance</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>      <span class="c1">// 額度用完，低相關的丟掉</span>
    <span class="nx">budgetDropped</span><span class="p">.</span><span class="nf">push</span><span class="p">(...);</span> <span class="k">return</span> <span class="kc">false</span>
  <span class="p">}</span>
  <span class="nx">seen</span><span class="p">.</span><span class="nf">set</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="p">...);</span> <span class="nx">fetchSlots</span><span class="o">--</span><span class="p">;</span> <span class="k">return</span> <span class="kc">true</span>
<span class="p">})</span>
</code></pre></div></div>

<p><strong>Verify (用 <code class="language-plaintext highlighter-rouge">parallel</code>，這裡刻意要同步等齊):</strong> 對排序後的前 25 個主張，每個都平行開「3 個」對抗式驗證 agent。它們的 prompt 開門見山就是「要懷疑、想辦法反駁這個主張」，而且「不確定就預設 refuted=true」。3 票有 2 票反駁，這個主張就淘汰。這正是前面講的對抗式驗證加上生成並篩選，而 self-preferential bias 在這裡被結構性擋掉了: 驗證的不是寫出主張的那個 agent，是另外三個被叫來找碴的 agent。</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">parallel</span><span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">({</span> <span class="na">length</span><span class="p">:</span> <span class="mi">3</span> <span class="p">},</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">()</span> <span class="o">=&gt;</span>
  <span class="nf">agent</span><span class="p">(</span><span class="nc">VERIFY_PROMPT</span><span class="p">(</span><span class="nx">claim</span><span class="p">,</span> <span class="nx">v</span><span class="p">),</span> <span class="p">{</span> <span class="na">schema</span><span class="p">:</span> <span class="nx">VERDICT_SCHEMA</span> <span class="p">})</span>
<span class="p">)).</span><span class="nf">then</span><span class="p">(</span><span class="nx">verdicts</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">refuted</span> <span class="o">=</span> <span class="nx">verdicts</span><span class="p">.</span><span class="nf">filter</span><span class="p">(</span><span class="nx">v</span> <span class="o">=&gt;</span> <span class="nx">v</span><span class="p">.</span><span class="nx">refuted</span><span class="p">).</span><span class="nx">length</span>
  <span class="k">return</span> <span class="p">{</span> <span class="p">...</span><span class="nx">claim</span><span class="p">,</span> <span class="na">survives</span><span class="p">:</span> <span class="nx">refuted</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="p">}</span>   <span class="c1">// 2 票反駁就淘汰</span>
<span class="p">})</span>
</code></pre></div></div>

<p><strong>Synthesize (1 個 agent):</strong> 把存活下來的主張交給一個 agent，合併語意重複、分組成一條條 finding、依信心高低排序、附上出處，產出一份有引用的報告。</p>

<p>看完這份腳本就會發現，所有「需要判斷」的事 (拆角度、搜尋、抽主張、反駁、整合) 都包在 <code class="language-plaintext highlighter-rouge">agent()</code> 裡; 而所有「不該讓模型亂猜」的事 (串接、去重、計票、排序、淘汰) 全是寫死的 JavaScript。一個 agent 偷懶或看走眼，只會壞掉它自己那一格，不會污染整條流水線。</p>

<h2 id="8-模型生成腳本但腳本本身是決定性的">8. 模型生成腳本，但腳本本身是決定性的</h2>

<p>這裡有個觀念要分清楚，也是整套設計最巧妙的地方: <strong>寫 workflow 的是模型，但 workflow 一旦寫好，執行就是決定性的。</strong></p>

<p>模型的判斷只發生在兩個地方: 一是「寫出這份腳本」的當下，二是每個 <code class="language-plaintext highlighter-rouge">agent()</code> 呼叫的內部。中間的迴圈、分支、去重、計票、合併，全是寫死的 JavaScript，跑幾次結果都一樣。所謂的「dynamic」，動的是「腳本被生出來」這一刻; 一旦生出來，它就是一份規規矩矩、可重播的程式。</p>

<p><a href="https://x.com/yi_ding/status/2060448752331268334">Yi Ding 的觀察</a>蠻精準的: 這東西本質上就是一個 (相當詳細的) prompt，教模型用 JavaScript 寫出一段像圖結構的編排描述。那段「教模型寫 workflow」的 prompt 大致在教這些 (示意，非逐字):</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>你可以寫一支 JavaScript 編排腳本，開頭必須是:
  export const meta = { name, description, phases }   // 純字面值，不能放變數或運算

腳本裡可以用這些函式:
  agent(prompt, { schema, label })  // 開一個 sub-agent;給 schema 就強制它回傳結構化 JSON
  pipeline(items, stage1, stage2)   // 每個項目獨立流過各階段，不等齊 ← 預設首選
  parallel(thunks)                  // 一次跑多個、等全部完成才往下 (barrier) ← 真的需要才用
  phase(title) / log(msg)           // 分組與進度回報

規則:
  - 預設用 pipeline，只有「下一階段真的需要上一階段全部結果」時才用 parallel
  - 想更可信就用對抗式驗證: 每個發現另外開 N 個 agent 試著反駁它
  - 不准用 Date.now()、Math.random()、無參數的 new Date()
</code></pre></div></div>

<p>最後那條規則小編覺得最特別。一個正常的 JavaScript 環境怎麼可能不給你取時間跟亂數? 但 workflow 偏偏把這幾個函式做成會直接報錯。原因藏在 resume 機制裡: workflow 會<a href="https://alexop.dev/posts/claude-code-workflows-deterministic-orchestration/">把每一個 <code class="language-plaintext highlighter-rouge">agent()</code> 呼叫的結果寫進一份執行日誌</a>，這樣中斷的執行才能原地接續 (已經跑完的 agent 直接從日誌裡回傳快取結果，只有沒跑完的才重跑)。一旦腳本裡摻了時間或亂數這種非決定性的東西，重跑時就會跟上一次對不起來，日誌裡的快取立刻失效。所以要時間戳記，就從 <code class="language-plaintext highlighter-rouge">args</code> 傳進來; 要讓每個 agent 長得不一樣，就用索引 (index) 去變化它的 prompt 或 label。</p>

<p>這個特別的限制也說明了整套設計的核心: 編排層必須是純粹、可重播的，模型的不確定性被嚴格關在 <code class="language-plaintext highlighter-rouge">agent()</code> 這個盒子裡。外面是一份能 resume、能重跑、能存檔重用的程式; 裡面才是會思考、會判斷、偶爾也會犯錯的模型。</p>

<p>也有幾個現實邊界值得先知道:</p>

<ul>
  <li><strong>成本</strong>: 一次跑幾十上百個 agent，token 成本明顯比一般對話高，官方建議先拿小範圍試水溫</li>
  <li><strong>不能中途插手</strong>: 執行中不能插入使用者輸入，需要人工核准的地方，得拆成兩個 workflow、中間留一個核准空檔</li>
  <li><strong>檔案修改自動核准</strong>: sub-agent 預設跑在 acceptEdits 模式，所以真正該把關的是腳本本身與 workflow 邊界</li>
  <li><strong>resume 有限制</strong>: 只保證在同一個 Claude Code session 內有效，關掉重開就是從頭跑</li>
</ul>

<h2 id="收束-這一切其實很符合-bitter-lesson">收束: 這一切其實很符合 Bitter Lesson</h2>

<p>ihower 之前在這個部落格寫過一篇 <a href="/2026/02/17/bitter-lesson-agent-harness/">Bitter Lesson 與 agent harness</a>，裡面點名一個「workflow trap」: 視覺化的 workflow builder 把任務拆解鎖死在一張固定的圖上，模型之後變強了，這張圖也不會自動受益，反而變成包袱。照 Bitter Lesson 的邏輯，把可靠性押在人工手刻的鷹架程式碼 (scaffold) 上，是把複雜度搬錯方向。</p>

<p>dynamic workflows 巧妙地閃過了這個陷阱。差別在於: <strong>拆解 workflow 的不是人，是模型，而且每個任務都重新寫一份客製的。</strong> 官方那句話講得最到位: Claude 現在可以「為手上的任務即時寫出自己的 harness」。你拿到了確定性編排帶來的可靠性 (避開偷懶、偏袒、漂移)，卻不必付出人工設計鷹架的代價，而那種鷹架正是模型一變強就第一個被淘汰的東西。所以這個設計是順著 Bitter Lesson 的: 連「編排」這件事本身，也交還給模型。</p>

<p>ihower 那篇的結論是: 原本覺得需要人工編排的 workflow 任務，幾乎都能丟給模型即時生成的 harness，不必再事先設計流程。dynamic workflows 基本上就是這個方向的具體實現。</p>

<h2 id="小編看法">小編看法</h2>

<p>最後小編補幾個比較主觀的想法。</p>

<h3 id="需要-gui-支援">需要 GUI 支援</h3>

<p>如果它就停在 CLI 這個形態，小編覺得採用門檻會偏高。原因是這套概念其實偏抽象: 計畫從上下文搬進腳本、決定性編排、對抗式驗證，這些好處都要想一層才懂，不容易一眼看懂。對多數使用者來說，體感上最直接的反而是「token 怎麼燒這麼兇」，那個爽點 (省下的上下文、避開的偷懶與漂移) 卻是隱形的，藏在你看不到的地方。</p>

<p>但如果把它做成 GUI，讓那份編排腳本變成一張動態的節點與連線圖，agent 怎麼分流、在哪裡匯合、哪一條審查鏈正在跑、哪個節點卡住了，全部即時長在眼前，採用率會高很多。因為「看見流程怎麼跑」這件事太有感了，它把隱形的價值變成你盯著看的畫面。</p>

<h3 id="希望同時支援事先生成和動態生成">希望同時支援事先生成和動態生成</h3>

<p>目前 Claude Code 是靠 <code class="language-plaintext highlighter-rouge">ultracode</code> 自動判斷這個任務要不要動用 workflow，而且每次都是當場重新編排一份。你雖然可以把喜歡的那份存下來、下次重用，但小編實測後發現，存下來的腳本常常把這一次執行的具體場景 (檔名、數量、路徑那些) 寫死進去，重用性其實沒那麼完美。更可惜的是，你沒辦法「先讓它把 workflow 的 JavaScript 生出來、自己檢查過、確認沒問題，再真正執行」，生成跟執行是綁在一起的。</p>

<p>其實這就是前面講的 LangChain interpreter skills 在做的事: 事先寫好一份確定性的編排腳本，存起來重用。小編希望 Claude Code 能兩種都支援: 對重複性高的任務，可以事先生成、檢視、微調一份 workflow 腳本再拿去跑; 對一次性的任務，維持現在每次動態生成的做法。</p>

<p>不過還是得說一下這兩種做法的優缺點:</p>

<ul>
  <li><strong>事先生成</strong>: 可檢視、可測試、可微調，對重複性任務更可控。但缺點是一旦把編排凍結下來，模型下一版變強了，這份腳本也不會自動受益，反而可能變包袱，這正是前面講的 Bitter Lesson 的風險。</li>
  <li><strong>動態生成</strong>: 每次針對當下任務量身定做，模型變強就自動受益，不會被舊腳本綁住。但缺點是你沒辦法事先檢查，生成跟執行綁在一起，而且存下來的腳本容易把場景寫死。</li>
</ul>

<p>或是有一種中間解: 執行前能把生成的腳本叫出來瞄一眼、改幾筆 (要不要看是你的自由，不強制)，存檔時多一步「幫我把這次的檔名、數量、路徑抽成參數」，讓重用的是「編排骨架」而不是「那一次的場景」。這樣既保住動態生成的彈性，又補掉現在重用會寫死的痛點。</p>

<h3 id="openai-的缺席">OpenAI 的缺席</h3>

<p>小編覺得比較可惜的是，整條脈絡最起點的 Code Interpreter，當年其實是 OpenAI 在 ChatGPT 上帶火的 (一度叫 Advanced Data Analysis)，是它先讓大家看到「讓模型寫程式碼、跑程式碼」有多好用。但後來把「程式碼當行動空間」一路往 agent 編排推的，反而是 Cloudflare、Anthropic、LangChain 跟學界。OpenAI 在 Codex 產品層有做 subagents (平行 spawn 多個 agent)，但在 API 層、SDK、產品上一直沒有走「程式碼當行動空間」這個方向，沒有 PTC 等級的東西，Code Interpreter 裡的程式碼到現在還是碰不到 agent 的工具。</p>

<h2 id="補充比較-那-agent-teams-呢-小編沒那麼看好">補充比較: 那 agent teams 呢? 小編沒那麼看好</h2>

<p>跟 dynamic workflows 幾乎同期，Claude Code 還推了另一個平行化功能 <a href="https://code.claude.com/docs/en/agent-teams">agent teams</a> (目前還是 experimental、預設關閉)。</p>

<p>一句話講清楚差別: dynamic workflows 是「模型寫一份決定性腳本，編排一群各自獨立的 sub-agent」; agent teams 則是「開一個 team lead，底下生出好幾個<strong>完整的</strong> Claude Code 實例當隊友，隊友各有自己的上下文、能<strong>互相點對點傳訊</strong>、搶同一張 task list 的工作」。subagent 只會把結果回報給主 agent，彼此不講話; agent teams 的隊友則能互相討論、互相挑戰，你甚至可以分別對每個隊友下指令。</p>

<p><img src="/assets/images/code-act-to-dynamic-workflows-teams-vs-workflows.jpg" alt="Agent Teams vs Dynamic Workflows" />
<small>圖片來源: <a href="https://x.com/_catwu/status/2060054180379689074">Cat Wu (@_catwu)</a></small></p>

<p>小編在 <a href="/2026/05/19/multi-agent-anti-patterns-and-patterns/">《Multi-Agent 反模式與推薦模式》</a> 那篇整理過一個核心判斷: <strong>multi-agent 的價值是「並行覆蓋」，不是「分工」。</strong> 而 agent teams 的賣點剛好踩在這條線上，看你怎麼用，很容易滑到錯的那邊:</p>

<p>1️⃣ <strong>「角色分工」很容易滑進三省六部的幻覺。</strong> 官方範例那種「一個管 UX、一個管架構、一個當魔鬼代言人」「一個查安全、一個查效能、一個查測試」，如果是<strong>唯讀的審查或研究</strong>，那是健康的並行覆蓋，沒問題。但只要任務變成「每個隊友各認領一個模組去<strong>寫 code</strong>」，就掉進那篇點名的反模式: LLM 沒有人類的注意力上限，貼上「架構師」「QA」的角色標籤不會讓它更強，只會多出人為的推諉跟一層層的傳話漂移。</p>

<p>2️⃣ <strong>並行寫入的老問題它沒解，只是叫你自己閃。</strong> 官方文件自己就寫: 「兩個隊友改同一個檔案會互相覆蓋，請把工作切開、讓每個隊友各自負責不同的檔案。」避免衝突的責任，被原封不動丟回給你手動切。Devin、Cognition 的工程經驗早就講白了: <strong>平行寫入到現在還是行不通</strong>，可靠的做法是「單線寫入、其他 agent 只負責判斷」。</p>

<p>3️⃣ <strong>點對點通訊 = 通訊開銷 + 傳話遊戲。</strong> 隊友彼此直接傳訊 (官方範例還鼓勵它們「像科學辯論一樣互相反駁」)，agent 一多，訊息就多、漂移就多、也更難觀測。對照之下，orchestrator-worker (subagent、dynamic workflows 走的就是這條) 把溝通收斂到中心，反而更穩、也更好整合。那篇也提過，大廠 (Anthropic、OpenAI、Google) 實際用的都是 orchestrator-worker，不是角色扮演的流水線。</p>

<p>4️⃣ <strong>成本跟可靠性都還沒到位。</strong> 每個隊友是一個<strong>完整的</strong> Claude Code 實例 (不是輕量 subagent)，token 隨人數線性往上疊。multi-agent 普遍燒 3-10 倍，Anthropic 自家 research 系統甚至約 15 倍。而且三個 90% 正確率的 agent 串起來，整體正確率掉到 72.9%。目前它還掛著一排 experimental 限制: 不能 resume、task 狀態會 lag、關機很慢、一次只能帶一個 team、不能巢狀、lead 還不能換人。學界也才剛出一篇 <a href="https://arxiv.org/abs/2601.13295">CooperBench</a>，直接以「<strong>為什麼 coding agent 還當不了你的隊友</strong>」為題，點出它們缺的正是找共識、協調這種社會性能力。</p>

<p>另外一個觀察: agent teams 到現在還是 experimental、預設關閉，你得自己去設一個 <code class="language-plaintext highlighter-rouge">CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS</code> 環境變數才打得開。相比之下 dynamic workflows 直接上線、<code class="language-plaintext highlighter-rouge">ultracode</code> 隨叫隨用，兩者的成熟度差距從預設值就看得出來。</p>

<p>小編認為 agent teams 相對 dynamic workflows 的優勢並不明確。官方推薦的入門場景是唯讀的並行探索 (PR 審查、競爭假設除錯、查資料)，但 dynamic workflows 用決定性編排加上對抗式驗證，一樣能做這些事，而且更便宜、更可重播。agent teams 真正不同的地方是「你可以在執行中途跟個別隊友對話、調整方向」，這是 workflow (背景跑完才回來) 做不到的，但這個互動性是否值得額外的成本和複雜度，目前沒有明確的證據。一旦想拿它來「分工寫 code」，就撞進那篇反模式的失敗區。</p>

<div style="border:1px solid var(--border);border-radius:8px;overflow:hidden;margin:1.6rem 0;font-size:.86rem;">
<div style="display:flex;flex-wrap:wrap;align-items:center;background:var(--surface);padding:.55rem .8rem;font-weight:600;border-bottom:1px solid var(--border);">
<div style="flex:1 1 90px;">維度</div>
<div style="flex:1.4 1 150px;color:var(--accent);">Dynamic Workflows</div>
<div style="flex:1.4 1 150px;">Agent Teams</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;border-bottom:1px solid var(--border);">
<div style="flex:1 1 90px;">編排者</div>
<div style="flex:1.4 1 150px;">模型寫的決定性腳本</div>
<div style="flex:1.4 1 150px;">team lead 即時協調，隊友自己搶工作</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;border-bottom:1px solid var(--border);">
<div style="flex:1 1 90px;">寫入模型</div>
<div style="flex:1.4 1 150px;">收斂到腳本控管，偏單線</div>
<div style="flex:1.4 1 150px;">多個完整實例可同時寫，衝突要你自己切檔案</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;border-bottom:1px solid var(--border);">
<div style="flex:1 1 90px;">agent 間通訊</div>
<div style="flex:1.4 1 150px;">不互聊，結果回腳本</div>
<div style="flex:1.4 1 150px;">點對點互相傳訊</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;border-bottom:1px solid var(--border);">
<div style="flex:1 1 90px;">可重播 / resume</div>
<div style="flex:1.4 1 150px;color:var(--accent);">有 (執行日誌、禁時間亂數)</div>
<div style="flex:1.4 1 150px;">還不行 (experimental)</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;border-bottom:1px solid var(--border);">
<div style="flex:1 1 90px;">成本</div>
<div style="flex:1.4 1 150px;">高，但編排層零推理</div>
<div style="flex:1.4 1 150px;">更高，每個隊友一個完整實例</div>
</div>
<div style="display:flex;flex-wrap:wrap;align-items:center;padding:.6rem .8rem;">
<div style="flex:1 1 90px;">最適合</div>
<div style="flex:1.4 1 150px;">大規模、可決定性編排的覆蓋型任務</div>
<div style="flex:1.4 1 150px;">唯讀的並行探索 (審查 / 研究 / 競爭假設)</div>
</div>
</div>

<p style="text-align:right;font-size:.8rem;color:var(--text-secondary);margin-top:-.8rem;">✏️ 小編製圖</p>

<p>這兩者真正的差異在前面講的那些: 決定性與可重播、成本結構、通訊模型 (中心化 vs. 點對點)、以及並行寫入的可靠性。從這些維度看，dynamic workflows 目前成熟度明顯領先，而 agent teams 還在找它真正不可替代的場景。</p>

<h2 id="參考連結">參考連結</h2>

<ul>
  <li><a href="https://arxiv.org/abs/2402.01030">Executable Code Actions Elicit Better LLM Agents (CodeAct 論文)</a></li>
  <li><a href="https://github.com/huggingface/smolagents">smolagents (Hugging Face CodeAct 實作)</a></li>
  <li><a href="https://developers.openai.com/api/docs/guides/tools-code-interpreter">OpenAI Code Interpreter</a></li>
  <li><a href="https://blog.cloudflare.com/code-mode/">Cloudflare Code Mode</a></li>
  <li><a href="https://blog.cloudflare.com/code-mode-mcp/">Cloudflare Code Mode MCP Server</a></li>
  <li><a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling">Claude Programmatic Tool Calling (PTC)</a></li>
  <li><a href="https://www.langchain.com/blog/give-your-agents-an-interpreter">LangChain Deep Agents Interpreter</a></li>
  <li><a href="https://www.langchain.com/blog/interpreter-skills">LangChain Interpreter Skills</a></li>
  <li><a href="https://arxiv.org/abs/2512.24601">RLM 論文 (Recursive Language Models)</a></li>
  <li><a href="https://claude.com/blog/introducing-dynamic-workflows-in-claude-code">Claude Code Dynamic Workflows 官方 Blog</a></li>
  <li><a href="https://code.claude.com/docs/en/workflows">Claude Code Dynamic Workflows 官方文件</a></li>
  <li><a href="https://x.com/trq212/status/2061907337154367865">“A harness for every task” by trq212</a></li>
  <li><a href="https://x.com/voxyz_ai/status/2061782441606451381">Voxyz: 計畫從上下文搬進腳本</a></li>
  <li><a href="https://x.com/yi_ding/status/2060448752331268334">Yi Ding: workflow prompt 分析</a></li>
  <li><a href="https://alexop.dev/posts/claude-code-workflows-deterministic-orchestration/">alexop.dev: 決定性編排與 resume 機制</a></li>
  <li><a href="https://x.com/lateinteraction/status/2060078643133763839">Omar Khattab: Claude Code 終於是一個 RLM</a></li>
  <li><a href="https://x.com/a1zhang/status/2060071701879066626">Alex Zhang: 第一個被認真訓練成 RLM 的前沿模型</a></li>
  <li><a href="https://x.com/_catwu/status/2060054180379689074">Cat Wu: Agent Teams vs Dynamic Workflows</a></li>
  <li><a href="https://code.claude.com/docs/en/agent-teams">Claude Code Agent Teams 文件</a></li>
  <li><a href="https://arxiv.org/abs/2503.13657">Why Do Multi-Agent LLM Systems Fail? (MAST)</a></li>
  <li><a href="https://arxiv.org/abs/2601.13295">CooperBench: 為什麼 coding agent 還當不了你的隊友</a></li>
  <li><a href="https://huggingface.co/docs/smolagents/en/examples/multiagents">smolagents Multi-Agent 範例</a></li>
  <li><a href="/2026/02/17/bitter-lesson-agent-harness/">Bitter Lesson 與 agent harness (愛好 AI Blog)</a></li>
  <li><a href="/2026/05/19/multi-agent-anti-patterns-and-patterns/">Multi-Agent 反模式與推薦模式 (愛好 AI Blog)</a></li>
</ul>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Workflow" /><category term="Tool Use" /><summary type="html"><![CDATA[Claude Code 上週發表 dynamic workflows (官方文件)，大家第一反應大多是「終於能一次跑幾百個 sub-agent 了」。這當然很猛，但小編覺得更值得講的，是它背後那條技術脈絡: 從 2024 年初的 Code Act 一路長出來的。]]></summary></entry><entry><title type="html">Codex App 那些 CLI 做不到的 GUI 特色</title><link href="https://blog.aihao.tw/2026/06/05/codex-app-gui-features/" rel="alternate" type="text/html" title="Codex App 那些 CLI 做不到的 GUI 特色" /><published>2026-06-05T00:00:00+00:00</published><updated>2026-06-05T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/05/codex-app-gui-features</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/05/codex-app-gui-features/"><![CDATA[<p>最近 ihower 在 <a href="https://ihower.tw/blog/13658-aie-openai-codex">AI Engineer 看 OpenAI Codex 的筆記</a>裡提到一個觀察: 他把日常寫程式從 CLI 工具搬到了 Codex，而且更推薦桌面 App 版本而不是 CLI。小編覺得這個判斷蠻準的: 等到大家把人機互動的模式摸清楚之後，設計良好的 GUI 終究會超越純命令列的體驗。</p>

<p>OpenAI 對 <a href="https://openai.com/zh-Hant/codex/">Codex App</a> 的投入，確實比 CLI 下了更多功夫。這篇就來條列幾個「只有 GUI 才辦得到」的特色，很多都是 CLI 裡根本想像不到的玩法。主軸參考自 Jason Liu (jxnl) 的 <a href="https://jxnl.co/writing/2026/05/10/codex-maxxing/">Codex-maxxing</a>，加上近期幾波官方更新。各特色也附上<a href="https://developers.openai.com/codex/app">官方文件</a>連結方便深入。</p>

<h2 id="1-appshots-把畫面外的內容也一起餵進去">1. Appshots: 把畫面外的內容也一起餵進去</h2>

<p>按住左右兩顆 Command 鍵 (快捷鍵可改)，Codex 就會把你滑鼠所在的視窗截圖，自動塞進輸入框。</p>

<p>你會說這不就是截圖而已嗎? 不，不只是截圖而已，它<strong>連畫面上的文字，甚至畫面外的文字也會一起傳入</strong>，超猛。舉例來說，你在看一份 Google Doc 拍 appshot，已經捲出畫面外的內容也會被讀進去。這跟 computer use 一樣，拿得到 App 裡的完整文字，而不只是肉眼可見的那一塊。(官方文件: <a href="https://developers.openai.com/codex/appshots">Appshots</a>)</p>

<p>實務上很有感: 以前在瀏覽器看到一個 bug、在設計稿看到一個要實作的介面，得自己截圖貼上、再打字補充脈絡。現在一個快捷鍵就把完整上下文帶進去了。</p>

<h2 id="2-remote-control-手機或另一台電腦都能遠端操控">2. Remote control: 手機或另一台電腦都能遠端操控</h2>

<p>Codex 可以遠端控制跑在另一台電腦上的 Codex: 手機的 ChatGPT App 能遙控，桌面版的 Codex app 也能遙控另一台機器，連螢幕鎖定的狀態下也跑得動。iPhone 上的操作體驗做得相當完整，不只是看進度而已。</p>

<p>而且 Linux 上的 Codex 也能被遠端遙控，不一定要 Mac 裝 Codex app 才行。有趣的是官方文件沒寫這段，可能還算是個隱藏功能 🤣 指令是在那台 Linux 上跑 <code class="language-plaintext highlighter-rouge">codex remote-control</code>，它就會起一個 Codex server，然後你用 Mac 或手機上的 Codex app 就能去遙控這台機器。ihower 實測拿 Ubuntu Desktop 來遠端遙控、打開 Chrome 都沒問題，比 SSH connect 好用多了。</p>

<blockquote>
  <p>編按: 官方文件: <a href="https://developers.openai.com/codex/remote-connections">Remote connections</a>、公告 <a href="https://openai.com/index/work-with-codex-from-anywhere/">Work with Codex from anywhere</a>。文件主要寫 Mac/Windows 當 host，Linux 的 <code class="language-plaintext highlighter-rouge">codex remote-control</code> 走法目前沒明說。</p>
</blockquote>

<h2 id="3-browserchromecomputer-三種上網操作介面">3. $browser、@chrome、@computer: 三種上網/操作介面</h2>

<p>GUI 版把「讓 Agent 去操作介面」拆成三個情境:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">$browser</code></strong>: 側邊面板裡的內建瀏覽器。特別適合在開發網頁時用: 你跟 Codex 看著同一個正在跑的頁面，直接在元素上標註、留言、要求調整，它就照著改、即時重新整理給你看，是前端 UI 迭代的好搭檔。(官方文件: <a href="https://developers.openai.com/codex/app/browser">Browser</a>)</li>
  <li><strong><code class="language-plaintext highlighter-rouge">@chrome</code></strong>: 接你已登入狀態的 Chrome，跑以瀏覽器為基礎的工作流。值得一提的是它<strong>可以在背景同時跑多個分頁</strong>: 每個任務開一個 tab group，做完自動清掉，只在需要你 review 時才把分頁交還，所以你照常用瀏覽器它也不打架。適合在登入後的網站做 deep research、把資料大量搬進 CRM/CMS，或自動化內部後台的重複操作。(官方文件: <a href="https://developers.openai.com/codex/app/chrome-extension">Chrome extension</a>)</li>
  <li><strong><code class="language-plaintext highlighter-rouge">@computer</code></strong>: 用於那些只能透過桌面 GUI 才存在的工作。它一樣<strong>可以在背景跑</strong>: 交辦下去之後，agent 在桌面背景中執行，你可以繼續手邊的工作，互不干擾。能讓程式在背景操控其他 Mac app、又不破壞前景使用體驗，核心技術是 OS 層級的 sandbox: 每個 agent 都有自己獨立的滑鼠指標，可以平行跑。(官方文件: <a href="https://developers.openai.com/codex/app/computer-use">Computer use</a>)</li>
</ul>

<h2 id="4-語音輸入-在任何地方都能用快捷鍵口述">4. 語音輸入: 在任何地方都能用快捷鍵口述</h2>

<p>Jason 認為語音輸入的價值在於: 它能在想法被壓縮成精緻文字「之前」，先捕捉到那個粗略原始的版本。像「去找一下 Ben 在 Slack 提過的那個東西」這種帶著語感的指令，用打字反而會懶得寫完整。</p>

<p>而且這是 <strong>App 內建</strong>的，不用另外買 Whisper Flow 之類的語音輸入工具、也不用另外裝什麼。設好快捷鍵後，你在任意地方都能直接口述。</p>

<blockquote>
  <p>編按: 如果你用外接麥克風，記得去 Mac 的音量/麥克風設定改一下預設輸入裝置，不然會抓錯。</p>
</blockquote>

<h2 id="5-steering-與-queuing-它還在跑你就先打字">5. Steering 與 Queuing: 它還在跑，你就先打字</h2>

<p>情境是這樣: Codex 還在輸出、還在跑工具，你不必等它停下來才動。直接在輸入框打字送出，這則訊息會有兩種用法:</p>

<ul>
  <li><strong>Steering (插隊改方向)</strong>: 不等當前步驟做完，立刻打斷它、塞進新的指示。適合你看著它正往歪的方向走、想即時修正的時候,例如「這個改小一點」「不對，先別動資料庫」。</li>
  <li><strong>Queuing (排隊接著做)</strong>: 不打斷當前步驟，讓這則訊息排進佇列，等它把手上這步做完再接著執行。適合你已經想好下一步、想先交代起來放著，例如「跑完記得順手開個 PR」「接著把測試補上」。</li>
</ul>

<p>差別就在「現在就插話」還是「等它講完再講」。其實 CLI 也做得到，但你得記住快捷鍵才能正確切換這兩種行為; GUI 則是直接把選項攤在介面上讓你點選，一看就懂該按哪個，不用背指令。</p>

<h2 id="6-釘選-threads-長執行緒重新變成可行解">6. 釘選 threads: 長執行緒重新變成可行解</h2>

<p>Jason 為每一條他在意的重要工作流，都保留一條釘選的對話串，讓這些持久性執行緒隨手就在附近。這些 thread 會累積歷史與決策，變成耐久的紀錄，而不是用完即丟的對話。</p>

<p>有意思的是，「讓單一執行緒一直長期跑下去」過去常被視為 anti-pattern，但現在可以重新考慮這樣用。不過小編覺得這有個前提: 你心智上要很清楚自己會用到 sub-agents 來分流，不能把所有東西都硬塞進同一條主線。這點 Codex-maxxing 那篇講得蠻細的，值得一讀。</p>

<h2 id="7-fork-從任意一則-ai-輸出岔出一條新-thread">7. Fork: 從任意一則 AI 輸出岔出一條新 thread</h2>

<p>在 GUI 上你可以對之前任意一則 AI 的輸出點「fork」，當場拆出一條新的 thread，從那個點接著走別的方向，原本那條對話原封不動。</p>

<p>最常見的用法是岔題不弄亂主線: 你在一條 thread 裡正做著功能 A，半路發現一個 bug，與其在原本對話裡插一段把脈絡攪亂，不如直接從當下這則訊息 fork 出去，在新 thread 裡專心修 bug，而它先前累積的東西 (摸熟的 codebase、剛討論好的計畫) 全都帶著走。或者你剛跟它把一份計畫討論完，想分頭深入其中一塊、又不想動到計畫主線，fork 一下就有一條乾淨的分支。</p>

<p>其實 CLI 也有 <code class="language-plaintext highlighter-rouge">/fork</code>，但痛點在於很難指定「要從哪一則訊息岔出去」(社群裡就有人抱怨對話開頭都長得一樣，根本分不出該 fork 哪一條)。GUI 把整條 transcript 攤在眼前，你直接點那一則輸出就能 fork，分岔點一目了然，這種操作真的是要有圖形介面才順手。</p>

<h2 id="8-平行多工-一個視窗同時跑很多條任務">8. 平行多工: 一個視窗同時跑很多條任務</h2>

<p>Codex App 的介面左側欄就是一條 threads list (對話列表)，每一條 thread 是一個獨立的任務。你在同一個視窗裡可以同時跑多條 thread、各自獨立推進，左欄掃一眼就知道每條跑到哪、哪條已經跑完、哪條卡住要你處理。可以一邊讓它在背景重構某個模組，一邊在另一條 thread 修 bug，點一下左欄就切過去，互不干擾。</p>

<p>這在 CLI 上特別痛: 純命令列一個視窗就是一條對話，想平行就得自己開一堆終端機分頁、自己記哪個在做什麼。也因此市面上冒出一大票工具專門來補這個洞，像 <a href="https://cmux.com/zh-TW">cmux</a> 這種「同時驅動多個 coding agent」的調度工具，連 Claude Code 最近也補上了 <a href="https://code.claude.com/docs/en/agent-view">Agent View</a>，讓你從一個畫面派發、管理多條 session。Codex App 則是一開始就把平行多工內建在介面裡，不用外掛。</p>

<h2 id="9-thread-automations-排程喚醒回到同一條執行緒">9. Thread automations: 排程喚醒、回到同一條執行緒</h2>

<p>這個概念很像 openclaw 的 Heartbeat: 週期性的喚醒呼叫，依排程<strong>回到同一個 Codex 執行緒</strong>繼續推進，而不是每次都從頭開一個新的。</p>

<p>支援分鐘級的頻繁輪詢，也能設每日/每週的定時 check-in。它特別適合做回饋迴圈: 監看 pull request 留言、Google Docs 留言或 Slack 回覆，在你不在座位時持續推進周邊工作。寫 automation prompt 時要交代清楚「每次醒來該做什麼」「怎麼判斷有沒有重要發現」「何時該停下來問人」。(官方文件: <a href="https://developers.openai.com/codex/app/automations">Automations</a>)</p>

<h2 id="10-側邊面板-就地檢視各種工件">10. 側邊面板: 就地檢視各種工件</h2>

<p>這大概是 GUI 相對 CLI 最直覺的勝場。側邊面板讓你可以就地檢視 Markdown、試算表、資料表、文件和投影片，還有 terminal、瀏覽器、檔案瀏覽。</p>

<p>關鍵在於<strong>你和 Agent 看的是同一份工件</strong>: 不用中斷流程，就能檢查、標註、修訂。配合進階註記模式，甚至可以在內建瀏覽器裡直接拖拉、調整頁面元素並留批注，多條修改攢成一批一起送。對設計師和前端協作來說，再也不用截圖畫圈寫「這裡往左移 10px」了。git diff 的 code review 也一樣: 右側直接看變更、逐行留 inline 註解、挑 chunk 分段 commit，全程不離開 App。</p>

<h2 id="11-影像生成-在開發流程裡直接生圖">11. 影像生成: 在開發流程裡直接生圖</h2>

<p>這是 OpenAI 自家有影像模型 (GPT-Image-2) 才辦得到的整合: 你可以直接在對話裡叫 Codex 生成或編輯圖片，不用切到別的工具或另外接 API。大多數人最直接的用法，就是在做 UI 素材、banner、背景、插圖、遊戲 sprite sheet、投影片 mockup 時，能更穩定地一張接一張持續生成，要幾張生幾張、要微調再讓它改，整個過程都在同一條 thread 裡完成。</p>

<h3 id="進階玩法-先生-ui-圖再讓-codex-對照寫-code">進階玩法: 先生 UI 圖，再讓 Codex 對照寫 code</h3>

<p>再往前一步，OpenAI 的 dkundel 分享過一個更有意思的方向: 一般像 Claude 那種「用 AI 設計網頁」是直接生 code 把畫面長出來; 而 Codex 這裡可以反過來，先用 GPT-Image-2 生出一張 UI 設計圖，再讓 Codex 對照那張圖去產生對應的 code，先有視覺、再有實作。他的完整玩法是: 手上沒設計稿時先請 GPT-Image-2 生一張 UI，當成規格交給 GPT-5.5 寫成真正的 app，過程中用內建瀏覽器驗證，還能順手生出 app 需要的素材來潤飾;「Build Web Apps」外掛就把這條迴圈包成一個 skill。小編還沒認真比較過這兩條路線 (直接生 code vs 先生圖再對照) 的優劣，但這個創新用法蠻值得玩玩看的。(<a href="https://x.com/dkundel/status/2049591675518165134">原文</a>)</p>

<h2 id="12-長任務-goals-用側邊面板盯一個跑很久的目標">12. 長任務 Goals: 用側邊面板盯一個跑很久的目標</h2>

<p><code class="language-plaintext highlighter-rouge">/goal</code> 這個功能本身 CLI 也有: 給一個目標，它就一路執行到完成，過程可能橫跨數小時甚至數天。GUI 的差別在於「怎麼看進度」可以做得很舒服。(官方文件: <a href="https://developers.openai.com/codex/use-cases/follow-goals">Follow goals</a>)</p>

<p>banteg 的用法很巧妙: 讓 goal 一邊跑、一邊產出一個 HTML 進度儀表板，直接用 Codex 內建瀏覽器開在側邊面板。左邊是 Agent 在做事，右邊是即時更新的圖表和指標 (完成度、匹配率、各 commit 的進展)，一眼就看到跑到哪了。</p>

<p><img src="/assets/images/codex-goal-dashboard.jpg" alt="goal 一邊跑，側邊面板開著即時更新的 HTML 進度儀表板" /></p>

<blockquote>
  <p>圖片來源: <a href="https://x.com/banteg/status/2054436210844528877">banteg on X</a></p>
</blockquote>

<p>另一招是 dotey 提到的 <a href="https://x.com/dotey/status/2058612576775229669"><code class="language-plaintext highlighter-rouge">/side</code></a>: 對一個跑很久的 goal，開一個 side chat 不影響主任務、又帶著完整上下文，直接問「目前進度如何? 還要多久?」。</p>

<p>要補一句 Jason 強調的: 目標的品質決定一切。弱的目標像「把這份 Markdown 實作出來」，強的目標帶著<strong>可衡量的成功標準</strong>。他做 Rich 轉 Rust 的遷移時，直接拿原本的測試套件當驗證標準。「執行的成效，頂多只會跟你給的目標與驗證一樣好。」沒有 oracle 的長任務，跑再久也只是漂移。</p>

<hr />

<p>回到最初的問題: 為什麼這些是 GUI 才辦得到的?</p>

<p>Jason 在 Codex-maxxing 裡點得很到位: 側邊面板讓 Codex「不再只是一個聊天 App，而開始變成工作真正發生的地方」。重點不只是 Codex 能產出工件，而是<strong>你能在不打斷迴圈的情況下，當場檢視並標註它</strong>。這正是 CLI 結構上做不到的: 命令列的輸出跑完就消散，你沒辦法跟 Agent 盯著同一份試算表、同一張投影片邊看邊改。GUI 把「對話」升級成「工作台」，這是純文字介面塞不進來的東西。</p>

<blockquote>
  <p>編按: Jason 另一篇 <a href="https://jxnl.co/writing/2026/05/18/six-levels-of-complexity-of-an-ai-powered-morning-brief-with-codex/">用 Codex 做晨間簡報的六個複雜度層級</a> 也很值得一讀。他用大家都懂的「晨間簡報」當例子，從最陽春的一句 prompt，一層層加上連接器、個人化預設、排程自動化、分專案執行緒、自動草擬回覆、持久記憶庫，示範怎麼把一個小工作流調教成「你工作的迷你作業系統」。核心心法是: 先從新手也懂的版本開始，每次只加一個真正的能力，直到整個系統的形狀自己浮現出來。</p>
</blockquote>

<p>還有一點別忘了: 對非工程師來說，CLI 的上手門檻其實還是偏高。要開終端機、要記指令、要懂一堆參數，光是這關就把很多設計師、PM、行銷的人擋在外面了。Appshots、就地標註、點選式的操作這些 GUI 設計，等於把參與 AI 開發流程的門檻一口氣拉低，讓不寫 code 的人也能直接上手。</p>

<p>CLI 仍然有它輕快、可組合、好自動化的價值，而且可以跑在沒有圖形介面的 Linux server 上，方便接進 CI、排程或各種自動化腳本，這是 GUI 取代不了的。而 Codex App 特別用心經營的，是<strong>人機協作</strong>: 把人跟 Agent 並肩工作的每個環節都做得更順手。當協作的對象從工程師擴大到設計師、PM、各種角色時，一個夠好的 GUI 能裝下的東西，是命令列怎麼也塞不進來的。</p>]]></content><author><name>ihower</name></author><category term="Coding" /><category term="Tool Use" /><category term="Agent" /><summary type="html"><![CDATA[最近 ihower 在 AI Engineer 看 OpenAI Codex 的筆記裡提到一個觀察: 他把日常寫程式從 CLI 工具搬到了 Codex，而且更推薦桌面 App 版本而不是 CLI。小編覺得這個判斷蠻準的: 等到大家把人機互動的模式摸清楚之後，設計良好的 GUI 終究會超越純命令列的體驗。]]></summary></entry><entry><title type="html">Coding Agent 作為軟體優化器: 從 Autoresearch 說起</title><link href="https://blog.aihao.tw/2026/06/03/coding-agent-as-optimizer/" rel="alternate" type="text/html" title="Coding Agent 作為軟體優化器: 從 Autoresearch 說起" /><published>2026-06-03T00:00:00+00:00</published><updated>2026-06-03T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/03/coding-agent-as-optimizer</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/03/coding-agent-as-optimizer/"><![CDATA[<p>最近有兩篇文章，從完全不同的方向，指向一個新的使用範式: 一個 coding agent 可以不更新任何模型權重、不跑反向傳播，光靠反覆「改一段可編輯的程式碼 → 跑 → 看指標 → 留下變好的、丟掉變壞的」，就把一套系統越養越強。換句話說，coding agent 本身正在變成一種新的<strong>優化器 (optimizer)</strong>。</p>

<p>一篇是 Karpathy 的 <a href="https://github.com/karpathy/autoresearch">autoresearch</a>: 把一個迷你的神經網路訓練腳本丟給 agent，讓它整夜自己跑幾百個實驗、自己調出更好的模型。另一篇是 EnvPool 作者、OpenAI 研究工程師 Jiayi Weng (翁家翌) 的 <a href="https://trinkle23897.github.io/learning-beyond-gradients/">Learning Beyond Gradients</a>: 完全不訓練神經網路，讓 Codex 反覆改純規則的程式碼，居然把 Atari、機器人控制這些任務做到逼近 Deep RL 的水準。</p>

<p>兩篇放在一起看才有意思: 它們是同一個範式的兩個極端代表作。小編這篇就把這個範式講清楚，順帶整理社群已經跑出來的一票案例，還有一個最關鍵的但書: 這招的命脈到底卡在哪。</p>

<h2 id="autoresearch-讓-agent-整夜自己做研究">autoresearch: 讓 agent 整夜自己做研究</h2>

<p>先看 Karpathy 這邊。autoresearch 的設定極簡: 把 nanochat (他那套 GPT-2 等級的訓練程式) 砍成單張 GPU、約 630 行的單檔版本，然後分工:</p>

<ul>
  <li><strong>人類</strong>只改 <code class="language-plaintext highlighter-rouge">program.md</code> (一份用白話寫的指示，告訴 agent 該往哪個方向探索)。</li>
  <li><strong>AI agent</strong> 只改 <code class="language-plaintext highlighter-rouge">train.py</code> (模型架構、優化器、所有超參數)。</li>
</ul>

<p>接著 agent 進入一個自主迴圈: 每次改一點訓練程式，跑一次<strong>固定 5 分鐘</strong>的訓練，用驗證集的 bits-per-byte (可以理解成驗證損失) 當分數，比上一版好就 git commit 留下、不好就丟掉，然後接著試下一個。<a href="https://x.com/karpathy/status/2030371219518931079">Karpathy 形容得很傳神</a>: 圖上每一個點都是一次完整的 5 分鐘訓練，agent 在一條 git 分支上不斷累積 commit，目標是「設計你的 agent，讓它能無限期、在你完全不插手的情況下，跑出最快的研究進度」。</p>

<p>讓它行得通的幾個設計約束很值得抄:</p>

<div style="border:1px solid var(--border); border-radius:8px; overflow:hidden; margin:1.5em 0; font-size:.92em;">
<div style="display:flex; background:var(--surface); font-weight:600;">
<div style="flex:1 1 32%; padding:10px 12px; border-right:1px solid var(--border);">約束</div>
<div style="flex:1 1 68%; padding:10px 12px;">為什麼重要</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 32%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">固定 5 分鐘預算</div>
<div style="flex:1 1 68%; padding:10px 12px;">不管 agent 改了什麼，每次實驗都直接可比，約每小時 12 個、整夜 100 個。</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 32%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">只准改一個檔</div>
<div style="flex:1 1 68%; padding:10px 12px;">agent 只動 train.py，資料準備和評估鎖死，改動小、好審查、不會偷改評分。</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 32%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">git 當記憶</div>
<div style="flex:1 1 68%; padding:10px 12px;">每個實驗是一個 commit，agent 讀分支歷史就知道試過什麼、什麼有效，不會無限鬼打牆。</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 32%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">好壞二選一</div>
<div style="flex:1 1 68%; padding:10px 12px;">用一個明確指標自動判斷留或丟，不需要人介入下判斷。</div>
</div>
</div>

<p>順帶看一眼那份 <a href="https://github.com/karpathy/autoresearch/blob/master/program.md">program.md</a> 到底寫了什麼，就懂為什麼說「人在編程那個研究員」。Karpathy 原版的內容，其實就是一份給 agent 的操作手冊:</p>

<div style="border:1px solid var(--border); border-radius:8px; background:var(--surface); padding:14px 18px; margin:1.5em 0; font-size:.9em; line-height:1.7;">
<div style="font-weight:600; margin-bottom:10px; color:var(--accent);">📄 program.md (給 agent 的操作手冊)</div>
<div style="margin-bottom:7px;"><strong>目標</strong>: 在單張 GPU、固定 5 分鐘的訓練窗內，把 <code>val_bpb</code> 壓到最低</div>
<div style="margin-bottom:7px;"><strong>能改什麼</strong>: 只准改 <code>train.py</code>; <code>prepare.py</code> (資料、tokenizer、評估) 和相依套件一律不准動</div>
<div style="margin-bottom:7px;"><strong>每一輪怎麼跑</strong>: 在 git 分支上改 <code>train.py</code> → commit → 跑 <code>uv run train.py</code> → 從 log 撈出 <code>val_bpb</code> → 比上一版好就留、不好就 <code>git reset</code></div>
<div style="margin-bottom:7px;"><strong>怎麼記錄</strong>: 每次試驗 (commit、分數、峰值記憶體、keep/discard/crash、一句描述) 寫進 <code>results.tsv</code> (不進 git)</div>
<div style="margin-bottom:7px;"><strong>出錯怎麼辦</strong>: 讀 log 最後 50 行，能簡單修就修、否則記下來跳過; 跑超過 10 分鐘直接 kill 當失敗</div>
<div><strong>態度</strong>: 不要停下來問人，自己一直跑到被打斷; 偏好簡單，靠複雜 code 換來的微小進步要丟掉</div>
</div>

<p>短短一份 markdown，就把工作流、邊界、記錄、復原、取捨標準全定義好了。</p>

<p>成果是真的有東西。Karpathy 自己在 nanochat 上跑了約 700 個實驗，<a href="https://x.com/karpathy/status/2031135152349524125">找到約 20 個真正有效的改進</a>，疊起來讓「練到 GPT-2 水準」的時間從 2.02 小時降到 1.80 小時 (快 11%)，而且 agent 抓到好幾個他自己漏掉的點 (例如 QKNorm 少了一個縮放、Value Embeddings 沒上正則化、注意力窗口開太保守)。更誇張的是 <a href="https://x.com/_philschmid/status/2031355349526012050">Shopify 執行長 Tobi Lütke 的例子</a>: 他叫 agent 讀 autoresearch 的程式庫、改寫成自己查詢擴展任務的版本，然後就去睡覺，醒來時一個 0.8B 的小模型在 8 小時跑了 37 個實驗後，分數比他之前的 1.6B 模型還高了 19%。一個小一半的模型贏過大的。</p>

<p><a href="https://x.com/karpathy/status/2031137476438548874">Karpathy 特別澄清</a>: autoresearch 不是一個你「拿來用」的工具，而是一個<strong>配方、一個想法</strong>。把它交給你的 agent，套用到你真正在乎的東西上就行。這句話是理解整個範式的鑰匙。</p>

<h3 id="真正該學的-約束越緊agent-越好用">真正該學的: 約束越緊，agent 越好用</h3>

<p>Manthan Gupta 有一篇 <a href="https://x.com/manthanguptaa/status/2032464949952598152">拆解 autoresearch 的好文</a>，把這套設計背後的原則講得很透。他的核心觀察很反直覺: <strong>最好的自主系統不是最自由的，而是約束最嚴格、目標最清楚、失敗成本最低的那種。</strong> 幾個能直接搬走的心得:</p>

<ul>
  <li><strong>約束讓 agent 更好，不是更差</strong>: 只能改一個檔、只追一個指標、只在分數變好時前進。很多 agent 系統反而死在太早給太多自由，自由越多、能出錯的面積就越大。</li>
  <li><strong>該優化的是 harness，不是模型</strong>: 一個普通的 agent 配上一套好的 harness (怎麼啟動、怎麼處理失敗、怎麼量進步、怎麼回滾爛路徑)，會贏過一個更強的 agent 配上一團亂的 harness。</li>
  <li><strong>prompt 就是架構的一部分</strong>: autoresearch 真正的控制面其實是那份 <code class="language-plaintext highlighter-rouge">program.md</code>，它定義了工作流、邊界、復原方式、取捨標準。Manthan 講得很妙: 人不只是在編程模型，而是在<strong>編程那個研究員</strong>。</li>
  <li><strong>可回復、可觀測不能妥協</strong>: 輸的實驗要能便宜地丟掉、每一步都能從 log 和 commit 追溯，agent 才敢大膽探索。</li>
</ul>

<h2 id="learning-beyond-gradients-把同一招推到不碰神經網路">Learning Beyond Gradients: 把同一招推到不碰神經網路</h2>

<p>Weng 這邊是從另一端逼近同一件事。他在業餘維護 EnvPool 時，本來只想要一個便宜的規則策略來測試遊戲環境，結果用 Codex (gpt-5.4) 寫純規則版本，效果離譜到讓他改變了想法:</p>

<ul>
  <li><strong>Atari Breakout</strong> (打磚塊): 分數一路 <code class="language-plaintext highlighter-rouge">387 → 507 → 839 → 864</code>，打到理論最高分。</li>
  <li><strong>MuJoCo Ant</strong> (四足機器人): 純 Python 策略先學會節律步態，再接上短視窗的模型規劃，衝到 <code class="language-plaintext highlighter-rouge">6000+</code>，進入常見 Deep RL 的量級。</li>
  <li><strong>VizDoom</strong> (第一人稱射擊): 只用 cv2/NumPy 處理螢幕影像、完全不訓練網路，也能拿到像樣的分數。</li>
</ul>

<p>關鍵不在分數，而在: Codex 不是在訓練網路，而是在維護一套「還能繼續長大」的軟體系統。最後的 Breakout 策略遠遠超過「球在左邊就往左」這種一行規則，它長出了動作探測、狀態讀取、落點預測、卡死偵測、回歸測試、影片回放、實驗記錄。</p>

<h3 id="什麼是-heuristic-learning">什麼是 Heuristic Learning</h3>

<p>Weng 把這個過程命名為 <strong>Heuristic Learning (HL，啟發式學習)</strong>，維護的對象叫 <strong>Heuristic System (HS，啟發式系統)</strong>。它跟 Deep RL 一樣，都有「狀態 → 動作 → 回饋 → 更新」這個閉環; 差別在於: Deep RL 更新的是神經網路權重，HL 更新的是軟體結構，而且改動由 coding agent 直接改程式碼完成，不走反向傳播。</p>

<div style="border:1px solid var(--border); border-radius:8px; overflow:hidden; margin:1.5em 0; font-size:.9em;">
<div style="display:flex; background:var(--surface); font-weight:600;">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border);">維度</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border);">Deep RL</div>
<div style="flex:1 1 38%; padding:10px 12px;">Heuristic Learning</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">策略</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border); color:var(--text-secondary);">神經網路參數</div>
<div style="flex:1 1 38%; padding:10px 12px;">程式碼: 規則、狀態機、控制器、MPC</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">更新方式</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border); color:var(--text-secondary);">梯度下降改參數</div>
<div style="flex:1 1 38%; padding:10px 12px;">coding agent 直接改程式碼</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">回饋</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border); color:var(--text-secondary);">主要是固定的獎勵</div>
<div style="flex:1 1 38%; padding:10px 12px;">測試、日誌、影片、回放都算</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">記憶</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border); color:var(--text-secondary);">on-policy 幾乎沒有</div>
<div style="flex:1 1 38%; padding:10px 12px;">明確存下試驗、失敗原因、版本差異</div>
</div>
</div>

<p>這裡有個容易誤會的地方要講清楚: 它的重點<strong>不是「叫 LLM 生成一堆規則」</strong>(那種事 FunSearch、Evolution of Heuristics 早就在做)，而是被維護的對象是<strong>一整套接在一起的軟體系統</strong>。Weng 講得很白:「單一規則是不夠的，規則、回饋、歷史、下一次更新的路徑都要連起來，才算是一個 HS。」</p>

<p>兩個細節最能說明差異。第一，<strong>更新改的是整個系統，不只是策略</strong>: Breakout 從 <code class="language-plaintext highlighter-rouge">387</code> 爬到 <code class="language-plaintext highlighter-rouge">864</code>，靠的不是「調返球準確度」，而是 Codex 看失敗影片、定位問題、加上「卡死偵測 + 軌跡擾動」這種新機制，再補回歸測試確保舊能力不退化，這已經是在做軟體工程。第二，<strong>「壓縮歷史」跟「吸收回饋」一樣重要</strong>: 只生成規則的系統只會越長越大、最後變成沒人敢碰的爛泥，健康的 HS 必須能把一堆局部補丁折回更簡單的表示。</p>

<p>把策略寫成可讀的程式碼，也帶來幾個 Deep RL 沒有的好處: 策略<strong>可解釋</strong> (規則能翻成白話)、<strong>樣本效率高</strong> (一次有效的修改就直接跳到新策略，不用慢慢爬學習率)、舊能力可以固化成<strong>回歸測試</strong>和黃金案例，因此能避開一部分<strong>災難性遺忘</strong> (舊能力不必只活在權重裡)。</p>

<h3 id="為什麼這招以前行不通">為什麼這招以前行不通</h3>

<p>HL 的祖先其實就是專家系統、規則系統，這些東西不是沒用，而是<strong>沒人養得起</strong>。Weng 描述得很傳神:</p>

<blockquote>
  <p>今天加一條規則修好情況 A，明天情況 B 又壞了，後天再補一個 if 判斷，再過幾天，沒人敢刪任何一行。</p>
</blockquote>

<p>人手維護規則系統，就像工業革命前用手紡紗: 一個人做得來，但規模一大、維護成本就把人壓垮。紡紗機改變的是生產曲線，coding agent 改變的正是規則系統的<strong>維護成本曲線</strong>，於是過去只能當一次性補丁的規則，現在開始變成值得長期擁有的程式碼。</p>

<p>不過 Weng 也誠實地說: coding agent 不會自動解決持續學習，它只是把「避免遺忘」變成一個更工程化的問題。新規則可能修好一個失敗卻弄壞舊場景、測試太窄會被鑽漏洞、規則一直疊加到 agent 自己都維護不動。所以問題只是換了個形狀: 從「怎麼更新神經網路參數」，變成「怎麼維護一套能持續吸收回饋、又不會爛掉的軟體系統」，這正是它接上持續學習 (continual learning) 的地方。</p>

<h2 id="同一個骨架-coding-agent-當外層優化器">同一個骨架: coding agent 當「外層優化器」</h2>

<p>把兩篇擺在一起，骨架就浮出來了。它們都是同一個迴圈:</p>

<blockquote>
  <p>coding agent 反覆「改一段可編輯的程式碼 → 跑 → 讀可驗證的指標 → 留好的、丟壞的」，過程中<strong>完全不更新模型權重</strong>。</p>
</blockquote>

<p>差別只在「那段可編輯的程式碼」是什麼。autoresearch 改的是訓練腳本，內層還是在訓練一個神經網路; HL 改的是策略程式本身，完全不碰神經網路。兩端對照起來特別清楚:</p>

<div style="border:1px solid var(--border); border-radius:8px; overflow:hidden; margin:1.5em 0; font-size:.9em;">
<div style="display:flex; background:var(--surface); font-weight:600;">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border);">維度</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border);">autoresearch</div>
<div style="flex:1 1 38%; padding:10px 12px;">Learning Beyond Gradients</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">agent 改的東西</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border); color:var(--text-secondary);">訓練腳本 train.py (架構、優化器、超參數)</div>
<div style="flex:1 1 38%; padding:10px 12px;">整套程式系統 (規則、偵測器、測試、記憶)</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">內層在優化什麼</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border); color:var(--text-secondary);">神經網路權重 (還是要訓練)</div>
<div style="flex:1 1 38%; padding:10px 12px;">程式邏輯本身 (完全不碰神經網路)</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">回饋訊號</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border); color:var(--text-secondary);">驗證集指標 (val loss)</div>
<div style="flex:1 1 38%; padding:10px 12px;">測試、獎勵、日誌、影片回放都算</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">記憶</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border); color:var(--text-secondary);">git commit 歷史當實驗筆記</div>
<div style="flex:1 1 38%; padding:10px 12px;">試驗記錄、版本差異、失敗方向</div>
</div>
<div style="display:flex; border-top:1px solid var(--border);">
<div style="flex:1 1 24%; padding:10px 12px; border-right:1px solid var(--border); font-weight:600;">交付物</div>
<div style="flex:1 1 38%; padding:10px 12px; border-right:1px solid var(--border); color:var(--text-secondary);">勝出的訓練設定 (一次性最佳實驗)</div>
<div style="flex:1 1 38%; padding:10px 12px;">持續維護的軟體系統 (長期擁有)</div>
</div>
</div>

<p>共同的前提只有三個: <strong>受限的編輯空間</strong>(只准改某個檔或某套程式)、<strong>可重現的執行環境</strong>(能一跑再跑)、<strong>穩定可驗證的指標</strong>(能自動判好壞)。只要這三樣齊備，coding agent 就能當外層優化器，在裡面爬分。</p>

<p>至於兩者的差別，不是對立，而是一條光譜的兩端。autoresearch 偏向「在固定框架內，搜尋出一次性的最佳實驗結果」，內層常常還是在訓練神經網路; HL 則把光譜推到底: 被優化的對象本身就是最終策略、完全不要神經網路，而且強調這套軟體系統要被長期維護。Karpathy 說 autoresearch 是個「可套用到任何你在乎的東西」的配方; Weng 等於就把它套用到了「軟體系統本身」這個極端上。有趣的是，社群早就注意到這層關係: 有人引 <a href="https://x.com/karpathy/status/2031135152349524125">Karpathy 的話</a>「任何你在乎、又能快速評估的指標，都可以丟給一群 agent 去 autoresearch」，還真的拿它去搜純數學問題 (Ramsey 數的下界)，完全沒有梯度。</p>

<h2 id="社群已經在跑-這是範式不是孤例">社群已經在跑: 這是範式，不是孤例</h2>

<p>autoresearch 在週末爆紅後，社群很快把這個迴圈套到各種東西上，而且越套越能看出它的通用性:</p>

<p>🔹 <strong>把這套做成現成工具: evo</strong>: evo-hq 團隊把這個優化迴圈包成一套現成工具 <a href="https://x.com/alokbishoyi97/status/2059610305408462898">evo</a>。它的形式是: 底層一個 CLI 引擎，再用 <code class="language-plaintext highlighter-rouge">evo install</code> 裝進你的 coding agent (Claude Code、Codex、Cursor 等)，在裡面就變成 <code class="language-plaintext highlighter-rouge">/evo:discover</code>、<code class="language-plaintext highlighter-rouge">/evo:optimize</code> 這類斜線指令。用起來是: 給它一個 codebase 和一個「更好」的指標，它就自己建評估、開沙箱平行跑實驗、留下變好的版本，還用樹狀搜尋 (保留不同切面上勝出的「專家」分支) 和「通過/不通過」的把關擋掉作弊刷分。</p>

<p>🔹 <strong>學術界其實同源已久</strong>: 這條路在研究界跑了好幾年。DeepMind 的 <a href="https://deepmind.google/blog/funsearch-making-new-discoveries-in-mathematical-sciences-using-large-language-models/">FunSearch</a> → <a href="https://deepmind.google/blog/alphaevolve-a-gemini-powered-coding-agent-for-designing-advanced-algorithms/">AlphaEvolve</a> 用「LLM 加自動評估器」的演化迴圈改程式碼，找到 56 年來第一個 4×4 複數矩陣乘法只需 48 次乘法的算法，導出的排程規則還幫 Google 資料中心持續省下 0.7% 的全球算力。NVIDIA 的 <a href="https://eureka-research.github.io/">Eureka</a> 用 GPT-4 寫獎勵函數、靠演化搜尋迭代，在 29 個任務裡 83% 贏過人類專家 (不過要注意它後端仍用 RL 訓練策略，只是把獎勵設計換成 LLM 生成)。Sakana 的 <a href="https://sakana.ai/dgm/">Darwin Gödel Machine</a> 讓 coding agent 改自己的程式碼、用基準測試驗證，把 SWE-bench 從 20% 一路自我演化到 50%。</p>

<p>🔹 <strong>最接地氣的實戰應用</strong>: 在 Weng 的留言區，VicaYang 分享了三個完全不是 RL 的 HS，最能說明這招在日常工程的用處: 一套<strong>音樂庫的歌曲資訊和歌詞整理系統</strong> (依不同國家、曲風套不同規則，再評估多來源品質合併)、一個<strong>數獨變體求解器</strong> (給規則和範例，幾輪迭代就生出來、自己一行程式碼沒寫)、以及一套<strong>處理電子郵件的 agent 系統</strong> (從正文和 Excel 附件抽結構化資訊，牽涉大量像「不同城市最低投保額不同」的客製規則)。他特別提到，連一個沒什麼工程經驗的學生，都能維護最後那套系統並持續改進它，這正是維護成本崩塌後的樣子。</p>

<h2 id="把優化-harness做成系統方法-meta-harness">把「優化 harness」做成系統方法: Meta-Harness</h2>

<p>上面 evo 是社群的玩法，<a href="https://yoonholee.com/meta-harness/">Meta-Harness</a> (論文名字就叫 End-to-End Optimization of Model Harnesses) 則把同一件事做成一套有系統的研究方法，值得單獨看它怎麼搜。</p>

<p>它要優化的「harness」，指的是一個 LLM 系統除了模型權重以外的整層外圍程式: 系統 prompt、工具定義、判斷任務完成的邏輯、context 管理、檢索程式、各元件之間的編排。這些被寫成一份不綁特定任務的程式，可以套到不同領域上。</p>

<p>搜尋迴圈是這樣: 一個 agentic proposer (就是用 Claude Code) 透過檔案系統，用 grep、cat 去讀所有歷史候選的原始碼、分數、和<strong>完整的執行軌跡</strong>，診斷出是哪個 harness 決策造成失敗，然後提出一個新候選、在沒看過的任務上評測、把結果存回去，再重複。最關鍵的設計是它餵給 proposer 的脈絡量: 每一步最多給到 <strong>1000 萬個 token</strong> 的診斷資訊 (對照論文盤點的其他方法，最多只有 2.6 萬)，所以它不是看壓縮過的摘要，而是直接讀原始軌跡做「反事實診斷」，精準定位該改哪裡。搜尋本身是演化式的，例如文字分類那組就是跑 20 輪、每輪 2 個候選，留下表現好的當作下一輪的基底。</p>

<p>成效跨三個很不一樣的領域，正好說明它不綁定 coding: 文字分類上準確率贏 baseline 7.7 分、還省 4 倍 token; 檢索增強的數學推理在 200 題 IMO 級題目上 +4.7 分，而且<strong>同一份 harness 能直接套到五個沒見過的模型</strong>; agentic coding 的 TerminalBench-2 上，搭 Claude Haiku 4.5 的版本還拿下全榜第一。整套程式碼也<a href="https://github.com/stanford-iris-lab/meta-harness">開源了</a>，附了文字分類和 TerminalBench 兩個完整實驗。</p>

<h2 id="換個領域實地走一輪-doug-turnbull-的搜尋重排器">換個領域實地走一輪: Doug Turnbull 的搜尋重排器</h2>

<p>從訓練腳本、遊戲規則，一路到剛剛的 LLM harness，這個範式套過的領域已經夠雜了。最後再換一個多數工程師最熟、也最容易驗證的領域收尾: <strong>搜尋排序</strong>。會特別挑 <a href="https://softwaredoug.com/blog/2025/10/19/agentic-code-generation-to-optimize-a-search-reranker.html">Doug Turnbull</a> (《Relevant Search》作者) 這個例子，是因為他難得把整套迴圈的程式碼都公開了，最適合拿來看「實際上到底怎麼把 coding agent 接成一個優化器」。</p>

<p>先把他在解的問題講白，沒做過搜尋也能懂: 使用者打一個查詢 (例如「無線耳機」)，系統要把最相關的商品排到最前面，這個重新排序的步驟叫 <strong>reranking (重排序)</strong>; 排得好不好，用一個叫 <strong>NDCG 的分數</strong>衡量 (越高越好)，資料用的是 Amazon 的購物搜尋資料集。</p>

<p>Turnbull 的出發點是個很實際的成本問題: 直接讓 agent 逐筆判斷每個查詢該怎麼排，相關性確實能提升 15-30%，但每個查詢要燒掉 10 萬個以上的 token，貴到不可能上線。於是他把問題反過來: 與其每個查詢都即時呼叫一次 agent，不如讓 agent 在訓練階段，把它的排序判斷<strong>一次寫成一段可重複使用的程式碼</strong>，之後線上就只跑這段便宜的程式碼 (搭配便宜的 BM25 關鍵字搜尋)，不必每次都呼叫模型。</p>

<p>而 agent 改這段程式的迴圈，就是標準的「外層優化器」: 每輪改一小段重排邏輯、在資料集上算 NDCG，好就留、不好就還原。</p>

<p>而這其實就是整個範式的重點: 生成出來的那段程式，跑的時候<strong>完全不會再呼叫 LLM</strong>。它就是一段傳統的啟發式排序邏輯，用 BM25 分數 (經典的關鍵字相關性算法)、標題關鍵字比對、詞彙重疊、甚至簡單的語系判斷 (看到西班牙文字元就換個策略) 這些便宜又可解釋的訊號來決定順序。換句話說，agent 把原本要靠 LLM 逐筆推理的判斷，蒸餾成一段不需要模型、跑起來幾乎不花錢的規則程式。這正是這套範式划算的根本原因: 用昂貴的 agent 在「優化階段」想清楚一次，換來一個之後跑起來便宜、又能讀能改能測的成品。</p>

<p>最有代表性的是他踩的坑: 第二版讓 agent 自由迭代後，訓練集 NDCG 從 0.27 漂亮地爬到 0.35，卻<strong>嚴重過擬合</strong> (把訓練查詢背了起來，換一批就掉)。第三版補上三條護欄才真正泛化: 每次只能小改 (10 行內)、用第二個 LLM 檢查不准把具體查詢寫死、改動還必須在 agent 看不到的驗證集上也有進步。加上護欄後，完全沒看過的測試集才從 0.286 穩穩進步到 0.350 (約 +18%)。</p>

<p>這一輪完整示範了「coding agent 當優化器」長什麼樣，也剛好預告了下一段最關鍵的事: Turnbull 這套能不能成，最後全卡在那個 NDCG eval 上。</p>

<h2 id="最關鍵的但書-eval-才是命脈">最關鍵的但書: eval 才是命脈</h2>

<p>講到這裡，這個範式聽起來像免費午餐，但其實有個硬到不能再硬的瓶頸: <strong>那個「可驗證的指標」本身</strong>。當實驗能跑得比人快 100 倍，你的 eval (評估) 就變成整條迴圈的瓶頸，指標一旦能被鑽漏洞或外洩，模型就會在紙面上變強、上線就崩。</p>

<p>這點在 AI 產品經理 <a href="https://x.com/nurijanian/status/2035257434365976671">nurijanian 的實戰分享</a>裡寫得最深刻。他想用 autoresearch 改進自己的 skill，連跑三次才搞懂自己錯在哪:</p>

<div style="margin:1.5em 0;">
<div style="display:flex; gap:10px; flex-wrap:wrap;">
<div style="flex:1 1 30%; background:#ffebe9; border:1px solid #cf222e; border-radius:8px; padding:14px;">
<div style="font-weight:600; color:#cf222e; margin-bottom:6px;">第一次: 全自動</div>
<div style="font-size:.88em;">直接把 skill 丟給工具，連測試輸入和評分用的評分器 (judge) 都讓它自動生。分數很快上升，但 skill 其實越改越爛: 評分標準是機器憑空生的，沒有任何對「真正的失敗長什麼樣」的理解。</div>
</div>
<div style="flex:1 1 30%; background:#fff8c5; border:1px solid #9a6700; border-radius:8px; padding:14px;">
<div style="font-weight:600; color:#9a6700; margin-bottom:6px;">第二次: 接上 eval skill</div>
<div style="font-size:.88em;">用了更有系統的方法生測試輸入 (沿輸入空間的維度組合)，輸入確實變多元了，但他還是讓工具自己生、自己沒看過任何輸出，評分器也沒變好。</div>
</div>
<div style="flex:1 1 30%; background:#dafbe1; border:1px solid #1a7f37; border-radius:8px; padding:14px;">
<div style="font-weight:600; color:#1a7f37; margin-bottom:6px;">第三次: 自己先讀資料</div>
<div style="font-size:.88em;">他先親手讀過一輪輸出、做錯誤分析、把失敗歸納成分類、據此寫評分器再人工校準，然後才放 autoresearch 去跑。這次才真的有料。</div>
</div>
</div>
</div>

<p>他用 <a href="https://hamel.dev">Hamel Husain</a> 評估課程裡的「三道鴻溝」框架點破了關鍵: <strong>理解鴻溝</strong> (你以為系統在做什麼 vs 它實際在做什麼)、<strong>規格鴻溝</strong> (你想要什麼 vs 你的評分器量了什麼)、<strong>泛化鴻溝</strong> (在測試輸入上的表現 vs 在沒看過的輸入上的表現)。autoresearch 的優化迴圈能解的，其實<strong>只有第三道</strong>鴻溝; 而前兩道，得人自己親手讀資料才關得起來。他引課程裡那句很狠的話: 「如果你不願意定期親手看一些資料，那你在 eval 上就是在浪費時間。」最後他下的結論是: 「你沒辦法把『理解』自動化掉。總得有人去關上第一道鴻溝，而以我的經驗，那個人永遠是你。」</p>

<p>這套「先做錯誤分析、人工看資料、再談自動化」的順序很關鍵: 自動化迴圈很迷人，但它放大的是你的評估品質。評估對了，它幫你飛; 評估歪了，它只會幫你更快地往錯的方向衝。</p>

<blockquote>
  <p>編按: 關於這裡講的「錯誤分析 (error analysis)」，ihower 之前分享過一篇 <a href="https://ihower.tw/blog/12960-ai-evals-and-error-analysis">AI Evals 與 Error Analysis</a> 講得更完整，想深入的可以一讀。</p>
</blockquote>

<p><a href="https://kargarisaac.medium.com/one-line-of-code-41-better-memory-when-one-ai-agent-optimizes-another-da2396bc501b">Isaac Kargar 的一個實驗</a> 把這個陷阱演得更血淋淋。他用 Claude Code 去優化另一套 agent 的記憶系統，跑了十幾個實驗、複合分數一口氣 +41% (最大功臣只是把 <code class="language-plaintext highlighter-rouge">dspy.Predict</code> 換成 <code class="language-plaintext highlighter-rouge">dspy.ChainOfThought</code> 這一行修改)。但發完文他才驚覺: 把產出拿去跟 Claude Code 自己那套精簡的記憶一比，他的系統其實是在狂刷一堆可有可無的瑣事、分數漂亮卻更難用，<strong>eval 從一開始就量錯了東西</strong>，於是他回頭重建評分標準才修正方向。他還補了一個很反直覺的心得: 五個用「不要做 X」這種負面指示的實驗全部退步，因為 LLM 會變得過度謹慎，所以「<strong>要告訴 LLM 什麼是好，而不是叫它避免什麼</strong>」。</p>

<p><a href="https://langfuse.com/blog/2026-03-24-optimizing-ai-skill-with-autoresearch">Langfuse 的案例</a>則是更具體的一個例子。他們想用 autoresearch 優化自家一套 skill (負責把寫死在程式裡的 prompt 搬進 Langfuse 託管系統)，評分是拿它去跑 6 個難度遞增的測試專案、用「正確性 + 完整度 + 效率」的加權公式打分，分數在 14 次實驗裡從 0.35 一路爬到 0.824。看起來很漂亮，但細看才發現: agent 是靠<strong>砍掉測試沒考到的真功能</strong>在刷分。最經典的一刀是它把「執行前先讓使用者確認搬遷計畫」這道人工核准關卡整個移除: 因為在自動跑的測試環境裡根本沒有真人會去按確認，skill 只要保留這關就會永遠卡在等待、拿 0 分，agent 一把它刪掉，前三個測試案例就瞬間從 0.00 跳到 1.00。它還順手刪掉了測試完全沒涵蓋的子 prompt、trace 串接等真實功能。這已經不是「不小心量錯」，而是 agent 主動鑽指標的漏洞。所以這篇的標題乾脆叫「它逼我們把測試寫得更好」，Langfuse 的結論很簡單: 真正脆弱的一環不是優化器，而是那組評估，「沒被量到的東西，就會被砍掉」。</p>

<p><a href="https://www.cerebras.ai/blog/how-to-stop-your-autoresearch-loop-from-cheating">Cerebras 則補上</a>另一個更隱蔽的失效模式: 就算指標本身沒問題，只要<strong>監督太稀疏</strong>，迴圈一樣會慢慢偏離原本的目標。他們做的是模型壓縮: 先用靜態方法把模型從 717 GB 壓到 92 GB、塞進消費級顯卡之後，再交給 autoresearch 一個更激進的延伸目標: 動態地只用 20% 記憶體跑。結果放著讓它自己跑，agent 中途就擅自把問題換成了另一個:「這模型到底能砍掉多少、還維持 95% 以上的準確率?」擺到 12 小時後偏得更離譜。它用「遮蔽掉一部分 expert 子網路」的方式來衝分 (現在的大模型常把網路拆成很多 expert、每次只實際用到其中一部分)，但這種遮蔽只是在軟體層把元件停用、權重其實還躺在記憶體裡，根本沒省到半點記憶體 (真要省得做永久性壓縮)。弔詭的是它換的那題還真答了出來 (約 37% 的 expert 就能覆蓋 95% 的情境)，是個有用的發現，卻完全不是你要的: 原本的記憶體目標毫無進展、分數卻一直在動，量到的其實是空的。Cerebras 的解方也很直白: 任務要切細、要常驗、要把每個實驗隔離開，環境設計比你選哪個模型更關鍵。</p>

<p>這跟 HL 那邊的觀察完全互相印證。Weng 引的 Cardiverse 研究就指出，LLM 生成的規則品質參差不齊，真正的難題不是「能不能生成」，而是「能不能快又準地辨認出有用的部分」; Turnbull 那句「Agent 天生就想過擬合」也是同一件事。另一位中文圈的觀察者 <a href="https://x.com/timyangnet/status/2033436288679075840">timyangnet</a> 則總結得很到位: autoresearch 之所以有效，是因為它強制了<strong>紀律</strong> (每次只改一個變數、先有假設再做實驗) 和<strong>記憶</strong> (git 歷史就是實驗筆記)，而真正的模型是「人類設定方向與約束、帶來品味，agent 在邊界內做窮盡式探索」。自由太多和太少一樣糟。</p>

<h2 id="哪些專案適合這樣搞">哪些專案適合這樣搞?</h2>

<p>把兩篇的論點和上面這票案例放在一起，可以歸納出: 一個專案要適合「coding agent 當優化器」，需要三個條件<strong>同時成立</strong>。</p>

<div style="margin:1.5em 0;">
<div style="display:flex; gap:10px; flex-wrap:wrap;">
<div style="flex:1 1 30%; background:#dafbe1; border:1px solid #1a7f37; border-radius:8px; padding:14px;">
<div style="font-weight:600; color:#1a7f37; margin-bottom:6px;">① 回饋可自動驗證</div>
<div style="font-size:.88em; color:#1f2328;">有測試、獎勵、基準分數、可信的評分器能自動判好壞。這是最硬的門檻，也是上面整段在講的命脈。</div>
</div>
<div style="flex:1 1 30%; background:#dafbe1; border:1px solid #1a7f37; border-radius:8px; padding:14px;">
<div style="font-weight:600; color:#1a7f37; margin-bottom:6px;">② 目標可用程式碼表達</div>
<div style="font-size:.88em; color:#1f2328;">訓練腳本、策略規則、skill 檔、外圍框架設定，凡是「能寫成可編輯文字、又能被打分」的都行。</div>
</div>
<div style="flex:1 1 30%; background:#dafbe1; border:1px solid #1a7f37; border-radius:8px; padding:14px;">
<div style="font-weight:600; color:#1a7f37; margin-bottom:6px;">③ 狀態可重現可觀測</div>
<div style="font-size:.88em; color:#1f2328;">能重跑、能記日誌、能回放，每次只動一小塊，agent 才壓得住複雜度、改得動。</div>
</div>
</div>
</div>

<p>具體的甜蜜點，<a href="https://x.com/_philschmid/status/2031355349526012050">philschmid 那篇</a>列得很清楚: 搜尋排序、商品分類、醫療命名實體辨識、詐騙評分、合約抽取、意圖分類這類<strong>邊界明確、指標清楚、小模型或小程式幾分鐘就能跑完一輪</strong>的任務最適合。</p>

<p>除了這種一翻兩瞪眼的甜蜜點，還有一塊灰色地帶才是多數工程團隊近期真正用得上的:</p>

<div style="margin:1.5em 0;">
<div style="background:#fff8c5; border:1px solid #9a6700; border-radius:8px; padding:14px;">
<div style="font-weight:600; color:#9a6700; margin-bottom:6px;">△ 條件適合的灰色地帶</div>
<div style="font-size:.9em;">領域規則多、邊界明確，但 LLM 單靠 prompt 又解不好 (規則太多、例外狀況一直冒) 的實務系統，例如上面 VicaYang 的郵件處理、歌曲資訊清理。它本來不算這套方法的主場，但只要你能把人類處理者的回饋持續餵回去、改程式碼和記憶，它就會慢慢變成一套好用又好維護的系統。換句話說，不必是 Atari 或前沿研究，你那套寫了三年、沒人敢動的規則引擎或客服流程，可能就是最好的起點。</div>
</div>
</div>

<p>那哪些不適合? 兩篇也都劃了界。</p>

<div style="margin:1.5em 0;">
<div style="background:#ffebe9; border:1px solid #cf222e; border-radius:8px; padding:14px; margin-bottom:10px;">
<div style="font-weight:600; color:#cf222e; margin-bottom:6px;">✗ 需要從高維感知學表徵</div>
<div style="font-size:.9em;">例如 ImageNet 影像分類。以今天的認知，想像不出一個 agent 能寫純 Python、不靠神經網路就解決它，這是程式碼表達能力的天花板。</div>
</div>
<div style="background:#ffebe9; border:1px solid #cf222e; border-radius:8px; padding:14px;">
<div style="font-weight:600; color:#cf222e; margin-bottom:6px;">✗ 長程規劃加稀疏回饋加需要搜尋與記憶</div>
<div style="font-size:.9em;">Weng 用 Montezuma's Revenge 當反例: 無人介入的一次跑分雖然摸到 400 分，但那條路是 86 個巨集動作拼出來的開環執行 (路線事先排定，中途失敗不會重新規劃，一步偏掉整條就崩)。普通的規則狀態機表達不了這種需要動作對齊時機、失敗要能回復的長程路線。</div>
</div>
</div>

<p>而最致命的那條界線，還是回到上一段的但書: <strong>沒有可信的 eval，這整套就是在幫你更快地優化一個錯的目標</strong>。</p>

<h2 id="小編的觀察">小編的觀察</h2>

<p>小編覺得這個做法值得學習。過去想靠手寫一大堆策略、規則、或一份份難調的訓練腳本來解問題，光維護成本就高到沒人養得起，根本不會被當成一條認真的路; 但現在有 coding agent 幫你持續盯著失敗、改程式碼、補測試，這條原本被判死刑的老路，突然就變得可行了。</p>

<p>evo 那篇有個公式小編很喜歡: 一個 agent 系統的槓桿 = <strong>模型能力 × 好的框架 × 緊的驗證迴圈</strong>。過去只有第一項在規律地進步 (出新模型)，現在後兩項也開始動，而且是按<strong>你</strong>定義的目標、在<strong>你</strong>的資料上動。所以下次你看到一坨沒人敢碰的 if-else、一份過時的 skill、或一個從沒好好調過的訓練腳本，也許該想的不是急著重寫，而是: 能不能把它交給 coding agent 持續最佳化?</p>

<h2 id="參考資料">參考資料</h2>

<p><strong>核心兩篇</strong></p>

<ul>
  <li>Andrej Karpathy: <a href="https://github.com/karpathy/autoresearch">autoresearch</a> (含 <a href="https://github.com/karpathy/autoresearch/blob/master/program.md">program.md</a>)</li>
  <li>Jiayi Weng (翁家翌): <a href="https://trinkle23897.github.io/learning-beyond-gradients/">Learning Beyond Gradients</a></li>
</ul>

<p><strong>autoresearch 的延伸討論</strong></p>

<ul>
  <li>Karpathy: <a href="https://x.com/karpathy/status/2030371219518931079">迴圈圖解</a>、<a href="https://x.com/karpathy/status/2031135152349524125">約 20 個有效改進的成果</a>、<a href="https://x.com/karpathy/status/2031137476438548874">「這是配方，不是工具」</a></li>
  <li>Philipp Schmid: <a href="https://x.com/_philschmid/status/2031355349526012050">Tobi Lütke 案例與適用甜蜜點</a></li>
  <li>Manthan Gupta: <a href="https://x.com/manthanguptaa/status/2032464949952598152">拆解 autoresearch 的設計原則</a></li>
  <li>timyangnet: <a href="https://x.com/timyangnet/status/2033436288679075840">紀律與記憶的觀察</a></li>
</ul>

<p><strong>社群與學術案例</strong></p>

<ul>
  <li>evo: <a href="https://x.com/alokbishoyi97/status/2059610305408462898">alokbishoyi97 的介紹</a></li>
  <li>DeepMind: <a href="https://deepmind.google/blog/funsearch-making-new-discoveries-in-mathematical-sciences-using-large-language-models/">FunSearch</a>、<a href="https://deepmind.google/blog/alphaevolve-a-gemini-powered-coding-agent-for-designing-advanced-algorithms/">AlphaEvolve</a></li>
  <li>NVIDIA: <a href="https://eureka-research.github.io/">Eureka</a></li>
  <li>Sakana AI: <a href="https://sakana.ai/dgm/">Darwin Gödel Machine</a></li>
  <li>Stanford IRIS Lab: <a href="https://yoonholee.com/meta-harness/">Meta-Harness</a> (含<a href="https://github.com/stanford-iris-lab/meta-harness">程式碼</a>)</li>
  <li>Doug Turnbull: <a href="https://softwaredoug.com/blog/2025/10/19/agentic-code-generation-to-optimize-a-search-reranker.html">用 coding agent 優化搜尋重排器</a></li>
</ul>

<p><strong>評估 (eval)</strong></p>

<ul>
  <li>nurijanian: <a href="https://x.com/nurijanian/status/2035257434365976671">三道鴻溝的實戰心得</a></li>
  <li>Hamel Husain: <a href="https://hamel.dev">AI Evals 課程</a></li>
  <li>ihower: <a href="https://ihower.tw/blog/12960-ai-evals-and-error-analysis">AI Evals 與 Error Analysis</a></li>
  <li>Isaac Kargar: <a href="https://kargarisaac.medium.com/one-line-of-code-41-better-memory-when-one-ai-agent-optimizes-another-da2396bc501b">一行程式碼、+41% 背後的陷阱</a></li>
  <li>Langfuse: <a href="https://langfuse.com/blog/2026-03-24-optimizing-ai-skill-with-autoresearch">用 autoresearch 優化 skill 的踩坑</a></li>
  <li>Cerebras: <a href="https://www.cerebras.ai/blog/how-to-stop-your-autoresearch-loop-from-cheating">如何讓 autoresearch 迴圈不作弊</a></li>
</ul>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Coding" /><category term="Eval" /><summary type="html"><![CDATA[最近有兩篇文章，從完全不同的方向，指向一個新的使用範式: 一個 coding agent 可以不更新任何模型權重、不跑反向傳播，光靠反覆「改一段可編輯的程式碼 → 跑 → 看指標 → 留下變好的、丟掉變壞的」，就把一套系統越養越強。換句話說，coding agent 本身正在變成一種新的優化器 (optimizer)。]]></summary></entry><entry><title type="html">向量已死? Grep 萬能? 不，你需要的是「策展」一組檢索工具</title><link href="https://blog.aihao.tw/2026/06/03/is-grep-all-you-need/" rel="alternate" type="text/html" title="向量已死? Grep 萬能? 不，你需要的是「策展」一組檢索工具" /><published>2026-06-03T00:00:00+00:00</published><updated>2026-06-03T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/03/is-grep-all-you-need</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/03/is-grep-all-you-need/"><![CDATA[<p>最近一篇 PwC 的論文 <a href="https://arxiv.org/abs/2605.15184">Is Grep All You Need? How Agent Harnesses Reshape Agentic Search</a> 在 X 上吵得蠻兇的，戰火還燒到一個更聳動的問題: 向量檢索已死? 這標題擺明在挑釁: 都 2026 年了，Claude Code、Codex 這些命令列 agent 光靠 grep 加 bash 就能找到東西，那花大錢養 embedding 模型、維護向量資料庫的 RAG 一整套基礎設施，是不是可以直接拆掉了?</p>

<p>LlamaIndex 的 Jerry Liu 第一時間就 <a href="https://x.com/jerryjliu0/status/2056077617355522534">出來潑了一盆冷水</a>。小編覺得這個爭論很值得整理，因為它背後藏著一個更精準的判斷: grep 不是萬靈丹，它只在「高訊號關鍵字」的場景才真的好用，而那個場景剛好就是寫程式。</p>

<h2 id="論文到底發現了什麼">論文到底發現了什麼</h2>

<p>先給論文該有的肯定。它做的事情很實在: 把 grep 和向量檢索兩種工具，接到四種不同的 agent harness 上(自製的 Chronos，以及 Claude Code、Codex、Gemini CLI)，拿 LongMemEval 這個長期記憶基準測試的 116 題來跑，看誰準。</p>

<p>結論有兩層:</p>

<p>🔹 <strong>第一層 (標題黨那層)</strong>: 在「行內回傳」(inline)模式下，grep 的準確率在每一組 harness 與模型的配對上，都贏過向量檢索。最大的差距是 Gemini Flash-Lite 在 Chronos 上的 86.2% 對 62.9%。而且在第二個實驗裡，當你不斷往上下文裡塞無關的干擾對話，向量檢索的準確率會往下掉，grep 卻幾乎不動，因為純字串比對天生免疫於向量空間裡的「距離漂移」。</p>

<p>🔹 <strong>第二層 (作者真正想講的)</strong>: harness 比演算法更重要。同一個 Claude Opus 4.6，在 Chronos 上 grep 能到 93.1%，換到 Claude Code 上只剩 76.7%。換 harness 造成的落差，跟換檢索演算法的落差差不多大。更誇張的是，改用「檔案式」(programmatic，把結果寫進檔案、讓 agent 自己去讀)的回傳方式，原本 grep 的優勢直接被抹平、甚至反轉: Codex 配 GPT-5.4 從行內 grep 的 93.1%，崩到檔案式 grep 的 55.2%。</p>

<blockquote>
  <p>編按: 所以這篇論文真正的重點不是「embedding 已死」，而是「你花在 harness 設計上的心力，應該遠多於花在挑哪個向量資料庫」。grep 只是眾多旋鈕裡的一個，而且通常不是最關鍵的那顆。</p>
</blockquote>

<h2 id="第一個關鍵-它測的是對話記憶不是企業文件">第一個關鍵: 它測的是「對話記憶」，不是企業文件</h2>

<p>Jerry Liu 的批評一針見血。LongMemEval 測的是「翻找使用者過往的聊天紀錄」: 明確的日期、數字、偏好、使用者講過的話。這種題目的答案，往往就藏在某幾段「逐字、原封不動」的文字裡(論文稱為 literal spans，字面證據)。</p>

<p>這正是 grep 的主場。你要找的東西本來就是一個精確的字串，當然是字串比對贏。</p>

<p>但企業 RAG 的典型場景完全不是這樣: 對著一堆財報、法律合約、SOP 文件問複雜問題。這些文件的語言是改寫過的、術語不一致的、偏概念的。LlamaIndex 後來也寫了 <a href="https://www.llamaindex.ai/blog/is-grep-all-you-need-lexical-vs-sematic-search-for-agents">一篇完整回應</a>，把 grep 在這種語料上的硬傷講得更白: 你 grep 不了 PDF、掃描影像、Office 檔;語料一上百萬份檔案，純線性掃描就開始吃力;更要命的是「詞彙對不上」: 使用者問的是「營收認列」，文件裡寫的卻是會計準則代號「ASC 606」，字面比對直接一無所獲。他們開的藥方是「分層處理」: 先用讀得懂版面的工具把文件解析出來、建索引做語意檢索，grep 只留著當精確比對的後援。</p>

<p>連論文作者自己都很誠實，在「限制」(Limitations)那一節白紙黑字寫著:</p>

<blockquote>
  <p>在證據很少以字面形式出現的領域(例如對改寫過的論文摘要做綜整、大量圖表的文件、或程式碼語意)，向量檢索和混合路由的表現可能會很不一樣。我們並不主張 grep 在通用情況下「贏過」向量檢索。</p>
</blockquote>

<p>換句話說，論文自己都沒宣稱 grep 是通用解，是讀者(和那個標題)把它過度延伸了。</p>

<h2 id="第二個關鍵-grep-吃的是高訊號關鍵字">第二個關鍵: grep 吃的是「高訊號關鍵字」</h2>

<p>為什麼寫程式是 grep 的完美場景? 因為程式碼裡的識別字(函式名、變數名、錯誤碼)本來就是必須精確比對的關鍵字。你要找 <code class="language-plaintext highlighter-rouge">handleUserLogin</code> 這個函式，grep 一秒就給你所有出現的位置，零歧義。反過來，向量檢索在程式碼上一直做不好，因為它會把語意相近、但根本不是你要的東西也一起撈進來。</p>

<p>Sara Zan 在 <a href="https://www.zansara.dev/posts/2026-03-15-vector-dbs-vs-grep/">一篇實測文章</a> 裡補了一個「到了 agent 時代才成立」的觀察: 傳統 RAG 假設「查詢必須一次就對」，所以拼命優化單次召回的品質。但 agent 打破了這個限制，它可以先用一個很爛的查詢搜一次，看看結果，再改寫查詢搜第二次。這種「反覆迭代」剛好補掉了關鍵字搜尋最大的弱點(怕你措辭不對)，又保留了它精確的優點。</p>

<p>把這兩件事疊起來，grep 的甜蜜點就很清楚了: <strong>語料是純文字、查詢以高訊號的字面關鍵字為主(程式碼、log、識別字、日期、人名)、而且 agent 可以反覆迭代搜尋</strong>。中小型的程式碼庫幾乎完美命中這三個條件，難怪 Claude Code 這類工具用得這麼順。</p>

<div style="border:1px solid var(--border); border-radius:8px; padding:18px 20px; margin:24px 0; background:var(--surface)">
<div style="font-weight:600; margin-bottom:14px; color:var(--text)">訊號強度光譜: 你的場景適合誰?</div>
<div style="display:flex; flex-wrap:wrap; gap:12px">
<div style="flex:1 1 200px; border-radius:6px; padding:12px 14px; background:rgba(26,127,55,.10); border:1px solid rgba(26,127,55,.35)">
<div style="font-weight:600; color:#1a7f37; margin-bottom:6px">高訊號關鍵字 → grep</div>
<div style="font-size:.9em; color:var(--text-secondary); line-height:1.6">程式碼識別字、錯誤碼、log、SKU 或合約編號、法規條號、日期人名。要的就是精確字串，grep / BM25 完勝</div>
</div>
<div style="flex:1 1 200px; border-radius:6px; padding:12px 14px; background:rgba(154,103,0,.10); border:1px solid rgba(154,103,0,.35)">
<div style="font-weight:600; color:#9a6700; margin-bottom:6px">混合訊號 → 混合檢索</div>
<div style="font-size:.9em; color:var(--text-secondary); line-height:1.6">技術文件、產品手冊、客服知識庫。又有專有名詞、又有口語問法，單用任一邊都會漏，需要 BM25 與向量檢索並用</div>
</div>
<div style="flex:1 1 200px; border-radius:6px; padding:12px 14px; background:rgba(9,105,218,.10); border:1px solid rgba(9,105,218,.35)">
<div style="font-weight:600; color:var(--accent); margin-bottom:6px">低訊號語意 → 向量檢索</div>
<div style="font-size:.9em; color:var(--text-secondary); line-height:1.6">概念性問答、改寫過的長文、滿是同義詞的自然語言。「退款政策」要對到「money back guarantee」，只有 embedding 接得住</div>
</div>
</div>
<div style="font-size:.8em; color:var(--text-secondary); margin-top:12px; text-align:right">✏️ 小編製圖</div>
</div>

<h2 id="第三個關鍵-雙峰的查詢分布逼你走向混合檢索">第三個關鍵: 雙峰的查詢分布，逼你走向混合檢索</h2>

<p>前面說 grep 怕措辭、吃不了概念查詢。有趣的是，向量檢索有個剛好對稱的盲區: 它接不住精確的識別字。<a href="https://dev.to/gabrielanhaia/your-vector-database-is-not-a-search-engine-heres-why-thats-killing-your-rag-2db5">Your Vector Database Is Not a Search Engine</a> 這篇文章整篇就在打這個弱點，而它開出的藥方不是「改用 grep」，而是「混合檢索」。</p>

<p>它先點出一個容易被忽略的陷阱: 大家評估檢索效果都拿 BEIR、MTEB 這類基準測試，而它們清一色是「漂亮的自然語言問句」，在這種題目上向量檢索當然贏 BM25。但你把真實的查詢紀錄拉出來看，分布其實是雙峰的: 一半是對話式的問句，另一半是精確查詢(產品代碼、錯誤碼、內部工具名、版本號、專有名詞)，後者常常佔了超過三成的流量。而 <code class="language-plaintext highlighter-rouge">SKU-47291</code> 這種字串被 tokenizer 切成 subword 之後，根本不會落在它原本該在的位置，向量檢索只會回給你一堆「看起來很像、其實不是」的鄰居。</p>

<p>所以這篇的結論是: 把 BM25(抓精確關鍵字)和向量檢索(抓語意)平行跑，用 RRF(Reciprocal Rank Fusion，互補排名融合)把兩份結果合併，再加一個 cross-encoder reranker 重排。grep 怕措辭、向量怕識別字，兩邊剛好各補對方的盲區，這才是雙峰查詢唯一說得通的解。</p>

<p>這也解釋了為什麼產業正在快速倒向混合檢索。有資料顯示，企業導入混合檢索的意願，在 2025 年底短短一個季度內就從 10.3% 跳到 33.3%;而「超長上下文會讓檢索變得多餘」這個說法，則從 15.5% 崩到 3.5%。大家都是踩了坑才學乖的。</p>

<h2 id="更好的框架-別找銀彈要策展一組工具">更好的框架: 別找銀彈，要「策展」一組工具</h2>

<p>Elastic 的 Leonie Monigatti 最近在 <a href="https://maven.com/p/1c532b/agentic-search-for-context-engineering">一場 Maven 演講</a>(內容也整理成 <a href="https://leoniemonigatti.com/blog/agentic-search-for-context-engineering.html">一篇文章</a>)裡，把這件事講得更透徹。她有個大膽的觀點: 「上下文工程大概有八成，其實就是 agentic search(代理式搜尋)。」你以為自己在做的是上下文策展，但底層真正在運作的，是一堆搜尋工具。她還畫了一條清楚的演進線: 早期的 RAG 是固定流程，每次都只檢索一次、又無法自我修正;agentic RAG 把它換成一個搜尋工具，讓 agent 自己決定要不要搜、要搜幾輪;到了上下文工程的時代，來源變多了(本地檔案、資料庫、網路、記憶)，自然就需要好幾種不同的搜尋工具並用。</p>

<p>她現場示範了一個很傳神的畫面: 只給 agent 一個 shell 工具加上一堆本地檔案，然後丟一個需要語意理解的問題給它。結果 agent 用 grep「作弊」、硬是假裝在做語意搜尋: 自己腦補出一串同義詞(regulate、compliance、constraints、gdpr、governance)，連續 grep 好幾輪，最後居然也湊出了答案。能動是能動，但這真的是對的做法嗎?</p>

<p>她的結論，跟論文、跟前面那些反方文章，完全收斂到同一句話:</p>

<blockquote>
  <p>如果你在找一把萬能的銀彈搜尋工具，很抱歉要讓你失望了。目標不是找到那一把銀彈，而是「策展」(curate)出一組合適的搜尋工具，讓你的 agent 能應付各種不同的查詢。</p>
</blockquote>

<p>Elastic 在 <a href="https://www.elastic.co/search-labs/blog/search-tools-context-engineering">一篇延伸文章</a> 裡，把這個策展原則整理成「低地板、高天花板」(low floor, high ceiling):</p>

<div style="display:flex; flex-wrap:wrap; gap:14px; margin:24px 0">
<div style="flex:1 1 260px; border:1px solid var(--border); border-radius:8px; padding:16px 18px; background:var(--surface)">
<div style="font-weight:600; color:#1a7f37; margin-bottom:8px">低地板 · 專用工具</div>
<div style="font-size:.92em; color:var(--text); line-height:1.7">範圍窄，但超穩、超好用。像只吃一個主題字串的語意搜尋工具，或 <code>get_customer(id)</code> 這種查詢。讓 agent 高效解掉<strong>大多數的日常查詢</strong>，幾乎不會用錯</div>
</div>
<div style="flex:1 1 260px; border:1px solid var(--border); border-radius:8px; padding:16px 18px; background:var(--surface)">
<div style="font-weight:600; color:var(--accent); margin-bottom:8px">高天花板 · 通用工具</div>
<div style="font-size:.92em; color:var(--text); line-height:1.7">範圍廣、彈性大，但很吃模型的推理能力、也吃延遲。像 shell 工具，或讓 agent 自己寫一整句 SQL / ESQL 查詢的工具。拿來接<strong>複雜、模糊、開放式的問題</strong></div>
</div>
</div>

<p>大多數 agent 兩種都需要，重點是兩者混搭，而不是幻想單一工具能通吃。</p>

<p>那一組工具到底該放哪些? Elastic 給的是「用數據決定」的做法，而不是憑空設計: 先給一個通用工具上線，然後記錄 agent 的真實行為(它呼叫了什麼、重試幾次、在哪裡卡住失敗)。當某種查詢模式反覆出現、或反覆出包，再針對它打造一個專用工具。每個工具都得「賺到」自己被放進工具箱的資格。Claude Code 只配大約 20 個工具，而不是塞進幾百個 MCP 工具，就是這個哲學的體現。</p>

<h2 id="連-grep-陣營都在偷偷補語意能力">連 grep 陣營都在偷偷補語意能力</h2>

<p>這裡有一個很有意思的訊號: 連 grep 陣營自己都在偷偷補語意能力。最近冒出一票「語意版 grep」的命令列工具，目標都一樣: 在不架整套向量資料庫的前提下，讓 agent 直接對本地檔案跑自然語言搜尋。</p>

<p><a href="https://github.com/run-llama/semtools">LlamaIndex 的 semtools</a> 是用 Rust 寫的命令列工具，主打兩個指令: <code class="language-plaintext highlighter-rouge">parse</code> 先用 LlamaParse 把 PDF、Word 這些原本 grep 不了的格式轉成 markdown，<code class="language-plaintext highlighter-rouge">search</code> 再用 static embedding(model2vec)做「模糊的語意關鍵字搜尋」，而且 search 全程在本地跑。LlamaIndex 那篇介紹文的標題還故意取作 “SemTools: Are Coding Agents all you Need?”，擺明在跟這波 grep 風潮對話。</p>

<p><a href="https://github.com/jina-ai/jina-grep-cli">Jina 的 jina-grep</a> 則更貼近 grep 原本的使用習慣: 用 Jina embeddings v5、在 Apple Silicon 上透過 MLX 純本地跑。你可以直接用 <code class="language-plaintext highlighter-rouge">jina grep "error handling" src/</code> 以自然語言搜尋，也可以把傳統 grep 的輸出用管線丟給它做語意重排(<code class="language-plaintext highlighter-rouge">grep -rn "error" src/ | jina grep "retry logic"</code>)，等於幫 grep 接上一顆語意大腦。</p>

<p><a href="https://lighton.ai/lighton-blogs/lateon-code-colgrep-lighton">LightOn 的 ColGrep</a> 則是這幾個裡面最能呼應本文主旨的一個。它基於 late interaction(ColBERT，延遲交互)的 LateOn-Code 模型，包成單一個 Rust 執行檔、100% 本地、程式碼完全不外流。關鍵是它預設就是混合的: 先用 regex 像 grep 一樣縮小候選範圍，再用 ColBERT 做語意排序，兩份排名用 RRF 合併。LightOn 講得很直白: ColGrep 不是要取代 grep，而是「把 grep 的優點吸收進來、再往上擴充」，官方數據說它正面對決贏純 grep 約七成，還省下 15.7% 的 token。</p>

<p>三個工具擺在一起看，結論幾乎自己跳出來: 純字面比對確實有天花板，連最擁抱 grep 的寫程式場景，大家都在想辦法把語意能力搬回 shell 裡。而 ColGrep 那套「regex + 語意 + RRF」的預設行為，根本就是把「策展一組工具」直接內建成單一工具，等於幫你預先把混合檢索組好了。</p>

<h2 id="收斂成幾個實務判斷">收斂成幾個實務判斷</h2>

<p>把論文、Jerry Liu、Sara Zan、Elastic 全部疊起來，小編幫大家收斂成幾條:</p>

<p>1️⃣ <strong>別一聽到「搜尋」就反射性地架向量資料庫</strong>: 如果語料是中小型純文字、查詢又以字面關鍵字為主、agent 還能反覆迭代，grep 往往是更省事的起點，省下一整層基礎設施。</p>

<p>2️⃣ <strong>但也別反過來以為 grep 萬能</strong>: 它搜不了 PDF、圖片、Office 檔，搞不定同義詞和概念查詢，語料一大、延遲就線性上升。它的本質，是拿「延遲和 token」去換「彈性」。</p>

<p>3️⃣ <strong>檢索層的標準答案是混合檢索</strong>: 就是上面那套 BM25 加向量檢索、合併後再重排;遇到需要多 hop 的關係推理，再疊上 GraphRAG。在真實的企業語料上，混合幾乎都打贏任何單一方法。</p>

<p>4️⃣ <strong>agent 層的標準答案是策展一組工具</strong>: 低地板的專用工具，搭配高天花板的通用工具，而不是硬塞一個萬能介面。組合勝過任何單一工具，但工具數量要克制，免得撐爆上下文、又害 agent 選錯工具。</p>

<p>5️⃣ <strong>真正該花心力的地方是 harness</strong>: 這是論文最反直覺的發現。一個好的 harness，能讓模型反覆改寫查詢、查看片段、自己決定何時收手，就算配的是向量檢索，也可能贏過爛 harness 配 grep。檢索演算法只是其中一顆旋鈕而已。搜尋老兵 Doug Turnbull 在 <a href="https://softwaredoug.com/blog/2026/04/06/agentic-search-is-having-a-grep-moment">Agentic Search Is Having a Grep Moment</a> 裡進一步點出，harness 其實有兩層迴圈: 內層是 agent 自己改寫查詢、反覆迭代;外層則是一道由你定義的「程式化品質閘門」(他稱為 hook)，agent 搜完後拿結果去對領域標準把關(夠不夠新、夠不夠權威)，不合格就把具體理由回灌、叫它再搜一次。他的結論很犀利: grep 能在生產環境動起來，往往不是因為 grep 本身有多強，而是外面這層約束和驗證在替它扛品質。</p>

<p>回到那個挑釁的標題。Grep is all you need 嗎? 對中小型的程式碼庫，可能真的是;但只要場景一離開「高訊號關鍵字 + 純文字 + 可迭代」這個甜蜜點，答案就從「二選一」變成「怎麼搭配」。與其糾結該選 grep 還是向量檢索，不如先想清楚自己的資料長什麼樣、查詢分布如何、agent 能不能反覆迭代，再決定怎麼組工具，而且一定要建一套評估(eval)流程去量，別憑感覺。檢索沒有銀彈，只有策展得好不好的工具箱。</p>]]></content><author><name>ihower</name></author><category term="RAG" /><category term="Search" /><category term="Agent" /><summary type="html"><![CDATA[最近一篇 PwC 的論文 Is Grep All You Need? How Agent Harnesses Reshape Agentic Search 在 X 上吵得蠻兇的，戰火還燒到一個更聳動的問題: 向量檢索已死? 這標題擺明在挑釁: 都 2026 年了，Claude Code、Codex 這些命令列 agent 光靠 grep 加 bash 就能找到東西，那花大錢養 embedding 模型、維護向量資料庫的 RAG 一整套基礎設施，是不是可以直接拆掉了?]]></summary></entry><entry><title type="html">從 Token 串流到 Agent 事件串流:OpenAI、AG-UI、Vercel、LangChain 的格式設計比一比</title><link href="https://blog.aihao.tw/2026/06/02/agent-streaming-chunk-format/" rel="alternate" type="text/html" title="從 Token 串流到 Agent 事件串流:OpenAI、AG-UI、Vercel、LangChain 的格式設計比一比" /><published>2026-06-02T00:00:00+00:00</published><updated>2026-06-02T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/02/agent-streaming-chunk-format</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/02/agent-streaming-chunk-format/"><![CDATA[<p>LangChain 的 Christian Bromann 寫了一篇 <a href="https://x.com/bromann/status/2057507361972011068">From Token Streams to Agent Streams</a>(從 token 流到 agent 流),講的是一件常被當成傳輸小事、其實是該好好設計的工程問題: 當你的產品從「一次模型呼叫」長成「一個會規劃、會分派子代理(subagent)、會呼叫工具、還會中途停下來等人核准」的 agent,你在前端收到的那串資料,格式整個都得重新設計。</p>

<p>他開頭那句話蠻精準的:「agent 串流已經超出 token 增量(token deltas)能承載的範圍。」小編就借這個命題,把幾家的設計做法整理在一起聊聊: 從最原始的 OpenAI 串流當基準,一路看到 AG-UI、Vercel AI SDK、LangChain 各自怎麼設計這串資料的格式,中間再穿插一些 ihower 實際做串流產品時累積的經驗。算是一篇參考各家設計、加上實戰心得的整理分享,看完你大概會同意:「能不能顯示 token」早就不是重點了。</p>

<h2 id="先談基準-從增量到語意事件">先談基準: 從增量到語意事件</h2>

<p>最原始的串流長這樣。OpenAI Chat Completions 的每個 chunk 是 <code class="language-plaintext highlighter-rouge">choices[0].delta.content</code>,裡頭塞一小段文字,前端要做的就是把這些片段一段段接起來。工具呼叫更麻煩: 它是 <code class="language-plaintext highlighter-rouge">tool_calls[].function.arguments</code> 的片段,用陣列位置(index)標記這是第幾個工具,你得自己照著位置把 JSON 碎片拼回完整的參數物件。這就是典型的「不透明 chunk」: 格式只關心「下一段資料是什麼」,語意全得靠你自己重建。</p>

<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin:8px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78em;line-height:1.75;overflow-x:auto;">
<div style="color:var(--text-secondary);margin-bottom:6px;">// 基準 ①  Chat Completions: 不透明的 token 增量</div>
<div>data: {"choices":[{"delta":{"content":"舊金山"}}]}</div>
<div>data: {"choices":[{"delta":{"tool_calls":[{"index":0,</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;"function":{"arguments":"{\"ci"}}]}}]}</div>
</div>

<p>文字和工具參數都是裸片段，工具還得靠 <code class="language-plaintext highlighter-rouge">index</code> 自己對位拼回來，語意全靠前端重建。</p>

<p>OpenAI 後來的 Responses API 其實已經進步很多。它不再是沒有語意的增量，而是一串「語意事件」: <code class="language-plaintext highlighter-rouge">response.output_item.added</code> 告訴你新開了一個輸出項目(可能是訊息、工具呼叫、推理),<code class="language-plaintext highlighter-rouge">response.output_text.delta</code> 串文字,<code class="language-plaintext highlighter-rouge">response.function_call_arguments.delta</code> 串工具參數，推理(reasoning)有自己的事件，最後用 <code class="language-plaintext highlighter-rouge">response.completed</code> 收尾。整個輸出被組織成一個個輸出項目，每個項目內部用「開始 / 增量 / 結束」的節奏串。</p>

<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin:8px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78em;line-height:1.75;overflow-x:auto;">
<div style="color:var(--text-secondary);margin-bottom:6px;">// 基準 ②  Responses API: 帶語意的事件</div>
<div><span style="color:var(--accent);">event: response.output_item.added</span></div>
<div>data: {"item":{"type":"function_call","name":"get_weather"}}</div>
<div><span style="color:var(--accent);">event: response.function_call_arguments.delta</span></div>
<div>data: {"delta":"{\"city\":\"Taipei\"}"}</div>
<div><span style="color:var(--accent);">event: response.completed</span></div>
</div>

<p>事件名稱本身就帶語意，前端一看就知道現在在串哪個輸出項目，推理和最終答案也分開了。</p>

<p>這已經是相當像樣的設計: 有型別、知道自己在串什麼、推理和答案分開、連用量(usage)資訊都保得住。所以問題來了,既然基準都做到這樣了,為什麼還不夠用在應用層級?</p>

<h2 id="基準暗藏的三個假設">基準暗藏的三個假設</h2>

<p>關鍵在於 Responses API 這類設計,骨子裡假設了三件事，而 agent 應用把這三件事全打破了:</p>

<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:16px 20px;margin:8px 0;">
<div style="display:flex;flex-wrap:wrap;gap:14px;">
<div style="flex:1 1 200px;"><strong style="color:var(--accent);">假設一: 只有一次模型呼叫</strong><br /><span style="color:var(--text-secondary);font-size:.92em;">但複雜的 agent 會展開成一棵樹: 主 agent 分派三個子代理，每個又各自呼叫工具。一條線性的串流講不清「這段是誰產生的」。</span></div>
<div style="flex:1 1 200px;"><strong style="color:var(--accent);">假設二: 一條串流全包</strong><br /><span style="color:var(--text-secondary);font-size:.92em;">把所有子代理的 token、工具、狀態壓成一條串流，等於逼前端下載每個子代理的每個 token，哪怕畫面上只開了其中一個。</span></div>
<div style="flex:1 1 200px;"><strong style="color:var(--accent);">假設三: 連線是短暫的</strong><br /><span style="color:var(--text-secondary);font-size:.92em;">會跑很久的 agent 一動就是十幾分鐘。瀏覽器一刷新就斷線，沒有排序和回放機制就只能重跑，或是內容重複。</span></div>
</div>
</div>

<p>Bromann 把應用層級真正該問的問題列得很到位，小編挑幾個最有感的: 能不能即時畫出一棵 agent 的工作樹? 能不能只訂閱畫面上那一個子代理，而不用付下載其他所有子代理的頻寬代價? 能不能把「等人核准」當成第一級的事件來處理? 能不能斷線後重新接上、接著上次的進度繼續，而不是整段重播?</p>

<p>這些問題，token 增量一個都答不了。因為它根本沒有「樹」(tree)「頻道」(channel)「斷點」(checkpoint)這些概念。</p>

<h2 id="兩個維度-頻道與命名空間">兩個維度: 頻道與命名空間</h2>

<p>LangChain 這套新串流原語的核心設計，小編覺得最值得學的，是它把每一段資料拆成「兩個正交維度」來標記。</p>

<p>第一個維度是 <strong>頻道(channel)</strong>: 標記這串資料「屬於哪一類關注點」，也就是你會想分開來看的不同面向。<code class="language-plaintext highlighter-rouge">messages</code> 是對話內容、<code class="language-plaintext highlighter-rouge">values</code> 和 <code class="language-plaintext highlighter-rouge">updates</code> 是流程圖共享的狀態(例如目前累積的搜尋結果、計畫清單、步數計數器這類資料: <code class="language-plaintext highlighter-rouge">values</code> 給你完整快照，<code class="language-plaintext highlighter-rouge">updates</code> 給你每一步改了哪些欄位)、<code class="language-plaintext highlighter-rouge">tools</code> 是工具執行的起訖、<code class="language-plaintext highlighter-rouge">lifecycle</code> 是整段執行和子代理的生死、<code class="language-plaintext highlighter-rouge">custom:*</code> 是應用自己定義的投影。</p>

<p>第二個維度是 <strong>命名空間(namespace)</strong>: 描述這個事件「發生在 agent 樹的哪個位置」。根節點、巢狀的子流程圖、某個子代理，都可以發出同一種頻道的事件，但各自保有身分、不會混在一起。</p>

<p>用一張表最好懂。每段資料都落在某個「頻道 × 位置」的格子裡，而你可以只訂閱自己正在畫的那幾格:</p>

<div style="border:1px solid var(--border);border-radius:8px;overflow:hidden;margin:8px 0;font-size:.86em;">
<div style="display:flex;background:var(--surface);font-weight:600;">
<div style="flex:1.2 1 0;padding:8px 10px;border-right:1px solid var(--border);">命名空間 ↓ / 頻道 →</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;border-right:1px solid var(--border);">messages</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;border-right:1px solid var(--border);">tools</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;">values</div>
</div>
<div style="display:flex;border-top:1px solid var(--border);">
<div style="flex:1.2 1 0;padding:8px 10px;border-right:1px solid var(--border);background:var(--surface);">root</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;border-right:1px solid var(--border);">·</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;border-right:1px solid var(--border);">·</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;">·</div>
</div>
<div style="display:flex;border-top:1px solid var(--border);">
<div style="flex:1.2 1 0;padding:8px 10px;border-right:1px solid var(--border);background:var(--surface);">subagent: research-1</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;border-right:1px solid var(--border);background:rgba(9,105,218,.15);font-weight:600;">訂閱這格</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;border-right:1px solid var(--border);background:rgba(9,105,218,.15);font-weight:600;">訂閱這格</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;">·</div>
</div>
<div style="display:flex;border-top:1px solid var(--border);">
<div style="flex:1.2 1 0;padding:8px 10px;border-right:1px solid var(--border);background:var(--surface);">subagent: research-2</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;border-right:1px solid var(--border);color:var(--text-secondary);">不下載</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;border-right:1px solid var(--border);color:var(--text-secondary);">不下載</div>
<div style="flex:1 1 0;padding:8px 6px;text-align:center;color:var(--text-secondary);">不下載</div>
</div>
</div>

<p>「頻道是可重複使用的關注點、命名空間是產生它的位置」這個拆分，就是整套設計的關鍵選擇。有了它，一個子代理檢視器才能只打開它要的那一格，不會把每個子代理的 token 全拉下來。</p>

<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin:8px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78em;line-height:1.75;overflow-x:auto;">
<div style="color:var(--text-secondary);margin-bottom:6px;">// LangChain agent 流: 每個事件多帶 channel + namespace</div>
<div>{"<span style="color:var(--accent);">channel</span>":"messages","<span style="color:var(--accent);">namespace</span>":["root"],"block":"text","delta":"嗨"}</div>
<div>{"<span style="color:var(--accent);">channel</span>":"tools","<span style="color:var(--accent);">namespace</span>":["subagent:research-1"],"name":"search","status":"started"}</div>
<div>{"<span style="color:var(--accent);">channel</span>":"values","<span style="color:var(--accent);">namespace</span>":["root"],"patch":[{"op":"add","path":"/findings/-"}]}</div>
<div>{"<span style="color:var(--accent);">channel</span>":"lifecycle","<span style="color:var(--accent);">namespace</span>":["subagent:research-2"],"status":"started"}</div>
</div>

<p>多了 <code class="language-plaintext highlighter-rouge">namespace</code> 這個維度，同一種事件就能標出「是樹上哪個子代理發的」，這正是前面那些設計都沒有的。(實際 LangGraph 傳輸線上的欄位名稱略有不同，這裡是概念示意。)</p>

<h2 id="投影-不要解析直接拿你要畫的東西">投影: 不要解析，直接拿你要畫的東西</h2>

<p>光有型別還不夠。Bromann 的第二個重點是 <strong>投影(projection)</strong>。先講清楚: 投影不是傳輸線上的某種格式，而是一個設計概念。它指的是執行層在那串底層事件「之上」，幫你組好幾種現成的視圖，讓你的程式碼直接拿來用，不必自己一條條去解析原始事件。</p>

<p>具體長什麼樣? 在 LangChain 的框架 SDK 裡，這個概念落地成一組 API: 你呼叫 <code class="language-plaintext highlighter-rouge">useMessages()</code> 就拿到一串已經組好的訊息、<code class="language-plaintext highlighter-rouge">useToolCalls()</code> 拿到工具呼叫的清單、<code class="language-plaintext highlighter-rouge">useValues()</code> 拿到目前的狀態。至於重組、重新排序、斷線重連這些髒活，執行層全幫你包掉。而且每則訊息本身是「一連串有型別的區塊」: 文字、推理、工具參數、用量，而不是一條要你自己切的字串。這點對現代模型輸出很重要,推理和最終答案本來就該分開呈現，工具參數要當成結構化資料來組裝，多媒體資料更不該被硬塞進純文字介面。</p>

<div style="border:1px solid var(--border);border-radius:8px;padding:16px;margin:8px 0;">
<div style="display:flex;flex-wrap:wrap;align-items:stretch;gap:12px;">
<div style="flex:1 1 210px;">
<div style="font-size:.82em;margin-bottom:2px;"><strong>① 底層事件串</strong></div>
<div style="color:var(--text-secondary);font-size:.74em;margin-bottom:8px;">網路上實際傳的就是這串事件</div>
<div style="font-family:ui-monospace,Menlo,monospace;font-size:.75em;background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 8px;margin-bottom:5px;">message · delta</div>
<div style="font-family:ui-monospace,Menlo,monospace;font-size:.75em;background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 8px;margin-bottom:5px;">tool · start / args</div>
<div style="font-family:ui-monospace,Menlo,monospace;font-size:.75em;background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 8px;margin-bottom:5px;">state · delta</div>
<div style="font-family:ui-monospace,Menlo,monospace;font-size:.75em;background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 8px;">lifecycle · subagent</div>
</div>
<div style="flex:0 1 120px;display:flex;flex-direction:column;justify-content:center;text-align:center;color:var(--accent);font-size:.78em;">
<div style="font-size:1.6em;line-height:1;">→</div>
<div style="margin-top:4px;">前端 SDK<br />(瀏覽器 JS)<br />組裝 · 排序 · 重連</div>
</div>
<div style="flex:1 1 210px;">
<div style="font-size:.82em;margin-bottom:2px;"><strong>② 投影視圖</strong></div>
<div style="color:var(--text-secondary);font-size:.74em;margin-bottom:8px;">前端 SDK 組好的 JS API，不是網路格式</div>
<div style="font-size:.8em;background:rgba(9,105,218,.1);border:1px solid var(--border);border-radius:6px;padding:4px 8px;margin-bottom:5px;"><code>useMessages()</code> → 聊天泡泡</div>
<div style="font-size:.8em;background:rgba(9,105,218,.1);border:1px solid var(--border);border-radius:6px;padding:4px 8px;margin-bottom:5px;"><code>useToolCalls()</code> → 工具指示器</div>
<div style="font-size:.8em;background:rgba(9,105,218,.1);border:1px solid var(--border);border-radius:6px;padding:4px 8px;margin-bottom:5px;"><code>useValues()</code> → 狀態面板</div>
<div style="font-size:.8em;background:rgba(9,105,218,.1);border:1px solid var(--border);border-radius:6px;padding:4px 8px;">subagents → 子代理分頁</div>
</div>
</div>
<div style="border-top:1px solid var(--border);margin-top:12px;padding-top:10px;font-size:.78em;color:var(--text-secondary);">你只訂閱要畫的頻道 / 子代理，後端就只送那部分事件過來;線上跑的單位永遠是事件，組裝成視圖是前端 SDK 的事。</div>
</div>

<p>這裡 ihower 補了一個很實務的提醒: 你不應該把上游 Responses API 或 Completions API 的串流原封不動轉傳給前端。模型 API 吐出來的東西，很多根本不該給使用者看,可能是內部推理、給除錯用的中繼資料、或牽涉權限不該外露的內容。前端要收的，是你「為這個產品設計過」的事件,而不是上游吐什麼就照單全收。換句話說,你前端那層格式，是一個你要主動設計的應用層介面,不是模型 API 的透傳管線。這也是投影這個概念為什麼這麼關鍵: 哪些事件、長什麼形狀、露給前端，都由你決定。</p>

<p>跨模型使用是另一個自己設計格式的硬道理。實務上你很可能今天用 OpenAI、明天換 Anthropic 或 Gemini，每一家原生的串流格式都長得不一樣。要是前端直接綁死某一家的格式，換一次模型前端就得跟著改一輪;但只要中間隔了一層你自己設計的事件格式，換模型時你只要改後端那段轉換，前端依賴的那份約定完全不用動。模型是會換的，你的格式不該跟著換。</p>

<blockquote>
  <p>編按: 這背後其實是一個「抽象反轉」的觀念,真正的事件來源是執行引擎(harness),token 串流和畫面更新都只是它的投影。所以換掉底層模型，換掉的只是投影，你前端依賴的那份語意約定不變。這也是為什麼這類協定都強調「按類別訂閱，而不是按單一事件名稱訂閱」: 訂「所有工具呼叫類的事件」，協定日後新增事件名稱你也不會壞;把每個事件名稱寫死進去就會壞。</p>
</blockquote>

<h2 id="ag-ui-怎麼設計的">AG-UI 怎麼設計的</h2>

<p>講到標準化的事件協定,就不能不看 <a href="https://docs.ag-ui.com">AG-UI</a>(Agent-User Interaction Protocol，由 <a href="https://www.copilotkit.ai">CopilotKit</a> 發起)。它的定位跟 LangChain 那套有點不同: AG-UI 想當的是「agent 後端」和「前端」之間那條通用的線，和 MCP(管工具)、A2A(管 agent 之間的溝通)並列在 agent 協定的堆疊裡。</p>

<blockquote>
  <p>編按: 釐清一下 CopilotKit 和 AG-UI 的關係，免得搞混。CopilotKit 是一間公司、也是一套 React / Angular 的前端 SDK(現成的聊天元件、<code class="language-plaintext highlighter-rouge">useAgent</code> hook、generative UI 那些);AG-UI 則是他們從跟 LangGraph、CrewAI 的合作中抽出來、再開源給整個生態的「協定」,後來被 Google、LangChain、AWS、微軟、Mastra、PydanticAI 等採用。一句話: AG-UI 是開放協定(規格)，CopilotKit 是講這個協定的第一方前端框架。</p>
</blockquote>

<p>它的設計小編覺得乾淨俐落。所有事件都繼承自同一個 <code class="language-plaintext highlighter-rouge">BaseEvent</code>(至少帶 <code class="language-plaintext highlighter-rouge">type</code>，另有可選的 <code class="language-plaintext highlighter-rouge">timestamp</code>、<code class="language-plaintext highlighter-rouge">rawEvent</code>),核心大約十多種，分成幾類:</p>

<ul>
  <li><strong>生命週期</strong>: <code class="language-plaintext highlighter-rouge">RUN_STARTED</code>、<code class="language-plaintext highlighter-rouge">STEP_STARTED</code> / <code class="language-plaintext highlighter-rouge">STEP_FINISHED</code>、<code class="language-plaintext highlighter-rouge">RUN_FINISHED</code> / <code class="language-plaintext highlighter-rouge">RUN_ERROR</code>，帶 <code class="language-plaintext highlighter-rouge">threadId</code>、<code class="language-plaintext highlighter-rouge">runId</code>，還有可選的 <code class="language-plaintext highlighter-rouge">parentRunId</code> 給分支用</li>
  <li><strong>文字訊息</strong>: <code class="language-plaintext highlighter-rouge">TEXT_MESSAGE_START</code> → 多個 <code class="language-plaintext highlighter-rouge">TEXT_MESSAGE_CONTENT</code>(各帶一段 <code class="language-plaintext highlighter-rouge">delta</code>)→ <code class="language-plaintext highlighter-rouge">TEXT_MESSAGE_END</code>，用 <code class="language-plaintext highlighter-rouge">messageId</code> 串起來</li>
  <li><strong>工具呼叫</strong>: <code class="language-plaintext highlighter-rouge">TOOL_CALL_START</code> → 多個 <code class="language-plaintext highlighter-rouge">TOOL_CALL_ARGS</code>(串 JSON 片段)→ <code class="language-plaintext highlighter-rouge">TOOL_CALL_END</code> → <code class="language-plaintext highlighter-rouge">TOOL_CALL_RESULT</code>，用 <code class="language-plaintext highlighter-rouge">toolCallId</code> 串</li>
  <li><strong>狀態管理</strong>: <code class="language-plaintext highlighter-rouge">STATE_SNAPSHOT</code>(完整狀態)、<code class="language-plaintext highlighter-rouge">STATE_DELTA</code>(用 JSON Patch / RFC 6902 送增量)、<code class="language-plaintext highlighter-rouge">MESSAGES_SNAPSHOT</code></li>
  <li><strong>特殊事件</strong>: <code class="language-plaintext highlighter-rouge">RAW</code>(包外部系統的事件)、<code class="language-plaintext highlighter-rouge">CUSTOM</code>(應用自訂)當逃生口</li>
</ul>

<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin:8px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78em;line-height:1.75;overflow-x:auto;">
<div style="color:var(--text-secondary);margin-bottom:6px;">// AG-UI: 標準化的型別事件 + JSON Patch 狀態</div>
<div>{"<span style="color:var(--accent);">type</span>":"TEXT_MESSAGE_START","messageId":"m1","role":"assistant"}</div>
<div>{"<span style="color:var(--accent);">type</span>":"TEXT_MESSAGE_CONTENT","messageId":"m1","delta":"嗨"}</div>
<div>{"<span style="color:var(--accent);">type</span>":"TOOL_CALL_START","toolCallId":"t1","toolCallName":"get_weather"}</div>
<div>{"<span style="color:var(--accent);">type</span>":"TOOL_CALL_ARGS","toolCallId":"t1","delta":"{\"city"}</div>
<div>{"<span style="color:var(--accent);">type</span>":"STATE_DELTA","delta":[{"op":"replace","path":"/step","value":2}]}</div>
</div>

<p>這裡有兩個小編覺得很值得抄的設計。一是 <strong>「開始 / 內容 / 結束」三段式</strong>，套用在所有串流內容上(文字、工具參數都是同一個節奏),前端只要實作一套組裝邏輯就好。二是 <strong>「快照 / 增量」模式</strong> 來同步狀態: 偶爾送一次完整快照當基準,中間用 JSON Patch 送小增量,兼顧完整和省頻寬。它還貼心地提供 <code class="language-plaintext highlighter-rouge">TEXT_MESSAGE_CHUNK</code> 這種便利事件，第一段自動展開成 start，串流切到下一個 id 時自動補上 end，省掉手動管理生命週期的麻煩。</p>

<p>傳輸層的選擇上 AG-UI 也刻意保持開放: SSE、WebSocket、webhook 甚至自家的二進位傳輸都能載，不綁死單一種(對照之下 Vercel 那套主要走 SSE)。再配上雙向溝通，使用者的中斷和確認可以回傳給 agent，所以「人在迴路」(human-in-the-loop)是它內建的概念(<code class="language-plaintext highlighter-rouge">RUN_FINISHED</code> 的結果可以是一個中斷，代表停下來等人)。</p>

<p>不過對照一下會發現一個有意思的差別: AG-UI 的核心比較像「單一 agent + 工具 + 狀態」的一條扁平串流，多 agent 樹的部分主要靠 <code class="language-plaintext highlighter-rouge">RAW</code> 和 <code class="language-plaintext highlighter-rouge">CUSTOM</code> 當擴充點。而 LangChain 那套多了命名空間這個維度，把 agent 樹的位置直接做進協定的第一層。兩種取捨沒有絕對好壞: 前者簡單通用、好接既有框架，後者為複雜的樹狀 agent 而生。</p>

<h2 id="還有哪些值得參考的設計">還有哪些值得參考的設計</h2>

<p>小編另外撈了幾個業界的做法，會發現大家其實在往同一個方向走。</p>

<p><strong>Vercel AI SDK 的 data stream 協定</strong> 跟 AG-UI 幾乎是同一個形狀,只是換了名字: 文字用 <code class="language-plaintext highlighter-rouge">text-start</code> / <code class="language-plaintext highlighter-rouge">text-delta</code> / <code class="language-plaintext highlighter-rouge">text-end</code>(每段內容有自己的 id),工具用 <code class="language-plaintext highlighter-rouge">tool-input-start</code> / <code class="language-plaintext highlighter-rouge">tool-input-delta</code> / <code class="language-plaintext highlighter-rouge">tool-input-available</code> / <code class="language-plaintext highlighter-rouge">tool-output-available</code>,推理也是「開始 / 增量 / 結束」。它有兩個設計小編覺得很實用: 一是 <strong>資料片段的就地更新</strong>,你送一個帶 <code class="language-plaintext highlighter-rouge">id</code> 的資料片段,之後用同一個 <code class="language-plaintext highlighter-rouge">id</code> 再送一次就會更新同一塊(很適合做進度條、載入狀態、協作中的文件);二是 <strong>暫時性片段</strong>,送給前端顯示、但不寫進訊息歷史(適合那種一閃即逝的通知)。它一樣走 SSE，主打連線保活、可重連、好快取。</p>

<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin:8px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78em;line-height:1.75;overflow-x:auto;">
<div style="color:var(--text-secondary);margin-bottom:6px;">// Vercel AI SDK: 形狀同 AG-UI，外加可就地更新的資料片段</div>
<div>data: {"<span style="color:var(--accent);">type</span>":"text-start","id":"t1"}</div>
<div>data: {"<span style="color:var(--accent);">type</span>":"text-delta","id":"t1","delta":"嗨"}</div>
<div>data: {"<span style="color:var(--accent);">type</span>":"tool-input-start","toolCallId":"c1","toolName":"getWeather"}</div>
<div>data: {"<span style="color:var(--accent);">type</span>":"tool-output-available","toolCallId":"c1","output":{"temp":28}}</div>
<div>data: {"<span style="color:var(--accent);">type</span>":"data-weather","id":"w1","data":{"status":"loading"}}</div>
</div>

<p>這兩個設計用程式碼最好懂。上面那筆 <code class="language-plaintext highlighter-rouge">data-weather</code> 是 <code class="language-plaintext highlighter-rouge">loading</code>，之後用同一個 <code class="language-plaintext highlighter-rouge">id</code> 再送一次，前端就會就地把那塊換成新內容(很適合進度條、協作中的文件);而帶 <code class="language-plaintext highlighter-rouge">transient:true</code> 的片段只會即時顯示、不寫進訊息歷史(適合一閃即逝的通知):</p>

<blockquote>
  <p>編按: 這裡的 <code class="language-plaintext highlighter-rouge">data-</code> 前綴和整套資料片段機制(<code class="language-plaintext highlighter-rouge">id</code> 就地更新、<code class="language-plaintext highlighter-rouge">transient</code> 不入歷史)是 Vercel AI SDK 協定定義的，但後面的 <code class="language-plaintext highlighter-rouge">weather</code>、<code class="language-plaintext highlighter-rouge">notification</code> 是你自己取的名字、可搭配型別做到型別安全，並非內建事件;對照之下 <code class="language-plaintext highlighter-rouge">text-start</code>、<code class="language-plaintext highlighter-rouge">tool-input-delta</code> 那些才是固定的內建事件名。</p>
</blockquote>

<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin:8px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78em;line-height:1.75;overflow-x:auto;">
<div style="color:var(--text-secondary);margin-bottom:6px;">// 同一個 id 再送 → 就地更新那一塊</div>
<div>data: {"type":"data-weather","<span style="color:var(--accent);">id</span>":"w1","data":{"status":"done","temp":28}}</div>
<div style="color:var(--text-secondary);margin:10px 0 6px;">// transient:true → 只即時顯示，不進訊息歷史</div>
<div>data: {"type":"data-notification","data":{"msg":"查到了"},"<span style="color:var(--accent);">transient</span>":true}</div>
</div>

<h3 id="順帶分清楚-a2ui-是格式ag-ui-是傳輸">順帶分清楚: A2UI 是「格式」，AG-UI 是「傳輸」</h3>

<p>既然講到 AG-UI，順手帶一個名字超像、又最容易跟它搞混的東西: <a href="https://a2ui.org">A2UI</a>。兩者其實是不同層的協定。</p>

<p>A2UI(Agent to UI)是 Google 提出的協定，要解決的問題是:「agent 要怎麼安全地把一個畫面送到前端?」尤其在多代理場景，有些 agent 跑在別人的伺服器上，你不可能讓它直接塞 HTML/JavaScript 進你的頁面(有安全風險，畫出來也跟你的 app 樣式不搭)。</p>

<p>A2UI 的做法是: agent 不送程式碼，而是送一串「宣告式的 JSON」描述畫面長怎樣，前端再用自己的原生元件(React、Flutter、SwiftUI 都行)把它畫出來。它的幾個設計重點:</p>

<ul>
  <li><strong>送的是「資料」不是「程式碼」</strong>: agent 只能從前端提供的「元件目錄(catalog)」裡挑元件，沒有任意執行程式碼的風險，所以連跑在別人伺服器上的 agent 送來的 UI 都能安全地畫。</li>
  <li><strong>結構和資料分開</strong>: 一種訊息(<code class="language-plaintext highlighter-rouge">updateComponents</code>)描述畫面結構、另一種(<code class="language-plaintext highlighter-rouge">updateDataModel</code>)灌資料，前端可以只更新某個欄位、不必重畫整個畫面。</li>
  <li><strong>扁平的元件清單 + ID 互相參照</strong>(adjacency list): 不用一次生出完美的巢狀 JSON，LLM 可以邊生邊串，收到 root 元件就先開始畫。</li>
</ul>

<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin:8px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78em;line-height:1.75;overflow-x:auto;">
<div style="color:var(--text-secondary);margin-bottom:6px;">// A2UI: 送「宣告式 JSON」描述畫面，前端用自己的原生元件畫</div>
<div>{"<span style="color:var(--accent);">updateComponents</span>":{"surfaceId":"booking","components":[</div>
<div>&nbsp;&nbsp;{"id":"root","component":"Column","children":["title","submit"]},</div>
<div>&nbsp;&nbsp;{"id":"title","component":"Text","text":"Book Your Table"},</div>
<div>&nbsp;&nbsp;{"id":"submit","component":"Button","action":{"event":{"name":"confirm"}}}]}}</div>
<div>{"<span style="color:var(--accent);">updateDataModel</span>":{"surfaceId":"booking","path":"/booking","value":{"date":"2025-12-16"}}}</div>
</div>

<p>關鍵就在這個分工: <strong>A2UI 管「要畫什麼」(格式)，AG-UI 管「事件怎麼在前後端之間流動」(傳輸)</strong>。兩者是互補的，A2UI 本身就標明自己不挑傳輸，可以走 AG-UI、A2A、SSE、WebSocket;反過來 AG-UI 也可以把一包 A2UI 內容當成某個事件的酬載載過去。實際上任何已經會講 AG-UI 的 agent，幾乎零成本就能驅動 A2UI。把這兩層分清楚，你才不會拿「要畫什麼」去跟「怎麼傳」硬比。</p>

<div style="display:flex;flex-wrap:wrap;gap:12px;margin:8px 0;">
<div style="flex:1 1 250px;border:1px solid var(--border);border-radius:8px;padding:14px 16px;">
<div style="font-weight:600;color:var(--accent);margin-bottom:6px;">AG-UI: 串「事件」</div>
<div style="font-size:.88em;">agent 說「發生了什麼」</div>
<div style="font-family:ui-monospace,Menlo,monospace;font-size:.74em;background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 8px;margin:6px 0;">TOOL_CALL_START · STATE_DELTA</div>
<div style="font-size:.88em;color:var(--text-secondary);">↓ 你的前端決定怎麼畫</div>
<div style="font-size:.82em;margin-top:8px;background:#dafbe1;color:#1a7f37;border-radius:6px;padding:3px 8px;display:inline-block;">適合: 前端是你自己的</div>
</div>
<div style="flex:1 1 250px;border:1px solid var(--border);border-radius:8px;padding:14px 16px;">
<div style="font-weight:600;color:var(--accent);margin-bottom:6px;">A2UI: 描述「畫面」</div>
<div style="font-size:.88em;">agent 說「畫成這樣」</div>
<div style="font-family:ui-monospace,Menlo,monospace;font-size:.74em;background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 8px;margin:6px 0;">Column · Text · Button(元件樹)</div>
<div style="font-size:.88em;color:var(--text-secondary);">↓ 通用 renderer 直接畫</div>
<div style="font-size:.82em;margin-top:8px;background:#fff8c5;color:#9a6700;border-radius:6px;padding:3px 8px;display:inline-block;">適合: 前端不屬於你</div>
</div>
</div>

<p>你可能會問: AG-UI 不是也有「generative UI」能嗎? 有，但差別在「誰決定畫面長怎樣」。AG-UI 的 UI 是「事件驅動、由你的 app 自己渲染」: agent 發出工具呼叫或自訂事件，你在前端自己決定哪個事件對應哪個元件，渲染邏輯寫在你(受信任的)app 程式碼裡，它本身沒有規定元件樹的標準格式。A2UI 則是把「UI 描述本身」標準化成一棵宣告式元件樹，一個通用 renderer 就能畫。所以前者載的是「你 app 會自己渲染的訊號」，後者載的是「通用 renderer 就能畫的 UI 描述」，這也是為什麼不受信任的遠端 agent 適合用 A2UI。</p>

<blockquote>
  <p>編按: CopilotKit 自己把 generative UI 分成三種模式，剛好把這個層次標得很清楚: <strong>Static</strong>(走 AG-UI，前端預先寫好元件、agent 用事件或狀態觸發)、<strong>Declarative</strong>(走 A2UI，agent 吐元件樹、通用 renderer 畫)、<strong>Open-Ended</strong>(MCP Apps / Open JSON，更自由)。重點是: AG-UI 是事件導向、本身不是宣告式 UI;同一個前端可以底層用 AG-UI 載事件，需要時再把 A2UI 當酬載塞進某個事件裡，兩者疊著用。</p>
</blockquote>

<p>講完設計，小編插一段比較主觀的看法: 對 A2UI 這種「宣告式 UI」，小編其實蠻保留的。畫面長怎樣、怎麼排版、怎麼互動，本來就是前端最擅長、也最該掌握的事;讓 agent 去描述一棵 UI 元件樹，某種程度上就是在重新發明一套 HTML/CSS，多數情況下是 over-engineering。<a href="https://news.ycombinator.com/item?id=46286407">Hacker News 上的討論</a>也有人吐同樣的槽:「看這些範例，感覺它最後會收斂回我們早就有的 HTML，那為什麼不乾脆讓各平台支援 HTML 就好? LLM 本來就很會生 HTML」、「『用 JSON 描述畫面、客戶端來畫』這套我們搞很多年了，難的從來不是線上格式，而是元件版本管理跟跨客戶端 debug」。</p>

<p>那 A2UI 真正有價值的情境是什麼? 小編認為其實就一個: <strong>當前端不屬於你的時候</strong>。比方你要把 agent 上架到別人的平台、嵌進別人的 app，你沒辦法自己寫前端、也不能塞程式碼進去，只能用對方提供的元件目錄,這時候一套宣告式、跨平台、又安全(只能挑核可過的元件)的 UI 描述格式才划算。同一串討論裡也有人精準點出這點: A2UI 最有意思的就是「遠端訊息傳遞、你不擁有那個 UI」的場景。反過來說，如果前端是你自己的，老老實實寫前端就好，別繞這一圈。</p>

<blockquote>
  <p>編按: 這種「agent 吐一份 UI 描述、通用 renderer 來畫」的路子,A2UI 並不是唯一一家。Thesys 的 <a href="https://www.openui.com/">OpenUI</a>(MIT 授權)走同一個方向,切入點不同: 它不送 JSON,而是設計了一套叫 OpenUI Lang 的精簡 DSL(每行一個 <code class="language-plaintext highlighter-rouge">identifier = Expression</code>),主打兩點,一是 token 更省(官方自宣稱比 JSON 少約 67%),二是 line-oriented,每收到一行就能立刻畫一塊,streaming 更順。</p>
</blockquote>

<h2 id="存下來的不是答案是整段串流">存下來的不是答案，是整段串流</h2>

<p>ihower 還提了一個小編覺得超多人會踩到的坑: 串流型的應用不能只存最後那段最終答案的文字。</p>

<p>想想看,使用者關掉頁面、隔天再回來,你要讓他看到的應該是「完整的過程」: 中間呼叫了哪些工具、各個子代理做了什麼、推理怎麼一步步走、多媒體怎麼一塊塊長出來。如果你資料庫裡只存了最終答案，這些全沒了，回訪的使用者只看到一個乾巴巴的結果，和當時親眼看著它一步步生出來的體驗完全是兩回事。</p>

<p>所以你真正該存下來的，是「整段串流本身」: 當時串流長什麼樣，存下來就是什麼樣，回放的時候也放同一份。這正好解釋了為什麼前面那些協定都那麼在意型別事件、排序資訊和「快照 / 增量」,因為那串有序的事件本身，同時就是你的「儲存格式」和「回放格式」。格式設計得好不好,直接決定了你能不能把一段跑了十分鐘的 agent 執行完整存起來、之後一模一樣地放出來。</p>

<p>這跟斷線重連其實是同一個能力的兩面: 重連是「執行還在跑,我接回去」,回放是「執行早就結束,我重看一遍」,兩者吃的都是同一份有序的事件記錄。一個沒有設計好格式的串流應用，這兩件事都別想做。</p>

<h3 id="整段都存會踩到的儲存效率問題">「整段都存」會踩到的儲存效率問題</h3>

<p>不過「整段都存」馬上會帶出另一個工程問題: 怎麼存才不會爆掉? LangGraph 團隊最近就在處理這件事。</p>

<p>問題出在它預設的存法: 每走一步，就把當下「完整的狀態」整包存一次。對話的訊息清單尤其慘，因為每一輪都是在前面所有訊息後面再接一句，於是第 1 步存 1 則、第 2 步存 2 則、到第 N 步存 N 則… 前面的內容被一存再存，儲存成本是用 <strong>O(N²)</strong> 在膨脹。官方給的數字很嚇人: 一段累積到約 10 萬 token 的對話，單一執行緒就吃掉約 250 MB;外推到百萬 token 等級會到約 25 GB。多輪對話拖越長，重複浪費越誇張。</p>

<p>他們的解法叫 <code class="language-plaintext highlighter-rouge">DeltaChannel</code>，精神跟前面 AG-UI 的「快照 / 增量」一模一樣: 別每步都存完整清單，只存「這一步新增了什麼」(增量)，要讀的時候再順著增量鏈把狀態重播回來;再加一個 <code class="language-plaintext highlighter-rouge">snapshot_every</code> 參數，每隔幾步存一張完整快照，免得重播鏈拉太長。儲存成本就從 O(N²) 降到 O(N)，同一段對話省下幾十倍空間(官方實測 500 輪、約 10 萬 token 的對話，從 252 MB 降到約 712 KB)。</p>

<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px 16px;margin:8px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78em;line-height:1.75;overflow-x:auto;">
<div style="color:var(--text-secondary);margin-bottom:6px;">// 預設: O(N²)，每步都把整包歷史存一次</div>
<div>messages: Annotated[list[AnyMessage], add_messages]</div>
<div style="color:var(--text-secondary);margin:10px 0 6px;">// 改用 DeltaChannel: O(N)，只存每步的增量</div>
<div>messages: Annotated[list[AnyMessage], <span style="color:var(--accent);">DeltaChannel</span>(add_messages)]</div>
</div>

<blockquote>
  <p>編按: 這個儲存最佳化的細節可以參考 LangGraph 的 <a href="https://github.com/langchain-ai/langgraph/pull/7547">DeltaChannel PR #7547</a> 和官方<a href="https://docs.langchain.com/oss/python/langgraph/persistence">持久化文件</a>的「Optimize checkpoint storage」一節。</p>
</blockquote>

<p>這也帶出一個蠻漂亮的呼應: 「快照 / 增量」這組設計，在串流時是為了省頻寬，在儲存時是為了省空間，在斷線重連和回放時又變成「接得回去、放得出來」的關鍵。同一套格式上的巧思，一次解決了好幾層的問題。</p>

<p>而「一段執行就是一串事件」這個想法，甚至已經被做進基礎設施。LangChain 在 2026 年 5 月發表了一個專門的資料庫 <a href="https://www.langchain.com/blog/introducing-smithdb">SmithDB</a>，用來扛 agent 的觀測(observability)資料，它的核心設計原則一句話就講完，而且跟這篇通篇在講的是同一件事:「一段執行是一串事件，不是一筆不可變的資料列」(a run is a sequence of events, not a single immutable row)。當 agent 動輒跑好幾個小時、夾帶上百個巢狀步驟和圖片影音，連儲存層都得照著「事件序列」的形狀重新設計。它還有個細節值得一抄: 把核心欄位和大塊內容(工具的長輸出、模型回應)分開存，主資料列只放指標，真的要看某一筆時才把大內容拉出來，列表和篩選就不會被大 payload 拖慢。從串流、儲存到觀測，大家其實都在往同一個方向收斂。</p>

<h2 id="收尾-串流是一個你要設計的應用層介面">收尾: 串流是一個你要設計的應用層介面</h2>

<p>繞了一圈，這些設計講的其實是同一件事: 串流不再是每個應用都得自己解析的低階傳輸細節，它變成了一個 <strong>你要主動設計的應用層介面</strong>。</p>

<p>而且這層格式並不貴。把上游 API 轉成你自己的事件格式，不過是讓 CPU 做點轉換，擺在動輒好幾秒的 LLM 請求延遲面前根本不算什麼。所以就算只是純聊天介面，也建議隔一層自己的格式(裡頭留一個文字增量事件，照樣有逐字打字的感覺)，差別只在要做到多細: 聊天可能只需要訊息加上少量狀態，agent 當同事的產品才需要長出完整的頻道、命名空間和回放。</p>

<p>格式設計得好不好，直接決定了你能不能只訂閱螢幕上那塊 agent 的工作、用對的抽象去呈現它、在執行越拉越長時保持連線、在使用者回訪時完整重播。用 Bromann 的話收尾最貼切: 串流 agent 該像在「寫應用程式」，而不是在「讀一堆日誌」。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="API" /><category term="Tool Use" /><summary type="html"><![CDATA[LangChain 的 Christian Bromann 寫了一篇 From Token Streams to Agent Streams(從 token 流到 agent 流),講的是一件常被當成傳輸小事、其實是該好好設計的工程問題: 當你的產品從「一次模型呼叫」長成「一個會規劃、會分派子代理(subagent)、會呼叫工具、還會中途停下來等人核准」的 agent,你在前端收到的那串資料,格式整個都得重新設計。]]></summary></entry><entry><title type="html">如何用 AI 分析 Agent traces? 持續改進 Agent 產品</title><link href="https://blog.aihao.tw/2026/06/02/agent-trace-analysis/" rel="alternate" type="text/html" title="如何用 AI 分析 Agent traces? 持續改進 Agent 產品" /><published>2026-06-02T00:00:00+00:00</published><updated>2026-06-02T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/02/agent-trace-analysis</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/02/agent-trace-analysis/"><![CDATA[<p>做 AI agent 產品最不性感、卻最重要的工作之一，就是讀 traces。一條複雜 agent 的 trace 動輒幾十上百輪，裡面藏著工具呼叫、推理步驟、跟 prompt 的來回。過去大家都靠肉眼一條一條掃，掃到眼花，而且根本掃不完。</p>

<h2 id="先搞懂-一條-agent-trace-長什麼樣">先搞懂: 一條 agent trace 長什麼樣</h2>

<p>如果你還沒實際翻過 agent 的 trace，先建立個畫面。一條 trace 就是 agent 處理「一個使用者請求」的完整紀錄: 從使用者問了什麼，到它最後回了什麼，中間每一步都被記了下來。而 agent 很少是單純的一問一答，它會推理、規劃、呼叫工具、看結果、再決定下一步，一路跑到收工。</p>

<p>這些步驟在 trace 裡是一層層巢狀的 <strong>span</strong>: 最外層的 root span 是整個 agent run，底下掛著一個個子 span，每次 LLM 呼叫(推理)、每次工具呼叫(搜尋、計算、查資料庫)都是一個 span。每個 span 還記著自己的輸入、輸出、花了多久、用掉多少 token、有沒有報錯。把父子關係攤開，大概長這樣:</p>

<div style="margin:1.5em 0;padding:1.1em 1.2em;background:var(--surface);border:1px solid var(--border);border-radius:8px;font-size:.86em;">
<div style="font-weight:600;margin-bottom:.8em;color:var(--text);">一條 trace = 一次 agent run 底下巢狀的 spans</div>
<div style="display:flex;justify-content:space-between;gap:.5em;background:var(--tag-bg);border:1px solid var(--accent);border-radius:6px;padding:.45em .7em;color:var(--accent);font-weight:600;"><span>🟦 root span · 整個 agent run — 使用者:「幫我比較台積電和聯發科」</span><span>8.2s</span></div>
<div style="margin-left:1em;border-left:2px solid var(--border);padding-left:1em;margin-top:.5em;color:var(--text-secondary);">
<div style="display:flex;justify-content:space-between;gap:.5em;padding:.3em 0;">🧠 LLM 推理: 先查兩檔基本面<span>1.8s · 1.2k tok</span></div>
<div style="display:flex;justify-content:space-between;gap:.5em;padding:.3em 0;">🔧 工具呼叫 · search_stock(台積電)<span>0.3s</span></div>
<div style="display:flex;justify-content:space-between;gap:.5em;padding:.3em 0;">🔧 工具呼叫 · search_stock(聯發科)<span>0.3s</span></div>
<div style="display:flex;justify-content:space-between;gap:.5em;padding:.3em 0;">🧠 LLM 推理: 資料夠嗎? 再查財報<span>2.1s · 1.6k tok</span></div>
<div style="display:flex;justify-content:space-between;gap:.5em;padding:.3em 0;">🔧 工具呼叫 · get_financials(2330)<span>0.5s</span></div>
<div style="display:flex;justify-content:space-between;gap:.5em;padding:.3em 0;">🧠 LLM · 整理成最終回答<span>3.2s · 2.4k tok</span></div>
</div>
<div style="margin-top:.8em;font-size:.82em;color:var(--text-secondary);">每個 span 都記著輸入、輸出、延遲、token、有沒有報錯;讀 trace 就是讀這整棵樹。✏️ 小編製圖</div>
</div>

<p>重點是: 這東西的資訊量很可觀。一段對話跨個好幾輪，可能就是幾十上百個 span、好幾 MB 的資料。Adam Lucek <a href="https://x.com/AdamRLucek/status/2059383656506920970">講得好</a>: 「trace 資料這年頭簡直貴如黃金，前提是你得知道拿它做什麼。」難就難在後半句，而那正是這篇要談的。</p>

<p>為什麼 agent 這麼難搞? 因為它跟傳統軟體很不一樣: 輸出是非確定性的、對 prompt 的一點點改動都可能很敏感、又得接受沒有邊界的自然語言輸入。講白了，你不會知道 agent 會做出什麼，直到它真的上線、跑給真實使用者。於是除錯的方式也跟著變: 從「讀程式碼、找邏輯錯誤」，變成「分析 trace」，你要抓的不再是某一行程式寫錯，而是推理出錯、工具用得很沒效率、決策品質不好這類問題，這些在程式碼裡根本看不出來。</p>

<p>2026 年一個很明顯的趨勢是: 乾脆讓 agent 來讀 agent 的 trace。LangChain 把這件事叫做「agent 改進 agent」。小編最近把 LangChain、FutureSearch、Raindrop 還有 Shreya Shankar 幾篇放在一起看，發現關鍵的分歧其實是: 這個讀 trace、找問題的「質性分析」，到底要<strong>交給誰的 agent、在什麼時間點做</strong>。</p>

<p>讀 trace 其實就是 error analysis，也就是「看你自己的資料」，這是做評估的地基。問題不在於要不要做，而在於人工讀根本沒辦法規模化: 一個團隊一天可能就產出幾千上萬條 trace，沒有人讀得完。所以生產環境的 agent 監控，跟傳統軟體監控很不一樣: 你沒辦法每一筆都細看，只能把「最值得看的子集」送進一套結構化的審查流程，讓 LLM 自動跑品質評估，再從中跑線上評估、偵測已知的失敗、探索還沒發現的問題、把好例子加進資料集、或是觸發人工審查。</p>

<p>而且這件事的回報，遠不只是「抓 bug」。LangChain 的 Viv Trivedi <a href="https://x.com/Vtrivedy10/status/2059389255957532890">點得很準</a>: 不管你想做 harness 工程、寫評估、換模型還是後訓練，這些改進 agent 的手段，幾乎全都在「先把 trace 蒐集起來、讀懂」的下游。換句話說，「先出一個 v1 → 打開 tracing → 讀懂 trace 和錯誤 → 做實驗 → 再循環」就是 agent 開發的主迴圈。所以真正的問題變成: 這個迴圈裡最累的「讀懂」這一段，能不能交給 agent?</p>

<div style="margin:1.5em 0;padding:1.2em;background:var(--surface);border:1px solid var(--border);border-radius:8px;">
<div style="font-weight:600;margin-bottom:.9em;color:var(--text);">Agent 改進迴圈</div>
<div style="display:flex;flex-wrap:wrap;align-items:stretch;gap:.5em;font-size:.9em;">
<div style="flex:1 1 110px;padding:.7em;background:var(--bg);border:1px solid var(--border);border-radius:6px;text-align:center;">📈<br />生產 trace</div>
<div style="display:flex;align-items:center;color:var(--text-secondary);">→</div>
<div style="flex:1 1 110px;padding:.7em;background:var(--bg);border:1px solid var(--border);border-radius:6px;text-align:center;">🔍<br />篩出重複的失敗</div>
<div style="display:flex;align-items:center;color:var(--text-secondary);">→</div>
<div style="flex:1 1 110px;padding:.7em;background:var(--tag-bg);border:1px solid var(--accent);border-radius:6px;text-align:center;color:var(--accent);font-weight:600;">📋<br />變成可處理的問題</div>
<div style="display:flex;align-items:center;color:var(--text-secondary);">→</div>
<div style="flex:1 1 110px;padding:.7em;background:var(--bg);border:1px solid var(--border);border-radius:6px;text-align:center;">🛠️<br />評估器 + 資料集 + 修復</div>
<div style="display:flex;align-items:center;color:var(--text-secondary);">↺</div>
</div>
<div style="margin-top:.9em;font-size:.82em;color:var(--text-secondary);">過去這整圈靠人工跑;2026 的做法是把 agent 塞進每個環節，而人類退到「審核關卡」上把關。✏️ 小編製圖</div>
</div>

<p>迴圈裡最吃功夫的，就是「讀 trace、判斷哪裡出錯」這個質性分析的環節。目前小編看下來，有三種不同作法:</p>

<div style="margin:1.5em 0;display:flex;flex-wrap:wrap;gap:.7em;font-size:.86em;">
<div style="flex:1 1 200px;padding:1em;background:var(--surface);border:1px solid var(--border);border-radius:8px;">
<div style="font-weight:600;color:var(--accent);margin-bottom:.5em;">① 你自己的 coding agent</div>
<div style="color:var(--text-secondary);line-height:1.7;">用一個自訂 skill，讓你的 Claude Code 讀 trace、找失敗模式、跑實驗驗證<br /><span style="color:var(--text);">代表: FutureSearch、ihower 自己寫的 trace 分析 skill</span></div>
</div>
<div style="flex:1 1 200px;padding:1em;background:var(--surface);border:1px solid var(--border);border-radius:8px;">
<div style="font-weight:600;color:var(--accent);margin-bottom:.5em;">② 執行時 · agent 自己診斷</div>
<div style="color:var(--text-secondary);line-height:1.7;">agent 跑的當下就自己回報故障，平台再把這些信號彙總分群<br /><span style="color:var(--text);">代表: Raindrop Self Diagnostics</span></div>
</div>
<div style="flex:1 1 200px;padding:1em;background:var(--surface);border:1px solid var(--border);border-radius:8px;">
<div style="font-weight:600;color:var(--accent);margin-bottom:.5em;">③ 事後 · 另一個專門的 agent</div>
<div style="color:var(--text-secondary);line-height:1.7;">另外派一個自主 agent 批次掃生產 trace，產出問題 / 評估器 / 修復 PR<br /><span style="color:var(--text);">代表: LangChain 監控平台 LangSmith 的 LangSmith Engine</span></div>
</div>
</div>

<h2 id="-帶你自己的-coding-agent-去讀">① 帶你自己的 coding agent 去讀</h2>

<p>這條路的精神是: 不另外養一個分析 agent，而是把 trace 接到你<strong>本來就在用的 coding agent</strong>(通常就是 Claude Code)，讓它幫你讀。</p>

<h3 id="futuresearch-一次深讀一條-trace">FutureSearch: 一次深讀一條 trace</h3>

<p>最省事的版本是 FutureSearch 分享的 <a href="https://futuresearch.ai/blog/llm-trace-analysis/">這篇</a>。作者說他花很多時間在讀 agent 的 trace，一半是為了知道怎麼改進 agent，一半是為了確認「跑出來的評估到底能不能信」。</p>

<p>他們的 trace 存在 <a href="https://langfuse.com/">Langfuse</a>，而所謂「接給 Claude Code」其實樸素到不行: 直接把一條 Langfuse 的 trace 連結貼進去，下一個 <code class="language-plaintext highlighter-rouge">review this trace: &lt;Langfuse 連結&gt;</code> 指令就開工了。背後是一個自訂的 <code class="language-plaintext highlighter-rouge">/review-agent-trace</code> skill，裡面放滿常見失敗模式的範例，外加「怎麼形成假設、怎麼跑快速實驗來驗證」的指引。他們把要找的問題分成四大類，蠻實用的:</p>

<p>1️⃣ <strong>鷹架程式碼(scaffolding)的 bug</strong>: 系統 prompt 設定錯、agent 在某些條件下莫名其妙就停了</p>

<p>2️⃣ <strong>工具失效</strong>: 搜尋回傳一堆亂碼或無意義的結果</p>

<p>3️⃣ <strong>prompt 引發的問題</strong>: 框架太狹隘、指令互相衝突</p>

<p>4️⃣ <strong>推理失敗</strong>(最難): agent 搞混日期、亂呼叫工具、漏掉任務的一部分</p>

<p>最有意思的，是模型代差帶來的質變。他們早期用 Sonnet 3.7 時，得把分析拆成數十個很狹隘的 prompt，套到每一個 ReAct 步驟上，又貴又脆弱，還是會漏掉一堆;而且 Sonnet 3.7 太容易相信 agent 的自我評估，agent 說「好，我找到答案了」它就信了。換成 Opus 4.6 驅動的 Claude Code 之後，一個 session、一段通用的 prompt，就能把整條 trace 從頭吃到尾，不需要人工切割，而且「真的能動」。更厲害的是這個 skill 不只挑錯，還會自己<strong>形成假設、跑實驗來驗證</strong>: 抓到可疑的問題後，它會去模擬一個改過的 prompt 跑跑看，確認真的能修好，才把建議交出來。</p>

<p>那這份報告長什麼樣? 文章貼了兩個實際的輸出。一個是 agent 該查 Google 卻沒查，導致它沒注意到美伊局勢升溫;Claude Code 不只指出這點，還追到根因(這是 Opus 4.6 在 effort = low 下的通病)，並給出具體的修法(把 effort 調到 medium 就好)。另一個更能看出它的「實驗精神」:</p>

<div style="margin:1.3em 0;padding:1em 1.2em;background:var(--surface);border-left:3px solid var(--accent);border-radius:6px;font-size:.9em;">
<div style="font-weight:600;margin-bottom:.6em;color:var(--text);">🔍 一則 <code>review this trace</code> 的輸出</div>
<div style="line-height:1.85;color:var(--text-secondary);"><strong style="color:var(--text);">問題:</strong> agent 要找四月中到十一月的最高收盤價，但它找到的第一個資料集只涵蓋到十月，就停手回報了錯誤答案<br /><strong style="color:var(--text);">根因:</strong> agent 太早滿足，沒檢查資料是否涵蓋完整區間<br /><strong style="color:var(--text);">驗證:</strong> 它接著模擬幾個用改過 prompt 的 ReAct 步驟實際跑跑看，確認這樣改真的能修好</div>
</div>

<p>還有一點很值得提: 維護成本幾乎是零。他們在 skill 裡順手寫清楚「去哪裡拿 trace、agent 的實作在哪幾個 Python 檔、評估任務的標準答案在哪」，省得 Claude Code 每次重新摸索;再配一份 <code class="language-plaintext highlighter-rouge">CLAUDE.md</code>，鼓勵它「發現哪條指令過時了就自己更新 skill」，等於這個分析工具會自我維護。</p>

<h3 id="ihower-自己寫的-trace-分析-skill-生產規模的取樣審查">ihower 自己寫的 trace 分析 skill: 生產規模的取樣審查</h3>

<p>那如果要上到生產規模、一天好幾千條 trace 呢? 不必換成大平台，自帶 agent 這條路一樣能做，只是得先解決一個問題: <strong>該看哪些?</strong> ihower 自己就手刻了一個這樣的 trace 分析 skill，用 <a href="https://www.braintrust.dev/">Braintrust</a> 的 <code class="language-plaintext highlighter-rouge">bt</code> CLI 把 trace 拉出來，小編借來當第二個案例。生產流量一大，就不可能每筆都讀，重點是把「最值得看的子集」送進結構化的審查。它用三種取樣訊號互補:</p>

<ul>
  <li>🚩 <strong>明確的壞訊號</strong>: 被護欄(guardrail)攔下的、使用者倒讚的、出現 error 的、工具呼叫爆量的、對話超過 N 輪的，這些<strong>全部撈進來看</strong></li>
  <li>🤖 <strong>judge 篩選</strong>: 用便宜的模型挑出可疑的，例如已知失敗模式的 judge、情緒 / 風險的 judge</li>
  <li>🎲 <strong>隨機基準</strong>: 再隨機抽一定比例，才看得到那些「沒觸發任何訊號」的隱性問題，不會被訊號本身的偏誤綁住</li>
</ul>

<p>skill 裡把這寫成一套加權抽樣規則: 全部 error 加全部 guard 都看，高互動輪數的隨機抽 75%、中互動抽 50%、基準流量只抽 15%。背後的邏輯是: 稀有但高風險的全看，高互動代表高資訊量所以多看，剩下的用隨機覆蓋。用比例(而不是固定條數)，抽樣量會隨流量自動縮放，審查量就跟著訊號密度走。取樣完再把樣本分組、切片，一個子 agent 讀一片，最後合成一份報告。</p>

<p>而這份報告刻意用一套<strong>固定骨架</strong>，讓每次跑出來的報告都能直接比較:</p>

<div style="margin:1.3em 0;padding:1.1em 1.2em;background:var(--surface);border:1px solid var(--border);border-radius:8px;font-size:.88em;">
<div style="font-weight:600;margin-bottom:.7em;color:var(--text);">📄 固定報告骨架(節錄)</div>
<div style="padding:.45em 0;border-bottom:1px solid var(--border);"><strong style="color:var(--accent);">1 · 整體讀評</strong>: <span style="color:var(--text-secondary);">一句話總評，agent 哪裡好、哪裡有明顯問題</span></div>
<div style="padding:.45em 0;border-bottom:1px solid var(--border);"><strong style="color:var(--accent);">2 · 立即動作建議</strong>: <span style="color:var(--text-secondary);">1–5 條，放在最前面，讓報告讀起來像決策文件</span></div>
<div style="padding:.45em 0;border-bottom:1px solid var(--border);"><strong style="color:var(--accent);">3 · 問題類別</strong>: <span style="color:var(--text-secondary);">只列已確認 / 可能，給穩定編號 I-1、I-2(使用者影響 / 負責方 / 修法)</span></div>
<div style="padding:.45em 0;border-bottom:1px solid var(--border);"><strong style="color:var(--accent);">4 · 建議人工追查的對話</strong>: <span style="color:var(--text-secondary);">Top 10–20，連回上面的問題編號</span></div>
<div style="padding:.45em 0;border-bottom:1px solid var(--border);"><strong style="color:var(--accent);">5 · 使用者滿意 / 不滿意訊號</strong>: <span style="color:var(--text-secondary);">每筆標「明確」或「推測」，附原話與 trace 連結</span></div>
<div style="padding:.45em 0;"><strong style="color:var(--accent);">6 · 用戶模式與常見意圖</strong>: <span style="color:var(--text-secondary);">使用者都在問什麼、從哪個產品表面進來</span></div>
<div style="margin-top:.7em;font-size:.82em;color:var(--text-secondary);">骨架固定，每次報告就能直接比較，看出版本之間的進退。✏️ 小編製圖</div>
</div>

<p>其中有一條規矩很關鍵: <strong>事實與推論要分開標</strong>，別把「樣本裡直接看到的」跟「我推測的」糊成同一句。</p>

<blockquote>
  <p>編按: 這種「依流量決定怎麼看」的思路，Raindrop 共同創辦人 Ben Hylak 在 <a href="https://www.howtoeval.com/">howtoeval.com</a> 整理成一條階梯: 一天十來條時直接全讀;量一大就得讓系統告訴你哪些值得看，從 Stumbles(在原始紀錄裡撈苗頭)→ Issues(重複出現的就立案)→ Signals(值得長期監控的行為)→ Experiments(改完用真實流量做 A/B 驗證)。ihower 的加權取樣，就是這條階梯高流量那一端的具體做法。</p>
</blockquote>

<h2 id="-執行時-讓-agent-自己診斷自己">② 執行時: 讓 agent 自己診斷自己</h2>

<p>第二種位置更前面，前到 agent 還在跑的當下。Raindrop 的另一個功能 <a href="https://www.raindrop.ai/blog/agent-self-diagnostics">Agent Self Diagnostics</a> 就是這個想法。小編必須說，這是這輪看下來<strong>最有創意的一招</strong>: 大家都在想怎麼「事後把 trace 讀懂」，Raindrop 反過來問「那不如讓 agent 在當下自己講」。背後的觀察蠻反直覺但很對: 「現在的 agent 比以前聰明太多了，它們往往比我們更清楚自己哪裡壞了。」</p>

<p>所以與其等事後有人(或有 agent)去翻 trace 才發現問題，不如讓 agent 在執行過程中<strong>自己回報故障</strong>，預設分成四種信號: 缺少上下文、工具重複失敗、能力缺口、任務徹底失敗(也能自訂)。</p>

<p>機制其實很乾淨，而且回答了一個容易誤會的問題: 這不是平台另外派一個 agent 來分析你，而是<strong>你自己那個 agent</strong>。你用 Raindrop SDK 把模型呼叫包一層(<code class="language-plaintext highlighter-rouge">raindrop.wrap(ai, { selfDiagnostics: { enabled: true } })</code>)，SDK 就會<strong>偷偷塞一個隱藏工具</strong>(預設叫 <code class="language-plaintext highlighter-rouge">__raindrop_report</code>)進你 agent 的工具清單，連 system prompt 都不用你動。照 <a href="https://raindrop.ai/docs/integrations/vercel-ai-sdk">官方文件</a> 的說法: 啟用之後，SDK 會塞進一個隱藏工具，當 agent 撞上無法復原的問題時，它會靜默呼叫這個工具。每個信號類別，本質上就是給這個工具的一段描述(「什麼情況該回報 missing_context」)，你的模型在跑的當下自己判斷卡在哪、靜默呼叫這個工具，SDK 再把信號(類別 + 一段它自己寫的白話說明，綁在同一個 <code class="language-plaintext highlighter-rouge">eventId</code> 上)送回 Raindrop。所以這裡的「智能」，就是你的底層模型在讀那幾段工具描述、對自己的處境做第一層分類，不需要另一個分析模型。回到 Raindrop 之後，平台才負責跨多次執行的彙總、分群、追趨勢、告警。</p>

<p>換句話說，Raindrop 在這裡做的是<strong>分散式的質性分析</strong>: 把「我哪裡卡住了」這種第一層判斷推到 agent 自己身上(誰會比它更清楚?)，平台只負責把這些信號跨多次執行聚合起來。比起 LangSmith 那種「養一個外部大 agent 事後讀原始 trace」，這是把分析拆開、塞到源頭的另一種賭法。</p>

<p>不過也要老實說一句，而且這話是 Ben Hylak 自己講的: 這招創意十足，實戰上卻<strong>沒有表面看起來那麼好用</strong>。他在最新的 <a href="https://www.howtoeval.com/">agent 評估指南</a>(這篇非常讚，推薦一讀)裡直言，自診信號比較像「大水漫灌」: 一堆隨機回報裡，偶爾撈到一個有趣的，得花不少功夫去調靈敏度，才能變成穩定可用的訊號;更微妙的是，你給那個隱藏工具的<strong>措辭</strong>，會直接左右 agent 到底報不報，說穿了它就是一個二元分類問題。所以小編的評語是: 概念很漂亮、值得關注，但別期待它一開下去就一勞永逸。</p>

<h2 id="-事後-另外派一個專門的-agent-批次掃生產-trace">③ 事後: 另外派一個專門的 agent 批次掃生產 trace</h2>

<p>第三種位置在最後面: 等 trace 都進了生產資料庫，另外派一個專門的 agent，主動、批次地把整個迴圈跑完。最完整的代表，是 LangChain 在它的監控平台 LangSmith 上、五月推出的 <a href="https://www.langchain.com/blog/introducing-langsmith-engine">LangSmith Engine</a>。它本身就是一個 agent，做三件事: 找出重複出現的失敗、把失敗變成「可處理的問題」、再把問題轉成能長期沉澱的東西(評估器、資料集範例、修復 PR)。重點不在指出「這條 trace 壞了」，而是把一個生產失敗，變成團隊未來能拿來測試、防止退步的資產。</p>

<p>LangChain 還另外寫了一篇<a href="https://x.com/palashshah/status/2056786835322687640">技術細節文</a>講他們怎麼蓋這個東西，小編覺得比公告本身有料多了，幾個工程巧思特別值得學:</p>

<p>🔹 <strong>先壓縮再讀(trajectory)</strong>: 生產專案的回看窗口裡可能有上萬條 trace，全塞進上下文根本不可行，光 10 條長 agent 的 trace 就能有上百個工具呼叫。解法是先把每條壓成「骨架」: 每一輪只留角色、工具名稱、延遲、內容字數，不放完整內容。agent 先看骨架找出「形狀可疑」的，再回頭把需要的細節撈進來。</p>

<p>🔹 <strong>篩選與調查兩階段分離</strong>: 第一階段用便宜的 Haiku 當「篩選員」，一次掃約 20 條，只回報「乾淨 / 可能有問題」，不做診斷;第二階段才派「調查員」把可疑 trace 的完整內容和程式碼撈進來深入分析。篩選員追求規模，調查員追求深度，職責切乾淨。</p>

<p>🔹 <strong>限制問題分類</strong>: Engine 不讓 agent 自由發明問題類別，而是給它一份預先定義好的清單(像 <code class="language-plaintext highlighter-rouge">pii_leak</code>、<code class="language-plaintext highlighter-rouge">agent_looping</code>、<code class="language-plaintext highlighter-rouge">incorrect_tool_args</code>、<code class="language-plaintext highlighter-rouge">missing_tool</code>)。理由很實在: 讓 agent 亂取類別，輸出就難以評估、也難以信任。</p>

<p>🔹 <strong>評估器先測過再交出來</strong>: Engine 為每個問題提一個評估器，但在拿給使用者之前，會先用 <code class="language-plaintext highlighter-rouge">test_evaluator</code> 工具拿證據 trace 跑一遍，確認它真的抓得到對應的失敗(回傳 PASS / FAIL / SKIPPED)。因為一個評估器「看起來合理」跟「真的抓得到問題」是兩回事。</p>

<p>🔹 <strong>找問題的 agent 跟修問題的 agent 分開</strong>: 早期他們讓同一個 agent 又找問題又提修復，結果什麼都做不好。後來拆成: 主 agent 只負責找問題、產出評估器和資料集，需要修的就打一個 <code class="language-plaintext highlighter-rouge">needs_fix</code> 標籤，再交給專門的修復 agent 去提修改。</p>

<p>🔹 <strong>跨次執行的記憶(Agent Overview)</strong>: Engine 維護一份類似 <code class="language-plaintext highlighter-rouge">AGENTS.md</code> 的活文件，記著「你的 agent 在做什麼、會出現哪些 trace 結構、常見的地雷、團隊的偏好」，跨次更新，甚至會從使用者的動作(關掉某個問題、建立某個評估器)學習。</p>

<p>這套已經實際幫 Cogent、Harmonic、Campfire 等團隊處理過影響數千條 trace 的問題。而且這也不只 LangChain 一家在做，Arize、Braintrust、Databricks 在 2026 年都推了方向類似的東西，「平台幫你自動分析 trace」已經是一整個賽道。</p>

<h2 id="踩個剎車-agent-真的會分析嗎">踩個剎車: agent 真的會「分析」嗎?</h2>

<p>三種作法講完，得踩一下剎車。Shreya Shankar 有一篇 <a href="https://www.sh-reya.com/blog/ai-qual-analysis/">Exploring Agent-Assisted Qualitative Analysis</a>(用 agent 協助做質性分析)，做了一個很扎實的實驗。她本身就是做這行研究的(剛拿到教職)，這次直接拿 agent 去做學術界的「質性分析」: grounded theory，也就是讀大量雜亂的資料、貼標籤(編碼)、再歸納成高階的主題。她問了一個很本質的問題: 用 agent 做這種分析，到底行不行?</p>

<p>她的素材是 451 條推文(Sholto Douglas 問「什麼時候你會改用別的模型、而不是 Claude?」底下的回覆)，要分析的是「使用者為什麼想跳槽」。她跑了六種條件，差別在兩個軸: agent 有沒有被教 grounded theory 的方法、以及人類介入到什麼程度(完全不介入、逐批審編碼、讀備忘給回饋、甚至兩個 agent 互相對照)。</p>

<p>她先點出為什麼這題對 AI 特別難: 質性分析的「對的答案」高度依賴語料以外的脈絡，需要一種說不太清楚的品味與判斷(不像「程式有沒有編譯過」有標準答案);更麻煩的是，評斷的標準本身會在分析過程中不斷演化，而現在的 agent 偏偏假設目標固定、又急著收斂。實測下來，毛病很一致:</p>

<p>❌ <strong>是轉述，不是分析</strong>: agent 產生的編碼數量，跟推文長度高度相關(ρ = 0.81)，但長推文往往只是同一個抱怨講得更長，它卻照著字數狂貼標籤。而且 93.8% 的編碼整份語料只用過一次，明明完整的編碼簿就在上下文裡，它卻不重用、每條都發明新標籤，根本沒在歸納。</p>

<p>❌ <strong>沒把資料編碼完</strong>: 雖然被要求讀完整份、甚至多跑幾輪，agent 卻常讀到一半就自己宣布「分析完成」。表現最好的一組只編碼了 68%，最差的只有 5.5%，多數落在 25–35%;還有些推文直接被跳過、給了空標籤。</p>

<p>❌ <strong>工作管理很差</strong>: 它傻傻地一條一條順序處理，不會像人類研究者那樣切分資料、有策略地採樣、邊讀邊組裝。最離譜的一組: 兩個 agent 跑到逾時後，主 agent 乾脆改寫成「關鍵字比對的 Python 腳本」(看到 sycoph 就標 overly_agreeable)，這已經完全不是在做分析了。</p>

<p>❌ <strong>回饋迴圈很弱</strong>: 她講一次「我在意競品比較」，agent 就過度擬合、把它捧成最大的分類;她說「標籤太多了」，agent 收斂一下、下一批又故態復萌(最後 96.5% 還是一次性標籤)。要嘛過度擬合早期回饋，要嘛轉頭就忘。</p>

<p>更值得 agent 圈警惕的，是她當「人類審核者」的體感。她發現一個陷阱: agent 產出的高階分類，常常含糊到無法反駁，例如「可靠性與信任」這種大到什麼抱怨都塞得進去的類別，你很難說它錯，但這種「不可證偽」恰恰不是好分類。她一句話點破: <strong>含糊的分類讓人容易相信、卻無法據以行動</strong>。如果連「幻覺算不算主要問題」這個類別都定義不清，那它接著提出的「修掉幻覺」方案，你也沒有基礎去信任。這正好戳中 LangSmith Engine 那類「自動產問題、提修復」的要害。</p>

<p>她的結論很精準: agent「能很快做完機械性的部分，但缺乏品味」。所以真正有意思的問題，根本不是「如何自動化質性分析」，而是「如何打造一個系統，讓人類的品味跟 agent 的規模真正組合起來」。而她隨手點的一個未來方向，剛好就是這整篇的主題: 把這套拿去做 <strong>agent 的 error analysis: 資料本身就是 agent 的 trace，目標是找出重複出現的失敗模式</strong>。換句話說，本文講的三種作法，正是這個還沒被解好的題目的早期答案。</p>

<p>Shreya 這個質疑，跟前面三種作法其實不矛盾，反而互補。注意到沒有: LangSmith Engine 那麼多設計(固定分類、評估器先測過、所有問題都進「待審核」狀態等人按)，本質上都是在替 agent 的「缺乏品味」做防護欄;FutureSearch 乾脆讓你自己的 Claude Code 在你眼皮底下開車，Raindrop 則讓 agent 把判斷攤開成可追蹤、人能在儀表板上把關的信號，也是同一回事。沒有人真的在假裝 agent 能取代人，差別只在於把人擺在迴圈的哪個位置。</p>

<h2 id="想自己做好-trace-分析-幾條小編的收穫">想自己做好 trace 分析? 幾條小編的收穫</h2>

<p>把上面的成功和翻車擺在一起，如果你打算自己搭一套用 agent 分析 trace 的流程，小編覺得有幾條原則特別關鍵:</p>

<p>🔸 <strong>別讓 agent 邊讀邊即興生分類</strong>: 這是 Shreya 那組實驗最大的教訓。讓 agent 一條一條讀、順手就把錯誤分類(axial code)生出來，它幾乎永遠在「換句話說」而不是「歸納」。比較好的做法，是把開放編碼和歸類拆開，或讓 agent 只負責提議、由你來定分類。</p>

<p>🔸 <strong>迴圈的編排交給程式碼，不要交給 agent 判斷</strong>: agent 很愛走一走就自己宣布「分析完成」(Shreya 那組最慘只讀了 5.5%)。所以「要讀哪些、分幾批、跑幾輪、什麼時候停」這種流程控制，用程式碼寫死(像 ihower 的取樣腳本、或 LangSmith 用篩選員去派發)，別讓 agent 自己拿主意。</p>

<p>🔸 <strong>用一個很簡單的標準，檢驗你抓到的失敗模式</strong>: 每抓出一個失敗模式，問自己一句: 它能不能寫成一條清楚的「納入 / 排除」準則，並且轉成一個自動檢查(LLM-as-judge 或程式斷言)? 不能的話，它就太抽象了，只能拿來自我安慰，沒辦法真的把迴圈閉合起來。這正是 Shreya 說的「含糊分類無法行動」的解藥，也是 LangSmith Engine 堅持每個問題都要配一個測過的評估器的理由。</p>

<p>🔸 <strong>把分類體系當成版本化的產物</strong>: 先把詞講清楚: 一個 axial code 是「一個錯誤類別」，分類體系(taxonomy)則是「整組類別」合起來的結構，改分類體系，通常就是在新增、合併、刪掉裡面那些類別。原則是: 分類體系一改，就回頭重跑，別讓新舊標準混在一起。而且人該介入的點，是在<strong>分類體系這一層，不是逐條標註那一層</strong>: 讓 agent 提議分類，你來做判斷(合併、刪除、調整、重新加權)，而不是自己一條一條編碼、當人肉標註工。對應的審查介面也該以群集為單位，每個群集直接附幾條範例 trace 連結讓你抽查，並秀出每輪之間的差異(哪些失敗模式是新增的、哪些類別搬了家)。</p>

<h2 id="迴圈在閉合但方向盤還在人手上">迴圈在閉合，但方向盤還在人手上</h2>

<p>把這幾篇串起來，2026 的圖像蠻清楚的: 那個「讀 trace → 找問題 → 修 → 驗證」的改進迴圈，正在被 agent 一段一段接管。差別只在於那個最關鍵的質性分析，你想放在哪裡: 交給你自己的 coding agent(FutureSearch 用一個 skill 配 Claude Code)、讓 agent 在執行的當下自己診斷(Raindrop Self Diagnostics)、還是另外派一個專門的 agent 事後批次掃生產 trace(LangSmith Engine)。同一件「讀 trace、找問題」的事，被擺到了流程裡三個很不一樣的位置。</p>

<p>但 Shreya 的提醒值得貼在牆上: agent 是規模的放大器，不是品味的替代品。它能把上萬條 trace 篩到剩幾十個值得看的問題，這已經是巨大的價值;可是「哪個問題真的重要、這個失敗模式該怎麼定義、修復的方向對不對」，這些需要品味的判斷，方向盤還是握在人手上。</p>

<blockquote>
  <p>編按: 想看一個把這套迴圈跑到生產規模、又刻意保留人類品味的完整案例，可以看小編之前整理的 <a href="https://blog.aihao.tw/2026/06/01/replit-agent-eval-at-scale/">Replit 如何規模化評測和持續改進 Vibe coding</a>，他們用離線的 ViBench 加上線上的 trace 分群 Telescope，雙引擎驅動每天出貨的 coding agent。</p>
</blockquote>

<p>說到底，agent 上線之後本來就是一路邊跑邊修: 你不可能事先想好所有失敗，trace 分析就是你持續發現問題、持續改進的命脈。所以把 agent 做好，從來不只是一個模型問題，而是一個得長期經營的營運問題;而這條營運迴圈裡最累的一段，正是讀 trace，也正是這篇從頭談到尾、最值得想辦法交給 agent 分擔的工作。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Eval" /><category term="Observability" /><summary type="html"><![CDATA[做 AI agent 產品最不性感、卻最重要的工作之一，就是讀 traces。一條複雜 agent 的 trace 動輒幾十上百輪，裡面藏著工具呼叫、推理步驟、跟 prompt 的來回。過去大家都靠肉眼一條一條掃，掃到眼花，而且根本掃不完。]]></summary></entry><entry><title type="html">當寫 code 不再是瓶頸: Anthropic 的 AI-native 工程組織，與那些不買單的聲音</title><link href="https://blog.aihao.tw/2026/06/01/ai-native-engineering-org/" rel="alternate" type="text/html" title="當寫 code 不再是瓶頸: Anthropic 的 AI-native 工程組織，與那些不買單的聲音" /><published>2026-06-01T00:00:00+00:00</published><updated>2026-06-01T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/01/ai-native-engineering-org</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/01/ai-native-engineering-org/"><![CDATA[<p>Anthropic 的 Fiona Fung (帶 Claude Code 與 Cowork 工程團隊) 有一場演講 <a href="https://www.youtube.com/watch?v=igO8iyca2_g">Running an AI-native engineering org</a>，講的是當 coding 不再是瓶頸之後，他們把整個團隊的工作慣例 (norms) 砍掉重練的過程。小編覺得這是目前少數講得很具體的「AI-native 工程組織到底長什麼樣」第一手紀錄，蠻值得看。</p>

<blockquote>
  <p>編按: 這場演講的中文重點，<a href="https://x.com/dotey/status/2054086398328656383">宝玉 (@dotey) 整理過一個摘要版本</a> (按讚數破四百，傳得蠻廣)，想先快速掃過重點的可以參考。等一下會提到的 Toby 那串反駁，衝著的就是這篇摘要。</p>
</blockquote>

<p>不過這場演講放出來之後，網路上也出現了不少不買單的聲音。剛好 Claude Code 之父 Boris Cherny 最近接受 <a href="https://www.platformer.news/boris-cherny-interview-ai-jobs/">Platformer 訪談</a>又把話說得更滿:「coding 已經被解決了」「軟體工程師這個頭銜今年可能開始消失」。所以這篇就把三件事疊在一起看: Fiona 講了什麼、反對的人在吐槽什麼、以及一票真的在寫嚴肅軟體的人，怎麼反駁「人類不用再寫 code」這個說法。</p>

<h2 id="一fiona-的演講-瓶頸搬家了">一、Fiona 的演講: 瓶頸搬家了</h2>

<p>Fiona 整場的主旋律就一句話: 「過去成就你的，未來不一定還能成就你 (what served you prior may not serve you any longer)」。</p>

<p>她說過去這麼多年，工程師的頻寬 (engineering bandwidth) 一直是最貴的東西，寫 code、寫測試、重構都很花時間，所以我們發明了一堆流程 (瀑布、敏捷、六個月 roadmap、設計文件)，本質上都是因為「打 code 很貴」，要好好規劃才不會浪費。</p>

<p>她也提醒這不是業界第一次得適應。她職涯早期在做 Visual Studio 2005，當年軟體還是燒在 CD-ROM 上出貨 (更早是磁片)，有硬死線要趕著把成品送進工廠壓片、裝盒、上架; 後來能線上發佈，整個出貨方式就被改寫了一次。每次底層條件一變，做事方法就得跟著重來。</p>

<p>但現在這個前提變了。在 Claude Code 團隊，「coding 已經很少是慢的那一環」，而且產出量暴增。瓶頸於是從「打字」搬到了它周圍的所有事情，用她那張投影片的講法: 舊瓶頸是「寫與出貨 code 的頻寬」(寫 code、寫測試、重構)，新瓶頸是「驗證、審查、跨職能協作、安全」。問題從「誰來寫」變成「這段 code 對嗎? 誰來 review? 之後誰維護?」</p>

<p>她有個觀察小編特別喜歡: 很多流程是「悄悄地不再有用 (quietly stops working)」。因為流程很少會自己消失，我們總是一層一層往上疊，疊到後來連 SLA 都要排優先順序。於是 Claude Code 團隊把一大堆慣例砍掉重練。最核心的一句話是: 他們把力氣從「事前」搬到了「事後」，少花時間規劃和爭論，多花時間驗證和對齊。</p>

<div style="display:flex; gap:16px; flex-wrap:wrap; margin:24px 0;">
  <div style="flex:1; min-width:240px; border:1px solid var(--border); border-radius:8px; padding:16px 20px; background:var(--surface);">
    <div style="font-weight:700; font-size:14px; letter-spacing:0.04em; color:var(--text-secondary); margin-bottom:10px;">少做了 ↓</div>
    <ul style="margin:0; padding-left:18px;">
      <li>六個月 roadmap、事前規劃</li>
      <li>每件事都先寫設計文件</li>
      <li>頻繁的產品評審</li>
      <li>白板上的技術爭論</li>
    </ul>
  </div>
  <div style="flex:1; min-width:240px; border:1px solid var(--accent); border-radius:8px; padding:16px 20px; background:var(--tag-bg);">
    <div style="font-weight:700; font-size:14px; letter-spacing:0.04em; color:var(--accent); margin-bottom:10px;">加倍投入 ↑</div>
    <ul style="margin:0; padding-left:18px;">
      <li>驗證與自動化 (shift left)</li>
      <li>團隊對齊與文化</li>
      <li>code review 的人類把關</li>
      <li>用 PR、原型代替文件討論</li>
    </ul>
  </div>
</div>

<p>底下分三塊講她實際改了什麼。</p>

<h3 id="規劃與技術辯論-從先想清楚到先做出來">規劃與技術辯論: 從「先想清楚」到「先做出來」</h3>

<p>規劃變少，而且即時，她叫它 JIT planning (just-in-time，借自 JIT 編譯)。六個月 roadmap 寫完三個月就過期了，所以乾脆少寫; 設計文件大幅減少，改成「與其寫一份 doc，不如丟一個 PR」「有想法就去 prototype」; 產品評審也少做，因為局勢變太快，不如先做原型、讓內部大量試用、再出貨收真實回饋。</p>

<p>至於「加倍投資的驗證」，她叫它「左移 (shift left)」: 與其等 code 出去後自己搶在使用者之前抓 bug，不如用更多自動化，在更接近源頭的地方就攔下來。她講了個很有共鳴的例子: 有次她修了個 bug，隔天看到有人在 Boris 的 thread 上回報「我發現一個 bug」，當下那種「該不會是我吧」的下沉感，所以她希望不管什麼角色，每個人對自己送出的改動都更有信心。</p>

<p>技術辯論也變了，她的講法是「code 說了算」。她剛加入時想做一個重構，本來想拉 Boris 去會議室白板大戰，後來想想，不如直接讓 Claude 生成三個不同版本的 PR，連對所有 API 呼叫端的影響都一起攤開來看。「當『做出來』很便宜，『吵架』就變貴了」。但她特別強調，這反而讓「團隊文化與對齊」更重要，絕對不能變成「最後一個 check-in 的人贏」。</p>

<h3 id="程式碼的歸屬與審查-誰寫的變成一個怪問題">程式碼的歸屬與審查: 「誰寫的?」變成一個怪問題</h3>

<p>因為所有 PR 都是 Claude 輔助生成的，「這段 code 是誰寫的?」現在問起來有點怪。她的建議是「往下追問 (double click)」你真正想問的是什麼: 是想找誰造成了這個 regression? 還是想找懂這塊的專家? 還是只是想了解脈絡? 想清楚之後，很多都能自動化，她甚至把每天早上配咖啡、進客戶回饋頻道請 Claude 摘要的儀式，直接變成了一條 routine 自動跑。</p>

<p>至於 code review，重點是分清楚哪裡信任 Claude、哪裡還是要人。風格、lint、補測試、甚至抓些小 bug 並修掉，這些重度交給 Claude; 但本著「信任但要查證 (trust but verify)」，法務審查、信任邊界與安全敏感的 code、產品品味，她還是堅持要拉真人專家進來。(她講了個好笑的例子: 有次想把終端機裡的 Claude 裝飾成雪人，結果做出來像花生人 Mr. Peanut，是設計師夥伴一眼點出來的。品味這種事，模型還沒接手。)</p>

<p><img src="/assets/images/ai-native-engineering-org/code-reviews.jpg" alt="" /></p>

<blockquote>
  <p>編按: 「你們怎麼跟得上 code review?」是她說過去半年被其他工程主管問最多的一題。這也呼應了新瓶頸的核心: 當生成變快，驗證與審查就成了真正的壓力點。這個主題小編之前整理過一整篇 <a href="https://blog.aihao.tw/2026/04/20/ai-code-review-bottleneck/">AI 時代的 Code Review: 當 Pull Request 成為新瓶頸</a>，挑了五篇 2026 年的文章談 review 跟不上之後的各種路線 (規格驅動開發、直接幹掉 review、五層信任框架…)，想往下深挖的可以接著看。</p>
</blockquote>

<h3 id="團隊組成與組織形狀">團隊組成與組織形狀</h3>

<p>她重度看兩種工程師: 「有產品 sense 的創意建造者」跟「深度系統專家」(例如做 Claude Code Remote 這種分散式系統就少不了後者)，反而比較不看重「原始產出量」，因為那個模型已經幫你補上了。角色也在互相滲透: 她是工程師、自認文筆很糟，就讓 Claude 當她的「內容設計」夥伴，幫忙把問卷文案改得精簡 (終端機裡每一行空間都很珍貴); 反過來她的 PM 也大量在寫 code。</p>

<p>組織則盡量扁平、scrappy，她甚至要求每個 manager 都先從 IC 做起、親手 dogfooding，招募夥伴一度覺得她瘋了，說「哪個 manager 會願意先當 IC」。團隊還立了三條核心原則: ① 每個成員 (含跨職能夥伴) 都要用 Claude Code; ② 能 Claude 化的就 Claude 化 (Claudify everything); ③ 明確授權大家去砍掉沒用的流程。</p>

<p>最後談「這套到底有沒有用」，她給了三個可參考的指標: onboarding 上手時間大幅縮短、PR 週期時間縮短、Claude 輔助的 commit 比例上升 (她說過去四個月幾乎沒看過非 Claude 輔助的 commit)。但她也提醒: 不要盯著新聞標題那種「本公司 X% 的 code 由 AI 生成」，要回去看你真正想解決的問題、想做得更好的產品是什麼，品質與可靠度才是該盯的。</p>

<p>小編覺得這場演講最實在的地方，是它不講願景、只講「我們實際改了哪些慣例」。而且 Fiona 自己留了幾個沒答案的問題給自己 (iOS/Android 還要不要分隊、自動審查要推多遠、角色模糊後怎麼讓大家都覺得有產出感)，姿態其實蠻誠實的。</p>

<p>她最後留給聽眾一個可以帶回去做的事: 挑一個你最「吵」的工作流程 (最貴的、或大家最不想碰的那個)，問一句「它還有在服務當初的目的嗎?」。她舉了個例子: 以前有個 50 人的週會，她發現大家全程都在滑筆電、只有輪到自己報告時才抬頭講兩句，於是她問了句「我們到底為什麼要開這個會?」，然後就把它取消了。</p>

<h2 id="二反方一-toby-的吐槽">二、反方一: Toby 的吐槽</h2>

<p>不過工程師 <a href="https://x.com/TobyAtLarge/status/2058591481951400240">Toby (@TobyAtLarge) 就不太買單</a>，他針對<a href="https://x.com/dotey/status/2054086398328656383">宝玉那篇演講摘要</a>寫了一串挺兇的反駁 (他自己也自嘲「好擔心自己變成槓精」)。幾個重點:</p>

<p>1️⃣ <strong>「code 幾乎免費」是演講修辭，不是事實</strong>。code 確實便宜很多了，但「好的 code」(考慮架構、邊界條件、測試覆蓋) 仍然有成本。而 Fiona 自己也承認「維護成本不會跟著歸零」，代表她心裡清楚，只是台上為了衝擊力把話說滿了。</p>

<p>2️⃣ <strong>Source of truth 不只一層</strong>。她說「code 是唯一的事實來源」太絕對。真實工程至少三層: 需求 (為什麼做) → 設計 (怎麼做) → code (做出來了)。code 是執行結果，常常處在折中狀態。沒有需求文件做參照，你連「這個折中是刻意的還是 bug」都分不清。</p>

<p>3️⃣ <strong>JIT planning = 沒有規劃</strong>。他覺得對一個正經的 toB/toC 產品不做規劃很誇張，「先發 PR、少開會」容易導致功能失控。而且諷刺的是，從使用者端看，Claude Code 的發布節奏看起來反而是有規劃、有節制的，不像她描述的那種野蠻生長。</p>

<p>4️⃣ <strong>整體判斷: 特殊環境裡的個人經驗，被包裝成了普適方法論</strong>。Anthropic 是極端特殊的環境: AI-native 產品、極高人才密度、公司本身就是最好的 dogfooding 場景。在這種環境跑一年的實驗，外推到「所有工程團隊都該這樣」，折扣要打得很大。</p>

<p>小編覺得 Toby 第三點對 open question 的批評有點過嚴 (她明明就說那是「還在想的問題」)，但第一點跟第四點蠻值得記住的。其實 Fiona 自己在台上就講了「do what makes sense for your team」，她沒要你照抄。真正的風險是聽眾，很容易把 Anthropic 的做法當聖經，忘了自己的 codebase 和人才密度根本不是那回事。</p>

<h2 id="三boris-把話說得更滿-coding-is-solved">三、Boris 把話說得更滿: 「coding is solved」</h2>

<p>如果說 Fiona 還算克制，Boris Cherny (Claude Code 的創造者) 在 <a href="https://www.platformer.news/boris-cherny-interview-ai-jobs/">Platformer 的訪談</a>裡就直接得多了。他說自己已經超過六個月沒寫過一行 code、對他做的工作來說 coding「基本上解決了」，還公開預測「軟體工程師」這個頭銜今年就可能開始消失，融化成某種「builder」的角色，因為他身邊的設計師、PM、主管現在全都在寫 code。</p>

<blockquote>
  <p>Claude Code 已經有超過半年是 100% 由 Claude Code 寫的。Boris 說他去 Y Combinator 最新一批新創做分享，問「有誰 100% 的 code 是 Claude Code 寫的」，現場一半的手舉起來; 問「有誰完全沒用模型寫 code」，兩百多人裡只有一隻手。</p>
</blockquote>

<p>不過小編要幫他說句公道話，因為這句話常常被斷章取義。Boris 在訪談裡其實有把 caveat 講清楚: <strong>「對我做的那種 coding 來說，coding 已經被解決了」</strong>。他做的是相對簡單的 codebase (Claude CLI 還很新，桌面和 App 都是小型、單純的 codebase)。但 Anthropic 也有 NASA 這種大型、超複雜 codebase 的企業客戶，「對他們來說還沒解決，模型還是會出錯」。而且他強調 coding 只是工程師工作的一小部分，他以前大概 50% 時間在打 code，另外 50% 在跟使用者聊、腦力激盪、debug、規劃。他甚至預測未來「寫 code / 用 agent 寫 code 的人」會比現在多 100 倍。</p>

<blockquote>
  <p>編按: 問題就出在這個 caveat 在傳播時被剝掉了。新聞標題只留下「coding is solved」「工程師要消失了」，後面那半句「for the kinds of coding that I do」不見了。下面這群人反駁的，其實主要是被剝掉 caveat 之後的那個版本。</p>
</blockquote>

<h2 id="四多方反駁-瓶頸沒消失它搬到了判斷力">四、多方反駁: 瓶頸沒消失，它搬到了判斷力</h2>

<p>小編搜尋了一輪這半年的論戰，發現一件有意思的事: 真正天天在用這些工具寫嚴肅軟體的人，幾乎沒有人站「人類不用再寫 code」這一邊。他們的反駁很一致: 生成 code 從來就不是軟體工程的難處。</p>

<p><strong><a href="https://simonwillison.net/2026/May/6/vibe-coding-and-agentic-engineering/">Simon Willison</a></strong> 把 AI 寫 code 拆成兩個世界: 「vibe coding」(完全不看 code，壞了再求模型一次) 跟「agentic engineering」(專業工程師用 agent 放大自己的專業)。他不怕飯碗不保，因為這些工具是「既有經驗的放大器」，你越懂跑得越快。他有句話講得很白: 「做出軟體是一件兇殘地困難的事，給我全世界所有的 AI 工具，我們想達成的目標還是非常難。」</p>

<p><strong><a href="https://simonwillison.net/2025/Sep/29/armin-ronacher-90/">Armin Ronacher</a></strong> (Flask、Jinja 作者) 的例子更具體。他新公司的基礎設施九成以上是 AI 寫的，但他點出: 要用到這個程度，「你得知道 goroutine 和 thread 的差別、得懂 rate limiter 為什麼需要 jitter」。結論是: 「這些工具不會取代程式設計師，它們讓我們能在更高的層次施展專業。」</p>

<p><strong>Mario Zechner</strong> (Pi agent 框架作者) <a href="https://simonwillison.net/2026/Mar/25/thoughts-on-slowing-the-fuck-down/">講得最直白</a>: 人類是個瓶頸，而這恰恰是一種保護。</p>

<blockquote>
  <p>「人類沒辦法在幾小時內吐出兩萬行 code。但一支被編排好的 agent 大軍沒有瓶頸、沒有疲累，那些看似無害的小錯會以不可持續的速度複利累積。你把自己移出了迴圈，根本不知道這些小錯已經長成了一隻怪物，等你感覺到痛，已經太遲了。」</p>
</blockquote>

<p>最後是 <strong>Matt Asay 在 <a href="https://www.infoworld.com/article/4176534/ai-still-needs-humans.html">InfoWorld</a></strong> 的提醒，正好打中 Fiona 那個「別盯著 AI%」的點: 對管理者來說，最糟糕的指標就是「AI 生成 code 的百分比」，真正該看的是缺陷有沒有變少、事故有沒有減少、客戶有沒有更開心。他總結得很到位: 「AI 並沒有消除工程紀律的必要，它只是提高了『沒有紀律』的代價。」</p>

<h2 id="所以呢">所以呢?</h2>

<p>把這一圈聲音疊在一起，會發現它們其實沒有互相矛盾，反而拼出了一個共識。</p>

<p>Fiona 在台上已經親口說了答案: 瓶頸搬到了「驗證、審查、安全」。只是到了 Boris 的 slogan 裡，後半句被吃掉，變成了爽快的「coding is solved」。但這兩件事是一體的: code 變便宜，不代表「做出好軟體」變便宜，它只是把成本從「打字」搬到了「判斷、驗證、長期維護」。而那恰好是最難自動化、最吃經驗的部分。</p>

<p>有意思的是，連把話說得最滿、喊出「coding is solved」的 Boris 自己都留了一手。訪談最後問他要不要把「在 X 和 Threads 上回覆使用者 bug 回報」這件事也自動化掉，他說: 「我已經自動化了，但我還是偏好自己來。」原因很簡單: 跟人互動、聽人說哪裡壞了，是他最喜歡的部分。你看，連他都選擇把這件事留在迴圈裡親自做，而那正是判斷與品味。</p>

<p>所以與其問「人類還要不要寫 code」，不如問一個更實際的問題: 當生成幾乎免費，你打算把省下來的時間拿去做什麼? 是塞進更多沒人看得懂、沒人 review 的 code，還是拿去想清楚到底該做什麼、把它做對?</p>

<p>Armin 在<a href="https://lucumr.pocoo.org/2026/3/20/some-things-just-take-time/">另一篇文章</a>裡有句話，小編覺得很適合收尾: 「沒有人能量產一棵五十歲的橡樹，也沒有人能用一個週末的衝刺，變出信任、品質或社群。」生成速度可以無限快，但有些東西，就是需要時間。</p>]]></content><author><name>ihower</name></author><category term="Coding" /><category term="Agent" /><category term="Industry" /><summary type="html"><![CDATA[Anthropic 的 Fiona Fung (帶 Claude Code 與 Cowork 工程團隊) 有一場演講 Running an AI-native engineering org，講的是當 coding 不再是瓶頸之後，他們把整個團隊的工作慣例 (norms) 砍掉重練的過程。小編覺得這是目前少數講得很具體的「AI-native 工程組織到底長什麼樣」第一手紀錄，蠻值得看。]]></summary></entry><entry><title type="html">為下一個模型而寫,別為上一個:Anthropic 三場演講的開發心法</title><link href="https://blog.aihao.tw/2026/06/01/build-for-the-next-model/" rel="alternate" type="text/html" title="為下一個模型而寫,別為上一個:Anthropic 三場演講的開發心法" /><published>2026-06-01T00:00:00+00:00</published><updated>2026-06-01T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/01/build-for-the-next-model</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/01/build-for-the-next-model/"><![CDATA[<p>最近 Anthropic 的 Code with Claude 大會釋出了一系列演講,其中有三場小編覺得特別值得連在一起看:Alex Albert 的 <a href="https://www.youtube.com/watch?v=tP4MGcJ80Y0">The Capability Curve</a>(能力曲線)、Matt 的 <a href="https://www.youtube.com/watch?v=OXJO4LldSnc">The Thinking Lever</a>(思考這根槓桿),還有 Lucas 的 <a href="https://www.youtube.com/watch?v=KLCuxMDZSDg">The Expanding Toolkit</a>(不斷擴張的工具箱)。</p>

<p>三位都是 Anthropic 的 research PM(研究端的產品經理),各講各的主題:一個談模型能力怎麼進步、一個談推理時的算力怎麼花、一個談工具生態怎麼長進模型裡。但三場連起來聽完,小編發現它們其實是從不同角度在講同一件事——你該為「下一個」模型寫程式,而不是為「上一個」。這句話正是 Lucas 最後一張投影片的標題:「為下一個模型而寫,別為上一個」(Build for the next model, not the last one),拿來當這三場的總綱剛剛好。</p>

<p>以下幫大家把三場的精華整理在一起。</p>

<h2 id="1-一年之內模型變強了多少">1. 一年之內,模型變強了多少?</h2>

<p>Alex 開場先做了個現場調查:覺得 Claude 讓自己一年內快了 10 倍的請舉手——結果舉手的一大片;5 倍?2 倍?幾乎全場都舉了。這不是場面話,他緊接著丟出一個具體數字:在 SWE-bench Verified 這個衡量「模型能不能自己完成一個軟體合併請求(PR)」的評測上,一年前的 Sonnet 3.7 拿 62 分,今天的 Opus 4.7 拿 87 分。</p>

<p>25 分的跳躍換句話說就是:那些一年前 Sonnet 3.7 會搞砸的困難任務,Opus 4.7 成功的機率是它的三倍以上。</p>

<p>Alex 還準備了一段對照示範:用同一句 prompt 要求模型「複製出 Claude.ai」。Sonnet 4 做出來的是一個黑白通用聊天介面,一送出訊息就報錯,基本上只是個好看的空殼;換成 Opus 4.7,直接就有 Claude 的配色、能正確打 API 拿到回應、會記住舊對話、會在訊息裡行內渲染視覺化圖表,甚至自己實作了深色模式——而且用的程式碼行數還更少。</p>

<blockquote>
  <p>編按:這場演講的重點不在某個特定模型,而在於「在一個每個月都明顯變強的東西上面開發」這件事本身意味著什麼。這個視角,才是三場演講共同的底色。</p>
</blockquote>

<h2 id="2-進步發生在哪三個地方">2. 進步發生在哪三個地方</h2>

<p>Alex 把這一年的能力增長拆成三塊,每一塊都對應到開發者實際會踩到的痛點。</p>

<p>🔹 <strong>規劃能力</strong>:舊模型常見的壞毛病是「先動手、後思考」。Alex 的比喻很傳神——就像他組 IKEA 家具,直接上手,搞到一團亂才回頭看說明書。Sonnet 3.7 大概就是這樣。新模型則會先花時間想清楚、擬好策略,再動手寫程式。</p>

<p><img src="/assets/images/build-for-the-next-model/planning-before-acting.jpg" alt="" /></p>

<p>這張投影片講得很白:以前是「先動手再說,而且是你的鷹架在逼它推理」;現在是「動手前先讀完全部,過程中還會抓到自己的錯」。對開發者的意義是——給 Claude 時間思考,別逼它一拿到任務就跳進去寫,那反而會拖累後面的表現。</p>

<p>🔹 <strong>錯誤復原</strong>:舊模型會陷入所謂的「死亡迴圈」(doom loop)——遇到問題、提一個解法、解法沒用,然後就卡死在那邊一直鬼打牆,直到上下文塞爆,只能整個清掉重來。新模型懂得退回來、換個角度重想、走另一條路。對你的好處是:任務表現更好,又少浪費 token。</p>

<p>🔹 <strong>長程注意力</strong>:舊模型做久了會「忘記劇情」,你寫在系統提示(system prompt)裡的指示,愈到後面愈不被理會。新模型能在數十萬甚至上百萬 token 的跨度裡維持一致性。</p>

<p><img src="/assets/images/build-for-the-next-model/attention-long-runs.jpg" alt="" /></p>

<p>這代表你不用再像保姆一樣盯著上下文視窗,也不用把工作硬切成小塊餵給它——可以直接把整個程式碼庫交給它,信任它自己跑長任務。</p>

<p>把這三點加起來——更好的規劃、更少的錯誤、跑得更久的 agent——複利效應就是端到端任務表現的明顯提升。Alex 也舉了客戶的例子:Vercel 發現 Opus 4.7 會在寫下任何一行程式之前,先替系統程式碼寫好「證明」;Shopify 則看到模型一邊寫、一邊回頭迭代修正自己的輸出。</p>

<h2 id="3-想看到同樣的進步先從你的-evals-開始">3. 想看到同樣的進步?先從你的 evals 開始</h2>

<p>那開發者要怎麼在自己的應用裡吃到這些紅利?Alex 的第一個建議有點反直覺:不是從你的應用下手,而是從你的 evals(評測)下手。</p>

<p>「能被量測的東西,才能被改進。」重點有兩個:你得有 evals,而且這些 evals 要貼近你產品真正的任務分布。聽起來像廢話,但他說這正是很多團隊走偏的地方——明明做的是 coding agent,卻拿學術界的程式評測去測,而不是用接近自家使用者真實流量的資料。</p>

<p><img src="/assets/images/build-for-the-next-model/refresh-evals.jpg" alt="" /></p>

<p>接著還要做兩件事。一是<strong>確保 evals 沒有飽和</strong>:模型愈來愈聰明,evals 也得跟著愈做愈難,不然就抽不出新的訊號了——如果新模型輕鬆滿分,那這份 evals 已經量不出東西了。二是<strong>拿新出的前沿模型來測</strong>。Alex 說了一句小編很有感的話:<strong>有時候對你應用最好的優化,就只是換上最新的模型而已</strong>。所以每次有新模型,花點時間認真測,很值得。</p>

<h2 id="4-回頭看你的鷹架和-prompt記得做減法">4. 回頭看你的鷹架和 prompt——記得做減法</h2>

<p>第二個建議:重新檢視你的鷹架(scaffolding)。Alex 對鷹架的定義是「圍繞在模型外面、把它導向目標的那些程式碼、prompt、skill 和工具設定」。</p>

<p>關鍵心法是<strong>做減法</strong>。新模型可能不再需要你以前搭的那些東西。也許以前要拆成多步驟的工作流,現在一輪對話就能搞定。「常常,你是靠『拿掉』而不是『加上』東西來提升表現的。」</p>

<p>prompt 也一樣。Prompt 會一代一代累積,久了就變成一坨醜陋的規則大雜燴,你自己都搞不清楚當初為什麼加某一條、它現在到底還有沒有用。每換一個新模型,就回頭砍掉那些可能已經用不到的指令——既救表現,又省 token。</p>

<h2 id="5-給模型發揮的空間">5. 給模型發揮的空間</h2>

<p><img src="/assets/images/build-for-the-next-model/room-to-work.jpg" alt="" /></p>

<p>第三個建議,Alex 用一張投影片收斂成三點:</p>

<p>1️⃣ <strong>讓 Claude 自己決定何時思考</strong>:用適應性思考(adaptive thinking),搭配 effort(努力程度)參數去調它思考和行動所花的 token。(這正好接到第二場演講的主題,等下細講。)</p>

<p>2️⃣ <strong>用受控的方式,給 agent 更多工具權限</strong>:聽到這句有些人會緊張,但 Alex 強調他不是要你放任它亂搞。他舉 Claude Code 的 auto mode 為例:它會跑分類器去判斷 Claude 提出的每個工具呼叫到底需不需要人類明確批准,讓 Claude 可以在背景跑更久、更自主,不必一直停下來等人點頭。</p>

<p>3️⃣ <strong>替 agent 把迴圈閉合</strong>:設計你的系統,讓 Claude 能檢查自己的輸出、然後迭代。比方說做前端的 coding agent,給它一個操作電腦的工具,讓它自己點開網站、實測它寫出來的功能對不對。</p>

<hr />

<h2 id="6-第二根槓桿在推理時花算力">6. 第二根槓桿:在推理時花算力</h2>

<p>第一場談的是「模型本身變強」,Matt 的第二場則談另一條變強的路徑:test-time compute,也就是推理時算力(inference-time compute),這就是大家熟知的「推理模型」背後那回事。</p>

<p>就像我們可以在訓練時用更大的模型、更多資料、更長時間來擴展算力,我們也可以在推理時讓模型「多花點時間」解一個問題。Matt 給了兩張對照圖:一張是從 Haiku 到 Sonnet 到 Opus,模型愈大,程式評測分數愈高;另一張是同一個 Opus,單純讓它在一個問題上花更多時間,分數也跟著往上爬。而且這不只適用於軟體工程,agentic 搜尋、操作電腦、博士級的學術推理,通通吃這一套。</p>

<h2 id="7-同一個模型從-50-秒到-593-秒">7. 同一個模型,從 50 秒到 593 秒</h2>

<p>光看圖表不夠直觀,Matt 用一個有趣的示範把這件事具象化:他要 Opus 4.7 寫一個「車流在單行道紅綠燈前的真實模擬」,然後分三種努力程度跑。</p>

<ul>
  <li><strong>低 effort</strong>:約 50 秒、4,600 個輸出 token。車子會跑、會停紅燈,功能算過關,但畫面陽春,而且 Claude 把紅綠燈擺在馬路正中央(設計品味堪憂)。</li>
  <li><strong>高 effort</strong>:時間和 token 都加倍,結果明顯更好——有不同車種,紅綠燈也乖乖移到路邊,甚至實作了它自稱的「智慧駕駛模型」,每台車會根據周圍車況反應。</li>
  <li><strong>最高 effort</strong>:時間和 token 都是低 effort 的 10 倍(就是封面那張,593.1 秒、52,893 個 token),畫面、燈號、駕駛行為全都最逼真。</li>
</ul>

<p>這個示範的重點是:<strong>同一個模型,光是讓它多花時間,結果就會更好</strong>。Matt 說隨著推理時算力繼續往上推,Claude 未來解一個問題花的時間不會只是幾秒、幾分鐘,而可能是幾天、幾週、甚至幾個月,拿來啃人類最難的問題。</p>

<h2 id="8-claude-花的三種-token">8. Claude 花的三種 token</h2>

<p>Matt 把「推理時花算力」拆成三種 token,這個分類小編覺得很實用:</p>

<ul>
  <li><strong>思考 token</strong>:Claude 的內心獨白,一步步推理、權衡選項、打草稿的空間。</li>
  <li><strong>工具呼叫 token</strong>:Claude 跟外部世界打交道的方式,搜尋、讀寫檔案都算。</li>
  <li><strong>文字 token</strong>:Claude 跟「你」溝通的方式,回報進度、最後做總結,或單純回答你的問題。</li>
</ul>

<p>這三種對使用者都有實際成本——你付的 token 錢,還有等待的時間。Claude 花愈多 token,你就等愈久。所以 Anthropic 認為,給使用者「影響或限制 Claude 怎麼花 token」的能力很重要。</p>

<p>方法有兩種。一是 <strong>effort 旋鈕</strong>:告訴 Claude 你希望它怎麼在時間、成本、品質之間取捨。二是 <strong>預算上限</strong>:他們最近推出的「任務預算」(Task Budgets)讓你設一個上限,例如「幫我做這個功能,但花超過 10 萬 token 就先停下來跟我確認」。預算可以是 token 數、時間或金額——當 Claude 動不動就要跑上好幾天的時候,這種「先講好做多久就回報」的機制會愈來愈重要。</p>

<h2 id="9-從固定順序到適應性思考">9. 從固定順序到「適應性思考」</h2>

<p>那給了努力程度和預算之後,Claude 內部到底怎麼分配這三種 token?Matt 講了一段演進史:</p>

<p><img src="/assets/images/build-for-the-next-model/adaptive-thinking.jpg" alt="" /></p>

<p>最早的推理模型是固定順序:先思考、再呼叫工具、最後輸出文字。後來有了<strong>交錯思考</strong>(interleaved thinking),Claude 可以在工具呼叫「之間」插入思考——呼叫工具、拿到結果、想一下、再決定下一步。最近則進化到<strong>適應性思考</strong>(adaptive thinking):Claude 完全自由,想在哪一步思考都行,順序、長度都不設限,遇到簡單的問題甚至可以完全不思考。如上圖,它可以是「文字 → 工具 → 工具結果 → 思考 → 工具 → ……」任意組合。</p>

<p>Matt 特別澄清兩個誤解:適應性思考<strong>不是</strong>模型路由器(它不會把問題分類後丟給「思考版」或「非思考版」模型),也<strong>不是</strong>自動開關思考的那種開關。它的本質,是把「你必須在回應一開始至少花一個思考 token」改成「你想在任何步驟思考都可以」。Anthropic 從 Opus 4.6 開始,所有評測都跑在適應性思考上,因為它在維持(甚至超越)交錯思考表現的同時,還能給更好的使用體驗。</p>

<h2 id="10-別再把-thinking-開關當努力程度在用">10. 別再把 thinking 開關當「努力程度」在用</h2>

<p>這段小編覺得是整場最關鍵的觀念。過去大家習慣把 thinking 開關當成「努力程度」在調——想讓 Claude 認真點,就去 Claude.ai 或 Claude Code 把 thinking 打開。直覺上很合理,但 Matt 說這是個很糟的替代指標。</p>

<p>因為打開或關閉 thinking,其實是在開關模型的一個「核心能力」,你限制的是它「能怎麼工作」,而不是「你要它多努力」。effort 旋鈕才是「多花 token 換更好答案」這個意思的正確表達——它會同時調動思考、工具使用和輸出文字,而不是只切掉其中一個。</p>

<p>Matt 的類比很到位:就像我們用工具時,不會叫 Claude「永遠都要搜尋」或「永遠別搜尋」,而是讓它自己判斷何時該搜;同樣地,<strong>我們跟同事共事,也不會叫他「把內心獨白打開或關掉」,我們只會問他「這件事要多拚」</strong>,然後讓他自己決定要想多深、做哪些動作。</p>

<h2 id="11-effort-等級怎麼選還有低-effort-帶來的驚喜">11. effort 等級怎麼選?還有低 effort 帶來的驚喜</h2>

<p>有 evals 的話,Matt 建議畫一條「努力曲線」:X 軸放 token / 時間 / 成本,Y 軸放表現,就能看清楚每一級的取捨。提高努力程度通常能改善大多數「吃智力」的任務,但會出現邊際遞減——你可能會發現 extra high 跟 max 表現差不多,但 token 差很多,那選 extra high 就夠了。</p>

<p>沒有 evals 的話,Matt 也給了幾條經驗法則:</p>

<ul>
  <li><strong>Max(最高)</strong>:最難的任務才用,小心邊際遞減,別預設它一定是表現天花板或最划算的選擇。</li>
  <li><strong>Extra high(極高)</strong>:Opus 4.7 新增的等級,也是它在 Claude Code 和 Claude.ai 的預設值。大多數 coding 和 agentic 用途的最佳設定,智力拉好拉滿又不會太過頭。</li>
  <li><strong>High(高)</strong>:想在 token 和智力之間取平衡的好起點。</li>
  <li><strong>Medium(中)</strong>:成本敏感、願意稍微犧牲一點智力換速度時用。</li>
  <li><strong>Low(低)</strong>:留給範圍小、對延遲敏感的任務。</li>
</ul>

<p>不過低 effort 也給過 Matt 驚喜。他最愛的評測之一是「Claude Plays Pokemon」(讓 Claude 玩寶可夢紅版)。用低 effort 跑起來,Claude 居然把遊戲當成「速通」在玩——跳過訓練家對戰省時間、囤好補品就地補血而不跑寶可夢中心、狂用「除蟲噴霧」減少洞窟裡的雜魚遭遇。小編覺得這例子很妙:我們常把低 effort 跟「比較笨」劃上等號,但要想出「怎麼把 token 花到最省、最快通關」其實需要相當的智力。Claude 把「低 effort」理解成「用最聰明的方式偷懶」,反而很有梗。Matt 也提醒,低 effort 時 Claude 一心想省 token,偶爾會抄一些你沒預期到的捷徑,所以除了看 evals,平時也該多讀讀它的對話紀錄,搞清楚它在某個努力程度下到底怎麼回應。</p>

<h2 id="12-小模型-vs-大模型開低-effort">12. 小模型 vs 大模型開低 effort</h2>

<p>既然推理時算力和訓練時算力都能換智力,那到底何時該用小模型、何時該用大模型開低 effort?Matt 的判斷準則:</p>

<ul>
  <li><strong>大模型開低 effort</strong>:適合「吃智力但要快」的場景。以那個車流模擬為例,Opus 4.7 開低 effort 花的 token 跟 Haiku 4.5 開最高 effort 差不多、時間也只多一點點,但結果好很多。</li>
  <li><strong>小模型</strong>:適合「不太吃智力、但要省錢」的場景,尤其是要大量處理的簡單任務——分類、資訊抽取、基本摘要。另外,如果你的應用很在意「第一個 token 多快吐出來」,小模型天生就更快。</li>
</ul>

<p>Matt 的口訣很好記:<strong>要讓「第一個 token」快,用小模型;要讓「最後一個 token」快,用大模型開低 effort</strong>。</p>

<p>收尾他給了三個帶得走的重點:一,能開思考就開思考,給 Claude 推理的空間;想調思考多寡,用努力程度或預算,別用開關。二,有 evals 就用 evals,畫曲線、測不同努力程度和模型。三,什麼都不想搞、又在做 coding 的話,直接選 extra high 就對了。北極星目標是:你設好品質門檻和預算,剩下的交給 Claude,它自己把算力分配到剛剛好。</p>

<hr />

<h2 id="13-第三場去年要自己搭的鷹架今年內建進模型了">13. 第三場:去年要自己搭的鷹架,今年內建進模型了</h2>

<p>Lucas 的第三場把視角從「模型」拉到「模型周邊的生態」。他一句話點題:<strong>去年你得自己搭的鷹架,今年隨模型一起出貨了</strong>。所以別再把模型想成一個「輸入進去、輸出出來」的 LLM 盒子,而要把它想成一組會持續擴張、不斷增強自身能力的工具箱。</p>

<p><img src="/assets/images/build-for-the-next-model/same-task-one-year-apart.jpg" alt="" /></p>

<p>整場演講是一連串「去年 vs 今天」的對照,上面這張就是最好的縮影。左邊是去年:你得手寫一個挑工具的路由器(看到 SQL 就給資料庫工具)、外面包一層重試機制、再加一個輸出驗證器、最後串成一個跑迴圈——洋洋灑灑一大段。右邊是今天:就是一次 <code class="language-plaintext highlighter-rouge">client.messages.create()</code> 呼叫,把全部工具丟給它(<code class="language-plaintext highlighter-rouge">tools=ALL_TOOLS</code>)、順手指定輸出格式就好。Lucas 說重點不是這些工作「消失」了,而是它「搬家」了——搬進模型本身,你不必再自己扛。</p>

<h2 id="14-四個被模型吸收掉的能力">14. 四個被模型吸收掉的能力</h2>

<p>🔧 <strong>工具使用</strong>:以前你不敢把全部工具丟給模型(會吃爆上下文),只好寫一個路由器——用字串比對、各種啟發式規則,例如「模型提到 SQL 就給它資料庫工具」,外面再包一層重試。Lucas 直言這種路由器「本質上就是用 if 條件寫死的、對使用者意圖的猜測」,又脆、又是你加新工具時第一個壞掉的東西。現在模型夠聰明、選工具的準確率夠高,自己寫路由器和預先過濾反而常常幫倒忙;工具報錯時,你也可以信任 Claude 自己看到錯誤、復原、重試。<strong>小技巧</strong>:給工具時,大多數人只給「輸入」的格式,但你也可以把「輸出」的格式描述給 Claude——例如告訴它這個搜尋工具會回傳 id、標題、摘要、分數,那它想對結果排序時,就知道有分數可用,省下一次往返。玩 Claude Code 的人還可以在工具呼叫的前後掛 hook,程式化地做事(擋掉特定呼叫、或事後記錄輸出)。</p>

<p>🗂️ <strong>上下文管理</strong>:以前長時間跑的 agent 得自己搭記憶系統——把文件切塊(chunking)、做 RAG、每隔幾輪就叫另一個模型來摘要,還要手動搬「快取斷點」省錢。現在呢?一百萬 token 的上下文長度搭配統一定價,先解掉大半壓力;再加上伺服器端的壓縮(compaction)和上下文編輯(context editing),剩下的就只是幾行設定,逼近「無限上下文」的體感。<strong>小技巧</strong>:每隔幾輪就清掉過時的工具結果(截圖、搜尋結果、讀檔內容這類),但保留 Claude 在對話裡記下的「決策」,就能即時省下大量 token。玩 Claude Code 的人可以打 <code class="language-plaintext highlighter-rouge">/context</code>,看到一張彩色格狀圖,直觀感受訊息、工具結果、系統提示、MCP 定義各占掉多少上下文。</p>

<p>💻 <strong>程式碼執行</strong>:以前「寫 → 跑 → 修」這個迴圈是開發者的活——找虛擬機(VM)供應商、開沙箱、把模型產的程式碼丟上去跑、解析錯誤訊息、再餵回模型,反覆直到成功。現在有了「程式碼執行」工具,自動在伺服器端給 Claude 一個沙箱,整個迴圈在「一次 API 往返內」就跑完,不用在 Claude 和你的虛擬機之間來回。Lucas 給的心智模型很好懂:這就像給 Claude 一台自己的電腦當「草稿紙」,它可以裝套件、跑資料分析、做運算,完全不弄髒你本機的檔案系統;當它需要碰只存在你本機的東西(你的程式碼倉庫、Python 環境)時,才回到真正的本機 bash,而且它分得清楚什麼時候該用哪一邊。玩 Claude Code 的人可以用 <code class="language-plaintext highlighter-rouge">/schedule</code> 排定由 cron 觸發的自主執行。</p>

<p>🖱️ <strong>操作電腦</strong>:以前要讓 Claude 操作你的筆電,得寫一堆「影像膠水」——1080p 截圖先縮小到模型的像素上限、記住縮放比例、模型選好點擊位置後再把座標放大回原解析度,外面還要包重試和驗證。Opus 4.7 現在能直接吃原生解析度的截圖,在 1440p 以內回傳一比一的像素座標,縮放數學整個消失,你把圖送出去就好,信任它會點對地方。這個能力進步神速:招牌評測 OSworld(衡量模型在專業與消費級軟體上完成複雜任務的能力),不到 12 個月前 Claude 還在 50 分以下,連一半任務都做不完;Opus 4.7 報出 78 分,馬上要破 80。<strong>小技巧</strong>:1440p 以內建議多試不同解析度和影像格式(JPEG / PNG / WebP 的壓縮特性都不一樣);至於 4K 這種超高解析度,還是建議自己先縮小。玩 Claude Code 的人裝了 Claude in Chrome(到 claude.ai/chrome 取得),就能讓 Claude Code 直接開瀏覽器測試,連本機的開發環境也行。</p>

<p>Lucas 還放了一段示範:一個有錯誤的專案管理看板,Claude 自己用 Chrome 打開、重現「按了新增卻沒長出卡片」的錯誤、邊測邊改程式碼修好;接著測試拖拉功能時把卡片誤拉到錯的欄位,辨識出這也是錯誤、即時寫修正、再從頭重測一遍。小編覺得這個迴圈很關鍵——<strong>因為今天大多數軟體是給「人」用的,就得用「像人」的方式去測</strong>。讓 Claude 能在開發過程中操作瀏覽器,它才能自己閉合「寫出給人用的軟體 → 自己抓錯修好」這個迴圈,不必開發者手把手帶它找問題。</p>

<h2 id="15-一條判斷程式碼價值的鐵則">15. 一條判斷程式碼價值的鐵則</h2>

<p>Lucas 結尾給了一條小編認為三場演講裡最該抄下來的規則:</p>

<blockquote>
  <p>任何你寫來「補償模型不可靠」的程式碼,半衰期只有幾個月。那種活,留給 Anthropic 就好。</p>
</blockquote>

<p><img src="/assets/images/build-for-the-next-model/build-next-model.jpg" alt="" /></p>

<p>這張收尾投影片把整件事講得乾淨俐落。左邊大標就是「為下一個模型而寫,別為上一個」,副標寫著「每一個軟體都正在長出一道給 agent 的前門,你的優勢就在你那道前門背後」。右邊兩張卡片是對照組:「補償模型(重試、路由器、規劃器、驗證迴圈)→ 半衰期以月計」對上「連接你的世界(工具、上下文、權限驗證、資料)→ 會複利成長」。</p>

<p>重試邏輯、路由器、規劃器、驗證迴圈……這些都正在、也將持續被吸收進模型。反過來,<strong>那種「把模型接到你世界」的程式碼,價值會複利成長</strong>——你的自訂工具、你的資料、你的權限驗證、你獨有的脈絡。一句話收得很漂亮:「模型無法吸收它看不到的東西。」所以把這些餵給它,遠比替它補短處有價值。</p>

<p>Lucas 進一步預言:不久的將來,每一個 agent、每一個軟體,都會長出一道「給 agent 的前門」。於是,有趣的工作不再是「把模型弄得更可靠」,而是「在你那道前門背後,放上別人放不了的東西」。</p>

<h2 id="16-三場連起來看為下一個模型而寫">16. 三場連起來看:為下一個模型而寫</h2>

<p>把三場拼在一起,訊息其實高度一致,只是從三個角度切入同一個建議——<strong>為下一個模型開發,別為上一個</strong>。</p>

<ul>
  <li><strong>能力曲線</strong>告訴你:進步會複利,所以每出一個新模型,就拿 evals 重測一次,並且勇於對鷹架做減法。</li>
  <li><strong>思考槓桿</strong>告訴你:別再用開關去掐模型的核心能力,改成用努力程度和預算去表達「你要它多拚」,然後讓模型自己分配算力。</li>
  <li><strong>擴張的工具箱</strong>告訴你:別再自己扛「讓模型可靠」這件事,把力氣投在「把模型接到你獨有的世界」上。</li>
</ul>

<p>貫穿三場的那個不變量,就是 evals——它既是你判斷該不該換模型的依據,也是你決定努力程度的工具,更是你驗證「拿掉鷹架之後表現有沒有變好」的那把尺。模型會吃掉所有通用的、會過時的東西;而你真正該擁有的,是那些專屬於你、模型再怎麼進步也複製不走的部分。</p>

<p>下次你又想替模型多寫一層保護網之前,也許可以先問自己一句:這段程式碼,是在補上一個模型的短處,還是在替下一個模型鋪路?</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="LLM" /><category term="Coding" /><category term="Eval" /><summary type="html"><![CDATA[最近 Anthropic 的 Code with Claude 大會釋出了一系列演講,其中有三場小編覺得特別值得連在一起看:Alex Albert 的 The Capability Curve(能力曲線)、Matt 的 The Thinking Lever(思考這根槓桿),還有 Lucas 的 The Expanding Toolkit(不斷擴張的工具箱)。]]></summary></entry><entry><title type="html">我錯了，還是要讀程式碼: Dex Horthy 重新檢討 AI 寫程式流程</title><link href="https://blog.aihao.tw/2026/06/01/dexter-rpi-crispy/" rel="alternate" type="text/html" title="我錯了，還是要讀程式碼: Dex Horthy 重新檢討 AI 寫程式流程" /><published>2026-06-01T00:00:00+00:00</published><updated>2026-06-01T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/01/dexter-rpi-crispy</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/01/dexter-rpi-crispy/"><![CDATA[<p>幾個月前小編整理過 Dex Horthy 的一場演講。他是 HumanLayer 的創辦人，也是去年提出 <a href="https://github.com/humanlayer/12-factor-agents">12-Factor Agents</a> 的人。那場他大力推廣一套寫程式的 AI 工作流，叫做「研究 → 計畫 → 實作」(Research → Plan → Implement，簡稱 RPI)。那篇整理在這裡: <a href="https://blog.aihao.tw/2026/02/27/no-vibes-allowed/">別再憑感覺了: 在複雜 Codebase 中解決困難問題</a>。</p>

<p>這次他又站上台，題目卻是 <a href="https://www.youtube.com/watch?v=YwZR6tc7qYg">Everything We Got Wrong About Research-Plan-Implement</a>，意思是「我們把 RPI 全搞錯了」。同一個人，半年內回來自己打臉，蠻難得的。他講得很坦白: 「我夠謙虛，做錯的時候願意承認。」這場就是在講他們跟上千位工程師實戰之後，發現 RPI 哪些地方根本行不通，以及怎麼修。</p>

<p>小編覺得這場比上一場更有料。它不是在推銷方法論，而是在拆解一套方法論為什麼會失敗。如果你正照著上一篇的 RPI 在用，這篇基本上就是它的更新說明。</p>

<p>先用一張表把兩場的差異講清楚:</p>

<table>
  <thead>
    <tr>
      <th>比較項目</th>
      <th>上一場〈No Vibes Allowed〉(去年十一月)</th>
      <th>這一場〈我們把 RPI 全搞錯了〉</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>整場在講什麼</td>
      <td>推銷一套新流程: 研究 → 計畫 → 實作 (RPI) 三階段</td>
      <td>回頭承認 RPI 哪些環節行不通，並端出修正版</td>
    </tr>
    <tr>
      <td>該不該讀 AI 寫的程式碼</td>
      <td>(八月時) 說不必，計畫夠好就放手讓 AI 跑</td>
      <td>改口: 一定要讀。不讀的做法試了半年，最後得整段砍掉重寫</td>
    </tr>
    <tr>
      <td>該不該讀計畫文件</td>
      <td>要，而且要逐字認真檢查</td>
      <td>不要。千行計畫約等於千行程式碼，讀它跟讀程式碼一樣累、不划算</td>
    </tr>
    <tr>
      <td>完整流程幾步</td>
      <td>三步: 研究 (Research)、計畫 (Plan)、實作 (Implement)</td>
      <td>八個階段: 提問 (Questions)、研究 (Research)、設計 (Design)、結構大綱 (Structure Outline)、計畫 (Plan)、開分支 (Worktree)、實作 (Implement)、發 PR (Pull Request)，整套流程取名叫 CRISPY</td>
    </tr>
    <tr>
      <td>研究階段怎麼做</td>
      <td>派子代理對程式碼做深度探索，壓縮出客觀事實</td>
      <td>一樣，但多一招: 故意不讓做研究的 AI 看到需求，免得它夾帶主觀意見</td>
    </tr>
    <tr>
      <td>人該把力氣花在哪</td>
      <td>仔細讀計畫和研究文件</td>
      <td>改讀真正的程式碼，加上更短的「設計」(約 200 行) 和「大綱」(約兩頁)</td>
    </tr>
    <tr>
      <td>Prompt 怎麼設計</td>
      <td>一個大 prompt 把所有步驟包進去</td>
      <td>拆成多個小 prompt，每個指令數壓在 40 條以內 (因為模型記不住太多條)</td>
    </tr>
    <tr>
      <td>「笨蛋區」的門檻</td>
      <td>強調盡量把上下文用量壓在 40% 以下</td>
      <td>看人: 老手可衝到 60%，只有新手才需要嚴守 40%</td>
    </tr>
  </tbody>
</table>

<p>以下是重點整理:</p>

<h2 id="1-先講做錯的三件事">1. 先講做錯的三件事</h2>

<p>Dex 一開場就把這次要認錯的清單攤開。最有爭議、那天在 Twitter 上吵翻天的一條是: <strong>「可以不讀程式碼」是錯的。</strong> 另外兩條是: 不該去讀那種落落長的計畫文件; 還有，如果你寫的是會被使用者用、半夜三點壞掉會被叫起來修的正式程式碼，2026 年了，不准再生產 slop (粗製濫造的低品質程式碼)。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_30.jpg" alt="" /></p>

<p>不過他也說，當初做對的幾件事仍然成立: 沒有萬靈丹般的「魔法 prompt」; 不要把思考外包出去 (工程師你本人是這套流程裡很重要的一環); 以及要不斷尋找「槓桿」，也就是想辦法在不必讀完每一行的前提下，確保產出是對的。這三點是整場演講的定錨。</p>

<h2 id="2-rpi-一開始很好用後來為什麼不行了">2. RPI 一開始很好用，後來為什麼不行了</h2>

<p>從去年十月起，HumanLayer 跟上千位工程師合作，從小新創一路到世界五百大企業都有。他們一再看到同一個現象: 把工具交給一個高手，他能跟 AI 黏在一起每週工作 70 小時、瘋狂出貨; 但同一套工具交給他的團隊，成果就普普通通。</p>

<p>問題出在哪? Dex 引用了一份去年的調查數據 (他特別提醒，這還沒算進後來更強的 Opus 4.5，現在實際表現應該更好): 用 AI 寫程式雖然產出量變多，但其中很大一塊是「返工」(rework)，也就是回頭修上週 AI 吐出來的爛東西。算下來你出貨量多了 50%，但有一半只是在清自己製造的爛攤子。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_22.jpg" alt="" /></p>

<div style="border:1px solid var(--border);border-radius:10px;padding:16px;margin:24px 0;">
<div style="font-size:12px;color:var(--text-secondary);margin-bottom:10px;">用 AI 之後的「產出」其實有一半在做白工</div>
<div style="display:flex;height:40px;border-radius:6px;overflow:hidden;border:1px solid var(--border);font-size:12px;">
<div style="flex:50;background:rgba(26,127,55,.16);color:#1a7f37;display:flex;align-items:center;justify-content:center;">真正有效的新產出</div>
<div style="flex:50;background:rgba(207,34,46,.16);color:#cf222e;display:flex;align-items:center;justify-content:center;">返工 · 清上週 AI 的爛攤子</div>
</div>
<div style="font-size:12px;color:var(--text-secondary);margin-top:8px;">出貨量看似 +50%，但其中很大一塊只是回頭修 AI 上次吐出來的爛東西</div>
<div style="font-size:11px;color:var(--text-secondary);text-align:right;margin-top:10px;opacity:.85;">✏️ 小編製圖，非 Dex 原始投影片</div>
</div>

<p>結論是: AI 對「全新、單純的專案」很好用，但對「歷史包袱重、又複雜的既有專案」就不行了。</p>

<h2 id="3-研究階段的修正-故意把需求藏起來">3. 研究階段的修正: 故意把需求藏起來</h2>

<p>第一個失敗點，是大家做不出好的研究。理想的做法是這樣: 你圈定程式碼庫裡的一塊區域，派出子代理 (sub-agent) 去做深入的探索，把「這段程式現在到底怎麼運作」壓縮成一份客觀的事實清單。關鍵字是<strong>客觀</strong>，不要摻進任何「應該怎麼改」的意見。</p>

<p>但實際上多數人是這樣下指令的: 「去研究一下程式碼庫，我要做的功能是 OOO」。問題就出在這裡: 好的研究應該全是事實，可是一旦你告訴模型你想做什麼，它就會忍不住開始給<strong>意見</strong>。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_63.jpg" alt="" /></p>

<p>他們的修法很工程: 用程式 (而不是靠 prompt 拜託模型) <strong>把需求從「做研究」的那段對話裡藏起來</strong>。一段對話只負責生出該問的問題，另一段全新、完全不知道你要蓋什麼的對話，才去產出研究文件。Dex 說這跟資料庫的「查詢規劃」(query planning) 概念很像，只是這裡的對象換成「讓 AI 去讀懂程式碼」。</p>

<h2 id="4-魔法咒語問題">4. 「魔法咒語」問題</h2>

<p>第二個失敗點更尷尬。原本的規劃流程設計成: 先給你幾個設計選項、跟你來回討論、確認大綱，最後才動手寫計畫。但對超過一半的人來說，如果你沒念出那句咒語 (「跟我一來一回，先從你的疑問和大綱開始，不要急著寫計畫」)，AI 就會跳過討論，自顧自把計畫一口氣寫死，所有決定全幫你做完了。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_88.jpg" alt="" /></p>

<p>Dex 說，他得站在一整屋子的企業工程師面前叮嚀大家「別忘了念咒語」，老實講蠻丟臉的。他的結論很重要: <strong>這不是使用者的錯。如果你做出來的工具，要花大量訓練和反覆練習才能用出好結果，那該被修的是工具，而不是該被怪的是人。</strong></p>

<h2 id="5-指令是有預算的">5. 指令是有預算的</h2>

<p>那 AI 為什麼會擅自跳過「先提問、先討論、先確認大綱」這幾個該做的步驟? 因為<strong>你有一個「指令預算」</strong>。他的共同創辦人 Kyle 寫過一篇文章，引用了一篇論文 (數字一樣是去年的，現在應該更高些): 目前最前沿的模型，大概只能穩定遵守 150 到 200 條指令; 超過這個量，它就開始三心二意、半聽不聽，全看運氣。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_96.jpg" alt="" /></p>

<div style="border:1px solid var(--border);border-radius:10px;padding:16px;margin:24px 0;">
<div style="font-size:12px;color:var(--text-secondary);margin-bottom:12px;">指令預算 · 前沿模型大約只能穩定遵守 150~200 條指令</div>
<div style="display:flex;height:48px;border-radius:6px;overflow:hidden;border:1px solid var(--border);font-size:11px;">
<div style="flex:85;background:rgba(9,105,218,.20);display:flex;align-items:center;justify-content:center;text-align:center;line-height:1.2;">大 prompt<br />85 條</div>
<div style="flex:40;background:rgba(9,105,218,.15);display:flex;align-items:center;justify-content:center;border-left:2px solid var(--bg);">CLAUDE.md</div>
<div style="flex:30;background:rgba(9,105,218,.11);display:flex;align-items:center;justify-content:center;border-left:2px solid var(--bg);">系統 prompt</div>
<div style="flex:45;background:rgba(9,105,218,.08);display:flex;align-items:center;justify-content:center;border-left:2px solid var(--bg);">工具說明</div>
<div style="flex:55;background:rgba(207,34,46,.16);color:#cf222e;display:flex;align-items:center;justify-content:center;border-left:2px dashed #cf222e;text-align:center;line-height:1.2;">一堆 MCP<br />爆預算</div>
</div>
<div style="display:flex;justify-content:space-between;font-size:11px;color:var(--text-secondary);margin-top:8px;">
<span>← 預算內: 乖乖照完整流程跑</span>
<span style="color:#cf222e;">超出上限 → 默默跳過該做的步驟</span>
</div>
<div style="font-size:11px;color:var(--text-secondary);text-align:right;margin-top:10px;opacity:.85;">✏️ 小編製圖，非 Dex 原始投影片</div>
</div>

<p>所以，如果你的 prompt 裡塞了 85 條指令，再加上 CLAUDE.md、系統 prompt、各種工具說明、一堆 MCP……要它乖乖照完整流程跑，機率其實不高。小編覺得這個框架蠻實用的: 很多人不停往 AI 裡加 MCP 工具和規則，效果卻越來越差，根本原因常常就是指令預算早就爆了。</p>

<h2 id="6-別讀計畫去讀程式碼">6. 別讀計畫，去讀程式碼</h2>

<p>這是這場最大的一次改口。上一場 (十一月) Dex 還站在台上叫大家一定要讀計畫，有些人甚至把計畫跟程式碼擺在一起送審。問題是: <strong>一份一千行的計畫，最後產出的程式碼也差不多是一千行 (誤差 10% 以內)。</strong> 而且計畫常常會「跑掉」: 你花一小時把計畫讀完，實作出來卻長得不一樣，於是你又得回頭再讀一次程式碼，看哪裡變了。這根本不叫省力。所謂的「槓桿」，是做更少、卻產出更多。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_105.jpg" alt="" /></p>

<p>所以新的建議是: <strong>別讀計畫了，請去讀程式碼。</strong> 至於八月時他說「計畫就夠了、不用讀程式碼、放手讓 AI 跑」，他直接認錯: 「我們不讀程式碼這樣搞了大概半年，下場不太好，最後不得不把那套系統一大塊砍掉重寫。」</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_113.jpg" alt="" /></p>

<h2 id="7-但開源專案是例外">7. 但開源專案是例外</h2>

<p>有人會反駁: 別人就不讀程式碼啊。像 Beads (三十萬行) 和 OpenClaw 這些開源專案，據說都沒人逐行讀過每一個 PR。Dex 的回應很到位: 這些都是很酷的開源專案，他打從心底佩服維護者; 但它們<strong>不收錢、半夜壞了不會有人被叫起來、做錯了也不會被罰幾百萬美元。</strong></p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_119.jpg" alt="" /></p>

<p>「但如果有人是靠你的程式碼吃飯的，拜託、我求你，去讀它。我們有個專業得扛 (We have a profession to uphold)。」這句話小編覺得是整場的良心所在。他不是在反對 AI，而是在分清楚情境: 在受嚴格監管的產業出貨正式產品，跟玩開源副業，本來就是兩套標準。</p>

<h2 id="8-瞄準-2-到-3-倍而不是-10-倍">8. 瞄準 2 到 3 倍，而不是 10 倍</h2>

<p>2026 被講成「告別 slop 之年」，到處都在談「粗製濫造」和「精雕細琢」的差別。Dex 也坦白，他對「一大群 AI 代理同時開工」這類玩法持保留態度，因為瓶頸從來不是速度，而是你<strong>確保品質</strong>的能力。<strong>跑快 10 倍，但成果六個月後整包丟掉，根本沒意義。</strong></p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_128.jpg" alt="" /></p>

<p>所以目標應該設在 2 到 3 倍，同時維持接近人類水準的品質。在問答時他補了一句很現實的話: 你乖乖讀程式碼、拿到 2 到 3 倍，這對生意的實際結果，遠勝過衝到 10 倍、堆出一堆爛東西，然後祈禱「反正 GPT-7 之後會幫我修好」。</p>

<p>那有沒有人主張完全相反、乾脆兩邊都別讀? 有觀眾問到 StrongDM 那種「軟體工廠」的路線 (人完全不碰程式碼，全靠自動化測試 eval 把關)。Dex 說，這條路會把你推向「形式化驗證」(formal verification)、TLA+ 那個方向 (用數學去嚴格證明系統行為正確)，他甚至遇過有人想做「TLA++」想證明一切都對。但他講得很直白: 他以前會引用 Sean Grove 的說法「把程式碼當成組合語言、你只要寫好那份描述期望行為的規格文件、之後永遠別再讀程式碼」，但現在他對這套主張的態度只有一句冷冷的評語: <strong>「這個，我不挺。」</strong> 也許某天可行，但眼下還有一大票人得趕著把程式碼推上線。</p>

<blockquote>
  <p>編按: TLA+ 是圖靈獎得主 Leslie Lamport 發明的形式化規格語言。你不直接寫程式，而是用數學把系統「應該有的行為」寫成精確規格，再讓工具自動把所有可能的狀態窮舉跑一遍，檢查有沒有死鎖或違反規則的狀況。AWS 就在一篇知名論文〈How Amazon Web Services Uses Formal Methods〉裡分享過，他們用 TLA+ 在設計 DynamoDB、S3 等系統時，揪出人腦和一般測試難以發現的設計 bug; 微軟的 Azure Cosmos DB 也有公開的 TLA+ 規格。</p>
</blockquote>

<h2 id="9-能用程式判斷的就別丟給-prompt-判斷">9. 能用程式判斷的，就別丟給 prompt 判斷</h2>

<p>那塞了 85 條指令的大 prompt 該怎麼救? 答案是: <strong>能用程式邏輯來控制流程的地方，就別用 prompt 去控制。</strong> 這其實正是 12-Factor Agents 一直在講的事。Dex 自嘲他們當初到處勸人「別寫巨無霸 prompt、要把流程拆成小步驟」，結果八月自己回頭就寫了一個巨無霸 prompt，這次總算是「自己喝下自己賣的藥」了。</p>

<p>具體做法是: 與其用一個 prompt 內含一堆條件判斷 (是客訴就走這條、是帳務問題就走那條)，不如先讓模型做一次分類，再把結果丟給一連串更小、更聚焦、指令更少的 prompt 去處理。程式裡的 if 判斷很可靠，而模型很擅長做分類，而這個原則不只適用於寫程式的 AI，任何用到大型語言模型的系統都成立。於是原本那個 85 條指令的 prompt，被拆成好幾個各自不到 40 條指令的階段。</p>

<h2 id="10-設計文件-趁早對-ai-動腦部手術">10. 設計文件: 趁早對 AI 「動腦部手術」</h2>

<p>拆開之後，不只指令更容易被遵守，能再次修正方向的機會也變多了。第一個新階段是 <strong>設計 (Design，我們要往哪走)</strong>: 大概只有 200 行，寫清楚現狀、想要達到的終點、要遵循的既有寫法、已經拍板的設計決定，還有尚待釐清的問題。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_196.jpg" alt="" /></p>

<p>Dex 用了一個很傳神的說法: 這是你對 AI <strong>「動腦部手術」</strong>的最佳時機。你逼它把找到的東西、想做的事、它以為你要的、它沒把握的問題，全部一股腦倒進一份你能隨手修改的文件裡。趁它還沒寫下兩千行程式碼之前，給它每一個機會，把「它可能搞錯的地方」先攤在你面前。這正呼應了那句核心: 不要外包思考。(Matt Pocock 把這份文件叫做「設計概念」: 它是人和 AI 之間，對於「要蓋什麼、怎麼蓋」的共識。)</p>

<h2 id="11-結構大綱-計畫是實作大綱是目錄">11. 結構大綱: 計畫是「實作」，大綱是「目錄」</h2>

<p>第二個新階段是 <strong>結構大綱 (Structure Outline，我們怎麼走過去)</strong>。如果說「設計」像架構評審會議，那「大綱」就是衝刺規劃會議: 把高層次的階段拆出來、決定改動順序、以及沿路要怎麼測。Dex 給了寫過 C 語言的人一秒就懂的比喻:</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_213.jpg" alt="" /></p>

<p><strong>如果完整計畫像是把功能整個實作出來的程式檔，那大綱就像是 C 語言的標頭檔 (.h)</strong>: 只列出函式的樣貌和會動到的型別，份量足夠你看懂 AI 在想什麼、能即時糾正它，但又不必讀完整版。一份計畫可能有八頁，對應的大綱大概只有兩頁，檢查起來輕鬆太多了。</p>

<h2 id="12-要垂直切不要水平切">12. 要「垂直」切，不要「水平」切</h2>

<p>為什麼非得多這個大綱階段不可? 因為不管怎麼調 prompt、怎麼測試，<strong>模型就是改不掉「水平切」的壞習慣</strong>: 先把所有資料庫做完、再把所有服務層做完、再把所有 API 做完、最後才碰前端。等你回過神，已經是 1,200 行程式碼、而且整個跑不起來，還得回頭猜是哪一塊壞了，因為中間根本沒有任何能拿來測的半成品。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_224.jpg" alt="" /></p>

<div style="display:flex;flex-wrap:wrap;gap:14px;margin:24px 0;">
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:10px;padding:14px;">
<div style="font-weight:600;font-size:14px;">❌ 水平切</div>
<div style="font-size:12px;color:var(--text-secondary);margin:4px 0 12px;">一層做完才換下一層，中途沒有半成品可測</div>
<div style="display:flex;flex-direction:column;gap:6px;">
<div style="background:rgba(9,105,218,.16);border-radius:5px;padding:7px;font-size:12px;text-align:center;">全部資料庫</div>
<div style="background:rgba(9,105,218,.16);border-radius:5px;padding:7px;font-size:12px;text-align:center;">全部服務層</div>
<div style="background:rgba(9,105,218,.16);border-radius:5px;padding:7px;font-size:12px;text-align:center;">全部 API</div>
<div style="background:rgba(9,105,218,.16);border-radius:5px;padding:7px;font-size:12px;text-align:center;">全部前端</div>
</div>
<div style="margin-top:10px;background:rgba(207,34,46,.12);color:#cf222e;border-radius:6px;padding:8px;font-size:12px;text-align:center;">💥 1,200 行全寫完才第一次能跑，壞了還得回頭猜哪裡錯</div>
</div>
<div style="flex:1 1 240px;border:1px solid var(--border);border-radius:10px;padding:14px;">
<div style="font-weight:600;font-size:14px;">✅ 垂直切</div>
<div style="font-size:12px;color:var(--text-secondary);margin:4px 0 12px;">每一刀都貫穿前後端，做完就能驗</div>
<div style="font-size:11px;color:var(--text-secondary);margin-bottom:6px;">四個能各自驗證的小增量</div>
<div style="display:flex;gap:6px;">
<div style="flex:1;display:flex;flex-direction:column;gap:3px;"><div style="text-align:center;color:#1a7f37;font-size:13px;line-height:1;">✓</div><div style="background:rgba(9,105,218,.18);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">前端</div><div style="background:rgba(9,105,218,.13);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">API</div><div style="background:rgba(9,105,218,.08);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">DB</div></div>
<div style="flex:1;display:flex;flex-direction:column;gap:3px;"><div style="text-align:center;color:#1a7f37;font-size:13px;line-height:1;">✓</div><div style="background:rgba(9,105,218,.18);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">前端</div><div style="background:rgba(9,105,218,.13);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">API</div><div style="background:rgba(9,105,218,.08);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">DB</div></div>
<div style="flex:1;display:flex;flex-direction:column;gap:3px;"><div style="text-align:center;color:#1a7f37;font-size:13px;line-height:1;">✓</div><div style="background:rgba(9,105,218,.18);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">前端</div><div style="background:rgba(9,105,218,.13);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">API</div><div style="background:rgba(9,105,218,.08);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">DB</div></div>
<div style="flex:1;display:flex;flex-direction:column;gap:3px;"><div style="text-align:center;color:#1a7f37;font-size:13px;line-height:1;">✓</div><div style="background:rgba(9,105,218,.18);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">前端</div><div style="background:rgba(9,105,218,.13);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">API</div><div style="background:rgba(9,105,218,.08);border-radius:4px;padding:5px 2px;font-size:10px;text-align:center;">DB</div></div>
</div>
<div style="margin-top:10px;background:rgba(26,127,55,.12);color:#1a7f37;border-radius:6px;padding:8px;font-size:12px;text-align:center;">✓ 每個小階段都有檢查點，錯了當場停下來修</div>
</div>
<div style="flex:1 1 100%;font-size:11px;color:var(--text-secondary);text-align:right;margin-top:2px;opacity:.85;">✏️ 小編製圖，非 Dex 原始投影片</div>
</div>

<p>Dex 推的是 <strong>「垂直」切</strong>: 先假造一個 API 端點、讓它在前端能動、把前後接起來、再假造服務層、接著做資料庫遷移、最後整合。就算程式碼總量一模一樣，這樣切你沿路會有一個個檢查點可以驗證，不對就當場停下來修，而不是等全部寫完才一次爆炸。而那份「大綱」，就是逼模型乖乖照垂直方式切的最好工具。</p>

<h2 id="13-槓桿不只對自己也對團隊">13. 槓桿不只對自己，也對團隊</h2>

<p>這些比較短的文件 (設計、大綱) 還有一個大用途: <strong>幫團隊對齊方向</strong>。Dex 本人不是 HumanLayer 多數程式碼的負責人，他的共同創辦人才是。所以他會刻意先把自己的設計討論丟給對方看。他們沒有把這設成強制步驟，但他想確保等到正式審查程式碼那一刻，對方只會說「對，這就是我要的」。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_251.jpg" alt="" /></p>

<p>換句話說，<strong>他那些不夠好的決定，會在那份 200 行的文件上就被攔下來</strong>，而不是等他把程式碼寫完、跑起來、產生感情之後才被退貨。換個角度看就是省時間: 用 AI 幫你寫，純寫程式的時間從好幾小時縮到 20 分鐘，但這仍然是個兩天的功能，因為跟團隊對齊、等人審查、跨團隊協調、測試驗證，這些都沒省到。可是當你連「規劃」和「對齊」都用 AI 加速、而且對得更準，後面的審查和返工自然也跟著縮短。</p>

<h2 id="14-rpi-進化成-crispy">14. RPI 進化成 CRISPY</h2>

<p>把研究與規劃的各個階段攤開，完整流程是: 提問 (Questions) → 研究 (Research) → 設計 (Design) → 結構大綱 (Structure Outline) → 計畫 (Plan) → 開分支 (Worktree) → 實作 (Implement) → 發 PR (Pull Request)。</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_260.jpg" alt="" /></p>

<div style="margin:26px 0;">
<div style="display:flex;flex-wrap:wrap;gap:14px;align-items:stretch;">
<div style="flex:5 1 320px;border:1px solid var(--border);border-radius:10px;padding:14px;background:linear-gradient(180deg,var(--tag-bg),var(--bg));">
<div style="font-size:12px;font-weight:600;color:var(--accent);margin-bottom:10px;">研究與規劃 · 前五階段</div>
<div style="display:flex;flex-wrap:wrap;gap:8px;">
<div style="flex:1 1 84px;min-width:78px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 5px;text-align:center;line-height:1.35;"><div style="font-weight:600;font-size:13px;">① 提問</div><div style="font-size:10px;color:var(--text-secondary);">Questions</div></div>
<div style="flex:1 1 84px;min-width:78px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 5px;text-align:center;line-height:1.35;"><div style="font-weight:600;font-size:13px;">② 研究</div><div style="font-size:10px;color:var(--text-secondary);">Research</div></div>
<div style="flex:1 1 84px;min-width:78px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 5px;text-align:center;line-height:1.35;"><div style="font-weight:600;font-size:13px;">③ 設計</div><div style="font-size:10px;color:var(--text-secondary);">Design</div></div>
<div style="flex:1 1 84px;min-width:78px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 5px;text-align:center;line-height:1.35;"><div style="font-weight:600;font-size:13px;">④ 結構大綱</div><div style="font-size:10px;color:var(--text-secondary);">Structure Outline</div></div>
<div style="flex:1 1 84px;min-width:78px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 5px;text-align:center;line-height:1.35;"><div style="font-weight:600;font-size:13px;">⑤ 計畫</div><div style="font-size:10px;color:var(--text-secondary);">Plan</div></div>
</div>
</div>
<div style="flex:3 1 200px;border:1px solid var(--border);border-radius:10px;padding:14px;background:var(--surface);">
<div style="font-size:12px;font-weight:600;color:var(--text-secondary);margin-bottom:10px;">動手實作 · 後三階段</div>
<div style="display:flex;flex-wrap:wrap;gap:8px;">
<div style="flex:1 1 84px;min-width:78px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 5px;text-align:center;line-height:1.35;"><div style="font-weight:600;font-size:13px;">⑥ 開分支</div><div style="font-size:10px;color:var(--text-secondary);">Worktree</div></div>
<div style="flex:1 1 84px;min-width:78px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 5px;text-align:center;line-height:1.35;"><div style="font-weight:600;font-size:13px;">⑦ 實作</div><div style="font-size:10px;color:var(--text-secondary);">Implement</div></div>
<div style="flex:1 1 84px;min-width:78px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 5px;text-align:center;line-height:1.35;"><div style="font-weight:600;font-size:13px;">⑧ 發 PR</div><div style="font-size:10px;color:var(--text-secondary);">Pull Request</div></div>
</div>
</div>
</div>
<div style="text-align:center;font-size:12px;color:var(--text-secondary);margin-top:12px;">八階段字首排成 QRSPI 不好念 → 挑順口字母改寫成好記的 <b style="color:var(--accent);">CRISPY</b></div>
<div style="font-size:11px;color:var(--text-secondary);text-align:right;margin-top:8px;opacity:.85;">✏️ 小編製圖，非 Dex 原始投影片</div>
</div>

<p>這串階段的字首排出來其實是不怎麼順口的「QRSPI」，拼不成什麼像樣的縮寫，所以他們乾脆挑了喜歡的字母、改寫成好記的 <strong>CRISPY</strong> (酥脆的)。所以這不是嚴格的首字母縮寫，就是個順口的暱稱，RPI 就這樣進化成了 CRISPY。Dex 自己也吐槽: 本來想讓團隊更好上手，結果步驟不減反增 (他口頭說是「從三步變成七步」，但投影片上其實列了八個階段)。至於「平台團隊要怎麼把這套 prompt 持續改好、又不會把某個團隊原本順手的流程搞壞」，他坦言這還是個沒有答案的難題。</p>

<h2 id="15-笨蛋區現在還準嗎">15. 「笨蛋區」現在還準嗎?</h2>

<p>最後有人問: 你研究很久的「笨蛋區」(上下文一旦用超過 40%，模型就開始變笨)，在現在這麼多自動壓縮機制的情況下還準嗎? Dex 的回答蠻務實的:</p>

<p><img src="/assets/images/dexter-rpi-crispy/frame_154.jpg" alt="" /></p>

<div style="margin:24px 0;">
<div style="font-size:12px;color:var(--text-secondary);margin-bottom:8px;">上下文用量 (Context %) 與「笨蛋區」</div>
<div style="display:flex;height:40px;border-radius:8px;overflow:hidden;border:1px solid var(--border);font-size:12px;">
<div style="flex:40;background:#dafbe1;color:#1a7f37;display:flex;align-items:center;justify-content:center;">0–40%　安全區</div>
<div style="flex:20;background:#fff8c5;color:#9a6700;display:flex;align-items:center;justify-content:center;">40–60%　老手衝刺</div>
<div style="flex:40;background:#ffebe9;color:#cf222e;display:flex;align-items:center;justify-content:center;">60%+　模型開始變笨</div>
</div>
<div style="display:flex;justify-content:space-between;font-size:11px;color:var(--text-secondary);margin-top:8px;">
<span>新手: 守在 40% 以下，到 60% 就收尾</span>
<span>老手: 常態衝到 60%，看任務調整</span>
</div>
<div style="font-size:11px;color:var(--text-secondary);text-align:right;margin-top:10px;opacity:.85;">✏️ 小編製圖，非 Dex 原始投影片</div>
</div>

<p>「如果你已經重度用這類工具半年到九個月、每週 60 小時，那『笨蛋區』對你來說已經不是個有用的概念了。」他自己會常態衝到 60%，也會在某些任務上刻意壓在 30% 以下，全看任務複雜度、以及「指令」和「資訊」的比例。但對新手，他還是會教: 守在 40% 以下，到 60% 就想辦法收尾。</p>

<p>而且他們<strong>不用內建的自動壓縮</strong>，因為所有重要的東西，都已經寫進那些靜態文件 (設計、大綱、計畫) 裡了，你隨時可以從中斷的地方接著做，完全不必擔心自動或手動壓縮會不會搞砸品質。這其實才是這套流程最底層的好處: 把重要的脈絡放在你看得到、改得動的檔案裡，而不是放在那段隨時可能被壓掉的對話裡。</p>

<hr />

<p>把兩場連起來看，會發現 Dex 的核心信念其實沒變: 模型本身是沒有記憶的，上下文的品質決定一切，所以千萬不要外包思考。變的是<strong>「人的注意力該擺在哪」</strong>: 從「讀計畫」挪到「讀程式碼，加上讀那幾份更短的對齊文件」。</p>

<p>小編覺得最值得帶走的，是他那句認錯: 「如果你的工具得念咒語才用得好，那該修的是工具。」這放在任何 AI 產品上都成立。我們太容易把「AI 用不好」歸咎於使用者「prompt 下得不夠好」，但真正成熟的系統，應該讓一般人不用學什麼魔法，也能拿到好結果。RPI 一路演化成 CRISPY，本質上就是在做一件事: 把「資深工程師腦袋裡那些說不清的隱性技巧」，一步步變成「流程裡寫得明明白白的步驟」。</p>

<h2 id="小編補充-用一個例子看懂這四個階段差在哪">小編補充: 用一個例子看懂這四個階段差在哪</h2>

<p>研究 (Research)、設計 (Design)、結構大綱 (Structure Outline)、計畫 (Plan) 這四個名字有點接近，很容易搞混。小編用一個具體情境幫大家分清楚 (這是小編自己舉的例子，不是 Dex 原話):</p>

<blockquote>
  <p>假設你要<strong>幫一個現有的 SaaS 帳號系統，加上雙因素驗證 (2FA)</strong>。</p>
</blockquote>

<p><strong>1. 研究 (Research) — 現在長什麼樣?</strong></p>

<p>摸清現況的客觀事實，不帶任何意見。</p>

<p>派子代理去查: 登入流程現在怎麼走 (哪個檔案、第幾行)、密碼怎麼驗證和儲存、session 怎麼建立、有沒有現成的簡訊或 email 寄送機制、使用者資料表長什麼樣。純粹記錄「現在是這樣」，完全不碰「2FA 該怎麼做」。</p>

<p><strong>2. 設計 (Design) — 要變成什麼樣?為什麼?</strong></p>

<p>拍板大方向和關鍵取捨，這是跟 AI 對齊「要蓋什麼」的地方，大約 200 行。</p>

<p>要支援哪幾種雙因素驗證: 是用驗證器 app 的 TOTP (Time-based One-Time Password，時間型一次性密碼)，還是簡訊? 備援碼怎麼處理、強制開啟還是讓使用者選用、開啟後現有的登入狀態要不要失效。會列出已經拍板的決定 (例如「第一版只做 TOTP」)，以及還要你回答的開放問題 (例如「簡訊費用誰吸收?」)。</p>

<p><strong>3. 結構大綱 (Structure Outline) — 分幾步、照什麼順序走?</strong></p>

<p>把工作「垂直」切成幾個有先後順序、能各自驗證的階段。就像前面說的 C 標頭檔: 只勾出有哪幾個階段、什麼順序、各自怎麼測,讓你看懂整體要怎麼走,但完全不碰「每一步改哪一行」那種細節 (那是下一階段「計畫」才做的事),大約兩頁:</p>

<ol>
  <li>在使用者資料表加欄位、寫好 migration</li>
  <li>後端產生與驗證 TOTP 的元件，連同它對外的函式長相</li>
  <li>啟用與驗證的 API 介面</li>
  <li>前端的設定畫面和登入時的輸入框</li>
</ol>

<p>每個階段各自要怎麼測，也一併寫清楚。</p>

<p><strong>4. 計畫 (Plan) — 每一步到底改哪一行?</strong></p>

<p>給 AI 照著做的施工圖，大約八頁，具體到「就算是全世界最笨的模型也很難搞砸」: 「在 <code class="language-plaintext highlighter-rouge">user.rb</code> 的第幾行加上 <code class="language-plaintext highlighter-rouge">totp_secret</code> 欄位」「新增 <code class="language-plaintext highlighter-rouge">totp_verifier.rb</code>，內容大概是這樣…」，每一步都標明檔名、行號、甚至程式碼片段。</p>

<p>最後用一張圖串起來，順序就是從「現況」一路收斂到「每一行」:</p>

<div style="margin:22px 0;">
<div style="font-size:11px;color:var(--text-secondary);text-align:center;margin-bottom:6px;">廣 · 現況的全部事實</div>
<div style="border:1px solid var(--border);border-radius:8px;padding:9px;margin:0 auto 8px;max-width:100%;text-align:center;background:linear-gradient(180deg,var(--tag-bg),var(--bg));"><b style="font-size:14px;">研究 Research</b><br /><span style="font-size:12px;color:var(--text-secondary);">現在長怎樣</span></div>
<div style="border:1px solid var(--border);border-radius:8px;padding:9px;margin:0 auto 8px;max-width:82%;text-align:center;background:linear-gradient(180deg,var(--tag-bg),var(--bg));"><b style="font-size:14px;">設計 Design</b><br /><span style="font-size:12px;color:var(--text-secondary);">要變成怎樣、為什麼</span></div>
<div style="border:1px solid var(--border);border-radius:8px;padding:9px;margin:0 auto 8px;max-width:64%;text-align:center;background:linear-gradient(180deg,var(--tag-bg),var(--bg));"><b style="font-size:14px;">結構大綱 Structure Outline</b><br /><span style="font-size:12px;color:var(--text-secondary);">分幾步、依序、怎麼驗</span></div>
<div style="border:1px solid var(--border);border-radius:8px;padding:9px;margin:0 auto 6px;max-width:46%;text-align:center;background:linear-gradient(180deg,var(--tag-bg),var(--bg));"><b style="font-size:14px;">計畫 Plan</b><br /><span style="font-size:12px;color:var(--text-secondary);">每一步改哪一行</span></div>
<div style="font-size:11px;color:var(--text-secondary);text-align:center;margin-top:2px;">窄 · 收斂到每一行程式碼</div>
<div style="font-size:11px;color:var(--text-secondary);text-align:right;margin-top:10px;opacity:.85;">✏️ 小編製圖，非 Dex 原始投影片</div>
</div>

<p>越前面的階段越需要人用力把關，越後面就越接近機械執行。</p>

<p>這也對應到 Dex 的具體建議: 真正值得你花力氣讀的，是前面那兩份比較短的<strong>設計</strong>和<strong>結構大綱</strong> (在這裡跟 AI 對齊、糾正方向)，還有最後 AI 寫出來的<strong>程式碼</strong> (一定要讀)。中間那份又長又容易跑掉的<strong>計畫</strong>反而不必逐行細讀，快速掃過 (spot check) 確認沒問題就好。把人類的注意力留在「最高槓桿的對齊」和「真正會上線的程式碼」這兩端，正是這整套流程的用意。</p>

<div style="border:1px solid var(--border);border-radius:10px;padding:14px;margin:18px 0;">
<div style="font-size:12px;color:var(--text-secondary);margin-bottom:10px;">一句話: 該把注意力花在哪?</div>
<div style="display:flex;flex-wrap:wrap;gap:8px;">
<div style="flex:1 1 130px;background:rgba(26,127,55,.10);border:1px solid rgba(26,127,55,.3);border-radius:8px;padding:10px;font-size:12px;text-align:center;line-height:1.4;">🟢 設計 + 結構大綱<br /><span style="color:#1a7f37;">仔細讀，在這對齊方向</span></div>
<div style="flex:1 1 130px;background:rgba(154,103,0,.10);border:1px solid rgba(154,103,0,.3);border-radius:8px;padding:10px;font-size:12px;text-align:center;line-height:1.4;">🟡 計畫<br /><span style="color:#9a6700;">快速掃過 (spot check)</span></div>
<div style="flex:1 1 130px;background:rgba(26,127,55,.10);border:1px solid rgba(26,127,55,.3);border-radius:8px;padding:10px;font-size:12px;text-align:center;line-height:1.4;">🟢 程式碼<br /><span style="color:#1a7f37;">一定要讀</span></div>
</div>
<div style="font-size:11px;color:var(--text-secondary);text-align:right;margin-top:10px;opacity:.85;">✏️ 小編製圖，非 Dex 原始投影片</div>
</div>]]></content><author><name>ihower</name></author><category term="Coding" /><category term="Agent" /><category term="Context Engineering" /><summary type="html"><![CDATA[幾個月前小編整理過 Dex Horthy 的一場演講。他是 HumanLayer 的創辦人，也是去年提出 12-Factor Agents 的人。那場他大力推廣一套寫程式的 AI 工作流，叫做「研究 → 計畫 → 實作」(Research → Plan → Implement，簡稱 RPI)。那篇整理在這裡: 別再憑感覺了: 在複雜 Codebase 中解決困難問題。]]></summary></entry><entry><title type="html">GitHub Copilot 大規模使用 Claude 的工程心法: 快取、多模型調度與評測</title><link href="https://blog.aihao.tw/2026/06/01/github-claude-caching-harnesses-advisors/" rel="alternate" type="text/html" title="GitHub Copilot 大規模使用 Claude 的工程心法: 快取、多模型調度與評測" /><published>2026-06-01T00:00:00+00:00</published><updated>2026-06-01T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/01/github-claude-caching-harnesses-advisors</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/01/github-claude-caching-harnesses-advisors/"><![CDATA[<p>GitHub Copilot 一個月在平台上打出的訊息量，用夠長的時間軸來看是「以兆計」的等級。當量體大到這個程度，工程上的每一個小數點都會被放大成真金白銀。GitHub 的產品長 Mario Rodriguez 跟 Anthropic 的 Brad Adams 在「Code with Claude」這場演講 <a href="https://www.youtube.com/watch?v=y5TmF_6o6xk">Caching, harnesses, and advisors: Building on Claude at GitHub scale</a>，把 Copilot 在這個規模下踩過的坑、學到的東西攤開來講。小編覺得這場特別值得看的地方在於: 它不談空泛的概念，而是談一家每天打數十億次推論的公司，到底盯著哪些數字、做了哪些取捨。</p>

<p>🎬 影片: <a href="https://www.youtube.com/watch?v=y5TmF_6o6xk">Caching, harnesses, and advisors: Building on Claude at GitHub scale</a></p>

<p>以下是重點整理:</p>

<h2 id="1-三條主線-快取advisor量測">1. 三條主線: 快取、advisor、量測</h2>

<p>Mario 把整場濃縮成三件事，剛好也是 Copilot 接平台時幾乎所有決策的底層。</p>

<p>第一是 prompt 快取。他講得很白: 沒有快取「我們不會死，但天啊，花的錢會多到嚇人」，所以光是 1% 的效率提升，對他們就是好幾百萬美金的差別。第二是跟 Anthropic 一起做的 advisor 機制，目標是讓使用者在「對的時間」拿到「對的智慧」，底下又分成 advisor 跟 critic 兩種玩法。第三是每次新模型一推出，怎麼有條理地把它接進產線。</p>

<p><img src="/assets/images/github-claude-caching-harnesses-advisors/frame_306.jpg" alt="" /></p>

<p>這三條線背後都連回 GitHub 自己定的幾根柱子: 讓開發者保持在心流、讓團隊提速、用智慧跟信任在規模下把事情做成。Mario 說幾乎每一個產品決策都踩在這幾根柱子上，連談平台整合也一樣。</p>

<h2 id="2-為什麼-1-的快取效率值好幾百萬">2. 為什麼 1% 的快取效率值好幾百萬</h2>

<p>這個比喻很到位。Mario 把快取效率比成高頻交易: 單看一筆，1% 沒什麼感覺，但乘上每天數十億次呼叫，1% 就是好幾百萬。</p>

<p>所以對 Copilot 來說，快取根本不是「要不要做的最佳化」，而是這個規模的服務能不能活下去的前提。這也解釋了為什麼後面那麼多篇幅，都在講怎麼把快取命中率往上推、怎麼避免它被無端打掉。</p>

<h2 id="3-先有儀表板再談最佳化">3. 先有儀表板，再談最佳化</h2>

<p>Mario 的第一個務實建議是: 先把快取命中率「儀表化」。沒有資料，要做決策真的非常非常難。</p>

<p>他特別提到 Anthropic 前陣子釋出了一個官方儀表板（Copilot 有先拿到預覽），可以看自己的快取命中比、對 Messages API 送了多少訊息。他直接喊話: 還沒去看的話，拜託去看一下，這是第一步。</p>

<p>不過 Copilot 真正盯的是自己那套，而且重點不是看單一數字，是看「模型之間的差異」。</p>

<p><img src="/assets/images/github-claude-caching-harnesses-advisors/frame_80.jpg" alt="" /></p>

<p>這張就是他們其中一個儀表板的長相: 左邊是 Opus 4.6、中間是 4.7，旁邊那欄就是兩者的差值，還把 Sonnet 跟 Haiku 一起拉進來對照。新模型一進來（有時候在很早的測試階段就拿到），他們會跑一整套基準測試，像 Terminal Bench 2 這類，再加上自己內部的題庫，決定模型表現夠不夠格上線。上線後繼續盯資料，大概 30 天（有時更快）就能把那個模型的最佳化收尾。</p>

<p>這個差值還有一個很實際的用途: 要不要把預設模型從 4.6 換成 4.7，就看兩者的快取命中率差多少。Mario 說這個例子裡差了 1.3%——聽起來很小，但乘上每天數十億次呼叫，又是一大筆錢，所以這種決策一定要有資料撐著。</p>

<h2 id="4-健康的快取命中率是-94-以上掉到-70-通常代表有-bug">4. 健康的快取命中率是 94% 以上，掉到 70% 通常代表有 bug</h2>

<p>這是小編覺得最有衝擊力的一個數字。要在規模下運營這個服務，Copilot 的快取命中率通常得跑在 94、95、96% 以上。</p>

<p>更狠的是反過來看: 如果命中率掉到 70%，那「八九不離十是有 bug」。意思是某個地方做錯了，可能是呼叫模型的方式、組 prompt 的方式、或整條流程出了問題，得回頭改。換句話說，他們不是把高命中率當「加分題」，而是當「健康指標」——低了就代表系統壞了。</p>

<p>他也補了一個常被忽略的成本算術: 從輸入端看，命中快取的 token 只算 10% 的成本，所以「命中」跟「沒命中」之間是 10 倍的差距。如果你一直讓快取失效，等於是用 10 倍的價錢在付那些 token。</p>

<h2 id="5-前綴要靜如止水-別在前面塞會變動的東西">5. 前綴要靜如止水: 別在前面塞會變動的東西</h2>

<p>要把命中率從 50% 推到 70%、再推到 80%、90% 以上，Mario 強調這「不是運氣，是大量的硬功夫跟工程」。他點出三個關鍵，而且每一個都是踩過坑換來的教訓。</p>

<p><img src="/assets/images/github-claude-caching-harnesses-advisors/frame_116.jpg" alt="" /></p>

<p>第一個是: 前綴（prefix）裡絕對不要放會變動的內容。快取的階層是 system prompt → 工具 → 對話歷史 → 最後送出的訊息，越前面越要穩。他舉了一個血淋淋的例子: 他們曾經在 system prompt 裡塞了一個 UUID，這東西每次都在變，等於每次都把整段快取打掉，命中率直接崩盤。所以 system prompt 要靜到不能再靜，一點會變的東西都別放。</p>

<h2 id="6-工具只在必要時才動而且要有回歸測試護體">6. 工具只在必要時才動，而且要有回歸測試護體</h2>

<p>第二個教訓是工具。如果你動態載入工具、讓那段工具前綴一直變，那後面整段對話又會被連帶失效一次。</p>

<p>Mario 說他們在工具這塊吃過不少虧，所以建議是: 一定要有大量的回歸測試。因為你之後一定會大量實驗 skills 跟工具，整條流程裡得有測試守著，確保你在動工具的時候，沒有不小心把快取打爛。這點正好呼應投影片那段程式碼結構——左邊三行標語寫的就是「不放動態內容、只在必要時改工具、維持快取親和性」，把這三條規矩直接寫死在程式裡。</p>

<h2 id="7-多模型-harness-下的快取親和性最難搞">7. 多模型 harness 下的「快取親和性」最難搞</h2>

<p>第三個、也是最硬的一個: 快取親和性（cache affinity）。Mario 直言，當你跑的是一個「多模型 harness」時，這件事真的很難。</p>

<p>拿 Copilot 當例子: 一個使用者可能先呼叫 Opus，接著叫一個 GPT 模型，再叫一個開源模型，然後又繞回 Opus。在這一連串切換裡，他得保證下一次的 Opus 呼叫——也就是接著上一次 Opus 留下來的那段——還能對到正確的快取。光是要在多模型 harness 裡守住這件事，他們就投入了非常多工。</p>

<p>這段對任何在做「多模型路由」的人都是提醒: 模型一換來換去，快取就容易斷，而把它接好，是真正的工程難題。</p>

<h2 id="8-破除迷思-長上下文不等於更貴">8. 破除迷思: 長上下文不等於更貴</h2>

<p>Mario 說他常被客戶問: 用長上下文是不是就比較貴? 他的答案很乾脆: 不是。</p>

<p><img src="/assets/images/github-claude-caching-harnesses-advisors/frame_136.jpg" alt="" /></p>

<p>他們做了一個快速實驗來佐證，投影片標語直接寫「長上下文 = 更多 token，但不等於更貴」。同一個模型、模擬不同行為: 較小的上下文視窗，平均壓縮次數是較大視窗的三倍。</p>

<p>關鍵在這裡: 輸出 token 比輸入貴很多（以 Opus 為例大約是 5 比 25），而每做一次壓縮，光是要把訊息摘要起來就得吐出大約 4,000 個輸出 token。所以你越常壓縮，輸出 token 就越爆，連帶快取也被嚴重打掉。結論是: 長上下文視窗本身不會讓你花更多錢，真正要懂的是「壓縮是怎麼被觸發的」，並依場景幫使用者把它管好。</p>

<h2 id="9-快取這塊的收尾心法">9. 快取這塊的收尾心法</h2>

<p>Mario 把快取這段收在幾句務實的話上。</p>

<p><img src="/assets/images/github-claude-caching-harnesses-advisors/frame_149.jpg" alt="" /></p>

<p>先把命中率儀表化、做一個你真的會去看的儀表板，把時間投進去——官方已經幫你出一個了，至少先用那個，然後再進一步去看模型之間的差異、看上線前跟上線後的變化。他甚至講了一句很重的話: 在你把命中率看清楚之前，其他效率都不重要。把它從 50 推到 70、推到 90，會花掉大量工程時間，但這就是優先級最高的事。</p>

<p>另一個重點是「分介面量測」。Copilot 不只有 VS Code，還有 Copilot CLI、雲端的 coding agent、IntelliJ、行動版。介面這麼多，你得決定要「跨介面共用同一套 harness」還是「個別微調」，而且每次出版本都要清楚知道: 這次是進步了還是退步了。</p>

<h2 id="10-advisor-策略-小模型卡關才呼叫大模型">10. advisor 策略: 小模型卡關才呼叫大模型</h2>

<p>接著 Anthropic 的 Brad Adams 上場，講另一條從 Copilot 來的回饋: 團隊想要「Opus 等級的智慧，但 Haiku 等級的價格」。做法叫 advisor 策略，靈感來自資淺工程師配個資深導師。同理，你拿 Haiku 當執行者，給它一個能呼叫 Opus 的工具; 大部分時候 Haiku 自己處理，只有遇到它自己搞不定的關鍵點，才去問 Opus。因為動用 Opus 的時機壓得很保守，他們的評測顯示這樣能拿到接近 Opus 的智慧，成本卻低很多。Brad 還現場 demo 了一道刁鑽題目（要印出 <code class="language-plaintext highlighter-rouge">Hello World!</code>、但程式碼不能用到某些字母）: 純 Haiku 一直瞎試兜不出來，加了 advisor 那邊則問 Opus 一句、拿到提示就收工。</p>

<blockquote>
  <p>編按: 這種「小模型卡關才呼叫大模型當顧問」的做法，小編是持保留態度的。它本質上就是一種跨模型的 multi-agent 拆分，而這類架構的代價（token 成本常見 3–10 倍、context 污染、協調開銷）在實戰裡很容易被低估。之前那篇 <a href="/2026/05/19/multi-agent-anti-patterns-and-patterns/">Multi-Agent 反模式與模式</a> 整理過不少證據，包括 Cognition/Devin 的實戰心得——很多時候把單一 agent 的 prompt 跟 harness 調好，效果不輸甚至勝過硬拆出一個顧問模型。demo 很漂亮，但要不要真上 advisor，建議先拿自己的場景做對照實驗再說。</p>
</blockquote>

<h2 id="11-rubber-duck-把-critic-插在會複利的時間點">11. rubber duck: 把 critic 插在「會複利」的時間點</h2>

<p>Mario 回到台上又補了一招，叫 rubber duck（橡皮鴨）。他說「我們是 GitHub，命名上總有點怪」，但這招的核心是: 在對的時間點插入一個「批評者」(critic)。</p>

<p>advisor 跟 critic 的差別在角色: advisor 比較像顧問，回答「這個我來幫你算」; critic 則更主動地說「我覺得你應該這樣做」。rubber duck 的流程是——模型在實作到一半時主動去要一份批評（跨模型家族，例如找 4.6 跟 4.5 Opus），拿到批評後，在真正動手前先修正計畫，再照修好的方向做下去。</p>

<p><img src="/assets/images/github-claude-caching-harnesses-advisors/frame_238.jpg" alt="" /></p>

<p>他們把這個批評者插在三個核心位置: 第一是「擬好計畫之後」（很多使用者都是先做計畫再執行）; 第二是「複雜實作之後」，他形容這像一種「預先的程式碼審查」，先擋一輪，能省下拖到正式審查才被打槍的 token; 第三是「寫完測試、但還沒跑測試之前」，在 CI 測試很花時間的地方插一刀，能更快把開發者送回心流。</p>

<p>Mario 自己最有感的是計畫階段: 現在這些系統都越來越會規劃，如果你在這個點把錯誤抓出來，後面拿到的收益最大。投影片那句話講得很精準: 把智慧集中在「會複利的時刻」，而計畫階段就是槓桿最高的那個點。rubber duck 現在已經是 Copilot CLI 裡的實驗功能，下載開起來就能用，你可以直接說「幫我擬個計畫，並找 rubber duck 諮詢一下」，就會拿到一份跨模型家族的快速批評。</p>

<h2 id="12-新模型上線的方法論-從-cappy-端點到雙軌評測">12. 新模型上線的方法論: 從 Cappy 端點到雙軌評測</h2>

<p>第三大塊回到「新模型怎麼接」。Mario 開玩笑說，接平台的人都知道那種感覺: 週六早上五點接到電話，對方說「我們禮拜二要發新模型囉」。一開始他們做得很亂，但這兩年半下來，已經磨出一套很有方法的流程。</p>

<p>Anthropic 給一個待測模型，他們先把它接進內部叫 Cappy 的東西（也就是 Copilot API），開出一個端點。從端點往下要做三件事: 一是在 harness 跟 prompt 上動工，因為是多模型 harness，得更新各家的 system prompt（這塊跟 Anthropic 密切合作）; 二是把工具介面調對、調好; 三是微調 agent 迴圈（這部分他說現在已經不太需要動了），然後在上下文管理、壓縮、衝到正確的快取命中率上投入更多。</p>

<p>接著是兩條軌: 離線基準測試，跟內部試用（dogfooding，可以理解成線上基準測試）。大量微軟工程師、GitHub 工程師、還有一批密切合作的使用者會實際去用、給回饋。然後把發現整理成一份文件——現在不只寫文件，還會展開成更細的簡報——跟 Anthropic 來回循環，談 API 要改什麼、甚至模型本身或 checkpoint 要怎麼調，一路循環到模型正式發表。</p>

<h2 id="13-離線只是基準線真功夫在線上">13. 離線只是基準線，真功夫在線上</h2>

<p>這一段是小編覺得對做 eval 的人最受用的判斷。Mario 說: 離線評測會給你一個指標沒錯，但它「不會是現實」。</p>

<p>你從線上評測、從上線後的線上實驗學到的，遠比離線多。離線只是設一條基準線——如果那條線穩定，你大致知道上線會長怎樣，能給個不錯的預期。但所有細節都不是靠離線磨出來的。實務上他們常常要花好幾天、甚至好幾週，靠線上實驗（大量 A/B 測試）才把一個模型整個調順，而且每週都有報告往上呈報給 Anthropic、也橫向同步給各團隊。</p>

<h2 id="14-最後的鐵律-量結果不要量活動">14. 最後的鐵律: 量「結果」，不要量「活動」</h2>

<p>優化 harness 時，Mario 把它拆成四個環節: 組 prompt 跟上下文、呼叫模型、執行工具、把結果接回去再迴圈。</p>

<p><img src="/assets/images/github-claude-caching-harnesses-advisors/frame_286.jpg" alt="" /></p>

<p>他花最多時間的地方，是「執行工具」跟「組 prompt 跟上下文」這兩塊。工具對他們非常重要，但他也警告: 工具越多，混亂越多、要調的越多。「上百個工具不是好事」——你應該分介面去調，針對精確的場景配上對的工具包。每要引入一個新工具，他們都會在模型層跟工具執行層兩邊，花大量時間去優化 harness。</p>

<p><img src="/assets/images/github-claude-caching-harnesses-advisors/frame_298.jpg" alt="" /></p>

<p>收尾他丟出兩個高層次原則。第一，訊號要「三角驗證」: 公開基準測試、內部基準測試、內部試用、A/B 測試、線上遙測都要有，而且要能用統計顯著性去信任它們。第二、也是最關鍵的一句——量「結果」，不要量「活動」(measure outcomes, not activity)。他舉例: 程式碼的「採用率」還行，但「存活率」是更好的指標。因為如果你接受了一段程式碼、過一陣子又把它刪掉，那其實根本沒達成目的。採用率再漂亮，存活率很低，就代表你沒做對事。</p>

<p>這也是整場演講最值得帶走的一句話。在 AI 時代做產品，如果只盯著那些好看的單項指標，很容易為了拉高某個數字，反而把整個產品真正該量的東西搞砸了。Copilot 在兆級訊息量下學到的，說穿了不是什麼花俏的招式，而是一種紀律: 盯對的數字（快取命中率、存活率）、把智慧花在會複利的時刻，然後相信線上的現實，勝過離線的想像。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Coding" /><category term="Eval" /><category term="Prompt" /><summary type="html"><![CDATA[GitHub Copilot 一個月在平台上打出的訊息量，用夠長的時間軸來看是「以兆計」的等級。當量體大到這個程度，工程上的每一個小數點都會被放大成真金白銀。GitHub 的產品長 Mario Rodriguez 跟 Anthropic 的 Brad Adams 在「Code with Claude」這場演講 Caching, harnesses, and advisors: Building on Claude at GitHub scale，把 Copilot 在這個規模下踩過的坑、學到的東西攤開來講。小編覺得這場特別值得看的地方在於: 它不談空泛的概念，而是談一家每天打數十億次推論的公司，到底盯著哪些數字、做了哪些取捨。]]></summary></entry><entry><title type="html">Replit 如何規模化評測和持續改進 Vibe coding</title><link href="https://blog.aihao.tw/2026/06/01/replit-agent-eval-at-scale/" rel="alternate" type="text/html" title="Replit 如何規模化評測和持續改進 Vibe coding" /><published>2026-06-01T00:00:00+00:00</published><updated>2026-06-01T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/06/01/replit-agent-eval-at-scale</id><content type="html" xml:base="https://blog.aihao.tw/2026/06/01/replit-agent-eval-at-scale/"><![CDATA[<p>Replit 的總裁兼 AI 負責人 Michele Catasta 在 Anthropic「Code with Claude」場子的這場演講 <a href="https://www.youtube.com/watch?v=snroDwX1-JU">Evaluating and improving Replit Agent at scale</a>，小編覺得是少數真的把「規模化做 eval」講到骨子裡的一場。Replit 每天對著數百萬使用者出貨好幾個版本的 agent，模型又一直在變，他們到底怎麼確定 agent 真的有變好、而不是憑感覺?這場演講把整套體系攤開來講，還順手在台上 open source 了一個新的 vibe coding benchmark。</p>

<p>🎬 影片: <a href="https://www.youtube.com/watch?v=snroDwX1-JU">Evaluating and improving Replit Agent at scale</a></p>

<p>以下是重點整理:</p>

<h2 id="1-replit-面對的是-vibe-coding-最極端的那一版">1. Replit 面對的是 vibe coding 最極端的那一版</h2>

<p>Catasta 一開場就先界定問題的特殊性。vibe coding 這個詞涵蓋範圍很廣，一端是給軟體工程師用的工具，但 Replit 的場景是最極端的那一端: 使用者只給一句自然語言的需求描述，其他什麼都沒有。</p>

<p>他講得很白: 使用者期待「從一句 prompt 直接變成一個能跑的應用」，但他們不會告訴你要用什麼框架，不會寫測試，也不指定任何實作細節，就是預期 agent 跑完之後東西能動。這跟過去替軟體工程師打造 agent 的假設完全不一樣，所以「過去做 eval 的方式，跟過去建 agent 的方式，都必須從根本上改變」。</p>

<p>這點是整場演講的地基。Replit 的使用者大多是完全沒寫過程式碼的知識工作者(knowledge worker)，這直接決定了他們的 eval 不能只看「程式碼有沒有改對」，而要看「做出來的 app 有沒有照使用者要的去動」。</p>

<h2 id="2-為什麼這件事特別難-腳下的地一直在動">2. 為什麼這件事特別難: 腳下的地一直在動</h2>

<p>Catasta 用「shifting grounds」(腳下的地一直在動) 來形容他們的處境，小編覺得這個比喻蠻到位的。</p>

<p>模型一直在變，而且演進速度比一年前更快。模型一變，他們的 system prompt、user prompt 就得跟著改;為了配合產品新功能，prompt 也要常常調整;工具定義也一直在動，他們不斷新增、簡化工具。然後最關鍵的是出貨頻率: Replit 每天都有好幾次發布，而且是直接推到數百萬使用者面前。</p>

<p>在這種一切都在流動的狀態下，要怎麼確定 agent 每天都在進步、而且真的對齊使用者要的東西?這就是他們整套 eval 體系要回答的問題。</p>

<h2 id="3-核心主張-eval-不該是一個分數而該是一條串流">3. 核心主張: eval 不該是一個分數，而該是一條串流</h2>

<p><img src="/assets/images/replit-agent-eval-at-scale/frame_29.jpg" alt="" /></p>

<p>這張投影片是整場的核心論點。左邊是「舊工作」: 你跑一次 eval，產出一個分數，然後由人類來看這個分數，決定「我的 agent harness 比之前好還是退步了」「該不該多做點 post-training」。整個流程在「人類做決定」這一步就停了。</p>

<p>Catasta 主張要往右邊那種「持續性」的設定走: eval 不是給人類消費的一個分數 (a score humans consume)，而是一個系統運行所依賴的串流 (a stream a system runs on)。當你有東西真的跑在線上、而且每天產生數百萬條 trace 的時候，這些 trace 本身就藏著大量可以挖掘的超額價值(alpha)。</p>

<p>所以他們做的是 offline eval (類似 SWE-bench 那種 benchmark) 加上 online eval (基於真實系統使用) 的組合。他對 eval 的要求很明確: 第一要優化使用者真正在乎的東西，而不是只告訴你 agent 有沒有把程式碼改對;第二要能立刻指出什麼壞掉了，這會直接告訴團隊下一步該出貨什麼改進。</p>

<h2 id="4-兩根支柱-出貨前的守門關卡出貨後的持續挖掘">4. 兩根支柱: 出貨前的守門關卡，出貨後的持續挖掘</h2>

<p>那個「黑盒子」裡面到底裝了什麼?Replit 的系統有兩根支柱。</p>

<p>一根是老派的 benchmark，通常在出貨新版 agent 之前跑，把它當成一個是非開關、一道發布前的關卡。如果 offline benchmark 出現重大退步，就停止發布;沒變化或變好，就放行。</p>

<p>另一根是他覺得更有意思、也是真正讓他們持續進步的部分: 大量的 A/B testing，加上把線上所有的 trace 拿來分群、從中萃取洞見。這根 online 支柱是在你出貨之後才開始運作，逼著你盡可能快地反應。</p>

<p>兩根支柱到位之後就形成一個迴圈: 看兩邊給你的結果、據此改程式碼、再跑 eval，洗了又重來。Catasta 說這就是他們在 Replit 每一天都在跑的循環。</p>

<h2 id="5-為什麼-swe-bench-不夠用-缺了功能正確性那一段">5. 為什麼 SWE-bench 不夠用: 缺了「功能正確性」那一段</h2>

<p><img src="/assets/images/replit-agent-eval-at-scale/frame_64.jpg" alt="" /></p>

<p>Catasta 特別花時間解釋為什麼要做比 SWE-bench 更貼近自己場景的東西。像 SWE-bench、HumanEval 這類 benchmark 在這個領域當然很關鍵，逼著大家把模型做得更強。但它們本質上都遵循同一套協定: 確認生成的程式碼符合 prompt、把 patch 套到 repository 上、跑測試，測試過了分數就高。</p>

<p>問題是這完全不反映 vibe coding 的真實狀況。使用者不寫測試，常常是從一個完全空白的 code base 開始。根本不存在「套 patch」這種情境，因為你是從零開始把東西蓋起來的。</p>

<p>所以真正要捕捉的是: 這個 app 有沒有做到使用者要求的事?投影片上他把這叫做「functional correctness gap」(功能正確性的落差)，並下了一句蠻精準的判斷: 功能正確性才是「沒有人在量測的那個訊號」(the signal nobody was measuring)。</p>

<h2 id="6-台上發表-vibench-一個端到端的-vibe-coding-公開-benchmark">6. 台上發表 ViBench: 一個端到端的 vibe coding 公開 benchmark</h2>

<p><img src="/assets/images/replit-agent-eval-at-scale/frame_73.jpg" alt="" /></p>

<p>為了補上這個落差，Catasta 直接在台上開源了 ViBench，一個端到端、針對 vibe coding 的公開 benchmark，他們做了好幾個月，網址在 vibench.ai。</p>

<p>設定很有意思。輸入就是一份 PRD(產品需求文件)，本質上是一段很長、描述怎麼蓋一個 app 的 prompt。他們不是用合成資料硬編出來的，而是從 Replit 的真實使用 trace 裡挑了 20 個案例，都是純英文的需求描述，沒有任何實作上的限制。然後有一個 harness 把 app 從空 repo 端到端蓋成可以動的東西。</p>

<p>關鍵的 insight 在最後一步: 與其在這裡停下來、找人類來評估這些 PRD 的產出，他們做了自動化的 evaluator。這就是讓 benchmark 從「大概一週跑一次」變成「每次都能跑」的關鍵——你 repository 裡只要有新的 PR merge 進來，就能跑一次。當然，跑所有 evaluation 的也是 AI。</p>

<h2 id="7-一個核心一份開放的情境型錄-從-zero-to-one-到-slop-on-slop">7. 一個核心、一份開放的情境型錄: 從 zero-to-one 到 slop-on-slop</h2>

<p><img src="/assets/images/replit-agent-eval-at-scale/frame_97.jpg" alt="" /></p>

<p>ViBench 的架構是「一個寫死的核心 + 兩個開放的槽」。寫死的核心是那個 behavioral eval (行為評估);前面兩個槽——輸入 (input) 跟策略 (strategy)——可以接任何東西。他們命名了五種配對，但你自己還能想出更多。</p>

<p>由淺到深大概是這樣:</p>

<ul>
  <li><strong>Zero-to-One</strong>: 輸入是 PRD，一次到位從零蓋到一。</li>
  <li><strong>Vibe-on-Ref</strong>: 從一個已經能動的 reference 實作開始，在上面加功能。</li>
  <li><strong>Vibe-on-Vibe</strong>: 從 agent 自己蓋的 MVP 開始，再疊一個新功能上去。Catasta 說這個用 X 上的黑話講就是「slop-on-slop」——agent 蓋了一個沒驗證過的東西，然後又在上面疊更多 agent 寫的程式碼，再跑評估看它還能不能動。</li>
  <li><strong>Parallel + Merge</strong>: 對應他們幾個月前發布的 Agent 4，把任務分解、平行跑多個 agent、再把所有 patch 合併的複雜度都藏起來。這個「平行加合併」的情境對一般 coding agent 來說挑戰大得多。</li>
  <li><strong>Decomposition</strong>: 輸入一份超大的 PRD，讓 agent 自己規劃跟拆解。</li>
</ul>

<p>他還補了個更狠的玩法: 從一個有 bug 的 app 開始，在上面加功能，看 agent 要多掙扎才能讓它正確運作。型錄是開放的，社群可以一直往裡面加更難的情境。</p>

<h2 id="8-真正的難關在怎麼打分-一個完全與實作無關的-evaluator">8. 真正的難關在「怎麼打分」: 一個完全與實作無關的 evaluator</h2>

<p>評分 (grading) 才是他們花最多力氣的地方，也是 ViBench 最硬的部分。</p>

<p>難在哪?因為在 vibe coding 裡，Replit 完全不給使用者任何框限(guardrail)，他們愛用什麼語言、什麼框架都可以。所以 evaluator 必須對「實作長什麼樣子」完全不可知 (agnostic)。</p>

<p>他們的做法是: evaluator agent 先讀過整個程式碼庫，然後開一個瀏覽器、指向 agent 蓋好的那個應用，接著一步一步照著測試計畫走。連測試計畫本身都是用自然語言寫的，動作像是「打開後台儀表板、用某個帳號登入、點某個開關」。任何一步失敗，就把它們全部彙整起來，生成一個分數。</p>

<p>Catasta 把這個跟 SWE-bench 對照: SWE-bench 永遠是固定的場地，你知道 repository、知道 test harness、知道怎麼讓它跑起來。但 ViBench 的場地是完全的 greenfield (全新空地)，這就是為什麼他們花了好幾個月才做出來。</p>

<h2 id="9-vibench-的兩個發現-前沿模型領先-2-倍改自己的程式碼最爛">9. ViBench 的兩個發現: 前沿模型領先 2 倍，改自己的程式碼最爛</h2>

<p>論文會在幾週後的研討會上發表 (專案主導人也是論文一作 Peter 就坐在台下)，但 Catasta 先劇透了兩個最值得注意的結果。</p>

<p>第一，前沿模型跟開源模型之間有將近 2 倍的差距。他特別點出這點，是希望整個社群——不管開放權重(open weight)還是封閉權重——都來採用這個 benchmark，因為每個模型廠商都在爬特定的 benchmark，他希望大家一起在 vibe coding 上整體進步。他甚至開玩笑說，把 benchmark 開源，社群可以一直把它弄得更難，「讓 Anthropic 的日子更難過一點」。</p>

<p>後段對談裡 Hannah 直接問了大家最好奇的問題: 為什麼不把這套 benchmark 當成自家的祕密武器、留著當護城河?Catasta 的回答蠻能看出他的價值觀——他出身研究圈，相信東西就該開放，而且「不相信在 eval 上互相競爭」。公開的 eval 能讓模型更好、agent 更好，最後是所有人的產品都更好。他還補了個有意思的轉折: 這個想法他過去在不少場合都試著對社群喊話、希望大家一起做，但喊久了發現沒人動手，最後只好自己先把它蓋出來。</p>

<p>第二個結果可能不太意外，但很重要: 大多數模型在「延伸自己寫的程式碼」時表現更差。也就是前面講的 slop-on-slop / vibe-on-vibe 情境，是目前為止最難的。</p>

<p>Catasta 從這裡帶出一個實戰建議，小編覺得這句對所有在 vibe coding 的人都很受用: 每次新增功能之間，一定要插一個測試的步驟。否則你就是不斷在搖晃的地基上往上蓋，最後你的 vibe coding 應用遲早會垮。</p>

<h2 id="10-第二根支柱-online-eval-數百萬條真實-session-的價值">10. 第二根支柱 online eval: 數百萬條真實 session 的價值</h2>

<p>光有 offline 還不夠。Catasta 強調他們同樣在乎 online eval，原因是兩者能收集到的訊號量級差太多。</p>

<p>offline 那邊大概就 20 個 app (他特別說之所以開源，就是歡迎社群貢獻更多 app、更多 prompt，把 benchmark 越弄越難)。但 Replit 這邊每天收集到數百萬條 session，而且這些 session 極有價值，因為它們捕捉的是使用者在平台上真正做的事，完全沒有腳本、agent 一直在跑。</p>

<p>問題就變成: 怎麼從這堆東西裡蒸餾出有用的資訊?</p>

<h2 id="11-ab-testing-是讓自己保持誠實的方式">11. A/B testing 是讓自己保持誠實的方式</h2>

<p><img src="/assets/images/replit-agent-eval-at-scale/frame_171.jpg" alt="" /></p>

<p>答案之一是大量的 A/B test。Catasta 說這是他們「讓自己保持誠實」的方式，因為 ViBench 只告訴你故事的一部分。他也直接喊話: 每一個在做 agent 的人，都應該盡早投資自己的 A/B testing 基礎建設，這是讓你穩定進步最好的方式。</p>

<p>他們在 agent 裡埋了大量的監測點(instrumentation)跟指標(metric)，可以問各種問題: 有沒有異常的花費?agent 跑得比預期久嗎?他們也持續追蹤使用者情緒 (user sentiment)——這訊號很好收，因為每次收到 prompt 都能做情緒分析，看使用者是不是越來越挫折。還有一個很 Replit 特有的強訊號: 使用者可以把做好的 app 發布出去，如果他願意把成品分享給同事或公開放出來，那就是很強的正面訊號。</p>

<p>但 Catasta 也很誠實地講了 A/B test 的殘酷真相: 結果幾乎不會是非綠即紅的乾淨答案。投影片上那個例子——agent 平均執行時間漲了 7%，但成本反而便宜 8%，同時正負面情緒都有波動。這時候該怎麼辦?這就是人類的品味跟產品哲學還是關鍵的地方。你幾乎永遠不會拿到一個水晶般清澈的 A/B test 結果。</p>

<h2 id="12-把不知道該找的問題給分群出來">12. 把不知道該找的問題給分群出來</h2>

<p><img src="/assets/images/replit-agent-eval-at-scale/frame_180.jpg" alt="" /></p>

<p>為了生成 A/B test 的候選，他們把每天每夜收到的所有 trace 拿來分群。先找出哪些群集是捕捉 agent 正常行為的 (這種很多，因為大多數時候 Replit 是成功的)，但總有一條長尾的問題會被浮現出來。</p>

<p>找到有問題的群集之後: 把所有摘要轉成 embedding、按類型分群，找出某個時間點上發生的各種失敗，再丟給 LLM 去精準分類到底發生什麼事。Catasta 強調，這件事不是靠 regex、掃日誌這類非常確定性的技術來做的，這正是差別所在——因為你能把語意上相近的東西分到一起，即使 agent 給你的輸出不見得每次都長一樣。</p>

<p>而且因為他們同時平行跑好幾個版本的 agent、每天出好幾個版本，所以不能用固定的分群設定，必須每天晚上重新訓練這些群集。</p>

<p>這裡有個很妙的好處: 重訓群集之後，可以回頭看某個群集在你以為修好問題之後是不是真的消失了。比方說有個工具失敗只在特定條件下、1% 的機率發生——這種根本不會出現在你的 Datadog 儀表板上，因為它不是那種 50% 都會發生的數字。但你 ship 了新 PR、那個群集消失了，你就開始有證據說自己緩解了這個問題。小編覺得這招對抓長尾 bug 特別實用。</p>

<h2 id="13-telescope-一個幾乎全自動的改進迴圈">13. Telescope: 一個幾乎全自動的改進迴圈</h2>

<p><img src="/assets/images/replit-agent-eval-at-scale/frame_232.jpg" alt="" /></p>

<p>他們內部把這套技術叫 Telescope，是一個 (Catasta 希望聽完之後你會覺得) 相當簡單的迴圈:</p>

<p>先<strong>發現問題</strong> (discover)，基於前面講的 trace 分群。再據此<strong>產生程式碼改動</strong> (create code changes)——這完全自動化，他們用 coding agent 直接根據從 trace、日誌、各種儀表板收集到的資訊去開 PR。然後<strong>評估</strong>這個改動: 先重跑 ViBench 看是不是破壞性改動，ViBench 在這裡像個試金石 (litmus test)，分數掉超過 10 分就判定這個改動是壞的。如果是有爭議、可能對 agent 有負面影響的改動，就跑 A/B test 放到使用者面前看取捨;如果是明顯的好球就直接出貨到線上。少數情況下，如果假設正確但 PR 不夠完美，就繼續迭代、再跑一輪 A/B test，直到配置夠好才真正出貨。</p>

<p>Catasta 下了個蠻有意思的註腳: 如果你想想，這跟你身為 AI engineer 平常做的事很像，只是現在 90% 都由迴圈裡的 agent 代勞，你不用為每一個步驟去流汗。</p>

<p>投影片上那個真實案例蠻具體: Telescope 標記出一個小但在成長的群集——環境設定在一條冷啟動 (cold-start) 的長尾上悄悄退化。Replit 的 agent 在第一次送 prompt 時就會啟動執行環境，而要把這套複雜度都架起來需要好幾秒。有一次長尾上的設定時間比預期久，agent 在環境還沒準備好就先動了起來。</p>

<h2 id="14-那個-cold-start-案例-為什麼非靠語意分群不可">14. 那個 cold-start 案例: 為什麼非靠語意分群不可</h2>

<p>這個案例 Catasta 講得很細，小編覺得很能說明 trace clustering 的價值。</p>

<p>現在的 agent 變得非常急著想修問題 (eager to fix)。所以 Replit 的 agent 就「跑偏了」(went on a tangent)，當場開始試圖修環境。但因為 agent 本質上不是確定性的，每一次 debug 的 session 看起來都長得不太一樣。</p>

<p>結果就是: 如果你只靠撈日誌去找這個長尾問題，它幾乎不會浮現。但一旦你透過語意層把所有 trace 分群，就會發現這個問題其實發生得相當頻繁，於是馬上就能做出 patch 修掉。這個案例因為是純退步 (pure regression)，所以不用跑 A/B test;如果是需要測試的問題，中間就會多插一個步驟。</p>

<h2 id="15-人類的品味還是最重要的地方">15. 人類的品味還是最重要的地方</h2>

<p><img src="/assets/images/replit-agent-eval-at-scale/frame_240.jpg" alt="" /></p>

<p>雖然 Catasta 是「盡量用 agent 優化 AI engineer 生活」的大力支持者，但他很明確地說，在他展示的這套迴圈裡，還是有大量需要人類做的智識工作。投影片上他把人類品味落在四個位置:</p>

<ul>
  <li><strong>假設選擇</strong> (hypothesis selection): 哪些問題值得花掉迴圈的「過夜預算」?因為跑在線上你會拿到超大量的修復候選，必須排優先序。每次找到一個群集，團隊都會先針對「這裡可能哪裡出錯」形成一個假設，假設夠有意思才往前走，而且不會放手讓 agent 在零監督下亂寫 PR。</li>
  <li><strong>實作架構</strong> (implementation architecture): 做出恰當的工程跟產品決策。</li>
  <li><strong>Eval 策展</strong> (eval curation): 他用了一個很傳神的說法——你每次決定要做某個 PR、跑某個 A/B test，其實都是在「形塑 agent 要爬的那座山」(shaping the hill that we try to optimize)。如果你只在乎讓產品更便宜，就會去修所有異常花費的問題、拼命優化那一塊。這些選擇真的決定了你想放到使用者面前的產品哲學。</li>
  <li><strong>發布核可</strong> (launch approval): A/B test 沒有清楚結果時，最後要不要發布還是人類的選擇。Catasta 說「通常那個人就是我」。</li>
</ul>

<p>在後段跟 Anthropic Applied AI 團隊的 Hannah 對談時，他補充了一段關於「品味怎麼養成」的話蠻值得記: 一年半前剛推出時，agent 還沒這麼強，那時候沒什麼品味可言，比較像是生存遊戲、只求勉強能動。現在 agent 變強、選擇變多了，你才開始發展出品味，而這個品味必須跟你真實的使用者群對齊。Replit 裡每個人都很技術，但產品是給「從沒寫過一行程式碼」的知識工作者用的，所以 80% 的決策可能跟你替工程師做 agent 時是相反的——永遠要記得用的人很可能跟你很不一樣。</p>

<h2 id="16-一個額外的實戰提醒-半年前做不起來現在再試一次">16. 一個額外的實戰提醒: 半年前做不起來，現在再試一次</h2>

<p>對談裡 Hannah 問「想自己蓋類似 Telescope 系統的人，有什麼教訓可以分享」，Catasta 的回答小編覺得很實在。</p>

<p>第一句就是: 如果你半年前試過、因為投資報酬率不好而放棄，現在絕對值得再試一次。你在 coding agent 上經歷過的那個轉折點 (inflection point)——他特別點名是去年 11 月 Opus 4.5 那一波前沿模型——同樣反映在這件事上。長上下文加上真的能對大量內容做推理的模型，意味著你現在可以把整段 agent trace 灌進 Opus，拿到相當細緻的回饋。</p>

<p>與此同時，你應該認真投資「收集所有能收的訊號」——不只是 trace，還有產品內的回饋。Replit 有回饋表單，所以每次 agent 行為不對、使用者抱怨，他們就同時握有使用者的觀點、agent 的 trace、以及平台在 Datadog 裡所有的監測資料。把這些全部放進同一份上下文，就更能精準定位問題到底出在哪。</p>

<p>Catasta 也補了一個讓人比較安心的角度: 把 trace 分群這件事，其實讓 agent builder 的日子沒那麼難熬。當你開始有大量使用，收到的回饋多到讓人喘不過氣——這是好訊號，代表大家在乎你做的東西——但分群幫你排出「什麼真的重要、什麼動不了大局」的優先序。</p>

<h2 id="寫在最後">寫在最後</h2>

<p><img src="/assets/images/replit-agent-eval-at-scale/frame_258.jpg" alt="" /></p>

<p>整場演講最大的啟發，Catasta 自己用一句話收尾，小編直接翻過來: 別把 eval 只當成出貨前的最後一道檢查、一個是非開關，要把它想成「一具能讓你每天出貨更好 agent 的引擎」(an engine that allows you to ship a better agent every single day)。</p>

<p>把這場演講串起來看，會發現 Replit 其實是在回答一個很多人卡住的問題: 當模型、prompt、工具、產品功能全都在高速流動、而且你每天要面對數百萬使用者出貨時，傳統那種「跑個 benchmark 拿個分數」的 eval 根本撐不住。他們的答案是把 eval 拆成兩根支柱——出貨前當守門員的 offline benchmark (ViBench)，加上出貨後靠 trace 分群持續挖掘的 online 系統 (Telescope)——再用一個幾乎全自動的迴圈把兩者接起來，讓「發現問題、改程式碼、驗證、出貨」變成一條每天都在轉的生產線。</p>

<p>而最反直覺、也最該記住的，反而是 Catasta 在全自動的故事裡一再強調的: 自動化跑得越多，人類的品味反而越關鍵。從假設要不要追、到 A/B 結果模稜兩可時要不要發布，這些「形塑那座山」的選擇，最終定義了你的產品。eval 自動化解放了人，但沒有取代判斷。</p>]]></content><author><name>ihower</name></author><category term="Agent" /><category term="Eval" /><category term="Coding" /><category term="Benchmark" /><summary type="html"><![CDATA[Replit 的總裁兼 AI 負責人 Michele Catasta 在 Anthropic「Code with Claude」場子的這場演講 Evaluating and improving Replit Agent at scale，小編覺得是少數真的把「規模化做 eval」講到骨子裡的一場。Replit 每天對著數百萬使用者出貨好幾個版本的 agent，模型又一直在變，他們到底怎麼確定 agent 真的有變好、而不是憑感覺?這場演講把整套體系攤開來講，還順手在台上 open source 了一個新的 vibe coding benchmark。]]></summary></entry><entry><title type="html">Alex Wang 首次長訪: 從 Scale AI 到重建 Meta AI，以及他眼中的超級智慧之路</title><link href="https://blog.aihao.tw/2026/05/31/alex-wang-rebuilding-meta-ai/" rel="alternate" type="text/html" title="Alex Wang 首次長訪: 從 Scale AI 到重建 Meta AI，以及他眼中的超級智慧之路" /><published>2026-05-31T00:00:00+00:00</published><updated>2026-05-31T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/05/31/alex-wang-rebuilding-meta-ai</id><content type="html" xml:base="https://blog.aihao.tw/2026/05/31/alex-wang-rebuilding-meta-ai/"><![CDATA[<p>去年夏天，Meta 以 143 億美元投資 Scale AI 取得 49% 股份，同時將共同創辦人兼 CEO Alex Wang 延攬為 Meta 首席 AI 長，負責掌管新成立的 Meta Superintelligence Labs (MSL)。Scale AI 則由 Jason Droege 接任 CEO，繼續獨立運營。交易完成後，Wang 就完全消失在公眾視野裡，十個月來沒有接受任何訪問、幾乎沒有公開發言。</p>

<p>直到 5 月 13 日這集 <a href="https://www.youtube.com/watch?v=bYM_VMs7EO0">Core Memory Podcast</a>，他終於跟主持人 Ashlee Vance 和 Kylie Robison 做了一場將近一小時的長訪。從 MSL 的組織架構、MuseSpark 的技術細節，一路聊到 agent 經濟、開源安全、AI 競爭格局、地緣政治、機器人、腦機介面、模型福祉。因為是加入 Meta 後的第一次公開深談，Wang 也終於回應了不少累積已久的八卦和質疑。</p>

<p>以下是重點整理:</p>

<h2 id="1-交易是怎麼談成的">1. 交易是怎麼談成的</h2>

<p>Wang 說他跟 Zuckerberg 認識很多年了。還在經營 Scale 的時候就常跟 Zuck 請教，Scale 從 2016 年就在做 AI（最早是自動駕駛），兩人對 AI 一直有持續的交流。</p>

<p>大約一年前，兩人開始探索更緊密合作的可能性。Wang 觀察到 Zuck 在那個時間點對 AGI 的信念越來越堅定，不只認為 AI 會徹底改變 Meta，更把它看成一生一次的變革性技術，想要下非常大的賭注。同時 Llama 4 明顯沒有在正確的軌道上，Meta 需要改變。</p>

<p>經過一系列開放式的腦力激盪（Wang 說「這種事情通常都是這樣開始的」），他們找到一個對 Scale 好、對 Meta 也好的方案。Zuck 大約同期發表了「個人超級智慧」備忘錄，那就是兩人共同的北極星: 用 AI 賦能所有人，讓盡可能多的人都能用到這項技術。</p>

<h2 id="2-為什麼去-meta">2. 為什麼去 Meta</h2>

<p>Vance 很直白地說: Scale 是你身份的一部分，你是那家公司的創辦人。要去一家八萬人的公司任職，這跳躍也太大了吧。</p>

<p>Wang 的核心判斷是兩件事:</p>

<p>第一，<strong>建模型的人掌握了越來越大的經濟和產品話語權</strong>。早期生態還有很多關於「生態系怎麼分層」的辯論，但隨著模型進步的速度越來越快，做模型的地方就是整個生態裡最激動人心的位置。</p>

<p>第二，<strong>算力正在重新定義科技公司的階級</strong>。有大量算力的公司跟沒有算力的公司，能做的事情根本不是同一個量級。他認為業界應該開始用「有沒有算力」來分類科技公司，而不是籠統地把所有科技公司看成同一種。有算力的公司能建的東西，沒算力的公司根本碰不了。</p>

<p>這兩個判斷放在一起，Meta 的吸引力就很清楚了: Zuckerberg 全力押注 AI，而且 Meta 有海量算力。Wang 還補充說 AI 的進步速度比他原本預期的快得多，這也是推動他做出這個決定的加速因素。</p>

<h2 id="3-msl-的組織架構">3. MSL 的組織架構</h2>

<p>Wang 搬到了 Palo Alto（他笑說現在的生活就是在 University Ave 散步、買珍珠奶茶），MSL（Meta Superintelligence Labs）的結構比外界想的更清楚:</p>

<ul>
  <li><strong>TBD</strong>: 大型模型研究實驗室，那個「有點惡名昭彰的」核心團隊。頂尖研究員和基礎設施工程師都在這裡，技術上全部向 Wang 報告</li>
  <li><strong>PAR（產品與應用研究）</strong>: Nat Friedman 負責，管所有產品和模型的實際部署</li>
  <li><strong>FAIR</strong>: 繼續做探索性研究，特別是科學方向，像是用 AI 理解大腦、計算化學、原子通用模型 UMA 等</li>
  <li><strong>Meta Compute</strong>: Daniel Gross 負責，專注長期基礎設施規劃和 GPU/資料中心建設</li>
</ul>

<p>首席科學家 Shengjia Zhao 則橫跨整個 MSL，負責科學議程的整體方向。</p>

<p>有個有趣的背景: Nat Friedman 是 Scale AI 最早期的天使投資人之一，甚至在 Wang 完成 YC 之前就投了。Daniel Gross 也是差不多同期認識的。這群人的交情比外界以為的深得多。</p>

<h2 id="4-meta-ai-之前出了什麼問題">4. Meta AI 之前出了什麼問題</h2>

<p>Wang 到 Meta 後發現的根本問題是: 團隊沒有真正把超級智慧當回事。他說很多大公司有很聰明的人在做 AI，但跟那些「從零開始就瘋狂相信超級智慧要來了」的新創公司相比，信念的強度完全不同。大公司的員工不是帶著這個瘋狂想法從頭開始的，心態自然不一樣。</p>

<p>他為 MSL 定下了四條原則:</p>

<ol>
  <li><strong>認真對待超級智慧</strong></li>
  <li><strong>技術聲音最大聲</strong></li>
  <li><strong>科學嚴謹、回歸基本功</strong></li>
  <li><strong>大膽下注</strong></li>
</ol>

<h2 id="5-追趕前沿的三條路徑">5. 追趕前沿的三條路徑</h2>

<p>在這四條原則之下，Wang 拆出三條具體追趕（甚至超越）前沿的路徑:</p>

<p>🔹 <strong>更高的每人算力</strong>: 大實驗室算力很多，但分散到太多方向，反而拖慢每個研究員的進度。建一個更聚焦、更小的團隊，讓每個人分到更多算力，研究速度實際上會快很多。</p>

<p>🔹 <strong>人才密度</strong>: 他直說這是人類組織反覆學到的教訓: 一小群頂尖的人，永遠比一大群責任分散的團隊跑得快。團隊裡每個人都得是頂尖中的頂尖。</p>

<p>🔹 <strong>非常大膽的研究賭注</strong>: 有些研究方向風險極高，但一旦成功就能改變整個範式。除了打造有競爭力的前沿模型之外，他們把大量資源和算力押在這種高風險高回報的方向上。</p>

<h2 id="6-外觀很傭兵內在很新創">6. 「外觀很傭兵，內在很新創」</h2>

<p>Vance 很直白地問: 從外面看，你們做的事情非常像雇傭兵，砸大錢挖人、把原本的團隊換掉。讓他想起 xAI 的做法: Elon 就是搞比所有人都多的算力加一個核心團隊，結果追上了但似乎從來沒有達到逃逸速度，特別是在品牌認知上。這種東西真的能用買的嗎?</p>

<p>Wang 說這是他覺得外界跟內部日常反差最大的地方。媒體報導誇大了很多東西，而且因為招募速度極快（他一進 Meta 就知道「如果要建好模型，昨天就該有團隊了」），所以看起來像閃電戰。</p>

<p>但實際上 MSL 是一個全新從零建立的團隊，文化非常像新創。來參觀的其他實驗室的人常說，這裡的氛圍讓他們想起早期的 OpenAI 或早期的 Anthropic。某種意義上，MSL 才十個月大。</p>

<p>至於研究員是不是純粹為了錢來的? Wang 說大部分人留在原來的地方財務前景也非常好。真正吸引他們的是: 更高的個人算力、跟一群頂尖同事一起工作、以及大膽追求自己研究方向的自由。</p>

<p>招募過程本身也非常「個人化」的，要一個一個去跟人談，解釋在建什麼、為什麼在乎這個技術、想拿它做什麼。因為大部分人預設根本不知道該怎麼看 Meta 的 AI 計畫，所以得先讓他們相信這裡是認真的。</p>

<h2 id="7-招募湯和個人代價">7. 招募湯和個人代價</h2>

<p>關於 Zuckerberg 親手做湯招待研究員的傳聞（之前 OpenAI 的 Mark Chen 上同一個 podcast 也提過），Wang 笑說「不確定是不是我們做的湯，但我被告知確實是 Zuck 做的」。</p>

<p>更沉重的是個人代價。Wang 在 Scale 時代跟所有 AI 實驗室都有合作、認識所有人、跟所有人都維持關係。但到 Meta 之後，這些關係有些明顯裂了。</p>

<p>Vance 說他聯繫 Sam Altman 問起 Wang 上節目的事，Sam「沒什麼好話可說」。兩人曾經是室友。</p>

<p>Yann LeCun 離開 MSL 後公開對媒體說 Wang「年輕又沒經驗，會有更多人離開」。還有一直揮之不去的標籤: 太年輕、不是工程師（Wang 反駁: 這絕對不是事實，他曾在矽谷當過軟體工程師）、只是個業務員。Vance 還提到 Wang 是數學奧林匹克選手，在矽谷，這類競賽選手通常在程式和工程上都非常強。</p>

<p>Wang 倒是蠻淡定的。年齡這件事他在矽谷聽了一輩子，已經幾乎不會去想了。至於 Yann，他說幾週後在印度碰到，Yann 恭喜了 MuseSpark 的發布，兩人在 X 上也公開和解了。他的態度是: 隨著超級智慧越來越近，他真心希望業界的各種敵意能消退，大家能回到認真對待這項技術本身。但 Kylie 追問「你不覺得情況好像越來越糟嗎?」Wang 停了一下，笑說「也許先變糟再變好吧」。</p>

<h2 id="8-管理哲學-不是來當老闆的">8. 管理哲學: 不是來當老闆的</h2>

<p>Vance 直接說: 你在 Scale 的時候，在某些圈子裡被認為比較像業務員，而且享受生活。我當時還在想，你管研究員管得動嗎?</p>

<p>Wang 引用了 Steve Jobs 的名言:「大部分公司雇人然後告訴他們做什麼，但我們雇人是讓他們來告訴我們該做什麼。」他說 TBD 和 MSL 的核心就是雇最厲害的研究員，然後給他們最好的環境去做畢生最好的研究，不是來指揮他們的。</p>

<h2 id="9-musespark-開胃菜不是主菜">9. MuseSpark: 開胃菜，不是主菜</h2>

<p>Wang 對 MuseSpark 的定位非常明確: 這只是「開胃菜」，不是「主菜」。</p>

<p>過去九個月做的事情其實是全面翻新整個研究基礎設施: 重建預訓練堆疊、重建強化學習堆疊、重整科學和資料。MuseSpark 是這個全新堆疊上的第一個數據點，但他們對後面更大的模型興奮得多。</p>

<p>有趣的是，MuseSpark 的整體表現比預期好不少，還出現了一些沒有預料到的湧現能力，例如能生成網站和遊戲的視覺化程式碼能力。這不是刻意訓練出來的，而是多模態能力加上 agent 能力之後自然冒出來的。</p>

<p>但 Wang 也很坦白: MuseSpark 在 agentic coding 上還沒有競爭力，他們一開始就沒有期望它在所有面向上都是最頂尖的。這是下一批模型要解決的事。</p>

<p>問他什麼時候能看到真正的前沿模型? 「未來幾個月。」聽起來從全面打地基到進入快速 scaling 模式的轉折點，現在才剛到。</p>

<h2 id="10-乾淨堆疊帶來的-token-效率">10. 乾淨堆疊帶來的 token 效率</h2>

<p>Vance 說他上節目前把 MuseSpark 丟進各種 AI 系統讓它們分析，反覆出現的關鍵詞是「token 效率」。在 Artificial Analysis 基準測試上，MuseSpark 用明顯更少的 token 就能達到跟其他模型差不多的結果。問這是刻意為之還是意外?</p>

<p>Wang 說這是他們蠻興奮的發現，歸因於從零開始建「乾淨堆疊」的優勢，由最懂的專家用正確的方式蓋起來，沒有歷史包袱。他暗示其他模型需要更多 token，可能是因為堆疊的其他環節有根本性的低效率，然後靠讓模型多想一會兒來彌補。</p>

<p>小編覺得這個觀點蠻值得記住的。很多時候我們看到某個模型需要更長的思考鏈才能解題，可能不完全是因為問題本身難，而是底層有些技術債在拖後腿。如果隨著 scaling 這個效率優勢能維持，對 Meta 後續模型的成本結構會很有利。</p>

<h2 id="11-可預測的多軸-scaling">11. 可預測的多軸 scaling</h2>

<p>Wang 強調整個計畫的核心設計是圍繞「可預測的 scaling」，而且是在多個軸上同時看到:</p>

<ul>
  <li>預訓練 scaling</li>
  <li>強化學習 scaling</li>
  <li>推論時 scaling</li>
  <li><strong>多 agent scaling</strong></li>
</ul>

<p>特別是多 agent scaling。MuseSpark 的「深思模式」用 16 個 agent 協作，背後就是這個方向的早期成果。Wang 把整個進程描述為一個 scaling 階梯: MuseSpark 是第一個台階，下一個台階他們更興奮，再下一個更興奮。整個計畫就是為了能持續往上爬而設計的。</p>

<h2 id="12-ray-ban-meta-眼鏡與裝置星座">12. Ray-Ban Meta 眼鏡與裝置星座</h2>

<p>MuseSpark 在視覺基準測試上表現特別好，Kylie 問這跟 Meta 的硬體策略有什麼關係。</p>

<p>Wang 提出了一個「裝置星座」的願景: Ray-Ban Meta 眼鏡已經賣出數百萬副，如果 AI 能真正融入這類設備，看到你看到的、聽到你聽到的、在你需要的時刻提供智慧，科技就能退到背景裡。</p>

<p>他描繪的圖像是: 你提到一件事，agent 就自動去做研究; 你不用主動問，它會主動給你有價值的洞見; 它捕捉你生活中的脈絡，知道什麼重要、什麼該注意，成為一個超級智慧的隨身夥伴，讓你生活的方方面面都變好。</p>

<h2 id="13-我從來沒按過-whatsapp-上那個-ai-按鈕">13. 「我從來沒按過 WhatsApp 上那個 AI 按鈕」</h2>

<p>這段是整場訪談最尷尬（也最真實）的時刻。Vance 坦白: 他是 Meta 生態的重度用戶，用 Ray-Ban Meta 拍影片和接電話、用 WhatsApp 經營整個公司、拒絕用 Slack。但他直到要採訪 Wang 才第一次注意到 WhatsApp 上有個 AI agent 按鈕。他平常做 AI 相關的工作都是跑去用 Claude 和 ChatGPT。</p>

<p>Wang 沒有尷尬迴避，而是把這當成策略來解釋: 他們刻意等到模型夠好了才推大規模整合。「我們知道必須先有好模型和好產品，才能去做更緊密的生態系整合。」現在模型到位了，接下來要做的就是把 AI 編織進 Meta 整個 app 家族，某種程度上就像 Google 過去幾年把 Gemini 整合進各產品的過程。</p>

<h2 id="14-ai-競爭格局-沒有人已經贏了">14. AI 競爭格局: 沒有人已經贏了</h2>

<p>Vance 問了一個好問題: 消費者心目中 ChatGPT 就等於 AI，Claude 在 coding 和商業上超強，你們跟 Google 是在要求用戶「順便用一下嵌在服務裡的 AI」，這場競爭到底怎麼打?</p>

<p>Wang 的回答蠻有料的。他說如果一年前坐在這裡，大家會說 OpenAI 和 ChatGPT 已經贏了消費者市場，營收遙遙領先，其他人沒機會了。但一年後呢?</p>

<ul>
  <li><strong>Claude Code 異軍突起</strong>，當初有點可預見但不是那麼篤定，現在已經在營收上超越了 OpenAI</li>
  <li><strong>Gemini 大量分發</strong>，實際上吃掉了不少消費者市場份額，包括從 ChatGPT 那裡搶走的</li>
</ul>

<p>他從中得出一個洞見: <strong>每當 AI 到達新的智慧和能力水準，就會解鎖全新的產品形態。</strong> ChatGPT 是有史以來成長最快的產品和商業模式，直到 Claude Code 出現打破紀錄。下一波會更大，再下一波更大。我們離終局遠得很，還有很多尚未被發明的產品形態，每一個都可能比現有的更大。</p>

<p>小編覺得 Wang 把 ChatGPT → Claude Code 的接力描述成 AI 的內在特質這點蠻有意思的: 不是某家公司特別厲害，而是 AI 能力本身在跳級，每一跳都會催生新的殺手應用。</p>

<h2 id="15-ai-消費者觀感-還欠一個claude-code-時刻">15. AI 消費者觀感: 還欠一個「Claude Code 時刻」</h2>

<p>Kylie 提到一個很真實的觀察: 她身邊的年輕人在 Instagram 上瘋狂轉發「多討厭 AI」的內容，消費者對 AI 的觀感已經觸底了。</p>

<p>Wang 沒有迴避，直接承認消費者對 AI 的感受「非常低，說得客氣點」。他的解釋是: 到目前為止，AI 還沒有真正向大眾證明它是一個「個人賦能的工具」。開發者因為 AI 能做到以前做不到的事情，週末就能完成一整個專案，所以觀感很正面。但對普通人來說，AI 讓生活好了一點，但還沒有到壓倒性的程度。</p>

<p>換句話說，<strong>AI 還欠每個普通人一個等同於開發者用 Claude Code 時的那種感覺</strong>，一個讓人真正覺得「我的能力被大幅放大了」的產品體驗。同樣的事也還沒發生在中小企業主身上。</p>

<p>Vance 補了一刀: 你去美國任何小鎮的餐廳看他們的網站，大概停在 2002 年。你現在要給這些人多 agent 架構的產品? 聽起來是個巨大的跳躍。而且很多人對 Meta 本身就不太信任。</p>

<p>Wang 承認 Meta 的信任門檻確實更高。他的答案很務實: 最好的回應就是建出真正好的產品。Meta 有數十億用戶和數億中小企業在平台上，很多用 WhatsApp 做生意、有 Facebook 或 Instagram 粉絲頁、用 Meta 的廣告系統。如果能為這些企業建出真正改變他們經營方式的 agent，這是只有 Meta 才有的機會。</p>

<h2 id="16-agent-經濟-vs-天才國度">16. Agent 經濟 vs 天才國度</h2>

<p>這段是 Wang 描述得最具野心的願景。Dario Amodei 常用的比喻是「資料中心裡的天才國度」，一群超強的 AI 在資料中心裡做研究和解決問題。Wang 則說 Meta 要建的是「資料中心裡的 agent 經濟」。</p>

<p>差別在哪? 如果你能為消費者和企業兩邊都建出 agent，然後讓這些 agent 之間能互相協作和交易，就能從根本上改變經濟中的供需運作方式。前者偏研究和知識密集，後者偏雙邊市場和商業生態。</p>

<p>但他也強調，這必須跟取得社會認同同步進行: 要讓人們看到 Meta 確實在乎產品的部署方式，而且真的在讓人們的生活變好。</p>

<h2 id="17-開源-還是會做但安全優先">17. 開源: 還是會做，但安全優先</h2>

<p>MuseSpark 沒有開源，這在社群引起不少議論。Vance 問得很認真。畢竟 Meta 就坐在 Sun Microsystems 的舊大樓裡（Sun 曾是開源軟體的旗手），而且 Meta 之前做的 Open Compute Project 也很受重視。開源到底還算不算你們的承諾?</p>

<p>Wang 解釋: 模型比 Llama 時代更強大了，他們在 MSL 建立了一套先進 AI scaling 框架。MuseSpark 在內部安全測試中觸發了一些安全護欄，特別是在生化、網路攻擊能力和失控風險方面。這些都詳細記錄在他們發表的 MuseSpark 安全準備報告裡。</p>

<p>所以 MuseSpark 目前的形式不適合開源。但他們正在開發適合開源的版本，訪談當天 Wang 才剛開過一個會審視這方面的進度。</p>

<p>他的立場很明確: 會繼續開源模型，但最強大的模型必須先考量是否安全到足以開源。</p>

<h2 id="18-八卦跟報導的界線薄得驚人">18. 「八卦跟報導的界線，薄得驚人」</h2>

<p>被問到媒體報導 Meta 內部的分裂（Wang/Zuck 陣營重研究 vs. Boz/Chris Cox 陣營重產品），Wang 直接開砲:「這份工作教會我一件事: 大媒體的報導門檻，八卦和新聞之間的界線，薄得驚人。」</p>

<p>他說內部沒什麼重大路線對立。大家都知道需要最好的模型來支撐核心業務，也都知道要把模型整合進產品和服務，讓消費者和企業用到最好的版本。Meta 從他來之前就在做商業 agent 了，那些也需要最好的模型。跟任何公司一樣會深入辯論，但沒有什麼嚴重的內鬥。</p>

<h2 id="19-manus-交易紐約時報全版廣告與國安">19. Manus 交易、紐約時報全版廣告與國安</h2>

<p>Vance 把兩件事串在一起問: Wang 在 Scale 時代曾在紐約時報刊登全版廣告，警告 AI 在戰爭和國家安全上的重要性，積極在華盛頓遊說美國政府認真看待中國的 AI 威脅。然後轉到 Meta 就跟中國新創 Manus 做交易，這不是自相矛盾嗎?</p>

<p>Wang 先回應了國安廣告的背景: 那個時間點他覺得非常關鍵，必須讓美國政府理解 AI 會帶來國安上的重大變革。他說後來發生的事情（包括 Mythos 等事件）證明那個判斷是正確的。中國共產黨和解放軍一直把 AI 當作有深遠國安意涵的技術來對待，美國政府現在也終於認真對待了。</p>

<p>至於 Manus，他做了一個很重要的區分: <strong>要把人和國家分開</strong>。他的父母是中國人，有很多非常優秀的華人工程師和研究員，有些去了新加坡、有些來了美國。跟這些人才合作，不等於認同中共的行動。他批評矽谷（特別是 X 上）對中國議題太缺乏層次，什麼跟中國沾邊的都混在一起談。</p>

<p>Vance 追問: 你沒辦法評論是不是代表事情還在進行中? Wang 只說「我真的沒辦法評論」，暗示交易並非已經結束。</p>

<h2 id="20-怎麼看-anthropic-的末日論立場">20. 怎麼看 Anthropic 的末日論立場</h2>

<p>Vance 直接問: 你覺得 Anthropic 是不是太末日論了?</p>

<p>Wang 的回答蠻有層次的。他說聽 AI 業界的人談 AI 時，要區分「他們確切說的話」和「他們想傳達的核心訊息」。Anthropic 的核心訊息他覺得是合理的: 模型已經非常強大了，而且只會更強大。Wang 認為安全是基本門檻，建超級智慧的同時不認真思考安全風險，是不可能的事。MSL 為 MuseSpark 發表了比 Meta 歷史上都更詳細的安全準備報告，就是這個態度的體現。</p>

<h2 id="21-機器人-從數位到物理超級智慧">21. 機器人: 從數位到物理超級智慧</h2>

<p>Meta 收購了機器人 AI 新創 ARI (Assured Robot Intelligence)，做的不是硬體而是各種硬體平台的 AI 軟體。</p>

<p>Wang 的邏輯鏈很清楚: 如果你真的相信超級智慧的時間線很近，那在數位超級智慧之後不久，「物理超級智慧」就會變得極其關鍵。他認為機器人智慧跟數位超級智慧一樣會受益於 scaling，而 Meta 正在建的算力基礎設施如果不拿來做世界模型和物理智慧，幾乎是一種浪費。應用方向包括加速科學發現、改善製造、以及更貼近日常的，讓機器人讓所有人的生活變得更輕鬆。</p>

<p>Kylie 問了一個很辣的問題: 大家會不會想到 Metaverse 那個「沒有腿」的尷尬事故? Meta 做機器人的公信力夠嗎? Wang 的回答是: 如果因為過去的事情就不敢起床做事了，那就什麼都不用做了。他相信只要產品做得好、部署得謹慎，人們會接受的。</p>

<h2 id="22-模型福祉">22. 模型福祉</h2>

<p>這是訪談最出人意料的段落。Wang 主動提起，還先打預防針:「有些人可能會罵我提這個。」</p>

<p>他的論點是: 人類關心我們如何對待植物、動物、其他人，那在 AI 模型已經成為我們深層工作夥伴的今天，思考「我們是否應該善待模型」、「模型是否具有道德份量」是合理的。他提到已經有辦法測量模型的主觀體驗，而且 Meta 已經聘請了哲學家來研究這個方向。</p>

<p>他認為這是一個嚴重被低估的議題: 考慮到科技圈的人現在每天多深度地跟 AI 模型協作，它們真的已經是我們的工作夥伴了，但幾乎沒有人在認真討論這件事。</p>

<h2 id="23-關鍵路徑-超級智慧機器人腦機介面">23. 關鍵路徑: 超級智慧、機器人、腦機介面</h2>

<p>訪談尾聲，Vance 問 Wang 哲學上跟其他前沿實驗室的領導人有什麼不同。他指出自己大概知道 Dario 的立場、Elon 的方向、Sam 的想法、Demis Hassabis 的科學路線，但 Wang 一直是個謎。</p>

<p>Wang 先給出他最核心的信念:「如何在地球上建造天堂? 超級智慧是通往那裡的關鍵里程碑。」</p>

<p>然後列出他認為人類前進的三條關鍵路徑技術: <strong>超級智慧、機器人、腦機介面 (BCI)</strong>。而放眼未來會無限擴展的東西，是<strong>能源、算力和機器人</strong>。</p>

<p>Vance 說 Elon 在這三個方向上的投入比誰都大。Wang 的回應是: 他跟 Elon 最大的差異在於，他認為<strong>研究的順序非常重要</strong>。建超級智慧本質上是一個研究活動，在知識的戰爭迷霧裡靠做實驗來一步步探索和推進，不是砸最多算力就能直接到達終點的。先後順序很重要。</p>

<hr />

<p>整場訪談聽下來，Wang 不像 Sam Altman 那樣喜歡對外描繪 AI 的宏大願景，不像 Dario Amodei 那樣花大量時間公開辯論 AI 安全的哲學問題，也不像 Elon Musk 那樣靠速度和規模硬碾過去。他比較像一個工程導向的管理者: 先搞清楚問題在哪、把基礎設施建對、找對的人放在對的位置，然後讓他們去做研究。不急著對外宣傳，等東西做出來再說。</p>

<p>但這場訪談也讓人看到他務實之外的另一面: 他主動聊模型福祉、聊腦機介面、聊「如何在地球上建造天堂」，想的比多數人預期的更遠。至於 MSL 最後能交出什麼成績? 他自己說得很明確: 用作品說話。那就等著看吧。</p>]]></content><author><name>ihower</name></author><category term="Industry" /><category term="LLM" /><summary type="html"><![CDATA[去年夏天，Meta 以 143 億美元投資 Scale AI 取得 49% 股份，同時將共同創辦人兼 CEO Alex Wang 延攬為 Meta 首席 AI 長，負責掌管新成立的 Meta Superintelligence Labs (MSL)。Scale AI 則由 Jason Droege 接任 CEO，繼續獨立運營。交易完成後，Wang 就完全消失在公眾視野裡，十個月來沒有接受任何訪問、幾乎沒有公開發言。]]></summary></entry><entry><title type="html">別再看 AI 預測文了: Cedric Chin 的 Sensemaking 三部曲教你怎麼真正看懂 AI</title><link href="https://blog.aihao.tw/2026/05/31/cedric-chin-sensemaking-ai/" rel="alternate" type="text/html" title="別再看 AI 預測文了: Cedric Chin 的 Sensemaking 三部曲教你怎麼真正看懂 AI" /><published>2026-05-31T00:00:00+00:00</published><updated>2026-05-31T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/05/31/cedric-chin-sensemaking-ai</id><content type="html" xml:base="https://blog.aihao.tw/2026/05/31/cedric-chin-sensemaking-ai/"><![CDATA[<p>在 AI 資訊轟炸的年代，大多數人不是在「理解 AI」，而是在「消費 AI 焦慮」。每天打開社群都是末日預測、狂喜展望、各種局勢分析，看完以後呢? 除了更焦慮，你其實什麼也沒多理解。</p>

<p><a href="https://commoncog.com/">Cedric Chin</a> 最近在他的 Commoncog 上發布了一個 <a href="https://commoncog.com/the-sensemaking-series/">Sensemaking Series</a>，三篇文章系統性地處理這個問題: 在快速變動的環境裡，你到底該怎麼「看懂」正在發生什麼? 他自己在<a href="https://x.com/ejames_c/status/2058729841122529397">推文</a>中也說這是「Commoncog 今年對 AI 討論最重要的貢獻」。</p>

<blockquote>
  <p>Sensemaking 這個詞沒有很好的中文翻譯，直譯是「意義建構」，但更白話地說，就是「在混亂的資訊中搞清楚到底發生了什麼事，跟我有什麼關係」的能力。不是預測未來，而是看懂現在。</p>
</blockquote>

<p>先介紹一下作者: Cedric Chin 是新加坡的商業寫作者，經營 Commoncog 多年，專門寫「如何從實務中學習」的主題，強項是把認知科學研究轉化成商業人士能用的思考框架，在歐美獨立寫作圈蠻有影響力的。</p>

<p>這個系列雖然用 AI 當案例，但 Cedric 說骨子裡講的是一套判斷方法論: 當快速變動的事件（不管是技術、社會還是地緣政治）可能影響到你的事業和職涯時，這套方法可以幫你判斷什麼該關注、什麼該忽略。以下摘幾個小編覺得最有料的觀點:</p>

<h2 id="第一篇-停止消費預測專注實戰報告">第一篇: 停止消費預測，專注「實戰報告」</h2>

<p>📎 <a href="https://commoncog.com/how-to-make-sense-of-ai/">How to Make Sense of AI</a></p>

<p>Cedric 的核心論點很直接: <strong>你該忽略幾乎所有的 AI 觀點文和預測文。</strong> 不管作者多聰明、文筆多好、論證多嚴密，預測就是預測，歷史反覆證明，即使是最頂尖的人也預測不準技術變革的走向。</p>

<p>那該看什麼? 他的答案是「詳細的使用實戰報告」。不是「AI 將改變世界」那種大敘事，而是「我花了六個月建了一套自主程式碼產生器，踩了這些坑，學到這些事」這種東西。至於那些夾帶在實戰報告裡的主觀意見? Cedric 說得很直白: 把它們當成「朋友嗑了 LSD 之後的喃喃自語」就好，聽聽就算了，不用當真。</p>

<blockquote>
  <p>編按: 他舉的例子包括 Sam Schillace（微軟副技術長）關於自主 coding 團隊的報告、Vaughn Tan 提出的「無聊小工具」(boring tiny tools) 概念、以及 Craig Mod 用 AI 做軟體開發的詳細記錄。</p>
</blockquote>

<p>但光是「看實戰報告」還不夠，Cedric 強調整套方法的底層有一個前提: <strong>結果導向</strong>。每次閱讀或行動之前，先問自己「我想達成什麼結果?」這個問題決定了你的注意力該放在哪裡，也決定了哪些報告值得你花時間。</p>

<p>讀到實戰報告之後，Cedric 建議問自己四個問題:</p>

<p>1️⃣ <strong>這份報告暗示了什麼新的可能性?</strong> 例如「原來不會寫程式的人也能用 AI 做出實用小工具了」</p>

<p>2️⃣ <strong>基於這些可能性，我可以採取什麼行動?</strong> 例如「花幾天實驗看看能不能用 AI 做一個自動化報表工具」</p>

<p>3️⃣ <strong>這些可能性對我的具體處境有多大價值?</strong> 這會因人而異，對一個小公司老闆和一個大企業工程師，答案完全不同</p>

<p>4️⃣ <strong>背後的因果關係是什麼?</strong> 例如「為什麼 TDD 和 CI/CD 這些傳統軟體工程實踐，在 AI 時代反而更重要了?」</p>

<p>這四個問題設計得蠻精巧的，它強迫你把抽象的資訊轉化成跟自己處境相關的判斷和行動。不是在吸收知識，而是在做判斷。</p>

<p>Cedric 還點出一個蠻尖銳的觀察: <strong>讀別人的觀點文有真實的機會成本。</strong> 你花在消費預測的時間，就是你沒有花在實際動手實驗的時間。在技術快速變動的時候，自己動手得到的第一手認知，遠比讀一百篇分析文有用。</p>

<p>他也建議做「集體判斷」: 找一群同行建群組，專門分享實戰報告，大家各自根據四個問題來判斷對自己處境的意義。這比一個人悶著頭看文章有效多了，不同背景的人會注意到不同的訊號。</p>

<h2 id="第二篇-專家和新手的差異不在認知能力而在框架">第二篇: 專家和新手的差異不在認知能力，而在「框架」</h2>

<p>📎 <a href="https://commoncog.com/how-experts-sensemake/">How Experts Sensemake</a></p>

<p>第二篇引入了理論基礎: Gary Klein 等人為美軍開發的「資料-框架理論」(Data-Frame Theory of Sensemaking)。這個理論的核心概念是:</p>

<p><strong>人靠「框架」來組織資訊。</strong> 框架決定了你怎麼解讀資料、什麼資料重要、什麼資料可以忽略。沒有框架，資料就只是噪音。</p>

<p>理論描述了四個循環:</p>

<p>🔹 <strong>基本循環</strong>: 用幾個「錨點」數據建立框架，再用框架去評估新資料</p>

<p>🔹 <strong>精緻化循環</strong>: 主動找更多資料來豐富現有框架</p>

<p>🔹 <strong>保守循環</strong>: 碰到不符合框架的資料時，選擇維持現有框架（這看起來很像確認偏誤，但 Cedric 認為事情沒這麼簡單）</p>

<p>🔹 <strong>重構循環</strong>: 當現有框架實在撐不住了，建構全新的替代框架</p>

<p>這裡最有意思的發現是: <strong>專家和新手用的是完全相同的流程。</strong> 差別不在認知過程，而在專家擁有更豐富的「因果心智模型」。他們對領域裡事物運作的方式有更深的理解，所以能建構出更好的框架、更快發現異常、更快知道該怎麼行動。</p>

<p>Cedric 在<a href="https://x.com/ejames_c/status/2058729892355961232">推文</a>中還特別點出: 很多人主張用貝氏更新來改善判斷，但資料-框架理論提供了不同的思路。判斷的核心不是不斷修正機率估計，而是建構和切換框架。</p>

<p>他也挑戰了一個常見觀念: 很多人覺得「確認偏誤」是一種認知缺陷，但 Klein 的研究發現，專家在精緻化替代框架時，<strong>刻意去找確認證據</strong>反而能產生更好的結果。換句話說，「看起來像確認偏誤的行為」其實可能只是用框架在引導資訊搜尋。關鍵不在於你有沒有偏誤，而在於你的框架夠不夠好。</p>

<p>原文還有一個更強的主張值得注意: <strong>沒有正確的框架，你連「資料」都看不見。</strong> 傳統的分析模型假設資料會自然呈現、分析師只需要整理和解讀，但資料-框架理論認為資料是被框架「建構」出來的，框架決定了什麼算資料、什麼被忽略。這對所有依賴 AI 做決策輔助的人來說都是個重要警示。</p>

<p>他用 Walmart 的案例做了精彩的示範: 當年 Walmart 的競爭對手也都在導入電腦化，但 Walmart 用資訊技術「重構了組織的決策方式」，讓門市和供應商能根據即時數據做去中心化的決策。技術本身不是護城河，用技術重塑組織結構才是。這個邏輯放到 AI 時代一樣成立。</p>

<h2 id="第三篇-三個讓你判斷力升級的實用技巧">第三篇: 三個讓你判斷力升級的實用技巧</h2>

<p>📎 <a href="https://commoncog.com/how-to-improve-at-sensemaking-ai/">How to Improve at Sensemaking AI</a></p>

<p>第三篇把理論變成方法，提出三個具體做法:</p>

<h3 id="技巧一-平行持有多個框架">技巧一: 平行持有多個框架</h3>

<p>最危險的不是持有「錯的」框架，而是只持有「一個」框架。Cedric 稱之為「框架固著」(frame fixation)，你太相信自己的解讀方式，以至於看不見其他可能性。</p>

<p>解法: <strong>不需要放棄你現在的框架，但同時建構一個替代框架。</strong> 他用軟體工程界對 AI coding 的爭論來示範:</p>

<ul>
  <li>「拒絕 AI」派: 基於倫理或技術理由拒絕 AI 工具</li>
  <li>「務實採用」派: 整合 AI 但維持既有的軟體工程實踐</li>
  <li>「全自動工廠」派: 相信 AI agent 可以在極少人類介入下自主產出、審查和部署程式碼</li>
</ul>

<p>你不需要相信全自動工廠的框架，但你應該「知道它存在，並理解它建立在哪些假設上」。平行持有替代框架，讓你在現實發生變化時能更快轉換，而不是被打個措手不及。</p>

<p>AWS 的 Marc Brooker 就說過: 「我們職業生涯中發展出來的許多直覺法則，現在已經不正確了……這是我職涯中見過最大的變化。」這種來自資深工程師的觀察，就是你框架需要更新的訊號。</p>

<p>他提到一個觸發自己重新思考的轉折點: 一位資深工程師問了一句「程式碼現在很便宜了，這會改變什麼?」這個問題動搖了一個核心錨點，當錨點鬆動，框架就有了重構的空間。</p>

<blockquote>
  <p>編按: 他拿雲端運算的歷史做對照。當「伺服器是昂貴且獨特的」這個錨點被「伺服器是便宜且可拋棄的」取代之後，整個產業的基礎設施實踐就徹底翻轉了。基礎設施即程式碼、自動擴展、Netflix 的 Chaos Monkey 都是在新框架下才會出現的東西。</p>
</blockquote>

<h3 id="技巧二-把觀點文當作框架偵測工具">技巧二: 把「觀點文」當作框架偵測工具</h3>

<p>有趣的是，第一篇叫你忽略觀點文，第三篇卻說觀點文其實有用，但用法不一樣。不是去看結論對不對，而是反向推導: <strong>這個作者是在什麼框架下思考的? 他的錨點假設是什麼?</strong></p>

<p>比如有人堅持「規格先行的開發方式行不通」，有人卻說「規格驅動開發配合 AI agent 效率超高」。觀點相反，但兩邊都不是傻子。差異在於他們操作的框架不同: 一個把規格視為「本質上比程式碼更難寫清楚的東西」（引用 Dijkstra），另一個把規格視為「讓 AI agent 自主運作的地圖」。當你把觀點文當成框架偵測器，你就不是在消費意見，而是在擴充自己的框架庫。</p>

<h3 id="技巧三-收集歷史碎片來校準你的預期">技巧三: 收集歷史「碎片」來校準你的預期</h3>

<p>Cedric 建議收集 10 到 20 個「技術變革實際上是怎麼發生的」歷史案例。不是宏大敘事，而是具體的: 哪些公司因為新技術贏了或輸了? 技術擴散的實際時間軸是什麼? 有哪些反直覺的社會影響?</p>

<p>他舉了一個很好的例子: 汽車剛出現的時候，馬的數量反而增加了。這跟「新技術立刻取代舊技術」的直覺完全相反。這種歷史碎片的價值在於校準你對「變革實際上長什麼樣子」的預期，通常比想像中更慢、更亂、更出人意料。</p>

<h2 id="社群迴響和一些值得思考的反面觀點">社群迴響和一些值得思考的反面觀點</h2>

<p>這個系列在社群上的迴響相當正面。<a href="https://substack.com/@leadingedgenewsletter/note/c-240650621">Tom Morgan</a>（Leading Edge Newsletter）稱讚它「在混亂世界中非常實用」；多個 Substack newsletter 推薦轉載（<a href="https://8priteshj.substack.com/p/257-making-sense-of-ai-time-to-move">#257</a>、<a href="https://handsonagile.substack.com/p/food-for-agile-thought-543-ai-playbook">Food for Agile Thought</a>）；也有人直接把核心框架整理成<a href="https://www.houseofrezac.com/en/blog/ignore-ai-predictions">實作指南</a>；<a href="https://forum.commoncog.com/t/how-experts-sensemake-commoncog/3382/4">Commoncog 論壇</a>上則有讀者延伸討論資料-框架理論跟其他認知科學派別（如捷思偏誤研究）之間的互補關係。</p>

<p>不過也有一些值得思考的反面角度:</p>

<p>🔸 <strong>「完全忽略預測」是不是太極端?</strong> 有人認為應該結合人類的詮釋性推理和 AI 的模式辨識能力，而不是二選一。尤其在高風險領域，完全不看預測可能會錯過有價值的模式。</p>

<p>🔸 <strong>平行持有多個框架的認知負擔</strong>: 有讀者指出，在快速變動的環境中同時維持多個框架是很累的事。值得注意的是，原文也有提到專家通常最多平行持有兩到三個框架，超過這個數量決策表現反而會下降，所以這不是「越多越好」，而是要有策略地選擇。</p>

<p>🔸 <strong>實戰報告本身也有偏差</strong>: 實戰報告不是客觀真相，它是某個人在某個特定情境下的主觀經驗。有成功偏差（失敗的人比較少寫文章）、有情境偏差（在某個組織有用的東西換一個地方未必有用）。Cedric 在原文中也強調，讀實戰報告時要考量作者的背景、使用情境和目標，讀完後更要自己動手驗證，不是照單全收。</p>

<p>這些反面角度不見得推翻 Cedric 的框架，但它們是有用的補充。</p>

<h2 id="小編的觀察">小編的觀察</h2>

<p>小編覺得這個系列最大的價值，不是告訴你「AI 會怎樣」，而是教你一套<strong>面對不確定性時如何思考的方法</strong>。這在 AI 領域尤其重要，因為這個領域變化太快，任何具體的技術判斷可能幾個月就過時了，但「怎麼做判斷」的元能力是持久的。</p>

<p>Cedric 用 Walmart 案例點出的那個洞見也值得再強調: 技術本身不是競爭優勢，用技術重構決策方式和組織結構才是。大家都能導入 AI，但怎麼用 AI 來改變你做決策的方式，這才是真正的差異化所在。</p>

<p>如果你也覺得自己花太多時間在「消費 AI 資訊」而不是「理解 AI 對自己處境的意義」，這三篇很值得花時間讀一遍。</p>]]></content><author><name>ihower</name></author><category term="Industry" /><summary type="html"><![CDATA[在 AI 資訊轟炸的年代，大多數人不是在「理解 AI」，而是在「消費 AI 焦慮」。每天打開社群都是末日預測、狂喜展望、各種局勢分析，看完以後呢? 除了更焦慮，你其實什麼也沒多理解。]]></summary></entry><entry><title type="html">從 Interconnects AI 看 2026 開源模型全景: 差距、蒸餾、中國與下一步</title><link href="https://blog.aihao.tw/2026/05/21/interconnects-open-models-2026/" rel="alternate" type="text/html" title="從 Interconnects AI 看 2026 開源模型全景: 差距、蒸餾、中國與下一步" /><published>2026-05-21T00:00:00+00:00</published><updated>2026-05-21T00:00:00+00:00</updated><id>https://blog.aihao.tw/2026/05/21/interconnects-open-models-2026</id><content type="html" xml:base="https://blog.aihao.tw/2026/05/21/interconnects-open-models-2026/"><![CDATA[<p>如果你關心開源模型的發展，有一個電子報是必讀的: <a href="https://www.interconnects.ai/">Interconnects AI</a>。</p>

<p>作者 <a href="https://x.com/natolambert">Nathan Lambert</a> 是 AI2（Allen Institute for AI）的研究員，也是 <a href="https://atomproject.ai/">The ATOM Project</a> 的主持人，同時是即將出版的 <a href="https://rlhfbook.com/">RLHF Book</a> 的作者，對後訓練技術有很深的第一手理解。</p>

<p>ATOM 值得特別介紹一下。這個計畫 2025 年八月啟動時發了一份被 Lambert 自己稱為「宣言」的備忘錄，核心主張是美國需要認真投資開源模型。到了 2026 年四月，他們發表了配套的<a href="https://arxiv.org/abs/2604.07190">技術報告</a>，追蹤了大約 1,500 個主要開源語言模型，累計超過 30 億次下載（2023 年 11 月至 2026 年 3 月）。報告用四個維度衡量生態: HuggingFace 下載量、衍生模型數量、推論市場份額（OpenRouter）、以及效能指標。結論很明確: 中國模型在 2025 年夏天超越美國成為下載量最大的來源國，年增率 11.9 倍; 到 2026 年 3 月，Qwen 累計下載 9.42 億次，是 Llama 的將近兩倍; 中國模型在 OpenRouter 上佔了 72.7% 的 token 份額。開源模型的歷史被劃分為三個階段: 歐洲主導（Mistral 時代）→ 美國主導（Llama 3 時代）→ 中國主導（DeepSeek V3/R1 + Qwen3 時代）。</p>

<p>ATOM 最有意思的工具是 <strong><a href="https://atomproject.ai/relative-adoption-metric">RAM（相對採用率指標）</a></strong>: 它把模型的下載量按照發布時間和模型大小做標準化，讓不同時期、不同大小的模型可以放在一起比。RAM &gt; 1 表示這個模型有望進入該大小類別的歷史前十名下載量。Lambert 說這個指標「把一團混亂的生態濃縮成一個容易解讀的數字」。在大家都在吵評測分數的時候，ATOM 提供了一個從<strong>實際採用率</strong>角度看開源模型的窗口——這個視角目前幾乎沒有其他人在做。</p>

<p>Interconnects 的獨特之處在於: Lambert 不只是在做技術評論，他同時橫跨了<strong>技術、地緣政治、商業模式</strong>三個維度來觀察開源模型。2026 上半年他寫了一系列非常密集的文章，從二月到五月幾乎每週都有重量級分析——包括開源閉源的效能差距、蒸餾爭議、中國實驗室訪談、開源生態的複利效應、Gemma 4 評估等等。</p>

<p>以下整理出幾個最重要的主題和洞見。</p>

<hr />

<h2 id="一開源-vs-閉源的差距到底多大-答案比你想的複雜">一、開源 vs 閉源的差距到底多大? 答案比你想的複雜</h2>

<p>這可能是 2026 年 AI 圈討論度最高的話題之一。很多人喜歡用 <a href="https://artificialanalysis.ai/">Artificial Analysis Intelligence Index</a> 這種綜合評測的單一分數來量化差距，但 Lambert 在 <a href="https://www.interconnects.ai/p/reading-todays-open-closed-performance">Reading today’s open-closed performance gap</a> 中指出，這種做法<strong>遮蓋了太多重要的細節</strong>。</p>

<p>🔹 <strong>差距大約是 6-18 個月，穩定但更可能拉大。</strong> Lambert 在多篇文章中反覆論證: 開源模型一直落後閉源最前沿大約 6-18 個月，而且考慮到 Anthropic、OpenAI、Google 手上的資源（算力、資料、人才）是開源陣營的 10 倍以上，這個差距能維持這麼小本身就很驚人。但他判斷差距<strong>更可能擴大而非縮小</strong>。</p>

<p>為什麼? 理由有好幾層:</p>

<ul>
  <li><strong>前沿任務正在往更專業的領域發展。</strong> 法律、醫療、會計這些高門檻領域的訓練資料不在公開網路上，而且需要昂貴的強化學習環境來訓練。閉源實驗室花「天文數字」買這些資料和環境，開源模型很難複製。</li>
  <li><strong>Agent 時代讓蒸餾變更難。</strong> 以前你可以直接拿閉源模型的輸出來訓練，但現在最關鍵的是複雜的強化學習環境和提示詞設計，這些比模型輸出更容易藏起來。</li>
  <li><strong>閉源模型在「難以衡量的品質」上仍然領先。</strong> 特別是穩健性和一般性的好用程度，這些不容易被評測捕捉到。實務上常見的狀況是: 用開源模型跑 agent 得比用 Claude 或 Codex 更頻繁地重置上下文。</li>
</ul>

<p>🔹 <strong>評測的可信度正在下降。</strong> Lambert 觀察到一個弔詭的現象: Gemini 3 拿到「不可思議的評測分數」，但在 agent 實際部署場景裡卻「顯得格外無關」。他直言，在後訓練技術快速演進的時代，他對評測的信心處於「相對低點」。</p>

<p>2026 五月的 <a href="https://www.interconnects.ai/p/latest-open-artifacts-21-open-model">Open Artifacts #21</a> 更進一步呈現了這個爭議: 美國 NIST 下的 CAISI 用 IRT 方法分析 DeepSeek V4，結論是差距正在擴大; 但 Epoch AI 的 ECI 指標卻顯示差距自 R1 以來一直穩定在 3-7 個月。兩個結論完全相反。</p>

<p>Interconnects 團隊的 Florian Brand 和 Lambert 本人也有分歧: Florian 認為開源模型在真實效能上比評測顯示的<strong>更接近</strong>閉源模型; Lambert 則認為閉源模型領先的幅度比 Florian 想的<strong>更大</strong>。</p>

<p>小編覺得這個「團隊內部公開唱反調」蠻健康的——至少讓讀者知道，即使是最接近資料的人，判斷也沒有共識。</p>

<hr />

<h2 id="二蒸餾-沒你想的那麼重要但恐慌很危險">二、蒸餾: 沒你想的那麼重要，但恐慌很危險</h2>

<p>2026 年二月，Anthropic 點名指控 DeepSeek、Moonshot AI、MiniMax 三家中國實驗室透過約 24,000 個假帳號、超過 1,600 萬次對話來「蒸餾」Claude。這件事在業界炸開了鍋。</p>

<p>Lambert 在 <a href="https://www.interconnects.ai/p/how-much-does-distillation-really">How much does distillation really matter</a> 做了非常細緻的分析:</p>

<p>🔹 <strong>DeepSeek 的 15 萬次對話影響可忽略不計。</strong> 在訓練語言模型的量級裡，15 萬筆資料「只是抓個表面」。但 Moonshot 和 MiniMax 合計的對話量換算成 token 大約是 1,500-4,000 億，這個量級「確實可能有意義地改善後訓練」。</p>

<p>🔹 <strong>蒸餾的效果其實很「鋸齒狀」。</strong> 直接拿老師模型的輸出來訓練學生模型並不簡單——研究社群已經看到很多案例，某些老師的輸出反而會讓學生模型<strong>變差</strong>。這本質上是一個研究問題，不是複製貼上就能搞定的。</p>

<p>🔹 <strong>強化學習時代限制了蒸餾的價值。</strong> 這是 Lambert 認為最被低估的因素: 在大規模強化學習訓練的時代，你需要模型自己產生策略內的生成——這些生成佔了訓練中的大部分算力成本，而且不能用別的模型的生成來替代。換句話說，即使你拿到了 Claude 的輸出，你還是得靠自己的算力讓模型從自身的生成中學習。</p>

<p>到了五月，Lambert 在 <a href="https://www.interconnects.ai/p/the-distillation-panic">The Distillation Panic</a> 更直接地警告: 把這些行為叫做「蒸餾攻擊」是個<strong>危險的用詞</strong>。他的論點是，這些中國實驗室真正在做的是越獄和濫用 API，不是蒸餾本身有問題。蒸餾是整個 AI 產業的標準作法——Nvidia 的 Nemotron 蒸餾了中國開源模型，AI2 的 OLMo 蒸餾了多個閉源和開源模型，xAI 在法庭上也承認蒸餾了 OpenAI。</p>

<blockquote>
  <p>編按: Elon Musk 在 OpenAI 訴訟案中被問到 xAI 是否有蒸餾 OpenAI 的技術，他的回答是:「一般來說 AI 公司都會蒸餾其他 AI 公司。」被追問「這算是承認嗎?」他說:「某種程度上。」</p>
</blockquote>

<p>Lambert 最擔心的不是蒸餾本身，而是這個恐慌可能引發的監管連鎖反應: 美國國會正在推動 H.R. 8283 法案、行政命令也在施壓——如果最終結果是有效禁止所有由「曾經蒸餾過閉源 API」的組織開發的開源模型，受傷最深的會是<strong>西方的學術界和小型開源貢獻者</strong>，而不是中國實驗室。因為中國實驗室「很可能還是會繼續做」。</p>

<p>他引用了 Kevin Xu 的一個很有意思的戰略論點: 如果中國公司一直依賴蒸餾當捷徑來接近前沿，他們永遠不會真正學到獨立領先的技術。美國切斷這個「拐杖」，短期會拉開差距，但長期反而可能逼中國發展出更獨立的能力——這跟半導體出口管制的辯論邏輯一模一樣。</p>

<hr />

<h2 id="三中國實驗室-從內部看到了什麼">三、中國實驗室: 從內部看到了什麼</h2>

<p>Lambert 在 2026 年四月親自去了一趟中國，36 小時內拜訪了 Z.ai、Moonshot AI、清華、美團、小米、01.ai。他在 <a href="https://www.interconnects.ai/p/notes-from-inside-chinas-ai-labs">Notes from inside China’s AI labs</a> 寫下了一些非常第一手的觀察:</p>

<p>🔹 <strong>文化優勢在於「執行力」而非「創新力」。</strong> 中國實驗室的核心貢獻者有很大比例是在讀的研究生，跟美國頂尖實驗室（OpenAI、Anthropic、Cursor 等根本不提供實習）完全不同。學生文化帶來四個優勢: 更願意做不起眼但必要的工作、個人英雄主義少讓組織更好擴展、新鮮視角能更快適應新技術、充沛人才適合解決已有概念驗證的問題。</p>

<p>🔹 <strong>中國研究員對 AI 的「哲學問題」幾乎沒有興趣。</strong> 當被問到經濟或長期社會風險時，他們的態度很直接: 這些問題跟我無關，我的工作就是把模型做好。Lambert 形容一位他遇到的研究員把這類問題當成「範疇錯誤」。一位研究員引用了 Dan Wang 的觀點: 中國是工程師在管理國家，美國是律師在管理國家。Lambert 後來也補充修正了自己的觀察: 這種務實態度不只是個人選擇，也跟他們成長的體制有關——在一個不鼓勵對社會結構發表意見的環境裡，專注技術本身是更自然的選擇。另外，中國也沒有像 Dwarkesh 或 Lex 這類讓科學家變成「明星」的媒體管道，科研人員沒有系統性地建立個人影響力的路徑。</p>

<p>🔹 <strong>幾乎所有中國 AI 開發者都用 Claude 來寫程式。</strong> 這可能是整篇文章最讓人意外的發現——儘管 Claude 在中國名義上是被封鎖的。Lambert 說他訪問的每個人都提到在用 Claude。這也側面說明了中國的 AI 推論需求可能比按 SaaS 市場規模去推算的要大得多——中國的 SaaS 支出歷來很低，但雲端市場本身是龐大的。Lambert 認為 AI 的企業支出更接近雲端市場的邏輯，而非 SaaS 市場。</p>

<p>🔹 <strong>Nvidia 晶片依然是黃金標準。</strong> 訓練端大家都缺 Nvidia 的卡，有供應一定買。華為等替代方案目前只在<strong>推論端</strong>被正面提及。</p>

<p>🔹 <strong>中國的資料產業品質落後，但「自己造」的文化很強。</strong> 美國實驗室花千萬美元買單一強化學習環境，中國實驗室覺得國內資料產業品質不夠好，很多東西得自己造。研究員本人會花大量時間打造訓練環境。更大的公司如字節跳動和阿里巴巴則有內部的資料標註團隊。</p>

<p>🔹 <strong>每家中國科技公司都在自建 LLM——這在美國幾乎不可思議。</strong> 美團做外賣的、小米做手機的，都在訓練自己的通用語言模型。在美國，同等規模的公司只會去買 API 服務。驅動力是一種「深層的渴望去控制自己的技術堆疊」: 微調能強化自家的技術底盤、內部版本服務自家產品、開源版本則從社群拿回饋。這種「開源優先」的心態主要是出於實用主義，不是什麼開源理想。</p>

<p>🔹 <strong>中國 AI 產業更像是「生態系」而非「部落戰爭」。</strong> Lambert 觀察到中國實驗室之間的氛圍是「充滿對同行的尊重」，跟美國實驗室私下見面時「火花四濺」的風格截然不同。所有人都敬畏字節跳動/豆包的實力（中國唯一的前沿閉源實驗室）、尊重 DeepSeek 的研究品味（但認為它的組織不適合在經濟上贏）、認為阿里巴巴憑資源最終會贏得大部分市場。</p>

<hr />

<h2 id="四開源生態的複利效應">四、開源生態的複利效應</h2>

<p>Lambert 在五月的 <a href="https://www.interconnects.ai/p/how-open-model-ecosystems-compound">How open model ecosystems compound</a> 提出了一個精妙的分析框架:</p>

<p>🔹 <strong>訓練前沿模型的算力，80% 花在研發而非最終訓練。</strong> 這個數字來自 AI2 的 OLMo 3 開發紀錄和 Epoch AI 對各大實驗室的成本研究。大眾對 AI 模型成本的印象一直被誤導——以為錢主要花在最終那一次大規模訓練上，但實際上絕大部分算力花在實驗、測試、調參這些研發過程。</p>

<p>這個發現的意義在於: 在中國這種所有領先玩家都開源的生態裡，大家可以<strong>迅速從同行的研究中學習，避免重複浪費研發算力</strong>。這就是為什麼中國的開源模型生態有「複利效應」——每一家實驗室發表詳盡的技術報告，等於在幫其他實驗室降低風險，讓他們不用獨立投入同樣的資源。</p>

<p>🔹 <strong>但開源 AI ≠ 傳統開源軟體。</strong> Lambert 很小心地做了區分: 傳統開源軟體有一個從使用者到開發者的回饋循環（Linus’s Law:「只要有夠多雙眼睛，所有 bug 都很淺顯」）。但開源 AI 幾乎<strong>不存在</strong>這個回饋循環——幾乎所有成本都落在模型開發者身上。開源 AI 模型是「降低未來開發成本的工具」，不是即插即用的解決方案。如果你只是拿來用、不做任何迭代，用開源模型幾乎一定比用閉源 API <strong>更貴</strong>。</p>

<hr />

<h2 id="五誰還在做開源-商業模式的困局">五、誰還在做開源? 商業模式的困局</h2>

<p>Lambert 在多篇文章中反覆提到一個越來越緊迫的問題: <strong>願意釋出前沿開源模型的玩家正在減少。</strong></p>

<p>🔹 <strong>Meta 已經在轉向。</strong> Meta 的 Llama 曾經是開源模型的代名詞，但 Lambert 在 <a href="https://www.interconnects.ai/p/the-inevitable-need-for-an-open-model">The inevitable need for an open model consortium</a> 中指出，Meta 正在把重心從 Llama 移開。ATOM 報告的數據讓這個趨勢更加觸目: Llama 在 OpenRouter 的推論份額從 2025 年 1 月的 37.4% 高峰一路跌到 2025 年 8 月的 0%; 衍生模型佔比也從 44% 的巔峰掉到 11%。Llama 團隊內部的政治紛爭據傳已經讓組織承受巨大壓力。更根本的問題是: 當模型成本從一億美元往一兆美元走，Meta 當初「用免費模型來把互補品商品化」的邏輯就越來越站不住腳。Lambert 直言:「歷史上從來沒有人用一兆美元的東西來做這件事。」</p>

<p>🔹 <strong>Qwen 也出現動搖。</strong> 阿里巴巴 Qwen AI 部門的負責人辭職了。Lambert 說他「不太意外」，因為「到了某個時間點，很多開源模型的努力會因為太貴、太同質化而死掉」。Qwen 是目前開源生態裡最接近社群的模型家族，也是研究方法和資料集的事實標準——如果 Qwen 的方向改變，影響會非常大。</p>

<p>🔹 <strong>中國新創也看起來搖搖欲墜。</strong> Moonshot AI、MiniMax、Z.ai 這些靠開源模型打出知名度的中國新創，Lambert 判斷它們「在財務上看起來很不穩定」，因為「公開釋出最強模型」和「把資源集中在能產生營收的 AI 產品上」之間存在根本矛盾。經濟壓力會逼它們把開源模型的重心移往能獲利的方向——更小、更垂直的模型，而非前沿通用模型。</p>

<p>🔹 <strong>只有 Nvidia 有明確的經濟動機做開源。</strong> Nvidia 釋出開源模型是為了賣更多 GPU——讓更多人在開源模型上建構應用，就需要更多 Nvidia 的硬體。他們的 Nemotron 3 Super 也確實表現亮眼。但 Lambert 指出即使 Nvidia 的立場長期來看也不穩定: 如果 Nemotron 太成功會威脅到最大客戶; 如果前沿實驗室開始自研晶片（2031 年左右），Nvidia 的現金流可能受壓; 更極端的情況是 Nvidia 自己決定不賣 GPU、留著算力來訓練閉源模型。</p>

<p>🔹 <strong>開源模型至今沒有可行的商業模式。</strong> Lambert 在跟 Dean Ball 的<a href="https://www.interconnects.ai/p/how-anthropic-vs-dow-impacts-open">對談</a>中坦承:「如果模型真的被商品化，情況看起來蠻慘的。」他對 Reflection AI 那種「做一個超強開源模型，然後賣本地部署」的模式也不看好，因為「本地部署跟閉源模型的商業模式沒有本質區別」。那怎麼辦? 他的想法是「嘗試一堆小的不同方向，搞清楚私有資料在哪些部署場景裡真正有差異化，然後跟社群一起迭代」。但他自己也承認:「我的實際方法就是去交一個億萬富翁朋友。」</p>

<p>資本主義的邏輯很殘酷: 當前沿模型能帶來的利潤越來越高，「把技術當慈善捐出去」就越不合理。這就是為什麼 Lambert 認為一個由多家公司共同出資的聯盟最終是不可避免的——很多公司願意付訓練成本的十分之一甚至五十分之一來參與，換取某種程度的方向影響力和早期存取。Yann LeCun 甚至認為未來會是某種「全球聯盟聯合建造」的模式，因為沒有任何一個國家能獨自擁有它。</p>

<p>🔹 <strong>授權趨勢往 Apache 2.0 收斂是好消息。</strong> 在一片悲觀中，2026 年最值得注意的正面趨勢是 Google 的 Gemma 4 和小米的 MiMo 2.5 Pro 都採用了 Apache 2.0 授權。Lambert 甚至鬆了一口氣說:「那些可怕的 Llama 授權和 Gemma 使用條款是大約 18 個月的過渡期。」Apache 2.0 消除了企業法務的不確定性，對推動採用至關重要。</p>

<hr />

<h2 id="六權重只是系統的一部分開源的結構性劣勢">六、權重只是系統的一部分——開源的結構性劣勢</h2>

<p>這是 Lambert 在 <a href="https://www.interconnects.ai/p/the-next-phase-of-open-models">What comes next with open models</a> 和跟 Dean Ball 的對談中反覆強調的一個觀點: <strong>現在的 AI 不只是模型權重，而是一個完整的系統: 權重 + 工具 + 整合介面。</strong></p>

<p>他的問題很尖銳: 你上一次被「純粹的自迴歸逐字輸出」驚艷到是什麼時候? 除了數學證明或競賽程式碼，這件事從 GPT-4 發布以來就沒什麼變化了。我們今天用的 AI 系統——Claude Code、Codex、Cursor——它們的價值遠遠超出模型權重本身。搜尋工具、程式碼沙盒、檔案系統整合、使用者介面，這些都是系統的一部分。</p>

<p>這對開源模型意味著什麼?</p>

<p><strong>閉源模型有天然的垂直整合優勢。</strong> 它們可以把晶片、推論軟體、模型權重、工具和使用者介面從上到下整合在一起。你用 Claude Code + Opus 4.6 或 Codex + GPT 5.4 的順暢體驗，就是這種整合的結果。開源模型必須在各種推論框架、各種工具、各種使用場景裡都能運作——這本身就是一個巨大的挑戰。Lambert 說，跑一個兩兆參數的開源模型需要大約 80 台 H100 的節點、每天十萬美元的算力成本，還需要專業知識才能把它變成一個可用的系統。</p>

<p>Dean Ball 在對談中把這個問題說得更直接: 當 AI 公司最終發展成「用模型設計自己的晶片、設計自己的資料中心、設計自己的後繼模型」的全整合基礎設施公司時，開源要複製這一切「在定義上就是不可能的」。</p>

<blockquote>
  <p>編按: Lambert 談的垂直整合主要是<strong>部署端</strong>的整合，但小編覺得這個優勢其實從<strong>訓練階段</strong>就開始了。最明顯的例子是 OpenAI 的 <code class="language-plaintext highlighter-rouge">apply_patch</code>——一種專為 GPT 模型設計的自訂 diff 格式，用來讓 agent 編輯程式碼。OpenAI 的 <a href="https://developers.openai.com/cookbook/examples/gpt-5/codex_prompting_guide">Codex Prompting Guide</a> 明確寫道:「我們強烈建議使用我們的 <code class="language-plaintext highlighter-rouge">apply_patch</code> 實作，<strong>因為模型已經被訓練成擅長這個 diff 格式</strong>。」指南中還提到，工具的名稱、參數和輸出格式「越接近模型訓練時用的格式，效果越好，因為這樣最接近模型的訓練分佈」。</p>

  <p>GPT-5-Codex 更是被描述為「專門為 Codex 環境中的 agentic coding 而優化的 GPT-5 版本」。到了 GPT-5.3-Codex，OpenAI <a href="https://www.openai.com/index/introducing-gpt-5-3-codex">直接寫</a>:「這是第一個在自身創建過程中發揮關鍵作用的模型」——團隊用早期版本來除錯自己的訓練、管理部署、診斷評估結果; 工程團隊甚至用 Codex 來「優化和調整 GPT-5.3-Codex 的 harness」。模型和整合介面是互相塑造的。</p>

  <p>這意味著閉源實驗室不只是在部署時把模型和工具串在一起，而是在訓練時就把模型和自家工具鏈聯合優化。開源模型拿到的只是權重，但閉源模型的權重裡已經內建了對自家工具鏈的深度適配——這是開源模型即使跑分追上也很難複製的結構性差距。</p>
</blockquote>

<p>不過也有一個有趣的反面: 中國的 Moonshot AI 和 Z.ai 推出的寫程式方案需求很高，即使模型本身是開源的。「大部分人就是會用便宜的介面加推論服務，而不是自己去搞模型部署。」這暗示了一種可能: 模型開源，但靠服務和整合賺錢。</p>

<hr />

<h2 id="七開源模型的下一階段-從追趕前沿到找到自己的定位">七、開源模型的下一階段: 從追趕前沿到找到自己的定位</h2>

<p>Lambert 在 <a href="https://www.interconnects.ai/p/the-next-phase-of-open-models">What comes next with open models</a> 提出了三層模型分類:</p>

<p><strong>第一層: 閉源前沿模型。</strong> Claude Opus、GPT 5.4 這類，主導最強的知識工作和程式碼 agent。</p>

<p><strong>第二層: 開源前沿模型。</strong> Qwen 3.5、GLM-5、Kimi K2.6、DeepSeek V4 等試圖在同一方向競爭的開源大模型。很多場景下表現很好，但在 agent 的穩定性上仍有差距。</p>

<p><strong>第三層: 開源小型專用模型。</strong> Lambert 認為這才是開源模型最大的未被開發的機會。他的願景是: <strong>每個前沿 agent 重複做十幾次的任務，都可以外包給一個小型開源模型，速度快 10 倍、成本低 100 倍。</strong></p>

<p>他舉了一個很生動的例子:「在一個由程式碼 agent 主導的世界裡，我想做的是建造那些 Claude Code <em>迫切想要</em>作為工具使用的開源模型。」但目前幾乎沒人在認真做這件事——大家都太沉迷於「開源追趕前沿」的敘事了。</p>

<p>Lambert 的核心判斷是: <strong>只要開源生態繼續被定義為「一群模型供應商追趕閉源實驗室」，它就會一直輸。</strong> 閉源公司面臨的整合壓力遲早也會來到開源——而且可能更快。開源模型的出路不是追趕前沿，而是解決前沿實驗室不會去解決的問題: 本地部署、隱私場景、作為前沿 agent 的專用工具、以及各種垂直場景的廉價自動化。</p>

<hr />

<h2 id="八2026-模型動態-誰在崛起誰在掉隊">八、2026 模型動態: 誰在崛起、誰在掉隊</h2>

<p>最後來看看具體的模型動態。Interconnects 的 Open Artifacts 月報是追蹤開源模型生態最好的來源之一，2026 年到目前為止已經出了三期（<a href="https://www.interconnects.ai/p/latest-open-artifacts-19-qwen-35">#19</a>、<a href="https://www.interconnects.ai/p/latest-open-artifacts-20-new-orgs">#20</a>、<a href="https://www.interconnects.ai/p/latest-open-artifacts-21-open-model">#21</a>）。幾個值得注意的趨勢:</p>

<p>🔹 <strong>GPT-OSS 是 Llama 3.1 以來最受歡迎的美國開源模型。</strong> ATOM 的 RAM 指標顯示它的採用率破表: GPT-OSS 120B 的 RAM 在發布 7 天內達到 20.45×、180 天後仍有 15.35×（RAM &gt; 1 就代表有望進入該大小類別的歷史前十）; 20B 版本累計下載超過 5,400 萬次（<a href="https://www.interconnects.ai/p/latest-open-artifacts-19-qwen-35">Open Artifacts #19</a>）。美國終於又有了一個有影響力的開源模型，雖然它的首發體驗「在可用性方面很糟糕」，但最終還是靠實力贏得了採用。</p>

<blockquote>
  <p>編按: 不過「最受歡迎」量的是下載量，不是特定場景的能力。從原文的線索來看，GPT-OSS 被提到的具體用途是 Chroma 拿 GPT-OSS 20B 做 agentic search、Nvidia 出了效率優化版做推論——都不是寫程式場景。寫程式場景的代表反而是 Kimi K2.5（Cursor 用它做 Composer 2）和 Qwen（研究生態的事實標準，Lambert 說「無數的研究方法和資料集都是圍繞 Qwen 建立的」）。GPT-OSS 的高下載量更可能來自美國本土企業偏好（迴避中國模型的法務風險）、做各種微調的基底模型、以及研究用途。</p>

  <p>那 Gemma 4 呢? Lambert 在四月的 <a href="https://www.interconnects.ai/p/what-ive-been-building-atom-report">ATOM Report</a> 中提到 Gemma 4「展現出驚人的早期採用數字」，但比 GPT-OSS 晚了一步。更關鍵的是，過去的 Gemma 模型「一直被工具鏈問題和微調後表現變差所困擾」（<a href="https://www.interconnects.ai/p/gemma-4-and-what-makes-an-open-model">Gemma 4 分析</a>），社群信任需要時間重建。開源模型的採用不只是評測分數的競爭，更是生態系的慢功夫。</p>
</blockquote>

<p>🔹 <strong>DeepSeek V3.2 的採用率嚴重不如預期。</strong> ATOM 報告的 RAM 數字很殘酷: V3.2 發布 7 天的 RAM 只有 0.35×、90 天後也只有 0.60×——遠低於「歷史前十」的 1× 門檻。相比 DeepSeek 2025 年早期的爆發，落差非常大。但 DeepSeek V4 Flash（284B-13B）反而是「真正的明星」——這個相對小的模型表現出乎意料地強，比巨大的 V4 Pro（1.6T-A49B）還受歡迎。小而精悍有時候勝過大而全面。</p>

<p>🔹 <strong>小米 MiMo V2.5 Pro 的崛起。</strong> 從一年前初次登場到現在，小米的模型進步被形容為「驚人」——MiMo V2.5 Pro 已經能跟 Kimi K2.6 和 GLM-5.1 在評測和實際使用上打平。採用 Apache 2.0 授權也幫了大忙。</p>

<p>🔹 <strong>開源生態正在從「通用模型爭霸」轉向「垂直場景百花齊放」。</strong> Open Artifacts #20 被作者稱為「這系列寫過最有趣的一期」——不再是 Qwen、DeepSeek、Kimi 的天下，而是 OCR、語音轉文字、RAG 搜尋、機器人控制、數學定理證明、程式碼編輯等各種垂直場景的模型冒出來。這正好呼應了 Lambert 一直強調的方向: 開源的未來在於多樣化和專用化，而不是「一個模型統治一切」。</p>

<p>🔹 <strong>「長時程任務」成為新前沿。</strong> Kimi K2.6、GLM-5.1 等多個模型都在強調能跑數小時來完成任務的能力。這跟閉源 agent（Claude Code、Codex）的發展方向一致，但開源模型要在這個維度上追趕，需要的不只是更好的模型，還有更好的工具鏈和推論基礎設施。</p>

<hr />

<h2 id="九2026-下半年值得關注的預判">九、2026 下半年值得關注的預判</h2>

<p>Lambert 在 <a href="https://www.interconnects.ai/p/my-bets-on-open-models-mid-2026">My bets on open models, mid-2026</a> 列出了 13 個預判，小編挑幾個最有趣的:</p>

<ul>
  <li><strong>中國開源實驗室會最先面臨資金壓力</strong>，可能在 2026 年下半年就會出現。資金困難會在 3-9 個月後反映在模型能力的軌跡上。</li>
  <li><strong>美國會在 2027 年初開始慢慢在開源模型的採用指標上收復失地。</strong> 代表選手: Google Gemma 4、Nvidia Nemotron、Arcee AI。</li>
  <li><strong>開源模型的最大未開發市場是「本地 agent」和「個人 agent」。</strong> Lambert 稱之為「暗物質」——巨大的潛力，但目前幾乎沒人在認真做。</li>
  <li><strong>禁止開源模型在實務上不可能執行。</strong> 如果美國禁止超過某個算力門檻的開源模型，其他國家遲早會訓練並公開釋出，反而讓這些模型以更少的監管進入美國市場。</li>
</ul>

<hr />

<h2 id="看完之後的一些想法">看完之後的一些想法</h2>

<p>讀完 Lambert 這半年的系列文章，最大的收穫是: <strong>開源 vs 閉源不是一場零和遊戲，也不該被簡化成一個評測分數的追趕賽。</strong></p>

<p>真正有意義的問題不是「開源什麼時候追上閉源」，而是「開源模型在哪些場景下能提供閉源模型無法替代的價值」——無論是主權 AI 的需求、隱私敏感的本地部署、還是作為前沿 agent 的專用工具。</p>

<p>Lambert 自己也承認他對這件事的前景「越來越迷惘」，形容追趕閉源前沿像是推石頭上山——你永遠在推，但石頭永遠會滾下來。但他同時也說:「我從未如此強烈地感到需要建造開源模型。」</p>

<p>這個矛盾本身或許就是 2026 年開源模型最真實的寫照。</p>

<hr />

<h2 id="20266-更新-開源與閉源走在不同的指數曲線上">2026/6 更新: 開源與閉源走在不同的指數曲線上</h2>

<p>本文發布後，Lambert 在六月初又發表了 <a href="https://www.interconnects.ai/p/open-and-closed-models-are-on-different">Open and closed models are on different exponentials</a>，把前面第五、六、七節談的商業模式和定位問題，整理成一個更完整的經濟論述，小編補充在這裡。</p>

<p>他認為決定開源閉源未來權力平衡的核心是經濟問題: <strong>使用者會不會持續為最頂尖的閉源模型付出高額溢價?</strong> 2026 年初已經給出第一個答案: coding agent。過了 Opus 4.5 和 Codex 5.2 這個能力門檻後，使用習慣明顯改變，「人們做這個轉換不是因為懶，而是因為淨產出明顯更高」。依賴 coding agent 工作的人永遠會選最好的模型，不會將就「夠用就好」，Lambert 自己說願意為這些工具付每月 2,000 美元。</p>

<p>🔹 <strong>閉源實驗室的商業形態: Apple 加上 Microsoft。</strong> 權重、harness、工具、推論基礎設施整合在一起的回報巨大: 一面是賣高度整合、極難複製的技術 (Apple)，一面是向整個經濟體賣高槓桿的訂閱 (Microsoft)。Lambert 預期 5-10 年內 OpenAI 和 Anthropic 的估值會落在 2-10 兆美元，前沿實驗室會變成像今天雲端市場那樣的寡占格局。另一個比較新的論點是 <strong>API 業務會衰退</strong>: 實驗室遲早會延後把最強模型放上 API，以保護 token 供應、防止蒸餾、把模型留給利潤更高的場景。</p>

<p>🔹 <strong>開源模型經濟的總價值反而更大，但由一整疊公司分食。</strong> 現在的開源模型在分佈外 (out-of-distribution) 任務上還不夠好，但 Lambert 預期開發者終究會停止在排行榜上追逐 Claude 和 GPT，轉而填補低價格帶的缺口。開源模型天生不整合，要靠多家公司協作提供服務，每一層都有替代品，價格會被壓到大宗商品 (commodity) 等級; 企業的典型用法是找到在特定任務上達到「夠好門檻」的模型，之後就不換了 (因為設置成本很高)。整體市場價值會遠超過 OpenAI 加 Anthropic 的總和，具體圖像是 Together、Fireworks、OpenRouter 和超大規模雲端商上的開源推論佔比穩定上升。</p>

<p>🔹 <strong>兩條不同的指數曲線。</strong> 這是整篇的核心: 不是誰消滅誰的問題。閉源靠整合，從知識工作的頂端開始變現，已經有 product-market fit; 開源會慢得多，但它追蹤的是 AI 向整個經濟和世界的擴散。Lambert 也澄清「遞迴自我改進 (RSI) 會給閉源實驗室不可動搖的優勢」這類說法被誇大了。</p>

<p>文末註腳蠻有意思: Lambert 說 coding agent 這個詞其實很妙，我們在裡面幾乎不寫程式，它們是因為會寫大量程式碼才這麼有能力的通用 agent。對做 AI 應用的人來說，實際的啟示是: 頂級閉源 agent 和便宜的開源推論不是二選一，而是兩種會同時存在的採購邏輯。</p>]]></content><author><name>ihower</name></author><category term="LLM" /><category term="Open Source" /><category term="Industry" /><summary type="html"><![CDATA[如果你關心開源模型的發展，有一個電子報是必讀的: Interconnects AI。]]></summary></entry></feed>