引言书接上回。今天我们不扯虚的直接扒开 mini-cc 的源码聊聊它到底是怎么设计的。对了所有代码都在这https://github.com/you-want/mini-cc/tree/main/typescript建议你一边读这篇文章一边打开src目录对照着看。我接下来讲的每一个模块源码里都有对应的实现。我坚持的几个设计原则一开始我也想过要不要搞个特别“企业级”的架构后来发现没必要。mini-cc 最后只守住了四条简单的原则。1. 分层架构各层管好各层的事就是最经典的三层表现层Presentation LayerUI 组件、命令行交互、打字机效果。应用层Application LayerAgent 循环、业务逻辑。基础设施层Infrastructure Layer工具注册、记忆管理、Provider 对接。你在src/下看到的目录跟这三层几乎一一对应没什么神奇的。2. 依赖倒置不把具体实现写死在核心代码里这是我能同时支持 OpenAI、Anthropic、Qwen 等多个模型的关键。核心引擎只依赖一个LLMProvider接口interfaceLLMProvider{chat(messages:Message[]):PromiseChatResponse;supportsToolCalling():boolean;}任何模型只要实现这个接口就能被 mini-cc 驱动。具体实现放在src/services/providers/下核心代码完全不需要改动。3. 单一职责一个文件只干一件事QueryEngine.ts– 只负责 Agent 循环Tool.ts– 只定义工具和执行逻辑MemoryManager.ts– 只管理记忆这样改 bug 的时候不用翻遍整个项目省心。4. 开闭原则对扩展开放对修改关闭想加一个新工具不用碰QueryEngine只需要实现Tool接口然后注册进去。想加一个新 Provider同理实现LLMProvider就行。核心代码像一块稳定的底座上面可以随意插拔。核心模块详解附源码路径1. Agent 循环引擎这是 mini-cc 的大脑。它的工作流程就是一个思考 → 执行 → 再思考的循环。// src/application/QueryEngine.tsexportclassQueryEngine{asyncrun(prompt:string):Promisestring{// 1. 构建上下文从记忆系统里捞历史constcontextawaitthis.memory.buildContext(prompt);// 2. 调用 LLM看它想干什么constresponseawaitthis.provider.chat(context);// 3. 解析 AI 的回复里有没有工具调用consttoolCallsthis.parseToolCalls(response);// 4. 如果有工具要执行执行完把结果喂给 AI递归继续if(toolCalls.length0){constresultsawaitthis.executeTools(toolCalls);returnthis.run(results);// 递归调用形成循环}// 5. 没有工具调用了直接把回答返回给用户returnresponse.content;}}设计要点递归循环、状态管理靠MemoryManager、Provider 和 Tool 都是接口注入。源码位置src/application/QueryEngine.ts– 整个 Agent 的入口你可以从这里开始读。2. 工具系统工具是 AI 能干实事的根本。没有工具它就是个只会耍嘴皮的聊天机器人。// src/infrastructure/tools/Tool.tsexportinterfaceTool{name:string;description:string;inputSchema:JSONSchema;execute(args:Recordstring,any):PromiseToolResult;}// 工具注册中心exportclassToolRegistry{privatetools:Mapstring,ToolnewMap();register(tool:Tool){this.tools.set(tool.name,tool);}get(name:string):Tool|undefined{returnthis.tools.get(name);}}内置的几个常用工具源码都在src/infrastructure/tools/下工具名称功能安全性源码文件FileReadTool读取文件✅ 安全fileRead.tsFileWriteTool写入文件⚠️ 需审批fileWrite.tsBashTool执行命令⚠️ 需审批bash.tsGrepTool代码搜索✅ 安全search.tsGitStatusToolGit 状态✅ 安全gitStatus.ts想加新工具在src/infrastructure/tools/下新建一个文件实现Tool接口就行其他地方不用改。3. Provider 抽象层这个模块负责屏蔽不同大模型 API 的差异。统一接口长这样// src/services/providers/index.tsexportinterfaceLLMProvider{name:string;supportsToolCalling:boolean;chat(messages:Message[],tools?:Tool[],options?:ChatOptions):PromiseChatResponse;streamChat(messages:Message[],tools?:Tool[],onChunk?:(chunk:string)void):PromiseChatResponse;}目前已经支持的 Provider每个都在src/services/providers/下有独立文件夹Provider工具调用流式响应Anthropic✅✅OpenAI✅✅Qwen (DashScope)✅✅DeepSeek✅✅Ollama⚠️ 有限支持✅源码指路接口定义在src/services/providers/index.ts每个具体 Provider 的实现都在子目录里。新增 Provider 只需在这个目录下加一套实现核心引擎零改动。4. 记忆系统对话一长Token 成本就爆炸。我设计了一个两层的记忆系统// src/memdir/MemoryManager.tsexportclassMemoryManager{privateshortTermMemory:Message[][];// 短期记忆最多50条privatelongTermMemory:MemoryItem[][];// 长期记忆存摘要asyncaddMessage(message:Message){this.shortTermMemory.push(message);// 超过50条 → 让 AI 压缩成摘要存入长期记忆if(this.shortTermMemory.length50){constsummaryawaitthis.summarize();this.longTermMemory.push({timestamp:Date.now(),summary});this.shortTermMemorythis.shortTermMemory.slice(-20);}}asyncbuildContext(prompt:string):PromiseMessage[]{constcontext:Message[][];// 把最近的长期记忆摘要加进去if(this.longTermMemory.length0){context.push({role:system,content:长期记忆摘要:\n${this.longTermMemory.slice(-5).map(mm.summary).join(\n)}});}context.push(...this.shortTermMemory);context.push({role:user,content:prompt});returncontext;}}记忆策略短期记忆存最近对话超过阈值就压缩成摘要扔进长期记忆。这样既保留关键信息又控制住 Token 消耗。源码位置核心逻辑分散在src/memdir/compact.ts和src/context/下。README 里提到的“防爆舱”那个truncateHeadForPTLRetry函数你翻到那儿就知道了。整体数据流用户输入 │ ▼ ┌─────────────────┐ │ UI 层接收输入 │ └────────┬────────┘ │ ▼ ┌─────────────────┐ ┌─────────────────┐ │ Agent 循环引擎 │←──→ │ 记忆系统 │ │ QueryEngine │ │ MemoryManager │ └────────┬────────┘ └─────────────────┘ │ ┌────┴────┐ ▼ ▼ ┌────────┐ ┌────────┐ │Provider│ │ Tool │ │ (LLM) │ │Registry│ └────────┘ └────────┘几个我觉得比较得意的设计亮点1. 插件化设计 – 通过 MCP 协议// src/utils/plugins/mcpPluginIntegration.tsexportclassMCPPluginIntegration{asyncloadPlugins():Promisevoid{constpluginsawaitthis.scanPlugins();// 扫描 ~/.mini-cc/plugins/for(constpluginofplugins){constserverawaitMCP.connect(plugin.path);consttoolsawaitserver.listTools();for(consttooloftools){this.toolRegistry.register(newMCPTool(tool));}}}}你配好settings.json启动时就会自动扫描并连接 MCP Server动态注册工具。源码src/utils/plugins/mcpPluginIntegration.ts2. 权限控制系统 – 安全不能马虎我分了三个安全等级STRICT_LOCAL– 严格本地模式所有工具调用都要审批USER_APPROVAL– 危险工具需要用户显式确认FULL_ACCESS– 完全信任不推荐// src/infrastructure/permissions/index.tsexportclassPermissionManager{checkPermission(toolName:string):PermissionResult{if(this.isDangerousTool(toolName)){return{allowed:false,requiresApproval:true};}return{allowed:true};}}使用运行mini-cc后输入/permissions就能看到当前策略。源码src/infrastructure/permissions/3. 可观测性 – 埋点但不侵犯隐私// src/services/analytics/firstPartyEventLogger.tsexportfunctionlogEvent(eventName:string,payload?:Recordstring,any){if(getPrivacyLevel()PrivacyLevel.STRICT_LOCAL)return;console.debug([Telemetry]${eventName},payload);}默认只上报使用次数、模型类型等聚合数据不传代码内容。用户可以在设置里完全关闭。总结mini-cc 的架构其实没有什么黑科技就是老老实实把几个经典设计原则落到代码里模块化– 目录结构即架构看src就知道每块干什么。可扩展– 接口插件新增 Provider、工具、技能都不动核心。安全优先– 权限控制从设计第一天就嵌进去而不是事后打补丁。可观测– 埋点、日志、超时兜底生产环境能跑稳。对了再贴一次源码地址https://github.com/you-want/mini-cc/tree/main/typescript欢迎 clone 下来跑一跑顺手点个 ⭐️ 就更好了。还有后续这是个连续剧还未完结请关注不迷路