Harness Engineering: OpenAI 工程師九個月不碰編輯器,全用 Agent 寫軟體的工程方法

看了 OpenAI 工程師 Ryan Lopopolo 在 AI Engineer Europe (倫敦) 的演講 Harness Engineering: How to Build Software When Humans Steer, Agents Execute,蠻有料的一場。Lopopolo 過去九個月完全用 Agent 開發軟體,甚至禁止團隊成員碰編輯器,所有程式碼都必須透過模型產生。他自稱「token 億萬富翁」,每天消耗超過十億個 output token (主持人補充: 一天超過一千美元)。這場演講前半是 keynote,後半是 Q&A 對談,把這套工作方式的細節講得蠻具體的。

以下是影片轉成逐字稿的全文: ihower.tw/watch/harness_engineering…

以下是重點整理:

1. 實作不再是稀缺資源,程式碼是免費的

整場演講的出發點是: 寫軟體的方式已經改變,「實作 (implementation)」不再是軟體工程的稀缺資源,程式碼是免費的 (code is free)。

我們過去把程式碼當成負擔,是因為它會持續消耗人類工程師的同步注意力。但模型「無限有耐心、無限平行」,產生、重構、刪除程式碼都不再是團隊資源分配的瓶頸。Lopopolo 說要相信模型有能力產出你需要的每一行程式碼,工程師的角色變成: 思考一天、一週、六個月後需要什麼結構,來駕馭這個產能。

他點出三個讓這件事成立的條件,都發生在 2025 下半年。對他來說關鍵時刻是 GPT-5.2 的發布: 模型從那時起有能力做軟體工程師的完整工作,在真實 codebase 裡產出解決真實用戶問題的高品質程式碼。

2. 新的稀缺資源: 人類時間、注意力、上下文視窗

取而代之的稀缺資源是三樣: 人類時間、人類與模型的注意力、模型的上下文視窗。工程師的技能組合因此往系統思考、系統設計、委派 (delegation) 移動,工作重點是觀察人類的同步時間花在哪裡,想辦法自動化掉,把時間移到更高槓桿的活動上。

程式碼免費之後的實際效果:

  • 以前的優先序排法是「P0 和 P2,P3 永遠不會做」。現在所有 P3 直接平行開四個 Agent 跑,挑一個能解決問題的合進去。

  • 他在 OpenAI 內部做了很多提升同事生產力的工具,從第一天就有完整的多語系支援,倫敦、巴黎、蘇黎世、慕尼黑的同事都能用母語操作,因為這不用跟其他工作搶產能。

  • 大規模重構也是免費的。不會再有一個 migration 卡六個月收不了尾,因為可以直接開 15 個 Agent 把它做完。

3. 重要的不是程式碼,是把你帶到那裡的 Prompt 和護欄

人類不再需要關心實作,「重要的不是程式碼,而是產出它的 prompt 和護欄 (guardrails)」。所以文件、ADR、歷史 ticket、code review 記錄這些「麵包屑」很重要: 這些是把你的團隊帶到今天這些程式碼和產品的過程,Agent 也需要同樣的東西才能到達同樣的地方。

你的工作是讓這些系統和結構對 Agent「可讀 (legible)」: 用 Agent 原生的方式組織、尊重稀缺的上下文、讓完成工作所需的 token 容易預測。具體做法是「盡量讓東西長得一樣」,限縮模型需要動用的注意力。

4. 什麼叫做好? 把 500 個隱性決策寫下來

做好軟體工程很難,需要多年經驗才能內化什麼是高品質、可維護、可靠的程式碼。把一個 patch 做好,背後大概有 500 個小決策,關於那些沒有明說的非功能性需求 (non-functional requirements)。

模型在訓練時看過數兆行程式碼,涵蓋了這些需求的每一種可能選擇。所以你的工作是把「什麼叫做可接受」明確寫下來讓 Agent 看到。他的說法是:

你可以直接說「不要產生 slop、不要接受 slop」,你的 codebase 就不會有 slop。

“You can just simply say do not produce slop, don’t accept slop, you won’t get slop in your code base.”

但做到這件事需要承受短期速度損失: 停下來研究 Agent 在你的環境裡到底卡在哪,把護欄放好讓它不再犯同樣的錯,然後才退回去做更高槓桿的事。

5. 把每個人的專業寫下來,槓桿會堆疊

他的團隊是全端組合,有人擅長前端架構、有人擅長後端擴展性、有人 product-minded。讓每個人把自己領域的「什麼叫做好」寫成文件,等於每個駕駛 Agent 的工程師都得到了團隊裡每個人的最佳能力。

例子: 團隊裡擅長產品的工程師很會寫 QA 計畫。把方法論寫下來之後,搭配 review agent 要求所有 user-facing 的工作都要附 QA 計畫,並指明 PR 該附上哪些截圖或影片來證明功能正常。結果是 Lopopolo 更信任 Agent 的產出、更不需要在旁邊盯著看,可以把更多工作委派出去。這種一次性的高槓桿投資會不斷堆疊。

6. 怎麼讓 Agent 做好: 持續刷新上下文

寫好 AGENTS.md 是基本,但隨著自動壓縮 (auto compaction) 越來越好 (他說 GPT-5.4 加 Codex 的壓縮已經好到他幾乎不用下 /new 開新對話),你要假設上下文會隨時間被換出 (paged out),所以需要在任務進行中持續刷新上下文。

做法是讓 reviewer agent 在過程中以「什麼叫成功」的視角檢查程式碼。他們的 codebase 有安全性和可靠性的 review agent,在每次 push 和 CI 時持續執行,對照文件檢查 proposed patch,問一些具體問題: 這段網路程式碼有 timeout 和 retry 嗎? 新引入的介面是不是設計成難以誤用?

7. 經典例子: 用一條 lint 永久解決 retry/timeout 問題

每個人都被 production 的網路問題 page 過,而那種事故往往一個 retry 加 timeout 就能避免。Lopopolo 承認自己也是那種「補上 retry 和 timeout、merge 完 bug fix 就忘了」的人: 對這個非功能性需求來說,人類既不是可靠的作者也不是可靠的 reviewer。

解法是花時間寫文件,再寫一條針對自己 codebase 的 lint 規則,檢查每個 fetch 呼叫都有包 retry 和 timeout。這樣就「一次性地、永久地」解決了這一整類問題。而這件事做得下去,靠的正是「程式碼免費」這個前提: Agent 可以把整個 codebase 遷移到新規範。

這套方法論的循環是: 觀察 Agent 和人類重複犯的「持久性錯誤類別 (durable classes of failures)」、搞清楚為什麼一直在這上面花時間、設計系統性消除這類行為的方案、然後繼續觀察和細化。

8. 為 Agent 調整 codebase: 測試原始碼本身、寫有指引的錯誤訊息

兩個具體技巧:

對原始碼本身寫測試,跟 lint 分開。例如已知上下文有限,就寫一個測試限制每個檔案不超過 350 行。這是在針對模型調整 codebase 結構,讓現有的模型能力發揮得更好。

錯誤訊息要附修復指引,對模型和人類都一樣。不要只說 lint 失敗,要直接在錯誤訊息裡給 prompt: 「這裡不該出現 unknown 型別,因為我們在邊界用 Zod parse 過了 (parse, don’t validate),你應該用那個衍生出來的 type」。他開玩笑說 Zod 是「我們 AI 未來的承重基礎設施」。

9. 萬物皆 Prompt

今天講的所有東西都是 prompt,完全不用動模型權重: AGENTS.md 是 prompt、rules 檔案是 prompt、skills 是 prompt、lint 錯誤訊息是 prompt、review agent 在 PR 上的留言也是 prompt。你會找到很多把 prompt 塞進 codebase 的方式,例如把 Agent SDK 嵌進測試裡,用 prompt 檢查程式碼的可接受性。

連寫 prompt 這件事也可以委派: 他把 Codex 指向 OpenAI 開發者指南上所有的 prompting cookbook,要它合成出一個「怎麼寫 prompt」的 skill。之後需要寫 prompt 改善 Agent 表現時,就用這個 Agent 寫的 skill 來寫 prompt。所有編進 repository 的槓桿都會互相堆疊。

10. (Q&A) 實際工作流: Codex 是入口,5-10 個 skills 藏住所有複雜度

Q&A 段落補了很多實作細節。他們的工作流從 ticket 開始: 把 ticket 連同幾個 skills 一起交給 Agent。重點是「Codex 是開發流程的入口」,而不是把 app 和 Codex 包進某個特製的 shell 環境。他們給 Codex 的 skills 包括: 怎麼啟動 app、怎麼開本地的 observability stack 拿 logging 和 telemetry、怎麼透過本地 CLI 接上 Chrome DevTools。

skills 數量刻意維持在 5 到 10 個,不求廣、只把現有的做深。因為 repository 內部的開發工具變動很快,把複雜度藏在 skill 底下,人類只要記得呼叫 skill,Agent 自己會搞定。有個例子: 底層從直接用 Chrome DevTools Protocol 換成 daemon 架構,他三週後才知道這件事,因為 Agent 靠文件一直正常完成工作。

11. (Q&A) 好的 harness 只做一件事: 在對的時間給模型對的文字

怎麼避免做出來的 harness 被模型進步淘汰 (苦澀教訓, bitter lesson)? 他的答案是只做最低限度的上下文管理。模型本來就被訓練成會遵循指令,harness 該做的就是「在對的時間把指令浮現給模型」,而「告訴模型任務需求」這件事永遠不會被淘汰。

而且要 just-in-time,不要前置塞爆: 例如你希望 React component 拆小、盡量 stateless 好做 snapshot test,不需要一開始就載入這些規範,讓 Agent 先自由打造和實驗 UI,到 lint/test 階段再告訴它「要完成這個工作,你必須把 component 拆開」。Agent 會把已寫好的 patch 改到符合規範再送上 GitHub。

另外他選擇直接依賴官方 harness (Codex/Claude Code 這類): 因為模型實驗室是「在 harness 的脈絡裡做 post-training」,apply patch 工具、bash 工具的呼叫方式都在訓練迴圈裡。透過 SDK 接上官方 harness,等於搭上 post-training 的順風車,自己只要專注在「什麼是正確的程式碼」。

12. (Q&A) 協作就用 GitHub,別讓 Agent 被 reviewer 圍毆

協作平台? 就是 repository 裡的 markdown 檔案加 GitHub,PR 是所有 Agent 和人類協作的中心。因為優化目標是吞吐量,任何回饋都不阻塞流程: 人類和 Agent 都可以 review 或不 review,實作 Agent 對每條回饋可以採納、延後或拒絕。

這個設計有個重要性質: 不把模型關進規則的箱子裡。如果硬性規定「每條回饋都必須處理」,會出現「coding agent 被所有 reviewer 霸凌」的失敗模式。應該偏向讓程式碼被接受,而不是追求完美、淹死在細節裡。

對 plan mode 他也持保留態度: 大部分人核准計畫時根本沒讀,等於把一堆你不見得想要的指令編進去了。真要用計畫,建議把計畫本身當成獨立 PR 推上去,人類逐行審過、核准合併後才開始執行。

13. (Q&A) 垃圾回收星期五: 把 code review 回饋變成系統性防呆

團隊三個人、每人每天 3 到 5 個 PR 之後,merge conflict 和人類 code review 變成瓶頸。他們的解法是每週五訂為「垃圾回收日 (garbage collection day)」: 整天的工作就是把這週觀察到的所有 slop 分類,想辦法讓每一類問題從根本上不再發生。

這個循環是: 人類在 PR 上給的回饋,代表 Agent 缺了某個上下文 → 把它寫進 repository 文件 → 之後透過失敗的測試或 review agent 自動浮現給 Agent,讓它自我修正。他們還請每個人把自己給過的 review 回饋依「人設 (persona)」分類: 前端架構師、可靠性工程師、擴展性工程師,然後為每個 persona 開一個 review agent,每次 push 自動觸發,對照文件只回報 P2 以上會擋合併的問題。slop 就這樣持續下降。

14. (Q&A) 架構也為 Agent 設計: 750 個 package 的 monorepo

專案一開始是一個 package 的 Electron app,最後變成一團混亂: 沒有 package 邊界可以強制 API 公私有的約束,Agent 在檔案系統裡也沒有具體的掛鉤點可以辨認領域邊界。後來改採「萬人組織等級」的架構: pnpm workspace 裡 750 個 package,依業務領域或技術分層隔離。

就算你實際上不用微服務,把 repository 結構切到「大部分修改都能限縮在一個目錄子樹裡」對 Agent 很有幫助。而且「檔案系統裡的程式碼也是文字,等於你餵給 Agent 的 prompt」: 讓程式碼盡量一致,Agent 不管看到 repository 的哪個角落,累積的都是可轉移的上下文。bounded concurrency helper 只有一種寫法、ORM 只有一個、CI script 只有一種寫法,模型要產生的 token 就更容易預測。

15. (Q&A) LLM 是模糊編譯器,程式碼是可拋棄的建置產物

被問到「程式碼是不是可拋棄的建置產物」,他的答案是肯定的,並給了一個心智模型:「LLM 是模糊編譯器 (fuzzy compiler)」。harness engineering 放進 codebase 的所有上下文,本質上是「哪些程式碼可以被接受」的約束條件和最佳化 pass,就像 LLVM 編譯 Rust 時做的靜態分析。

換模型就像把 Rust 編譯器的後端從 LLVM 換成 Cranelift: 產出的 x86 指令不同,但只要「什麼是可接受的程式碼」的規則夠完整,出來的都是合格的成品。

順帶一提 token 的用途分配: 大約三等分,規劃/ticket 整理/文件、實作、跑在 CI 上的各佔三分之一。把 token 花在 CI 是必要的,因為「寫程式碼已經不是難的部分,讓程式碼被接受、推進產品才是」。

16. 終點: 把自己從迴圈中移除

他描述的願景是: 給定一個 token 預算和一季 (或一年) 的工作量,人類負責排優先序、定義成功指標和可靠性指標,然後機器持續推進產品,完全不需要手放在方向盤上。他現在的日常已經是下班前開好任務、筆電綁在汽車後座繼續跑推論,通勤 30 分鐘也不浪費。目標是 50 個 Agent 全年無休地跑,而他完全不需要介入。

軟體工程還有一大塊在寫程式之外: 分流用戶回饋、處理 oncall、確認 production log 沒有洩漏個資、確保客服人員有寫好的 runbook。當人不再需要產出程式碼,心力就移到這些更高層次的活動上,但 Agent 也做得了這些,所以「把流程和驗收標準寫下來」變成這份工作後設編程 (meta programming) 的部分。

整場演講可以收斂成他的一句話:

每次我需要對 Agent 輸入「continue」,都是 harness 的失敗: 它沒有提供足夠的上下文,說明什麼叫做「繼續直到完成」。

“Every time I have to type continue to the agent is a failure of the harness to provide enough context around what it means to continue to completion.”

值得注意的是,這整套方法沒有任何神秘的基礎設施: 全部都是文件、lint、測試、review agent 這些現成的工具,差別只在有沒有系統性地把「什麼叫做好」寫下來,並在對的時間餵給模型。