MiniCode 是一个轻量级终端 AI 编程助手,类 Claude Code 工作流,181 ⭐。整个核心只有 5000 行 TypeScript,依赖极简(只有 diff 和 zod 两个运行时依赖),非常适合学习 AI Agent 的架构设计。这篇文章从源码角度拆解它的实现,作为面试准备。
整体架构
1 | 用户输入 |
这是标准的 ReAct(Reasoning + Acting) 模式:模型思考 → 调用工具 → 观察结果 → 继续思考,循环直到任务完成。
核心模块拆解
1. Agent Loop(src/agent-loop.ts)
这是整个项目最核心的文件,实现了 LLM 的工具调用循环:
1 | // 伪代码,展示核心逻辑 |
关键细节:
- 重试逻辑:模型返回空响应时自动重试
- 权限检查:每次工具调用前先过权限管理器
- 错误处理:工具执行失败时把错误信息反馈给模型,让它自行纠正
2. 工具系统(src/tool.ts + src/tools/)
所有工具(内置 + MCP 外部工具)遵循同一个接口:
1 | interface Tool { |
内置工具清单:
| 工具 | 功能 |
|---|---|
read_file |
读取文件内容 |
write_file |
写入文件(需权限审批) |
edit_file |
局部编辑(生成 diff 预览) |
list_files |
列出目录文件 |
grep_files |
正则搜索文件内容 |
run_command |
执行 Shell 命令(需权限审批) |
web_fetch |
抓取网页内容 |
web_search |
网络搜索 |
ask_user |
向用户提问 |
load_skill |
加载本地 Skill 文件 |
Zod 的作用:模型返回的工具参数是 JSON,Zod 在运行时校验参数类型,防止模型幻觉导致的非法参数进入执行层。
3. 权限系统(src/permissions.ts)
这是 AI Agent 安全性的核心设计。所有”危险操作”(写文件、执行命令)都需要用户审批:
1 | 模型想执行 run_command("rm -rf /tmp/xxx") |
审批模式:
- Allow this turn — 本次会话允许
- Allow always — 加入白名单,后续不再询问
- Reject with guidance — 拒绝并告诉模型为什么,让它换个方案
文件修改会先生成 unified diff 预览,用户看到具体改了什么再决定是否允许。
4. MCP 集成(src/mcp.ts)
MCP(Model Context Protocol)是 Anthropic 定义的工具服务器协议,允许外部进程提供工具给模型使用。
MiniCode 的 MCP 实现亮点是自动协商帧协议:
1 | 启动 MCP 服务器进程 |
这样可以兼容不严格遵循 MCP 规范的服务器,实用性更强。
外部 MCP 工具被包装成和内置工具相同的接口,Agent Loop 不需要区分工具来源。
5. 终端 UI(src/tty-app.ts)
这是代码量最大的文件(39KB),实现了一个全屏 TUI(Terminal User Interface):
1 | ┌─────────────────────────────────────────┐ |
技术细节:
- 直接操作 TTY 转义序列(不依赖 ncurses 或 blessed)
- 输入历史持久化(
src/history.ts) - Slash 命令:
/help、/tools、/skills、/mcp、/model
6. Skills 系统(src/skills.ts)
Skills 是存储在本地的 Markdown 文件,模型可以通过 load_skill 工具加载:
1 | 发现路径(优先级从高到低): |
这个设计让用户可以把常用的工作流(比如”提交代码”、”写测试”)封装成 Skill,模型按需加载,不需要每次都在 System Prompt 里塞满指令。
依赖极简的设计哲学
整个项目运行时只依赖两个包:
| 包 | 用途 | 为什么不自己实现 |
|---|---|---|
diff |
生成 unified diff | diff 算法(Myers diff)实现复杂,有成熟库直接用 |
zod |
运行时 Schema 校验 | 工具参数校验是安全关键路径,用成熟库更可靠 |
其他所有功能(HTTP 请求、文件操作、终端控制)全部用 Node.js 内置模块实现。这让整个项目安装极快,没有依赖地狱。
面试常见问题
Q:什么是 ReAct 模式?
ReAct = Reasoning + Acting。模型不是一次性给出答案,而是交替进行”思考”和”行动”:
- 思考:分析当前状态,决定下一步做什么
- 行动:调用工具获取信息或执行操作
- 观察:把工具结果加入上下文
- 重复,直到任务完成
相比单次推理,ReAct 能处理需要多步骤、需要外部信息的复杂任务。
Q:为什么 AI Agent 需要权限系统?
LLM 会产生幻觉,可能调用错误的工具参数(比如删错文件)。权限系统在执行层做最后一道防线:
- 危险操作(写文件、执行命令)必须人工确认
- 展示 diff 让用户看到具体改动
- 拒绝时把原因反馈给模型,让它自我纠正
这是”Human in the loop”设计原则的体现。
Q:MCP 协议是什么?
Model Context Protocol,Anthropic 提出的开放标准,定义了 AI 模型和外部工具服务器之间的通信协议。类似 LSP(Language Server Protocol)之于编辑器,MCP 让工具服务器和 AI 客户端解耦,任何支持 MCP 的工具都能被任何支持 MCP 的模型使用。
Q:Zod 在这里解决什么问题?
模型返回的工具调用参数是 JSON 字符串,TypeScript 的类型系统在运行时不起作用。Zod 在运行时校验参数结构,如果模型返回了错误类型的参数(比如把数字传成了字符串),Zod 会抛出详细的错误信息,可以反馈给模型让它重试,而不是让错误参数进入执行层导致不可预期的行为。
Q:为什么只用两个运行时依赖?
依赖越少,安全风险越低(供应链攻击),安装越快,代码越容易理解。Node.js 内置模块已经足够实现 HTTP、文件操作、进程管理。只在”自己实现成本高且风险大”的地方(diff 算法、Schema 校验)引入外部依赖,这是工程上的克制。
如何本地运行
1 | git clone https://github.com/LiuMengxuan04/MiniCode |
值得学习的设计
- 统一工具接口:内置工具和 MCP 外部工具用同一套接口,Agent Loop 不感知来源
- 权限前置:危险操作在执行前拦截,而不是执行后补救
- 错误反馈给模型:工具失败不直接报错,而是把错误信息加入消息历史让模型自行纠正
- Skills 本地优先:不依赖云端 marketplace,项目级和用户级 Skill 都支持
- 协议自动协商:MCP 帧格式自动降级,兼容性更好