内置工具注册表 本节摘要:上一节讲了工具执行的形式,这一节看清 Grok Build 到底「自带哪些工具」。答案是:50 多个,按命名空间组织——GrokBuild 命名空间是主力(读文件、bash、搜索、编辑、计划、子代理等),Codex 命名空间是从 OpenAI codex 移植的工具(最著名的是 applypatch),OpenCode 命名空间是从 sst/opencode 移植的变体。这些工具在编译期被组装进一个叫 ToolRegistryBuilder 的注册表,运行时还可挂载 MCP 工具与 out-of-tree 工具包。本节会讲清注册表的组织、命名空间的意义、几个代表性工具,以及 applypatch 这个「移植来的工具」的来历。
本节摘要:上一节讲了工具执行的形式,这一节看清 Grok Build 到底「自带哪些工具」。答案是:50 多个,按命名空间组织——GrokBuild 命名空间是主力(读文件、bash、搜索、编辑、计划、子代理等),Codex 命名空间是从 OpenAI codex 移植的工具(最著名的是 apply_patch),OpenCode 命名空间是从 sst/opencode 移植的变体。这些工具在编译期被组装进一个叫 ToolRegistryBuilder 的注册表,运行时还可挂载 MCP 工具与 out-of-tree 工具包。本节会讲清注册表的组织、命名空间的意义、几个代表性工具,以及 apply_patch 这个「移植来的工具」的来历。
Grok Build 的工具按 命名空间(namespace) 组织。每个工具的身份字符串是 Namespace:tool_id 的形式,例如:
GrokBuild:read_fileGrokBuild:bashCodex:apply_patchMCP:sentry__get_issue(MCP 工具,第 6 章详谈)主要的命名空间(在 xai-grok-tools/src/types/tool.rs):
ToolNamespace: ├── GrokBuild # 主力,GrokBuild:read_file 等 ├── GrokBuildConcise # 紧凑变体(输出更精简) ├── GrokBuildHashline # 哈希行格式变体 ├── Codex # 从 openai/codex 移植 ├── OpenCode # 从 sst/opencode 移植 └── MCP # 外部 MCP server 提供的工具
为什么需要命名空间
命名空间解决几个问题:
Codex:apply_patch 就知道这是移植来的,行为可能与 GrokBuild 原生的有差异默认客户端名
UI 或日志里显示工具时,通常只显示 tool_id(去掉命名空间前缀)。xai-grok-tools-api 提供了切分函数,把 "GrokBuild:grep" 切成 "grep" 给用户看。
除了命名空间,工具还有 ToolKind 分类,描述工具的功能性质:
ToolKind: ├── Read # 读(读文件、列目录) ├── Edit # 编辑(改文件) ├── Write # 写(新建文件) ├── Execute # 执行(跑命令) ├── Search # 搜索(grep、find) ├── Lsp # LSP 集成(语言服务器) ├── WebSearch # 联网搜索 ├── Plan # 计划(plan 模式相关) ├── Skill # Skill 调用 ├── MemorySearch # 记忆检索 └── ...
ToolKind 的作用:
ToolKind 与命名空间是正交的——同一个 ToolKind 可能在多个命名空间里(如 GrokBuild 和 Codex 都有 Read 类工具)。
工具注册表在编译期组装,核心是 ToolRegistryBuilder(在 xai-grok-tools/src/registry/types.rs)。它的结构大致是:
fn ToolRegistryBuilder::new() -> Self { let mut b = ToolRegistryBuilder::empty() # ── GrokBuild 命名空间:主力工具 ── b.register_with_params::<grok_build::BashTool, BashParams>() b.register_with_params::<grok_build::ReadFileTool, ReadFileParams>() b.register_with_params::<grok_build::SearchReplaceTool, EditParams>() b.register::<grok_build::ListDirTool>() b.register::<grok_build::GrepTool>() b.register::<grok_build::KillTaskTool>() b.register::<grok_build::TodoWriteTool>() b.register::<grok_build::WebSearchTool>() b.register::<grok_build::WebFetchTool>() b.register::<grok_build::TaskTool>() # 子代理 b.register::<grok_build::EnterPlanModeTool>() b.register::<grok_build::ExitPlanModeTool>() b.register::<grok_build::AskUserQuestionTool>() # ... 约 50 个工具,含 hashline/memory/search/use_tool 变体 # ── Codex 命名空间:从 openai/codex (Apache-2.0) 移植 ── b.register::<codex::apply_patch::ApplyPatchTool>() b.register::<codex::list_dir::CodexListDirTool>() # ── OpenCode 命名空间 ── b.register::<opencode::OpenCodeBashTool>() # ── Skill 自动发现的 reminder ── b.register_reminder(SkillDiscoveryReminder) # ── out-of-tree 工具包(运行时挂载)── for pack in tool_packs().lock().iter() { pack(&mut b) } b }
几个关键点:
register 与 register_with_params
两种注册方式:
register::<T>():工具自包含,不需要外部参数register_with_params::<T, P>():工具需要一组共享参数 P(下一节详谈 SharedResources/Params 模式)例如 BashTool 需要 BashParams(终端后端、超时、cwd 等),用 register_with_params;ListDirTool 自包含,用 register。
编译期 vs 运行时
内置工具是编译期注册的——它们被硬编码在 ToolRegistryBuilder::new 里。这意味着:
out-of-tree 工具包
最后那个 for pack in tool_packs() 是 out-of-tree(树外)工具包机制——允许在不改主仓库的情况下,通过外部工具包注册自定义工具。具体做法是用一个全局的工具包列表(tool_packs() 返回一个被 Mutex 保护的全局列表),外部代码在启动时调用 register_tool_pack 把自己的工具注册进去。
这是 Grok Build 工具系统的扩展点之一(另一个是 MCP,第 6 章详谈)。第 06 节会讲如何用它写自定义工具。
GrokBuild 命名空间是主力,涵盖 Agent 工作的全流程。几个代表性的:
文件操作类
GrokBuild:read_file:读文件内容GrokBuild:list_dir:列目录GrokBuild:search_replace:搜索替换编辑(精确改文件)GrokBuild:write_file:写新文件执行类
GrokBuild:bash:执行 shell 命令(流式输出,第 02 节讲过)GrokBuild:kill_task:终止后台任务搜索类
GrokBuild:grep:正则搜索代码GrokBuild:web_search:联网搜索GrokBuild:web_fetch:抓取网页任务管理类
GrokBuild:todo_write:管理 TODO 列表(让 Agent 跟踪多步任务)GrokBuild:task:派生子代理(第 8 章详谈)交互类
GrokBuild:ask_user_question:向用户提问(需要用户提供信息时)GrokBuild:enter_plan_mode / exit_plan_mode:进入/退出计划模式(第 8 章)记忆与 Skill 类
GrokBuild:memory_search:检索跨会话记忆GrokBuild:use_tool(Skill 调用):调用已发现的 Skill(第 6 章)这些工具覆盖了 Agent 「读、写、执行、搜、问、计划、记忆」的全部能力。理解这个清单,你能大致判断 Agent 「能做什么」。
Codex 命名空间里最知名的是 apply_patch——一种用文本补丁描述文件修改的工具,从 OpenAI 的 codex 项目移植而来(Apache-2.0 许可)。
apply_patch 的输入
apply_patch 接收一个参数:整段补丁文本。补丁格式(V4d)大致是:
*** Begin Patch *** Update File: src/main.rs @@ context line -old line +new line context line *** End Patch
补丁用 *** Update File:、*** Add File:、*** Delete File: 等指令描述操作,用 @@ 提供上下文,用 +/- 表示新增/删除行。
apply_patch 与 SearchReplace 的区别
GrokBuild 命名空间有自己的编辑工具(SearchReplace),为什么还要 apply_patch?两者风格不同:
apply_patch 适合「一次性改很多」的场景,SearchReplace 适合「精准改一处」。给模型提供两种选择,让它按任务复杂度选合适的工具。
移植的含义
apply_patch 在 xai-grok-tools/src/implementations/codex/apply_patch/ 下,源码注释明确写「ported from openai/codex (Apache-2.0, Copyright 2025 OpenAI)」。移植意味着:
这种「移植优秀实现」的做法,让 Grok Build 不必重新发明 apply_patch 这种成熟机制,直接复用社区成果。
注册表里有一项特殊的:register_reminder(SkillDiscoveryReminder)。这关联到 Skill——第 6 章会详谈,这里先提一句。
Skill(技能)是 SKILL.md 格式的可复用 prompt 包。它不是传统意义上的「工具」(没有强类型 Args/Output),而是一个提示注入机制:当模型决定用某个 Skill 时,框架把 Skill 的内容包成 <skill> 信封注入对话。
为了让模型「知道有这些 Skill」,框架注册了一个 SkillDiscoveryReminder——它不是一个可执行的工具,而是一个提示(reminder),在每轮请求时把已发现的 Skill 列表告诉模型。模型据此决定是否「调用」某个 Skill(通过 use_tool 工具)。
Skill 的特殊性在于:它扩展了工具系统的边界——传统工具是「执行代码」,Skill 是「注入知识」。两者统一在「让 Agent 获得新能力」这个目标下。
注册表里没有写死 MCP 工具——它们是运行时注册的。当用户配置一个 MCP server 并启动会话时:
1. MCP 客户端连接 MCP server 2. 查询 server 提供哪些工具 3. 把这些工具包装成实现 ToolDyn 的对象 4. 注册到工具注册表,命名空间为 MCP 5. 工具名是 server__tool 的形式(如 sentry__get_issue)
注册后,MCP 工具与内置工具同构——都实现 ToolDyn,都能被模型调用,都走同样的鉴权与执行流程。这种「MCP 工具即工具」的统一,让 Grok Build 的工具能力可以无限扩展(任何 MCP server 都是新的工具来源),第 6 章会详谈。
把注册表看下来,它的价值在于:
价值一:集中管理
所有内置工具在一个地方注册,易于维护与审计。新增内置工具只需改 ToolRegistryBuilder::new。
价值二:类型安全
注册在编译期发生,编译器检查工具类型。注册错了(如忘了实现某个方法)编译就过不了。
价值三:多来源共存
内置(GrokBuild/Codex/OpenCode)、运行时(MCP)、外部(out-of-tree)三种来源的工具,统一在同一个注册表里,对模型一视同仁。
价值四:可扩展
新增工具能力强——内置改代码重编译,MCP 配置即可,out-of-tree 写工具包。无论哪种,模型都能立刻用上。
关键概念:工具注册表是 Grok Build 工具能力的「目录」。它把「内置的、移植的、外接的、知识型的」各种工具统一管理,让 Agent 拥有一个丰富、可扩展、类型安全的工具箱。理解这个目录,你就理解了 Agent 「能干什么」的全集(至少是内置部分)。
理解内置工具注册表,后续章节会展开它的几个维度:
Namespace:tool_id。下一节,我们走完工具调用的完整生命周期——从模型决定调用,到鉴权、执行、流式回显、结果回填历史。