230行代码,零依赖,我用一个文件造了一个AI Agent

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...

相关推荐
code_pgf2 小时前
Jetson Orin NX 16GB 的推荐传感器组合 + 资源预算 + 软件栈安装顺序(humble)
人工智能·数码相机
sp422 小时前
通过 RootEncoder 进行安卓直播 RTSP 推流
前端
源码学社2 小时前
[特殊字符] 字节跳动开源 DeerFlow:一个“深度研究型 AI Agent 框架”详解
人工智能
_院长大人_2 小时前
构建一个 Vue 基于el-input的磨损区间选择器组件 —— WearRangeSelector
前端·javascript·vue.js
遗憾随她而去.2 小时前
前端 Vue 虚拟列表(Virtual List),从原理到实战
前端·javascript·vue.js
tangdou3690986552 小时前
图文并茂手把手教你Claude Code 多智能体 Agent Teams,一人变团队
前端·后端·ai编程
AINative软件工程2 小时前
Structured Outputs 实战:让大模型稳定输出 JSON 的三种方案对比
人工智能
Entropy-Go2 小时前
一图了解AI热门词汇 - OpenClaw/Prompt/Agent/Skill/MCP/LLM/GPU
人工智能·agent·skill·mcp·openclaw
惠惠软件2 小时前
AI 龙虾 | 对学习工作的影响和未来前瞻
人工智能·学习
是糖糖啊2 小时前
Agent 不好用?先别怪模型,试试 Harness Engineering
人工智能·设计模式