第十四章:代码质量分析
📊 本章分析 MiniClaw 的代码质量、设计模式和最佳实践。
14.1 代码风格
14.1.1 TypeScript 严格模式
┌─────────────────────────────────────────────────────────────────────┐
│ TypeScript 严格模式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ tsconfig.json 中的严格配置: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ { │ │
│ │ "compilerOptions": { │ │
│ │ "strict": true, │ │
│ │ "noImplicitAny": true, │ │
│ │ "strictNullChecks": true, │ │
│ │ "strictFunctionTypes": true, │ │
│ │ "noUnusedLocals": true, │ │
│ │ "noUnusedParameters": true, │ │
│ │ "noImplicitReturns": true, │ │
│ │ "noFallthroughCasesInSwitch": true │ │
│ │ } │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 严格模式的好处: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ✅ 编译时捕获更多错误 │ │
│ │ ✅ 更好的类型推断 │ │
│ │ ✅ 更安全的代码 │ │
│ │ ✅ 更好的 IDE 支持 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.1.2 ES Module 规范
┌─────────────────────────────────────────────────────────────────────┐
│ ES Module 规范 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ package.json 配置: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ { "type": "module" } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 导入导出风格: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ // 命名导出 │ │
│ │ export interface Entity { ... } │ │
│ │ export class SkillCache { ... } │ │
│ │ export function getTimeMode(): TimeMode { ... } │ │
│ │ │ │
│ │ // 命名导入 │ │
│ │ import { Server } from '@modelcontextprotocol/sdk/server';│ │
│ │ import { promises as fs } from 'fs'; │ │
│ │ │ │
│ │ // 默认导出 │ │
│ │ export default kernel; │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 文件扩展名要求: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • TypeScript 源文件:.ts │ │
│ │ • 编译输出文件:.js │ │
│ │ • 导入时需要包含 .js 扩展名(编译后) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.1.3 异步编程模式
┌─────────────────────────────────────────────────────────────────────┐
│ 异步编程模式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ async/await 风格: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ // 推荐:async/await │ │
│ │ async function boot(options: BootOptions): Promise<string> {│ │
│ │ const state = await this.loadState(); │ │
│ │ const files = await this.scanFiles(); │ │
│ │ return this.compileContext(files); │ │
│ │ } │ │
│ │ │ │
│ │ // 并行执行 │ │
│ │ const [agents, soul, identity] = await Promise.all([ │ │
│ │ this.readFile('AGENTS.md'), │ │
│ │ this.readFile('SOUL.md'), │ │
│ │ this.readFile('IDENTITY.md'), │ │
│ │ ]); │ │
│ │ │ │
│ │ // 错误处理 │ │
│ │ try { │ │
│ │ const result = await this.execCommand(cmd); │ │
│ │ return result; │ │
│ │ } catch (error) { │ │
│ │ this.bootErrors.push(error.message); │ │
│ │ return null; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 避免: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ❌ 嵌套回调 │ │
│ │ ❌ 未处理的 Promise 拒绝 │ │
│ │ ❌ 混用回调与 Promise │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.2 设计模式
14.2.1 单例模式(Kernel)
┌─────────────────────────────────────────────────────────────────────┐
│ 单例模式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ContextKernel 作为核心内核,在应用生命周期中只存在一个实例 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ class ContextKernel { │ │
│ │ private static instance: ContextKernel; │ │
│ │ │ │
│ │ private constructor() { │ │
│ │ // 私有构造函数,防止外部实例化 │ │
│ │ this.skillCache = new SkillCache(); │ │
│ │ this.entityStore = new EntityStore(); │ │
│ │ } │ │
│ │ │ │
│ │ public static getInstance(): ContextKernel { │ │
│ │ if (!ContextKernel.instance) { │ │
│ │ ContextKernel.instance = new ContextKernel(); │ │
│ │ } │ │
│ │ return ContextKernel.instance; │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ │ // 使用 │ │
│ │ const kernel = ContextKernel.getInstance(); │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 好处: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ✅ 全局唯一的状态管理 │ │
│ │ ✅ 避免重复初始化 │ │
│ │ ✅ 共享缓存和实体存储 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.2.2 缓存模式(SkillCache)
┌─────────────────────────────────────────────────────────────────────┐
│ 缓存模式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ SkillCache 实现带 TTL 的缓存机制 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ class SkillCache { │ │
│ │ private cache: Map<string, SkillCacheEntry>; │ │
│ │ private lastScanTime: number = 0; │ │
│ │ private readonly TTL_MS: number = 5000; // 5 秒 │ │
│ │ │ │
│ │ async getAll(): Promise<Map<string, SkillCacheEntry>> { │ │
│ │ // 检查缓存是否有效 │ │
│ │ if (Date.now() - this.lastScanTime < this.TTL_MS) { │ │
│ │ return this.cache; // 返回缓存 │ │
│ │ } │ │
│ │ // 缓存过期,重新扫描 │ │
│ │ await this.refresh(); │ │
│ │ return this.cache; │ │
│ │ } │ │
│ │ │ │
│ │ invalidate(): void { │ │
│ │ this.lastScanTime = 0; // 使缓存失效 │ │
│ │ } │ │
│ │ │ │
│ │ private async refresh(): Promise<void> { │ │
│ │ // 扫描技能目录 │ │
│ │ this.cache = await this.scanSkillsDir(); │ │
│ │ this.lastScanTime = Date.now(); │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 缓存模式优势: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ✅ 减少文件系统 I/O │ │
│ │ ✅ 提高响应速度 │ │
│ │ ✅ TTL 自动过期 │ │
│ │ ✅ 可手动失效 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.2.3 策略模式(TimeMode)
┌─────────────────────────────────────────────────────────────────────┐
│ 策略模式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 时间模式根据当前时间选择不同的上下文策略 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ type TimeMode = 'morning' | 'work' | 'break' | 'evening' | 'night';│
│ │ │ │
│ │ interface TimeModeConfig { │ │
│ │ emoji: string; │ │
│ │ greeting: string; │ │
│ │ features: string[]; │ │
│ │ } │ │
│ │ │ │
│ │ const TIME_MODES: Record<TimeMode, TimeModeConfig> = { │ │
│ │ morning: { │ │
│ │ emoji: '☀️', │ │
│ │ greeting: 'Good morning!', │ │
│ │ features: ['briefing', 'planning'], │ │
│ │ }, │ │
│ │ work: { │ │
│ │ emoji: '💼', │ │
│ │ greeting: 'Let\'s work!', │ │
│ │ features: ['focus', 'minimal'], │ │
│ │ }, │ │
│ │ night: { │ │
│ │ emoji: '😴', │ │
│ │ greeting: 'Good night!', │ │
│ │ features: ['minimal'], │ │
│ │ }, │ │
│ │ }; │ │
│ │ │ │
│ │ function getTimeMode(): TimeMode { │ │
│ │ const hour = new Date().getHours(); │ │
│ │ if (hour >= 6 && hour < 9) return 'morning'; │ │
│ │ if (hour >= 9 && hour < 18) return 'work'; │ │
│ │ if (hour >= 22 || hour < 6) return 'night'; │ │
│ │ return 'break'; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 策略模式优势: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ✅ 易于扩展新的时间模式 │ │
│ │ ✅ 配置与逻辑分离 │ │
│ │ ✅ 运行时动态切换 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.2.4 工厂模式(Skill Discovery)
┌─────────────────────────────────────────────────────────────────────┐
│ 工厂模式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 技能发现机制动态创建技能对象 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ interface SkillCacheEntry { │ │
│ │ name: string; │ │
│ │ description: string; │ │
│ │ prompts?: SkillPromptDeclaration[]; │ │
│ │ tools?: SkillToolDeclaration[]; │ │
│ │ resources?: SkillResourceDeclaration[]; │ │
│ │ } │ │
│ │ │ │
│ │ async discoverSkillPrompts(): Promise<SkillPromptDeclaration[]> {│
│ │ const skills = await this.skillCache.getAll(); │ │
│ │ const prompts: SkillPromptDeclaration[] = []; │ │
│ │ │ │
│ │ for (const [name, skill] of skills) { │ │
│ │ if (skill.prompts) { │ │
│ │ for (const prompt of skill.prompts) { │ │
│ │ prompts.push({ │ │
│ │ name: `skill_${name}_${prompt.name}`, │ │
│ │ description: prompt.description, │ │
│ │ }); │ │
│ │ } │ │
│ │ } │ │
│ │ } │ │
│ │ return prompts; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 工厂模式优势: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ✅ 动态创建对象 │ │
│ │ ✅ 解耦创建逻辑 │ │
│ │ ✅ 支持插件化扩展 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.3 错误处理
14.3.1 try-catch 模式
┌─────────────────────────────────────────────────────────────────────┐
│ try-catch 模式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 文件读取错误处理: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ async function readFileSafely(path: string): Promise<string | null> {│
│ │ try { │ │
│ │ return await fs.readFile(path, 'utf-8'); │ │
│ │ } catch (error) { │ │
│ │ if ((error as NodeJS.ErrnoException).code === 'ENOENT') {│
│ │ // 文件不存在,返回 null │ │
│ │ return null; │ │
│ │ } │ │
│ │ // 其他错误,记录并返回 null │ │
│ │ console.error(`Error reading ${path}:`, error); │ │
│ │ return null; │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 启动错误收集: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ async boot(options: BootOptions): Promise<string> { │ │
│ │ this.bootErrors = []; // 重置错误列表 │ │
│ │ │ │
│ │ // 尝试加载各个文件 │ │
│ │ const agents = await this.readFileSafely('AGENTS.md'); │ │
│ │ const soul = await this.readFileSafely('SOUL.md'); │ │
│ │ │ │
│ │ // 如果有错误,在上下文中标记 │ │
│ │ if (this.bootErrors.length > 0) { │ │
│ │ context += `\n## ⚠️ 启动警告\n${this.bootErrors.join('\n')}`;│
│ │ } │ │
│ │ │ │
│ │ return context; │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.3.2 McpError 使用
┌─────────────────────────────────────────────────────────────────────┐
│ McpError 使用 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ MCP 协议错误处理: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ import { McpError } from '@modelcontextprotocol/sdk/types';│ │
│ │ │ │
│ │ server.setRequestHandler(CallToolRequestSchema, async (req) => {│
│ │ try { │ │
│ │ const result = await handleToolCall(req.params); │ │
│ │ return { content: [{ type: 'text', text: result }] }; │ │
│ │ } catch (error) { │ │
│ │ if (error instanceof McpError) { │ │
│ │ throw error; // 重新抛出 MCP 错误 │ │
│ │ } │ │
│ │ // 包装为 MCP 错误 │ │
│ │ throw new McpError( │ │
│ │ ErrorCode.InternalError, │ │
│ │ error.message │ │
│ │ ); │ │
│ │ } │ │
│ │ }); │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 常用错误码: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ErrorCode.InvalidRequest → 无效请求 │ │
│ │ ErrorCode.MethodNotFound → 方法不存在 │ │
│ │ ErrorCode.InvalidParams → 参数无效 │ │
│ │ ErrorCode.InternalError → 内部错误 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.3.3 错误日志
┌─────────────────────────────────────────────────────────────────────┐
│ 错误日志 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 日志记录策略: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ // 错误日志输出到 stderr(不影响 MCP 通信) │ │
│ │ function logError(context: string, error: unknown): void { │ │
│ │ const message = error instanceof Error │ │
│ │ ? error.message │ │
│ │ : String(error); │ │
│ │ │ │
│ │ console.error(`[MiniClaw ERROR] ${context}: ${message}`);│ │
│ │ } │ │
│ │ │ │
│ │ // 使用示例 │ │
│ │ try { │ │
│ │ await someOperation(); │ │
│ │ } catch (error) { │ │
│ │ logError('someOperation', error); │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 注意事项: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • MCP 使用 stdin/stdout 通信 │ │
│ │ • 日志必须输出到 stderr │ │
│ │ • 不能使用 console.log(会干扰 MCP 协议) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.4 代码度量
14.4.1 代码规模
┌─────────────────────────────────────────────────────────────────────┐
│ 代码规模 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 文件 行数 说明 │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ src/index.ts ~300 MCP 服务入口 │ │
│ │ src/kernel.ts ~2400 核心内核实现 │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ 总计 ~2700 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 与 OpenClaw 对比: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ OpenClaw: ~50,000+ 行 │ │
│ │ MiniClaw: ~2,700 行 │ │
│ │ 压缩比: ~5% │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
14.4.2 圈复杂度
┌─────────────────────────────────────────────────────────────────────┐
│ 圈复杂度 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 主要函数复杂度评估: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 函数 复杂度 评估 │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ boot() 中等 核心启动逻辑 │ │
│ │ getTimeMode() 低 简单条件判断 │ │
│ │ evaluateDistillation() 中等 多条件判断 │ │
│ │ execCommand() 低 安全检查 + 执行 │ │
│ │ compileContext() 中等 段落组装 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 复杂度控制策略: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ✅ 单一职责原则 │ │
│ │ ✅ 函数拆分 │ │
│ │ ✅ 提前返回 │ │
│ │ ✅ 避免深层嵌套 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
本章小结
┌─────────────────────────────────────────────────────────────────────┐
│ 第十四章 核心要点 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 📝 代码风格 │
│ • TypeScript 严格模式 │
│ • ES Module 规范 │
│ • async/await 异步模式 │
│ │
│ 🎨 设计模式 │
│ • 单例模式:ContextKernel │
│ • 缓存模式:SkillCache │
│ • 策略模式:TimeMode │
│ • 工厂模式:Skill Discovery │
│ │
│ ⚠️ 错误处理 │
│ • try-catch 模式 │
│ • McpError 协议错误 │
│ • stderr 日志输出 │
│ │
│ 📊 代码度量 │
│ • 总代码量:~2,700 行 │
│ • 压缩比:~5%(相对 OpenClaw) │
│ • 复杂度:中等可控 │
│ │
└─────────────────────────────────────────────────────────────────────┘
下一章:性能考量 →