前面九节我们一直在拆 OpenClaw。Gateway、CLI、Bridge、Workspace。模型、工具、浏览器。Skill、Prompt、Context。用户输入、队列、工具执行。Browser、Shell、Canvas。这些概念如果只停留在文字上还是有点散。所以第 10 节我们换个方式。不直接写 OpenClaw 的完整实现。而是手写一个“最小 Agent”用几十行代码把 Agent Loop 的核心跑通。目标不是造一个能上生产的框架。目标是让你明白Agent 本质上就是一个循环 看上下文 → 调模型 → 判断是否调用工具 → 执行工具 → 把结果放回上下文 → 继续只要这个循环懂了后面看 OpenClaw 的 Gateway、Workspace、Tool Policy、Skill、Browser、Shell都不会再觉得神秘。先说结论最小 Agent 只需要四个东西一个最小 Agent 需要1. Message保存用户、模型、工具结果 2. Model根据上下文生成下一步 3. Tool执行真实动作 4. Loop把模型和工具串起来用图表示就是用户输入 ↓ messages.push(user) ↓ 调用模型 ↓ 模型选择 ├─ 直接回复 → 结束 └─ 调用工具 → 执行工具 ↓ messages.push(tool_result) ↓ 再次调用模型这就是 Agent 的最小内核。OpenClaw 在这个内核外面加了很多工程能力Gateway Session Workspace Tool Policy Browser Shell Canvas Skills Prompt Assembly Streaming Persistence Approvals Sandbox但核心循环没变。不要一开始就写复杂框架很多人写 Agent上来就想做多 Agent长期记忆RAG浏览器自动化MCP插件市场可视化界面分布式队列SaaS 多租户结果还没理解最小循环代码已经变成一团。第一个 Agent 应该非常小。它只需要支持两个工具read_file write_file再加一个简单模型调用。甚至在教学阶段我们可以先用伪模型模拟工具调用。因为这一节的重点不是某个 SDK而是 Agent Loop 的结构。最小数据结构先定义消息const messages [ { role: system, content: You are a careful coding assistant. }, { role: user, content: Read README.md and summarize it. } ];一个 Agent 运行时messages 里会持续追加内容system message user message assistant tool request tool result assistant final answer它不是只保存聊天文本。它保存整个推理和执行过程。如果没有 messages模型每一轮都是失忆的。最小工具定义工具要有三个部分名称 参数 schema 执行函数伪代码如下const tools { read_file: { description: Read a text file from the workspace., schema: { path: string }, async run(args) { return await fs.readFile(args.path, utf8); } }, write_file: { description: Write text content to a workspace file., schema: { path: string, content: string }, async run(args) { await fs.writeFile(args.path, args.content, utf8); return Wrote ${args.path}; } } };注意这里有一个重要点模型看到的不是函数代码。模型看到的是工具名称、描述、参数 schema。真正执行的是运行时。这和 OpenClaw 的工具机制是一致的。最小模型返回格式为了理解 Agent Loop我们假设模型每次返回两种结果之一// 直接回复 { type: final, content: README.md explains how to start the project. }或者// 请求工具 { type: tool_call, name: read_file, args: { path: README.md } }真实模型 API 的格式可能更复杂。但抽象上就是这两类回复 调用工具理解这个抽象比背某个 SDK 字段更重要。最小 Agent Loop现在可以写核心循环async function runAgent(userInput) { const messages [ { role: system, content: You are a careful assistant. Use tools when needed. }, { role: user, content: userInput } ]; for (let step 0; step 8; step) { const next await callModel({ messages, tools }); if (next.type final) { messages.push({ role: assistant, content: next.content }); return next.content; } if (next.type tool_call) { const tool tools[next.name]; if (!tool) { messages.push({ role: tool, name: next.name, content: Error: unknown tool ${next.name} }); continue; } const result await tool.run(next.args); messages.push({ role: tool, name: next.name, content: result }); } } return Stopped: reached max steps.; }这就是最小 Agent。它没有 Gateway。没有 Workspace 管理。没有权限审批。没有浏览器。没有队列。没有持久化。但它已经有了 Agent 的核心模型可以基于上下文决定调用工具。 工具结果会回到上下文。 模型可以继续推理。给最小 Agent 加一点安全边界上面的代码很危险。因为read_file和write_file没有任何限制。一个稍微靠谱的最小 Agent至少要限制 workspacefunction resolveWorkspacePath(workspaceRoot, userPath) { const full path.resolve(workspaceRoot, userPath); if (!full.startsWith(workspaceRoot)) { throw new Error(Path escapes workspace); } return full; }然后工具执行时async run(args) { const safePath resolveWorkspacePath(WORKSPACE_ROOT, args.path); return await fs.readFile(safePath, utf8); }这个小例子解释了 OpenClaw 为什么需要 Workspace。Workspace 不是普通文件夹。它是 Agent 的工作边界。给最小 Agent 加工具策略再加一个最小 allowlistconst allowedTools new Set([read_file]); function assertToolAllowed(name) { if (!allowedTools.has(name)) { throw new Error(Tool not allowed: ${name}); } }执行工具前检查assertToolAllowed(next.name);这样就能做到模型可以请求 write_file 但运行时可以拒绝这非常关键。模型“想做什么”和系统“允许做什么”必须分开。OpenClaw 的 tool policy、审批和 sandbox本质上都是这个思想的工程化版本。给最小 Agent 加 Prompt现在加一点系统提示词const systemPrompt You are a careful file assistant. Read files before answering questions about them. Never write files unless the user explicitly asks. If a tool fails, explain the error briefly and stop. ;这个 Prompt 会改变模型倾向更愿意先读文件 更少主动写文件 工具失败时不继续乱试但注意Prompt 不是权限控制。如果write_file工具可见、可用而且没有策略阻止模型仍然可能调用它。所以最小 Agent 也要区分Prompt 行为引导 Policy 执行边界给最小 Agent 加 SkillSkill 可以先简单实现成一段按需加入的说明。比如用户任务里出现“总结文件”就追加一个 Skillconst summarizeFileSkill When summarizing a file: 1. Read the file first. 2. Identify the topic, structure, and key points. 3. Return summary, important details, and next actions. ;伪代码if (userInput.includes(summarize) || userInput.includes(总结)) { messages.push({ role: system, content: Relevant skill:\n${summarizeFileSkill} }); }这就是 Skill 的最小形态遇到某类任务时给模型一套可复用方法。真实 OpenClaw 会把 Skill 做得更完整扫描、优先级、eligible、路径、按需读取SKILL.md。但思想一样。最小 Agent 和 OpenClaw 的关系现在回头看 OpenClaw你会更容易理解它在做什么。我们的最小 Agentmessages callModel tools loop workspace check allowlist prompt skill snippetOpenClaw 的完整系统Gateway 接收输入 Session 保存历史 Context 组装 Prompt、工具、Skill、Workspace 文件 Provider 调用模型 Tool Runtime 执行 Browser、Shell、Canvas、MCP Tool Policy 控制能力边界 Workspace 保存文件和状态 Transcript 持久化全过程 Channel Adapter 返回消息它们不是两个世界。OpenClaw 就是在最小 Agent Loop 外面加上生产级运行时能力。常见误解误解一Agent 就是一个 Prompt不是。Prompt 只是输入的一部分。Agent 的核心是循环和工具执行。误解二Agent 会自动变聪明不会。如果没有可靠工具、上下文、策略和反馈模型只是生成文本。误解三工具函数写好了就安全不一定。工具还需要参数校验、路径限制、权限策略、超时、日志和审批。误解四最小 Agent 可以直接上生产不要。最小 Agent 只是教学模型。生产 Agent 需要网关、会话、权限、沙箱、观测、持久化、重试、审计。最后总结手写一个最小 Agent真正要理解的是 Agent Loop。它不是神秘的。它就是保存消息 调用模型 识别工具请求 执行工具 把结果放回消息 继续循环 直到输出最终答案OpenClaw 的强大不在于它改变了这个本质。而在于它把这个本质放进了可部署、可调试、可扩展、可控的运行时系统里。如果你能写出最小 Agent就能更清楚地看懂 OpenClaw 为什么需要 Gateway、Workspace、Tool Policy、Skill、Browser、Shell、Canvas。本节作业用你熟悉的语言写一个最小 Agent Loop只支持read_file一个工具。给它加一个最大循环步数比如 8 步防止无限执行。给工具加 workspace 路径限制禁止读取工作区外文件。给它加一个 allowlist让模型可以请求工具但运行时可以拒绝。写一个最小 Skill当用户要求总结文件时先读文件再输出摘要。下一节预告下一节开始进入第二阶段环境部署。我们会从 Docker 安装开始把前面讲过的概念放到真实运行环境里。到那一步你会看到理解 Agent Loop 只是第一步真正让 Agent 长期运行还需要部署、端口、配置、日志、权限和恢复能力。参考资料OpenClaw Agent loopOpenClaw Agent runtimeOpenClaw ContextOpenClaw System promptOpenClaw Tools overviewOpenClaw Exec tool原文链接手写一个最小 Agent | Harries Blog™