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技术;
相关推荐
MoonBit月兔20 小时前
MoonBit 获日本最大技术社区Zenn原生支持,热度持续上升中!
人工智能·ai编程·moonbit
想搞艺术的程序员21 小时前
AI 编程 - 提示词技巧
人工智能·ai编程
HyperAI超神经1 天前
IQuest-Coder-V1:基于代码流训练的编程逻辑增强模型;Human Face Emotions:基于多标注维度的人脸情绪识别数据集
人工智能·深度学习·学习·机器学习·ai编程
砚边数影2 天前
AI数学基础(一):线性代数核心,向量/矩阵运算的Java实现
java·数据库·人工智能·线性代数·矩阵·ai编程·金仓数据库
Darkbluelr2 天前
[开源发布] Dev-PlayBooks:让 AI 编程不再“抽卡”,面向 Claude/Codex等 的确定性Spec+TDD开发工作流框架
人工智能·软件工程·ai编程
github.com/starRTC2 天前
Claude Code中英文系列教程:在云上虚拟机并行运行多个会话
ai编程
HyperAI超神经2 天前
【vLLM 学习】Rlhf Utils
人工智能·深度学习·学习·机器学习·ai编程·vllm
ThinkPet2 天前
【AI】大模型知识入门扫盲以及SpringAi快速入门
java·人工智能·ai·大模型·rag·springai·mcp
小雨青年2 天前
开篇 2026 开发者新范式 本地逻辑引擎结合云端国产大模型架构详解
ai编程
cloud studio AI应用2 天前
CodeBuddy 一周更新亮点丨IDE 新增 Hooks 等功能、CLI 新增Prompt 建议、SDK 自定义工具支持
腾讯云·ai编程·codebuddy