昨天的主軸在於將深度分析的環節撰寫出來。今天進入最後一個環節:產生與交付報告,並補上先前尚未明確設計、可能斷鏈的流程。
這套流程剛跑起來沒多久就遇到:同一個專案、兩份 MR、同時交給 AI 審。當時的設計是同一個 repo 共用一份 clone,於是兩個 AI 得在同一個工作目錄裡各自 git checkout 到自己要審的分支,互相踩掉對方的工作狀態。
今天要收的就是這類東西:報告以什麼形狀存在、檔案放在哪裡、哪些要隔離、哪些要留下來。沒有明確設計好的流程,就會用這種方式提醒你。
我一開始的設計,是由主 AI Agent 直接輸出 Markdown 報告。
正式完成前還會自檢一次;AI Agent 必須把整份報告重新放進 context,修正內容與行號後再輸出,因此又會消耗一輪 token。
發布前自我審查:確認報告內容正確,語氣正面且具建設性。
所以後面我採用的方式是:在完整 MR/branch review 中,各階段確認後要進入主報告的內容,統一寫進一份 report JSON;scanner raw output 則分開保存。要產生發布到 GitLab 的回饋時,再由 Python script 把 report JSON 轉換成 Markdown。
發布前需要問過使用者,使用者同意後才發布。發布後需要提供直接連結,方便使用者點擊。
發布完成後有些資料要回填到 JSON 中,例如本次報告發布在 GitLab 上的留言 ID。
這份報告使用 Pydantic 定義欄位格式,確保 AI 在審查期間生成、寫入的內容,從中間狀態到最終報告都符合預期的結構與資料型態。
另外,整份 Skill 以英文撰寫,並要求 AI Agent 使用英文工作與思考;呈現給使用者的內容,例如輸出在 terminal 上的文字以及最終報告,則統一使用繁體中文(
zh-TW)。這個分工的理由 Day 3 講過:Claude 的 tokenizer 處理中文約為英文的 1.65 倍,skill 全文英文化後從 ~40K 降到 ~29K tokens。這件事主要對 Claude 成立(中文的跨家平均只有 1.02 倍,Anthropic 是離群值)。所以規則這樣切:給機器讀的用英文,省的是 token;報告是給人讀的,用繁體中文。
new/reconfirmed)的 Critical → 結論必為 Request Changes,不得 Approve;沒有仍有效的 Critical、但有仍有效的 Suggestion → Approved with Comments其餘 → Approved。不讓 AI 在「發現了 Critical」和「給出結論」之間自由發揮app/api/account.py:34),不得出現審查機器本機路徑(/tmp/ncr/...)第 1 條後來真的被繞過一次 😅
我不定期會對整套 skill 做一次健檢,通常是體感變動累積多了、或是新模型發布之後。其中一場健檢造出了一份結構合法的報告:掃描驗出的 Critical CVE 只寫在「靜態分析」那一段,沒進 findings 清單。結論只數清單,所以它是 Approved,信心 95%。
修法分兩步:先規定掃描驗出的 Critical 一定要進清單;後來直接取消「掃描結果自成一段」的設計,所有驗證過的發現都進同一條清單,這條繞道就不存在了。
機械規則只鎖得住它數得到的東西。
「報告改成 JSON、渲染交給 script」這個決定值多少,現在講只是省 token 而已。
等到後面把這條流程接上 profiler,我會用秒數跟金額算給你看 🫡
這邊說明 AI 審查期間的檔案路徑規劃,分成工作期間跟報告位置:
/tmp/ncr/{group}-{repo}-mr{iid}/repo
/tmp/ncr/{group}-{repo}-mr{iid}/ 中,獨立於 repo 外為何目錄要帶
iid而非同一個 repo 共用?就是開頭那場撞車,因此改用「一個 MR 一個目錄」進行隔離。
為何要帶 {group}?
同名 repo 可能存在於不同 group 底下(his/abc-backend和lis/abc-backend),只用 repo 名會撞在同一個目錄。為何放 /tmp?
這是刻意的:clone 是用完即棄的工作副本,因此放在執行環境的暫存目錄。Skill 目前沒有額外實作清理流程,檔案何時移除取決於該環境的暫存目錄政策;若執行環境會長期運作,仍需另外設定定期清理機制。
歷史報告是下一輪判定「首次 vs re-review」的依據,屬永久資料,因此不放 /tmp,存放於:$HOME/ncr/{gitlab-group}/{subgroup}/{repo}/
一次掃描的檔名前綴:mr{mr-iid}_from_{source_branch}_to_{target_branch}_{YYYY-mm-dd_HHMM}
注意:branch 名稱中常帶
/(如feature/xxx),組檔名前須將/替換為-,否則會被當成子目錄。
.json
.opengrep.json
.trivy.json
.lint.json(ruff/ty/oxlint)圖中是加入 lint artifact 前的版本;現行歸檔會再多一份
.lint.json。
今天把報告這一段收掉了:在完整 MR/branch review 中,各階段確認後要進入主報告的內容統一寫進同一份 report JSON,scanner raw output 則分開保存;要發布的時候才用 Python script 套模板渲染成 Markdown。這樣做省的不只是 token,更重要的是結論不再由 AI 自由發揮:有仍有效的 Critical 就一定是 Request Changes,這件事由程式決定,不由語氣決定。
另外四條報告硬規則(機械結論、自我完備、對事不對人、只點問題不給出路不合格),以及昨天 CodeGraph 那段「先寫清楚它看不見什麼」,其實是同一個姿態:先講清楚邊界,再決定它能負責什麼。
明天處理讓這一切真的跑起來的東西:GitLab API 要用到哪些端點、Note 跟 Discussion 差在哪裡。