| 工具 | Plan | Main | 用途 |
|---|---|---|---|
task_done | ✗ | ✓ | 「我完成了」——終止迴圈 |
code_comment | ✗ | ✓ | 發出一條帶行範圍 + 建議的評審評論 |
file_read | ✗ | ✓ | 讀變更後快照中某檔的一段 |
file_read_diff | ✓ | ✓ | 讀另一檔的 diff 確認跨檔關切 |
file_find | ✓ | ✓ | 依檔名關鍵字定位檔案 |
code_search | ✓ | ✓ | 全倉 grep(git grep) |
task_done 與 code_comment 在 plan 階段有意不可用——plan 是唯讀的。
file_read / file_read_diff / file_find / code_search 讓模型更好理解目前檔的 diff;收集脈絡時發現的問題按設計被忽略。跨檔關切只有在目前檔 diff 中可觀察時,才會成為評論。{ "name": "task_done", "input": { "state": "DONE" } }
agent 看到 task_done 後停止呼叫 LLM,開始處理已累積的 code_comment。state 可為 DONE 或 FAILED。
{
"name": "code_comment",
"input": {
"comments": [{
"content": "`tx.Rollback()` is never deferred — early returns leak the transaction.",
"existing_code": "tx, err := db.Begin()\nif err != nil {\n return err\n}",
"suggestion_code": "tx, err := db.Begin()\nif err != nil {\n return err\n}\ndefer tx.Rollback()"
}]
}
}
comments 是陣列,一次可發多條。每條錨定到 existing_code 片段,OCR 自動算行號。
RE_LOCATION_TASK。比對對空白不敏感。最後手段以 start_line=0 交付——問題是真的,但需自行定位。
{ "name": "file_read", "input": { "file_path": "src/foo.go", "start_line": 10, "end_line": 80 } }
讀變更後形式的一段行(每行以 1 起始行號 + | 前綴)。每次最多 500 行。
{ "name": "file_read_diff", "input": { "path_array": ["src/api/handler.go", "src/db/queries.go"] } }
讀同一變更集中其他檔的 diff。路徑不在變更集則靜默省略。
{ "name": "file_find", "input": { "query_name": "UserService", "case_sensitive": false } }
與每個檔的 basename 做子串匹配,最多 100 條。無匹配回 // The file was not found。
{
"name": "code_search",
"input": {
"search_text": "TODO|FIXME",
"file_patterns": ["*.go", ":(exclude)vendor/"],
"case_sensitive": false,
"use_perl_regexp": true
}
}
由 git grep 驅動,理解 pathspec、遵循 .gitignore。每檔命中上限 100。
| 目標 | file_patterns |
|---|---|
| 所有 Go 檔 | ["*.go"] |
| 除測試外所有 Go | ["*.go", ":(exclude)*_test.go"] |
| 僅一個目錄 | ["src/api/"] |
| 多型別、排除 vendor | ["*.go", "*.ts", ":(exclude)vendor/", ":(exclude)node_modules/"] |
tools.json 刪掉不要的條目,跑 ocr review --tools ./my-tools.json。name、改 description 引導模型(如「讀 file_read 時至少讀變更附近 30 行」)。新增新工具名需在 Go 側接入(internal/tool/definitions.go)——單靠 JSON 無法加新行為。
Plan 階段只能用 file_read_diff、file_find、code_search——三個脈絡工具。task_done 和 code_comment 被禁用。為什麼?Plan 是唯讀分析階段——只收集脈絡,不做決策。這確保 plan 產出是純指引,不附帶評論。
錨定不是一次性嘗試:
existing_codeRE_LOCATION_TASK 請模型重新錨定比對對空白不敏感。最後手段以 start_line=0 交付——問題是真的,但需自行定位。
file_read、file_read_diff、file_find、code_search 都是唯讀脈絡工具。它們讓模型更好理解目前檔的 diff,但收集脈絡時發現的問題按設計被忽略。只有在目前檔 diff 中可觀察的跨檔問題才會成為評論。這是刻意的——避免「噪音評論」。
code_comment 的錨定三階段。延伸閱讀:架構 · 程式碼:internal/tool · MCP 伺服器
你發現 Agent 在審 API 檔時,file_read 只讀變更附近的 10 行——抓不到「這個 handler 依賴的 service 方法定義」,導致評論像瞎子摸象。你要重新描述 file_read,強制 Agent 讀更廣的範圍,並禁用 code_search(你不想它在每次評審都全 repo grep 一遍,token 太貴)。
ocr --help 2>/dev/null | rg -i tool # 找內建 tools.json 位置(或從上游 repo 抓)
cp /path/to/internal/tool/tools.json ./my-tools.json
jq '.tools[] | select(.name=="file_read")' my-tools.json
# 修改 description:
# "讀變更後快照中某檔的一段。當你審查 API handler 時,
# 務必同時讀取該 handler 依賴的 service/mapper 定義範圍(跨 50 行),
# 以理解資料流,而不只是變更附近的 10 行。"
# Step 3 — 禁用 code_search(從陣列刪除)
jq 'del(.tools[] | select(.name=="code_search"))' my-tools.json > /tmp/t.json && mv /tmp/t.json my-tools.json
# Step 4 — 用 --tools 跑評審
ocr review --tools ./my-tools.json --format json --audience agent
開 viewer 的 main_task 泳道,確認:file_read 的 end_line - start_line 變寬、code_search 不再出現。行為驗證比「設定成功」重要。
工具是「Agent 的手」——描述改變行為、禁用改變成本。重新描述 file_read 讓 Agent「讀得更廣」(品質),禁用 code_search 省 token(成本)。這兩個都透過 --tools 的 JSON 達成,不用改 Go 源碼——因為你只改「描述」,沒加「新工具」(加新工具才需要 internal/tool/definitions.go)。
預期產出
my-tools.json: 6 tools → 5 tools (code_search removed)
file_read description: 擴充為「讀 50 行範圍」
review: 4 comments(原 3 條),file_read 平均範圍 12 → 48 行
token: 每檔省 ~15% (code_search 往返消失)
code_comment 的 existing_code 不是直接查行號——它被當成「樣本」,用滑動視窗在 diff 的 hunk 新側/舊側、或整個變更後檔案中找「最像」的位置。比對對空白不敏感(縮排差異不影響)。這讓「模型重複貼了略縮排的程式碼」也能配到——但代價是可能配到「看起來像」的地方。
# Agent 的 existing_code:
"return err"
# 檔案有兩處:
# 函式 A (行 20): if err != nil { return err } ← 真正的問題在這
# 函式 B (行 77): if err != nil { return err } ← 只是長得像
# 滑動視窗先配到第一處 → 評論貼到函式 A 附近 → 看似錨定成功
# 但真正的問題在函式 B —— 評論貼錯了位置,reviewer 看到 A 覺得「沒問題啊」
錨定「成功」≠ 錨定「正確」——短的、重複的 existing_code 會誤配到第一個相似處。隱患:評論存在、行號也非零、但貼在錯誤位置,比「貼不出來」更危險(因為你看不到 start_line: 0 的警告)。防禦:existing_code 應包含「足以區分位置」的上下文(多帶一兩行);配合 viewer 的 re_location_task 泳道觀察是否頻繁觸發。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| 自訂 tools.json 沒生效 | JSON 語法錯誤或 --tools 路徑錯 | jq . my-tools.json 驗證 JSON;確認 --tools 指向對的檔 |
| 禁用工具後 Agent 一直報錯 | Agent 被 prompt 教導「有 code_search 可用」,但執行時找不到 | 禁用工具時同步檢查模板 prompt 是否還提到它(或用 description 引導) |
| 評論行號看似正確其實錯位 | 短的 existing_code 誤配到第一個相似處 | 讓 existing_code 帶更多上下文;看 re_location_task 泳道 |
| file_read 讀了但 Agent 沒用 | 讀取的範圍還是太窄,或 prompt 沒要求 | 把「讀 50 行」寫進 file_read description;調高 max-tokens 給餘裕 |
| 加了「新工具」沒用 | 新工具名需 Go 側接入,JSON 無法加新行為 | 用內建工具重新組合,或改 Go 源碼(internal/tool/definitions.go) |
file_find 的新 description,讓 Agent 在找不到檔時「改用 code_search 的 pathspec 找同 basename」。這是否違反「脈絡工具」原則?