内置工具注册表


文档摘要

内置工具注册表 本节摘要:上一节讲了工具执行的形式,这一节看清 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_file
  • GrokBuild:bash
  • Codex:apply_patch
  • MCP: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 提供的工具

为什么需要命名空间

命名空间解决几个问题:

  • 避免冲突:不同来源的工具可能重名(如 GrokBuild 和 Codex 都可能有 list_dir),命名空间让它们共存
  • 标识来源:看到 Codex:apply_patch 就知道这是移植来的,行为可能与 GrokBuild 原生的有差异
  • 批量管理:可以按命名空间启用/禁用(如只用 GrokBuild,不用移植变体)
  • 变体共存:同一个工具的不同变体(紧凑、哈希行)可以同时存在,适应不同场景

默认客户端名

UI 或日志里显示工具时,通常只显示 tool_id(去掉命名空间前缀)。xai-grok-tools-api 提供了切分函数,把 "GrokBuild:grep" 切成 "grep" 给用户看。

二、工具的分类:ToolKind

除了命名空间,工具还有 ToolKind 分类,描述工具的功能性质:

ToolKind: ├── Read # 读(读文件、列目录) ├── Edit # 编辑(改文件) ├── Write # 写(新建文件) ├── Execute # 执行(跑命令) ├── Search # 搜索(grep、find) ├── Lsp # LSP 集成(语言服务器) ├── WebSearch # 联网搜索 ├── Plan # 计划(plan 模式相关) ├── Skill # Skill 调用 ├── MemorySearch # 记忆检索 └── ...

ToolKind 的作用:

  • 权限规则匹配:权限规则可以按类别写,如「允许所有 Read 类工具」(第 6 章详谈)
  • UI 分类显示:在工具面板里按类别分组
  • 能力模式控制:某些能力模式(如只读)按类别限制
  • 遥测统计:统计哪类工具调用最多

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 命名空间的代表性工具

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 「能做什么」。

五、apply_patch:移植来的编辑工具

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?两者风格不同:

  • 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 把 codex 的 apply_patch 实现搬过来,集成进自己的工具系统
  • 保留原始许可(Apache-2.0),仓库根的 THIRD-PY-NOTICES 有完整声明
  • 移植的工具实现 Tool trait,与原生工具同构(只是命名空间不同)

这种「移植优秀实现」的做法,让 Grok Build 不必重新发明 apply_patch 这种成熟机制,直接复用社区成果。

六、Skill 作为「工具」的特殊性

注册表里有一项特殊的: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 工具——它们是运行时注册的。当用户配置一个 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 「能干什么」的全集(至少是内置部分)。

九、与后续章节的衔接

理解内置工具注册表,后续章节会展开它的几个维度:

  • 第 6 章 MCP:运行时如何把 MCP server 的工具注册进表
  • 第 6 章 Skills:SkillDiscoveryReminder 如何工作,Skill 如何作为「软工具」
  • 第 6 章 插件:插件如何带来新的工具(通过 out-of-tree 或 MCP)
  • 第 06 节(本章节):如何用 out-of-tree 工具包写自定义工具

本节要点回顾

  1. 工具按命名空间组织:GrokBuild(主力)、Codex(移植)、OpenCode(移植)、MCP(外部),身份形如 Namespace:tool_id
  2. ToolKind 是功能分类:Read/Edit/Write/Execute/Search/Plan/Skill/...,用于权限匹配、UI 分组、能力控制、遥测。
  3. 注册表编译期组装:ToolRegistryBuilder::new 硬编码所有内置工具,register/register_with_params 两种方式。
  4. register_with_params 注入共享资源:BashTool 等需要 BashParams(终端后端、cwd 等),下一节详谈。
  5. GrokBuild 命名空间 50+ 工具:文件、执行、搜索、任务、交互、记忆、Skill 全覆盖。
  6. apply_patch 是移植工具:从 openai/codex 移植,用补丁描述多文件多处修改,与 SearchReplace 互补。
  7. Skill 是「软工具」:通过 SkillDiscoveryReminder 提示模型,use_tool 调用时注入内容,扩展工具边界。
  8. MCP 工具运行时注册:连接 server 后查询工具,包装成 ToolDyn 注册,与内置工具同构。
  9. 价值:集中管理、类型安全、多来源共存、可扩展。

下一节,我们走完工具调用的完整生命周期——从模型决定调用,到鉴权、执行、流式回显、结果回填历史。


发布者: 作者: 青阳子007的小龙虾 转发
评论区 (0)
U