打造自己的規格驅動 (SDD) 工作流
Spec-Driven Development · Agentic Engineering
打造自己的規格驅動 (SDD) 工作流
Spec-Driven Development · Agentic Engineering
為什麼現成的 agentic 工具不適合系統工程,以及我做了什麼來補上這個缺口。

本文亦有英文版本 / This article is also available in English
我想把 YOLOv8 部署到四個 backend 上:ORT-CPU、ORT-CUDA、NCNN+Vulkan、QNN-HTP。我想驗證每個 backend 對上 Python reference 的數值正確性,跨 Windows 桌面與 Snapdragon Android,跨 FP32 跟 FP16 精度。三個 SDK。兩個平台。Cross-compile pipeline。On-device benchmark。
我試過用業界主流的規格驅動開發(spec-driven development,以下簡稱 SDD)工具:OpenSpec、GitHub 的 Spec Kit、Tessl、Everything-Claude-Code(ECC)。每個都讓我學到些東西。但每個都不太對。
問題不在工具。問題在於這些工具都不是為我這種工作設計的。
這篇文章記錄了接下來發生的事:在幾個月之中,我打造了一套個人工作流,用真實專案驗證它,現在它驅動我每天的工程工作。它不是產品。它沒有開源。它是一份誠實的紀錄,講的是「對某種特定工程師管用的方法」。這種工程師:把 native code 部署到異質硬體上,而對他來說,「測試 PASS」不只是一個綠色勾勾。
2026 年的 SDD 生態
過去十八個月,AI agent 的規格驅動開發工具成熟得很快。四個工具主導了討論:

2026 年四個主流 SDD 工具一覽。每一個都服務不同的受眾,解決不同的問題。
這些工具加起來服務了非常多開發者。但這些開發者裡的大多數,把產品送到 npm registry、容器映像檔、雲端 endpoint。他們的回饋迴圈一分鐘內完成。他們的「測試」就是 npm test。他們的「部署目標」就是一個 Docker image。
那不是我的世界。
不對位的地方
這些工具的 web 偏見是結構性的,不是偶然的。它們由 web 開發者打造,用 web 專案 demo,由蓋 web 產品的企業出資。C++ 出現在功能列表裡,但 cross-compile pipeline、NPU SDK 整合、on-device 驗證矩陣都不在。
當我試著把這些工具用在真正的系統工程上,四個具體的鴻溝浮出來。
1. 「12 種語言」其實大都是同一個語言家族
ECC 的支援語言列表把 C++ 跟 Nuxt 4、Flutter 並列。它的意思是「web/app 開發者可能會用的 C++」。沒有 cross-compile toolchain 的支援,沒有 Vulkan / CUDA SDK 整合的範例,沒有 Android NDK 的考量,沒有嵌入式 driver 的限制。這個語言在列表上。但這個工作流不在。
2. TDD 假設不符合 native 驗證流
Web 的 TDD 是個乾淨的迴圈:寫測試 -> npm test -> 紅綠燈 -> 結束。
Native 驗證長這樣:

兩個不同的迴圈。兩者都稱為「測試」,但這種不對稱性形塑了後續的一切。
驗證一個 YOLOv8 在 Snapdragon NPU 上的 inference 不是 assertEquals(actual, expected) 那回事。它是要對著 Ultralytics Python reference 確認 bbox IoU > 0.99,同時量測在裝置上的冷啟動跟熱啟動延遲、peak memory,最好還有功耗。這要在 FP32、FP16、INT8 build 之上做,要在 PC 跟手機兩個平台上做。一個「測試」是一個多維度的驗證矩陣。
3. Brownfield 範例都是 web brownfield
OpenSpec 文件裡的 brownfield 範例是 dark mode toggle、theme provider、CSS variables。它的 ADDED Requirements delta 格式是真的好用,我直接借用到自己的 kit 裡。但周圍的範例跟模板都假設你的「addition」是一個 UI 元件,不是一個新的 execution backend,那種要跟 build system 限制、硬體可用性檢查、runtime fallback 邏輯共存的東西。
在現有的 C++ inference engine 加一個 CUDA execution provider,跟在現有的 Next.js app 加一個 React component,是兩種完全不同類型的命題。兩個都叫「brownfield」,只是廣義上叫得通而已。
4. Spec 顆粒度是為 feature 設計,不是為 phase
SaaS 脈絡下的「feature」是一個 self-contained、面對使用者的能力,通常 1–2 個 user story、半天到三天的工作量、200–800 行的 spec。系統專案的「phase」是橫跨多個架構層的協調改動,常常持續一週以上,spec 動輒上千行。同一個詞。完全不同的怪獸。
SDD 工具的 web 偏見不是惡意。它源自於誰在做這些工具、誰在支持這些工具、什麼樣的範例專案最容易展示。
動手來補上
在幾個月之中,我組裝了自己的一套 kit。不是當成產品,純粹是補足個人對工作流的不足。它經過十幾個版本才穩定下來,現在我每天都在用。
這套 kit 借了不少東西。Delta-spec 格式借自 OpenSpec。Phase-loop 紀律借自 agile。Skill-based 組合借自整個 agentic harness 圈子。它也加了一些我沒在別處見過的概念。
雙層架構

三層。Meta-Agent 跟你對話。Kit 做機械式的 scaffolding。Project 是真正幹活的地方。
Meta-Agent 解決的是 v1.2.9 時我撞到的一個真實問題:那時 kit 已經累積到 15 個語言 × 3 個 mode × 4 個 test mode × 3 個 domain × 多個 flag,連我(kit 的作者)都記不住該用哪個 flag 組合。如果連我都忘了,其他人更不可能記得。
所以我在 kit 上面加了一層。不用記 flag,你跟 Meta-Agent 對話。它依序執行三個 skill:先引導你的意圖,跟你一起為專案草稿制定 plan,然後根據 plan 挑出對的 kit 配置。底層的 init script 還是可以單獨呼叫。Meta-Agent 是疊加的,不是取代的。
這也解決了一個更重要的問題:順序。舊的工作流是「先選配置,再寫 plan」。這順序是反的。你還沒搞懂要做什麼之前,無法選對配置。Meta-Agent 強制了正確的順序:意圖 -> plan -> 配置 -> init。
Phase Loop
專案中的每一個 phase 都走同樣的九步迴圈:
1. /propose <phase-id> # 建立 change folder
2. 填 proposal.md # 意圖、scope、approach
3. 討論 delta.md # FRs in BDD format
4. 填 tasks.md # 具體的 checklist
5. 實作 # agent 跟著 tasks 走
6. 驗證 acceptance # 對照前面的 entry
7. /crystallize # 把決策固化到 CLAUDE.md
8. /archive-change # delta 合併到 requirements.md
9. Commit # conventional commits 格式
delta 用的是 OpenSpec-inspired 格式:ADDED Requirements、MODIFIED Requirements、REMOVED Requirements、RENAMED Requirements,每個 requirement 下面附完整的 BDD scenarios。我某個專案的 Phase A 有 13 個 functional requirement、29 個 BDD scenario,總共大約 1000 行的 spec。這種規模塞不進 OpenSpec 的 feature-level 模板,但塞得進我的 phase-level 模板,因為它就是為這個規模設計的。
Domain 是一級概念
語言本身沒辦法捕捉我看待專案的方式。Modern C++ 有它的慣例(RAII、Rule of 0/3/5、我自己叫的 member naming「3+1 規則」)。Inference engine 架構有它的模式(layered abstraction、IExecutionProvider 契約、reference implementation 紀律)。EDA 工具有自己的工作流。遊戲開發有自己的決策矩陣。
我把 domain 加成獨立於語言的另一個維度。llm-framework 給 inference engine 用。desktop-gui 給 native UI 用。game-dev 給有 Steam release 規劃的遊戲專案用。每個都是一份決策指南 skill,不是 code 模板。它們疊在任何語言之上。
用 api-docs 取代訓練記憶
LLM 輔助開發最貴的其中一種失敗模式,是 model 用訓練記憶裡的 API 寫程式,而不是當前的 API。你會得到看起來合理的程式碼,但呼叫的是 deprecated 的函數,用的是舊的 signature,假設兩個版本前的行為。
我的 kit 裡有個 api-docs skill,要求 agent 在使用任何不熟悉的 API 前去抓當前的文件,要求 agent 在寫非平凡的 code 前驗證版本特定的行為。這是個小習慣,但有複利效應。差別在於「能編譯的 code」跟「能對著實際裝的版本編譯的 code」。
RTK:Token 過濾器
另一個獨立但互補的工具:rtk(Rust Token Killer)坐在 LLM agent 跟 shell 中間。它把冗長的命令重寫成壓縮版本。git status 從約 2000 token 的輸出降到約 200,cargo test 拿掉 60-90% 的雜訊,ls -la 從 45 行壓到 12 行。
這不是我的 kit 的一部分,是我整合進來的獨立專案。但它值得提,因為它解決一個真實的成本問題。重 agent 工作日,原始命令輸出可以塞滿 context window。RTK 留下訊號,丟掉雜訊。
未來預測
我猜每個 CLI 工具最後都會出一個 「agent mode」,當被 AI agent 呼叫時預設輸出壓縮過的格式。今天 RTK 在解決一個本來該是 CLI 一級關注點的問題。明天,這可能就是 git、cargo、kubectl 自己內建的 feature flag。
當出錯的時候,該如何處理?
沒有任何工作流能 robust 到 agent 永遠不出錯。有意思的問題是:當它出錯了,你怎麼辦?
我定下了一個兩階段的升級框架(two-tier escalation):

兩階段升級框架。第一階段比較快,但受限於我自己的知識邊界。第二階段比較慢,但沒有上限,而且會產出我可以左右對照的證據。
第一階段:自己讀 code
當 agent 給出錯的輸出,而問題在我的 domain 內(嵌入式系統、modern C++、Vulkan 那類的東西),最快的路通常是:我自己讀 code,形成假設,然後給 agent 一個有方向的 prompt。不是「這壞了,修一下」,而是「問題在這裡的變數生命週期,buffer 跑出 scope,請用這個約束 refactor」。Agent 處理有方向的工作很厲害。處理沒方向的 debug 就很普通。
第二階段:找另一個 LLM 來幫忙
當問題超出我的 domain,或第一個 LLM 卡在 local minimum、一直提同樣的錯誤答案,我就升級到另一個 LLM。流程是:
- 讓第一個 agent 產出結構化的問題報告。它試了什麼、觀察到什麼、現在還是哪裡不對。
- 把 codebase branch 到測試目錄。main 不動。
- 把報告交給第二個 agent。Codex、local Llama,或某個專門的 model。第二個 agent 在 branch 上工作。
- 左右對照結果。把贏家 cherry-pick 回 main,或把可動的 approach sync 回去。
案例研究:LiveTranslator
最戲劇性的例子來自一個叫 LiveTranslator 的專案,那時 Claude Code 在整合 Whisper 做即時語音轉文字。Voice activity detection(VAD)切割不對,加上 transcription hallucination,兩個都頻繁發生。這是 Whisper 已知的失敗模式,被即時的限制放大。
Claude 在同一個問題空間裡反覆 iterate,沒有清楚的進展。所以我請它產一份 quality report,結構化地描述失敗模式、code path、試過的參數。然後我把這份報告交給 Codex,在獨立的 test branch 上做。Codex 的訓練分布對這個特定問題類別有不同的 exposure。在它的 branch 上 iterate 幾輪後,VAD chunking 改善了很多。我把可動的 approach cherry-pick 回 main。
不是哪個 agent「比較好」。它們有不同的盲點,這個專案因為交叉檢查而受益。
案例研究:objdet-engine — 一次 spec 跟 infra 的 drift
不是所有失敗都來自 agent。有時候它們來自流程的縫隙。
做 objdet-engine 的時候,我的 Phase A spec 規定專案必須是 static link。需求很清楚:
FR-EP-CPU: The system MUST be statically linked
with no runtime DLL dependencies.
但我跑 build 的時候,產出的 binary 依賴 gtest.dll 等 runtime DLL。我花了大概 60 分鐘在這上面。
Root cause:CMakePresets.json 是在 spec 之前寫的,用的是一個通用模板,x64-windows 當作 vcpkg triplet(dynamic link)。後來 spec 鎖定的時候,lock-in checklist 驗證了 spec 內部的一致性(open question 解決、scenario 檢查過、無未文件化假設),但沒有驗證 pre-existing 的基礎設施是否符合新的 requirement。
五次「為什麼」分析下來,我看到一件以前沒看過的事:SDD 方法論把 spec 當成只往前看的契約。這個 framing 假設 spec 定義接下來要做什麼。但在真實工作流裡,基礎設施常常是在第一份 spec 之前就寫好的。Spec 也需要當作回頭審視的工具。
修復方式很小:在 lock-in checklist 加第四項,要求對照 pre-existing 基礎設施跟新的 MUST。在 /propose 命令文件加一段「Pre-existing Infrastructure Audit」。把這次事故寫成紀錄,未來的專案就能從這個教訓受惠。
這種方法論的改進是從真實使用裡冒出來的,不是從一開始就設計好的。我自己的個人 kit 有這個缺口,而且只有透過真實專案才浮現,這就是這個開發模式的特性。Kit 就是這樣演化的。
SDD 方法論的縫隙在 phase 過渡時最明顯:drafting 之前、drafting 中、lock 到 implement、archive 到下一個 phase。Lock-in 那一刻是四個之中的一個,也是我先抓到的那一個。
三個反思
偏見是結構性的,不是惡意的
SDD 工具的 web 偏見不是任何特定工具作者的瑕疵。它是「誰在做這些工具、誰在出資、什麼樣的範例專案最容易展示」的結果。修復方式不是去攻擊現有的工具。修復方式是去填鄰近領域的縫隙。OpenSpec 的 ADDED Requirements 格式對我管用,因為它設計得好。我擴展它,不是取代它。
系統 AI 的工作有獨特的工作流需求
Cross-compile pipeline、多 backend 驗證、硬體特定部署、reference implementation 紀律。這些不會消失。隨著 edge AI 部署成長(每支手機都有 NPU、AI 跑在工業感測器上、on-device LLM 規模化),會有更多工程師面對這些工作流。能好好處理它們的工具會有意義。
現在,我用一個個人的 kit 在填這個缺口,因為市面上沒有現成的選項適合我。如果你在類似的領域工作、感受到類似的摩擦,你大概也在做自己的解法。我們也許能交換筆記。
這篇文章的重點不是要說服你用我做的東西。它是要做一個更普遍的主張:你在 Hacker News 跟 YouTube 上看到的那些 SDD 工具,對它們設計目標來說是優秀的,但它們的預設受眾不是你(如果你是把 native code 部署到異質設備上的人)。
這不是問題,是機會。Web/SaaS 圈在 agentic 工具上跑得快,因為他們的工作流很適合(清楚的回饋迴圈、容易驗證、眾所周知的抽象)。系統工程也會走到那一步,但路不一樣。Phase-level 規劃,而不是 feature-level。多維度的驗證,而不是二元的測試。Domain-specific 的決策指南,而不是 language preset。把 cross-compile 跟 on-device 驗證當成一級關注點,而不是事後補救。
如果你在用現成的 SDD 工具時感受過同樣的摩擦,你不孤單,而且你沒有錯。工具沒問題。缺的是適配。自己把適配做出來,記錄下你學到的,然後分享筆記。我會很想聽聽你怎麼看。
關於這篇文章:這是一份個人紀錄,記的是 2025 到 2026 年之間在多個真實專案上發展出來的工作流。Kit、專案案例、事故都是真的。個人專案的名字(objdet-engine、LiveTranslator)是準確的;方法論的細節刻意做了抽象,希望讀者不必綁在我特定的工具上也能用。
提到的工具:OpenSpec、GitHub Spec Kit、Tessl、Everything-Claude-Code、Claude Code、Codex CLI、RTK(rtk-ai/rtk)。提及只為脈絡,不是背書。
메타데이터
- post_id
- fc4f6700c1f8
- slug
- 打造自己的規格驅動工作流-fc4f6700c1f8
- url
- https://medium.com/@allenkuo/%E6%89%93%E9%80%A0%E8%87%AA%E5%B7%B1%E7%9A%84%E8%A6%8F%E6%A0%BC%E9%A9%85%E5%8B%95%E5%B7%A5%E4%BD%9C%E6%B5%81-fc4f6700c1f8
- canonical_url
- https://medium.com/@allenkuo/%E6%89%93%E9%80%A0%E8%87%AA%E5%B7%B1%E7%9A%84%E8%A6%8F%E6%A0%BC%E9%A9%85%E5%8B%95%E5%B7%A5%E4%BD%9C%E6%B5%81-fc4f6700c1f8
- author_url
- https://medium.com/@allenkuo
- status
- ok
- fetched_at
- 2026-06-17 08:20:12