跳到內容

內建代理工具

這些是內建代理執行階段直接登錄的伺服器端繫結工具——語言伺服器、結構化搜尋、除錯工具、核心、提問與工作程序控制——有別於它橋接進來的 MCP 工具,以及來自執行環境的通用檔案、搜尋與 shell 工具。它們不在 MCP 登錄表中:外部的 MCP 用戶端看不到它們,因為每一個都繫結到一個存活的伺服器端資源——語言伺服器行程、除錯配接器、直譯器——而那個資源屬於某一次執行。

這裡的每一個工具,都只有在它的後端資源已為該次執行接上時才會登錄——語言伺服器或除錯監督程式、tree-sitter 剖析器、核心工廠、提問埠、vibe 名冊。針對這次簽出偵測語言伺服器或除錯配接器,是另一項較晚進行的檢查(status / adapters):這些工具會留在工具介面上,讓代理可以問為什麼什麼都不可用,而不是憑空猜測。

每個工具都會宣告一個層級,而層級決定的是哪些介面看得到它——不只是它是否會跳出提示。計畫模式與唯讀子代理的上限是 read,因此 write 或 exec 層級的工具,會完全不存在於它們的結構描述中。

層級 意義 這裡的工具
read 沒有副作用 lsp、ast_grep、ask_user、五個 vibe_*
write 會變更檔案 lsp_rename、ast_edit、resolve
exec 會啟動或控制一個行程 debug、eval

當執行具有語言伺服器監督程式與工作目錄時登錄。伺服器是否真的能作答,是在呼叫當下以交集方式偵測的:根標記必須存在,而且伺服器執行檔必須能解析得到。只有其中一項成立,就代表不會有任何伺服器啟動——status 會如此說明——而不是默默猜測。

位置是以 line 加上 symbol 子字串來指定,永遠不靠欄。被要求提供欄位的模型只能用猜的,而猜錯會解析到同一行上的另一個符號——一個看起來正確、實際上卻在講別的東西的答案。在導覽動作中省略 symbol 是錯誤,而不是猜測第一欄。

動作 引數 傳回
diagnostics file(或 "*") 錯誤與警告;"*" 改為執行專案檢查器
definition file, line, symbol 符號宣告的位置
type_definition file, line, symbol 其型別宣告的位置
implementation file, line, symbol 具體實作
references file, line, symbol 每一處使用,上限 50
hover file, line, symbol 簽章與文件註解
symbols query 或 file 以 query 進行工作區搜尋(上限 100);以 file 取得文件符號(上限 200)
code_actions file, line, symbol 伺服器提供的快速修正
status — 哪些伺服器正在執行,以及各自對應的根
reload — 為這個根重新啟動伺服器

真正的收穫不是這個工具。write、edit 和 apply_patch 都被包了起來:寫入之後,檔案會經過語言伺服器同步並儲存,而且只有新產生的診斷會併入工具結果。代理會在建置壞掉的那一刻就知道自己弄壞了它,不必記得要去問。

已回報過的診斷會由每次執行的帳本抑制,帳本以嚴重性、代碼和訊息為鍵——刻意排除位置——因此只是往下移了四行的診斷,不會被當成新問題重新回報。

一個獨立的 write 層級工具,理由只有一個:層級決定可見性。把重新命名併入 lsp,不是讓唯讀的探索者擁有改寫儲存庫的能力,就是讓每一次 hover 都必須先過核准提示。

引數 意義
file 符號所在的位置
line 從 1 起算的行號
symbol 在該行上識別它的子字串
new_name 要改成什麼名稱

以形狀而非文字來尋找程式碼。$NAME 比對任何節點,$$$NAME 比對一連串同層節點,而 $_ 是不繫結任何東西的萬用字元。

讓它是真正的比對器、而不是「多加幾步的 regex」的關鍵性質是:同一個模式中重複出現的中繼變數,必須補抓到相同的文字。

ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")

會比對到「處置它剛剛檢查過的對象」的防護,而不會比對到處置其他東西的防護。

引數 意義
pattern 帶有中繼變數的程式碼片段
path 要搜尋的檔案或目錄;預設為整個工作區
language 隨附的文法 id(dart、python、rust、c、……)。當 path 指向檔案時會自動推斷
max_results 預設 50

相同的比對方式,再加上一個 rewrite 範本,把中繼變數替換回去。不會寫入任何東西。 變更會被暫存,而結果中會有一個 edit_id。

ast_edit(pattern: "dispose($X)", rewrite: "$X.dispose()", language: "dart")
→ (proposed) 12 replacements in 4 files … call resolve with edit_id "edit_1"
引數 意義
pattern 要比對什麼
rewrite 要改成什麼,並替換 $NAME
path 範圍;預設為整個工作區
language 語言 id
引數 意義
edit_id 來自 (proposed) 結果
action accept 或 discard

每個檔案都會拿來與暫存當下捕捉到的內容比對,只要有任何不符,整份變更都會被拒絕。只套用一半的結構化重寫,會留下一棵在新舊兩種形狀下都編譯不過的樹。


當執行具有除錯工作階段監督程式、工作目錄與對話 id 時登錄。配接器是否真的能啟動,是在呼叫當下以和語言伺服器相同的交集規則偵測(根標記而且執行檔)。exec 層級;不具平行安全性(工作階段是跨呼叫的狀態,因此 continue 若超車 stack,就會讀到一個已經移動過的框架)。

每個對話一個工作階段,並有硬性 TTL(30 分鐘)。第二次 launch 會被拒絕,而不是默默取代第一個。

操作 引數 備註
adapters — 這裡有哪些配接器可用,以及哪些為什麼不可用
status — 工作階段是否正在執行,以及它停在哪裡
breakpoints file, lines[] 空的 lines 會清除該檔案。可以在啟動之前設定
launch program, args[] 啟動程式,並停在你設定的中斷點
attach port 連線到一個已在執行的被除錯程式
continue thread_id? 執行到下一個停止點
pause thread_id? 讓它在原地停下
step_over / step_in / step_out thread_id? 前進一步
threads — 執行緒 id 與名稱
stack thread_id? 框架,上限 30
scopes frame_id 某個框架上的範圍,各自帶有一個 reference
variables reference 子項,上限 50,值上限 400 個字元
evaluate expression, frame_id? 在該框架自身的範圍內求值
output — 被除錯程式的 stdout/stderr,有界
terminate — 結束工作階段並終止配接器
Id 命令 根標記
dart dart debug_adapter pubspec.yaml
debugpy python3 -m debugpy.adapter pyproject.toml, setup.py, requirements.txt
lldb lldb-dap Cargo.toml, CMakeLists.txt
delve dlv dap go.mod

debugpy 偏好專案本地的 .venv/bin 或 venv/bin,而不偏好 $PATH:$PATH 上的 python 是另一個環境,而從錯誤的那一個附加,失敗的方式會讀起來像是程式碼壞了。


一個 Python 或 JavaScript 儲存格,其變數、匯入項目與已載入的資料都會在多次呼叫之間保留。exec 層級;不具平行安全性。

引數 意義
code 儲存格
description 必填。逐字稿標籤用的 5–10 個字
language python(預設)或 javascript
reset 丟棄目前的直譯器,並先從乾淨的狀態開始
  • 最後一個運算式會像 notebook 那樣被回顯。
  • display() 可以運作,pandas 資料框架會以表格形式呈現,而 matplotlib 圖形會以逐字稿所顯示的影像回傳。
  • 人們直覺就會輸入的 IPython 慣用寫法——%pip、%cd、!cmd、%%bash、%%timeit——會被改寫成一般程式碼。%pip 也會逐出 sys.modules,因此下一次 import 能看到剛剛安裝的內容。
contents = tool("read", {"path": "lib/auth.dart"})

這座橋接會重新進入執行環境的工具登錄表,因此護欄、核准以及每一層包裝,都會與模型發出的呼叫完全相同地套用。它走的是核心自己的管線,而不是回送通訊端:沒有連接埠、沒有密鑰、沒有對外連線規則。

儲存格的逾時是一份無活動預算(預設 5 分鐘),而且在橋接呼叫尚未回傳期間會暫停計時,因此一個展開到子代理的儲存格,不會在展開途中被終止。


read 層級。這是一次執行向操作者提問的唯一途徑。它會在對話中呈現一張編號卡片,並封鎖直到有人回答、略過,或問題逾時。絕對不要把問題寫成聊天訊息——那會結束這個回合。

引數 意義
question 要問什麼
context 選填:你為什麼要問,以及答案會影響什麼
options[] 選項,每一項為 {label, description?}
allow_free_text 是否接受手動輸入的答案
multi_select 是否可以選擇多個選項

略過也算成功:依一項明確陳述的假設繼續進行,不要再問一次。逾時是錯誤,並附帶同樣的指示。權限提示(「我可以做這件事嗎?」)是另一道關卡,並在對話中以相同的卡片呈現。

只有在提問埠與目標空間都存在時才會登錄。在沒有連接任何人的情況下,這個呼叫會失敗,而不是永遠封鎖——這與護欄 prompt 在沒有連接任何核准者時所採取的失效即拒絕姿態相同。


只有在 vibe 模式開啟時才會登錄。五個全部都是 read 層級:閘門應該設在工作程序的工具上,因為實際的編輯是在那裡發生。

工具 引數 備註
vibe_spawn brief, label, tier? 立即回傳;工作程序繼續執行
vibe_send worker_id, message 附加到簡報並重新執行
vibe_wait worker_id, timeout_seconds? 封鎖直到它定案
vibe_kill worker_id 將它停止
vibe_list — 層級、狀態、經過時間與最近一次結果

tier 是 fast(規格明確的機械性工作)或 good(設計、判斷,或審查 fast 工作程序的產出)。