本日核心價值 (Core Focus): 把開發者指令拆成
prompts/模組(review / sql / test / refactor),用 YAML 索引與 Python 載入器按標籤組裝,讓同一套 Output contract 服務多種任務,而不是每人一份聊天收藏。
概念說明與實戰情境 (Overview)
團隊若把「Code Review 規則」複製進每一次對話,規則會漂移:有人要嚴重程度表,有人只要口語建議。Prompt 模組化把穩定規則存成檔案,任務描述才當執行期輸入。載入器依標籤拼接模組,輸出仍走 Day 03 的 JSON 契約。今天先落地四個核心模組:review、sql、test、refactor,並用索引檔避免腳本硬編碼路徑。
關鍵操作與範例 (Implementation & Example)
目錄只放「會重複出現的規則」,不放單次需求。單次 Task 從 stdin 或 CI 變數進來。建議結構:
prompts/
_contract.md # 共用 Output contract
review.md # Code Review:嚴重程度表
sql.md # PostgreSQL 變更規則
test.md # 測試撰寫與 Edge Case
refactor.md # Refactoring 邊界
index.yaml # 模組 id、標籤、路徑
index.yaml 是模組的 API。載入器只讀這個檔,不掃目錄亂序拼接。tags 用於組合:例如 review + sql 處理「含 Migration 的 PR」。
version: 1
always:
- id: contract
path: prompts/_contract.md
tags: [core]
modules:
- id: review
path: prompts/review.md
tags: [review, pr]
- id: sql
path: prompts/sql.md
tags: [sql, postgres]
- id: test
path: prompts/test.md
tags: [test]
- id: refactor
path: prompts/refactor.md
tags: [refactor]
共用契約 _contract.md 對齊 CodeChangePlan,避免各模組各發明一種 JSON。
## Output contract
Return ONLY JSON matching CodeChangePlan:
summary, blocked, questions, files[], risk, test_command, sql_needed, sql
files[].action must be create | modify | delete
risk must be low | medium | high
No markdown fences. No source code in this response.
If this is a review task, still fill files[] with the files you inspected
or would change; put findings in summary as a markdown table is forbidden
here — use the review module's JSON findings field only when the task
loader asks for review_report (see review.md). For plan tasks, ignore
findings and follow CodeChangePlan only.
Review 模組必須產出可掃描的嚴重程度表,而不是「建議再想想」。以下 review.md 要求表格欄位固定,方便貼進 PR 或轉成之後的 JSON。
## Module: review
You are reviewing a C# + PostgreSQL diff.
Find issues in these categories only:
- correctness
- security
- test-gap
- sql
For each finding, emit a row with:
| severity | category | file | line | issue | fix |
severity enum: blocker | major | minor
If no issues, output one row: minor / correctness / - / - / none / n/a
Do not suggest drive-by Refactoring.
Do not approve when any blocker exists.
After the table, also emit CodeChangePlan JSON if fixes are required;
otherwise set files=[] and blocked=false with summary "no code change".
sql.md、test.md、refactor.md 保持短、可執行:
## Module: sql
PostgreSQL only.
Prefer additive migrations. Do not DROP COLUMN unless TASK says so.
Every SQL change sets sql_needed=true and includes the statement in sql.
Index names: idx_<table>_<columns>.
Reject MySQL functions (IFNULL, LIMIT x,y). Use COALESCE and LIMIT/OFFSET.
## Module: test
Add or extend tests that reproduce the bug before the fix.
Cover null, empty list, max decimal, and discountRate 0 / 1.
test_command must be a real command: `dotnet test` or `pytest`.
Do not snapshot entire HTTP payloads unless TASK requires it.
## Module: refactor
Behavior must stay identical. If behavior changes, abort and set blocked=true.
No public API rename. No new dependencies.
If the Task is a feature, do not load this module.
載入器把 always 模組放最前,再依 --tags 附加。最後接 Task。順序穩定,模型才不會有時先看到 SQL、有時先看到 Review。
from __future__ import annotations
import argparse
import sys
from pathlib import Path
import yaml
ROOT = Path(__file__).resolve().parent
def load_index() -> dict:
return yaml.safe_load((ROOT / "prompts" / "index.yaml").read_text(encoding="utf-8"))
def read_module(rel: str) -> str:
text = (ROOT / rel).read_text(encoding="utf-8").strip()
if not text:
raise SystemExit(f"fail closed: empty module {rel}")
return text
def assemble(tags: set[str], task: str) -> str:
index = load_index()
parts: list[str] = []
for item in index["always"]:
parts.append(read_module(item["path"]))
selected = []
for item in index["modules"]:
if tags & set(item["tags"]):
selected.append(item)
if not selected:
raise SystemExit(f"fail closed: no modules match tags {sorted(tags)}")
for item in selected:
parts.append(read_module(item["path"]))
parts.append("## Task\n" + task.strip())
return "\n\n".join(parts)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--tags", required=True, help="comma tags, e.g. review,sql")
args = parser.parse_args()
tags = {t.strip() for t in args.tags.split(",") if t.strip()}
task = sys.stdin.read()
if not task.strip():
raise SystemExit("fail closed: empty task")
sys.stdout.write(assemble(tags, task))
使用方式:
python assemble.py --tags review,sql <<'EOF'
Review PR diff for OrderService discountRate. Failing test:
GetTotal_applies_discount_to_subtotal. Flag missing index if SQL is added.
EOF
組裝後的 Prompt 仍要通過 Day 03 閘門:先得到合法 JSON,再決定要不要進 Codex。Review 若出現 blocker,CI 應把 PR 標成不可合併,即使模型同時給了「看起來能過」的 CodeChangePlan。sql 模組管方言與遷移方向,不能代替 DBA 核准 DROP。test 模組強迫 test_command 在 repo 根目錄可執行。refactor 的 blocked=true 是功能:行為一變就該停,而不是改完再補測試圓謊。四個模組各管一件事,載入器只負責拼接,不負責發明新契約。
組合規則建議寫進團隊 README,避免標籤爆炸:
| 場景 | tags | 預期 |
|---|---|---|
| 功能實作 | test |
計畫 + 測試指令 |
| PR 檢視 | review |
嚴重程度表;blocker 不可合併 |
| 含 Migration | review,sql |
同時抓 SQL 方言與正確性 |
| 純整理 | refactor,test |
行為不變;仍要測試指令 |
模組要版本化:改 review.md 的 severity enum 等於改 API。用 git 審核 Prompt 變更,不要在聊天裡「口頭更新規則」。Day 05 的 AGENTS.md 只放 repo 級耐久規則(如何跑測試);任務型指令繼續放 prompts/,兩者不要重複貼同一段長文。
CI 可把 assemble.py 當建置步驟:依 PR 標籤決定 tags,組裝結果寫入產物再送給模型。如此一來,Review 規則的變更會出現在 git blame,而不是出現在某個人的 ChatGPT 歷史。模組檔保持短:超過一螢幕就該拆檔,而不是繼續往 review.md 堆例外條款。載入器是唯一入口;應用程式禁止直接 open("prompts/review.md"),否則 index.yaml 的 tags 與順序會失效。
Review 嚴重程度表要能被機器掃描。約定 blocker 必須擋合併、major 必須有對應測試或明確風險接受、minor 可延後。表格欄位固定為 severity / category / file / line / issue / fix,不要讓模型改成「建議 / 說明 / 備註」。若 diff 含 SQL,tags 必須同時包含 sql,否則 PostgreSQL 方言規則根本不會進 Context,模型卻仍可能寫出 IFNULL。這不是模型問題,是組裝漏模組。
新增模組前先問三個問題:這段文字是否每週重複?有沒有可測試的輸出契約?會不會與既有模組搶 Output contract?三個都肯定才加檔。個人偏好(空行風格、註解語氣)不要進指令庫;那會讓每次 Review 變成格式戰爭,掩蓋正確性與資安。
注意事項與常見失敗 (Pitfalls)
review.md 要表格、_contract.md 要純 JSON,模型會兩個都做不好。修法:契約模組只定義一種機器輸出;表格若需要,規定放在 summary 內的固定欄位字串,或另開 review_report 任務型 Schema(下一階段再拆),不要兩個契約並行且未說明優先序。本例以「先表後 JSON、JSON 仍必填」寫在 review 模組末段。os.listdir 無序拼接: 同一 tags 每次順序不同,輸出漂移。修法:只走 index.yaml 陣列順序。review.md: 模組變成專案日記。修法:模組只放穩定規則;單次內容走 Task / Context 檔。pg 與 sql 並存): 有人載到模組、有人載不到。修法:索引裡每個模組 tags 列在文件表;未知 tag 直接 fail closed(如上載入器)。refactor;載入器可加互斥檢查(feature 與 refactor 不能同時出現)。本日總結 (Takeaways)
prompts/ 放穩定規則,Task 放單次需求;用 index.yaml 當模組 API。review.md、sql.md、test.md、refactor.md + 共用契約。明日預告 (Next)
明日進入 Day 05 Codex 基礎入門:如何在沙箱環境 (Sandbox) 中獨立執行與驗證 Code,把組裝好的計畫真正丟進 Codex:安裝 CLI、選對 sandbox mode,並跑 generate → pytest → fix 迴圈。