跳转到内容

内置智能体工具

这些是内置智能体运行时直接注册的绑定服务端资源的工具——语言服务器、结构化搜索、调试器、内核、提问与工作进程控制——它们既不同于它桥接进来的 MCP 工具,也不同于运行器运行时提供的通用文件、搜索和 shell 工具。它们不在 MCP 注册表里:外部 MCP 客户端看不到它们,因为每一个都绑定着一项服务器端的实时资源——一个语言服务器进程、一个调试适配器、一个解释器——而这项资源只属于一次运行。

这里的每一个工具,只有在它的支撑资源为本次运行接入之后才会注册——语言服务器或调试监督进程、一个 tree-sitter 解析器、一个内核工厂、一个提问端口、一份 vibe 名册。对这次检出的语言服务器或调试适配器的检测,是另一项更靠后的检查(status / adapters):这些工具会一直留在工具面上,好让智能体能问清楚为什么什么都不可用,而不是靠猜。

每个工具都会声明一个层级,而层级决定的是哪些调用方能看到这个工具,而不只是它是否会弹出提示。计划模式和只读子智能体都被限制在 read,因此 write 或 exec 层级的工具根本不会出现在它们的 schema 中。

层级 含义 这里的工具
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 匹配一串同级节点,$_ 是通配符,不绑定任何东西。

让它成为一个匹配器、而不只是一个多绕几步的正则的特性在于:同一个模式里重复出现的元变量必须捕获相同的文本。

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

resolve — 提交或丢弃一次暂存的改动

标题为“resolve — 提交或丢弃一次暂存的改动”的部分
参数 含义
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 工作进程的产出)。