Gemini CLI 非交互模式工具调用机制详解
概述
本文档详细解析 Gemini CLI 在非交互模式(-p 参数)下如何实现工具调用和权限控制,这对理解如何将 Claude Code SDK 适配到 Gemini CLI 非常重要。
1. 整体架构
1.1 核心文件位置
gemini-cli/
├── packages/cli/src/
│ ├── nonInteractiveCli.ts # 非交互模式主入口
│ ├── nonInteractiveCliCommands.ts # 非交互命令处理
│ └── nonInteractive/
│ ├── session.ts # 会话管理
│ └── types.ts # 类型定义
└── packages/core/src/
├── core/
│ ├── nonInteractiveToolExecutor.ts # 工具执行器
│ └── coreToolScheduler.ts # 工具调度器(核心)
└── utils/
└── shell-permissions.ts # Shell 权限控制
1.2 执行流程图
用户输入 (-p "prompt")
↓
nonInteractiveCli.runNonInteractive()
↓
处理输入(@命令、斜杠命令)
↓
geminiClient.sendMessageStream()
↓
监听事件流
├─ GeminiEventType.Content → 输出文本
├─ GeminiEventType.ToolCallRequest → 收集工具请求
└─ GeminiEventType.Error → 处理错误
↓
executeToolCall() → CoreToolScheduler
↓
权限检查 (isAutoApproved)
├─ YOLO 模式 → 自动批准
├─ 允许列表匹配 → 自动批准
└─ 非交互模式 + 需要确认 → 抛出错误
↓
工具执行
↓
结果转换为 FunctionResponse
↓
发送回 Gemini (多轮对话)
↓
继续直到无工具调用
2. 非交互模式主流程
2.1 入口函数:runNonInteractive()
文件 : packages/cli/src/nonInteractiveCli.ts
typescript
export async function runNonInteractive({
config,
settings,
input, // 用户输入
prompt_id,
hasDeprecatedPromptArg,
resumedSessionData,
}: RunNonInteractiveParams): Promise<void>
关键步骤:
-
初始化环境
- 设置 ConsolePatcher(捕获 console 输出)
- 创建工作区 stdio
- 设置取消监听器(Ctrl+C)
-
处理输入
typescript// 处理斜杠命令 if (isSlashCommand(input)) { query = await handleSlashCommand(input, ...); } // 处理 @ 命令(@include 等) if (!query) { const { processedQuery, error } = await handleAtCommand({ query: input, config, ... }); query = processedQuery; } -
多轮对话循环
typescriptwhile (true) { // 1. 发送消息 const responseStream = geminiClient.sendMessageStream( currentMessages[0]?.parts || [], abortController.signal, prompt_id, ); // 2. 收集工具调用请求 const toolCallRequests: ToolCallRequestInfo[] = []; for await (const event of responseStream) { if (event.type === GeminiEventType.ToolCallRequest) { toolCallRequests.push(event.value); } } // 3. 如果有工具调用 if (toolCallRequests.length > 0) { for (const requestInfo of toolCallRequests) { const completedToolCall = await executeToolCall( config, requestInfo, abortController.signal, ); // 收集结果 completedToolCalls.push(completedToolCall); } // 4. 将工具结果发送回 Gemini currentMessages = [{ role: 'user', parts: toolResponseParts }]; } else { // 5. 没有工具调用,结束 return; } }
3. 工具执行器
3.1 executeToolCall 函数
文件 : packages/core/src/core/nonInteractiveToolExecutor.ts
typescript
export async function executeToolCall(
config: Config,
toolCallRequest: ToolCallRequestInfo,
abortSignal: AbortSignal,
): Promise<CompletedToolCall> {
return new Promise<CompletedToolCall>((resolve, reject) => {
// 创建工具调度器
const scheduler = new CoreToolScheduler({
config,
getPreferredEditor: () => undefined,
onAllToolCallsComplete: async (completedToolCalls) => {
if (completedToolCalls.length > 0) {
resolve(completedToolCalls[0]);
} else {
reject(new Error('No completed tool calls returned.'));
}
},
});
// 调度工具执行
scheduler.schedule(toolCallRequest, abortSignal).catch((error) => {
reject(error);
});
});
}
4. 工具调度器(Core)
4.1 CoreToolScheduler 类
文件 : packages/core/src/core/coreToolScheduler.ts
这是整个工具调用系统的核心,负责:
- 工具生命周期管理
- 权限检查
- 执行调度
- 结果处理
4.2 工具状态机
typescript
export type ToolCall =
| ValidatingToolCall // 验证中
| ScheduledToolCall // 已调度
| ErroredToolCall // 错误
| SuccessfulToolCall // 成功
| ExecutingToolCall // 执行中
| CancelledToolCall // 已取消
| WaitingToolCall; // 等待批准(交互模式)
状态转换:
Validating → Scheduled → Executing → Success
↓ ↓
WaitingApproval Error
↓
Cancelled
4.3 调度流程
typescript
async schedule(
request: ToolCallRequestInfo | ToolCallRequestInfo[],
signal: AbortSignal,
): Promise<void> {
// 1. 创建工具调用对象
const newToolCalls: ToolCall[] = requestsToProcess.map(
(reqInfo): ToolCall => {
const toolInstance = this.config
.getToolRegistry()
.getTool(reqInfo.name);
const invocation = toolInstance.build(reqInfo.args);
return {
status: 'validating',
request: reqInfo,
tool: toolInstance,
invocation: invocation,
startTime: Date.now(),
};
},
);
// 2. 处理每个工具调用
await this._processNextInQueue(signal);
}
5. 权限控制机制
5.1 权限检查函数
文件 : packages/core/src/core/coreToolScheduler.ts:1442-1456
typescript
private isAutoApproved(toolCall: ValidatingToolCall): boolean {
// 1. YOLO 模式:全部自动批准
if (this.config.getApprovalMode() === ApprovalMode.YOLO) {
return true;
}
// 2. 获取允许的工具列表
const allowedTools = this.config.getAllowedTools() || [];
const { tool, invocation } = toolCall;
const toolName = typeof tool === 'string' ? tool : tool.name;
// 3. Shell 命令特殊处理
if (SHELL_TOOL_NAMES.includes(toolName)) {
return isShellInvocationAllowlisted(invocation, allowedTools);
}
// 4. 其他工具匹配检查
return doesToolInvocationMatch(tool, invocation, allowedTools);
}
5.2 非交互模式的限制
文件 : packages/core/src/core/coreToolScheduler.ts:898-904
typescript
if (!this.config.isInteractive()) {
throw new Error(
`Tool execution for "${
toolCall.tool.displayName || toolCall.tool.name
}" requires user confirmation, which is not supported in non-interactive mode.`,
);
}
重要: 在非交互模式下,如果工具需要用户确认但不在自动批准列表中,会直接报错退出。
6. ApprovalMode 枚举
6.1 模式定义
typescript
enum ApprovalMode {
DEFAULT, // 默认模式:需要确认(除非在允许列表中)
YOLO, // 全自动模式:自动批准所有工具
AUTO_EDIT, // 自动编辑模式:自动批准编辑操作
}
6.2 模式配置
命令行参数:
bash
# YOLO 模式
gemini -y "prompt"
gemini --yolo "prompt"
gemini --approval-mode=yolo "prompt"
# 默认模式
gemini --approval-mode=default "prompt"
# 自动编辑模式
gemini --approval-mode=auto_edit "prompt"
配置文件:
toml
[general]
approval_mode = "yolo" # 或 "default", "auto_edit"
7. Shell 命令权限系统
7.1 权限检查函数
文件 : packages/core/src/utils/shell-permissions.ts
typescript
export function checkCommandPermissions(
command: string,
config: Config,
sessionAllowlist?: Set<string>,
): {
allAllowed: boolean;
disallowedCommands: string[];
blockReason?: string;
isHardDenial?: boolean;
}
7.2 两种权限模式
模式 1: "Default Deny"(默认拒绝)
- 用途: 自定义命令脚本
- 规则: 命令必须在全局允许列表或会话允许列表中
- 优先级 :
- 黑名单检查(最高)
- 会话允许列表
- 全局允许列表
模式 2: "Default Allow"(默认允许)
- 用途: 直接工具调用(AI 调用)
- 规则 :
- 如果有严格的允许列表,命令必须在其中
- 不能在黑名单中
- 如果没有允许列表,允许所有(除了黑名单)
7.3 权限检查顺序
typescript
// 1. 黑名单检查(最高优先级)
const excludeTools = config.getExcludeTools();
if (excludeTools.has('run_shell_command')) {
return { allAllowed: false, blockReason: 'Shell tool is disabled' };
}
// 2. 检查具体命令是否在黑名单
if (doesToolInvocationMatch('run_shell_command', invocation, excludeTools)) {
return { allAllowed: false, blockReason: 'Command is blocked' };
}
// 3. 通配符检查
const coreTools = config.getCoreTools();
if (coreTools.includes('run_shell_command')) {
return { allAllowed: true }; // 所有 shell 命令都允许
}
// 4. 允许列表匹配
if (sessionAllowlist) {
// Default Deny 模式
if (!matchesAllowlist(command, sessionAllowlist) &&
!matchesAllowlist(command, coreTools)) {
return { allAllowed: false };
}
} else {
// Default Allow 模式
if (hasSpecificAllowedCommands && !matchesAllowlist(command, coreTools)) {
return { allAllowed: false };
}
}
return { allAllowed: true };
7.4 Shell 命令自动批准
文件 : packages/core/src/utils/shell-permissions.ts:210-270
typescript
export function isShellInvocationAllowlisted(
invocation: AnyToolInvocation,
allowedPatterns: string[],
): boolean {
if (!allowedPatterns.length) {
return false;
}
// 检查是否有通配符
const hasShellWildcard = allowedPatterns.some((pattern) =>
SHELL_TOOL_NAMES.includes(pattern),
);
if (hasShellWildcard) {
return true; // 所有 shell 命令都允许
}
// 检查具体命令
const command = invocation.params.command;
const parseResult = parseCommandDetails(command);
const commandsToValidate = parseResult.details.map(d => d.text);
// 每个命令段都必须匹配
return commandsToValidate.every((cmd) =>
doesToolInvocationMatch(
'run_shell_command',
{ params: { command: cmd } },
allowedPatterns,
),
);
}
8. 允许列表配置
8.1 配置方式
配置文件 (~/.gemini/config.toml):
toml
# 允许所有 shell 命令
[tools]
core_tools = ["run_shell_command"]
# 只允许特定命令
[tools]
core_tools = ["run_shell_command(git)", "run_shell_command(ls)", "run_shell_command(cat)"]
# 允许特定工具
[tools]
core_tools = ["read_file", "write_to_file", "search_files"]
# 排除特定工具
[tools]
exclude_tools = ["run_shell_command(rm)", "run_shell_command(del)"]
8.2 模式匹配语法
typescript
// 通配符:所有 shell 命令
"run_shell_command"
// 特定命令:只允许 git
"run_shell_command(git)"
// 带参数:只允许 git status
"run_shell_command(git status)"
// 通配符文件:所有文件读取
"read_file(*)"
// 特定路径:只允许特定目录
"read_file(/path/to/dir/*)"
9. 工具调用事件流
9.1 事件类型
typescript
enum GeminiEventType {
Content, // 文本内容
ToolCallRequest, // 工具调用请求
LoopDetected, // 检测到循环
MaxSessionTurns, // 超过最大轮次
Error, // 错误
}
9.2 工具调用请求结构
typescript
interface ToolCallRequestInfo {
callId: string; // 调用 ID
name: string; // 工具名称
args: Record<string, unknown>; // 工具参数
}
9.3 工具响应结构
typescript
interface ToolCallResponseInfo {
callId: string;
responseParts: Part[]; // Gemini FunctionResponse 格式
resultDisplay?: ToolResultDisplay; // 显示结果
error?: Error;
errorType?: ToolErrorType;
contentLength?: number;
outputFile?: string; // 截断输出的文件路径
}
10. 与 Claude Code SDK 的适配要点
10.1 关键发现
-
非交互模式完全支持工具调用
- 通过事件流机制
- 自动多轮对话
- 无需用户干预(如果配置正确)
-
权限系统完善
- 三层控制:ApprovalMode → 允许列表 → 黑名单
- 支持细粒度配置
- 安全性高
-
错误处理
- 非交互模式下不能请求用户确认
- 必须预先配置权限
- 否则直接报错
10.2 SDK 适配建议
配置策略
python
# SDK 启动 Gemini CLI 时推荐配置
config = {
'approval_mode': 'yolo', # 或配置允许列表
'allowed_tools': [
'run_shell_command(git)',
'read_file',
'write_to_file',
'search_files'
],
'exclude_tools': [
'run_shell_command(rm)',
'run_shell_command(del)'
]
}
通信协议
python
# SDK 需要处理的事件流
async for event in gemini_process.stdout:
if event.type == 'content':
# 处理文本输出
print(event.value)
elif event.type == 'tool_use':
# 工具调用开始
print(f"Tool: {event.tool_name}")
print(f"Args: {event.parameters}")
elif event.type == 'tool_result':
# 工具执行结果
print(f"Result: {event.output}")
elif event.type == 'error':
# 处理错误
print(f"Error: {event.message}")
最佳实践
-
使用 YOLO 模式进行开发
bashgemini -y "prompt" -
生产环境使用允许列表
toml[tools] core_tools = [ "run_shell_command(git)", "read_file(*)", "write_to_file(*)" ] -
处理长输出
- SDK 需要处理工具输出截断
- 读取
outputFile字段 - 使用 Read 工具读取完整输出
-
错误恢复
- 捕获非交互模式错误
- 提供清晰的配置指引
- 支持会话恢复
11. 完整示例
11.1 非交互模式调用流程
bash
# 启动命令
gemini -p "列出当前目录的文件,然后创建一个 test.txt"
# 内部执行流程
1. 发送消息到 Gemini
2. Gemini 返回: run_shell_command("ls")
3. 检查权限: isAutoApproved() → true
4. 执行工具: ls
5. 返回结果给 Gemini
6. Gemini 返回: write_to_file("test.txt", ...)
7. 检查权限: isAutoApproved() → true
8. 执行工具: 创建文件
9. 返回结果给 Gemini
10. Gemini 返回文本: "文件已创建"
11. 输出到控制台
12. 结束
11.2 权限拒绝示例
bash
# 配置: 只允许 git 命令
gemini -p "删除所有文件"
# 内部执行流程
1. 发送消息到 Gemini
2. Gemini 返回: run_shell_command("rm -rf *")
3. 检查权限: isAutoApproved() → false
4. 非交互模式检查: isInteractive() → false
5. 抛出错误:
"Tool execution requires user confirmation,
which is not supported in non-interactive mode."
6. 退出,返回错误码
12. 总结
12.1 核心要点
- ✅ 完整支持: 非交互模式完全支持工具调用和多轮对话
- ✅ 权限系统: 三层权限控制(模式 → 允许列表 → 黑名单)
- ✅ 安全性: 默认需要确认,YOLO 模式需显式启用
- ⚠️ 限制: 非交互模式下不能动态请求权限
- ⚠️ 配置: 必须预先配置好权限或使用 YOLO 模式
12.2 对 Claude Code SDK 的意义
- 可行性: ✅ 可以完美适配 Claude Code SDK
- 功能完整: ✅ 支持所有必要功能
- 安全性: ✅ 有完善的权限控制
- 易用性: ⚠️ 需要正确配置权限
12.3 下一步工作
- 实现 SDK 的子进程通信层
- 实现事件流解析器
- 实现权限配置管理
- 实现错误处理和恢复
- 添加测试用例
附录
A. 相关代码文件
packages/cli/src/nonInteractiveCli.ts- 非交互模式主入口packages/core/src/core/nonInteractiveToolExecutor.ts- 工具执行器packages/core/src/core/coreToolScheduler.ts- 工具调度器packages/core/src/utils/shell-permissions.ts- 权限控制packages/core/src/utils/tool-utils.ts- 工具匹配工具
B. 配置参考
toml
# ~/.gemini/config.toml
[general]
approval_mode = "default" # or "yolo", "auto_edit"
[tools]
# 允许的工具
core_tools = [
"run_shell_command(git)",
"run_shell_command(ls)",
"run_shell_command(cat)",
"read_file",
"write_to_file",
"search_files"
]
# 排除的工具
exclude_tools = [
"run_shell_command(rm)",
"run_shell_command(del)",
"run_shell_command(format)"
]
C. 命令行参数参考
bash
# 非交互模式
gemini -p "your prompt"
gemini --prompt "your prompt"
# 权限模式
gemini -y -p "prompt" # YOLO 模式
gemini --approval-mode=yolo -p "prompt" # 明确指定
gemini --approval-mode=default -p "prompt" # 默认模式
# 输出格式
gemini -p "prompt" --output=json # JSON 输出
gemini -p "prompt" --output=stream-json # 流式 JSON
# 其他选项
gemini -p "prompt" --max-turns=10 # 最大轮次
gemini -p "prompt" --model=gemini-2.0 # 指定模型
文档版本 : 1.0
最后更新 : 2025-01-02
作者: 基于 Gemini CLI 源码分析
Gemini CLI 非交互模式工具调用机制详解
概述
本文档详细解析 Gemini CLI 在非交互模式(-p 参数)下如何实现工具调用和权限控制,这对理解如何将 Claude Code SDK 适配到 Gemini CLI 非常重要。
1. 整体架构
1.1 核心文件位置
gemini-cli/
├── packages/cli/src/
│ ├── nonInteractiveCli.ts # 非交互模式主入口
│ ├── nonInteractiveCliCommands.ts # 非交互命令处理
│ └── nonInteractive/
│ ├── session.ts # 会话管理
│ └── types.ts # 类型定义
└── packages/core/src/
├── core/
│ ├── nonInteractiveToolExecutor.ts # 工具执行器
│ └── coreToolScheduler.ts # 工具调度器(核心)
└── utils/
└── shell-permissions.ts # Shell 权限控制
1.2 执行流程图
用户输入 (-p "prompt")
↓
nonInteractiveCli.runNonInteractive()
↓
处理输入(@命令、斜杠命令)
↓
geminiClient.sendMessageStream()
↓
监听事件流
├─ GeminiEventType.Content → 输出文本
├─ GeminiEventType.ToolCallRequest → 收集工具请求
└─ GeminiEventType.Error → 处理错误
↓
executeToolCall() → CoreToolScheduler
↓
权限检查 (isAutoApproved)
├─ YOLO 模式 → 自动批准
├─ 允许列表匹配 → 自动批准
└─ 非交互模式 + 需要确认 → 抛出错误
↓
工具执行
↓
结果转换为 FunctionResponse
↓
发送回 Gemini (多轮对话)
↓
继续直到无工具调用
2. 非交互模式主流程
2.1 入口函数:runNonInteractive()
文件 : packages/cli/src/nonInteractiveCli.ts
typescript
export async function runNonInteractive({
config,
settings,
input, // 用户输入
prompt_id,
hasDeprecatedPromptArg,
resumedSessionData,
}: RunNonInteractiveParams): Promise<void>
关键步骤:
-
初始化环境
- 设置 ConsolePatcher(捕获 console 输出)
- 创建工作区 stdio
- 设置取消监听器(Ctrl+C)
-
处理输入
typescript// 处理斜杠命令 if (isSlashCommand(input)) { query = await handleSlashCommand(input, ...); } // 处理 @ 命令(@include 等) if (!query) { const { processedQuery, error } = await handleAtCommand({ query: input, config, ... }); query = processedQuery; } -
多轮对话循环
typescriptwhile (true) { // 1. 发送消息 const responseStream = geminiClient.sendMessageStream( currentMessages[0]?.parts || [], abortController.signal, prompt_id, ); // 2. 收集工具调用请求 const toolCallRequests: ToolCallRequestInfo[] = []; for await (const event of responseStream) { if (event.type === GeminiEventType.ToolCallRequest) { toolCallRequests.push(event.value); } } // 3. 如果有工具调用 if (toolCallRequests.length > 0) { for (const requestInfo of toolCallRequests) { const completedToolCall = await executeToolCall( config, requestInfo, abortController.signal, ); // 收集结果 completedToolCalls.push(completedToolCall); } // 4. 将工具结果发送回 Gemini currentMessages = [{ role: 'user', parts: toolResponseParts }]; } else { // 5. 没有工具调用,结束 return; } }
3. 工具执行器
3.1 executeToolCall 函数
文件 : packages/core/src/core/nonInteractiveToolExecutor.ts
typescript
export async function executeToolCall(
config: Config,
toolCallRequest: ToolCallRequestInfo,
abortSignal: AbortSignal,
): Promise<CompletedToolCall> {
return new Promise<CompletedToolCall>((resolve, reject) => {
// 创建工具调度器
const scheduler = new CoreToolScheduler({
config,
getPreferredEditor: () => undefined,
onAllToolCallsComplete: async (completedToolCalls) => {
if (completedToolCalls.length > 0) {
resolve(completedToolCalls[0]);
} else {
reject(new Error('No completed tool calls returned.'));
}
},
});
// 调度工具执行
scheduler.schedule(toolCallRequest, abortSignal).catch((error) => {
reject(error);
});
});
}
4. 工具调度器(Core)
4.1 CoreToolScheduler 类
文件 : packages/core/src/core/coreToolScheduler.ts
这是整个工具调用系统的核心,负责:
- 工具生命周期管理
- 权限检查
- 执行调度
- 结果处理
4.2 工具状态机
typescript
export type ToolCall =
| ValidatingToolCall // 验证中
| ScheduledToolCall // 已调度
| ErroredToolCall // 错误
| SuccessfulToolCall // 成功
| ExecutingToolCall // 执行中
| CancelledToolCall // 已取消
| WaitingToolCall; // 等待批准(交互模式)
状态转换:
Validating → Scheduled → Executing → Success
↓ ↓
WaitingApproval Error
↓
Cancelled
4.3 调度流程
typescript
async schedule(
request: ToolCallRequestInfo | ToolCallRequestInfo[],
signal: AbortSignal,
): Promise<void> {
// 1. 创建工具调用对象
const newToolCalls: ToolCall[] = requestsToProcess.map(
(reqInfo): ToolCall => {
const toolInstance = this.config
.getToolRegistry()
.getTool(reqInfo.name);
const invocation = toolInstance.build(reqInfo.args);
return {
status: 'validating',
request: reqInfo,
tool: toolInstance,
invocation: invocation,
startTime: Date.now(),
};
},
);
// 2. 处理每个工具调用
await this._processNextInQueue(signal);
}
5. 权限控制机制
5.1 权限检查函数
文件 : packages/core/src/core/coreToolScheduler.ts:1442-1456
typescript
private isAutoApproved(toolCall: ValidatingToolCall): boolean {
// 1. YOLO 模式:全部自动批准
if (this.config.getApprovalMode() === ApprovalMode.YOLO) {
return true;
}
// 2. 获取允许的工具列表
const allowedTools = this.config.getAllowedTools() || [];
const { tool, invocation } = toolCall;
const toolName = typeof tool === 'string' ? tool : tool.name;
// 3. Shell 命令特殊处理
if (SHELL_TOOL_NAMES.includes(toolName)) {
return isShellInvocationAllowlisted(invocation, allowedTools);
}
// 4. 其他工具匹配检查
return doesToolInvocationMatch(tool, invocation, allowedTools);
}
5.2 非交互模式的限制
文件 : packages/core/src/core/coreToolScheduler.ts:898-904
typescript
if (!this.config.isInteractive()) {
throw new Error(
`Tool execution for "${
toolCall.tool.displayName || toolCall.tool.name
}" requires user confirmation, which is not supported in non-interactive mode.`,
);
}
重要: 在非交互模式下,如果工具需要用户确认但不在自动批准列表中,会直接报错退出。
6. ApprovalMode 枚举
6.1 模式定义
typescript
enum ApprovalMode {
DEFAULT, // 默认模式:需要确认(除非在允许列表中)
YOLO, // 全自动模式:自动批准所有工具
AUTO_EDIT, // 自动编辑模式:自动批准编辑操作
}
6.2 模式配置
命令行参数:
bash
# YOLO 模式
gemini -y "prompt"
gemini --yolo "prompt"
gemini --approval-mode=yolo "prompt"
# 默认模式
gemini --approval-mode=default "prompt"
# 自动编辑模式
gemini --approval-mode=auto_edit "prompt"
配置文件:
toml
[general]
approval_mode = "yolo" # 或 "default", "auto_edit"
7. Shell 命令权限系统
7.1 权限检查函数
文件 : packages/core/src/utils/shell-permissions.ts
typescript
export function checkCommandPermissions(
command: string,
config: Config,
sessionAllowlist?: Set<string>,
): {
allAllowed: boolean;
disallowedCommands: string[];
blockReason?: string;
isHardDenial?: boolean;
}
7.2 两种权限模式
模式 1: "Default Deny"(默认拒绝)
- 用途: 自定义命令脚本
- 规则: 命令必须在全局允许列表或会话允许列表中
- 优先级 :
- 黑名单检查(最高)
- 会话允许列表
- 全局允许列表
模式 2: "Default Allow"(默认允许)
- 用途: 直接工具调用(AI 调用)
- 规则 :
- 如果有严格的允许列表,命令必须在其中
- 不能在黑名单中
- 如果没有允许列表,允许所有(除了黑名单)
7.3 权限检查顺序
typescript
// 1. 黑名单检查(最高优先级)
const excludeTools = config.getExcludeTools();
if (excludeTools.has('run_shell_command')) {
return { allAllowed: false, blockReason: 'Shell tool is disabled' };
}
// 2. 检查具体命令是否在黑名单
if (doesToolInvocationMatch('run_shell_command', invocation, excludeTools)) {
return { allAllowed: false, blockReason: 'Command is blocked' };
}
// 3. 通配符检查
const coreTools = config.getCoreTools();
if (coreTools.includes('run_shell_command')) {
return { allAllowed: true }; // 所有 shell 命令都允许
}
// 4. 允许列表匹配
if (sessionAllowlist) {
// Default Deny 模式
if (!matchesAllowlist(command, sessionAllowlist) &&
!matchesAllowlist(command, coreTools)) {
return { allAllowed: false };
}
} else {
// Default Allow 模式
if (hasSpecificAllowedCommands && !matchesAllowlist(command, coreTools)) {
return { allAllowed: false };
}
}
return { allAllowed: true };
7.4 Shell 命令自动批准
文件 : packages/core/src/utils/shell-permissions.ts:210-270
typescript
export function isShellInvocationAllowlisted(
invocation: AnyToolInvocation,
allowedPatterns: string[],
): boolean {
if (!allowedPatterns.length) {
return false;
}
// 检查是否有通配符
const hasShellWildcard = allowedPatterns.some((pattern) =>
SHELL_TOOL_NAMES.includes(pattern),
);
if (hasShellWildcard) {
return true; // 所有 shell 命令都允许
}
// 检查具体命令
const command = invocation.params.command;
const parseResult = parseCommandDetails(command);
const commandsToValidate = parseResult.details.map(d => d.text);
// 每个命令段都必须匹配
return commandsToValidate.every((cmd) =>
doesToolInvocationMatch(
'run_shell_command',
{ params: { command: cmd } },
allowedPatterns,
),
);
}
8. 允许列表配置
8.1 配置方式
配置文件 (~/.gemini/config.toml):
toml
# 允许所有 shell 命令
[tools]
core_tools = ["run_shell_command"]
# 只允许特定命令
[tools]
core_tools = ["run_shell_command(git)", "run_shell_command(ls)", "run_shell_command(cat)"]
# 允许特定工具
[tools]
core_tools = ["read_file", "write_to_file", "search_files"]
# 排除特定工具
[tools]
exclude_tools = ["run_shell_command(rm)", "run_shell_command(del)"]
8.2 模式匹配语法
typescript
// 通配符:所有 shell 命令
"run_shell_command"
// 特定命令:只允许 git
"run_shell_command(git)"
// 带参数:只允许 git status
"run_shell_command(git status)"
// 通配符文件:所有文件读取
"read_file(*)"
// 特定路径:只允许特定目录
"read_file(/path/to/dir/*)"
9. 工具调用事件流
9.1 事件类型
typescript
enum GeminiEventType {
Content, // 文本内容
ToolCallRequest, // 工具调用请求
LoopDetected, // 检测到循环
MaxSessionTurns, // 超过最大轮次
Error, // 错误
}
9.2 工具调用请求结构
typescript
interface ToolCallRequestInfo {
callId: string; // 调用 ID
name: string; // 工具名称
args: Record<string, unknown>; // 工具参数
}
9.3 工具响应结构
typescript
interface ToolCallResponseInfo {
callId: string;
responseParts: Part[]; // Gemini FunctionResponse 格式
resultDisplay?: ToolResultDisplay; // 显示结果
error?: Error;
errorType?: ToolErrorType;
contentLength?: number;
outputFile?: string; // 截断输出的文件路径
}
10. 与 Claude Code SDK 的适配要点
10.1 关键发现
-
非交互模式完全支持工具调用
- 通过事件流机制
- 自动多轮对话
- 无需用户干预(如果配置正确)
-
权限系统完善
- 三层控制:ApprovalMode → 允许列表 → 黑名单
- 支持细粒度配置
- 安全性高
-
错误处理
- 非交互模式下不能请求用户确认
- 必须预先配置权限
- 否则直接报错
10.2 SDK 适配建议
配置策略
python
# SDK 启动 Gemini CLI 时推荐配置
config = {
'approval_mode': 'yolo', # 或配置允许列表
'allowed_tools': [
'run_shell_command(git)',
'read_file',
'write_to_file',
'search_files'
],
'exclude_tools': [
'run_shell_command(rm)',
'run_shell_command(del)'
]
}
通信协议
python
# SDK 需要处理的事件流
async for event in gemini_process.stdout:
if event.type == 'content':
# 处理文本输出
print(event.value)
elif event.type == 'tool_use':
# 工具调用开始
print(f"Tool: {event.tool_name}")
print(f"Args: {event.parameters}")
elif event.type == 'tool_result':
# 工具执行结果
print(f"Result: {event.output}")
elif event.type == 'error':
# 处理错误
print(f"Error: {event.message}")
最佳实践
-
使用 YOLO 模式进行开发
bashgemini -y "prompt" -
生产环境使用允许列表
toml[tools] core_tools = [ "run_shell_command(git)", "read_file(*)", "write_to_file(*)" ] -
处理长输出
- SDK 需要处理工具输出截断
- 读取
outputFile字段 - 使用 Read 工具读取完整输出
-
错误恢复
- 捕获非交互模式错误
- 提供清晰的配置指引
- 支持会话恢复
11. 完整示例
11.1 非交互模式调用流程
bash
# 启动命令
gemini -p "列出当前目录的文件,然后创建一个 test.txt"
# 内部执行流程
1. 发送消息到 Gemini
2. Gemini 返回: run_shell_command("ls")
3. 检查权限: isAutoApproved() → true
4. 执行工具: ls
5. 返回结果给 Gemini
6. Gemini 返回: write_to_file("test.txt", ...)
7. 检查权限: isAutoApproved() → true
8. 执行工具: 创建文件
9. 返回结果给 Gemini
10. Gemini 返回文本: "文件已创建"
11. 输出到控制台
12. 结束
11.2 权限拒绝示例
bash
# 配置: 只允许 git 命令
gemini -p "删除所有文件"
# 内部执行流程
1. 发送消息到 Gemini
2. Gemini 返回: run_shell_command("rm -rf *")
3. 检查权限: isAutoApproved() → false
4. 非交互模式检查: isInteractive() → false
5. 抛出错误:
"Tool execution requires user confirmation,
which is not supported in non-interactive mode."
6. 退出,返回错误码
12. 总结
12.1 核心要点
- ✅ 完整支持: 非交互模式完全支持工具调用和多轮对话
- ✅ 权限系统: 三层权限控制(模式 → 允许列表 → 黑名单)
- ✅ 安全性: 默认需要确认,YOLO 模式需显式启用
- ⚠️ 限制: 非交互模式下不能动态请求权限
- ⚠️ 配置: 必须预先配置好权限或使用 YOLO 模式
12.2 对 Claude Code SDK 的意义
- 可行性: ✅ 可以完美适配 Claude Code SDK
- 功能完整: ✅ 支持所有必要功能
- 安全性: ✅ 有完善的权限控制
- 易用性: ⚠️ 需要正确配置权限
12.3 下一步工作
- 实现 SDK 的子进程通信层
- 实现事件流解析器
- 实现权限配置管理
- 实现错误处理和恢复
- 添加测试用例
附录
A. 相关代码文件
packages/cli/src/nonInteractiveCli.ts- 非交互模式主入口packages/core/src/core/nonInteractiveToolExecutor.ts- 工具执行器packages/core/src/core/coreToolScheduler.ts- 工具调度器packages/core/src/utils/shell-permissions.ts- 权限控制packages/core/src/utils/tool-utils.ts- 工具匹配工具
B. 配置参考
toml
# ~/.gemini/config.toml
[general]
approval_mode = "default" # or "yolo", "auto_edit"
[tools]
# 允许的工具
core_tools = [
"run_shell_command(git)",
"run_shell_command(ls)",
"run_shell_command(cat)",
"read_file",
"write_to_file",
"search_files"
]
# 排除的工具
exclude_tools = [
"run_shell_command(rm)",
"run_shell_command(del)",
"run_shell_command(format)"
]
C. 命令行参数参考
bash
# 非交互模式
gemini -p "your prompt"
gemini --prompt "your prompt"
# 权限模式
gemini -y -p "prompt" # YOLO 模式
gemini --approval-mode=yolo -p "prompt" # 明确指定
gemini --approval-mode=default -p "prompt" # 默认模式
# 输出格式
gemini -p "prompt" --output=json # JSON 输出
gemini -p "prompt" --output=stream-json # 流式 JSON
# 其他选项
gemini -p "prompt" --max-turns=10 # 最大轮次
gemini -p "prompt" --model=gemini-2.0 # 指定模型
文档版本 : 1.0
最后更新 : 2025-01-02
作者: 基于 Gemini CLI 源码分析