GitLab Duo CLI 進階篇 — 批次自動化、多模型比較、與降噪技巧
上一篇講了 Duo CLI 的基本用法。這篇來聊聊我怎麼把它串進 shell script 做批次分析、怎麼用多模型交叉比對抓盲點、以及怎麼讓 AI 自己幫你過濾垃圾結果。
Contents +
這篇是上一篇入門文的延續。如果你還沒用過 Duo CLI,建議先看那篇。
為什麼要批次跑
先講個故事。
有一次我接手 review 一個跨了十幾個模組的重構 MR。在網頁上一個模組一個模組問 Duo Chat,問到第五個的時候我已經忘記第一個的結論了。而且每次都要重新給 context,因為 Duo Chat 不知道我之前問了什麼(那時候還沒有 session 同步)。
那次之後我就開始寫腳本了。把十幾個模組的 review 任務排成佇列,一口氣跑完,結果存成檔案。我泡杯咖啡回來,所有結果都在那裡等我。
這就是批次自動化的核心價值:把「等 AI 回答」的時間從你的注意力裡解放出來。
最小可行的批次架構
不需要搞得很複雜。我的做法是三個資料夾:
my-review/
├── cells/ # 每個任務一個 JSON 檔
├── state/ # 完成標記(.done 檔)
└── results/ # AI 回覆存在這
一個 cell 長這樣:
{
"id": "auth-module-error-handling",
"target": "/path/to/repo",
"model": "claude-opus-4-6",
"goal": "Review auth 模組的錯誤處理,特別注意 token 過期的 edge case"
}
跑的腳本:
#!/bin/bash
for cell_file in cells/*.json; do
cell_id=$(jq -r '.id' "$cell_file")
# 已經跑過就跳過
[ -f "state/${cell_id}.done" ] && continue
goal=$(jq -r '.goal' "$cell_file")
target=$(jq -r '.target' "$cell_file")
model=$(jq -r '.model' "$cell_file")
echo "⏳ Running: $cell_id"
glab duo cli run \
-C "$target" \
--goal "$goal" \
--model "$model" \
--dangerously-skip-permissions \
> "results/${cell_id}.md" 2>&1
touch "state/${cell_id}.done"
echo "✅ Done: $cell_id"
sleep 30 # 避免打太快
done
echo "全部完成!"
重點是那個 .done 檔。跑到一半斷線了?重跑一次,它會自動跳過已完成的。不需要資料庫,一個 touch 搞定。
我通常在 cell 之間 sleep 30 秒。不是技術上一定要,但實測打太快偶爾會被限流,30 秒是穩定的間隔。
別忘了加 timeout
這是我學到的教訓:有些任務 AI 會想超級久,特別是你給它一個很大的 repo 加一個很模糊的問題。有一次我睡前開始跑 10 個 cell,醒來發現它卡在第 3 個已經跑了 6 小時。
後來我加了 timeout 機制:
# 背景執行 + 計時
glab duo cli run -C "$target" --goal "$goal" \
--model "$model" --dangerously-skip-permissions \
> "results/${cell_id}.md" 2>&1 &
PID=$!
ELAPSED=0
while kill -0 "$PID" 2>/dev/null; do
sleep 10
ELAPSED=$((ELAPSED + 10))
if [ "$ELAPSED" -ge 900 ]; then # 15 分鐘上限
kill "$PID" 2>/dev/null || true
echo "TIMEOUT after 15min" > "results/${cell_id}.md"
break
fi
done
wait "$PID" 2>/dev/null || true
touch "state/${cell_id}.done"
15 分鐘是我目前覺得合理的上限。正常的 review 任務大概 2-5 分鐘會跑完,超過 15 分鐘通常代表它陷入某種迴圈了。標記 timeout、跳過、下一個。
多模型比較的正確姿勢
上一篇提過我會用多模型交叉比對。這篇來分享具體數據。
我做過一次正式的比較實驗:同一個 code review 任務,丟給 9 個模型跑,結果是:
| 模型 | 輸出大小 | 我的評價 |
|---|---|---|
| Claude Opus 4.6 | 10.2 KB | 面面俱到,什麼都講到了,適合第一次看不熟的 code |
| Claude Opus 4.8 | 7.9 KB | 務實導向,直接告訴你該改什麼 |
| Claude Opus 4.7 | 5.9 KB | 極簡,只講結論,適合你只想快速確認 |
| GPT 5.5 | 7.4 KB | 偶爾切入角度完全不同,有驚喜 |
| Gemini 3.1 Pro | 7.1 KB | 結構清晰,範圍切得很精準 |
| Gemini 3.5 Flash | 4.4 KB | 最快,但深度不夠,當快篩用 |
| GPT 5.4 | 0.3 KB | 直接拒絕回答(安全護欄太敏感) |
幾個實用的心得:
不要追求「最好的模型」。 沒有最好,只有最適合。我現在的組合是:
- 快速初篩:Gemini 3.5 Flash(最快,成本最低)
- 全面分析:Claude Opus 4.6(什麼都不想漏掉的時候)
- 第二意見:GPT 5.5(思路跟 Claude 很不一樣)
兩個模型的交集比一個模型的全集有用。 如果兩個思路完全不同的模型都指出同一個問題,那個問題大概率是真的。反過來說,只有一個模型提到的東西,值得多想一下是不是誤報。
寫成腳本就是多跑幾次:
MODELS=("claude-opus-4-6" "kimi-k2" "devstral-2")
for model in "${MODELS[@]}"; do
glab duo cli run -C "$TARGET" \
--goal "$GOAL" \
--model "$model" \
--dangerously-skip-permissions \
> "results/${CELL_ID}-${model}.md" 2>&1
sleep 30
done
git log 錨點:給 AI 一張地圖
這是我從實戰中摸出來的,效果提升非常明顯。
問題是這樣的:你叫 AI「review 這個 repo」,它不知道從哪裡開始。一個幾千個檔案的 repo,它要自己決定看什麼、跳過什麼。結果常常是:花了很多時間在不重要的地方,真正有問題的地方反而沒看到。
解法:先給它一張地圖。
# 撈出特定主題的 commit 歷史
git log --all \
--grep='error\|exception\|retry\|timeout\|fallback' \
--regexp-ignore-case \
--name-only \
--format='%h %cs %s' \
> error-handling-anchors.txt
這份檔案告訴 AI:「這些是最近跟錯誤處理相關的改動,專注看這些。」
glab duo cli run -C ~/project \
--goal "以下是近期跟錯誤處理相關的 commit:
$(head -50 error-handling-anchors.txt)
根據這些 commit:
1. 錯誤處理的 pattern 是否一致?
2. 有沒有某些模組做了 retry 但類似的模組忘了做?
3. 有沒有 exception 被吞掉(catch 了但沒處理)的跡象?"
為什麼效果好?因為 git log --grep 撈出來的是真正被改過的地方。開發者花時間修的東西,代表那裡曾經有問題。AI 從這些點出發去分析「還有沒有類似的問題」,比它自己亂翻精準十倍。
我常用的幾組 keyword:
- 錯誤處理:
error|exception|retry|timeout|fallback - 重構:
refactor|extract|rename|move|split - 效能:
perf|slow|cache|optimize|batch - 技術債:
TODO|FIXME|HACK|workaround|temporary
prompt 驗證條件:降噪 90% 的密技
我用了一陣子之後遇到的最大痛點不是「AI 找不到問題」,而是「AI 找到太多問題,大部分是假的」。
一個 code review 任務回報 20 幾個 finding,你一個一個看,結果 18 個是「嗯理論上是個問題但實際上不會發生」或「已經有其他機制處理了」。浪費的時間比不跑 AI 還多。
轉捩點是我開始在 prompt 尾端加驗證條件:
glab duo cli run -C ~/project \
--goal "Review 這個模組的錯誤處理 pattern。
回報前,每個發現都要通過以下檢查:
1. 在預設設定下這個問題真的存在嗎?(不是只有特殊設定才觸發)
2. 有沒有現有的防護機制已經處理了?(看上下文有沒有 catch / retry / guard)
3. 影響範圍是否超出一般使用?(不是要極端操作才碰到的)
4. 這個問題是真的會造成影響的,不是 style 偏好?
不通過的標為「不適用 — 原因:___」。
只詳細報告通過全部四項的發現。"
實測結果:某次跑出 21 個結果,加上驗證條件後剩 2 個。AI 自己幫你把 19 個噪音過濾掉了,每個都附上為什麼不適用的理由。你只要看那 2 個真正重要的。
這個技巧的精髓是:讓 AI 做兩次判斷。 第一次找問題,第二次驗證問題是否真的成立。兩次判斷之間的落差,就是你省下的人工篩選時間。
驗證條件可以根據場景客製化。做 code review 和做技術債盤點的條件不一樣,但結構相同:給 AI 一組「這個 finding 值得回報」的判斷標準。
跟其他工具的搭配
最後講一下工具鏈的搭配。我不是只用 Duo CLI,我是把它放在一個更大的流程裡。
| 階段 | 工具 | 為什麼 |
|---|---|---|
| 廣度掃描 | Duo CLI | 多模型、可批次、走 GitLab Credits 最省 |
| 深度分析 | Claude Code | 有記憶、有工具鏈、適合來回多輪 |
| 機械性驗證 | Codex CLI | 沙盒執行、適合跑確認腳本 |
流程大概是:
- Duo CLI 把 repo 的模組批次掃一遍(廣度),用 2-3 個模型交叉看
- 結果裡標記「需要深入看」的部分,丟給 Claude Code 逐一挖(深度)
- 最後整理成結論,確認修正方向
Duo CLI 在這裡的角色是「第一道濾網」。它不需要做得完美,它需要做得快、做得廣。漏掉的讓下一輪的深度分析補回來。
成本方面,Duo CLI 走 GitLab Credits,對有 Enterprise seat 的人是最划算的入口。把大量的初篩工作交給它,省下其他工具的使用量。
一些零散但實用的小提醒
- 結果存 .md 比 .txt 好。 AI 的回覆通常有 markdown 格式,存成 .md 之後用任何 markdown viewer 打開就很好讀。
- 用
GITLAB_DUO_MODEL環境變數設預設模型。 不用每次都打--model。 - 大 repo 拆模組跑。 一次丟整個 repo 效果通常不好。指定子目錄或用
--ai-context-items只注入相關檔案,分析會更集中。 - prompt 越具體越好。 「review 這段 code」不如「review 這段 code 的錯誤處理,特別注意 timeout 的 edge case」。
- 留一份 prompt 模板。 好的 prompt 值得重複用。我有一個資料夾專門放常用的 prompt 模板,不同任務類型套不同模板。
結語
Duo CLI 的進階玩法核心就三件事:批次化、多模型、降噪。把這三個搞定,你就從「一次問一個問題」變成「一次掃一整個 codebase」。
這不是什麼黑魔法,就是把 CLI 工具該有的可組合性用出來。GitLab 把 AI 放進了命令列,shell script 的威力自然就跟著來了。
希望這些 pattern 對你有用。如果你有更好的做法,歡迎在社群分享 — 我也還在持續優化我的流程。