第十五章:性能考量
⚡ 本章分析 MiniClaw 的性能优化策略和考量。
15.1 并行 I/O 优化
15.1.1 Promise.all 使用
┌─────────────────────────────────────────────────────────────────────┐
│ 并行 I/O 优化 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 问题:顺序读取多个文件效率低下 │
│ │
│ ❌ 顺序读取(慢): │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ const agents = await fs.readFile('AGENTS.md', 'utf-8'); │ │
│ │ const soul = await fs.readFile('SOUL.md', 'utf-8'); │ │
│ │ const identity = await fs.readFile('IDENTITY.md', 'utf-8');│ │
│ │ const user = await fs.readFile('USER.md', 'utf-8'); │ │
│ │ // 总耗时 = t1 + t2 + t3 + t4 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ✅ 并行读取(快): │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ const [agents, soul, identity, user] = await Promise.all([ │ │
│ │ fs.readFile('AGENTS.md', 'utf-8'), │ │
│ │ fs.readFile('SOUL.md', 'utf-8'), │ │
│ │ fs.readFile('IDENTITY.md', 'utf-8'), │ │
│ │ fs.readFile('USER.md', 'utf-8'), │ │
│ │ ]); │ │
│ │ // 总耗时 = max(t1, t2, t3, t4) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 性能对比: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 场景 顺序读取 并行读取 提升 │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ 4 个文件 ~40ms ~15ms 62% │ │
│ │ 8 个文件 ~80ms ~18ms 77% │ │
│ │ 12 个文件 ~120ms ~20ms 83% │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
15.1.2 独立扫描并行化
┌─────────────────────────────────────────────────────────────────────┐
│ 独立扫描并行化 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ boot() 方法中的并行扫描: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ async boot(options: BootOptions): Promise<string> { │ │
│ │ // 并行执行独立的 I/O 操作 │ │
│ │ const [ │ │
│ │ agents, │ │
│ │ soul, │ │
│ │ identity, │ │
│ │ user, │ │
│ │ tools, │ │
│ │ memory, │ │
│ │ heartbeat, │ │
│ │ entities, │ │
│ │ skills, │ │
│ │ dailyLog, │ │
│ │ gitStatus, │ │
│ │ ] = await Promise.all([ │ │
│ │ this.readFile('AGENTS.md'), │ │
│ │ this.readFile('SOUL.md'), │ │
│ │ this.readFile('IDENTITY.md'), │ │
│ │ this.readFile('USER.md'), │ │
│ │ this.readFile('TOOLS.md'), │ │
│ │ this.readFile('MEMORY.md'), │ │
│ │ this.readFile('HEARTBEAT.md'), │ │
│ │ this.entityStore.load(), │ │
│ │ this.skillCache.getAll(), │ │
│ │ this.loadDailyLog(), │ │
│ │ this.getGitStatus(), │ │
│ │ ]); │ │
│ │ │ │
│ │ // 后续处理... │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 并行化原则: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ✅ 无依赖关系的操作可以并行 │ │
│ │ ✅ 文件读取之间通常无依赖 │ │
│ │ ✅ Git 状态检查独立于文件读取 │ │
│ │ ❌ 有依赖关系的操作必须顺序执行 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
15.2 缓存策略
15.2.1 技能缓存 TTL
┌─────────────────────────────────────────────────────────────────────┐
│ 技能缓存 TTL │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ SkillCache 实现带 TTL 的缓存机制: │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ class SkillCache { │ │
│ │ private cache: Map<string, SkillCacheEntry>; │ │
│ │ private lastScanTime: number = 0; │ │
│ │ private readonly TTL_MS = 5000; // 5 秒有效期 │ │
│ │ │ │
│ │ async getAll(): Promise<Map<string, SkillCacheEntry>> { │ │
│ │ const now = Date.now(); │ │
│ │ │ │
│ │ // 缓存有效,直接返回 │ │
│ │ if (now - this.lastScanTime < this.TTL_MS) { │ │
│ │ return this.cache; │ │
│ │ } │ │
│ │ │ │
│ │ // 缓存过期,重新扫描 │ │
│ │ await this.refresh(); │ │
│ │ return this.cache; │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ TTL 选择考量: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ TTL 太短(如 1s) │ │
│ │ ❌ 频繁扫描文件系统 │ │
│ │ ❌ 性能下降 │ │
│ │ │ │
│ │ TTL 太长(如 60s) │ │
│ │ ❌ 新技能不能及时被发现 │ │
│ │ ❌ 数据可能过时 │ │
│ │ │ │
│ │ TTL 适中(5s) │ │
│ │ ✅ 平衡性能和实时性 │ │
│ │ ✅ 适合大多数使用场景 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
15.2.2 缓存失效机制
┌─────────────────────────────────────────────────────────────────────┐
│ 缓存失效机制 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 手动失效场景: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ class SkillCache { │ │
│ │ // 手动使缓存失效 │ │
│ │ invalidate(): void { │ │
│ │ this.lastScanTime = 0; │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ │ // 使用场景 │ │
│ │ // 1. 新增技能后 │ │
│ │ kernel.skillCache.invalidate(); │ │
│ │ │ │
│ │ // 2. 删除技能后 │ │
│ │ kernel.skillCache.invalidate(); │ │
│ │ │ │
│ │ // 3. 技能内容更新后 │ │
│ │ kernel.skillCache.invalidate(); │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 自动失效时机: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • TTL 过期自动刷新 │ │
│ │ • 服务重启时缓存清空 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
15.3 Token 预算管理
15.3.1 预算分配策略
┌─────────────────────────────────────────────────────────────────────┐
│ Token 预算分配策略 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 默认预算:8000 Token(可配置) │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 段落优先级分配: │ │
│ │ │ │
│ │ 优先级 内容类型 预算占比 │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ 1 (最高) AGENTS.md 必须包含 │ │
│ │ 2 SOUL.md 必须包含 │ │
│ │ 3 IDENTITY.md 必须包含 │ │
│ │ 4 USER.md 高优先 │ │
│ │ 5 TOOLS.md 高优先 │ │
│ │ 6 MEMORY.md 中优先(仅主会话) │ │
│ │ 7 工作空间感知 中优先 │ │
│ │ 8 每日日志 低优先 │ │
│ │ 9 实体概览 低优先 │ │
│ │ 10 (最低) 技能索引 可截断 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 预算编译流程: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ function compileWithBudget( │ │
│ │ sections: ContextSection[], │ │
│ │ budget: number │ │
│ │ ): string { │ │
│ │ // 1. 按优先级排序 │ │
│ │ sections.sort((a, b) => a.priority - b.priority); │ │
│ │ │ │
│ │ // 2. 依次添加段落 │ │
│ │ let result = ''; │ │
│ │ let usedTokens = 0; │ │
│ │ │ │
│ │ for (const section of sections) { │ │
│ │ const tokens = estimateTokens(section.content); │ │
│ │ │ │
│ │ if (usedTokens + tokens <= budget) { │ │
│ │ result += section.content; │ │
│ │ usedTokens += tokens; │ │
│ │ } else { │ │
│ │ // 预算不足,截断或跳过 │ │
│ │ break; │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ │ return result; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
15.3.2 截断策略
┌─────────────────────────────────────────────────────────────────────┐
│ 截断策略 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 不同内容的截断策略: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 内容类型 截断策略 说明 │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ AGENTS.md 不截断 宪法级别 │ │
│ │ SOUL.md 不截断 核心人格 │ │
│ │ IDENTITY.md 不截断 身份必需 │ │
│ │ USER.md 保留最新偏好 按时间截断 │ │
│ │ MEMORY.md 保留最重要记忆 按重要性截断 │ │
│ │ 每日日志 保留最近条目 按时间截断 │ │
│ │ 技能索引 完全截断 预算不足时跳过 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 日志截断示例: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ // 保留最近 N 条日志条目 │ │
│ │ function truncateLog(log: string, maxEntries: number): string {│
│ │ const entries = log.split('\n---\n'); │ │
│ │ const truncated = entries.slice(-maxEntries); │ │
│ │ return truncated.join('\n---\n'); │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
15.3.3 Token 估算
┌─────────────────────────────────────────────────────────────────────┐
│ Token 估算 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 简化估算方法: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ // 经验值:约 4 个字符 = 1 Token │ │
│ │ const CHARS_PER_TOKEN = 4; │ │
│ │ │ │
│ │ function estimateTokens(content: string): number { │ │
│ │ return Math.ceil(content.length / CHARS_PER_TOKEN); │ │
│ │ } │ │
│ │ │ │
│ │ // 示例 │ │
│ │ const text = "Hello, World!"; // 13 字符 │ │
│ │ const tokens = estimateTokens(text); // ~4 tokens │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 更精确的估算(可选): │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ // 使用 tiktoken 库(更精确但更慢) │ │
│ │ import { encode } from 'tiktoken'; │ │
│ │ │ │
│ │ function countTokensExact(content: string): number { │ │
│ │ return encode(content).length; │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ MiniClaw 选择简化估算的原因: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ✅ 无额外依赖 │ │
│ │ ✅ 计算速度快 │ │
│ │ ✅ 估算足够准确 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
15.4 启动性能
15.4.1 启动流程优化
┌─────────────────────────────────────────────────────────────────────┐
│ 启动流程优化 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 启动时间分解: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 阶段 耗时 优化措施 │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ 文件扫描 ~10ms 并行读取 │ │
│ │ 实体加载 ~5ms 懒加载 │ │
│ │ 技能发现 ~15ms 缓存 TTL │ │
│ │ 上下文编译 ~5ms 预算控制 │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ 总计 ~35ms │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 懒加载策略: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ class EntityStore { │ │
│ │ private loaded: boolean = false; │ │
│ │ │ │
│ │ private async ensureLoaded(): Promise<void> { │ │
│ │ if (!this.loaded) { │ │
│ │ await this.load(); │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ │ async query(name: string): Promise<Entity | null> { │ │
│ │ await this.ensureLoaded(); // 首次使用时加载 │ │
│ │ return this.entities.find(e => e.name === name); │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
15.4.2 会话延续检测
┌─────────────────────────────────────────────────────────────────────┐
│ 会话延续检测 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 检测会话是否延续,避免重复加载: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ interface SessionContinuation { │ │
│ │ isContinuation: boolean; │ │
│ │ lastActivity: Date; │ │
│ │ minutesSinceLastActivity: number; │ │
│ │ } │ │
│ │ │ │
│ │ function detectSessionContinuation(state: MiniClawState): SessionContinuation {│
│ │ const lastActivity = new Date(state.lastActivityTime); │ │
│ │ const now = new Date(); │ │
│ │ const minutesSince = (now.getTime() - lastActivity.getTime()) / (1000 * 60);│
│ │ │ │
│ │ return { │ │
│ │ isContinuation: minutesSince < 30, // 30 分钟内 │ │
│ │ lastActivity, │ │
│ │ minutesSinceLastActivity: minutesSince, │ │
│ │ }; │ │
│ │ } │ │
│ │ │ │
│ │ // 会话延续时,使用精简上下文 │ │
│ │ if (continuation.isContinuation) { │ │
│ │ // 跳过完整加载,仅加载增量 │ │
│ │ return generateMinimalContext(); │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 性能收益: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 完整启动:~35ms │ │
│ │ 延续会话:~10ms │ │
│ │ 提升:~70% │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
本章小结
┌─────────────────────────────────────────────────────────────────────┐
│ 第十五章 核心要点 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ⚡ 并行 I/O │
│ • Promise.all 并行读取文件 │
│ • 独立操作并行化 │
│ • 性能提升 60%+ │
│ │
│ 💾 缓存策略 │
│ • SkillCache 带 TTL 缓存(5 秒) │
│ • 手动失效机制 │
│ │
│ 📊 Token 预算 │
│ • 默认 8000 Token(可配置) │
│ • 优先级分配策略 │
│ • 智能截断策略 │
│ │
│ 🚀 启动性能 │
│ • 总启动时间 ~35ms │
│ • 懒加载策略 │
│ • 会话延续检测 │
│ │
└─────────────────────────────────────────────────────────────────────┘
下一章:扩展性设计 →