Gemini CLI 深度源码分析:从零到一理解 AI 命令行代理的设计与实现

33 阅读13分钟

Gemini CLI 深度源码分析:从零到一理解 AI 命令行代理的设计与实现

本文基于 Gemini CLI v0.21.0 源码进行深度分析,带你从架构设计、核心原理到实现细节,全方位理解这个 Google 开源的 AI 命令行代理工具。

目录

  1. 什么是 Gemini CLI?
  2. 整体架构设计
  3. 核心模块详解
  4. 记忆系统:让 AI "记住" 你的偏好
  5. 上下文工程:如何管理有限的 Token 窗口
  6. 工具系统:AI 如何执行实际操作
  7. MCP 协议:标准化的 AI 工具扩展
  8. 通信协议与流式响应
  9. Hook 系统:可扩展的生命周期钩子
  10. UI 层:终端中的 React 应用
  11. 使用指南
  12. 总结与思考

1. 什么是 Gemini CLI?

1.1 简单来说

Gemini CLI 是 Google 开源的一个 AI 命令行代理(AI Agent)。你可以把它想象成一个住在你终端里的智能助手,它能:

  • 理解你的自然语言指令:比如"帮我修复这个 bug"、"给这个函数加上单元测试"
  • 自动执行操作:读写文件、运行命令、搜索代码
  • 记住你的偏好:你喜欢用 TypeScript、偏好 4 空格缩进等
  • 持续对话:可以恢复之前的会话继续工作

1.2 技术栈一览

类别技术说明
语言TypeScript 5.3+核心开发语言
运行时Node.js 20+最低版本要求
UI 框架React 19 + Ink终端中的 React
包管理npm workspacesMonorepo 结构
构建工具esbuild快速打包
测试框架Vitest单元测试
AI SDK@google/genaiGemini API 客户端
扩展协议MCP SDK v1.23工具扩展支持

1.3 项目结构

gemini-cli/
├── packages/
│   ├── cli/          # 前端 UI 层 - 用户交互界面
│   ├── core/         # 核心业务逻辑 - AI 调用、工具执行
│   ├── a2a-server/   # Agent-to-Agent 服务器
│   └── vscode-ide-companion/  # VS Code 插件
├── bundle/           # 打包后的可执行文件
├── scripts/          # 构建和自动化脚本
└── integration-tests/ # 集成测试

2. 整体架构设计

2.1 分层架构图

graph LR
    subgraph "用户层"
        A[用户输入] --> B[CLI 入口<br/>gemini.tsx]
    end

    subgraph &#34;UI 层 - packages/cli&#34;
        B --> C[AppContainer<br/>React 应用容器]
        C --> D[Composer<br/>输入组件]
        C --> E[MessageDisplay<br/>消息展示]
        C --> F[ToolCallDisplay<br/>工具调用展示]
    end

    subgraph &#34;服务层 - packages/core&#34;
        D --> G[GeminiClient<br/>对话管理器]
        G --> H[GeminiChat<br/>API 交互]
        G --> I[ToolScheduler<br/>工具调度器]
        G --> J[ContextManager<br/>上下文管理]
    end

    subgraph &#34;工具层&#34;
        I --> K[内置工具<br/>read/write/edit/shell]
        I --> L[MCP 工具<br/>外部扩展]
    end

    subgraph &#34;外部服务&#34;
        H --> M[Gemini API<br/>REST + SSE]
        L --> N[MCP Server<br/>Stdio/SSE]
    end

    style A fill:#e1f5fe
    style M fill:#fff3e0
    style N fill:#fff3e0

2.2 核心设计原则

项目采用了几个关键的设计原则:

1. 前后端分离

  • packages/cli:负责用户界面、输入处理、显示渲染
  • packages/core:负责业务逻辑、API 调用、工具执行

2. 流式处理

  • 使用 JavaScript 的 AsyncGenerator 实现流式响应
  • 用户可以实时看到 AI 的输出,而不是等待完整响应

3. 插件化工具系统

  • 内置工具通过统一的 Tool 接口注册
  • 外部工具通过 MCP 协议动态发现和调用

4. 分层记忆

  • 全局记忆:用户级别的偏好设置
  • 项目记忆:项目级别的约定
  • 会话记忆:当前对话的上下文

3. 核心模块详解

3.1 启动流程

让我们跟随代码,看看当你在终端输入 gemini 时发生了什么:

sequenceDiagram
    participant User as 用户
    participant CLI as gemini.tsx
    participant Config as 配置加载
    participant Auth as 认证模块
    participant App as AppContainer
    participant Client as GeminiClient

    User->>CLI: 执行 gemini 命令
    CLI->>Config: 加载配置 loadCliConfig()
    Config-->>CLI: 配置对象
    CLI->>Auth: 检查认证状态
    Auth-->>CLI: 认证结果

    alt 需要认证
        CLI->>User: 显示认证界面
        User->>CLI: 完成认证
    end

    CLI->>App: 渲染 React 应用
    App->>Client: 初始化 GeminiClient
    Client->>Client: 加载工具、记忆
    Client-->>App: 准备就绪
    App-->>User: 显示输入框

关键代码位置:

  • 入口文件:packages/cli/src/gemini.tsx
  • 配置加载:packages/cli/src/config/config.ts
  • 应用容器:packages/cli/src/ui/AppContainer.tsx

3.2 GeminiClient - 对话管理的核心

GeminiClient 是整个系统的"大脑",它协调了对话的方方面面:

// packages/core/src/core/client.ts
export class GeminiClient {
  private chat?: GeminiChat;           // 实际的 API 交互
  private sessionTurnCount = 0;        // 会话轮次计数
  private loopDetector: LoopDetectionService;      // 循环检测
  private compressionService: ChatCompressionService; // 上下文压缩

  // 最大轮次限制,防止无限循环
  private static MAX_TURNS = 100;
}

主要职责:

职责说明
对话初始化创建 GeminiChat 实例,设置系统提示
消息发送处理用户输入,调用 API
工具调用协调当 AI 请求工具时,协调执行
上下文压缩当 token 超限时,压缩历史
循环检测防止 AI 陷入无限工具调用循环
会话恢复支持从之前的会话继续

3.3 Turn - 一次对话轮次

每次用户发送消息到收到完整回复,被称为一个 "Turn":

graph LR
    A[用户消息] --> B[发送到 API]
    B --> C{响应类型}
    C -->|文本| D[显示内容]
    C -->|工具调用| E[执行工具]
    C -->|思考| F[显示思考过程]
    E --> G[工具结果]
    G --> B
    D --> H[Turn 结束]
    F --> H

事件类型定义:

// packages/core/src/core/turn.ts
export enum GeminiEventType {
  Content = 'content',           // 文本内容
  ToolCallRequest = 'tool_call_request',    // 工具调用请求
  ToolCallResponse = 'tool_call_response',  // 工具调用响应
  Thought = 'thought',           // AI 的思考过程
  Finished = 'finished',         // 完成
  Error = 'error',               // 错误
  ChatCompressed = 'chat_compressed',       // 上下文被压缩
  LoopDetected = 'loop_detected',           // 检测到循环
  // ...
}

4. 记忆系统:让 AI "记住" 你的偏好

4.1 记忆的层次结构

Gemini CLI 实现了一个三层记忆系统,让 AI 能够记住从全局偏好到项目特定约定的各种信息:

graph LR
    subgraph &#34;记忆层次(优先级从低到高)&#34;
        A[&#34;全局记忆<br/>~/.gemini/GEMINI.md&#34;] --> B[&#34;项目记忆<br/>project/GEMINI.md&#34;]
        B --> C[&#34;子目录记忆<br/>project/src/GEMINI.md&#34;]
    end

    subgraph &#34;记忆内容示例&#34;
        D[&#34;全局偏好<br/>- 喜欢 TypeScript<br/>- 偏好 4 空格缩进&#34;]
        E[&#34;项目约定<br/>- 使用 ESLint<br/>- 测试用 Vitest&#34;]
        F[&#34;模块特定<br/>- 组件用函数式<br/>- 状态用 hooks&#34;]
    end

    A --- D
    B --- E
    C --- F

    style A fill:#e8f5e9
    style B fill:#e3f2fd
    style C fill:#fce4ec

4.2 记忆文件格式

记忆存储在 Markdown 文件中,便于人类阅读和编辑:

# GEMINI.md - 项目记忆文件

## 项目概述
这是一个 React + TypeScript 前端项目。

## 编码约定
- 使用函数式组件,不用类组件
- 状态管理使用 React hooks
- 样式使用 Tailwind CSS

## 构建和测试
- 构建命令:`npm run build`
- 测试命令:`npm test`
- Lint 命令:`npm run lint`

## Gemini Added Memories
- 用户偏好使用 async/await 而不是 .then()
- 日志使用 console.log 而不是 alert

4.3 记忆发现机制

sequenceDiagram
    participant App as 应用
    participant Discovery as MemoryDiscovery
    participant FS as 文件系统
    participant Processor as ImportProcessor

    App->>Discovery: loadServerHierarchicalMemory()

    Note over Discovery: 第一步:查找全局记忆
    Discovery->>FS: 检查 ~/.gemini/GEMINI.md
    FS-->>Discovery: 文件内容(如果存在)

    Note over Discovery: 第二步:向上查找项目根
    Discovery->>FS: findProjectRoot(查找 .git)
    FS-->>Discovery: 项目根目录路径

    Note over Discovery: 第三步:收集所有 GEMINI.md
    loop 从当前目录到项目根
        Discovery->>FS: 检查目录下的 GEMINI.md
        FS-->>Discovery: 文件路径
    end

    Note over Discovery: 第四步:处理 @import
    Discovery->>Processor: processImports()
    loop 递归处理导入
        Processor->>FS: 读取被导入的文件
        FS-->>Processor: 文件内容
    end

    Processor-->>Discovery: 合并后的记忆内容
    Discovery-->>App: 最终的用户记忆字符串

核心代码解析:

// packages/core/src/utils/memoryDiscovery.ts
async function getGeminiMdFilePathsInternal(
  currentWorkingDirectory: string,
  includeDirectoriesToReadGemini: readonly string[],
  userHomePath: string,
  // ...
): Promise {
  // 1. 查找全局记忆文件
  const globalMemoryPath = path.join(homedir(), '.gemini', 'GEMINI.md');

  // 2. 从当前目录向上查找项目根目录
  const projectRoot = await findProjectRoot(currentWorkingDirectory);

  // 3. 收集路径上的所有 GEMINI.md 文件
  // 顺序:全局 -> 项目根 -> 子目录(越近优先级越高)
}

4.4 保存记忆

当你让 AI "记住" 某些事情时,会调用 MemoryTool

// packages/core/src/tools/memoryTool.ts
export class MemoryTool extends BaseDeclarativeTool {
  // 保存事实到记忆文件
  async performAddMemoryEntry(fact: string): Promise {
    // 1. 读取当前 GEMINI.md 内容
    const content = await fs.readFile(memoryPath, 'utf-8');

    // 2. 找到 &#34;## Gemini Added Memories&#34; 部分
    // 3. 添加新的记忆项
    const newContent = addMemoryEntry(content, fact);

    // 4. 写回文件
    await fs.writeFile(memoryPath, newContent);
  }
}

5. 上下文工程:如何管理有限的 Token 窗口

5.1 Token 的概念

在与 AI 对话时,每条消息都会消耗 "Token"。Token 大致可以理解为:

  • 英文:大约 4 个字符 = 1 个 Token
  • 中文:大约 1-2 个字符 = 1 个 Token

每个模型都有 Token 限制:

// packages/core/src/core/tokenLimits.ts
export function tokenLimit(model: Model): TokenCount {
  switch (model) {
    case 'gemini-1.5-pro':
      return 2_097_152;   // 200 万 Token
    case 'gemini-2.5-pro':
    case 'gemini-2.5-flash':
      return 1_048_576;   // 100 万 Token
    default:
      return 1_048_576;
  }
}

5.2 上下文组成

pie title 上下文 Token 分配(示例)
    &#34;System Prompt (系统提示)&#34; : 15
    &#34;User Memory (用户记忆)&#34; : 10
    &#34;对话历史&#34; : 50
    &#34;工具输出&#34; : 20
    &#34;环境上下文&#34; : 5

5.3 上下文管理策略

graph LR
    subgraph &#34;上下文组成&#34;
        A[System Prompt<br/>系统提示] --> E[总上下文]
        B[对话历史<br/>Messages] --> E
        C[用户记忆<br/>GEMINI.md] --> E
        D[环境上下文<br/>日期/OS/目录] --> E
    end

    E --> F{Token 数量}
    F -->|小于50%限制| G[正常继续]
    F -->|大于50%限制| H[触发压缩]

    H --> I[保留最近 30%]
    H --> J[压缩旧历史为状态快照]
    I --> K[新的对话历史]
    J --> K

5.4 对话压缩的实现

当上下文过长时,系统会自动压缩旧的对话历史:

// packages/core/src/services/chatCompressionService.ts
export const DEFAULT_COMPRESSION_TOKEN_THRESHOLD = 0.5;  // 50% 时触发
export const COMPRESSION_PRESERVE_THRESHOLD = 0.3;       // 保留最近 30%

export class ChatCompressionService {
  async compress(chat: GeminiChat, ...): Promise<{
    newHistory: Content[] | null;
    info: ChatCompressionInfo;
  }> {
    // 1. 检查是否需要压缩
    if (tokenCount < 0.5 * tokenLimit(model)) {
      return { newHistory: null, compressionStatus: 'NOOP' };
    }

    // 2. 确定分割点(保留最近 30%)
    const splitPoint = findCompressSplitPoint(
      history,
      1 - 0.3  // 压缩前 70%
    );

    // 3. 使用 AI 生成状态快照
    const summary = await this.generateStateSnapshot(historyToCompress);

    // 4. 组合新历史
    return {
      newHistory: [
        { role: 'user', parts: [{ text: summary }] },
        { role: 'model', parts: [{ text: 'Got it!' }] },
        ...historyToKeep  // 保留的最近历史
      ],
      compressionStatus: 'COMPRESSED'
    };
  }
}

5.5 状态快照格式

压缩后的状态快照包含关键信息:


    
        实现用户认证功能
    

    
        - Build Command: `npm run build`
        - Testing: Tests run with `npm test`
        - 使用 JWT 进行认证
    

    
        - CWD: `/home/user/project/src`
        - READ: `package.json`, `auth/login.ts`
        - MODIFIED: `auth/jwt.ts`
        - CREATED: `auth/__tests__/jwt.test.ts`
    

    
        - 创建了 JWT 验证函数
        - 运行测试,3 个通过,1 个失败
        - 修复了 token 过期验证的 bug
    

    
        1. [DONE] 实现 JWT 生成
        2. [DONE] 实现 JWT 验证
        3. [IN PROGRESS] 修复测试失败
        4. [TODO] 集成到登录流程
    


6. 工具系统:AI 如何执行实际操作

6.1 工具的概念

工具(Tool)是 AI 与外部世界交互的方式。当 AI 需要读文件、执行命令时,它会"调用工具":

sequenceDiagram
    participant AI as Gemini AI
    participant Scheduler as ToolScheduler
    participant Tool as 具体工具
    participant FS as 文件系统/Shell

    AI->>Scheduler: 请求调用 read_file
    Scheduler->>Scheduler: 验证参数
    Scheduler->>Tool: 创建 ReadFileTool 实例
    Tool->>Tool: shouldConfirmExecute()

    alt 需要确认
        Tool-->>User: 显示确认对话框
        User-->>Tool: 确认执行
    end

    Tool->>FS: 读取文件
    FS-->>Tool: 文件内容
    Tool-->>Scheduler: 返回结果
    Scheduler-->>AI: 工具调用结果

6.2 内置工具列表

工具名功能类型需确认
read_file读取文件内容(支持文本、图片、PDF)Read
write_file创建或覆写文件Write
edit编辑文件的特定部分Write
shell执行 Shell 命令Execute
grep在文件中搜索文本Read
glob匹配文件路径模式Read
ls列出目录内容Read
web_fetch获取网页内容Read
web_search执行网络搜索Read
memory保存信息到记忆Write

6.3 工具的生命周期

stateDiagram-v2
    [*] --> Pending: AI 请求工具
    Pending --> Validating: 开始验证
    Validating --> Scheduled: 验证通过
    Validating --> Error: 验证失败

    Scheduled --> Confirming: 需要确认
    Scheduled --> Executing: 无需确认

    Confirming --> Executing: 用户同意
    Confirming --> Cancelled: 用户拒绝

    Executing --> Success: 执行成功
    Executing --> Error: 执行失败

    Success --> [*]
    Error --> [*]
    Cancelled --> [*]

6.4 工具基类设计

所有工具都继承自 BaseDeclarativeTool

// packages/core/src/tools/tools.ts
export abstract class BaseDeclarativeTool {
  constructor(
    readonly name: string,           // 工具名称
    readonly displayName: string,    // 显示名称
    readonly description: string,    // 描述(给 AI 看)
    readonly kind: 'Read' | 'Write' | 'Execute',  // 类型
    readonly schema: FunctionDeclaration,  // 参数 schema
    readonly requiresConfirmation: boolean // 是否需要确认
  ) {}

  // 验证参数
  protected abstract validateToolParamValues(params: TParams): string | null;

  // 创建执行实例
  protected abstract createInvocation(params: TParams): ToolInvocation;
}

// 工具调用实例接口
export interface ToolInvocation {
  params: TParams;
  getDescription(): string;
  toolLocations(): ToolLocation[];
  shouldConfirmExecute(signal: AbortSignal): Promise;
  execute(signal: AbortSignal, updateOutput?: Function): Promise;
}

6.5 工具调用示例

read_file 为例,看看一次完整的工具调用:

// 1. AI 请求调用工具
{
  name: &#34;read_file&#34;,
  args: {
    file_path: &#34;/path/to/file.ts&#34;,
    should_read_entire_file: true
  }
}

// 2. 验证参数
// - 检查路径是否存在
// - 检查是否有权限读取

// 3. 执行读取
const content = await fs.readFile(filePath, 'utf-8');

// 4. 返回结果
{
  llmContent: [{ text: content }],   // 给 AI 看的内容
  returnDisplay: {
    type: 'markdown',
    content: '```typescript\n' + content + '\n```'
  }
}

7. MCP 协议:标准化的 AI 工具扩展

7.1 什么是 MCP?

MCP(Model Context Protocol)是一个开放协议,用于标准化 AI 模型与外部工具之间的通信。就像 USB 让各种设备能够连接电脑一样,MCP 让各种工具能够接入 AI。

graph TB
    subgraph &#34;Gemini CLI&#34;
        A[MCP Client] --> B[工具注册表]
    end

    subgraph &#34;MCP Servers&#34;
        C[数据库工具<br/>SQL 查询]
        D[API 工具<br/>REST 调用]
        E[自定义工具<br/>任何功能]
    end

    A |Stdio| C
    A |SSE| D
    A |HTTP| E

    style A fill:#e3f2fd
    style C fill:#fff3e0
    style D fill:#fff3e0
    style E fill:#fff3e0

7.2 MCP 支持的传输方式

graph LR
    subgraph &#34;传输方式&#34;
        A[Stdio<br/>本地进程]
        B[SSE<br/>Server-Sent Events]
        C[HTTP<br/>Streamable]
    end

    subgraph &#34;适用场景&#34;
        D[本地工具<br/>高性能]
        E[远程服务<br/>实时推送]
        F[Web API<br/>通用兼容]
    end

    A --> D
    B --> E
    C --> F
// packages/core/src/tools/mcp-client.ts

// 1. Stdio 传输 - 通过标准输入/输出与本地进程通信
const transport = new StdioClientTransport({
  command: 'node',
  args: ['./my-mcp-server.js'],
});

// 2. SSE 传输 - 通过 Server-Sent Events
const transport = new SSEClientTransport(
  new URL('https://mcp-server.example.com')
);

// 3. HTTP Streamable 传输
const transport = new StreamableHTTPClientTransport(
  new URL('https://api.example.com/mcp')
);

7.3 MCP 服务器配置

~/.gemini/settings.json 中配置:

{
  &#34;mcpServers&#34;: {
    &#34;my-database&#34;: {
      &#34;command&#34;: &#34;npx&#34;,
      &#34;args&#34;: [&#34;@my-org/db-mcp-server&#34;],
      &#34;env&#34;: {
        &#34;DATABASE_URL&#34;: &#34;postgres://...&#34;
      }
    },
    &#34;github&#34;: {
      &#34;url&#34;: &#34;https://github-mcp.example.com&#34;,
      &#34;auth&#34;: {
        &#34;type&#34;: &#34;oauth&#34;
      }
    }
  }
}

7.4 MCP 客户端实现

// packages/core/src/tools/mcp-client.ts
export enum MCPServerStatus {
  DISCONNECTED = 'disconnected',
  CONNECTING = 'connecting',
  CONNECTED = 'connected',
  DISCONNECTING = 'disconnecting'
}

export class McpClient {
  private client: Client | undefined;
  private status: MCPServerStatus = MCPServerStatus.DISCONNECTED;

  async connect(): Promise {
    this.updateStatus(MCPServerStatus.CONNECTING);

    // 创建连接
    this.client = await connectToMcpServer(
      this.serverName,
      this.serverConfig,
      this.debugMode
    );

    // 监听工具更新(动态工具发现)
    const capabilities = this.client.getServerCapabilities();
    if (capabilities?.tools?.listChanged) {
      this.client.setNotificationHandler(
        ToolListChangedNotificationSchema,
        async () => {
          await this.refreshTools();  // 动态更新工具列表
        }
      );
    }

    this.updateStatus(MCPServerStatus.CONNECTED);
  }

  async discoverTools(): Promise {
    const response = await this.client.listTools({});
    return response.tools.map(tool => new DiscoveredMCPTool(tool));
  }
}

8. 通信协议与流式响应

8.1 与 Gemini API 的通信

Gemini CLI 使用 REST + SSE 的混合通信方式:

sequenceDiagram
    participant CLI as Gemini CLI
    participant API as Gemini API

    CLI->>API: POST /generateContent<br/>(包含历史、工具、配置)

    Note over API: 开始生成响应

    loop 流式响应 (SSE)
        API-->>CLI: data: {&#34;text&#34;: &#34;正在&#34;}
        API-->>CLI: data: {&#34;text&#34;: &#34;思考&#34;}
        API-->>CLI: data: {&#34;text&#34;: &#34;...&#34;}
    end

    alt 需要调用工具
        API-->>CLI: data: {&#34;functionCall&#34;: {...}}
        CLI->>CLI: 执行工具
        CLI->>API: POST(包含工具结果)
    end

    API-->>CLI: data: {&#34;finishReason&#34;: &#34;STOP&#34;}

8.2 SSE 解析实现

// packages/core/src/code_assist/server.ts
async *requestStreamingPost(
  method: string,
  req: object,
): Promise> {
  const res = await this.client.request({
    url: this.getMethodUrl(method),
    method: 'POST',
    params: { alt: 'sse' },  // 请求 SSE 格式
    responseType: 'stream',
    body: JSON.stringify(req),
  });

  // 解析 SSE 数据流
  const rl = readline.createInterface({
    input: res.data as NodeJS.ReadableStream,
  });

  let bufferedLines: string[] = [];
  for await (const line of rl) {
    if (line.startsWith('data: ')) {
      bufferedLines.push(line.slice(6).trim());
    } else if (line === '') {
      // 空行表示一个完整的事件
      if (bufferedLines.length > 0) {
        yield JSON.parse(bufferedLines.join('\n')) as T;
        bufferedLines = [];
      }
    }
  }
}

8.3 请求格式

发送给 Gemini API 的请求结构:

interface GenerateContentRequest {
  model: string;                    // 模型名称
  contents: Content[];              // 对话历史
  systemInstruction?: Content;      // 系统提示
  tools?: Tool[];                   // 可用工具
  toolConfig?: {
    functionCallingConfig: {
      mode: 'AUTO' | 'ANY' | 'NONE' // 工具调用模式
    }
  };
  generationConfig?: {
    temperature?: number;           // 温度(创意度)
    topP?: number;                  // Top-P 采样
    topK?: number;                  // Top-K 采样
    maxOutputTokens?: number;       // 最大输出 Token
    thinkingConfig?: {
      thinkingBudget: number;       // 思考 Token 预算
    }
  };
}

9. Hook 系统:可扩展的生命周期钩子

9.1 Hook 的概念

Hook 允许你在 AI 执行的关键节点插入自定义逻辑,比如:

  • 在工具执行前审批
  • 在模型调用前/后记录日志
  • 在会话开始/结束时执行清理

9.2 可用的 Hook 事件

graph TB
    subgraph &#34;会话生命周期&#34;
        A[SessionStart] --> B[BeforeAgent]
        B --> C[BeforeModel]
        C --> D[AfterModel]
        D --> E[BeforeTool]
        E --> F[AfterTool]
        F --> G[AfterAgent]
        G --> H[SessionEnd]
    end

    subgraph &#34;特殊事件&#34;
        I[PreCompress]
        J[Notification]
        K[BeforeToolSelection]
    end
// packages/core/src/hooks/types.ts
export enum HookEventName {
  SessionStart = 'SessionStart',     // 会话开始
  SessionEnd = 'SessionEnd',         // 会话结束
  BeforeAgent = 'BeforeAgent',       // 代理执行前
  AfterAgent = 'AfterAgent',         // 代理执行后
  BeforeTool = 'BeforeTool',         // 工具执行前
  AfterTool = 'AfterTool',           // 工具执行后
  BeforeModel = 'BeforeModel',       // 模型调用前
  AfterModel = 'AfterModel',         // 模型调用后
  PreCompress = 'PreCompress',       // 上下文压缩前
  Notification = 'Notification',     // 通知事件
  BeforeToolSelection = 'BeforeToolSelection' // 工具选择前
}

9.3 Hook 执行流程

graph LR
    A[事件触发] --> B[查找匹配的 Hooks]
    B --> C{有匹配?}
    C -->|否| D[继续执行]
    C -->|是| E[准备 Hook 输入]
    E --> F[执行 Hook 命令]
    F --> G[解析 Hook 输出]
    G --> H{决定类型}
    H -->|allow| D
    H -->|deny/block| I[阻止执行]
    H -->|ask| J[询问用户]
    J --> K{用户决定}
    K -->|同意| D
    K -->|拒绝| I

9.4 配置 Hook

~/.gemini/settings.json 中配置:

{
  &#34;hooks&#34;: {
    &#34;BeforeTool&#34;: [
      {
        &#34;matcher&#34;: &#34;shell&#34;,
        &#34;command&#34;: &#34;/path/to/approve-shell.sh&#34;,
        &#34;timeout&#34;: 5000
      }
    ],
    &#34;SessionStart&#34;: [
      {
        &#34;command&#34;: &#34;echo 'Session started' >> ~/gemini.log&#34;
      }
    ]
  }
}

9.5 Hook 输入/输出格式

// Hook 接收的输入(通过 stdin)
interface HookInput {
  session_id: string;        // 会话 ID
  transcript_path: string;   // 对话记录路径
  cwd: string;               // 当前工作目录
  hook_event_name: string;   // 事件名称
  timestamp: string;         // 时间戳
  // ... 事件特定的数据
}

// Hook 返回的输出(通过 stdout)
interface HookOutput {
  continue?: boolean;        // 是否继续执行
  decision?: 'allow' | 'deny' | 'ask' | 'block';  // 决定
  reason?: string;           // 原因
  systemMessage?: string;    // 要添加的系统消息
  suppressOutput?: boolean;  // 是否抑制输出
}

10. UI 层:终端中的 React 应用

10.1 Ink - 终端中的 React

Gemini CLI 使用 Ink 库在终端中渲染 React 组件。这意味着 UI 代码和普通的 React 应用非常相似:

// packages/cli/src/ui/components/Composer.tsx
export function Composer(): JSX.Element {
  const [input, setInput] = useState('');
  const { sendMessage } = useUIActions();

  const handleSubmit = useCallback(() => {
    sendMessage(input);
    setInput('');
  }, [input, sendMessage]);

  return (
    
      › 
      
    
  );
}

10.2 UI 组件结构

graph TB
    subgraph &#34;AppContainer&#34;
        A[SettingsContext] --> B[MouseProvider]
        B --> C[SessionProvider]
        C --> D[VimModeProvider]
        D --> E[App]
    end

    subgraph &#34;App 组件&#34;
        E --> F[Header<br/>标题栏]
        E --> G[MessageList<br/>消息列表]
        E --> H[Composer<br/>输入框]
        E --> I[Footer<br/>状态栏]
    end

    subgraph &#34;消息类型&#34;
        G --> J[UserMessage<br/>用户消息]
        G --> K[GeminiMessage<br/>AI 回复]
        G --> L[ToolCallDisplay<br/>工具调用]
        G --> M[ThoughtDisplay<br/>思考过程]
    end

10.3 关键 Context

Context作用
SettingsContext应用设置和配置
SessionContext当前会话状态
UIActionsContextUI 操作(发送消息、执行工具等)
VimModeContextVim 模式支持
MouseContext鼠标交互
StreamingContext流式响应状态

10.4 主题系统

Gemini CLI 支持 24+ 种终端主题:

// packages/cli/src/ui/themes/
export interface Theme {
  name: string;
  colors: {
    primary: string;
    secondary: string;
    background: string;
    text: string;
    error: string;
    warning: string;
    success: string;
    // ...
  };
}

11. 使用指南

11.1 安装

# 使用 npm
npm install -g @google/gemini-cli

# 或者直接运行
npx @google/gemini-cli

11.2 首次配置

# 首次运行会引导你完成认证
gemini

# 支持的认证方式:
# 1. Google 账号 OAuth(推荐)
# 2. API Key

11.3 基本使用

# 交互模式
gemini

# 单次查询
gemini &#34;这个函数是做什么的?&#34;

# 指定模型
gemini --model gemini-2.5-pro &#34;解释这段代码&#34;

# 恢复上次会话
gemini --resume latest

11.4 常用命令

在交互模式中,可以使用这些命令:

命令功能
/help显示帮助
/clear清空当前会话
/memory编辑记忆文件
/model切换模型
/settings打开设置
/quitCtrl+C退出

11.5 记忆文件配置

创建项目记忆文件 GEMINI.md

# 项目说明

这是一个 Next.js 14 项目,使用 App Router。

## 技术栈
- Next.js 14
- TypeScript
- Tailwind CSS
- Prisma (PostgreSQL)

## 开发规范
- 组件放在 `src/components/`
- API 路由放在 `src/app/api/`
- 使用 Zod 进行数据验证

## 常用命令
- `npm run dev` - 启动开发服务器
- `npm run build` - 构建生产版本
- `npm run test` - 运行测试

12. 总结与思考

12.1 架构亮点

亮点说明
清晰的分层设计UI 层和业务逻辑层完全分离,便于测试和维护
优雅的流式处理使用 AsyncGenerator 实现流式响应,体验流畅
强大的扩展性MCP 协议支持任意工具扩展,Hook 系统允许自定义逻辑
智能的上下文管理三层记忆系统 + 自动压缩,充分利用 Token 窗口
完善的安全机制敏感操作需确认,策略引擎控制工具权限

12.2 可以借鉴的设计模式

模式在项目中的应用
工厂模式工具的创建和注册
策略模式模型路由、压缩策略
观察者模式事件系统、Hook 触发
装饰器模式日志包装、录制包装
适配器模式MCP 工具适配

12.3 对 AI 应用开发的启示

mindmap
  root((AI 应用开发启示))
    上下文工程
      精心设计 System Prompt
      管理好 Token 窗口
      利用压缩保留关键信息
    工具设计
      清晰的接口定义
      完善的错误处理
      考虑安全和权限
    记忆系统
      持久化重要信息
      分层管理记忆
      让用户可编辑
    用户体验
      流式响应
      实时显示进度
      优雅的错误提示

附录:核心文件速查表

功能文件路径
应用入口packages/cli/src/gemini.tsx
应用容器packages/cli/src/ui/AppContainer.tsx
对话客户端packages/core/src/core/client.ts
API 交互packages/core/src/core/geminiChat.ts
工具调度packages/core/src/core/coreToolScheduler.ts
工具基类packages/core/src/tools/tools.ts
工具注册packages/core/src/tools/tool-registry.ts
记忆发现packages/core/src/utils/memoryDiscovery.ts
记忆工具packages/core/src/tools/memoryTool.ts
上下文压缩packages/core/src/services/chatCompressionService.ts
System Promptpackages/core/src/core/prompts.ts
MCP 客户端packages/core/src/tools/mcp-client.ts
Hook 类型packages/core/src/hooks/types.ts
Token 限制packages/core/src/core/tokenLimits.ts

  • 本文基于 Gemini CLI v0.21.0 源码分析,作者通过深入阅读源码编写而成。如有任何疑问或建议,欢迎交流讨论!
  • 作者【前端领秀】一个喜欢探索前端领域AI赋能的开发者,喜欢我的可以关注我,私信我邀请进入技术群,我会时刻分享最新前沿AI技术;

扫码_搜索联合传播样式-白色版.png