Autopilot
一個可攜帶的自主循環,因為它詢問工作本身什麼是「完成」,而不是自行假設——並且不會停止,直到該答案被滿足。
這個技能旨在讓以下三種說法不可能成立:
- 「它可行」但實際上從未執行過。
- 「它完成了」但唯一的評判者就是執行者本人。
- 「我處理過了」卻沒有任何關於嘗試了什麼、什麼失敗了以及為何失敗的記錄。
主題無關緊要。重構、遷移、報告、資料集、渲染農場、集群:以下的循環完全相同,只有檢查不同。
0. 引導——每個專案一次
下面的 <skill> 是此檔案所在的目錄——CLI 安裝後是 .agents/skills/autopilot,或是任何它被克隆到的位置。從專案根目錄執行所有命令。
node <skill>/scripts/discover.mjs # 檢查、記憶存放點、危險信號
node <skill>/scripts/bootstrap.mjs # 如果帳本不存在則建立
node <skill>/scripts/skills.mjs # 技能庫——選擇此專案的特定領域
node <skill>/scripts/tools.mjs # 此機器目前已可觸及的:MCP 伺服器、外掛
discover 會輸出專案自己的檢查命令。該命令就是以下每個迭代的完成定義。
如果它輸出 checks: [],先別相信它。發現程序只會讀取那些宣告了檢查的檔案;專案真正的檢查通常以文字描述的形式存在。搜尋它在 memoryHomes 中列出的所有路徑,以及 README 和 CI 設定檔,查找一個可執行的命令——曾經有一個專案的 checks: [] 旁邊就有一個 CLAUDE.md,裡面指定了 ./tests/run_all.sh,該腳本在四秒內跑了 63 個測試,而代理原本可能會發明的檢查 (node --check) 在一個死亡應用程式上也顯示綠色。
只有在搜尋完全沒有結果時,專案才真正沒有自動檢查——這在散文、研究和運維中很常見,但並不代表你可以自行判斷自己的工作。在第一次迭代之前就商定一個驗收檢查,並將其寫入目標中(§1)。以下任一都可作為檢查;「我審查過了,看起來沒問題」則不是:
| 工作類型 | 一個非你個人意見的檢查 |
|---|---|
| 程式碼 | 一個退出碼為 0 的命令,在前景執行 |
| 文件 | 一個逐項宣告的清單,每個宣告附有來源或命令 |
| 資料 | 一個他人可重新執行並返回預期形狀的查詢 |
| 基礎設施/運維 | 來自系統本身的觀察,分為前後對照 |
| 研究 | 產生數字的查詢或提取,加上你事先註冊為反例的結果——兩者皆可由他人重新執行 |
在觸碰任何東西之前,先閱讀發現結果:
memoryHomes——持久的知識已存在的存放位置。使用那些檔案。絕不要建立第二個CHANGELOG.md、第二個學習目錄、第二個AGENTS.md。為同一種知識建立第二個存放位置是代理讓倉庫變得更糟的最常見方式。signals.envFilesPresent和signals.remote——在第一次迭代之前,詢問工作環境是否指向生產環境。曾經有過這樣的情況:倉庫中沒有任何相關說明,且每個本地執行看起來都無害,但實際上環境就是指向生產的。在被告知不是之前,都假設是。signals.vcs——none表示沒有復原功能。在第一次迭代之前將輸入複製到安全的地方,並在goal.md中說明位置;§8 中「提交綠色的內容」在這裡沒有意義,而這正是工作丟失的時候。signals.dirty——未提交的工作是某人的。建立分支,絕不要建立在髒的基礎上。
在第一次迭代之前武裝好自己
skills.mjs 是值得擁有的公開技能簡表,按領域標記。這些標記就是詞彙表——每個專案都有 any 和 review,然後根據專案實際需求加入 web、react、db、docs、perf、a11y、mac、infra、research、design。
node <skill>/scripts/skills.mjs # 目錄及其標記
node <skill>/scripts/skills.mjs --install any --dry-run # 它將要執行什麼
node <skill>/scripts/skills.mjs --install any # 總是實用的集合
node <skill>/scripts/skills.mjs --install react,perf,db # 此專案的特定領域
選擇,不要囤積。 每個安裝的技能描述在每一次回合中都會被載入:一百個技能不代表一百個能力,而是更小的視窗和一個只會略讀的模型。
安裝就是在你的權限下執行第三方程式碼,因此這是一個需要宣告的行為:先執行 --dry-run,說明你即將加入什麼以及原因,然後再安裝。如果專案不符合目錄中的任何領域,那麼 any 單獨就是正確答案——而 find-skills 是目錄中較薄弱的領域(研究、基礎設施、資料目前都還很薄弱)的入口點。
然後衡量你剛剛花費了什麼。 skill-cleaner 隨 any 集合一起安裝,它就是讓「選擇,不要囤積」不再只是建議的方式:
node --experimental-strip-types <skills-root>/skill-cleaner/scripts/skill-cleaner.ts \
--root .agents/skills --root-only --no-logs
它會報告已安裝描述所佔用的預算、重複項(兩個技能回答一個問題——這是此循環在上一個層級追捕的缺陷),以及值得壓縮的長描述。採取行動:卸載這個專案不使用的技能,合併重複項而不是保留兩者。你閱讀後卻忽略的審計,其成本與沒有審計相同。
技能只是其中一半——另一半是可觸及性
skills.mjs 說明代理知道什麼。tools.mjs 說明它可以觸及什麼:它在它知道如何讀取的設定檔中找到的 MCP 伺服器、承載更多功能的外掛,以及磁碟上無法告訴你的任何東西。空的結果意味著「不在這些檔案中」,絕不代表「不在這台機器上」。
node <skill>/scripts/tools.mjs
你缺少的能力不是縮小目標的理由。 它是一個階梯——已可觸及 → 應用程式自己的 CLI → 公開伺服器 → 連接器(暫放,§6)→ 使用 new-mcp.mjs 自行編寫。每一級階梯、各自的成本以及其中的陷阱都在 references/tooling.md 中;在構建任何東西之前先閱讀它,因為它防止的兩個失敗從這裡是看不到的:判斷一個能力缺失而它實際上已配置好,以及為某個已被回答的問題建立第二個答案。
最會咬人的一個: 大多數執行環境在啟動時讀取它們的 MCP 設定,因此在執行過程中編寫的伺服器通常對編寫它的會話是不可見的。這就是為什麼腳手架也是一個 CLI——node server.mjs --call <tool> '<json>',同樣的處理程序。現在就使用這種形式,讓下一個會話取得伺服器。「在重啟後繼續」是一個持有檔案的停滯循環。
然後,在整個過程中:你在這裡學到的東西會變成它自己的技能(§5),由循環編寫並在同一會話中使用——而它缺少的能力會變成它自己的工具,以相同的方式使用。
1. 目標——循環執行的對象
在第一次迭代之前,寫下一個可證偽的句子和 2–5 條完成標準。寫入 goal.md,或者發現結果已持有的任何檔案中。
- 可證偽:「讓 X 更好」不行。「每個花錢的路徑都在一個明確的開關後面,並且每個開關都由一個型別或一個失敗的測試所約束」可以。
- 每個標準必須可由不是你的人檢查——與 §0 相同的標準。只有你能確認的標準就不是標準;改寫它。
- 未知是允許的,作為它們自己的標準:「我們知道 N 是否為原因」是研究的一個合法完成標準。
- 一個大目標是透過小目標達成的,而這些標準就是該分解——每一個都是可以單獨落地、檢查和驗證的片段。一個對一次迭代來說太大的標準會被拆分為它自己的標準;深度是允許的,並且每一層級都保持相同的標準(可由不是你的人檢查)。一個不再可證偽的層級是一個計劃,而不是一個標準。
- 你缺少的能力是一個標準,不是一個繞道。「循環可以無人值守地驅動 X,由一次往返證明」被放在它所解鎖的工作旁邊的表格中(
references/tooling.md)。寫下來,構建一個工具就是有界限的,它的缺失是可見的;沒寫下來,它就變成了一個沒有產生任何標準且看起來像進展的下午。 - 如果沒有人給你目標——「繼續」然後就沒了——第一次迭代的唯一輸出就是一個提議的
goal.md和給人類的問題。一個由你編寫然後又滿足的標準就是上面的主張 #2,只是穿上了一份核對清單。 goal.md也承載了固定狀態:檢查命令、其基準輸出、關於生產環境問題的答案、分支(或輸入複本的位置),以及安裝了哪些技能。一個必須重新推導這些項目的恢復不算是恢復。
然後循環執行,直到每個標準都被滿足且驗證。不是直到模型覺得完成了,也不是直到評論者安靜下來——他們從不會安靜。每個迭代結束時,在 goal.md 中標記哪個標準有了進展以及什麼證明了它。
僅出於 §6(詢問)、§7(無效勞動)和 §8(上下文)中的原因才提前停止循環。
2. 迭代
一次迭代 = 一個落地的變更,已檢查、已驗證、已記錄。不是一個檔案,不是一次對話。
選擇。 從目標中未滿足的標準中選擇。如果有多個標準開放,選擇那個失敗會使最多其他工作無效的標準——按風險×影響範圍排序,而不是按什麼容易做。
在縮短之前先理解。 從頭到尾追蹤真實的東西。然後,在構建任何新東西之前,詢問它是否已存在——參見 references/premise-check.md,這是這個技能中單一產出最高的習慣,也是最難學會的。
構建最小的、實際上是正確的東西。根本原因,而不是請求中命名的症狀:首先找到所有其他具有相同形狀的地方,然後在它們全都通過的地方修復它。
證明。 執行發現到的檢查,在前景中,讀取在執行該檢查的 shell 中的退出碼——或者更好的是,讓不是正在寫摘要的東西為你讀取它:
node <skill>/scripts/prove.mjs --record -- npm run verify # 檢查、其真實狀態,寫入 goal.md
node <skill>/scripts/prove.mjs --times 3 -- <check> # 一個與自身不一致的檢查是不穩定的
證明和驗證是模型可以敘述的兩個步驟——其他步驟留下檔案、提交或差異,而「檢查通過了」和「審查者同意了」都是由希望它們為真的上下文所寫的文字。執行器回答第一個問題:它在沒有 shell 的情況下產生,因此它報告的狀態是子程序自己的狀態;它拒絕一個其 npm 腳本包含管線的檢查("verify": "tsc | tail" 報告了 tail 的狀態,這正是導致六次紅色部署顯示為綠色的形狀);當循環被停止時它報告 250,當同一個檢查返回兩個不同的狀態時報告 251,而使用 --record 時它寫入運行產生的數字——包括失敗的——到 goal.md 中。
它看不到的:shell 腳本中的管線、Makefile 目標或你的檢查所呼叫的二進位檔。它移除了它可以證明的謊言;它不認證你的檢查。§3 是回答驗證的部分——將判決及其證據記錄在帳本中,否則該步驟也只是敘述。
在第一次迭代之前也執行一次並將其貼入 goal.md:一個在你之前就存在的失敗是一個要記錄的決定,而不是你的迭代的錯,而沒有基準線你將要麼承擔別人的紅色,要麼把他們的綠色當作進展。一個曾經與自身不一致的檢查就是不穩定的——在一個標準被標記為滿足之前,重新執行它三次。檢查說謊的三種方式,都觀察到了:
| `check | tail` |
check > log; echo "EXIT=$?" | ; 使得狀態是最後一個命令的——總是 0 |
| 一個背景化的包裝器 | 完成通知報告的是包裝器的狀態 |
當沒有命令時,證明不會被跳過,也不會是「我又讀了一遍」。完全按照 goal.md 中的措辭重新執行驗收檢查,貼上其輸出,並說明失敗的結果看起來會是什麼樣子。如果你無法說明失敗,你就沒有一個檢查。
每一個新的防護都需要一個你已經觀察到它失敗的演示(沒有修復時)。刪除修復,執行它,看到紅色,恢復。一個無法觸發的防護比沒有更糟:它讀起來像是案例已被處理的證明。同樣的規則也適用於程式碼之外——如果檢查無法失敗,它就是裝飾。
而當工作產生了某個東西——設定檔、腳本、單元檔案、產生的伺服器——時,執行你產生的東西。 閱讀它證明不了什麼;同一場戰役中三次,缺陷在頁面上不可見,而在第一次執行時卻很明顯。一個產生的 MCP 伺服器看起來正確,但會讓它的客戶端掛起,因為 JSON-RPC 通知不能得到回覆。一個運營商的包裝器讀起來完美,退出碼為 0,但從未實際呼叫代理,因為它的步驟是用 && 連接的。一個 launchd plist 要嘛通過 plutil -lint,要嘛不通過——只有 launchctl bootstrap + kickstart,然後讀取它寫的內容,才顯示它實際在運行。產出不等於執行。
驗證——§3。絕不要跳過它,絕不要自己做。
落地,無論此專案如何接受工作:提交和 PR、發布、應用、交接。按名稱暫存——絕不使用 -A/-u/.,它們會撿起僅限本機的編輯,而 -u 會錯過新檔案。然後檢查東西是否真的落地了:綠色的本地運行不是部署,合併的 PR 不是線上變更。
記錄——§4。 然後更新 goal.md 並開始下一個迭代。
3. 由未執行工作的人驗證
這與執行檢查不同,而且它不是可選的。
你自己的修正是第二大的缺陷來源——在一個衡量的戰役中,後期輪次中所有發現的 58–68%,在一個儲存庫上。你看不到它們,因為編寫它們正是排除看到它們的原因。
- 每個迭代:至少一個子代理,它沒有執行工作,被給予精確的範圍,唯讀,其工作是反駁工作已完成的聲明。來自一個沒有真正嘗試過……的審查者的「已確認」