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>

关键步骤:

  1. 初始化环境

    • 设置 ConsolePatcher(捕获 console 输出)
    • 创建工作区 stdio
    • 设置取消监听器(Ctrl+C)
  2. 处理输入

    typescript 复制代码
    // 处理斜杠命令
    if (isSlashCommand(input)) {
      query = await handleSlashCommand(input, ...);
    }
    
    // 处理 @ 命令(@include 等)
    if (!query) {
      const { processedQuery, error } = await handleAtCommand({
        query: input,
        config,
        ...
      });
      query = processedQuery;
    }
  3. 多轮对话循环

    typescript 复制代码
    while (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"(默认拒绝)
  • 用途: 自定义命令脚本
  • 规则: 命令必须在全局允许列表或会话允许列表中
  • 优先级 :
    1. 黑名单检查(最高)
    2. 会话允许列表
    3. 全局允许列表
模式 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 关键发现

  1. 非交互模式完全支持工具调用

    • 通过事件流机制
    • 自动多轮对话
    • 无需用户干预(如果配置正确)
  2. 权限系统完善

    • 三层控制:ApprovalMode → 允许列表 → 黑名单
    • 支持细粒度配置
    • 安全性高
  3. 错误处理

    • 非交互模式下不能请求用户确认
    • 必须预先配置权限
    • 否则直接报错

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}")
最佳实践
  1. 使用 YOLO 模式进行开发

    bash 复制代码
    gemini -y "prompt"
  2. 生产环境使用允许列表

    toml 复制代码
    [tools]
    core_tools = [
        "run_shell_command(git)",
        "read_file(*)",
        "write_to_file(*)"
    ]
  3. 处理长输出

    • SDK 需要处理工具输出截断
    • 读取 outputFile 字段
    • 使用 Read 工具读取完整输出
  4. 错误恢复

    • 捕获非交互模式错误
    • 提供清晰的配置指引
    • 支持会话恢复

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 的意义

  1. 可行性: ✅ 可以完美适配 Claude Code SDK
  2. 功能完整: ✅ 支持所有必要功能
  3. 安全性: ✅ 有完善的权限控制
  4. 易用性: ⚠️ 需要正确配置权限

12.3 下一步工作

  1. 实现 SDK 的子进程通信层
  2. 实现事件流解析器
  3. 实现权限配置管理
  4. 实现错误处理和恢复
  5. 添加测试用例

附录

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>

关键步骤:

  1. 初始化环境

    • 设置 ConsolePatcher(捕获 console 输出)
    • 创建工作区 stdio
    • 设置取消监听器(Ctrl+C)
  2. 处理输入

    typescript 复制代码
    // 处理斜杠命令
    if (isSlashCommand(input)) {
      query = await handleSlashCommand(input, ...);
    }
    
    // 处理 @ 命令(@include 等)
    if (!query) {
      const { processedQuery, error } = await handleAtCommand({
        query: input,
        config,
        ...
      });
      query = processedQuery;
    }
  3. 多轮对话循环

    typescript 复制代码
    while (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"(默认拒绝)
  • 用途: 自定义命令脚本
  • 规则: 命令必须在全局允许列表或会话允许列表中
  • 优先级 :
    1. 黑名单检查(最高)
    2. 会话允许列表
    3. 全局允许列表
模式 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 关键发现

  1. 非交互模式完全支持工具调用

    • 通过事件流机制
    • 自动多轮对话
    • 无需用户干预(如果配置正确)
  2. 权限系统完善

    • 三层控制:ApprovalMode → 允许列表 → 黑名单
    • 支持细粒度配置
    • 安全性高
  3. 错误处理

    • 非交互模式下不能请求用户确认
    • 必须预先配置权限
    • 否则直接报错

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}")
最佳实践
  1. 使用 YOLO 模式进行开发

    bash 复制代码
    gemini -y "prompt"
  2. 生产环境使用允许列表

    toml 复制代码
    [tools]
    core_tools = [
        "run_shell_command(git)",
        "read_file(*)",
        "write_to_file(*)"
    ]
  3. 处理长输出

    • SDK 需要处理工具输出截断
    • 读取 outputFile 字段
    • 使用 Read 工具读取完整输出
  4. 错误恢复

    • 捕获非交互模式错误
    • 提供清晰的配置指引
    • 支持会话恢复

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 的意义

  1. 可行性: ✅ 可以完美适配 Claude Code SDK
  2. 功能完整: ✅ 支持所有必要功能
  3. 安全性: ✅ 有完善的权限控制
  4. 易用性: ⚠️ 需要正确配置权限

12.3 下一步工作

  1. 实现 SDK 的子进程通信层
  2. 实现事件流解析器
  3. 实现权限配置管理
  4. 实现错误处理和恢复
  5. 添加测试用例

附录

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 源码分析

相关推荐
代码游侠2 小时前
复习——SQLite3 数据库
linux·服务器·数据库·笔记·网络协议·sqlite
hgz07104 小时前
Docker Compose
运维·docker·容器
chenyuhao20244 小时前
Linux网络编程:传输层协议UDP
linux·服务器·网络·后端·udp
小鹏linux4 小时前
【linux】进程与服务管理命令 - batch
linux·运维·服务器
人工智能训练10 小时前
OpenEnler等Linux系统中安装git工具的方法
linux·运维·服务器·git·vscode·python·ubuntu
郭涤生10 小时前
第十章_信号_《UNIX环境高级编程(第三版)》_笔记
服务器·笔记·unix
QT 小鲜肉11 小时前
【Linux命令大全】001.文件管理之which命令(实操篇)
linux·运维·服务器·前端·chrome·笔记
额呃呃11 小时前
select详细分析
服务器
网创联盟,知识导航11 小时前
沐雨云香港大宽带云服务器 · 配置全览
服务器·阿里云·腾讯云