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

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

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

目录

  1. [什么是 Gemini CLI?](#什么是 Gemini CLI? "#1-%E9%A1%B9%E7%9B%AE%E6%A6%82%E8%BF%B0%E4%BB%80%E4%B9%88%E6%98%AF-gemini-cli")
  2. 整体架构设计
  3. 核心模块详解
  4. [记忆系统:让 AI "记住" 你的偏好](#记忆系统:让 AI "记住" 你的偏好 "#4-%E8%AE%B0%E5%BF%86%E7%B3%BB%E7%BB%9F%E8%AE%A9-ai-%E8%AE%B0%E4%BD%8F-%E4%BD%A0%E7%9A%84%E5%81%8F%E5%A5%BD")
  5. [上下文工程:如何管理有限的 Token 窗口](#上下文工程:如何管理有限的 Token 窗口 "#5-%E4%B8%8A%E4%B8%8B%E6%96%87%E5%B7%A5%E7%A8%8B%E5%A6%82%E4%BD%95%E7%AE%A1%E7%90%86%E6%9C%89%E9%99%90%E7%9A%84-token-%E7%AA%97%E5%8F%A3")
  6. [工具系统:AI 如何执行实际操作](#工具系统:AI 如何执行实际操作 "#6-%E5%B7%A5%E5%85%B7%E7%B3%BB%E7%BB%9Fai-%E5%A6%82%E4%BD%95%E6%89%A7%E8%A1%8C%E5%AE%9E%E9%99%85%E6%93%8D%E4%BD%9C")
  7. [MCP 协议:标准化的 AI 工具扩展](#MCP 协议:标准化的 AI 工具扩展 "#7-mcp-%E5%8D%8F%E8%AE%AE%E6%A0%87%E5%87%86%E5%8C%96%E7%9A%84-ai-%E5%B7%A5%E5%85%B7%E6%89%A9%E5%B1%95")
  8. 通信协议与流式响应
  9. [Hook 系统:可扩展的生命周期钩子](#Hook 系统:可扩展的生命周期钩子 "#9-hook-%E7%B3%BB%E7%BB%9F%E5%8F%AF%E6%89%A9%E5%B1%95%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90")
  10. [UI 层:终端中的 React 应用](#UI 层:终端中的 React 应用 "#10-ui-%E5%B1%82%E7%BB%88%E7%AB%AF%E4%B8%AD%E7%9A%84-react-%E5%BA%94%E7%94%A8")
  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 workspaces Monorepo 结构
构建工具 esbuild 快速打包
测试框架 Vitest 单元测试
AI SDK @google/genai Gemini API 客户端
扩展协议 MCP SDK v1.23 工具扩展支持

1.3 项目结构

bash 复制代码
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 入口
gemini.tsx] end subgraph "UI 层 - packages/cli" B --> C[AppContainer
React 应用容器] C --> D[Composer
输入组件] C --> E[MessageDisplay
消息展示] C --> F[ToolCallDisplay
工具调用展示] end subgraph "服务层 - packages/core" D --> G[GeminiClient
对话管理器] G --> H[GeminiChat
API 交互] G --> I[ToolScheduler
工具调度器] G --> J[ContextManager
上下文管理] end subgraph "工具层" I --> K[内置工具
read/write/edit/shell] I --> L[MCP 工具
外部扩展] end subgraph "外部服务" H --> M[Gemini API
REST + SSE] L --> N[MCP Server
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 是整个系统的"大脑",它协调了对话的方方面面:

typescript 复制代码
// 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

事件类型定义:

typescript 复制代码
// 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 "记忆层次(优先级从低到高)" A["全局记忆
~/.gemini/GEMINI.md"] --> B["项目记忆
project/GEMINI.md"] B --> C["子目录记忆
project/src/GEMINI.md"] end subgraph "记忆内容示例" D["全局偏好
- 喜欢 TypeScript
- 偏好 4 空格缩进"] E["项目约定
- 使用 ESLint
- 测试用 Vitest"] F["模块特定
- 组件用函数式
- 状态用 hooks"] end A --- D B --- E C --- F style A fill:#e8f5e9 style B fill:#e3f2fd style C fill:#fce4ec

4.2 记忆文件格式

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

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: 最终的用户记忆字符串

核心代码解析:

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

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

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

4.4 保存记忆

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

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

    // 2. 找到 "## Gemini Added Memories" 部分
    // 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 限制:

typescript 复制代码
// 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 分配(示例) "System Prompt (系统提示)" : 15 "User Memory (用户记忆)" : 10 "对话历史" : 50 "工具输出" : 20 "环境上下文" : 5

5.3 上下文管理策略

graph LR subgraph "上下文组成" A[System Prompt
系统提示] --> E[总上下文] B[对话历史
Messages] --> E C[用户记忆
GEMINI.md] --> E D[环境上下文
日期/OS/目录] --> E end E --> F{Token 数量} F -->|小于50%限制| G[正常继续] F -->|大于50%限制| H[触发压缩] H --> I[保留最近 30%] H --> J[压缩旧历史为状态快照] I --> K[新的对话历史] J --> K

5.4 对话压缩的实现

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

typescript 复制代码
// 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 状态快照格式

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

xml 复制代码
<state_snapshot>
    <overall_goal>
        实现用户认证功能
    </overall_goal>

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

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

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

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

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

typescript 复制代码
// packages/core/src/tools/tools.ts
export abstract class BaseDeclarativeTool<TParams, TResult> {
  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<TParams, TResult> {
  params: TParams;
  getDescription(): string;
  toolLocations(): ToolLocation[];
  shouldConfirmExecute(signal: AbortSignal): Promise<ToolCallConfirmationDetails | false>;
  execute(signal: AbortSignal, updateOutput?: Function): Promise<TResult>;
}

6.5 工具调用示例

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

typescript 复制代码
// 1. AI 请求调用工具
{
  name: "read_file",
  args: {
    file_path: "/path/to/file.ts",
    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 "Gemini CLI" A[MCP Client] --> B[工具注册表] end subgraph "MCP Servers" C[数据库工具
SQL 查询] D[API 工具
REST 调用] E[自定义工具
任何功能] 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 "传输方式" A[Stdio
本地进程] B[SSE
Server-Sent Events] C[HTTP
Streamable] end subgraph "适用场景" D[本地工具
高性能] E[远程服务
实时推送] F[Web API
通用兼容] end A --> D B --> E C --> F
typescript 复制代码
// 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 中配置:

json 复制代码
{
  "mcpServers": {
    "my-database": {
      "command": "npx",
      "args": ["@my-org/db-mcp-server"],
      "env": {
        "DATABASE_URL": "postgres://..."
      }
    },
    "github": {
      "url": "https://github-mcp.example.com",
      "auth": {
        "type": "oauth"
      }
    }
  }
}

7.4 MCP 客户端实现

typescript 复制代码
// 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<void> {
    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<DiscoveredMCPTool[]> {
    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
(包含历史、工具、配置) Note over API: 开始生成响应 loop 流式响应 (SSE) API-->>CLI: data: {"text": "正在"} API-->>CLI: data: {"text": "思考"} API-->>CLI: data: {"text": "..."} end alt 需要调用工具 API-->>CLI: data: {"functionCall": {...}} CLI->>CLI: 执行工具 CLI->>API: POST(包含工具结果) end API-->>CLI: data: {"finishReason": "STOP"}

8.2 SSE 解析实现

typescript 复制代码
// packages/core/src/code_assist/server.ts
async *requestStreamingPost<T>(
  method: string,
  req: object,
): Promise<AsyncGenerator<T>> {
  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 的请求结构:

typescript 复制代码
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 "会话生命周期" A[SessionStart] --> B[BeforeAgent] B --> C[BeforeModel] C --> D[AfterModel] D --> E[BeforeTool] E --> F[AfterTool] F --> G[AfterAgent] G --> H[SessionEnd] end subgraph "特殊事件" I[PreCompress] J[Notification] K[BeforeToolSelection] end
typescript 复制代码
// 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 中配置:

json 复制代码
{
  "hooks": {
    "BeforeTool": [
      {
        "matcher": "shell",
        "command": "/path/to/approve-shell.sh",
        "timeout": 5000
      }
    ],
    "SessionStart": [
      {
        "command": "echo 'Session started' >> ~/gemini.log"
      }
    ]
  }
}

9.5 Hook 输入/输出格式

typescript 复制代码
// 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 应用非常相似:

tsx 复制代码
// 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 (
    <Box flexDirection="column">
      <Text color="cyan">› </Text>
      <TextInput
        value={input}
        onChange={setInput}
        onSubmit={handleSubmit}
      />
    </Box>
  );
}

10.2 UI 组件结构

graph TB subgraph "AppContainer" A[SettingsContext] --> B[MouseProvider] B --> C[SessionProvider] C --> D[VimModeProvider] D --> E[App] end subgraph "App 组件" E --> F[Header
标题栏] E --> G[MessageList
消息列表] E --> H[Composer
输入框] E --> I[Footer
状态栏] end subgraph "消息类型" G --> J[UserMessage
用户消息] G --> K[GeminiMessage
AI 回复] G --> L[ToolCallDisplay
工具调用] G --> M[ThoughtDisplay
思考过程] end

10.3 关键 Context

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

10.4 主题系统

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

typescript 复制代码
// 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 安装

bash 复制代码
# 使用 npm
npm install -g @google/gemini-cli

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

11.2 首次配置

bash 复制代码
# 首次运行会引导你完成认证
gemini

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

11.3 基本使用

bash 复制代码
# 交互模式
gemini

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

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

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

11.4 常用命令

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

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

11.5 记忆文件配置

创建项目记忆文件 GEMINI.md

markdown 复制代码
# 项目说明

这是一个 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 Prompt packages/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技术;
相关推荐
程序员晚枫5 小时前
AI的“USB-C时刻”:巨头罕见联手,只为打通AI世界的“任督二脉”
mcp
leonwgc6 小时前
🎯 AI 写代码?我用 MCP 让 Copilot 秒变全栈工程师!
react.js·ai编程
猫头虎8 小时前
openEuler异构算力体系下的GPU加速性能测试与优化研究
开源·github·aigc·ai编程·ai写作·gpu算力·华为snap
lomocode12 小时前
AI 模型占了 10G 显存,服务重启却没释放?
后端·ai编程
努力的小郑12 小时前
MCP 完全指南(上):撕掉标签,看清本质
前端·后端·ai编程
水木姚姚12 小时前
TensorFlow在Microsoft Windows 11下编程
人工智能·windows·python·深度学习·tensorflow·ai编程
remaindertime12 小时前
一文掌握 Spring AI:集成主流大模型的完整方案与思考
后端·spring·ai编程
水灵龙14 小时前
需求一体化管理平台专业演进史 (2015-2025)
ai编程·ai写作·规格说明书
谷粒.14 小时前
AI编程助手横向评测:GitHub Copilot vs CodeWhisperer vs Cursor
运维·人工智能·云原生·自动化·github·copilot·ai编程