內建代理工具
這些是內建代理執行階段直接登錄的伺服器端繫結工具——語言伺服器、結構化搜尋、除錯工具、核心、提問與工作程序控制——有別於它橋接進來的 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 |
lsp — 語言伺服器查詢
標題為「lsp — 語言伺服器查詢」的章節當執行具有語言伺服器監督程式與工作目錄時登錄。伺服器是否真的能作答,是在呼叫當下以交集方式偵測的:根標記必須存在,而且伺服器執行檔必須能解析得到。只有其中一項成立,就代表不會有任何伺服器啟動——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 都被包了起來:寫入之後,檔案會經過語言伺服器同步並儲存,而且只有新產生的診斷會併入工具結果。代理會在建置壞掉的那一刻就知道自己弄壞了它,不必記得要去問。
已回報過的診斷會由每次執行的帳本抑制,帳本以嚴重性、代碼和訊息為鍵——刻意排除位置——因此只是往下移了四行的診斷,不會被當成新問題重新回報。
lsp_rename — 專案範圍的重新命名
標題為「lsp_rename — 專案範圍的重新命名」的章節一個獨立的 write 層級工具,理由只有一個:層級決定可見性。把重新命名併入 lsp,不是讓唯讀的探索者擁有改寫儲存庫的能力,就是讓每一次 hover 都必須先過核准提示。
| 引數 | 意義 |
|---|---|
file |
符號所在的位置 |
line |
從 1 起算的行號 |
symbol |
在該行上識別它的子字串 |
new_name |
要改成什麼名稱 |
ast_grep — 結構化搜尋
標題為「ast_grep — 結構化搜尋」的章節以形狀而非文字來尋找程式碼。$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 |
ast_edit — 結構化重寫,先暫存
標題為「ast_edit — 結構化重寫,先暫存」的章節相同的比對方式,再加上一個 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 |
resolve — 提交或捨棄暫存的變更
標題為「resolve — 提交或捨棄暫存的變更」的章節| 引數 | 意義 |
|---|---|
edit_id |
來自 (proposed) 結果 |
action |
accept 或 discard |
每個檔案都會拿來與暫存當下捕捉到的內容比對,只要有任何不符,整份變更都會被拒絕。只套用一半的結構化重寫,會留下一棵在新舊兩種形狀下都編譯不過的樹。
debug — 驅動除錯工具
標題為「debug — 驅動除錯工具」的章節當執行具有除錯工作階段監督程式、工作目錄與對話 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 是另一個環境,而從錯誤的那一個附加,失敗的方式會讀起來像是程式碼壞了。
eval — 持久直譯器
標題為「eval — 持久直譯器」的章節一個 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 分鐘),而且在橋接呼叫尚未回傳期間會暫停計時,因此一個展開到子代理的儲存格,不會在展開途中被終止。
ask_user — 向人類提問
標題為「ask_user — 向人類提問」的章節read 層級。這是一次執行向操作者提問的唯一途徑。它會在對話中呈現一張編號卡片,並封鎖直到有人回答、略過,或問題逾時。絕對不要把問題寫成聊天訊息——那會結束這個回合。
| 引數 | 意義 |
|---|---|
question |
要問什麼 |
context |
選填:你為什麼要問,以及答案會影響什麼 |
options[] |
選項,每一項為 {label, description?} |
allow_free_text |
是否接受手動輸入的答案 |
multi_select |
是否可以選擇多個選項 |
略過也算成功:依一項明確陳述的假設繼續進行,不要再問一次。逾時是錯誤,並附帶同樣的指示。權限提示(「我可以做這件事嗎?」)是另一道關卡,並在對話中以相同的卡片呈現。
只有在提問埠與目標空間都存在時才會登錄。在沒有連接任何人的情況下,這個呼叫會失敗,而不是永遠封鎖——這與護欄 prompt 在沒有連接任何核准者時所採取的失效即拒絕姿態相同。
vibe_* — 指揮背景工作程序
標題為「vibe_* — 指揮背景工作程序」的章節只有在 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 工作程序的產出)。