Skip to content
海博賽特
海博賽特
最初的旅程:一個人,也能啟動一個世界

GitLab Duo CLI 進階篇 — 批次自動化、多模型比較、與降噪技巧

上一篇講了 Duo CLI 的基本用法。這篇來聊聊我怎麼把它串進 shell script 做批次分析、怎麼用多模型交叉比對抓盲點、以及怎麼讓 AI 自己幫你過濾垃圾結果。

For English readers, switch to the English version.EN
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 沙盒執行、適合跑確認腳本

流程大概是:

  1. Duo CLI 把 repo 的模組批次掃一遍(廣度),用 2-3 個模型交叉看
  2. 結果裡標記「需要深入看」的部分,丟給 Claude Code 逐一挖(深度)
  3. 最後整理成結論,確認修正方向

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 對你有用。如果你有更好的做法,歡迎在社群分享 — 我也還在持續優化我的流程。