230行代码,零依赖,我用一个文件造了一个AI Agent
一个文件,230行代码,零npm依赖------这是我造一个AI Agent的全部成本。
它能自主思考、调用工具、读写文件、执行命令,完成你交给它的任务。它叫 Mini OpenClaw。
为什么要造这个轮子
市面上的Agent框架,LangChain、AutoGPT、CrewAI......动辄几十个依赖,上千个文件,光node_modules就能吃掉半个硬盘。
我只想搞清楚一件事:Agent的本质到底是什么?
翻遍所有框架的源码,剥掉封装、抽象、设计模式,剩下的核心逻辑只有一个循环:
markdown
用户提问 → LLM思考 → 需要工具吗?
├── 是 → 执行工具 → 结果喂回LLM → 继续思考...
└── 否 → 输出最终回答 ✅
这就是 ReAct(Reasoning + Acting)。
Agent不是魔法,是一个while循环。
既然核心这么简单,为什么不能用一个文件实现它?
于是我动手了。
架构:极简到不能再简
整个项目只有一个文件 mini-openclaw.mjs,分成4个部分:
objectivec
mini-openclaw.mjs(单文件,4个部分)
┌──────────────────────────────────────┐
│ 第一部分:工具定义 │ ← 4个内置工具
│ 第二部分:LLM调用 │ ← CodeBuddy CLI驱动
│ 第三部分:ReAct循环 │ ← 思考→行动→观察
│ 第四部分:交互式REPL │ ← 命令行界面
└──────────────────────────────────────┘
| 模块 | 职责 | 代码量 |
|---|---|---|
| 工具定义 | read_file · write_file · list_dir · run_command |
~40行 |
| LLM调用 | 通过CodeBuddy CLI非交互式调用大模型 | ~50行 |
| ReAct循环 | 检测<tool_call>标签 → 执行工具 → 结果追加 → 继续 |
~80行 |
| REPL | readline交互界面,支持多轮对话 | ~60行 |
没有package.json。没有node_modules。没有构建步骤。
node mini-openclaw.mjs,一行命令,直接跑。
最好的架构,是你能一眼看完的架构。
核心实现:4个关键设计决策
决策一:文本标签式工具调用
主流方案用OpenAI的function calling格式,需要特定的API结构。
Mini OpenClaw用了一种更简单的方式------文本标签:
bash
<tool_call>{"name":"read_file","args":{"path":"test.txt"}}</tool_call>
一个正则就能解析:
javascript
const m = text.match(/<tool_call>([\s\S]*?)<\/tool_call>/);
Before :依赖特定API格式,换模型就得改代码 After:纯文本协议,任何模型都能用
决策二:CLI而非HTTP
调用LLM不走HTTP API,直接spawn CodeBuddy CLI进程:
javascript
const proc = spawn("codebuddy", [
"-p", // 非交互式
"--output-format", "json", // JSON输出
"--max-turns", "1", // 单轮
"--tools", "", // 禁用内置工具
"--model", model,
prompt,
]);
Before :需要HTTP客户端、处理鉴权、管理连接 After:一个spawn搞定,零网络代码
决策三:纯文本对话历史
不用复杂的消息对象数组,直接用字符串拼接:
makefile
User: 读取test.txt
Assistant: [调用了 read_file]
[工具结果]: hello world
Assistant: 文件内容是 hello world
简单、直观、debug友好。
决策四:重复调用检测
模型有时会陷入死循环,反复调用同一个工具。Mini OpenClaw用一行代码解决:
javascript
const callKey = JSON.stringify(call);
if (callKey === lastCallKey) {
// 强制终止,让模型总结已有结果
}
Before :Agent卡死,token烧光 After:自动刹车,强制输出
好的防御机制,是用户永远感知不到的那种。
实战效果:3个场景
场景一:普通对话(0次工具调用)
shell
You> 你好,介绍一下你自己
⚙ 迭代 1/15
🤖 Assistant:
我是一个智能助手,拥有读写文件、列出目录、执行命令等工具,
可以帮助你完成编程、调试、项目维护等开发相关工作。
LLM判断不需要工具,1次迭代,直接回答。
场景二:读取文件(1次工具调用)
vbnet
You> 读取 .gitattributes 文件内容并解释它的作用
⚙ 迭代 1/15
🔧 调用工具: read_file({"path":".gitattributes"})
📋 结果: # Auto detect text files and perform LF normalization
* text=auto
⚙ 迭代 2/15
🤖 Assistant:
.gitattributes 文件用于配置 Git 对文件的处理方式:
- `* text=auto`:对所有文件启用文本自动检测和行尾LF标准化
LLM思考 → 调用工具 → 拿到结果 → 总结回答。2次迭代。
场景三:执行命令(1次工具调用)
css
You> 用命令查看当前的 node 版本和操作系统信息
⚙ 迭代 1/15
🔧 调用工具: run_command({"command":"node --version && uname -a"})
📋 结果: v22.22.0
Darwin Enos-MacBook-Pro.local 24.6.0 ... arm64
⚙ 迭代 2/15
🤖 Assistant:
当前环境信息:
- Node.js 版本:v22.22.0
- 操作系统:macOS Darwin (Apple Silicon arm64)
Before :手动敲命令,自己看输出 After:Agent自主决策执行什么命令,自动总结结果
Agent的价值不在于它能做什么,在于你不用告诉它怎么做。
ReAct循环:一张图看懂全部
scss
┌─────────┐ prompt ┌──────────────┐ spawn ┌───────────────┐
│ 用户 │ ────────────→ │ ReAct循环 │ ──────────→ │ CodeBuddy CLI │
│ 输入 │ │ (react函数) │ ←────────── │ (LLM回复) │
└─────────┘ └──────┬───────┘ JSON └───────────────┘
│
检测 <tool_call> 标签
│
┌───────────┴───────────┐
│ 有 │ 无
▼ ▼
┌──────────┐ ┌──────────┐
│ 执行工具 │ │ 输出回答 │
│ 追加结果 │ │ 循环结束 │
│ 继续循环 │ └──────────┘
└──────────┘
整个流程的代码实现,核心就是react函数里的一个for循环:
javascript
for (let i = 1; i <= maxIter; i++) {
const prompt = buildSystemPrompt() + "\n\n" + history.join("\n\n");
const reply = await callLLM(prompt, model, apiKey);
const call = extractToolCall(reply);
if (!call) {
// 没有工具调用 → 最终回答
return reply;
}
// 有工具调用 → 执行 → 结果追加到历史 → 继续循环
const result = executeTool(call);
history.push(`[工具结果]: ${result}`);
}
这就是一个AI Agent的全部核心逻辑。
没有中间件。没有插件系统。没有抽象层。
一个循环,一个正则,一个spawn。
快速上手:3步跑起来
bash
# 1. 克隆
git clone https://github.com/wscats/enoclaw.git
cd enoclaw
# 2. 设置Key
export CODEBUDDY_API_KEY=ck_你的key
# 3. 运行
node mini-openclaw.mjs
想换模型?一个环境变量:
bash
MODEL=hunyuan-2.0-thinking node mini-openclaw.mjs
支持的模型:deepseek-v3-2-volc · hunyuan-2.0-thinking · glm-5.0 · glm-4.7 · minimax-m2.5 · kimi-k2.5
前置条件只有两个 :Node.js ≥ 18,CodeBuddy CLI(npm i -g @tencent-ai/codebuddy-code)。
好工具的标准:README都不用看完就能跑起来。
写在最后
230行代码能造一个Agent,这件事本身说明了什么?
AI Agent的门槛,从来不在代码量。
LangChain有10万行代码,Mini OpenClaw有230行。它们的核心循环,一模一样。
区别在于:一个让你用框架,一个让你理解框架。
所有Agent框架,都是这230行的变体。
📎 GitHub:github.com/Wscats/mini...