iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
ChatGPT & Codex

ChatGPT + Codex 打造高效能 AI 開發工作流系列 第 4

Day 04: 高效 Prompt 模組化:打造可複用的 AI Coding Assistant 指令庫

  • 分享至 

  • xImage
  •  

Day 04: 高效 Prompt 模組化:打造可複用的 AI Coding Assistant 指令庫 (Modular Prompt Library)

本日核心價值 (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.mdtest.mdrefactor.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 標成不可合併,即使模型同時給了「看起來能過」的 CodeChangePlansql 模組管方言與遷移方向,不能代替 DBA 核准 DROPtest 模組強迫 test_command 在 repo 根目錄可執行。refactorblocked=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)

  • 模組互相覆寫 Output contract: review.md 要表格、_contract.md 要純 JSON,模型會兩個都做不好。修法:契約模組只定義一種機器輸出;表格若需要,規定放在 summary 內的固定欄位字串,或另開 review_report 任務型 Schema(下一階段再拆),不要兩個契約並行且未說明優先序。本例以「先表後 JSON、JSON 仍必填」寫在 review 模組末段。
  • 載入器 os.listdir 無序拼接: 同一 tags 每次順序不同,輸出漂移。修法:只走 index.yaml 陣列順序。
  • 把單次需求寫進 review.md 模組變成專案日記。修法:模組只放穩定規則;單次內容走 Task / Context 檔。
  • tags 同義詞(pgsql 並存): 有人載到模組、有人載不到。修法:索引裡每個模組 tags 列在文件表;未知 tag 直接 fail closed(如上載入器)。
  • Refactoring 模組與功能 Task 一起載入: 模型會在加折扣時順便改命名。修法:功能任務不要加 refactor;載入器可加互斥檢查(featurerefactor 不能同時出現)。

本日總結 (Takeaways)

  • prompts/ 放穩定規則,Task 放單次需求;用 index.yaml 當模組 API。
  • 最小組合:review.mdsql.mdtest.mdrefactor.md + 共用契約。
  • 載入器依 tags 拼接,缺模組或空 Task 就 fail closed。
  • Review 輸出固定嚴重程度表(blocker / major / minor),禁止含糊建議。
  • Prompt 檔與程式碼一樣走 git;改 enum 就是改契約。

明日預告 (Next)
明日進入 Day 05 Codex 基礎入門:如何在沙箱環境 (Sandbox) 中獨立執行與驗證 Code,把組裝好的計畫真正丟進 Codex:安裝 CLI、選對 sandbox mode,並跑 generate → pytest → fix 迴圈。


上一篇
Day 03: 結構化輸出工作流 (Structured Output):利用 JSON Schema 實現穩定 API 對接
系列文
ChatGPT + Codex 打造高效能 AI 開發工作流4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言