01

CLI 入口与启动流程

深入理解 cli.tsx 的 Fast Path 优化、动态导入机制、以及零依赖启动策略

1.1

cli.tsx 架构概览

理解 CLI 入口文件的整体结构和职责划分

核心架构
命令解析层 Commander.js 解析 CLI 参数
Fast Path 分发层 13个零模块命令快速通道
懒加载层 动态 import() 按需加载
REPL 初始化层 完整环境启动
设计理念
启动性能优化 简单命令不加载完整 REPL 环境
内存优化 按需加载大型依赖模块
用户体验 --version 等命令瞬间响应
可维护性 清晰的分层架构
💡 关键洞察

cli.tsx 是整个应用的性能瓶颈点。每次用户运行 claude-code,都会从这里开始。因此,这里的每毫秒延迟都会直接影响用户体验。Fast Path 策略确保 90% 的简单命令在 100ms 内响应。

1.2

Fast Path 实现细节

13个零模块命令的快速通道实现

TypeScript cli.tsx
// Fast Path: 零模块加载路径
if (process.argv.includes('--version')) {
  // ✓ 不加载任何 REPL 模块
  // ✓ 不初始化 AI 客户端
  // ✓ 不加载工具系统
    console.log(getVersion());
    process.exit(0);
}

// 其他 Fast Path 命令:
// --help, --auth, config, setup, doctor,
// update, compress-context, etc.

// Slow Path: 完整 REPL 启动
await import('./repl');  // 动态加载

命令执行决策树

🚀
命令启动
process.argv
🔍
Fast Path 检查
13个命令匹配
Fast Path
~50ms 响应
🔄
Slow Path
完整 REPL 加载
⚠️ 性能注意

添加新的 Fast Path 命令时需要权衡:每个额外命令都会增加启动时的检查逻辑。建议只将高频使用且无需 REPL 环境的命令加入 Fast Path。

1.3

动态导入与懒加载机制

理解 ES Module 动态 import() 在启动优化中的应用

传统导入 vs 动态导入
// ❌ Eager Loading import { REPL } from './repl';
// 所有模块立即加载 启动时间: ~2-3s
// 内存占用: ~150MB 即使只运行 --version
// ✓ Lazy Loading const { REPL } = await import('./repl');
// 按需加载模块 启动时间: ~50ms (Fast Path)
// 内存占用: ~20MB 只在需要时加载
懒加载策略
分层加载 核心层 → 工具层 → AI 层
依赖链优化 避免循环依赖
缓存策略 首次加载后缓存模块
错误处理 模块加载失败降级
🎯 最佳实践

在大型 Node.js 应用中,将启动路径分为 Fast Path(简单命令)和 Slow Path(完整功能)是标准做法。关键是要识别出哪些操作不需要完整的应用环境。

1.4

完整启动流程图

从命令行到 REPL 的完整生命周期

📋 参数解析
Commander.js 解析 CLI 参数
if (args.includes('--version'))
⚡ Fast Path 检查
13个快速命令匹配
match → executeFastCommand()
🔄 加载 REPL
动态 import 核心模块
await import('./repl')
🛠️ 初始化工具
注册 40+ 工具
ToolRegistry.register()
🤖 初始化 AI
配置 Anthropic API
new Anthropic()
✅ 就绪
等待用户输入
readline.prompt()
1.5

代码理解测验

检验你对 CLI 启动流程的理解

以下哪个命令会走 Fast Path(零模块加载)?

为什么 cli.tsx 使用动态 import() 加载 REPL 模块?

分析以下代码,哪个描述是正确的?

if (process.argv.includes('--version')) { console.log('v2.1.88'); process.exit(0); }

Fast Path 策略主要优化了什么指标?

02

REPL 核心循环

深入理解 REPL.tsx 的 Query 状态机、40+ 自定义 Hooks 体系、错误恢复机制

2.1

REPL 架构概览

理解 REPL 的核心组件和职责划分

REPL 核心组件
Read (读取) readline 接收用户输入
Eval (执行) Query 状态机处理请求
Print (输出) 流式响应渲染
Loop (循环) while(true) 持续运行
状态管理
AppState 全局应用状态
QueryState 查询状态机
ToolState 工具执行状态
ErrorState 错误恢复状态
🔑 核心概念

REPL 不是一个简单的循环,而是一个复杂的状态机。每个用户输入都会触发一系列状态转换,涉及 AI 调用、工具执行、流式响应等多个子系统。

2.2

Query 状态机详解

理解查询执行的完整生命周期

TypeScript query.ts
// Query 状态机的核心循环
async function* executeQuery(prompt: string) {
  const state = QueryState.IDLE;

  while (true) {
    switch (state) {
      case QueryState.IDLE:
        state = QueryState.THINKING;
        break;

      case QueryState.THINKING:
        // 调用 AI API
        response = await anthropic.messages.create(params);
        state = QueryState.STREAMING;
        break;

      case QueryState.STREAMING:
        // 流式输出响应
        for await (const chunk of response) {
          yield chunk;
        }
        state = QueryState.TOOL_EXECUTION;
        break;

      case QueryState.TOOL_EXECUTION:
        // 执行工具调用
        if (stopReason === 'tool_use') {
          toolResults = await executeTools(toolCalls);
          state = QueryState.THINKING;
        } else {
          state = QueryState.COMPLETE;
        }
        break;

      case QueryState.COMPLETE:
        return;
    }
  }
}
💤 IDLE
初始状态,等待查询
🤔 THINKING
AI 处理用户请求
📡 STREAMING
流式输出响应
🛠️ TOOL_EXECUTION
执行工具调用
✅ COMPLETE
查询完成
2.3

自定义 Hooks 体系

深入理解 REPL 的 40+ 自定义 Hooks 及其依赖关系

useAppState()
全局应用状态管理
核心状态
useQueryState()
查询状态机管理
查询状态
useToolRegistry()
工具注册表访问
工具系统
useAnthropicClient()
AI 客户端实例
AI 集成
useStreamRenderer()
流式响应渲染器
UI 渲染
useErrorRecovery()
三层错误恢复机制
错误处理
useFileSystem()
文件系统操作封装
文件操作
useGitOperations()
Git 命令执行
版本控制
useBashExecutor()
Shell 命令执行
命令执行
useContextCompressor()
上下文压缩优化
性能优化
useToolExecutor()
工具执行协调器
工具系统
useMemoryManager()
内存使用监控
性能监控
💡 Hooks 设计模式

REPL 使用自定义 Hooks 来封装复杂逻辑,每个 Hook 负责一个特定领域。这种设计使得代码模块化、可测试,并且易于维护。Hooks 之间可以相互依赖,形成复杂的依赖关系图。

2.4

三层错误恢复机制

深入理解 REPL 如何处理和恢复各种错误

错误恢复层级
第一层: Tool 重试 工具执行失败自动重试
第二层: Query 重启 查询失败重新发起
第三层: REPL 恢复 致命错误后恢复会话
实现细节
指数退避 重试间隔逐渐增加
错误分类 区分可恢复/不可恢复错误
状态保存 错误前状态快照
用户提示 友好的错误信息
TypeScript errorRecovery.ts
// 第一层: Tool 重试
async function executeToolWithRetry(
  tool: Tool,
  maxRetries = 3
): Promise<ToolResult> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await tool.execute();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await sleep(Math.pow(2, i) * 1000);  // 指数退避
    }
  }
}

// 第二层: Query 重启
async function retryQuery(
  query: Query,
  context: Context
): Promise<QueryResult> {
  // 保存失败前的上下文
  const snapshot = context.snapshot();

  try {
    return await executeQuery(query);
  } catch (error) {
    // 恢复上下文并重试
    context.restore(snapshot);
    return await executeQuery(query);
  }
}

// 第三层: REPL 恢复
function recoverREPL(error: Error) {
  console.error('致命错误,正在恢复 REPL...');
  // 清理状态
  // 重新初始化核心组件
  // 返回到 IDLE 状态
}
⚠️ 错误恢复权衡

过于激进的重试可能导致资源浪费和用户体验下降。需要根据错误类型智能选择重试策略:临时性错误(网络超时)可以重试,逻辑性错误(语法错误)应该直接返回。

2.5

代码理解测验

检验你对 REPL 核心循环的理解

在 Query 状态机中,TOOL_EXECUTION 状态之后会转换到哪个状态?

case QueryState.TOOL_EXECUTION: if (stopReason === 'tool_use') { toolResults = await executeTools(toolCalls); state = QueryState.??; } else { state = QueryState.COMPLETE; }

useToolExecutor() Hook 最可能依赖以下哪些 Hook?

当工具执行失败时,第一层错误恢复会做什么?

为什么 Query 状态机使用 while(true) 循环而不是递归?

03

Query 执行引擎

掌握 query.ts 的核心算法、流式响应处理、性能优化技巧

3.1

Query 引擎架构

理解 query.ts 的核心职责和设计原则

核心职责
状态机管理 while(true) 循环控制
AI 交互 Anthropic API 调用
工具调度 协调 40+ 工具执行
流式输出 实时响应渲染
设计原则
生成器模式 async function* 流式输出
状态不可变 每次转换创建新状态
提前退出 支持中断和取消
错误隔离 工具失败不影响主循环
🎯 设计目标

Query 引擎是整个系统的"大脑",它需要协调 AI、工具、用户界面等多个子系统。设计时优先考虑可观测性(可调试性)和可恢复性(错误处理)。

3.2

while(true) 循环深度解析

理解核心循环的每个细节

TypeScript query.ts (简化版)
export async function* query(
  prompt: string,
  options: QueryOptions = {}
): AsyncGenerator<QueryEvent> {
  // ========== 初始化阶段 ==========
  const context = new QueryContext(options);
  const messages: Message[] = [
    { role: 'user', content: prompt }
  ];

  // ========== 核心循环 ==========
  while (true) {
    // 检查提前退出条件
    if (context.shouldStop) {
      yield { type: 'stop', reason: context.stopReason };
      break;
    }

    try {
      // 阶段 1: 调用 AI
      yield { type: 'thinking_start' };

      const response = await anthropic.messages.create({
        model: "claude-sonnet-4-6",
        messages,
        stream: true,
        max_tokens: 8192,
        tools: context.availableTools
      });

      // 阶段 2: 流式处理响应
      let fullContent = '';
      let toolCalls: ToolCall[] = [];

      for await (const chunk of response) {
        if (chunk.type === 'content_block_delta') {
          fullContent += chunk.delta.text;
          yield {
            type: 'content',
            text: chunk.delta.text
          };
        }

        if (chunk.type === 'content_block_stop') {
          yield { type: 'thinking_end' };
        }

        if (chunk.type === 'tool_use') {
          toolCalls.push(chunk.toolUse);
        }
      }

      // 阶段 3: 处理工具调用
      if (toolCalls.length > 0) {
        yield {
          type: 'tool_execution_start',
          count: toolCalls.length
        };

        const toolResults = await executeTools(
          toolCalls,
          context
        );

        // 将工具结果添加到消息历史
        messages.push({
          role: 'assistant',
          content: fullContent,
          tool_calls: toolCalls
        });

        messages.push({
          role: 'user',
          content: toolResults
        });

        // 继续循环,返回 THINKING 状态
        continue;
      }

      // 阶段 4: 完成
      yield {
        type: 'complete',
        content: fullContent
      };
      break;

    } catch (error) {
      // 错误处理
      const recovered = await context.handleError(error);
      if (!recovered) {
        yield {
          type: 'error',
          error: error.message
        };
        break;
      }
    }
  }
}
💡 关键设计点

1. 生成器函数:使用 async function* 实现流式输出
2. 消息累积:每次循环保留完整对话历史
3. 工具循环:工具调用后自动返回 THINKING 状态
4. 错误恢复:错误不立即退出,尝试恢复后继续

3.3

流式响应处理机制

深入理解 Server-Sent Events (SSE) 和流式渲染

流式处理流程
1. AI 开始响应 thinking_start 事件
2. 内容流式到达 content 事件 (多次)
3. 工具调用检测 tool_use 事件
4. 工具执行 tool_execution_start 事件
5. 结果返回 继续循环或完成
性能优化
增量渲染 每次到达立即渲染
批处理 减少 DOM 操作频率
缓冲策略 平衡实时性和性能
取消机制 支持中断流式传输
TypeScript streamHandler.ts
// 流式响应处理器
class StreamHandler {
  private renderer: StreamRenderer;
  private buffer: string[] = [];
  private flushInterval = 16;  // ~60fps

  async handleStream(
    stream: AsyncGenerator<QueryEvent>
  ): Promise<void> {
    const timer = setInterval(() => {
      this.flush();
    }, this.flushInterval);

    try {
      for await (const event of stream) {
        switch (event.type) {
          case 'content':
            // 累积到缓冲区
            this.buffer.push(event.text);
            break;

          case 'thinking_start':
            this.renderer.showThinking();
            break;

          case 'tool_execution_start':
            this.renderer.showToolExecution(
              event.count
            );
            break;

          case 'complete':
            this.flush();
            this.renderer.finalize(event.content);
            return;
        }
      }
    } finally {
      clearInterval(timer);
    }
  }

  private flush() {
    if (this.buffer.length === 0) return;

    const content = this.buffer.join('');
    this.renderer.append(content);
    this.buffer = [];
  }
}
3.4

性能优化技巧

Query 引擎中的关键优化策略

上下文压缩
智能裁剪历史消息,只保留关键上下文
内存优化
并行工具执行
独立的工具调用并行执行
性能优化
缓存策略
缓存文件内容和 API 响应
性能优化
懒加载工具
按需加载工具代码
启动优化
流式优先
优先输出可见内容
用户体验
提前退出
支持中断长时间运行的查询
用户体验
TypeScript optimizations.ts
// 优化 1: 并行工具执行
async function executeToolsParallel(
  toolCalls: ToolCall[]
): Promise<ToolResult[]> {
  // 识别独立工具
  const independentTools = findIndependentTools(toolCalls);
  const dependentTools = findDependentTools(toolCalls);

  // 并行执行独立工具
  const parallelResults = await Promise.all(
    independentTools.map(tool => tool.execute())
  );

  // 串行执行依赖工具
  const serialResults = [];
  for (const tool of dependentTools) {
    const result = await tool.execute();
    serialResults.push(result);
  }

  return [...parallelResults, ...serialResults];
}

// 优化 2: 上下文压缩
function compressContext(
  messages: Message[],
  maxTokens: number
): Message[] {
  const compressed: Message[] = [];
  let currentTokens = 0;

  // 从最新消息开始倒序遍历
  for (let i = messages.length - 1; i >= 0; i--) {
    const msg = messages[i];
    const tokens = estimateTokens(msg);

    if (currentTokens + tokens > maxTokens) {
      // 压缩旧消息
      const summary = summarizeMessage(msg);
      compressed.unshift(summary);
    } else {
      compressed.unshift(msg);
      currentTokens += tokens;
    }
  }

  return compressed;
}

// 优化 3: 提前退出机制
class QueryContext {
  private aborted = false;

  abort() {
    this.aborted = true;
  }

  shouldStop() {
    return this.aborted || this.stopReason;
  }
}
⚠️ 优化权衡

性能优化需要权衡多个因素:并行工具执行可能增加复杂性,上下文压缩可能丢失信息,缓存可能导致一致性挑战。建议基于实际性能分析数据选择优化策略。

3.5

代码理解测验

检验你对 Query 执行引擎的理解

在 while(true) 循环中,什么条件会跳出循环?

while (true) { if (context.shouldStop) { yield { type: 'stop', reason: context.stopReason }; break; // 这里 } // ... if (toolCalls.length === 0) { yield { type: 'complete', content: fullContent }; break; // 或者这里 } // 否则继续循环 continue; }

当检测到工具调用(toolCalls.length > 0)后,代码会做什么?

为什么使用 async function* 而不是普通函数?

并行工具执行的前提条件是什么?

const independentTools = findIndependentTools(toolCalls); const parallelResults = await Promise.all( independentTools.map(tool => tool.execute()) );
3.6

最佳实践总结

从前三个模块中提炼的关键工程经验

架构设计
分层启动 Fast Path vs Slow Path
状态机驱动 while(true) 循环
模块化 Hooks 单一职责原则
错误隔离 多层恢复机制
性能优化
懒加载 动态 import()
流式处理 async generator
并行执行 Promise.all()
上下文优化 智能压缩
🚀 核心要点

1. 性能是用户体验的基础:从 CLI 启动到 Query 执行,每一毫秒都重要。
2. 错误处理是系统的生命线:三层错误恢复确保系统稳定性。
3. 状态机是复杂系统的最佳抽象:while(true) 循环简洁而强大。
4. 流式优先:用户不需要等待完整响应,增量输出提升体验。

🎉 恭喜完成课程!

你已经深入理解了 Claude Code 的核心架构和实现细节。

04

权限系统深度解析

深入理解 permissions.ts 的 13 步权限检查管道、7 种权限模式、风险评估逻辑

4.1

权限系统架构概览

理解 permissions.ts 的整体设计目标和核心组件

核心组件
PermissionChecker 13步检查管道引擎
PermissionMode 7种权限模式枚举
RiskAssessor 工具风险评估器
PermissionCache 权限决策缓存层
设计目标
安全第一 默认拒绝,显式允许
用户控制 细粒度权限配置
智能决策 基于风险的自动判断
性能优化 缓存避免重复检查
🔐 权限系统的重要性

Claude Code 拥有强大的文件系统、网络、Shell 操作能力。权限系统是保护用户安全的最后一道防线,确保 AI 不会执行未经授权的危险操作。

4.2

13步权限检查管道

完整解析权限检查的每个步骤

TypeScript permissions.ts
async function checkPermissions(
  tool: Tool,
  input: ToolInput,
  context: PermissionContext
): Promise<PermissionDecision> {

  // ========== Step 1: 模式检查 ==========
  const mode = context.getPermissionMode();
  if (mode === PermissionMode.BYPASS) {
    return { allowed: true, reason: 'bypass_mode' };
  }

  // ========== Step 2: 工具白名单 ==========
  if (isWhitelistedTool(tool)) {
    return { allowed: true, reason: 'whitelisted' };
  }

  // ========== Step 3: 缓存查询 ==========
  const cached = await cache.get(tool, input);
  if (cached) {
    return cached;
  }

  // ========== Step 4: 风险评估 ==========
  const risk = assessRisk(tool, input);

  // ========== Step 5: 低风险自动通过 ==========
  if (risk.level === RiskLevel.LOW) {
    return { allowed: true, reason: 'low_risk' };
  }

  // ========== Step 6: 高风险必须确认 ==========
  if (risk.level === RiskLevel.HIGH) {
    if (mode === PermissionMode.AUTO) {
      return { allowed: false, reason: 'high_risk_auto_mode' };
    }
  }

  // ========== Step 7: 中等风险检查模式 ==========
  if (risk.level === RiskLevel.MEDIUM) {
    if (mode === PermissionMode.AUTO) {
      return { allowed: true, reason: 'medium_risk_auto' };
    }
  }

  // ========== Step 8: 计划模式检查 ==========
  if (mode === PermissionMode.PLAN) {
    return {
      allowed: false,
      reason: 'plan_mode_readonly'
    };
  }

  // ========== Step 9: 用户确认检查 ==========
  if (mode === PermissionMode.CONFIRM) {
    const confirmed = await promptUser(tool, input, risk);
    if (!confirmed) {
      return { allowed: false, reason: 'user_denied' };
    }
  }

  // ========== Step 10: 不再询问模式 ==========
  if (mode === PermissionMode.DONT_ASK) {
    return { allowed: true, reason: 'dont_ask_mode' };
  }

  // ========== Step 11: 接受编辑模式 ==========
  if (mode === PermissionMode.ACCEPT_EDITS) {
    if (tool.isEditOperation()) {
      return { allowed: true, reason: 'accept_edits_mode' };
    }
  }

  // ========== Step 12: 冒泡模式 ==========
  if (mode === PermissionMode.BUBBLE) {
    return {
      allowed: true,
      requiresConfirmation: true,
      reason: 'bubble_mode'
    };
  }

  // ========== Step 13: 缓存决策 ==========
  const decision = { allowed: true, reason: 'default_allow' };
  await cache.set(tool, input, decision);
  return decision;
}

13步权限检查流程

1. 模式检查
2. 白名单
3. 缓存
4. 风险评估
5. 低风险
6. 高风险
7. 中风险
8. 计划
9. 确认
10. 不问
11. 接受编辑
12. 冒泡
13. 缓存
💡 管道设计优势

13步管道提供了细粒度的控制点,每个步骤都可以快速返回,避免不必要的计算。这种设计使得权限检查既安全又高效。

4.3

7种权限模式对比

深入理解每种权限模式的使用场景

DEFAULT
标准权限检查,中低风险自动允许,高风险需要确认
默认模式
PLAN
只读模式,拒绝所有修改操作,仅允许读取和查询
安全模式
ACCEPT_EDITS
自动接受所有编辑操作,适用于批量重构场景
高效模式
BYPASS_PERMISSIONS
完全跳过权限检查,仅限测试和调试使用
危险模式
DONT_ASK
不提示用户,自动允许所有操作
自动模式
AUTO
基于风险评估自动决策,低中风险允许,高风险拒绝
智能模式
BUBBLE
允许操作但标记需要确认,延迟到执行时提示
延迟确认
TypeScript permissionModes.ts
enum PermissionMode {
  // 默认:基于风险的智能决策
  DEFAULT = 'default',

  // 只读:拒绝所有修改
  PLAN = 'plan',

  // 自动接受编辑
  ACCEPT_EDITS = 'acceptEdits',

  // 完全跳过权限(危险)
  BYPASS_PERMISSIONS = 'bypassPermissions',

  // 不询问,自动允许
  DONT_ASK = 'dontAsk',

  // 自动模式(高风险拒绝)
  AUTO = 'auto',

  // 冒泡确认
  BUBBLE = 'bubble'
}

// 使用示例
const decision = await checkPermissions(
  tool,
  input,
  { mode: PermissionMode.PLAN }  // 只读模式
);
4.4

风险评估系统

理解如何评估工具调用的风险等级

风险因子
操作类型 删除 > 修改 > 创建 > 读取
文件数量 批量操作 > 单文件操作
路径范围 系统路径 > 用户路径 > 项目路径
命令类型 Shell > 网络 > 文件系统
风险等级
LOW 读取操作、白名单工具
MEDIUM 单文件修改、项目内操作
HIGH 删除、批量操作、系统路径
CRITICAL rm -rf、网络请求、Git force
TypeScript riskAssessment.ts
interface RiskAssessment {
  level: RiskLevel;
  factors: RiskFactor[];
  confidence: number;
}

function assessRisk(
  tool: Tool,
  input: ToolInput
): RiskAssessment {
  const factors: RiskFactor[] = [];
  let score = 0;

  // 因子1: 操作类型
  if (tool.isDestructive()) {
    factors.push({
      type: 'destructive_operation',
      weight: 0.4
    });
    score += 40;
  }

  // 因子2: 文件数量
  const fileCount = input.getFileCount();
  if (fileCount > 10) {
    factors.push({
      type: 'batch_operation',
      weight: 0.3
    });
    score += 30;
  }

  // 因子3: 路径敏感性
  if (input.affectsSystemPath()) {
    factors.push({
      type: 'system_path',
      weight: 0.5
    });
    score += 50;
  }

  // 因子4: 网络操作
  if (tool.isNetworkOperation()) {
    factors.push({
      type: 'network_access',
      weight: 0.3
    });
    score += 30;
  }

  // 计算风险等级
  let level: RiskLevel;
  if (score >= 70) level = RiskLevel.HIGH;
  else if (score >= 40) level = RiskLevel.MEDIUM;
  else level = RiskLevel.LOW;

  return {
    level,
    factors,
    confidence: calculateConfidence(factors)
  };
}
⚠️ 风险评估的局限性

风险评估基于启发式规则,无法覆盖所有场景。对于关键操作(如 rm -rf、git push --force),应该强制要求用户确认,无论风险评估结果如何。

4.5

权限缓存机制

理解如何避免重复的权限检查

TypeScript permissionCache.ts
class PermissionCache {
  private cache = new Map<string, CachedDecision>();
  private ttl = 5 * 60 * 1000;  // 5分钟过期

  async get(
    tool: Tool,
    input: ToolInput
  ): Promise<CachedDecision | null> {
    const key = this.generateKey(tool, input);
    const cached = this.cache.get(key);

    if (!cached) return null;

    // 检查是否过期
    if (Date.now() - cached.timestamp > this.ttl) {
      this.cache.delete(key);
      return null;
    }

    // 检查上下文是否变化
    if (!this.isContextValid(cached, input)) {
      this.cache.delete(key);
      return null;
    }

    return cached;
  }

  async set(
    tool: Tool,
    input: ToolInput,
    decision: PermissionDecision
  ): Promise<void> {
    const key = this.generateKey(tool, input);

    this.cache.set(key, {
      decision,
      timestamp: Date.now(),
      contextHash: this.hashContext(input)
    });

    // 限制缓存大小
    if (this.cache.size > 1000) {
      this.evictOldest();
    }
  }

  private generateKey(tool: Tool, input: ToolInput): string {
    // 生成唯一的缓存键
    return hash({
      tool: tool.name,
      operation: input.operation,
      target: input.getTarget(),
      user: input.userId
    });
  }

  private isContextValid(
    cached: CachedDecision,
    input: ToolInput
  ): boolean {
    // 检查文件是否被修改
    if (input.hasFileChanged(cached.contextHash)) {
      return false;
    }

    // 检查权限模式是否变化
    if (input.mode !== cached.mode) {
      return false;
    }

    return true;
  }
}
💡 缓存策略要点

1. TTL 过期:避免永久缓存导致权限决策过时
2. 上下文验证:文件变化后缓存失效
3. 大小限制:防止内存无限增长
4. 键生成:包含工具、操作、目标、用户信息

4.6

代码理解测验

检验你对权限系统的理解

在13步权限检查管道中,哪一步最先执行?

哪个权限模式会拒绝所有修改操作?

以下哪个操作会被评估为高风险(HIGH)?

// 评估代码片段 if (tool.isDestructive()) { score += 40; } if (fileCount > 10) { score += 30; } if (input.affectsSystemPath()) { score += 50; } // score >= 70 => HIGH

权限缓存在什么情况下会失效?

05

Settings 配置系统

掌握 settings.ts 的 5层配置级联、三层缓存机制、MDM 集成

5.1

配置系统架构概览

理解 settings.ts 的设计目标和核心组件

5层配置优先级
Layer 1: MDM 企业移动设备管理配置
Layer 2: 全局配置 ~/.claude/settings.json
Layer 3: 项目配置 ./.claude/settings.json
Layer 4: 环境变量 CLAUDE_* 环境变量
Layer 5: 命令行参数 CLI arguments
三层缓存
内存缓存 最快的访问速度
文件监视 自动重载配置
配置验证 Schema 验证和类型检查
热更新 无需重启应用
⚙️ 配置系统设计原则

优先级明确:命令行参数 > 环境变量 > 项目配置 > 全局配置 > MDM
层次清晰:每层配置职责明确,不会相互干扰
即时生效:配置变更后自动重载,无需重启
类型安全:TypeScript Schema 确保配置正确性

5.2

5层配置级联详解

理解配置合并和优先级处理

TypeScript settings.ts
interface SettingsCascade {
  mdm?: Settings;          // Layer 1: 最低优先级
  global?: Settings;       // Layer 2
  project?: Settings;      // Layer 3
  env?: Settings;          // Layer 4
  cli?: Settings;          // Layer 5: 最高优先级
}

class SettingsManager {
  private cascade: SettingsCascade;

  function getEffectiveSettings(): Settings {
    // 从低到高合并配置
    const merged: Settings = {
      // 从 MDM 开始(基础配置)
      ...this.cascade.mdm,

      // 全局配置覆盖 MDM
      ...this.cascade.global,

      // 项目配置覆盖全局
      ...this.cascade.project,

      // 环境变量覆盖项目配置
      ...this.cascade.env,

      // CLI 参数覆盖一切
      ...this.cascade.cli
    };

    // 特殊处理:数组类型配置
    return this.mergeArrays(merged);
  }

  private mergeArrays(settings: Settings): Settings {
    // 某些配置需要数组合并而非覆盖
    const result = { ...settings };

    // 示例:工具白名单需要累加
    result.toolWhitelist = [
      ...(settings.mdm?.toolWhitelist || []),
      ...(settings.global?.toolWhitelist || []),
      ...(settings.project?.toolWhitelist || []),
      ...(settings.env?.toolWhitelist || []),
      ...(settings.cli?.toolWhitelist || [])
    ];

    return result;
  }
}

// 使用示例
const settings = await Settings.load();
const effectiveSettings = settings.getEffective();
console.log(effectiveSettings.permissionMode);  // 最终值

配置级联可视化

🏢
MDM
企业配置
🌍
全局
用户配置
📁
项目
项目配置
🔧
环境
环境变量
CLI
命令行参数
💡 配置级联要点

覆盖而非替换:高级配置只覆盖指定的字段,保留低级配置的其他值
数组特殊处理:某些配置(如工具白名单)需要累加而非覆盖
类型验证:每层配置都经过 Schema 验证,确保类型正确

5.3

三层缓存机制详解

理解配置读取的优化策略

缓存层次
L1: 内存缓存 ~0.01ms,当前进程
L2: 文件缓存 ~1ms,JSON 文件
L3: 默认值 ~0ms,硬编码配置
缓存策略
Cache-Aside 先查缓存,未命中再查文件
Write-Through 写入时更新所有层
TTL 过期 内存缓存定期刷新
文件监视 文件变化自动重载
TypeScript settingsCache.ts
class SettingsCache {
  // L1: 内存缓存
  private memoryCache = new Map<string, any>();
  private cacheTTL = 5000;  // 5秒

  // L2: 文件缓存
  private fileCachePath = '~/.claude/settings.cache.json';

  // L3: 默认值
  private defaults = loadDefaults();

  async get(key: string): Promise<any> {
    // ========== L1: 检查内存缓存 ==========
    const memCached = this.memoryCache.get(key);
    if (memCached && !this.isExpired(memCached)) {
      return memCached.value;
    }

    // ========== L2: 检查文件缓存 ==========
    const fileCached = await this.loadFromFileCache(key);
    if (fileCached) {
      // 回填内存缓存
      this.memoryCache.set(key, {
        value: fileCached,
        timestamp: Date.now()
      });
      return fileCached;
    }

    // ========== L3: 返回默认值 ==========
    return this.defaults[key];
  }

  async set(key: string, value: any): Promise<void> {
    // 写入所有层

    // L1: 更新内存
    this.memoryCache.set(key, {
      value,
      timestamp: Date.now()
    });

    // L2: 更新文件缓存
    await this.saveToFileCache(key, value);

    // L3: 更新实际配置文件
    await this.saveToSettingsFile(key, value);
  }

  // 文件监视器
  private setupFileWatcher() {
    const watcher = fs.watch(this.settingsPath, async () => {
      // 文件变化时清除内存缓存
      this.memoryCache.clear();

      // 重新加载配置
      await this.reload();

      // 触发配置变更事件
      this.emitter.emit('settings-changed');
    });
  }
}
🚀 缓存性能对比

无缓存:每次读取文件 ~1-5ms,频繁读取严重影响性能
单层缓存:内存缓存 ~0.01ms,但文件变化不会自动更新
三层缓存:结合性能和实时性,文件监视确保配置即时生效

5.4

MDM(移动设备管理)集成

企业级配置管理方案

TypeScript mdm.ts
interface MDMConfig {
  // 企业策略配置
  permissionMode?: PermissionMode;
  allowedDomains?: string[];
  dataRetentionDays?: number;
  auditLoggingEnabled?: boolean;

  // 安全策略
  disableDangerousTools?: boolean;
  requireEncryption?: boolean;
  allowedIPRanges?: string[];

  // 合规配置
  dataResidency?: string;  // 'eu', 'us', 'asia'
  complianceMode?: boolean;
}

class MDMManager {
  private configPath = '/Library/Managed Preferences/com.anthropic.claude-code.plist';

  async load(): Promise<MDMConfig | null> {
    try {
      // macOS MDM 配置路径
      const plist = await readPlist(this.configPath);

      return {
        permissionMode: plist.permissionMode,
        allowedDomains: plist.allowedDomains,
        dataRetentionDays: plist.dataRetentionDays || 30,
        auditLoggingEnabled: plist.auditLoggingEnabled || false,
        disableDangerousTools: plist.disableDangerousTools || false,
        requireEncryption: plist.requireEncryption || false,
        allowedIPRanges: plist.allowedIPRanges || [],
        dataResidency: plist.dataResidency || 'us',
        complianceMode: plist.complianceMode || false
      };
    } catch (error) {
      // 无 MDM 配置,返回 null
      return null;
    }
  }

  // 验证 MDM 配置合规性
  validateCompliance(config: MDMConfig): ComplianceReport {
    const violations: string[] = [];

    // 检查数据驻留
    if (config.dataResidency === 'eu') {
      // 确保 API 调用使用 EU 区域
      violations.push(...this.checkDataResidency());
    }

    // 检查加密要求
    if (config.requireEncryption) {
      violations.push(...this.checkEncryption());
    }

    return {
      compliant: violations.length === 0,
      violations,
      timestamp: Date.now()
    };
  }
}

// 使用示例
const mdm = await MDMManager.load();
if (mdm?.disableDangerousTools) {
  // 禁用 rm、git push --force 等危险工具
  ToolRegistry.disableDangerousTools();
}
⚠️ MDM 优先级最低

MDM 配置的优先级最低,用户可以通过项目配置、环境变量或 CLI 参数覆盖。这是为了确保开发者在特殊情况下可以灵活调整配置,但同时企业策略会在常规使用中生效。

5.5

配置热更新机制

理解配置变更如何即时生效

TypeScript hotReload.ts
class HotReloadManager {
  private watchers = new Map<string, FSWatcher>();
  private debounceTimers = new Map<string, NodeJS.Timeout>();

  watchFile(filePath: string, callback: () => void) {
    // 防抖:避免频繁触发
    const debouncedCallback = debounce(callback, 500);

    const watcher = fs.watch(filePath, async (eventType) => {
      if (eventType === 'change') {
        // 等待文件写入完成
        await sleep(100);

        // 重新加载配置
        const newConfig = await this.loadConfig(filePath);

        // 验证配置
        const validation = this.validateConfig(newConfig);
        if (!validation.valid) {
          console.error('配置验证失败:', validation.errors);
          return;
        }

        // 应用新配置
        await this.applyConfig(newConfig);

        // 触发回调
        debouncedCallback();

        // 通知所有订阅者
        this.emitter.emit('config-updated', newConfig);
      }
    });

    this.watchers.set(filePath, watcher);
  }

  // 监听多个配置文件
  watchAllConfigs() {
    // 全局配置
    this.watchFile(
      '~/.claude/settings.json',
      () => console.log('全局配置已更新')
    );

    // 项目配置
    this.watchFile(
      './.claude/settings.json',
      () => console.log('项目配置已更新')
    );

    // MDM 配置
    this.watchFile(
      '/Library/Managed Preferences/com.anthropic.claude-code.plist',
      () => console.log('MDM 配置已更新')
    );
  }

  // 清理监视器
  dispose() {
    this.watchers.forEach(watcher => watcher.close());
    this.watchers.clear();
  }
}

// 全局实例
const hotReload = new HotReloadManager();
hotReload.watchAllConfigs();
💡 热更新实现要点

1. 防抖处理:文件保存可能触发多次 change 事件
2. 配置验证:确保新配置符合 Schema
3. 原子更新:配置更新失败时回滚到旧配置
4. 事件通知:所有订阅者都能收到配置变更通知

5.6

代码理解测验

检验你对 Settings 配置系统的理解

以下哪种配置方式的优先级最高?

三层缓存中,哪一层访问速度最快?

MDM 配置的优先级如何?

配置热更新使用什么技术监听文件变化?

const watcher = fs.watch(filePath, async (eventType) => { if (eventType === 'change') { await reloadConfig(); } });
06

Agent 系统架构

理解 AgentTool.tsx 的子代理机制、Fork 模式、Prompt Cache 共享

6.1

Agent 系统架构概览

理解 Agent 系统的设计目标和核心组件

核心组件
AgentTool 工具形式的 Agent 包装器
ForkAgent 进程隔离的子 Agent
PromptCache 跨 Agent 的 Prompt 共享
AgentRegistry 10+ 内置 Agent 管理
设计目标
任务分解 复杂任务交给专门 Agent
进程隔离 子 Agent 独立运行
资源复用 共享 Prompt Cache
专业化 每个 Agent 专注特定领域
🤖 为什么需要 Agent 系统?

单个 AI 模型难以处理所有场景。通过 Agent 系统,我们可以:
1. 任务专业化:代码审查、设计评审、QA 测试各有专门 Agent
2. 并行执行:多个 Agent 可以同时工作
3. 错误隔离:一个 Agent 失败不影响其他 Agent
4. 资源优化:通过 Prompt Cache 减少重复计算

6.2

AgentTool 实现详解

理解如何将 Agent 封装为工具

TypeScript AgentTool.tsx
interface AgentToolConfig {
  name: string;
  description: string;
  agentType: AgentType;
  systemPrompt: string;
  parameters: ToolParameter[];
}

class AgentTool implements Tool {
  private config: AgentToolConfig;
  private agent: Agent;
  private cache: PromptCache;

  constructor(config: AgentToolConfig) {
    this.config = config;
    this.agent = this.createAgent();
    this.cache = PromptCache.getInstance();
  }

  // 工具元数据
  get name() { return this.config.name; }
  get description() { return this.config.description; }

  // 工具执行入口
  async execute(
    input: ToolInput,
    context: ToolContext
  ): Promise<ToolResult> {
    try {
      // 1. 构建完整的 Prompt
      const fullPrompt = this.buildPrompt(input);

      // 2. 检查 Prompt Cache
      const cached = await this.cache.get(fullPrompt);
      if (cached) {
        return {
          success: true,
          data: cached,
          cached: true
        };
      }

      // 3. 执行 Agent
      const result = await this.agent.execute(
        fullPrompt,
        context
      );

      // 4. 缓存结果
      await this.cache.set(fullPrompt, result);

      // 5. 返回结果
      return {
        success: true,
        data: result
      };

    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  private createAgent(): Agent {
    switch (this.config.agentType) {
      case AgentType.FORK:
        return new ForkAgent(this.config);

      case AgentType.INLINE:
        return new InlineAgent(this.config);

      default:
        throw new Error(`Unknown agent type: ${this.config.agentType}`);
    }
  }

  private buildPrompt(input: ToolInput): string {
    return `
${this.config.systemPrompt}

用户请求:
${input.prompt}

上下文信息:
${JSON.stringify(input.context, null, 2)}
    `.trim();
  }
}

// 使用示例:注册 Code Review Agent
const codeReviewTool = new AgentTool({
  name: 'code_review',
  description: 'Review code changes and provide feedback',
  agentType: AgentType.FORK,
  systemPrompt: 'You are a code review expert...',
  parameters: [
    { name: 'diff', type: 'string', required: true },
    { name: 'baseBranch', type: 'string', required: false }
  ]
});

ToolRegistry.register(codeReviewTool);
💡 AgentTool 的优势

1. 统一接口:Agent 通过 Tool 接口调用,与 Bash、Read 等工具一致
2. 可组合性:可以与其他工具串联使用
3. 缓存友好:Prompt Cache 自动生效
4. 类型安全:强类型的参数定义

6.3

Fork Agent 深度解析

理解进程隔离和 context 共享机制

TypeScript ForkAgent.tsx
class ForkAgent implements Agent {
  private childProcess: ChildProcess | null = null;
  private config: AgentConfig;

  async execute(
    prompt: string,
    parentContext: Context
  ): Promise<AgentResult> {

    // ========== 步骤1: 准备共享 Context ==========
    const sharedContext = this.prepareSharedContext(parentContext);

    // ========== 步骤2: Fork 子进程 ==========
    this.childProcess = fork(
      './agent-worker.js',
      [this.config.name],
      {
        silent: true,  // 不共享 stdio
        env: {
          ...process.env,
          CLAUDE_AGENT_MODE: 'fork',
          CLAUDE_AGENT_NAME: this.config.name
        }
      }
    );

    // ========== 步骤3: 发送任务和 Context ==========
    this.childProcess.send({
      type: 'execute',
      prompt,
      context: sharedContext,
      config: this.config
    });

    // ========== 步骤4: 等待结果 ==========
    return new Promise((resolve, reject) => {
      let result: AgentResult | null = null;

      // 接收子进程消息
      this.childProcess!.on('message', (msg) => {
        switch (msg.type) {
          case 'progress':
            // 转发进度更新
            parentContext.emitProgress(msg.progress);
            break;

          case 'result':
            result = msg.result;
            break;

          case 'error':
            reject(new Error(msg.error));
            break;
        }
      });

      // 监听进程退出
      this.childProcess!.on('exit', (code) => {
        if (code === 0) {
          resolve(result!);
        } else {
          reject(new Error(`Agent exited with code ${code}`));
        }
      });

      // 超时保护
      setTimeout(() => {
        this.childProcess!.kill('SIGTERM');
        reject(new Error('Agent execution timeout'));
      }, this.config.timeout || 30000);
    });
  }

  // 准备共享的 Context
  private prepareSharedContext(parentContext: Context): SharedContext {
    return {
      // 共享文件系统状态
      workingDirectory: parentContext.cwd,

      // 共享环境变量(过滤敏感信息)
      env: this.filterEnvVars(process.env),

      // 共享工具注册表(只读)
      toolRegistry: parentContext.toolRegistry.serialize(),

      // 共享 Prompt Cache(只读)
      promptCache: parentContext.promptCache.export(),

      // 不共享:AI 客户端、状态变量
      // 子进程需要创建自己的 AI 实例
    };
  }
}

// agent-worker.js (子进程入口)
if (process.env.CLAUDE_AGENT_MODE === 'fork') {
  process.on('message', async (msg) => {
    if (msg.type === 'execute') {
      const { prompt, context, config } = msg;

      // 创建独立的 AI 客户端
      const agent = new InlineAgent(config);

      try {
        // 执行任务
        const result = await agent.execute(prompt, context);

        // 发送结果回父进程
        process.send({ type: 'result', result });
      } catch (error) {
        process.send({ type: 'error', error: error.message });
      }

      // 退出子进程
      process.exit(0);
    }
  });
}
⚠️ Fork vs Inline

Fork Agent:进程隔离,适合 CPU 密集型、可能崩溃的任务
Inline Agent:同进程执行,开销小但影响主进程稳定性
选择建议:代码审查、设计评审用 Fork;简单查询用 Inline

6.4

Prompt Cache 共享机制

理解如何在 Agent 间共享 Prompt 结果

缓存策略
Prompt Hash SHA256 哈希作为键
结果存储 Redis 或内存缓存
TTL 管理 24小时过期
跨进程共享 Fork Agent 也可访问
性能优势
减少 API 调用 相同 Prompt 不重复请求
降低延迟 缓存命中 ~10ms
节省成本 API 调用减少 50%+
提升一致性 相同输入产生相同输出
TypeScript promptCache.ts
class PromptCache {
  private cache = new Map<string, CachedPrompt>();
  private redis?: Redis;
  private static instance: PromptCache;

  static getInstance(): PromptCache {
    if (!PromptCache.instance) {
      PromptCache.instance = new PromptCache();
    }
    return PromptCache.instance;
  }

  async get(prompt: string): Promise<Anthropic.Message | null> {
    const hash = this.hashPrompt(prompt);

    // L1: 内存缓存
    const memCached = this.cache.get(hash);
    if (memCached && !this.isExpired(memCached)) {
      return memCached.result;
    }

    // L2: Redis 缓存(跨进程共享)
    if (this.redis) {
      const redisCached = await this.redis.get(`prompt:${hash}`);
      if (redisCached) {
        const result = JSON.parse(redisCached);

        // 回填内存缓存
        this.cache.set(hash, {
          result,
          timestamp: Date.now()
        });

        return result;
      }
    }

    return null;
  }

  async set(prompt: string, result: Anthropic.Message): Promise<void> {
    const hash = this.hashPrompt(prompt);
    const cached: CachedPrompt = {
      result,
      timestamp: Date.now()
    };

    // L1: 内存缓存
    this.cache.set(hash, cached);

    // L2: Redis 缓存
    if (this.redis) {
      await this.redis.setex(
        `prompt:${hash}`,
        86400,  // 24小时
        JSON.stringify(result)
      );
    }
  }

  private hashPrompt(prompt: string): string {
    const normalized = prompt.trim().toLowerCase();
    return createHash('sha256')
      .update(normalized)
      .digest('hex');
  }

  // 为 Fork Agent 导出缓存
  async exportForFork(): Promise<Map<string, CachedPrompt>> {
    // Fork Agent 可以读取但不能写入主进程缓存
    return new Map(this.cache);
  }

  // 清理过期缓存
  private isExpired(cached: CachedPrompt): boolean {
    const age = Date.now() - cached.timestamp;
    return age > 86400000;  // 24小时
  }
}

// 在 AgentTool 中使用
const result = await this.cache.get(fullPrompt);
if (result) {
  console.log('✓ Prompt cache hit');
  return result;
}
// Cache miss,调用 AI
const aiResult = await anthropic.messages.create(...);
await this.cache.set(fullPrompt, aiResult);
💡 Prompt Cache 性能数据

缓存命中率:~60-70%(代码审查、设计评审等重复任务)
延迟降低:从 2-5s 降至 ~10ms
API 调用减少:~65%
成本节省:每月可节省数千次 API 调用

6.5

内置 Agent 生态

探索 10+ 个专业化的内置 Agent

/review
代码审查 Agent,分析 diff 并提供反馈
代码质量
/design-review
设计评审 Agent,视觉审计和一致性检查
设计
/qa
QA 测试 Agent,自动化测试和 bug 发现
测试
/plan-ceo-review
CEO 评审 Agent,产品策略和需求分析
产品
/plan-eng-review
工程评审 Agent,架构设计和技术方案
架构
/investigate
调试调查 Agent,系统性 root cause 分析
调试
/benchmark
性能测试 Agent,基准测试和回归检测
性能
/cso
安全审计 Agent,OWASP 和威胁建模
安全
/browse
网页浏览 Agent,无头浏览器自动化
自动化
/codex
代码审查 Agent,独立 diff review 和挑战
代码审查
/ship
发布流程 Agent,自动化版本发布
DevOps
/document-release
文档更新 Agent,自动更新 README/CHANGELOG
文档
TypeScript agentRegistry.ts
// 内置 Agent 注册表
const builtinAgents = registerBuiltinAgents() {
  return [
    // 代码质量
    new AgentTool({
      name: 'review',
      agentType: AgentType.FORK,
      systemPrompt: 'You are a code review expert...'
    }),

    new AgentTool({
      name: 'codex',
      agentType: AgentType.FORK,
      systemPrompt: 'You are an independent code reviewer...'
    }),

    // 测试
    new AgentTool({
      name: 'qa',
      agentType: AgentType.FORK,
      systemPrompt: 'You are a QA testing expert...'
    }),

    new AgentTool({
      name: 'benchmark',
      agentType: AgentType.FORK,
      systemPrompt: 'You are a performance testing expert...'
    }),

    // 安全
    new AgentTool({
      name: 'cso',
      agentType: AgentType.FORK,
      systemPrompt: 'You are a security officer...'
    }),

    // 产品和架构
    new AgentTool({
      name: 'plan-ceo-review',
      agentType: AgentType.INLINE,
      systemPrompt: 'You are a CEO reviewing product plans...'
    }),

    new AgentTool({
      name: 'plan-eng-review',
      agentType: AgentType.INLINE,
      systemPrompt: 'You are an engineering manager...'
    }),

    // DevOps
    new AgentTool({
      name: 'ship',
      agentType: AgentType.INLINE,
      systemPrompt: 'You are a release automation expert...'
    })
  ];
};

// 注册所有内置 Agent
builtinAgents().forEach(agent => {
  ToolRegistry.register(agent);
});

console.log(`✓ Registered ${builtinAgents().length} builtin agents`);
6.6

代码理解测验

检验你对 Agent 系统的理解

Agent 为什么要封装为 Tool 接口?

Fork Agent 和 Inline Agent 的主要区别是什么?

Prompt Cache 如何实现跨进程共享?

对于代码审查任务,应该选择哪种 Agent 类型和哪个内置 Agent?

6.7

课程总结

回顾6个模块的核心知识点

模块回顾
模块1: CLI 启动 Fast Path、懒加载、动态 import
模块2: REPL 循环 状态机、40+ Hooks、错误恢复
模块3: Query 引擎 while-true、流式处理、性能优化
模块4: 权限系统 13步管道、7种模式、风险评估
模块5: Settings 5层级联、三层缓存、MDM
模块6: Agent Fork、Prompt Cache、10+ 内置
核心设计模式
状态机模式 Query REPL 权限检查
策略模式 权限模式 Agent 类型
缓存模式 多层缓存 Prompt Cache
管道模式 权限检查 配置级联
生成器模式 流式响应处理
代理模式 Agent Tool 封装
🎓 学习成果

完成这6个模块后,你已经:
1. 掌握了大型 Node.js 应用的架构设计
2. 理解了复杂状态机的实现模式
3. 学会了多层缓存和性能优化策略
4. 深入了解了权限和安全系统的设计
5. 掌握了 Agent 系统和进程间通信

🎉 恭喜完成全部课程!

你已经深入理解了 Claude Code 的完整技术架构。

07

工具系统架构

深入理解 Tool 接口定义、生命周期管理、并行执行机制和权限检查集成

7.1

Tool 接口定义与生命周期

理解 Tool.ts 的核心接口设计和工具生命周期

TypeScript Tool.ts
// ========== Tool 接口核心定义 ==========
interface Tool {
  // 工具元数据
  readonly name: string;
  readonly description: string;
  readonly category: ToolCategory;
  readonly permissions: Permission[];

  // 输入输出 Schema (JSON Schema)
  readonly inputSchema: JSONSchema;
  readonly outputSchema?: JSONSchema;

  // ========== 生命周期方法 ==========

  // 1. 验证阶段:检查参数有效性
  validate(params: unknown): ValidationResult;

  // 2. 权限检查:验证用户权限
  checkPermissions(
    user: User,
    params: unknown
  ): PermissionCheck;

  // 3. 执行阶段:核心业务逻辑
  execute(
    params: unknown,
    context: ExecutionContext
  ): Promise<ToolResult>;

  // 4. 清理阶段:释放资源
  cleanup?() => Promise<void>;

  // ========== 可选钩子 ==========

  // 执行前钩子
  beforeExecute?(params: unknown) => Promise<unknown>;

  // 执行后钩子
  afterExecute?(result: ToolResult) => Promise<ToolResult>;

  // 错误处理钩子
  onError?(error: Error) => Promise<ToolResult | null>;
}

// ========== 工具类别枚举 ==========
enum ToolCategory {
  FILE = 'file',
  BASH = 'bash',
  SEARCH = 'search',
  GIT = 'git',
  MEMORY = 'memory',
  AGENT = 'agent',
  WEB = 'web'
}

// ========== 权限级别 ==========
enum Permission {
  READ_FILES = 'read:files',
  WRITE_FILES = 'write:files',
  EXECUTE_BASH = 'execute:bash',
  MODIFY_GIT = 'modify:git',
  NETWORK_ACCESS = 'network:access'
}

工具生命周期流程图

📋
验证
validate(params)
🔐
权限检查
checkPermissions()
⚙️
前置钩子
beforeExecute()
▶️
执行
execute()
后置钩子
afterExecute()
🧹
清理
cleanup()
💡 生命周期设计

工具的生命周期设计借鉴了中间件模式,每个阶段都可以注入自定义逻辑。这种设计使得工具系统既有统一的执行流程,又能保持各个工具的灵活性。

7.2

buildTool 工厂模式

理解如何使用工厂模式创建和注册工具

TypeScript toolFactory.ts
// ========== 工厂模式实现 ==========
function buildTool<TInput = unknown, TOutput = unknown>(config: {
  name: string;
  description: string;
  category: ToolCategory;
  inputSchema: JSONSchema<TInput>;
  outputSchema?: JSONSchema<TOutput>;
  permissions?: Permission[];
  execute: (params: TInput, context: ExecutionContext) => Promise<TOutput>;
  beforeExecute?: (params: TInput) => Promise<TInput>;
  afterExecute?: (result: TOutput) => Promise<TOutput>;
  onError?: (error: Error) => Promise<TOutput | null>;
  cleanup?: () => Promise<void>;
}): Tool {
  return {
    name: config.name,
    description: config.description,
    category: config.category,
    permissions: config.permissions || [],
    inputSchema: config.inputSchema,
    outputSchema: config.outputSchema,

        validate(params: unknown): ValidationResult {
      return validateJSONSchema(params, config.inputSchema);
    },

        checkPermissions(user: User, params: unknown): PermissionCheck {
      return config.permissions?.every(perm => user.hasPermission(perm))
        ? { granted: true }
        : { granted: false, reason: 'Missing permissions' };
    },

        async execute(params: unknown, context: ExecutionContext): Promise<ToolResult> {
      let processedParams = params;
      if (config.beforeExecute) {
        processedParams = await config.beforeExecute(processedParams as TInput);
      }

      let result: TOutput;
      try {
        result = await config.execute(processedParams as TInput, context);
      } catch (error) {
        if (config.onError) {
          const recovered = await config.onError(error as Error);
          if (recovered !== null) return { success: true, data: recovered };
        }
        throw error;
      }

      if (config.afterExecute) {
        result = await config.afterExecute(result);
      }

      return { success: true, data: result };
    },

    cleanup: config.cleanup
  };
}
🎯 工厂模式优势

1. 类型安全:泛型确保输入输出类型正确
2. 统一接口:所有工具遵循相同的生命周期
3. 可扩展:钩子机制允许添加自定义逻辑
4. 声明式:配置清晰,易于理解和维护

7.3

StreamingToolExecutor 并行执行机制

深入理解工具并行执行算法和依赖关系解析

TypeScript toolExecutor.ts
// ========== 并行工具执行核心算法 ==========
class StreamingToolExecutor {
  async executeTools(
    toolCalls: ToolCall[]
  ): AsyncGenerator<ToolExecutionEvent> {
    // ========== 阶段 1: 依赖分析 ==========
    const dependencyGraph = await this.analyzeDependencies(toolCalls);
    const independentTools = dependencyGraph.getIndependentTools();
    const dependentTools = dependencyGraph.getDependentTools();

    // ========== 阶段 2: 并行执行独立工具 ==========
    if (independentTools.length > 0) {
      const parallelResults = await Promise.all(
        independentTools.map(tool => this.executeSingleTool(tool))
      );
    }

    // ========== 阶段 3: 串行执行依赖工具 ==========
    for (const toolBatch of dependentTools) {
      for (const toolCall of toolBatch) {
        await this.executeSingleTool(toolCall);
      }
    }
  }

  // ========== 依赖分析:拓扑排序 ==========
  private async analyzeDependencies(
    toolCalls: ToolCall[]
  ): Promise<DependencyGraph> {
    const graph = new DependencyGraph();

    // 检测资源冲突和数据依赖
    for (let i = 0; i < toolCalls.length; i++) {
      for (let j = i + 1; j < toolCalls.length; j++) {
        if (this.hasResourceConflict(toolCalls[i], toolCalls[j])) {
          graph.addDependency(toolCalls[i].id, toolCalls[j].id);
        }
      }
    }

    return graph.topologicalSort();
  }
}
⚠️ 并行执行挑战

并行工具执行面临多个挑战:1) 资源冲突检测可能遗漏隐式依赖 2) 拓扑排序可能产生循环依赖 3) 错误处理在并行环境下更复杂。建议在工具注册时明确声明依赖关系。

7.4

代码理解测验

检验你对工具系统架构的理解

Tool 生命周期中,权限检查在哪个阶段执行?

08

上下文系统

掌握 System Context 和 User Context 的双上下文系统、并行 Git 操作优化、Memoization 策略

8.1

双上下文系统架构

理解 System Context 和 User Context 的职责划分

TypeScript context.ts
// ========== 双上下文系统定义 ==========
interface SystemContext {
  // 系统环境信息
  readonly env: { os: string; shell: string; };
  readonly workingDirectory: { current: string; gitRoot?: string; };
  readonly gitState: GitState;
  readonly toolRegistry: ToolRegistry;
  readonly permissions: Permission[];
}

interface UserContext {
  readonly prompt: string;
  readonly messages: Message[];
  readonly memory: SessionMemory;
  readonly preferences: UserPreferences;
  readonly currentTask?: Task;
}
💡 双上下文设计理念

System Context 是相对静态的、全局的,初始化后变化较少;User Context 是动态的、会话相关的,每次查询都可能更新。分离两者可以优化缓存策略和更新频率。

8.2

并行 Git 操作优化

深入理解 Git 命令的并行执行策略

TypeScript gitOperations.ts
// ========== 并行 Git 状态获取 ==========
async function getGitStateParallel(cwd: string): Promise<GitState> {
  // 并行执行 5 个独立的 Git 命令
  const results = await Promise.all([
    execGit(['status', '--porcelain'], cwd),
    execGit(['rev-parse', '--abbrev-ref', 'HEAD'], cwd),
    execGit(['rev-parse', 'HEAD'], cwd),
    execGit(['remote', '-v'], cwd),
    execGit(['config', '--get', 'user.email'], cwd)
  ]);

  return {
    status: parseGitStatus(results[0]),
    branch: results[1].trim(),
    commit: results[2].trim(),
    remote: parseGitRemote(results[3]),
    userEmail: results[4].trim()
  };
}

// ========== Git Status 超截机制 ==========
function parseGitStatus(output: string): GitStatus {
  const MAX_FILES = 100;
  const lines = output.split('\n').slice(0, MAX_FILES);

  const status = { modified: [], added: [], deleted: [], untracked: [] };

  for (const line of lines) {
    const [statusCode, ...pathParts] = line.split(' ');
    const filePath = pathParts.join(' ');

    switch (statusCode) {
      case 'M': status.modified.push(filePath); break;
      case 'A': status.added.push(filePath); break;
      case 'D': status.deleted.push(filePath); break;
      case '??': status.untracked.push(filePath); break;
    }
  }

  return status;
}
⚠️ 超截权衡

Git status 在大型仓库中可能返回上千个文件,导致上下文爆炸。超截到 100 个文件可以保护 Token 使用量,但可能丢失重要信息。

8.3

代码理解测验

检验你对上下文系统的理解

System Context 和 User Context 的主要区别是什么?

09

Memory 系统

理解基于阈值的自动记忆提取、四种 Memory 类型、Fork Agent 集成

9.1

Memory 系统架构

理解四种 Memory 类型和提取阈值机制

TypeScript sessionMemory.ts
// ========== Memory 类型定义 ==========
enum MemoryType {
  USER = 'user',          // 用户偏好
  PROJECT = 'project',    // 项目信息
  SESSION = 'session',    // 会话上下文
  TOOL = 'tool'           // 工具使用数据
}

interface Memory {
  id: string;
  type: MemoryType;
  content: string;
  metadata: {
    importance: number;        // 重要性评分 (0-1)
    frequency: number;
    tags: string[];
    confidence: number;
  };
  createdAt: number;
  lastAccessed: number;
}

// ========== 重要性阈值过滤 ==========
class SessionMemory {
  async addCandidateMemory(
    content: string,
    type: MemoryType
  ): Promise<boolean> {
    const importance = await this.calculateImportance(content, type);

    if (importance < this.config.importanceThreshold) {
      return false;  // 不够重要,不存储
    }

    this.memories.set(id, { content, type, metadata: { importance } });
    return true;
  }
}
💡 Memory 设计权衡

阈值设置:太低会存储太多噪音,太高会丢失重要信息
时间衰减:旧信息逐渐降低权重,但不是立即删除
去重机制:相似内容合并而非重复存储

9.2

Fork Agent 集成

理解如何使用 Fork Agent 提取和存储 Memory

TypeScript memoryAgent.ts
// ========== Fork Agent 用于 Memory 提取 ==========
class MemoryExtractionAgent {
  async extractMemoriesFromConversation(
    messages: Message[]
  ): Promise<Memory[]> {
    const prompt = `
分析以下对话,提取值得记忆的信息。
- 用户偏好(代码风格、工具偏好)
- 项目信息(架构决策、代码约定)
- 会话上下文(当前任务)
- 工具使用模式

对话:
${messages}.map(m => m.content).join('\\n')
`;

    const response = await anthropic.messages.create({
      model: "claude-sonnet-4-6",
      messages: [{ role: 'user', content: prompt }],
      temperature: 0.3
    });

    const candidates = parseJSON(response.content[0].text);

    const memories: Memory[] = [];
    for (const candidate of candidates) {
      const added = await memory.addCandidateMemory(
        candidate.content,
        candidate.type
      );
      if (added) memories.push(candidate);
    }

    return memories;
  }
}
🎯 Fork Agent 优势

使用独立的 Fork Agent 进行 Memory 提取:1) 不影响主任务性能 2) 可以使用不同温度参数 3) 专注于提取任务,提高准确性 4) 可以批量处理历史对话

9.3

代码理解测验

检验你对 Memory 系统的理解

以下哪种信息应该存储为 PROJECT Memory?

9.4

课程总结

回顾所有模块的核心知识点

🚀 学习成果

通过这 12 个模块的学习,你已经深入理解了:
1. 高性能启动:Fast Path 策略和动态导入
2. 状态机驱动:REPL 和 Query 的核心循环
3. 工具系统:并行执行和依赖解析
4. 上下文优化:双上下文和智能缓存
5. Memory 系统:阈值提取和 Fork Agent
6. MCP 集成:8种传输类型和动态工具发现
7. Feature Gates:编译时优化和死代码消除
8. Git 优化:零子进程和直接文件读取

🎉 恭喜完成全部课程!

10

MCP 系统架构

深入理解 Model Context Protocol 集成,掌握8种传输类型、连接管理和动态工具发现机制

10.1

MCP 架构概览

理解 Model Context Protocol 的整体架构和核心组件

MCP 核心架构
传输层 (Transport Layer) 8种传输类型:STDIO、SSE、WebSocket、HTTP等
协议层 (Protocol Layer) JSON-RPC 2.0 消息协议
服务器层 (Server Layer) MCP 服务器实现和生命周期管理
客户端层 (Client Layer) Claude Code 的 MCP 集成层
💡 设计目标

MCP 提供标准化协议让 AI 应用能够连接外部数据源和工具。Claude Code 通过 MCP 实现:
扩展性:动态发现和加载 MCP 服务器的工具/资源
隔离性:每个 MCP 服务器运行在独立进程
可靠性:健康检查和自动重连机制

10.2

8种传输类型详解

深入理解不同传输方式的特点、适用场景和实现细节

📡 MCP 传输类型对比表

传输类型 连接方式 适用场景 性能
STDIO 标准输入/输出 本地 CLI 工具 ★★★★★
SSE Server-Sent Events HTTP 长连接 ★★★★☆
WebSocket 双向通信 实时数据流 ★★★★★
HTTP REST API 无状态服务 ★★★☆☆
typescript mcp/transport.ts
1// 传输类型枚举和接口定义
2export enum TransportType {
3  STDIO = 'stdio',
4  SSE = 'sse',
5  WEBSOCKET = 'websocket',
6  HTTP = 'http',
7  // ... 其他4种类型
8}
9
10interface Transport {
11  connect(): Promise<Connection>;
12  send(message: JSONRPCMessage): Promise<void>;
13  onMessage(handler: (msg: JSONRPCMessage) => void): Disposable;
14  disconnect(): Promise<void>;
15}
10.3

连接管理和健康检查

掌握 MCP 服务器的连接生命周期管理和健康监控机制

连接生命周期
初始化 (Init) 从配置加载 MCP 服务器列表
连接 (Connect) 建立传输层连接
握手 (Handshake) 发送 initialize 请求,交换能力
运行 (Running) 正常收发消息,健康检查
断开 (Disconnect) 清理资源,触发重连
typescript mcp/connectionManager.ts
1class MCPConnectionManager {
2  private connections = new Map<string, MCPConnection>();
3  private healthCheckInterval: NodeJS.Timeout;
4
5  async connect(serverConfig: MCPServerConfig) {
6    const transport = this.createTransport(serverConfig);
7    const connection = new MCPConnection(transport);
8
9    // 建立连接并握手
10    await connection.connect();
11    await connection.handshake();
12
13    // 启动健康检查
14    this.startHealthCheck(connection);
15
16    this.connections.set(serverConfig.name, connection);
17    return connection;
18  }
19
20  startHealthCheck(connection: MCPConnection) {
21    const interval = setInterval(async () => {
22      const healthy = await connection.ping();
23      if (!healthy) {
24        this.handleUnhealthy(connection);
25      }
26    }, 30000); // 每30秒检查一次
27
28    connection.healthCheckInterval = interval;
29  }
30}
10.4

动态工具和资源发现

理解 MCP 服务器工具和资源的动态发现、注册和调用机制

发现流程
tools/list 请求获取所有可用工具
resources/list 请求获取所有可用资源
动态注册 将发现的工具注册到工具系统
工具调用 通过 MCP 协议调用远程工具
typescript mcp/toolDiscovery.ts
1class MCPToolDiscovery {
2  async discoverTools(connection: MCPConnection) {
3    // 调用 tools/list RPC 方法
4    const response = await connection.sendRequest({
5      jsonrpc: '2.0',
6      id: generateId(),
7      method: 'tools/list',
8      params: {}
9    });
10
11    // 转换为 Claude Code 工具格式
12    const tools = response.result.tools.map(mcpTool => ({
13      name: `mcp_${connection.name}_${mcpTool.name}`,
14      description: mcpTool.description,
15      inputSchema: mcpTool.inputSchema,
16      handler: async (args) => {
17        // 调用 MCP 工具
18        return await connection.callTool(mcpTool.name, args);
19      }
20    }));
21
22    // 注册到工具系统
23    this.toolRegistry.registerTools(tools);
24  }
25}

🔧 MCP 工具调用流程

1. 发现工具
调用 tools/list 获取可用工具列表
2. 注册工具
将 MCP 工具包装为 Claude Code 工具
3. 调用工具
通过 MCP 协议执行远程工具
10.5

服务器生命周期管理

掌握 MCP 服务器的启动、监控、重启和清理机制

typescript mcp/serverLifecycle.ts
1class MCPServerLifecycle {
2  private servers = new Map<string, ServerProcess>();
3
4  async startServer(config: MCPServerConfig) {
5    if (config.transport === TransportType.STDIO) {
6      // 启动子进程
7      const proc = spawn(config.command, config.args, {
8        stdio: ['pipe', 'pipe', 'pipe'],
9        env: { ...process.env, ...config.env }
10      });
11
12      this.servers.set(config.name, {
13        process: proc,
14        config,
15        startTime: Date.now(),
16        restartCount: 0
17      });
18
19      // 监听进程退出
20      proc.on('exit', () => {
21        this.handleServerExit(config.name);
22      });
23    }
24  }
25
26  async handleServerExit(serverName: string) {
27    const server = this.servers.get(serverName);
28
29    // 检查是否需要重启
30    if (server.restartCount < MAX_RESTARTS) {
31      this.logger.info(`Restarting ${serverName}...`);
32      await this.startServer(server.config);
33      server.restartCount++;
34    } else {
35      this.logger.error(`Max restarts reached for ${serverName}`);
36    }
37  }
38}
⚡ 关键优化

延迟启动:MCP 服务器按需启动,而非全部在启动时加载
优雅关闭:发送 shutdown 请求后等待进程退出
资源清理:确保关闭时清理所有子进程和连接

10.6

知识检测

测试你对 MCP 系统架构的理解

MCP 的 STDIO 传输方式最适合什么场景?

11

Feature Gates 系统

深入理解编译时特性开关、Bun bundle 机制和死代码消除原理

11.1

Feature Gates 概览

理解 68 个编译时特性开关的设计理念和实现机制

Feature Gate 层级
编译时开关 (Compile-time) Bun bundle feature() 死代码消除
运行时配置 (Runtime) GrowthBook 动态特性开关
环境变量 (Environment) ENV 特性控制
用户配置 (User Settings) settings.json 中的特性覆盖
💡 设计目标

Feature Gates 实现渐进式功能发布编译时优化
死代码消除:未启用的功能不会进入最终产物
A/B 测试:通过 GrowthBook 运行时控制
降级策略:关键功能可独立禁用
性能优化:减少约 40% 的产物体积

11.2

68个编译时特性开关

探索核心特性开关及其作用

🎛️ 核心特性开关分类

🚀 性能优化 (8)
fast-path、lazy-import、cache 等
🔧 工具系统 (12)
bash、git、editor 等
🌐 网络功能 (6)
http、websocket、browser 等
🤝 集成功能 (10)
mcp、skills、hooks 等
🛡️ 安全功能 (5)
permissions、validation 等
📊 监控调试 (7)
telemetry、logging、debug 等
typescript features/index.ts
1// 特性开关定义
2export const FEATURES = {
3  // 性能优化
4  fastPath: feature('fast-path', true),
5  lazyImport: feature('lazy-import', true),
6  responseCache: feature('response-cache', true),
7
8  // 工具系统
9  toolBash: feature('tool-bash', true),
10  toolGit: feature('tool-git', true),
11  toolEditor: feature('tool-editor', true),
12
13  // ... 68个特性开关
14} as const;
15
16export type FeatureName = keyof typeof FEATURES;
11.3

Bun bundle feature() 机制

深入理解 Bun 的条件编译和死代码消除原理

编译流程
1. 静态分析 扫描所有 feature() 调用
2. 常量折叠 计算编译时的布尔值
3. 死代码消除 移除不可达的代码分支
4. Tree Shaking 删除未使用的导入和函数
typescript features/feature.ts
1import { feature } from 'bun:feature';

2export function createBashTool() {
3  // 如果 feature 返回 false,整个函数会被消除
4  if (!feature('tool-bash')) {
5    return null;
6  }

7  // 这段代码只会在 tool-bash 启用时编译
8  const BashTool = await import('./tools/bash');
9  return new BashTool();
10}
11
12// 编译后的产物(如果 tool-bash = false)
13// export function createBashTool() { return null; }
📊 优化效果

产物体积:从 15MB 减少到 9MB(减少 40%)
启动时间:减少 25% 的初始化工作
内存占用:降低约 30% 的常驻内存

11.4

死代码消除原理

理解编译器如何识别和移除死代码

typescript examples/deadCodeElimination.ts
1// 源代码
2import { HeavyDependency } from './heavy';
3
4if (feature('experimental')) {
5  const heavy = new HeavyDependency();
6  heavy.doSomething();
7}
8

9// 如果 experimental = false,编译后:
10// import 语句被删除(因为 HeavyDependency 未使用)
11// if 分支被删除
12// 整个 heavy 模块不会被打包

🔍 死代码消除示例

❌ 编译前
import { A, B, C } from './lib';
if (feature('a')) A();
if (feature('b')) B();
if (feature('c')) C();
✅ 编译后
// 只有 a=true, c=false
import { A } from './lib';
A();
11.5

GrowthBook 运行时配置

理解运行时特性开关和 A/B 测试机制

typescript features/growthbook.ts
1import { GrowthBook } from '@growthbook/growthbook';

2const gb = new GrowthBook({
3  apiHost: 'https://cdn.growthbook.io',
4  clientKey: process.env.GROWTHBOOK_KEY,
5  enableDevMode: process.env.NODE_ENV === 'development'
6});

7export async function isFeatureEnabled(
8  featureKey: string,
9  defaultValue: boolean = false
): Promise<boolean> {
10  // 等待特性配置加载
11  await gb.init();
12
13  // 检查特性是否启用
14  const enabled = gb.isOn(featureKey);
15
16  // 记录特性使用情况
17  有关日志。debug(`Feature ${featureKey}: ${enabled}`);
18

19  return enabled ?? defaultValue;
20}
🎯 使用场景

渐进式发布:先向 10% 用户开放,逐步扩大到 100%
A/B 测试:对比不同算法或 UI 的效果
快速回滚:发现问题可立即禁用,无需重新部署
用户分组:按用户属性定向展示功能

11.6

知识检测

测试你对 Feature Gates 系统的理解

死代码消除主要发生在哪个阶段?

12

Git 集成系统

掌握零子进程 Git 优化、直接 .git 文件读取和 GitFileWatcher 实现

12.1

Git 集成概览

理解零子进程 Git 优化的设计理念和性能提升

Git 集成层级
gitFilesystem.ts 直接读取 .git 目录文件
Git 解析器 解析 pack、loose object、索引
GitFileWatcher 监控 .git 目录变化
高级功能 Worktree、Submodule 支持
💡 零子进程优化

传统 Git 操作需要启动 git 子进程(如 `git status`),每次调用耗时 50-200ms。
Claude Code 通过直接读取 .git 文件,将耗时降低到 5-20ms,性能提升 10-40倍
对于频繁的 Git 操作(如每次查询都检查状态),累计节省数百毫秒。

12.2

gitFilesystem.ts 实现

深入理解直接读取 .git 文件的核心实现

typescript git/gitFilesystem.ts
1class GitFilesystem {
2  private gitDir: string;
3
span class="highlight">4  constructor(repoPath: string) {
5    this.gitDir = join(repoPath, '.git');
6  }
7
8  // 读取 HEAD 文件获取当前分支
9  async getCurrentBranch(): Promise<string> {
10    const headPath = join(this.gitDir, 'HEAD');
11    const content = await fs.readFile(headPath, 'utf-8');
12

13    // HEAD 内容格式: "ref: refs/heads/main\n"
14    const match = content.match(/^ref: refs\/heads\/(.+)\n?$/);
15    if (!match) throw new Error('Invalid HEAD format');

16    return match[1];
17  }
18
19  // 读取 refs/heads 获取所有分支
20  async getBranches(): Promise<string[]> {
21    const headsDir = join(this.gitDir, 'refs', 'heads');
22    const entries = await fs.readdir(headsDir, { withFileTypes: true });

23    const branches: string[] = [];
24    for (const entry of entries) {
25      if (entry.isDirectory()) {
26        // 处理嵌套分支(如 feature/xxx)
27        const nested = await this.getNestedBranches(join(headsDir, entry.name));
28        branches.push(...nested);
29      } else if (entry.isFile()) {
30        branches.push(entry.name);
31      }
32    }

33    return    branches;
34  }
35}
12.3

直接读取 .git 文件

掌握 Git 对象存储格式和解析方法

Git 对象类型
Blob 文件内容
Tree 目录结构
Commit 提交信息
Tag 标签
typescript git/objectParser.ts
1class GitObjectParser {
2  // 解析 loose object(.git/objects/xx/xxxx...)
3  async parseLooseObject(sha: string): Promise<GitObject> {
4    const objectPath = join(
5      this.gitDir,
6      'objects',
7      sha.slice(0, 2),
8      sha.slice(2)
9    );

10    const compressed = await fs.readFile(objectPath);
11    const decompressed = zlib.inflate(compressed);

12    // Git 对象格式: "type size\0content"
13    const nullIndex = decompressed.indexOf('\0');
14    const header = decompressed.slice(0, nullIndex).toString();
15    const [type, size] = header.split(' ');
16    const content = decompressed.slice(nullIndex + 1);

17    return { type, size, content };
18  }

19  // 解析 pack file(.git/objects/pack/*.pack)
20  async parsePackFile(packPath: string) {
21    const pack = await fs.readFile(packPath);
22    // 解析 pack file 格式...
23  }
24}
📁 .git 目录结构
.git/
├── HEAD              # 当前分支引用
├── objects/          # Git 对象存储
│   ├── xx/           # loose objects
│   └── pack/         # pack files
├── refs/             # 引用
│   ├── heads/        # 分支引用
│   └── tags/         # 标签引用
└── index             # 暂存区索引
12.4

GitFileWatcher 实现

理解如何高效监控 Git 状态变化

typescript git/gitFileWatcher.ts
1class GitFileWatcher extends EventEmitter {
2  private watcher: FSWatcher;
3  private debounceTimer: NodeJS.Timeout;

4  constructor(private gitDir: string) {
5    super();
6    this.setupWatcher();
7  }

8  setupWatcher() {
9    // 监控关键 Git 文件
10    const watchPaths = [
11      join(this.gitDir, 'HEAD'),
12      join(this.gitDir, 'index'),
13      join(this.gitDir, 'refs'),
14    ];

15    this.watcher = fs.watch(gitDir, { recursive: true }, (eventType, filename) => {
16      if (this.isRelevantGitFile(filename)) {
17        this.debounceNotify();
18      }
19    });
20  }

21  debounceNotify() {
22    clearTimeout(this.debounceTimer);
23    this.debounceTimer = setTimeout(() => {
24      this.emit('change');
25    }, 50); // 50ms 防抖
26  }

27  isRelevantGitFile(filename: string): boolean {
28    return filename === 'HEAD' ||
29           filename === 'index' ||
30           filename?.startsWith('refs/');
31  }
32}
12.5

Worktree 和 Submodule 支持

理解高级 Git 功能的实现

typescript git/worktree.ts
1class GitWorktree {
2  // 读取 .git/worktrees 获取所有 worktree
3  async getWorktrees(repoPath: string): Promise<Worktree[]> {
4    const worktreesDir = join(repoPath, '.git', 'worktrees');

5    try {
6      const entries = await fs.readdir(worktreesDir);
7

8      return Promise.all(entries.map(async (name) => {
9        const wtPath = join(worktreesDir, name);
10        const gitdir = join(wtPath, 'gitdir');
11        const head = join(wtPath, 'HEAD');

12        const [gitdirContent, headContent] = await Promise.all([
13          fs.readFile(gitdir, 'utf-8'),
14          fs.readFile(head, 'utf-8')
15        ]);

16        return {
17          name,
18          path: gitdirContent.trim(),
19          head: headContent.trim()
20        };
21      }));
22    } catch {
23      return []; // 没有 worktrees
24    }
25  }
26}
typescript git/submodule.ts
1class GitSubmodule {
2  // 解析 .gitmodules 文件
3  async getSubmodules(repoPath: string): Promise<Submodule[]> {
4    const modulesPath = join(repoPath, '.gitmodules');

5    try {
6      const content = await fs.readFile(modulesPath, 'utf-8');
7      const config = this.parseGitConfig(content);

8      return Object.entries(config).map(([path, info]) => ({
9        path,
10        url: info.url,
11        branch: info.branch
12      }));
13    } catch {
14      return [];
15    }
16  }
17}
12.6

性能对比

零子进程 vs 传统 Git 命令的性能差异

⚡ Git 操作性能对比

操作 传统 Git 零子进程 性能提升
获取当前分支 80ms 8ms 10x
获取所有分支 150ms 15ms 10x
读取 commit 信息 120ms 12ms 10x
检查文件状态 200ms 20ms 10x
🚀 累计效果

对于一个典型的 Query 流程:
• 需要检查当前分支、读取 commit、获取文件状态等 5-10 次 Git 操作
• 传统方式:400-1000ms
• 零子进程:40-100ms
累计节省 350-900ms,显著提升用户体验

12.7

知识检测

测试你对 Git 集成系统的理解

零子进程 Git 优化的核心原理是什么?

13

Prompt 设计系统

深入理解 Static/Dynamic Prompt 分离和缓存优化机制

13.1

Prompt 系统架构

914行 prompts.ts 的完整设计

📝
Static Prompt
系统指令、工具定义、约束条件
Dynamic Prompt
上下文信息、Memory、工具列表
🎯
Boundary
缓存边界优化
💾
Three-Layer Cache
Global → Section → Context
PROMPTS.TS 结构
Lines 1-100 导入依赖、类型定义、feature flags
Lines 100-300 Static Prompt 常量定义
Lines 300-500 Section 管理器和工具注册
Lines 500-700 Dynamic Prompt 构建逻辑
Lines 700-914 缓存优化和导出函数
设计理念

Static/Dynamic 分离是 Prompt 系统的核心优化策略。

Static Prompt 包含不变的内容:系统指令、工具定义、输出格式等。这些内容可以被预编译和缓存,大幅降低 token 消耗。

Dynamic Prompt 包含每次请求都会变化的内容:用户上下文、当前目录、Memory、启用的工具等。这些内容需要实时构建。

通过边界优化,我们精确控制哪些内容进入缓存,哪些内容实时构建,在性能和灵活性之间找到最佳平衡点。

💡 为什么需要分离?

Token 成本:Static Prompt 可能占据 5000+ tokens,如果每次都重新发送,成本巨大
缓存效率:分离后 Static Prompt 只需发送一次,Dynamic Prompt 按需构建
性能提升:缓存命中率提升 80%+,响应速度提升 3-5 倍

13.2

Static/Dynamic Prompt 分离机制

深入理解缓存边界和构建逻辑

typescript prompts.ts - SYSTEM_PROMPT_DYNAMIC_BOUNDARY
// 🎯 关键:定义 Static 和 Dynamic Prompt 的边界
const SYSTEM_PROMPT_DYNAMIC_BOUNDARY = '
═══════════════════════════════════════════════════════════════
STATIC PROMPT ABOVE - THIS LINE IS THE CACHE BOUNDARY
═══════════════════════════════════════════════════════════════
DYNAMIC PROMPT BELOW - NOT CACHED
═══════════════════════════════════════════════════════════════
'

// Static Prompt (可缓存部分)
const STATIC_SECTIONS = [
  '# System Instructions',
  '# Tool Definitions',
  '# Output Format Requirements',
  '# Safety Guidelines',
  // ... 其他不变内容
]

// Dynamic Prompt (实时构建部分)
function buildDynamicPrompt(context: RequestContext): string {
  const sections = []

  // 1. 当前工作目录
  sections.push(`## Current Directory: ${context.cwd}`)

  // 2. Git 状态
  if (context.isGit) {
    sections.push(`## Git Repository: ${context.gitBranch}`)
  }

  // 3. Memory 上下文
  if (context.memories.length > 0) {
    sections.push('## Relevant Memory:')
    sections.push(context.memories.join('\n'))
  }

  // 4. 启用的工具列表
  sections.push('## Available Tools:')
  sections.push(context.enabledTools.map(t => t.name).join(', '))

  return sections.join('\n\n')
}
🎯 边界策略

Static 边界内:所有不会影响 LLM 行为的内容,如系统指令、工具描述格式、输出示例
Dynamic 边界外:所有会根据用户请求变化的内容,如文件路径、Git 状态、Memory、环境变量

Prompt 构建流程

📋
加载 Static
从缓存读取
⚙️
构建 Dynamic
实时生成
🔗
合并 Prompt
边界拼接
📤
发送请求
完整上下文
13.3

Section 管理器

模块化 Prompt 构建系统

typescript prompts.ts - Section Manager
// 📦 Section 管理器 - 模块化 Prompt 构建
interface PromptSection {
  id: string
  priority: number
  static: boolean
  build: (context: RequestContext) => string
}

class SectionManager {
  private sections: Map<string, PromptSection> = new Map()

  // 注册新的 Section
  register(section: PromptSection) {
    this.sections.set(section.id, section)
  }

  // 构建 Static Prompt
  buildStatic(): string {
    const staticSections = Array.from(this.sections.values())
      .filter(s => s.static)
      .sort((a, b) => a.priority - b.priority)
      .map(s => s.build({}))
      .join('\n\n')

    return staticSections + SYSTEM_PROMPT_DYNAMIC_BOUNDARY
  }

  // 构建 Dynamic Prompt
  buildDynamic(context: RequestContext): string {
    const dynamicSections = Array.from(this.sections.values())
      .filter(s => !s.static)
      .sort((a, b) => a.priority - b.priority)
      .map(s => s.build(context))
      .join('\n\n')

    return dynamicSections
  }
}
🔧 Section 管理器优势

模块化:每个 Prompt 部分独立管理,易于维护和扩展
优先级控制:通过 priority 确保 Prompt 部分的正确顺序
条件渲染:根据上下文动态决定是否包含某个 Section
测试友好:可以独立测试每个 Section 的构建逻辑

13.4

三层缓存优化

Global → Section → Context 缓存策略

缓存层次结构

🌍
Global Cache
跨会话共享
永久缓存
📦
Section Cache
Section 级别
会话内缓存
🎯
Context Cache
请求级别
即时缓存
缓存实现
Global Cache Static Prompt 完整内容,跨所有会话共享
Section Cache 每个 Section 的构建结果,key 为 sectionId + contextHash
Context Cache 单次请求内的中间结果,避免重复计算
缓存命中率
Global Cache 95%+
Section Cache 80%+
Context Cache 60%+
13.5

代码理解测验

检验你对 Prompt 系统的理解

SYSTEM_PROMPT_DYNAMIC_BOUNDARY 的作用是什么?

Global Cache 的命中率为什么能达到 95%+?

14

综合项目实践

将所学知识应用于真实场景,完成完整的工程实践

14.1

真实场景代码走查

分析完整的用户请求处理流程

完整请求流程

🚀
启动
Fast Path 初始化
模块加载
📝
接收请求
Query 循环
状态机切换
🔍
上下文构建
双上下文
Memory 加载
🛠️
工具执行
并行调用
依赖解析
返回结果
Memory 更新
状态重置
typescript 完整场景示例:用户请求"帮我优化这个函数"
// 🎯 场景:用户发送 "帮我优化 src/utils/format.ts 中的 formatDate 函数"

// === 步骤 1: Query 循环接收 ===
// repl.ts - state machine
async function handleQuery(userQuery: string) {
  currentState = State.QUERY
  // 解析意图
  const intent = parseIntent(userQuery)
}

// === 步骤 2: 上下文构建 ===
// context.ts - dual context
const context = await buildContext({
  cwd: '/project',
  files: ['src/utils/format.ts'],
  memories: await memory.getRelevant(userQuery),
  gitStatus: await git.getStatus()
})

// === 步骤 3: Prompt 构建 ===
// prompts.ts - section manager
const prompt = sectionManager.buildFullPrompt({
  static: cachedStaticPrompt,  // 从缓存读取
  dynamic: [
    systemInstructions.build(context),
    fileContext.build('src/utils/format.ts'),
    toolDefinitions.build([ReadTool, EditTool])
  ]
})

// === 步骤 4: 工具并行执行 ===
// tool.ts - parallel execution
const results = await executeToolsInParallel([
  { tool: 'read', file: 'src/utils/format.ts' },
  { tool: 'grep', pattern: 'formatDate', path: 'src/utils/format.ts' }
])

// === 步骤 5: LLM 调用和结果处理 ===
const optimized = await claude.optimizeFunction(prompt, results)

// === 步骤 6: Memory 更新 ===
await memory.add({
  type: 'PROJECT',
  content: '用户优化了 formatDate 函数,关注性能和可读性',
  tags: ['optimization', 'format.ts']
})

// === 步骤 7: 返回结果 ===
currentState = State.REPL
return optimized
🔍 关键观察点

状态转换:REPL → QUERY → EXECUTE → REPL 的清晰流程
上下文复用:Static Prompt 从缓存读取,只构建 Dynamic 部分
并行优化:Read 和 Grep 工具并行执行,节省 50% 时间
Memory 学习:自动提取和存储项目信息,为未来请求提供上下文

14.2

性能优化实践

真实场景中的优化策略和效果

优化案例 1:冷启动优化
问题 首次启动需要加载 50+ 模块,耗时 3.5 秒
方案 Fast Path:优先加载核心模块,延迟导入次要模块
结果 启动时间降至 0.8 秒,提升 77%
优化案例 2:Prompt 缓存
问题 每次请求都重新构建完整 Prompt,平均 8000 tokens
方案 Static/Dynamic 分离,三层缓存策略
结果 平均降至 3000 tokens,节省 62%
🎯 优化最佳实践

测量先行:使用 performance API 精确测量每个环节
缓存策略:区分可缓存和不可缓存内容
并行思维:识别可并行化的操作
渐进优化:从大处着手,逐步细化

14.3

常见陷阱和最佳实践

避免常见错误,遵循最佳实践

❌ 陷阱 1:过度缓存

错误:将所有内容都缓存,包括频繁变化的数据
后果:返回过时结果,用户困惑
正确:只缓存不变的 Static Prompt,Dynamic Prompt 实时构建

✅ 最佳实践 1:精确的缓存失效

策略:为不同类型的数据设置不同的 TTL
实现:Global Cache 永久,Section Cache 1 分钟,Context Cache 请求级
效果:平衡性能和准确性

📋 最佳实践 Checklist

启动优化:使用 Fast Path,延迟加载非核心模块
缓存策略:Static/Dynamic 分离,精确控制缓存边界
并行执行:构建依赖图,最大化并行度
上下文管理:双上下文策略,平衡灵活性和性能
Memory 优化:设置阈值,智能淘汰低价值 Memory
错误处理:优雅降级,提供有意义的错误信息
监控指标:跟踪关键性能指标,持续优化

14.4

扩展开发指南

如何为 Claude Code 添加新功能

typescript 添加新工具的完整流程
// 🎯 案例:添加一个新的 "SearchOnline" 工具

// === 步骤 1: 创建工具类 ===
export class SearchOnlineTool extends Tool {
  name = 'search_online'
  description = 'Search the web for current information'

  async execute(params: { query: string }) {
    return await searchOnline(params.query)
  }
}

// === 步骤 2: 创建 Prompt 定义 ===
export const SEARCH_ONLINE_SECTION = `
## Search Online Tool
Use this tool when you need to find current information.
`

// === 步骤 3: 注册到系统 ===
// 在 prompts.ts 中注册工具
const ALL_TOOLS = [
  { name: SEARCH_ONLINE_TOOL_NAME, section: SEARCH_ONLINE_SECTION }
]

// === 步骤 4: 添加权限控制 ===
export const TOOL_PERMISSIONS = {
  [SEARCH_ONLINE_TOOL_NAME]: {
    allowedInModes: ['standard', 'careful'],
    requiresConfirmation: false
  }
}
🔧 扩展开发原则

模块化:每个工具独立,职责单一
可测试:提供完整的测试覆盖
文档化:清晰的使用说明和示例
性能意识:考虑对整体性能的影响
安全优先:适当的权限控制和验证

14.5

课程总结与展望

回顾 14 个模块的核心知识点

完整知识体系

模块 1-3
启动与核心循环
Fast Path + REPL + Query
模块 4-6
权限与配置
RBAC + Settings + Agent
模块 7-9
工具与上下文
Tools + Context + Memory
模块 13-14
Prompt 与实践
Design + Optimization
🚀 学习成果

通过这 14 个模块的学习,你已经深入理解了:

1. 高性能启动:Fast Path 策略和动态导入
2. 状态机驱动:REPL 和 Query 的核心循环
3. 工具系统:并行执行和依赖解析
4. 上下文优化:双上下文和智能缓存
5. Memory 系统:阈值提取和 Fork Agent
6. Prompt 设计:Static/Dynamic 分离和三层缓存
7. 性能优化:真实场景的优化策略
8. 工程实践:常见陷阱和最佳实践

📚 学习路径建议
✅ 初级工程师
重点学习:模块 1-3(启动和核心循环)
目标:理解 CLI 工具的基本架构

⭐ 中级工程师
重点学习:模块 4-9(权限到 Memory)
目标:掌握复杂系统的设计和优化

🚀 高级工程师
重点学习:模块 13-14(Prompt 和实践)
目标:深入性能优化和架构设计

💡 进阶资源
• Claude API 文档:https://docs.anthropic.com
• 源码仓库:github.com/anthropics/claude-code
• 社区讨论:Claude Code Discord
• 最佳实践:ARCHITECTURE.md

🎉 恭喜完成全部课程!

你现在已经深入理解了 Claude Code 的完整技术栈
可以开始构建自己的 AI 工程应用了

查看源码 →
14.6

综合能力测验

检验你对整个 Claude Code 系统的理解

在添加新工具时,以下哪步不是必须的?

Static/Dynamic Prompt 分离的核心优势是什么?

在性能优化时,应该优先关注哪个指标?

三层缓存策略的正确顺序是?

15

Bash 解析器

纯 TypeScript 实现的 Bash 解析器,Tree-sitter 兼容 AST,UTF-8 字节偏移计算

Bash 解析器架构

文件: src/utils/bash/bashParser.ts (4,436 行)

核心职责:

  • 纯 TypeScript 实现的 Bash 解析器
  • 生成与 tree-sitter-bash 兼容的 AST
  • 完整的词法分析(Lexer)和语法分析(Parser)
  • UTF-8 字节偏移计算

解析器架构

Bash 解析器
bashParser.ts: 4436行
Lexer
词法分析
15种Token类型
Parser
语法分析
AST构建
UTF-8
字节偏移
懒加载byteTable
AST 节点树
Token流 → 完整语法树

🎯 设计目标

  • 兼容性:与 tree-sitter-bash AST 兼容
  • 性能:50ms 超时限制,50k 节点限制
  • 安全性:处理对抗性输入
  • 国际化:完整的 UTF-8 支持

词法分析(Lexer)

Lexer 负责将 Bash 源代码分解为 Token 流。

Token 类型(15种)

type TokenType =
  | 'WORD'          // 单词/标识符
  | 'NUMBER'        // 数字
  | 'OP'            // 操作符
  | 'NEWLINE'       // 换行符
  | 'COMMENT'       // 注释
  | 'DQUOTE'        // 双引号
  | 'SQUOTE'        // 单引号
  | 'ANSI_C'        // ANSI-C 字符串 ($'...')
  | 'DOLLAR'        // 美元符号
  | 'DOLLAR_PAREN'  // $(( 算术扩展
  | 'DOLLAR_BRACE'  // ${} 参数扩展
  | 'BACKTICK'      // 反引号
  | 'LT_PAREN'      // <(
  | 'GT_PAREN'      // >(
  | 'EOF'           // 文件结束

💡 中文解释

Bash 词法分析器识别 15 种不同的 Token 类型,覆盖了 Bash 语法的所有基本元素:

  • 基本元素:WORD(单词)、NUMBER(数字)、OP(操作符)
  • 字符串:DQUOTE(双引号)、SQUOTE(单引号)、ANSI_C(ANSI-C 转义)
  • 变量扩展:DOLLAR、DOLLAR_PAREN、DOLLAR_BRACE、BACKTICK
  • 其他:NEWLINE(换行)、COMMENT(注释)、EOF(结束)

Lexer 状态

type Lexer = {
  src: string           // 源代码
  len: number          // 源代码长度
  i: number           // JS string index (字符索引)
  b: number           // UTF-8 byte offset (字节偏移)
  heredocs: HeredocPending[]  // 待处理的 heredoc
  byteTable: Uint32Array | null  // 字节偏移表(懒加载)
}

🔍 关键设计

  • 双索引系统:i(字符索引)和 b(字节偏移)分开维护
  • Heredoc 队列:延迟处理,先收集后扫描
  • 懒加载 byteTable:只在遇到非 ASCII 字符时才构建
  • 性能优化:纯 ASCII 代码零开销

UTF-8 字节偏移计算

这是 Bash 解析器最复杂的部分之一,需要正确处理多字节 UTF-8 字符。

字符前进函数

function advance(L: Lexer): void {
  const c = L.src.charCodeAt(L.i)
  L.i++
  if (c < 0x80) {
    // ASCII: 1 字节 = 1 字符
    L.b++
  } else if (c < 0x800) {
    // 2 字节 UTF-8
    L.b += 2
  } else if (c >= 0xd800 && c <= 0xdbff) {
    // 高代理项:需要下一个字符完成代理对
    L.b += 4
    L.i++
  } else {
    // 3 字节 UTF-8
    L.b += 3
  }
}

📝 UTF-8 编码规则

  • ASCII (0x00-0x7F):1 字节
  • 2 字节 (0x80-0x7FF):2 字节
  • 3 字节 (0x800-0xFFFF):3 字节
  • 代理项 (0xD800-0xDFFF):4 字节

关键点:JavaScript 字符串索引(i)和 UTF-8 字节偏移(b)不同,需要分别维护。

字节偏移表(懒加载)

function byteAt(L: Lexer, charIdx: number): number {
  // 快速路径:纯 ASCII 前缀
  if (!L.byteTable) return charIdx
  
  // 懒构建表:首次遇到非 ASCII 时构建
  const t = new Uint32Array(L.len + 1)
  let b = 0
  let i = 0
  while (i < L.len) {
    t[i] = b
    const c = L.src.charCodeAt(i)
    if (c < 0x80) {
      b++
      i++
    } else if (c < 0x800) {
      b += 2
      i++
    } else if (c >= 0xd800 && c <= 0xdbff) {
      t[i + 1] = b + 2
      b += 4
      i += 2
    } else {
      b += 3
      i++
    }
  }
  t[L.len] = b
  L.byteTable = t
  return t[charIdx]
}

⚡ 性能优化策略

  • 快速路径:纯 ASCII 代码直接返回字符索引(零开销)
  • 懒加载:只在需要时构建字节表
  • O(1) 查找:建立后常数时间查找
  • 内存优化:使用 Uint32Array,每个条目 4 字节

🎯 实际例子

// 输入: "echo 你好"
// 字符索引:  0 1 2 3 4 5 6 7
// 字符串:    e c h o   你  好
// 字节偏移:  0 1 2 3 4 5 7 9
// 
// '你' = U+4F60 (UTF-8: E4 BD A0, 3字节)
// '好' = U+597D (UTF-8: E5 A5 BD, 3字节)

语法分析(Parser)

Parser 负责将 Token 流转换为 AST(抽象语法树)。

ParseState

type ParseState = {
  L: Lexer
  src: string
  srcBytes: number
  /** True when byte offsets == char indices (no multi-byte UTF-8) */
  isPureAscii: boolean
  /** Parser node budget - bail out before OOM on deeply nested input */
  nodes: number
  inBacktick: number
}

🔍 关键字段

  • isPureAscii:优化标志,纯 ASCII 时跳过字节偏移计算
  • nodes:节点计数器,防止深度嵌套导致 OOM
  • inBacktick:反引号嵌套层级

主解析函数

function parseSource(source: string, timeoutMs?: number): TsNode | null {
  const startTime = Date.now()
  
  // 1. 初始化 Lexer
  const L = makeLexer(source)
  
  // 2. 初始化 ParseState
  const P: ParseState = {
    L,
    src: source,
    srcBytes: L.b,
    isPureAscii: true,
    nodes: 0,
    inBacktick: 0,
  }
  
  // 3. 解析程序
  const program = parseProgram(P)
  
  // 4. 超时检查
  const elapsed = Date.now() - startTime
  if (timeoutMs && elapsed > timeoutMs) {
    return null
  }
  
  return program
}

📝 解析流程

  1. 初始化 Lexer(词法分析器)
  2. 初始化 ParseState(解析状态)
  3. 调用 parseProgram 解析整个程序
  4. 检查是否超时
  5. 返回 AST 或 null(超时)

Heredoc 和变量扩展

Heredoc 和变量扩展是 Bash 语法中最复杂的部分之一。

Heredoc 处理

type HeredocPending = {
  delim: string        // 分隔符
  stripTabs: boolean  // 是否去除制表符
  quoted: boolean     // 是否引用
  bodyStart: number   // body 开始位置
  bodyEnd: number     // body 结束位置
  endStart: number    // 结束标记开始
  endEnd: number      // 结束标记结束
}

// 词法分析:检测 << delimiter
if (c === '<' && c1 === '<') {
  const start = L.b
  advance(L)
  advance(L)
  
  const delim = parseHeredocDelimiter(L)
  if (delim) {
    L.heredocs.push({
      delim: delim.delim,
      stripTabs: delim.stripTabs,
      quoted: delim.quoted,
      bodyStart: -1,
      bodyEnd: -1,
      endStart: -1,
      endEnd: -1,
    })
  }
}

🔍 Heredoc 工作流程

  1. 词法分析:检测 << delimiter
  2. 收集阶段:将 heredoc 信息加入队列
  3. 扫描阶段:遇到换行时扫描 heredoc body
  4. AST 构建:将 heredoc 作为节点加入 AST

变量扩展解析

function parseDollarLike(P: ParseState): TsNode | null {
  // $(( 算术扩展
  if (c1 === '(' && c2 === '(') {
    advance(L)
    advance(L)
    advance(L)
    const expansion = parseExpansionBody(P, true, false)
    // ...
  }
  
  // $( 命令替换
  if (c1 === '(') {
    advance(L)
    advance(L)
    const expansion = parseExpansionBody(P, false, false)
    // ...
  }
  
  // ${} 参数扩展
  if (c1 === '{') {
    advance(L)
    advance(L)
    const expansion = parseExpansionBody(P, false, true)
    // ...
  }
  
  // $'...' ANSI-C 字符串
  if (c1 === "'") {
    advance(L)
    advance(L)
    // 扫描到结束的 '
    // ...
  }
  
  // $ 简单变量
  advance(L)
  return parseBareWord(P)
}

💡 变量扩展类型

  • $((...)):算术扩展
  • $(...):命令替换
  • ${...}:参数扩展
  • $'...':ANSI-C 转义字符串
  • $var:简单变量扩展

性能优化

Bash 解析器实现了多层性能优化,确保在各种场景下都能快速响应。

⚡ 优化策略

1. 超时保护

const PARSE_TIMEOUT_MS = 50  // 50ms 墙钟超时

function parseSource(source: string, timeoutMs?: number): TsNode | null {
  const startTime = Date.now()
  // ... 解析逻辑
  
  const elapsed = Date.now() - startTime
  if (timeoutMs && elapsed > timeoutMs) {
    return null  // 超时保护
  }
}

目的:防止恶意输入导致无限循环

2. 节点预算

const MAX_NODES = 50_000  // 50k 节点限制

function parseStatements(P: ParseState): TsNode[] {
  const out: TsNode[] = []
  
  while (P.nodes < MAX_NODES) {  // 检查节点预算
    // ... 解析逻辑
  }
  
  return out
}

目的:防止深度嵌套导致 OOM

3. 字节偏移表懒加载

type Lexer = {
  byteTable: Uint32Array | null  // 懒加载
}

function byteAt(L: Lexer, charIdx: number): number {
  // 快速路径:纯 ASCII
  if (!L.byteTable) return charIdx
  
  // 懒加载:首次遇到非 ASCII 时构建
  // ...
}

目的:纯 ASCII 代码零开销

4. 纯 ASCII 快速路径

type ParseState = {
  isPureAscii: boolean  // 纯 ASCII 标志
}

// 在 advance 中更新标志
if (c < 0x80) {
  // ASCII 路径(快速)
} else {
  // 非 ASCII 路径(需要更新字节偏移表)
  P.isPureAscii = false
}

目的:跳过不必要的 UTF-8 处理

🎯 性能对比

场景 优化前 优化后
纯 ASCII 脚本 ~10ms ~1ms
混合 UTF-8 ~15ms ~5ms
深度嵌套 OOM 50k 节点限制

知识点检验

测试你对 Bash 解析器的理解程度

1. Bash 解析器支持多少种 Token 类型?

2. 字符 '你'(U+4F60)在 UTF-8 中占用几个字节?

3. 解析器的超时限制是多少?

4. 以下哪个不是 Bash 变量扩展的语法?

16

附件系统

40+ 种附件类型管理、智能相关性检索、增量更新机制

附件系统架构

文件: src/utils/messages.ts (5,512 行)

核心职责:

  • 消息生命周期管理、格式化、规范化、索引构建
  • 40+ 种附件类型管理
  • 智能相关性检索
  • 增量更新机制
  • Token 预算管理

消息处理系统架构

消息处理系统
messages.ts: 5512行
消息创建
Creation
Assistant/User/Progress
消息规范化
Normalize
多块拆分/UUID派生
消息索引
Lookups
O(1)查找/工具关联
智能附件系统
40+类型/相关性检索/增量更新

🎯 设计亮点

  • 智能检索:自动选择最相关的 5 个 memories
  • 增量更新:只发送变化部分(diff)
  • 双重限制:行数 + 字节数双重约束
  • 会话累积:60KB 后停止检索
  • 并行执行:独立的附件生成同时运行

消息类型系统

附件系统支持多种消息类型,每种类型都有特定的用途和格式。

基础消息类型

type Message = 
  | UserMessage          // 用户消息
  | AssistantMessage     // 助手消息
  | AttachmentMessage    // 附件消息
  | ProgressMessage      // 进度消息
  | SystemMessage        // 系统消息
  | TombstoneMessage     // 墓碑消息(已删除)

// 规范化消息类型(每个消息只包含一个 content block)
type NormalizedMessage = 
  | NormalizedUserMessage      // 单块用户消息
  | NormalizedAssistantMessage // 单块助手消息

💡 消息类型说明

  • UserMessage:用户发送的消息
  • AssistantMessage:AI 助手的回复
  • AttachmentMessage:附件文件或数据
  • ProgressMessage:任务进度更新
  • SystemMessage:系统级消息
  • TombstoneMessage:已删除的消息标记

助手消息结构

interface AssistantMessage {
  type: 'assistant'
  uuid: UUID
  timestamp: string
  message: {
    id: UUID
    role: 'assistant'
    content: ContentBlock[]  // 可包含多个 block
    model: string
    stop_reason: string | null
  }
}

interface ContentBlock {
  type: 'text' | 'tool_use' | 'tool_result' | 'thinking'
  text?: string
  tool_use?: {...}
  tool_result?: {...}
}

🔍 ContentBlock 类型

  • text:普通文本内容
  • tool_use:工具调用请求
  • tool_result:工具执行结果
  • thinking:模型思考过程

关键点:一个消息可以包含多个 ContentBlock,这是规范化的关键。

消息规范化

规范化是将多块消息拆分为单块消息的过程,便于统一处理。

规范化函数

function normalizeMessages(messages: Message[]): NormalizedMessage[] {
  const out: NormalizedMessage[] = []
  
  for (const message of messages) {
    if (message.type === 'user' || message.type === 'assistant') {
      const content = message.message.content
      
      if (content.length === 0) {
        continue  // 跳过空消息
      }
      
      if (content.length === 1) {
        // 单块消息,直接使用
        out.push(message as NormalizedMessage)
      } else {
        // 多块消息,拆分为多个单块消息
        for (const block of content) {
          out.push({
            type: message.type,
            uuid: deriveUUID(message.uuid, content.indexOf(block)),
            timestamp: message.timestamp,
            message: {
              ...message.message,
              content: [block]
            }
          })
        }
      }
    }
  }
  
  return out
}

🔍 规范化流程

  1. 遍历消息:逐个处理输入消息
  2. 检查块数:判断消息包含多少个 ContentBlock
  3. 单块直接使用:如果只有一个 block,直接使用
  4. 多块拆分:如果有多个 block,拆分为多个消息
  5. UUID 派生:为拆分出的消息派生新的 UUID

UUID 派生

function deriveUUID(parentUuid: UUID, index: number): UUID {
  // 使用父 UUID + 索引派生新 UUID
  const data = `${parentUuid}-${index}`
  return uuid5(DNS_NAMESPACE, data)
}

// 示例:
// 父消息 UUID: "abc123"
// 拆分后:
//   block 0 → "abc123-0" 的 hash
//   block 1 → "abc123-1" 的 hash
//   block 2 → "abc123-2" 的 hash

💡 UUID 派生的优势

  • 可追溯:可以从子 UUID 追溯回父消息
  • 唯一性:保证每个拆分消息的 UUID 唯一
  • 稳定性:相同输入总是产生相同输出
  • 去重:避免重复处理相同内容

🎯 规范化示例

// 原始消息(3个 block)
{
  type: 'assistant',
  uuid: 'msg-001',
  message: {
    content: [
      { type: 'text', text: '让我帮你...' },
      { type: 'tool_use', name: 'read', ... },
      { type: 'tool_result', ... }
    ]
  }
}

// 规范化后(3个单块消息)
[
  {
    type: 'assistant',
    uuid: 'msg-001-0-hash',
    message: { content: [{ type: 'text', text: '让我帮你...' }] }
  },
  {
    type: 'assistant',
    uuid: 'msg-001-1-hash',
    message: { content: [{ type: 'tool_use', ... }] }
  },
  {
    type: 'assistant',
    uuid: 'msg-001-2-hash',
    message: { content: [{ type: 'tool_result', ... }] }
  }
]

智能附件检索

附件系统的核心功能是根据上下文智能选择最相关的附件。

相关性检索

async function retrieveRelevantAttachments(
  query: string,
  context: SessionContext,
  maxAttachments: number = 5
): Promise {
  // 1. 计算 query 和所有 memories 的相似度
  const similarities = memories.map(memory => ({
    memory,
    score: computeSimilarity(query, memory)
  }))
  
  // 2. 按相似度排序
  similarities.sort((a, b) => b.score - a.score)
  
  // 3. 选择 top K
  const topK = similarities.slice(0, maxAttachments)
  
  // 4. 构建附件
  return topK.map(item => buildAttachment(item.memory))
}

🔍 检索策略

  1. 相似度计算:计算查询与每个 memory 的相似度
  2. 排序:按相似度降序排序
  3. Top K 选择:选择最相关的 5 个
  4. 附件构建:将 memory 转换为附件格式

增量更新

function buildIncrementalUpdate(
    previousAttachments: Attachment[],
    currentAttachments: Attachment[]
  ): Attachment[] {
  // 1. 计算差异
  const added = currentAttachments.filter(a => 
    !previousAttachments.some(p => p.uuid === a.uuid)
  )
  
  const removed = previousAttachments.filter(p => 
    !currentAttachments.some(a => a.uuid === p.uuid)
  )
  
  const modified = currentAttachments.filter(a => {
    const prev = previousAttachments.find(p => p.uuid === a.uuid)
    return prev && !deepEqual(prev, a)
  })
  
  // 2. 只发送变化部分
  return {
    added,
    removed,
    modified
  }
}

💡 增量更新优势

  • 减少带宽:只传输变化部分
  • 降低延迟:更少的数据传输
  • 节省 Token:减少 API 调用成本
  • 提高响应速度:更快的处理速度

Token 预算管理

附件系统需要严格控制 Token 使用,避免超出预算。

双重限制

interface AttachmentBudget {
  // 行数限制
  maxLines: number
  
  // 字节数限制
  maxBytes: number
  
  // 会话累积限制
  sessionMaxBytes: number
}

const DEFAULT_BUDGET: AttachmentBudget = {
  maxLines: 1000,
  maxBytes: 100_000,  // 100KB
  sessionMaxBytes: 60_000  // 60KB 后停止检索
}

function checkBudget(
  attachment: Attachment,
  budget: AttachmentBudget
): boolean {
  // 检查行数
  if (attachment.lineCount > budget.maxLines) {
    return false
  }
  
  // 检查字节数
  if (attachment.byteCount > budget.maxBytes) {
    return false
  }
  
  return true
}

🔍 预算检查层次

  • 单附件限制:每个附件不能超过 1000 行或 100KB
  • 会话累积:整个会话的附件总大小超过 60KB 后停止检索
  • 动态调整:根据剩余预算调整检索策略

会话累积跟踪

class AttachmentSessionManager {
  private sessionBytes = 0
  private readonly limit = 60_000  // 60KB
  
  addAttachment(attachment: Attachment): boolean {
    // 检查是否超过会话限制
    if (this.sessionBytes >= this.limit) {
      return false  // 停止检索
    }
    
    // 检查添加后是否会超限
    if (this.sessionBytes + attachment.byteCount > this.limit) {
      // 可以添加,但之后会停止
      this.sessionBytes += attachment.byteCount
      return true
    }
    
    // 正常添加
    this.sessionBytes += attachment.byteCount
    return true
  }
  
  shouldStopRetrieval(): boolean {
    return this.sessionBytes >= this.limit
  }
}

💡 会话管理策略

  • 累积计数:跟踪整个会话的附件总大小
  • 软限制:达到 60KB 后停止检索
  • 软边界:允许最后一个附件超出限制
  • 重置机制:新会话重置计数器

⚡ 性能优化

并行附件生成

async function buildAttachmentsParallel(
    sources: AttachmentSource[]
  ): Promise {
  // 并行执行独立的附件生成
  const results = await Promise.allSettled(
    sources.map(source => buildAttachment(source))
  )
  
  // 过滤成功的
  return results
    .filter(r => r.status === 'fulfilled')
    .map(r => r.value)
}

优势:独立的附件生成同时运行,大幅提升速度

缓存优化

const attachmentCache = new Map()

function getCachedAttachment(key: string): Attachment | null {
  return attachmentCache.get(key) || null
}

function setCachedAttachment(key: string, attachment: Attachment): void {
  attachmentCache.set(key, attachment)
}

优势:避免重复生成相同附件

知识点检验

测试你对附件系统的理解程度

1. 附件系统支持多少种附件类型?

2. 智能检索最多选择多少个最相关的 memories?

3. 会话累积限制是多少?

4. 增量更新的主要优势是什么?

17

API 调用系统

queryModel 核心查询、智能重试机制、Prompt 缓存优化、Tool Search 动态加载

API 调用系统架构

文件: src/services/api/claude.ts (3,419 行)

核心职责:

  • 封装 Claude API 调用
  • 处理请求构建、响应解析
  • 智能重试机制
  • Prompt 缓存优化
  • Tool Search 动态加载
  • Streaming 处理

queryModel 核心架构

queryModel()
核心查询函数
请求准备
Preparation
模型解析/Beta headers
执行阶段
Execution
Streaming/重试逻辑
响应处理
Response
Delta处理/成本追踪
Claude API
Request/Response

🎯 核心特性

  • 智能重试:自动降级 max_tokens 和 fallback model
  • 动态工具加载:Tool Search 减少初始工具数量
  • Prompt 缓存:Global cache + 1h TTL
  • Streaming:增量构建消息,低延迟
  • Beta Headers Latching:防止缓存失效
  • Thinking 支持:Adaptive 和 Budget 两种模式

请求准备阶段

在调用 API 之前,需要准备各种请求参数和配置。

getExtraBodyParams()

export function getExtraBodyParams(betaHeaders?: string[]): JsonObject {
  // 第一步:解析环境变量
  const extraBodyStr = process.env.CLAUDE_CODE_EXTRA_BODY
  let result: JsonObject = {}
  
  if (extraBodyStr) {
    const parsed = safeParseJSON(extraBodyStr)
    if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
      result = { ...(parsed as JsonObject) }
    }
  }
  
  // 第二步:反蒸馏保护(1P CLI 专用)
  if (feature('ANTI_DISTILLATION_CC') && 
      process.env.CLAUDE_CODE_ENTRYPOINT === 'cli' &&
      shouldIncludeFirstPartyOnlyBetas() &&
      getFeatureValue('tengu_anti_distill_fake_tool_injection', false)) {
    result.fake_tools = ['reading', 'writing']
  }
  
  // 第三步:处理 Beta headers
  if (betaHeaders && betaHeaders.length > 0) {
    result.beta_headers = betaHeaders
  }
  
  return result
}

🔍 参数构建流程

  1. 环境变量:解析 CLAUDE_CODE_EXTRA_BODY
  2. 反蒸馏保护:添加 fake_tools 混淆真实工具
  3. Beta Headers:添加 Beta 功能请求头

模型解析

function parseModel(modelString: string): ParsedModel {
  // 支持 "model:beta_header" 格式
  const parts = modelString.split(':')
  const model = parts[0]
  const betaHeaders = parts.slice(1)
  
  return {
    model,
    betaHeaders: betaHeaders.length > 0 ? betaHeaders : undefined
  }
}

// 示例:
// "claude-sonnet-4-6:prompt-caching,pdf-tools"
// → {
//     model: "claude-sonnet-4-6",
//     betaHeaders: ["prompt-caching", "pdf-tools"]
//   }

💡 Beta Headers 作用

  • 功能开关:启用实验性功能
  • Prompt Caching:减少重复 Token 计费
  • PDF Tools:启用 PDF 处理能力
  • 格式:model:beta1,beta2

智能重试机制

API 调用可能因为各种原因失败,智能重试机制确保请求最终成功。

重试策略

async function queryModelWithRetry(
  request: ApiRequest,
  options: QueryOptions
): Promise {
  const MAX_RETRIES = 3
  let lastError: Error
  
  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
    try {
      const response = await queryModelOnce(request, options)
      return response  // 成功,直接返回
    } catch (error) {
      lastError = error
      
      // 检查是否可重试
      if (!isRetryableError(error)) {
        throw error  // 不可重试,直接抛出
      }
      
      // 智能降级
      request = applyFallback(request, error, attempt)
      
      // 等待后重试
      if (attempt < MAX_RETRIES - 1) {
        await sleep(calculateBackoff(attempt))
      }
    }
  }
  
  throw lastError  // 所有重试都失败
}

🔍 重试逻辑

  1. 尝试调用:执行 API 请求
  2. 成功返回:立即返回结果
  3. 失败检查:判断是否可重试
  4. 智能降级:调整请求参数
  5. 等待重试:指数退避
  6. 最终失败:抛出最后一个错误

智能降级

function applyFallback(
  request: ApiRequest,
  error: Error,
  attempt: number
): ApiRequest {
  // 1. max_tokens 降级
  if (error instanceof MaxTokensError) {
    const newMaxTokens = Math.floor(request.max_tokens * 0.8)
    return {
      ...request,
      max_tokens: newMaxTokens
    }
  }
  
  // 2. Model fallback
  if (error instanceof ModelUnavailableError && attempt === 1) {
    return {
      ...request,
      model: FALLBACK_MODEL  // 降级到备用模型
    }
  }
  
  // 3. 其他调整
  return request
}

// 降级策略:
// attempt 0: 原始请求
// attempt 1: max_tokens * 0.8
// attempt 2: max_tokens * 0.6 + fallback model

💡 降级策略

  • Max Tokens 降级:逐步减少输出长度(80% → 60%)
  • Model Fallback:第2次重试使用备用模型
  • 指数退避:等待时间递增(1s → 2s → 4s)
  • 上下文保留:保持消息内容不变

⚡ 重试性能

指数退避算法

function calculateBackoff(attempt: number): number {
  // 基础延迟 1s,指数增长
  const baseDelay = 1000  // 1秒
  const maxDelay = 4000   // 最大 4秒
  
  const delay = Math.min(
    baseDelay * Math.pow(2, attempt),
    maxDelay
  )
  
  // 添加随机抖动(±25%)
  const jitter = delay * 0.25 * (Math.random() * 2 - 1)
  
  return delay + jitter
}

// attempt 0: 1s ± 250ms
// attempt 1: 2s ± 500ms
// attempt 2: 4s ± 1000ms

目的:避免雷鸣效应(thundering herd)

Prompt 缓存优化

Prompt 缓存可以大幅减少 Token 使用和 API 延迟。

缓存策略

interface PromptCacheConfig {
  // Global cache scope(跨会话复用)
  enabled: boolean
  
  // TTL(1 小时)
  ttl: number
  
  // 最大缓存大小
  maxSize: number
}

const DEFAULT_CACHE_CONFIG: PromptCacheConfig = {
  enabled: true,
  ttl: 60 * 60 * 1000,  // 1 小时
  maxSize: 1000
}

function shouldUsePromptCache(user: User): boolean {
  // 检查用户是否 eligible
  if (!user.isEligibleForPromptCache) {
    return false
  }
  
  // 检查是否启用
  if (!getFeatureValue('prompt_cache_enabled', false)) {
    return false
  }
  
  return true
}

🔍 缓存条件

  • 用户资格:特定订阅级别才能使用
  • 功能开关:Feature Gate 控制
  • Global Scope:跨会话复用缓存
  • TTL:1 小时后过期

缓存键计算

function computeCacheKey(request: ApiRequest): string {
  // 只缓存 System Prompt 部分
  const systemPrompt = request.messages[0]
  
  // 计算 hash
  const data = JSON.stringify({
    model: request.model,
    systemPrompt: systemPrompt,
    tools: request.tools.map(t => t.name)
  })
  
  return sha256(data)
}

// 示例:
// 相同的 system prompt + 工具列表 → 相同的缓存键
// 不同 user prompt → 不同的缓存键

💡 缓存设计

  • 只缓存 System Prompt:这是不变的部分
  • 不包括 User Prompt:每个请求都不同
  • 包括工具列表:工具变化影响缓存
  • SHA256 Hash:快速且冲突概率低

🎯 缓存效果

场景 无缓存 有缓存
首次请求 10,000 tokens 10,000 tokens
相似请求(1h内) 10,000 tokens 1,000 tokens(90% off)
延迟 ~2s ~0.5s

Tool Search 动态加载

Tool Search 减少初始工具数量,只在需要时加载工具详情。

Tool Search 机制

interface ToolSearchRequest {
  query: string
  limit: number
}

interface ToolSearchResponse {
  tools: ToolSummary[]  // 只包含摘要信息
}

async function searchTools(query: string): Promise {
  // 1. 调用 Tool Search API
  const response = await apiClient.post('/tools/search', {
    query,
    limit: 5
  })
  
  // 2. 获取工具摘要
  const summaries = response.data.tools
  
  // 3. 按需加载完整工具定义
  const tools = await Promise.all(
    summaries.map(async summary => {
      return loadToolDefinition(summary.name)
    })
  )
  
  return tools
}

🔍 工作流程

  1. 搜索:根据查询搜索相关工具
  2. 摘要:返回工具摘要(不含完整定义)
  3. 按需加载:只加载选中的工具的完整定义
  4. 减少 Token:大幅减少 Prompt 大小

对比:传统 vs Tool Search

// 传统方式:发送所有工具
const allTools = [
  { name: 'read', inputSchema: {...} },
  { name: 'write', inputSchema: {...} },
  { name: 'edit', inputSchema: {...} },
  // ... 50+ 更多工具
]
// Prompt 大小:~50,000 tokens

// Tool Search:按需加载
const toolSummaries = await searchTools('文件操作')
// 返回:['read', 'write', 'edit']
// Prompt 大小:~3,000 tokens

💡 优势

  • 减少 Token:从 50k 降到 3k(94% off)
  • 提高准确度:只展示相关工具
  • 降低延迟:更小的 Prompt = 更快的响应
  • 降低成本:更少的 Token 计费

知识点检验

测试你对 API 调用系统的理解程度

1. API 调用系统支持的最大重试次数是?

2. Prompt 缓存的 TTL 是多少?

3. Tool Search 可以减少多少 Prompt 大小?

4. 智能重试使用什么退避算法?

18

认证系统

多认证源管理、SWR 缓存机制、并发控制与锁机制、订阅信息管理、3P 服务集成

认证系统架构

文件: src/utils/auth.ts (2,002 行)

核心职责:

  • 管理所有认证相关的功能
  • API Key、OAuth Token、AWS/GCP 凭证管理
  • 订阅信息管理
  • SWR 缓存机制
  • 并发控制与锁机制
  • 3P 服务集成(AWS Bedrock、GCP Vertex、Foundry)

认证源层次结构

认证源优先级
7个层次
环境变量
ANTHROPIC_API_KEY
配置文件
File Descriptor
Helper 脚本
apiKeyHelper
安全存储
Keychain
1P 认证
Claude.ai OAuth
3P 认证
AWS/GCP/Foundry
高优先级 →

🎯 认证源优先级

  1. Bare Mode:--bare 标志
  2. 环境变量:ANTHROPIC_API_KEY
  3. File Descriptor:CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR
  4. apiKeyHelper:自定义脚本
  5. 配置文件或 Keychain:持久化存储

API Key 管理

API Key 是最基础的认证方式,支持多种来源。

API Key 获取优先级

export function getAnthropicApiKeyWithSource(opts): {
  key: null | string
  source: ApiKeySource
} {
  // 1. Bare Mode(最高优先级)
  if (opts.bareMode) {
    return { key: null, source: 'bare' }
  }
  
  // 2. 环境变量
  const envKey = process.env.ANTHROPIC_API_KEY
  if (envKey) {
    return { key: envKey, source: 'ANTHROPIC_API_KEY' }
  }
  
  // 3. File Descriptor
  const fdKey = process.env.CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR
  if (fdKey) {
    return { key: fdKey, source: 'file_descriptor' }
  }
  
  // 4. apiKeyHelper
  if (!opts.skipRetrievingKeyFromApiKeyHelper) {
    const helperKey = await getApiKeyFromApiKeyHelper()
    if (helperKey) {
      return { key: helperKey, source: 'apiKeyHelper' }
    }
  }
  
  // 5. 配置文件或 Keychain
  const storedKey = await getStoredApiKey()
  return { key: storedKey, source: 'stored' }
}

🔍 优先级设计

  • Bare Mode:确保隔离环境,不使用任何认证
  • 环境变量:最灵活,适合 CI/CD
  • File Descriptor:安全传递敏感信息
  • apiKeyHelper:动态获取,支持复杂逻辑
  • 持久化存储:最后的备选方案

apiKeyHelper 的 SWR 缓存

let _apiKeyHelperCache: { value: string; timestamp: number } | null = null
let _apiKeyHelperInflight: {
  promise: Promise
  startedAt: number | null
} | null = null

export async function getApiKeyFromApiKeyHelper(
  isNonInteractiveSession: boolean,
): Promise {
  const ttl = calculateApiKeyHelperTTL()  // 5 分钟
  
  // 缓存有效
  if (_apiKeyHelperCache) {
    if (Date.now() - _apiKeyHelperCache.timestamp < ttl) {
      return _apiKeyHelperCache.value
    }
    
    // 缓存过期:后台刷新,先返回旧值(SWR)
    if (!_apiKeyHelperInflight) {
      _apiKeyHelperInflight = {
        promise: _runAndCache(...),
        startedAt: null,
      }
    }
    return _apiKeyHelperCache.value
  }
  
  // 缓存未命中:去重并发调用
  if (_apiKeyHelperInflight) {
    return _apiKeyHelperInflight.promise
  }
  
  // 执行脚本
  _apiKeyHelperInflight = {
    promise: _runAndCache(...),
    startedAt: Date.now(),
  }
  return _apiKeyHelperInflight.promise
}

💡 SWR 策略

  • Stale-While-Revalidate:缓存过期时先返回旧值
  • 后台刷新:异步更新缓存,不阻塞请求
  • 去重:并发调用共享同一个 Promise
  • Epoch 机制:配置变更时增加 epoch,旧请求不更新缓存

OAuth Token 管理

OAuth Token 需要定期刷新,使用文件锁确保多进程安全。

Token 刷新机制

export function checkAndRefreshOAuthTokenIfNeeded(
  retryCount = 0,
  force = false,
): Promise {
  // 去重并发调用
  if (retryCount === 0 && !force) {
    if (pendingRefreshCheck) {
      return pendingRefreshCheck
    }
    
    const promise = checkAndRefreshOAuthTokenIfNeededImpl(retryCount, force)
    pendingRefreshCheck = promise.finally(() => {
      pendingRefreshCheck = null
    })
    return pendingRefreshCheck
  }
  
  return checkAndRefreshOAuthTokenIfNeededImpl(retryCount, force)
}

async function checkAndRefreshOAuthTokenIfNeededImpl(
  retryCount: number,
  force: boolean,
): Promise {
  const MAX_RETRIES = 5
  
  // 1. 清理磁盘变更缓存
  await invalidateOAuthCacheIfDiskChanged()
  
  // 2. 检查是否过期
  const tokens = getClaudeAIOAuthTokens()
  if (!force && !isOAuthTokenExpired(tokens.expiresAt)) {
    return false  // 未过期
  }
  
  // 3. 重新读取(可能已被其他进程刷新)
  getClaudeAIOAuthTokens.cache?.clear?.()
  clearKeychainCache()
  const freshTokens = await getClaudeAIOAuthTokensAsync()
  if (!isOAuthTokenExpired(freshTokens.expiresAt)) {
    return false  // 已被其他进程刷新
  }
  
  // 4. 获取锁并刷新
  const claudeDir = getClaudeConfigHomeDir()
  await mkdir(claudeDir, { recursive: true })
  
  let release
  try {
    release = await lockfile.lock(claudeDir)
  } catch (err) {
    if ((err as { code?: string }).code === 'ELOCKED') {
      // 其他进程持有锁,重试
      if (retryCount < MAX_RETRIES) {
        await sleep(1000 + Math.random() * 1000)
        return checkAndRefreshOAuthTokenIfNeededImpl(retryCount + 1, force)
      }
      return false
    }
    return false
  }
  
  try {
    // 5. 再次检查(竞争条件)
    const lockedTokens = await getClaudeAIOAuthTokensAsync()
    if (!isOAuthTokenExpired(lockedTokens.expiresAt)) {
      return false
    }
    
    // 6. 执行刷新
    const refreshedTokens = await refreshOAuthToken(
      lockedTokens.refreshToken,
      { scopes: lockedTokens.scopes }
    )
    
    saveOAuthTokensIfNeeded(refreshedTokens)
    
    // 7. 清除缓存
    getClaudeAIOAuthTokens.cache?.clear?.()
    clearKeychainCache()
    
    return true
  } finally {
    await release()
  }
}

🔍 刷新流程

  1. 去重:防止并发刷新
  2. 检查过期:判断是否需要刷新
  3. 重新读取:可能已被其他进程刷新
  4. 获取锁:确保单进程执行
  5. 竞争检查:获取锁后再次检查
  6. 执行刷新:调用 refresh API
  7. 清除缓存:确保下次读取最新值

🎯 锁机制的重要性

// 场景:两个进程同时检测到 Token 过期

// 进程 A:检查 Token → 过期
// 进程 B:检查 Token → 过期

// 没有 lock:
// 进程 A:调用 refresh API
// 进程 B:调用 refresh API(重复!)
// 结果:浪费 API 调用,可能导致限流

// 有 lock:
// 进程 A:获取锁 → 刷新 Token → 释放锁
// 进程 B:尝试获取锁 → 失败 → 重试 → 检查 Token → 已刷新
// 结果:只调用一次 API,高效且安全

AWS/GCP 认证

支持 AWS Bedrock 和 GCP Vertex AI 作为 3P 提供商。

AWS 认证刷新

async function runAwsAuthRefresh(): Promise {
  const awsAuthRefresh = getConfiguredAwsAuthRefresh()
  if (!awsAuthRefresh) return false
  
  // 安全检查
  if (isAwsAuthRefreshFromProjectSettings()) {
    const hasTrust = checkHasTrustDialogAccepted()
    if (!hasTrust && !getIsNonInteractiveSession()) {
      logAntError('awsAuthRefresh invoked before trust check', error)
      logEvent('tengu_awsAuthRefresh_missing_trust', {})
      return false
    }
  }
  
  // 先检查 STS 身份
  try {
    await checkStsCallerIdentity()
    return false  // STS 有效,无需刷新
  } catch {
    // STS 无效,执行刷新
    return refreshAwsAuth(awsAuthRefresh)
  }
}

export function refreshAwsAuth(awsAuthRefresh: string): Promise {
  const authStatusManager = AwsAuthStatusManager.getInstance()
  authStatusManager.startAuthentication()
  
  return new Promise(resolve => {
    const refreshProc = exec(awsAuthRefresh, {
      timeout: AWS_AUTH_REFRESH_TIMEOUT_MS,  // 3 分钟
    })
    
    // 实时流式输出
    refreshProc.stdout!.on('data', data => {
      const output = data.toString().trim()
      if (output) {
        authStatusManager.addOutput(output)
        logForDebugging(output)
      }
    })
    
    refreshProc.on('close', (code, signal) => {
      if (code === 0) {
        authStatusManager.endAuthentication(true)
        resolve(true)
      } else {
        const timedOut = signal === 'SIGTERM'
        const message = timedOut
          ? 'AWS auth refresh timed out after 3 minutes'
          : 'Error running awsAuthRefresh'
        console.error(chalk.red(message))
        authStatusManager.endAuthentication(false)
        resolve(false)
      }
    })
  })
}

🔍 AWS 认证流程

  1. 安全检查:项目配置需要信任确认
  2. 智能检查:先验证 STS 身份,避免不必要的刷新
  3. 超时保护:3 分钟超时
  4. 实时反馈:流式输出,用户可见进度
  5. 缓存管理:TTL 缓存,自动刷新

GCP 认证检查

const GCP_CREDENTIALS_CHECK_TIMEOUT_MS = 5_000  // 5 秒

export async function checkGcpCredentialsValid(): Promise {
  try {
    // 动态导入,避免不必要的加载
    const { GoogleAuth } = await import('google-auth-library')
    const auth = new GoogleAuth({
      scopes: ['https://www.googleapis.com/auth/cloud-platform'],
    })
    
    // 超时保护
    const probe = (async () => {
      const client = await auth.getClient()
      await client.getAccessToken()
    })()
    
    const timeout = sleep(GCP_CREDENTIALS_CHECK_TIMEOUT_MS).then(() => {
      throw new GcpCredentialsTimeoutError('GCP credentials check timed out')
    })
    
    await Promise.race([probe, timeout])
    return true
  } catch {
    return false
  }
}

💡 GCP 认证设计

  • 超时保护:5 秒超时,避免 GCE 元数据服务器延迟
  • 动态导入:按需加载 google-auth-library
  • 智能检查:先验证凭证,避免不必要的刷新
  • 安全检查:项目配置需要信任确认

⚡ 认证性能优化

综合刷新流程

export const refreshAndGetAwsCredentials = memoizeWithTTLAsync(
  async (): Promise<{
    accessKeyId: string
    secretAccessKey: string
    sessionToken: string
  } | null> => {
    // 1. 运行认证刷新
    const refreshed = await runAwsAuthRefresh()
    
    // 2. 获取凭证
    const credentials = await getAwsCredsFromCredentialExport()
    
    // 3. 清除 AWS INI 缓存
    if (refreshed || credentials) {
      await clearAwsIniCache()
    }
    
    return credentials
  },
  DEFAULT_AWS_STS_TTL,  // 1 小时
)

优势:TTL 缓存,自动刷新,减少 API 调用

订阅信息管理

订阅信息决定了用户可以使用哪些功能和模型。

订阅类型

export type SubscriptionType =
  | 'max'        // 最高级别
  | 'enterprise' // 企业版
  | 'team'       // 团队版
  | 'pro'        // 专业版
  | null         // API 用户或未填充

export function getSubscriptionType(): SubscriptionType | null {
  // Mock 订阅(ANT 测试)
  if (shouldUseMockSubscription()) {
    return getMockSubscriptionType()
  }
  
  if (!isAnthropicAuthEnabled()) return null
  
  const oauthTokens = getClaudeAIOAuthTokens()
  if (!oauthTokens) return null
  
  return oauthTokens.subscriptionType ?? null
}

export function hasOpusAccess(): boolean {
  const subscriptionType = getSubscriptionType()
  
  return (
    subscriptionType === 'max' ||
    subscriptionType === 'enterprise' ||
    subscriptionType === 'team' ||
    subscriptionType === 'pro' ||
    subscriptionType === null  // API 用户或未填充的订阅者
  )
}

💡 订阅层级

  • max:最高级别,所有功能
  • enterprise:企业级功能
  • team:团队协作功能
  • pro:专业版功能
  • null:API 用户或未填充

订阅相关工具函数

// 判断订阅类型
export function isMaxSubscriber(): boolean {
  return getSubscriptionType() === 'max'
}

export function isTeamSubscriber(): boolean {
  return getSubscriptionType() === 'team'
}

export function isEnterpriseSubscriber(): boolean {
  return getSubscriptionType() === 'enterprise'
}

export function isProSubscriber(): boolean {
  return getSubscriptionType() === 'pro'
}

// Rate Limit Tier
export function getRateLimitTier(): string {
  const oauthTokens = getClaudeAIOAuthTokens()
  return oauthTokens?.rateLimitTier ?? 'default'
}

// 订阅名称(用于显示)
export function getSubscriptionName(): string {
  const subscriptionType = getSubscriptionType()
  
  switch (subscriptionType) {
    case 'enterprise':
      return 'Claude Enterprise'
    case 'team':
      return 'Claude Team'
    case 'max':
      return 'Claude Max'
    case 'pro':
      return 'Claude Pro'
    default:
      return 'Claude API'
  }
}

🔍 使用场景

  • 功能开关:根据订阅类型启用/禁用功能
  • Rate Limit:应用不同的速率限制
  • UI 显示:显示订阅信息
  • 模型访问:控制哪些模型可用

🎯 订阅与功能对应

订阅级别 Opus 访问 Prompt Cache 其他特性
Max 所有功能
Enterprise 企业级
Team 团队协作
Pro 基础功能
API 按使用计费

安全机制

认证系统实现了多层安全机制,保护用户凭证。

信任检查

// apiKeyHelper、awsAuthRefresh、gcpAuthRefresh、
// awsCredentialExport、otelHeadersHelper
// 都需要信任确认

if (isFromProjectSettings()) {
  const hasTrust = checkHasTrustDialogAccepted()
  if (!hasTrust && !isNonInteractiveSession) {
    logAntError('...', error)
    logEvent('tengu_..._missing_trust', {})
    return null
  }
}

🔍 信任检查目的

  • 防止恶意代码:项目配置可能包含恶意脚本
  • 用户确认:执行前需要用户明确同意
  • 安全追踪:记录所有安全事件
  • 非交互模式:CI/CD 环境跳过检查

超时保护

// apiKeyHelper: 10 分钟
const API_KEY_HELPER_TIMEOUT = 10 * 60 * 1000

// awsAuthRefresh: 3 分钟
const AWS_AUTH_REFRESH_TIMEOUT_MS = 3 * 60 * 1000

// awsCredentialExport: 30 秒
const AWS_CREDENTIAL_EXPORT_TIMEOUT = 30 * 1000

// otelHeadersHelper: 30 秒
const OTEL_HEADERS_HELPER_TIMEOUT = 30 * 1000

// GCP credentials check: 5 秒
const GCP_CREDENTIALS_CHECK_TIMEOUT_MS = 5_000

💡 超时策略

  • apiKeyHelper:10 分钟,允许复杂脚本
  • AWS 刷新:3 分钟,避免长时间阻塞
  • AWS 导出:30 秒,快速获取凭证
  • GCP 检查:5 秒,快速验证

🔒 安全最佳实践

错误处理

// 详细的错误信息
const why = result.timedOut ? 'timed out' : `exited ${result.exitCode}`
const stderr = result.stderr?.trim()
throw new Error(stderr ? `${why}: ${stderr}` : why)

目的:帮助用户快速定位问题

日志记录

// 安全事件
logEvent('tengu_apiKeyHelper_missing_trust11', {})
logEvent('tengu_awsAuthRefresh_missing_trust', {})
logEvent('tengu_oauth_token_refresh_starting', {})

// 调试日志
logForDebugging('Fetching AWS caller identity...')
logForDebugging('GCP credentials are still valid')

目的:安全审计和问题排查

知识点检验

测试你对认证系统的理解程度

1. API Key 的最高优先级来源是?

2. apiKeyHelper 的 SWR 缓存 TTL 是多少?

3. OAuth Token 刷新最多重试几次?

4. 以下哪个订阅级别无法访问 Opus 模型?

19

Hooks 系统

24种Hook事件、4种Hook类型、超时控制、错误处理

Hooks 系统架构

文件: src/utils/hooks.ts (5,022 行)

核心职责:

  • 24 种 Hook 事件类型
  • 4 种 Hook 类型(Command、Prompt、Agent、HTTP)
  • Hook 配置管理
  • 同步/异步执行
  • 超时控制:防止 Hook 阻塞主流程
  • 错误处理:Hook 失败不影响主流程

Hook 事件类型(24种)

工具生命周期
PreToolUse, PostToolUse等6种
会话生命周期
SessionStart, SessionEnd等
用户交互
UserPromptSubmit等
Agent & MCP
AgentStart, McpConnect等

🎯 Hook 执行方式

  • Command Hooks:执行shell命令
  • Prompt Hooks:调用AI模型
  • Agent Hooks:启动子Agent
  • HTTP Hooks:HTTP请求回调

知识点检验

1. Hooks 系统支持多少种 Hook 事件类型?

20

插件/Marketplace 系统

6种插件组件类型、5种插件来源、版本化缓存、Marketplace集成

插件系统架构

文件: pluginLoader.ts (3,302 行) + marketplaceManager.ts (2,643 行)

核心职责:

  • 6 种插件组件类型
  • 5 种插件来源(marketplace、git、npm、local、session)
  • 版本化缓存:避免重复加载
  • Marketplace 集成:自动发现和安装

插件组件类型(6种)

Commands
自定义斜杠命令
Agents
AI代理定义
Hooks
生命周期钩子
Skills
技能定义
MCP
MCP服务器配置
Output Styles
自定义输出样式

🎯 插件来源(5种)

  1. Marketplace:从官方或第三方市场安装
  2. Git:从Git仓库克隆
  3. NPM:从NPM包安装
  4. Local:本地文件系统
  5. Session:会话专用(--plugin-dir)

知识点检验

1. 插件系统支持多少种组件类型?

21

Bridge/Remote 架构

3种Spawn模式、32个并发会话支持、Git Worktree隔离、SSH隧道

Bridge 架构

文件: bridgeMain.ts (2,999 行) + replBridge.ts (2,406 行)

核心职责:

  • 3 种 Spawn 模式(ssh、local-docker、local-process)
  • 32 个并发会话支持
  • Git Worktree 隔离
  • SSH 隧道支持
  • WebSocket 传输

3种 Spawn 模式

🎯 核心特性

  • 多会话管理:最多32个活跃会话
  • 隔离机制:Git worktree确保环境独立
  • 自动重连:指数退避重试
  • 心跳机制:30秒心跳防止超时

知识点检验

1. Bridge 最多支持多少个并发会话?

22

Compact 架构

对话压缩策略、PTL重试机制、Token预算管理

Compact 系统

文件: compact.ts (1,705 行) + sessionMemoryCompact.ts (630 行)

核心职责:

  • 对话压缩策略
  • PTL(Prompt Too Long)重试机制
  • Token 预算管理
  • 1M 上下文支持

🎯 压缩触发条件

  • 轮次触发:128k 轮次
  • Token 触发:200k token
  • PTL 重试:压缩后重试失败的请求

知识点检验

1. Compact 压缩的Token触发阈值是?

23

Cron 系统

5字段Cron表达式、分布式锁、Jitter优化

Cron 调度系统

文件: cron.ts (308 行) + cronScheduler.ts (565 行)

核心职责:

  • 5 字段 Cron 表达式(分 时 日 月 周)
  • 分布式锁:防止多实例冲突
  • Jitter 优化:避免惊群效应

🎯 Cron 表达式格式

* * * * *
│ │ │ │ │
│ │ │ │ └─ 星期几 (0-6, 0=周日)
│ │ │ └─── 月份 (1-12)
│ │ └───── 日期 (1-31)
│ └─────── 小时 (0-23)
└───────── 分钟 (0-59)

知识点检验

1. Cron 表达式包含几个字段?

24

Skills & Discovery 系统

3种Tool Search模式、延迟工具发现、文件监控

Tool Search & Discovery

文件: toolSearch.ts (756 行) + skillChangeDetector.ts (311 行)

核心职责:

  • 3 种 Tool Search 模式(tst、tst-auto、standard)
  • 延迟工具发现:减少初始 Prompt 大小
  • 文件监控:自动重载 Skills

🎯 Tool Search 模式

  • tst:Tool Search Talk,按需加载
  • tst-auto:自动模式,智能选择
  • standard:标准模式,加载所有工具

知识点检验

1. Tool Search 有几种模式?

25

Insights 系统

三阶段数据处理、AI驱动的Facet提取、6个并行Insights生成

Insights 数据分析

文件: insights.ts (3,200 行)

核心职责:

  • 三阶段数据处理(Lite Scan → Load Meta → Facet Extraction)
  • AI 驱动的 Facet 提取
  • 6 个并行 Insights 生成
  • Multi-Clauding 检测
  • Homespace 数据收集

三阶段架构

🎯 6个并行 Insights

  1. 项目区域:识别主要工作领域
  2. 交互风格:用户使用模式
  3. 成功案例:典型成功操作
  4. 摩擦分析:常见障碍
  5. 建议:优化建议
  6. 总结:整体概览

知识点检验

1. Insights 系统生成几个并行 Insights?

26

Bash 安全检查系统

23个安全检查点、Early-Allow机制、Heredoc安全验证

Bash Security 层级

文件: bashSecurity.ts (2,592 行)

核心职责:

  • 23 个安全检查点
  • Early-Allow 机制:可证明安全的命令跳过验证
  • Heredoc 安全验证
  • Git Commit 特殊处理
  • Zsh 危险命令黑名单

分层验证

🎯 关键安全检查

  • INCOMPLETE_COMMANDS:检测不完整命令
  • SHELL_METACHARACTERS:特殊字符检查
  • 边界验证:防止前缀匹配攻击
  • Zsh 黑名单:危险命令(zmodload、emulate等)

知识点检验

1. Bash Security 有多少个安全检查点?

27

系统 Prompt 管理

有效System Prompt构建、Override机制、Coordinator模式支持

System Prompt 架构

文件: systemPrompt.ts (124 行) + systemPromptType.ts (15 行)

核心职责:

  • 有效 System Prompt 构建
  • Override 机制
  • Coordinator 模式支持
  • Proactive 模式集成

🎯 优先级顺序

  1. Override:最高优先级
  2. Coordinator:Coordinator 模式专用
  3. Agent:Agent System Prompt
  4. Custom:用户自定义
  5. Default:默认 Prompt

知识点检验

1. System Prompt 优先级最高的是?