一個需求從 Prompt 到 Production 的完整旅程
這是「Agentic Engineering 實戰手冊」系列的第七篇。上一篇:Agent 產出品質保證
先說清楚:這是從 Commit 還原的案例
前幾篇我們聊了 Context Engineering、Spec-Driven Development、品質保證。每篇都有它的理論框架。
但理論歸理論,你可能最想看的是實際操作起來到底長什麼樣。
這篇用一個真實需求回答:在 Astro 部落格裡加上「文章閱讀進度條」和「預估閱讀時間」。不過它不是當時逐分鐘寫下的 live log,而是根據 2025 年 12 月 21 日的兩次 commit、現存原始碼與我的工作筆記,事後還原成一條完整流程。
閱讀進度條和閱讀時間其實分成兩個 commit 上線,後者還在 2026 年 7 月修過中文計數。所以下面的時間是當時的粗略紀錄,prompt 也是整理後的版本;我不會把重建內容假裝成終端機原始輸出。
任務背景
需求來源:我自己在分析部落格的 analytics 時發現,長文的跳出率偏高。假設之一是讀者不知道文章有多長、讀到哪裡了,缺乏「進度感」。
功能描述:
- 頁面頂部顯示閱讀進度條(scroll-based progress bar)
- 文章開頭顯示預估閱讀時間(estimated reading time)
- 只在 blog post 頁面顯示,其他頁面不要
複雜度評估:中等。涉及一個新 component、一段 client-side JS、一個 build-time 計算。不是 trivial,但也不需要架構重構。
為什麼選這個任務示範:它有代表性——有 UI component、有邏輯計算、有跨 layer 的整合(build-time + client-side)。而且它有明確的決策點和至少一次 iteration。
Step 1: 把當時的需求補成可驗收 Spec
如果用上一篇的 template 回頭整理,spec 會長這樣:
## Task: Blog Reading Progress & Estimated Read Time
### Goal
在 blog 文章頁面加兩個功能:
1. 頁面頂部固定的閱讀進度條(隨滾動更新)
2. 文章 metadata 區塊顯示預估閱讀時間
### Context
- Astro 5 + MDX + Tailwind CSS 4 + TypeScript
- Blog layout: src/layouts/BlogLayout.astro
- 現有 design tokens: --color-primary, --color-bg-primary
- Dark mode 透過 .dark class 切換
- 已有 View Transitions,script 要用 Astro 的頁面生命週期重新初始化,並避免重複綁 listener
### Constraints
- 不引入任何 npm dependency
- 進度條用 CSS custom properties + Tailwind
- 閱讀時間在 build time 計算,不在 client side
- 中文內容以每分鐘 400 字計算(非英文的 200 wpm)
- 進度條只在 blog post 頁面出現
- 進度條不能蓋住 navigation bar
### Files to modify
- src/layouts/BlogLayout.astro(加入 component)
- 可新增 src/components/blog/ReadingProgress.astro
- 閱讀時間邏輯放在 src/lib/utils.ts,由頁面把結果傳給 layout
### Verification Criteria
1. 滾動頁面時,頂部進度條從 0% 填充到 100%
2. 進度條在到達文章底部時是 100%,在頂部是 0%
3. 顯示格式:「約 N 分鐘閱讀」
4. 2000 字文章 → 顯示「約 5 分鐘閱讀」
5. Dark mode 下進度條顏色正確
6. 在非 blog 頁面(如 /about)不顯示進度條
7. npm run build 通過,無 TypeScript 錯誤
### Out of Scope
- 不做 reading analytics(不追蹤用戶實際閱讀行為)
- 不做 bookmark / save progress 功能
- 不做 table of contents(之後另外做)
真正重要的決策:
- 中文 400 字/分鐘而非英文 200 wpm——這個如果不寫,agent 很可能沿用常見的英文閱讀速度算法
- 「不引入 npm dependency」——防止 agent 引入 reading-time 之類的 library
- 「進度條不能蓋住 navigation bar」——這是一個很容易被忽略的 UI 細節
- View Transitions 相關的提醒——持久化 DOM 與頁面導航會改變 script 的初始化時機,應依 Astro 文件使用對應 lifecycle event,並確認舊 listener 有清理
Step 2: 準備必要 Context
CLAUDE.md 已經有了大部分必要的 context(tech stack、project structure、conventions)。額外需要的只有兩件事:
- 讓 agent 看一下
BlogLayout.astro的現有結構,知道 component 要插在哪裡 - 確認 design tokens 的變數名
這些不需要我手動貼——agent 會自己 Read 相關檔案。但在 spec 裡提到「design tokens: —color-primary」是有幫助的,讓 agent 知道去找什麼。
Step 3: 讓 Agent 跑一輪,再看完整 Diff
我把 spec 貼給 Claude Code,讓它先完成一輪探索與實作,再回來看完整 diff。
這不是「agent 沒人管就一定做得更好」。如果它碰到高風險檔案、開始擴張 scope,當然要立刻停;對這種隔離、可回滾的小功能,我只是不想因為每個小選擇都插嘴,讓它無法交出可整體 review 的版本。
以下是根據 diff 重建的行動順序,不是逐字 transcript:
Agent 行動記錄
00:00-02:00 — 探索 codebase
Agent: 讀取 BlogLayout.astro... 了解現有結構
Agent: 讀取 global.css... 找到 design tokens
Agent: 讀取 BaseLayout.astro... 了解 nav 的 position
Agent: 讀取已有的 blog component... 理解命名慣例
02:00-08:00 — 建立 ReadingProgress.astro
Agent: 建立 src/components/blog/ReadingProgress.astro
- Fixed position bar at top of page
- Uses --color-primary for fill color
- z-index lower than nav bar
- Client-side script for scroll tracking
- Handles View Transitions with astro:after-swap
第二個 commit — 加入閱讀時間
Agent: 修改 src/lib/utils.ts 與文章頁
- getReadingTime(post.body) 在 build time 計算
- 結果透過 props 傳到 BlogLayout 與文章卡片
- 初版先移除 frontmatter、code block、HTML tag 與部分 Markdown 符號
接著 — 整合到 BlogLayout.astro
Agent: 修改 BlogLayout.astro
- Import ReadingProgress,接收 readingTime prop
- ReadingProgress placed after nav, before main content
- Reading time placed in article header metadata section
- 頁面使用 post.body 計算,不是不存在的 ReadingTime component 或 compiledContent API
20:00-25:00 — 自我驗證
Agent: npm run build... ✅ 通過
Agent: 檢查 TypeScript... ✅ 無錯誤
Agent: 在 dev server 確認 progress bar 行為...
進度條 commit 的 diff 是 2 個檔案、133 行;閱讀時間則和 view count、文章卡片一起進到另一個較大的 commit。把它們說成單一 25 分鐘任務會比 git history 更漂亮,但不準確。
Step 4: Review 真正抓到什麼
Git history 留下最有價值的反例,是閱讀時間的初版用空白切詞。英文勉強可用,中文幾乎沒有空白,一篇長文也可能只算成一兩個「word」。這個問題直到 2026 年 7 月才改成 CJK 字元與拉丁單字分開計算。
如果當時就把「2000 個中文字要顯示約 5 分鐘」寫成測試,這個 bug 不會留半年。修正 prompt 可以很短:
getReadingTime 目前用空白切詞,會嚴重低估中文文章。
請把 CJK 字元與拉丁單字分開計數:中文 400 字/分鐘、英文 200 words/min,
至少 1 分鐘。保留移除 frontmatter、code block 和 HTML tag 的處理,並補中文、英文、混合內容測試。
這個案例也提醒我:npm run build 通過,只能證明程式可建置,不能證明中文閱讀時間算對。Verification criteria 必須真的覆蓋使用者行為。
Step 5: CI/CD & Deploy
程式完成後,才進入自動化流程。以下 commit message 是整理版;實際上兩個功能分開提交:
# Commit
git add src/components/blog/ReadingProgress.astro \
src/layouts/BlogLayout.astro
git commit -m "feat(blog): add reading progress bar and estimated reading time
- Fixed progress bar at page top, scroll-synced 0-100%
- Handles Astro View Transitions correctly"
Push 之後由 GitHub Actions 驗證,再部署到 Cloudflare Workers。CI 現在包含 format、ESLint、Astro check、內容檢查、build 與測試;當時的 workflow 較簡單,不能把今天的檢查項目倒填回 2025 年的 log。
在 preview 或 production 前至少手動驗證:
- 長文(3000 字)→ 顯示「約 8 分鐘閱讀」 ✅
- 滾動 → progress bar 正確填充 ✅
- Dark mode → 顏色正確 ✅
- /about 頁面 → 沒有 progress bar ✅
- 手機寬度 → 3px 高度 ✅
這些項目通過,才適合往 production 走。
時間對比:只代表這一次
這次的 Agentic Workflow
| 步驟 | 時間 | 誰做的 |
|---|---|---|
| 寫 Spec | 8 分鐘 | 我 |
| Context 準備 | 3 分鐘 | 我 |
| Agent 執行 | 25 分鐘 | Agent(我去倒咖啡) |
| Review | 7 分鐘 | 我 |
| Feedback + 修正 | 5 分鐘 | 我 + Agent |
| CI/CD + 驗證 | 12 分鐘 | 自動化 |
| 總計 | ~60 分鐘 |
表格能看出每一步花多久,但看不出時間怎麼接在一起——畫成流程才看得出來,這一小時其實是這樣跑的:
flowchart TD
S1["Step 1 寫 Spec<br/>8 分鐘・我"] --> S2["Step 2 Context 準備<br/>3 分鐘・我"]
S2 --> S3["Step 3 Agent 執行<br/>25 分鐘<br/>Agent(我去倒咖啡)"]
S3 --> S4["Step 4 Review<br/>7 分鐘・我"]
S4 -->|"沒問題"| S5["Step 5 CI/CD 驗證<br/>12 分鐘・自動化"]
S4 -->|"抓到問題"| FB["Feedback 修正<br/>5 分鐘<br/>我 + Agent"]
FB -->|"回頭再跑一次"| S3
S5 --> TOTAL["總計<br/>約 60 分鐘"]
圖裡看得出一件表格沒直接講的事:我自己動手的三段——8 分鐘 Spec、7 分鐘 Review、5 分鐘 Feedback 修正——單獨看都不到 10 分鐘,但卡在 25 分鐘 Agent 執行的前後,才是那 25 分鐘能不能一次跑對的關鍵;而 Feedback 修正繞回 Agent 執行那條線,代表這一圈也可能不只跑一次,這次的 60 分鐘只是繞了一圈就過關的結果。
如果用傳統方式
| 步驟 | 時間 |
|---|---|
| 理解需求 + 研究做法 | 30 分鐘 |
| 寫 ReadingProgress component | 45 分鐘 |
| 寫 ReadingTime component | 30 分鐘 |
| 整合到 BlogLayout | 20 分鐘 |
| 處理 View Transitions | 30 分鐘 |
| 寫 CSS / responsive | 20 分鐘 |
| 測試 + debug | 45 分鐘 |
| CI/CD + 驗證 | 12 分鐘 |
| 總計 | ~4 小時 |
依我當時的估算,這類任務大約從半天縮到一小時。不過這不是 benchmark:需求熟悉度、既有 component、CI 與我對 Astro 的熟悉程度都占了便宜。它能證明這次流程有效,不能證明 coding agent 普遍快四倍。
但不是每個任務都這麼理想
公平地說,這是一個「適合 agentic workflow」的任務——需求明確、scope 有限、技術棧 agent 很熟。
以下是 agentic workflow 不太適合的情況:
| 場景 | 原因 | 建議 |
|---|---|---|
| 全新的架構設計 | Agent 未必掌握你的業務 constraint | 你主導取捨,agent 協助探索與實作 |
| 跨多個 repo 的變更 | Context 太分散 | 拆成每個 repo 一個 task |
| Debug 未知的 production issue | 需要即時互動和直覺 | 你帶頭,agent 輔助 |
| 涉及敏感權限的操作 | 安全風險 | 你自己做,agent 不碰 |
| 第三方 API 整合(文件不完整) | Agent 可能 hallucinate API | 你先確認 API,agent 來寫整合 |
每個決策點的標注
回顧整個流程,有幾個關鍵的決策點值得標記:
決策 1:寫 Spec 還是直接叫 Agent 做?
選擇:寫 Spec。
原因:這個功能涉及 UI + 計算 + 整合,如果不寫 spec,agent 很可能做出我不想要的東西(比如用 npm library、用英文 wpm、progress bar 蓋住 nav)。8 分鐘的 spec 投資,預防了至少 1 小時的拆除工作。
決策 2:離開電腦 vs 盯著 Agent 做
選擇:離開。
原因:這是低風險、可回滾的小功能,先拿到一份完整 diff 比每幾分鐘調整細節容易 review。若 agent 開始動部署、權限、資料或不在 scope 的檔案,我會立即介入。
決策 3:怎麼給 Feedback
選擇:描述問題 + 給方向,不規定做法。
原因:我指出「目前的字數計算來源不對」,並要求它回到 Astro 實際提供的文章內容,而不是指定一個我沒確認過的 API。最後實作直接用 post.body 計算,也避開了為了修 regex 又疊一層 workaround。
決策 4:這個 PR 是合成一個還是拆兩個?
選擇:合成一個。
原因:ReadingProgress 和 ReadingTime 雖然是兩個 component,但它們服務同一個 feature,而且修改的文件有重疊(都要改 BlogLayout.astro)。拆成兩個 PR 反而增加 overhead。
流程的核心公式
把整個流程提煉成一個公式:
Agentic Workflow =
高品質 Spec(10 分鐘)
+ 充足 Context(CLAUDE.md + codebase)
+ 有邊界的執行(低風險時讓 agent 完整跑一輪)
+ 精準 Review(focus 在 agent 特有的錯誤模式)
+ 快速 Feedback Loop(描述問題 + 給方向)
每一步都不難。難的是紀律——動手前先寫清楚驗收,執行時看住邊界,完成後別因為 diff 很工整就省略 review。至於 spec 要寫 3 分鐘還是 30 分鐘、要不要中途介入,都應由任務風險決定。
這個紀律,就是 Agentic Engineering 和 Vibe Coding 的分界線。
Takeaway
-
Agentic workflow 的核心是「前置投資」——Spec 和 Context 的品質決定後續效率。8 分鐘的 spec + 3 分鐘的 context 準備,讓 25 分鐘的 agent 執行幾乎零障礙。
-
這個案例的速度提升不能直接外推——它受益於範圍小、既有架構清楚、容易回滾。把它當工作流範例,不要當四倍生產力的普遍證明。
-
最重要的技能不是寫 prompt,是知道什麼時候介入、什麼時候放手——低風險任務可以等完整 diff,高風險或 scope 漂移要立刻停。放手從來不等於離席不管。