工具系统与调度器:Gemini CLI 如何执行操作
前置知识
- ›第 2 篇:智能体循环
- ›理解构建者模式与策略设计模式
- ›MCP(Model Context Protocol)基础知识
工具系统与调度器:Gemini CLI 如何执行操作
当 Gemini 模型决定读取文件、执行 shell 命令或调用 MCP 工具时,这个决策会进入一套精密的处理流水线:工具调用被验证、经过策略检查、可能需要用户确认,执行完毕后将结果返回给模型,用于下一轮对话。本文将深入介绍 Gemini CLI 的工具系统与调度器是如何协同编排这整个流程的。
ToolInvocation 接口
Gemini CLI 中的每次工具调用都遵循由 ToolInvocation 接口 定义的统一生命周期:
flowchart LR
V[validate params] --> D[getDescription]
D --> C[shouldConfirmExecute]
C --> E[execute]
E --> P[getPolicyUpdateOptions]
该接口提供了清晰的契约:
params— 本次调用经过验证的参数getDescription()— 以人类可读的方式描述工具将要执行的操作toolLocations()— 工具将影响的文件路径(用于 UI 展示)shouldConfirmExecute(abortSignal, forcedDecision?)— 若需要用户确认,返回确认详情;否则返回falseexecute(signal, updateOutput?, options?)— 执行工具并返回ToolResultgetPolicyUpdateOptions(outcome)— 当用户选择"始终允许"时,提供工具特定的策略更新选项
shouldConfirmExecute 方法是策略决策转化为用户可见行为的关键所在。它接收可能来自 MessageBus 策略检查的 forcedDecision,返回 false(直接执行,无需确认)或一个包含特定 UI 数据的 ToolCallConfirmationDetails 对象。
BaseDeclarativeTool 与构建者模式
工具通过 BaseDeclarativeTool 以声明式方式定义,将 schema 定义与执行逻辑解耦。每个工具类需要指定:
- 供模型 function declarations 使用的名称、显示名称和描述
- 用于参数验证的 JSON parameter schema
Kind枚举值(ReadOnly、Write、Execute、Other)- 输出是否为 markdown,以及是否支持实时更新
当模型请求工具调用时,BaseDeclarativeTool 会通过 SchemaValidator 对参数进行 schema 验证,然后调用 createInvocation() 生成一个 ToolInvocation 实例。这正是构建者模式的体现:工具定义是构建者,调用实例是最终产物。
classDiagram
class BaseDeclarativeTool~TParams, TResult~ {
+name: string
+displayName: string
+description: string
+kind: Kind
+parameterSchema: object
#createInvocation(): ToolInvocation
+validate(params): ToolInvocation
}
class ToolInvocation~TParams, TResult~ {
<<interface>>
+params: TParams
+getDescription(): string
+shouldConfirmExecute(): Promise
+execute(): Promise~TResult~
+toolLocations(): ToolLocation[]
}
class BaseToolInvocation~TParams, TResult~ {
+messageBus: MessageBus
+respectsAutoEdit: boolean
+getApprovalMode: Function
#getConfirmationDetails(): Promise
#getMessageBusDecision(): Promise
}
BaseDeclarativeTool ..> ToolInvocation : creates
BaseToolInvocation ..|> ToolInvocation
BaseToolInvocation 抽象类提供了默认的确认流程。其 shouldConfirmExecute 方法会检查工具是否遵守 AUTO_EDIT 模式,然后向 MessageBus 查询策略决策。若决策为 allow,则无需确认直接执行;若为 deny,则抛出异常;若为 ask_user,则委托给 getConfirmationDetails() 展示特定的 UI 界面。
提示:
BaseToolInvocation上的respectsAutoEdit标志控制工具是否可以在AUTO_EDIT模式下自动审批。只有写入类工具(WriteFile、Edit)会将其设为true——shell 命令在默认模式下始终需要明确确认。
ToolRegistry 与模型感知的工具定义
ToolRegistry 是所有工具定义的中央存储,包括内置工具和动态发现的工具。它将工具名称映射到 AnyDeclarativeTool 实例,并为模型的 function-calling API 提供 getFunctionDeclarations(modelId?)。
一个关键的设计特性是模型族感知的工具定义。coreTools.ts 顶部的 getToolSet 函数根据模型类型路由到不同的 schema 集合:
export function getToolSet(modelId?: string): CoreToolSet {
const family = getToolFamily(modelId);
switch (family) {
case 'gemini-3':
return GEMINI_3_SET;
case 'default-legacy':
default:
return DEFAULT_LEGACY_SET;
}
}
Gemini 3 模型可能支持与旧版模型不同的参数 schema 或描述。每个工具定义(如 READ_FILE_DEFINITION)都有一个返回默认旧版 schema 的 base 属性,以及一个根据当前模型解析到对应集合的 overrides 函数。
内置工具覆盖文件操作、代码智能、网络访问和智能体控制等多个类别:
| 类别 | 工具 |
|---|---|
| 文件 I/O | ReadFile、WriteFile、Edit、ReadManyFiles、Glob、LS |
| 搜索 | Grep(基于 ripgrep) |
| 执行 | Shell |
| 网络 | WebSearch、WebFetch |
| 记忆 | Memory(保存/检索事实) |
| 智能体控制 | ActivateSkill、AskUser、ExitPlanMode、UpdateTopic |
| 任务跟踪 | WriteTodos |
调度器:事件驱动的工具编排
Scheduler 是模型工具调用请求与实际工具执行之间的桥梁。源码中将其描述为"工具执行的事件驱动编排器"。
flowchart TD
A[Tool Call Requests from Turn] --> B[Scheduler.schedule]
B --> C{Already processing?}
C -- Yes --> D[Enqueue request]
C -- No --> E[Start batch]
E --> F[ToolModificationHandler<br/>validates & transforms]
F --> G[Check Policy via PolicyEngine]
G --> H{Policy Decision}
H -- ALLOW --> I[Execute tool]
H -- DENY --> J[Return error]
H -- ASK_USER --> K[Evaluate BeforeTool hook]
K --> L[Resolve confirmation via MessageBus]
L --> M{User confirms?}
M -- Yes --> N[Update policy if always-allow]
N --> I
M -- No --> O[Return cancelled]
I --> P[ToolExecutor.execute]
P --> Q[Track state via SchedulerStateManager]
Q --> R[Return CompletedToolCall]
调度器协调以下五个组件:
ToolModificationHandler— 在执行前对工具调用请求进行验证和转换PolicyEngine(通过checkPolicy)— 评估规则,决定 allow/deny/ask_userevaluateBeforeToolHook— 触发执行前的钩子,可以修改或阻止执行resolveConfirmation— 通过 MessageBus 处理用户确认流程ToolExecutor— 实际执行工具并捕获结果
第 192 行的 schedule() 方法接受一个或多个 ToolCallRequestInfo 对象。当模型在单次响应中发出多个 function call(并行工具调用)时,它们会被批量处理。如果已有一批正在执行,新的请求会通过 _enqueueRequest() 进入队列等待。
每次工具调用的状态由 SchedulerStateManager 追踪,经历以下阶段:Scheduled → Validating → Executing → Completed(或 Errored)。状态管理器还负责处理 MCP 进度更新——当 MCP 工具上报增量进度时,调度器会将执行状态更新为对应的进度百分比和消息。
深入解析:ShellTool 与 EditTool
在所有内置工具中,ShellTool 是最复杂的一个。它需要:
- 解析命令以进行策略评估(使用 shell-quote 解析)
- 与沙箱管理器集成,用于命令包装
- 处理后台执行并跟踪 PID
- 通过
ShellExecutionService支持平台特定的执行方式
Edit 工具采用了另一种思路——不是全量替换文件内容,而是使用基于 diff 的修改模型。模型提供 old_string 和 new_string 参数,工具执行精准替换。这比发送整个文件内容更节省 token,同时也能为用户确认界面生成更清晰的 diff 视图。
这两个工具都很好地展示了确认流程的实际运作。ShellTool 生成 exec 类型的确认详情,包含解析后的命令和根命令;EditTool 生成 edit 类型的确认详情,包含文件 diff、原始内容和新内容——在终端 UI 中以可视化 diff 的形式呈现。
MCP 工具集成
MCP(Model Context Protocol)工具通过 DiscoveredMCPTool 类和 mcp_{serverName}_{toolName} 命名约定进行集成。配置 MCP 服务器后,McpClientManager 会发现可用工具,并以 mcp_ 前缀将其注册到 ToolRegistry 中。
sequenceDiagram
participant Config as Config.initialize()
participant MCM as McpClientManager
participant MCP as MCP Server
participant TR as ToolRegistry
Config->>MCM: connectToServers()
MCM->>MCP: listTools()
MCP-->>MCM: tool schemas
MCM->>TR: registerTool(DiscoveredMCPTool)
Note over TR: Tool registered as<br/>mcp_serverName_toolName
MCP 工具与内置工具走相同的调度器流水线——经过验证、策略检查和用户确认后才会执行。mcp_ 前缀支持通配符策略规则,如 mcp_serverName_*,可以一次性允许或拒绝某个特定服务器的所有工具。这一点我们将在下一篇关于策略引擎的文章中详细介绍。
提示: 调试 MCP 工具问题时,可以查看 ToolRegistry 的
allKnownTools映射。MCP 工具以带前缀的名称注册,并在_serverName注解中包含服务器名称,供策略匹配使用。
调度器与工具系统的构建者模式以及 MessageBus 确认流程相结合,构建出一套灵活且安全的执行流水线。下一篇文章中,我们将重点介绍管控这套流水线的安全层——策略引擎、沙箱机制和安全检查器。