不要假設。不要隱藏困惑。呈現權衡取捨。
在實作之前:
LLM 的核心特性是傾向「完成任務」而非「釐清任務」。當你說「加入匯出功能」,模型會默默假設格式、欄位、範圍,然後直接寫出完整實作。這些假設往往是錯的,但模型不會主動告诉你。
這個原則的核心是把 LLM 從「執行者」變成「協作者」——在動手之前先跟你確認假設。這就像資深工程師收到需求後,不會馬上寫程式碼,而是先問清楚再動手。
關鍵心態:把「困惑」當作正常的開發狀態,而不是失敗。停下來問問題比默默做錯更專業。
def export_users(format='json'):
"""Export all users to a file."""
users = User.query.all()
# 直接假設:所有欄位、本地檔案、JSON/CSV 格式
# 沒問過:範圍?隱私?欄位?資料量?
在實作之前,我需要釐清: 1. **範圍**:匯出所有還是篩選後的子集?(隱私考量) 2. **格式**:瀏覽器下載?背景任務?API 端點? 3. **欄位**:哪些欄位?(可能涉及敏感資訊) 4. **資料量**:通常多少使用者?(影響做法) 最簡單的做法:回傳分頁 JSON 的 API 端點。
到互動範例頁看這個原則的 ❌/✅ 對照:
「好的,我開始把訂單模組拆成微服務:」 1. 建立 orders-service/ 目錄(預設用 Go) 2. 把訂單相關的 model 複製過去(預設:直接複製) 3. 加入 REST API(預設:GET/POST /orders) 4. 設定 Dockerfile 與 CI(預設:與現有 CI 同格式) 5. 把 monolith 的 route 指向新服務(預設:直接切換) —— 沒有一個預設問過使用者,五個預設只要錯一個就白做。
「拆微服務」在動手前必須先回答四個問題,否則拆完也不知道拆對沒有: 1. 邊界:訂單模組的邊界到哪?出貨、庫存、發票算不算訂單? → 提出問題,不是默默選「只要 order table」。 2. 資料:訂單資料仍然共用同一個資料庫嗎? → 這是「拆分成本」的核心權衡,必須讓使用者決定。 3. 通訊:同步 REST 還是異步 event? → 影響一致性模型;先確認哪個符合業務,再談技術。 4. 成功標準:「拆完」如何衡量?哪個指標算達成? → 原則 4 的輸入在這裡就要問清楚。 我的提案(呈現權衡,讓使用者選): - 最保守路徑:先拆「讀取」路徑,寫入路徑後置 → 回滾容易 - 最快速路徑:整體搬移 → 一次到位但回滾困難 - 我建議保守路徑,除非你告訴我必須在一週內上線。
LLM 的本質是next-token prediction——它被訓練成「接續一段文字」。當你丟出「把訂單模組拆成服務」,模型最「順」的接續方式是把程式碼寫完:完成一個任務會產生一個完整的、令人滿意的輸出,而提問會中斷這個接續迴圈。這是訓練資料的統計事實:程式碼範例幾乎都是「需求 → 完整實作」,很少是「需求 → 反問十個問題」。
Token 經濟的推力:釐清需要多輪往返,每輪都消耗 token;RLHF(人類回饋強化學習)在評分時傾向獎勵「一次給出完整答案」的模型,因為那看起來更「有用」。於是「先問清楚再動手」對模型來說是反直覺的、成本更高的行為——你必須把「停下來問」變成明確指令,它才會做。
上下文崩潰(context collapse):在長對話中,模型對早期需求的記憶會淡化(研究稱之為 lost-in-the-middle:模型對上下文中間的內容記得最差)。當它遺忘初始需求,最省力的做法是重新詮釋需求來讓工作繼續——這正是「默默地越做越偏」的底層原因。對策:把關鍵需求寫在專案檔頂端、或每幾個步驟重述一次。
Agent 收到需求後,輸出:「我需要釐清:① 格式?② 欄位?③ 範圍?」——看起來完美遵守原則 1。但同一則訊息的最後卻附上了 export_users(format='csv') 的完整實作,欄位與範圍都預設好了。問問題與實作並存,等於沒有問:模型把「釐清」當作聊天語氣,而不是控制流程的閘門。真正的遵守是「停在此處,等使用者確認才動手」——問題列表是煞車,不是裝飾。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| AI 直接開始寫程式碼,完全沒列假設 | 模型把需求視為明確指令,自動補齊預設 | 明確要求「先列出所有假設,再開始」,把這句話寫進指令開頭 |
| 列了假設,但同則訊息就附上實作 | 假設清單淪為形式,流程沒有真的停下 | 要求「停在此處,等我確認」,並把實作移出該訊息 |
| 問的問題答案它其實都知道 | 「假問題」填充——模型在表演合作,不是真在收集資訊 | 追問「這些問題的答案會改變你的做法嗎?」把沒有資訊量的問題刪掉 |
| 列出的選項不完整或刻意傾斜 | 模型傾向引導到它擅長或最省力的路徑 | 要求「列出所有可行解與各自的權衡」,再讓使用者選 |
| 長對話後需求被「重新詮釋」 | 上下文崩潰,模型遺忘初始需求 | 把關鍵需求固定寫在檔頭/重複聲明,作為不可漂移的錨點 |
收到需求後,先列假設清單: 1. **付款閘道**:Stripe?信用卡直接收?還是要支援第三方支付? → 成本與合規要求完全不同。 2. **幣別**:單一幣別?多幣別需要匯率轉換? 3. **地址**:帳單地址 vs. 寄送地址?是否需要地址驗證? 4. **訂單暫存**:使用者中途離開怎麼辦?購物車要不要持久化? 5. **安全性**:PCI DSS 合規?卡號能進我們的資料庫嗎? → 先停在這裡,把這 5 個問題與權衡列給使用者確認。 確認:用 Stripe Checkout(PCI 合規由 Stripe 處理)、單一幣別、 帳單地址、購物車存 Redis。
第一版只做:選商品 → 填地址 → 送 Stripe Checkout → 回調確認。
先不做:購物車持久化(使用者說不急)、多幣別、優惠券、發票。
# 最小實作 ≈ 80 行
@app.route('/checkout', methods=['POST'])
def checkout():
items = request.json['items'] # 原則 1 確認過的欄位
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{'price': item['price_id'], 'quantity': item['qty']}
for item in items],
mode='payment',
success_url=url_for('success', _external=True),
cancel_url=url_for('cancel', _external=True),
)
return redirect(session.url)
# 沒有 Strategy 模式、沒有抽象類別、沒有配置物件。
# 三個月後要加第二種付款方式時,再提重構。
新增的檔案:checkout.py, templates/checkout.html 修改的檔案:app.py(加一行 route 掛載) 刻意沒動的: - app.py 裡的既有 routes — 格式不一致?不關我的事。 - templates/layout.html 的 CSS — 看到變數名不夠好?不改。 - utils.py 裡「看起來沒用」的舊函式 — 不刪。
計畫與驗證: 1. 建立 Stripe 測試帳號 → 驗證:能用測試卡完成一筆 $1 付款 2. 實作 /checkout 路由 → 驗證:POST 請求回傳 Stripe Checkout URL 3. 實作 /success 回調 → 驗證:付款後跳轉到成功頁、訂單記錄建立 4. 手動端到端測試 → 驗證:選商品 → 填地址 → 刷卡 → 成功頁 → DB 有紀錄 5. 既有測試 → 驗證:全部仍然全綠 「完成」的定義:端到端手動測試通過 + 既有測試無回歸。
效能:未釐清的假設直接轉化為效能問題。例如假設「匯出所有資料」而未確認範圍——資料量從 100 筆到 100 萬筆時,同一個實作從 0.1 秒變成 30 秒甚至 OOM。假設「同步處理」而未確認是否可用非同步——一個慢查詢阻塞整條 API。原則 1 的價值不只是「問對問題」,而是在效能問題被寫入程式碼之前攔截它。
品質:假設錯誤導致的品質問題通常在 code review 後才被發現——但那時修正成本已經升高。例如假設了 CSV 欄位順序與前端不一致,導致前端解析崩潰;假設了 API 回傳格式但下游消費者期待不同結構。品質的核心不是「寫對的程式碼」,而是「在動手之前確認什麼是對的」。
安全:安全假設是最致命的一類。例如假設「token 永遠有效」而未確認過期策略;假設「輸入已經被前端驗證過」而跳過後端校驗;假設「內部 API 不需要認證」——每一個都是 OWASP Top 10 的常見入口。原則 1 在安全場景的關鍵是:把「我假設它是安全的」變成「我確認它的安全機制是什麼」。
Agent 收到「列出所有訂單」的需求,直接寫了 Order.query.all()。初期 500 筆訂單秒回,三個月後 50 萬筆——API 超時。根本原因:Agent 假設了資料量「永遠很小」,沒有問「預期多少筆?需要分頁嗎?」。修復:加上分頁 + 索引,但那已經是三天的緊急加班。
| 面向 | 原則 1 · 編碼前思考 | 相關原則 | 差異說明 |
|---|---|---|---|
| 核心動作 | 停下來、列假設、問問題 | 原則 4(目標驅動) | 原則 1 是「在動手前釐清方向」;原則 4 是「釐清之後把目標變成可驗證的數字」。前者管「問什麼」,後者管「怎麼衡量完成」。 |
| 時機 | 編碼之前的最早階段 | 原則 3(精準修改) | 原則 1 在「什麼都沒寫」之前攔截問題;原則 3 在「已經在改」時防止蔓延。一個是入口閘門,一個是過程中的煞車。 |
| 對抗的偏誤 | Completion bias(完成偏誤)——模型想直接動手 | 原則 2(簡潔優先) | 兩者都反對「多做」,但方向不同:原則 1 反對的是「做錯的方向」(假設錯誤),原則 2 反對的是「做多的範圍」(過度工程)。 |
| 最大價值 | 避免「整週返工」的高成本錯誤 | 原則 4(目標驅動) | 原則 1 防止方向錯;原則 4 確保方向對之後能可靠地完成。兩者搭配:先問對問題,再定義什麼叫「做對了」。 |