一、项目概述
OpenClaw 作为一款具备工具调用能力的 AI Agent 智能体框架,其核心设计理念是 大模型做大脑,技能做手脚。通过将大语言模型与具体工具能力相结合,用户可以通过自然语言指令让 AI 自动完成文件操作、任务执行等多种复杂操作。
本文将完整拆解OpenClaw核心架构,并提供一套 可直接商用、开箱即用 的全栈实现方案,涵盖Vue3前端界面、Node.js后端、AI 智能体调度、多模型适配、自动技能注册等核心能力,帮助开发者快速落地AI智能体项目。
二、系统架构
OpenClaw 的核心逻辑是让 AI 智能体具备“判断是否调用工具、选择工具、执行工具、总结结果”的完整能力,整体架构采用分层设计,链路清晰,各层职责明确,代码可直接复用:
前端(Vue3 + 流式聊天 UI)
↓
API 层(/api/chat)
↓
Agent 代理层(上下文组装 + 循环调度)
↓
LLM 适配层(统一对接各类大模型)
↓
大模型返回【文本】或【工具调用】
↓
Skill 执行层(技能注册与执行)
↓
结果返回前端
总结 OpenClaw 核心架构:前端 → 代理层 → LLM 大模型 → 工具调用(Skill)→ 前端返回结果。
整个架构的核心优势在于:
- 分层解耦:前端、Agent、LLM、Skill各司其职,便于维护和扩展;
- 多模型兼容:统一LLM适配层支持通义千问、Ollama、GPT等主流大模型;
- 技能自动化:Skill系统支持自动注册、按需调用,可快速扩展业务能力;
- 流式交互:前端实现类ChatGPT的实时打字效果,提升用户体验。
架构设计要点
1. 前端层
采用 Vue3 构建流式聊天界面,支持实时打字效果,模拟 ChatGPT 的用户体验。前端不直接调用大模型,而是通过 API 与后端通信,实现前后端分离。
2. API 层
提供统一的 RESTful 接口,包括聊天接口 /api/chat、技能列表接口 /api/skills、日志接口 /api/logs 等。
3. Agent 代理层
这是系统的核心,负责:
- 组装系统提示词和对话历史:根据用户指令和上下文,动态生成大模型的输入。
- 判断大模型返回的是文本还是工具调用:根据返回内容判断是否需要调用具体的技能。
- 执行技能并将结果反馈给大模型:根据大模型的工具调用请求,执行对应的技能,并将结果返回给大模型。
- 支持多轮工具调用循环:当大模型需要连续调用多个工具时,能够循环执行,直到满足条件或达到最大循环次数。
4. LLM 适配层
实现统一的模型适配接口,支持快速切换不同大模型:
- 通义千问(阿里云)
- Ollama(本地模型)
- OpenAI GPT(需配置API密钥)
5. Skill 执行层
技能系统采用自动注册机制,skills 目录下的文件会自动被加载执行,便于扩展。
三、前端实现:Vue3仿ChatGPT炫酷聊天界面
前端不仅承担交互功能,还需展示AI可用技能列表、处理流式返回、实现加载状态,以下是完整可复用的代码实现:
Vue3 聊天界面
<!-- ChatAI.vue(核心前端组件) -->
<template>
<div class="chat-ai-wrapper">
<div class="chat-container">
<!-- 技能面板:展示AI可调用的技能 -->
<div class="skill-panel" v-if="skillList.length">
<div class="skill-title">🧩 AI 可用技能</div>
<div class="skill-list">
<div v-for="s in skillList" :key="s.id" class="skill-item">
{{ s.name }}
</div>
</div>
</div>
<!-- 聊天消息列表 -->
<div class="message-list" ref="msgListRef">
<div
v-for="(item, idx) in messages"
:key="idx"
class="message-item"
:class="item.role"
>
<div class="avatar">{{ item.role === "user" ? "我" : "AI" }}</div>
<div class="bubble">
<div v-if="item.loading" class="loading">思考中...</div>
<div v-else>{{ item.content }}</div>
</div>
</div>
</div>
<!-- 输入区域 -->
<div class="input-area">
<input v-model="inputText" @keyup.enter="send" placeholder="输入消息..." />
<button @click="send">发送</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from "vue";
// 前端配置
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || "http://localhost:3000/api";
// 响应式数据
const inputText = ref("");
const messages = reactive([]);
const msgListRef = ref(null);
const skillList = ref([]);
// 初始化:获取AI可用技能列表
onMounted(async () => {
const res = await fetch(`${API_BASE_URL}/skills`);
skillList.value = await res.json();
});
// 发送消息核心逻辑
async function send() {
const txt = inputText.value.trim();
if (!txt) return;
// 添加用户消息
messages.push({ role: "user", content: txt });
inputText.value = "";
await nextTick();
// 添加AI加载状态
messages.push({ role: "assistant", content: "", loading: true });
await nextTick();
// 调用后端流式接口
const response = await fetch(`${API_BASE_URL}/chat`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "sk_openclaw_123456",
},
body: JSON.stringify({ history: messages.filter(m => !m.loading) }),
});
// 流式读取返回结果
const reader = response.body.getReader();
const decoder = new TextDecoder();
let finalText = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
finalText += decoder.decode(value);
// 实时更新AI回复内容
messages[messages.length - 1].content = finalText;
await nextTick();
// 自动滚动到底部
msgListRef.value.scrollTop = msgListRef.value.scrollHeight;
}
// 关闭加载状态
messages[messages.length - 1].loading = false;
}
</script>
<style scoped>
.chat-ai-wrapper {
width: 100vw;
height: 100vh;
background: #f8fafc;
display: flex;
align-items: center;
justify-content: center;
}
.chat-container {
width: 800px;
height: 90vh;
background: white;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
display: flex;
flex-direction: column;
overflow: hidden;
}
.skill-panel {
padding: 12px 20px;
border-bottom: 1px solid #eee;
}
.skill-title {
font-size: 14px;
color: #666;
margin-bottom: 6px;
}
.skill-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.skill-item {
background: #e6f7ff;
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
}
.message-list {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.message-item {
display: flex;
margin-bottom: 16px;
align-items: flex-end;
}
.user {
flex-direction: row-reverse;
}
.avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: #007bff;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
margin: 0 10px;
}
.bubble {
max-width: 70%;
padding: 10px 14px;
border-radius: 18px;
background: #f5f5f5;
}
.user .bubble {
background: #007bff;
color: white;
}
.loading {
color: #666;
}
.input-area {
display: flex;
padding: 14px;
border-top: 1px solid #eee;
}
input {
flex: 1;
padding: 12px 16px;
border: 1px solid #ddd;
border-radius: 24px;
outline: none;
}
button {
margin-left: 10px;
padding: 12px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 24px;
cursor: pointer;
}
</style>
四、后端实现:Node.js搭建OpenClaw核心服务
后端分为接口层、Agent调度层、LLM适配层、Skill技能层,四层架构复刻OpenClaw核心逻辑。
1. 服务入口 server.js
负责提供HTTP接口,处理跨域、请求解析,对接Agent核心逻辑:
// 入口文件:server.js(接口层)
const express = require("express");
const cors = require("cors");
const fs = require("fs");
const path = require("path");
const { agentChat } = require("./agent");
const { skills } = require("./skillSystem");
const { checkAuth } = require("./auth");
const app = express();
// 跨域配置
app.use(cors());
// JSON请求解析
app.use(express.json());
// 权限校验中间件
app.use((req, res, next) => {
if (req.path === "/api/chat") {
if (!checkAuth(req)) {
return res.status(401).json({ error: "Unauthorized" });
}
}
next();
});
// 核心聊天接口(流式返回)
app.post("/api/chat", async (req, res) => {
const { history } = req.body;
const stream = agentChat(history);
// 设置响应头,支持流式输出
res.setHeader("Content-Type", "text/plain; charset=utf-8");
// 逐段返回流式数据
for await (const chunk of stream) {
res.write(chunk);
}
res.end();
});
// 获取技能列表接口
app.get("/api/skills", (req, res) => {
res.json(skills);
});
// 日志接口
app.get("/api/logs", (req, res) => {
const date = new Date().toISOString().slice(0, 10);
const logFile = path.join(__dirname, "logs", `${date}.log`);
if (fs.existsSync(logFile)) {
res.send(fs.readFileSync(logFile, "utf8"));
} else {
res.send("暂无日志");
}
});
// 管理后台
app.get("/admin", (req, res) => {
res.sendFile(path.join(__dirname, "admin.html"));
});
// 启动服务
app.listen(3000, () => {
console.log("✅ OpenClaw AI 服务已启动:http://localhost:3000");
});
2. Agent 智能体核心 agent.js
OpenClaw 的灵魂模块,实现“调用LLM→判断工具调用→执行技能→多轮循环→总结结果”的完整逻辑:
// 核心调度:agent.js(Agent智能体)
const { callLLM } = require("./llm");
const { skills, runSkill } = require("./skillSystem");
const { log } = require("./logger");
// 防止无限循环,设置最大轮次
const MAX_LOOP = 8;
/**
* Agent核心循环:处理聊天逻辑,支持多轮工具调用
* @param {Array} history - 聊天历史
* @returns {AsyncGenerator} 流式返回结果
*/
async function* agentChat(history) {
try {
// 系统提示词:定义AI行为规范,指定工具调用格式
const system = {
role: "system",
content: `你是AI助手,可以调用工具。
返回格式严格二选一:
1. 调用工具:{"tool":"技能ID","params":{"key":value}}
2. 直接回答:{"text":"回答内容"}`,
};
const messages = [system, ...history];
let loop = 0;
// 多轮循环:支持连续调用多个技能
while (loop < MAX_LOOP) {
loop++;
try {
// 1. 调用大模型获取响应
const aiResp = await callLLM(messages, skills);
// 2. 判断:纯文本回复 → 直接返回
if (aiResp.type === "text") {
yield aiResp.content;
return;
}
// 3. 判断:工具调用 → 执行对应技能
if (aiResp.type === "tool_call") {
const { name, params } = aiResp;
log("TOOL_CALL", `技能=${name}, 参数=${JSON.stringify(params)}`);
// 执行技能
const toolRes = await runSkill(name, params);
// 将工具结果返回给模型做最终总结
messages.push({ role: "assistant", content: "" });
messages.push({ role: "tool", content: JSON.stringify(toolRes) });
// 实时返回技能执行状态
yield `🔧 执行技能:${name}\n📝 ${toolRes.message || toolRes.error || "处理中..."}\n\n`;
}
} catch (error) {
log("ERROR", `Agent循环错误:${error.message}`);
yield `⚠️ 执行过程中遇到错误:${error.message}\n\n`;
}
}
// 循环结束,返回最终提示
yield "✅ 任务执行完成(已达最大轮次)";
} catch (error) {
log("ERROR", `Agent初始化错误:${error.message}`);
yield `❌ 系统错误:${error.message}`;
}
}
module.exports = { agentChat };
3. LLM 多模型统一适配层 llm.js
实现通义千问、Ollama、GPT的统一适配,支持Function Calling解析:
// 模型适配:llm.js(多模型统一调用)
const axios = require("axios");
// 切换模型:llama / tongyi / openai
const MODEL_TYPE = process.env.MODEL_TYPE || "ollama";
/**
* 统一调用大模型接口
* @param {Array} messages - 对话消息
* @param {Array} skills - 技能列表(可选)
* @returns {Object} 模型响应(text/tool_call类型)
*/
async function callLLM(messages, skills = []) {
try {
if (MODEL_TYPE === "ollama") {
return await callOllama(messages);
}
if (MODEL_TYPE === "tongyi") {
return await callTongyi(messages);
}
if (MODEL_TYPE === "openai") {
return await callOpenAI(messages);
}
} catch (e) {
return { type: "text", content: "模型异常:" + e.message };
}
return { type: "text", content: "你好!" };
}
// 调用Ollama(本地大模型)
async function callOllama(messages) {
const res = await axios.post("http://localhost:11434/api/chat", {
model: "llama3",
messages,
stream: false,
});
return parseAIResponse(res.data.message.content);
}
// 调用通义千问
async function callTongyi(messages) {
const res = await axios.post(
"https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation",
{
model: "qwen-turbo",
input: { messages },
},
{
headers: { Authorization: `Bearer ${process.env.TONGYI_API_KEY || "YOUR_API_KEY"}` }, // 替换为你的通义 API Key
}
);
return parseAIResponse(res.data.output.text);
}
// 调用OpenAI GPT
async function callOpenAI(messages) {
const res = await axios.post(
"https://api.openai.com/v1/chat/completions",
{
model: "gpt-3.5-turbo",
messages,
},
{
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY || "YOUR_API_KEY"}` }, // 替换为你的GPT API Key
}
);
return parseAIResponse(res.data.choices[0].message.content);
}
/**
* 统一解析AI响应,区分文本/工具调用
* @param {string} text - AI原始回复
* @returns {Object} 解析后的结果
*/
function parseAIResponse(text) {
try {
const json = JSON.parse(text.trim());
// 工具调用格式
if (json.tool) {
return {
type: "tool_call",
name: json.tool,
params: json.params || {},
};
}
// 纯文本格式
if (json.text) {
return { type: "text", content: json.text };
}
} catch (e) {}
// 解析失败则返回原始文本
return { type: "text", content: text };
}
module.exports = { callLLM };
4. Skill 自动注册系统 skillSystem.js
实现技能的自动扫描、注册和执行,支持快速扩展新技能:
// 技能系统:skillSystem.js(自动注册与执行)
const fs = require("fs");
const path = require("path");
// 技能文件夹路径
const skillDir = path.join(__dirname, "skills");
// 技能列表(给前端/LLM展示)
const skills = [];
// 技能映射(执行函数)
const skillMap = {};
// 确保目录存在
if (!fs.existsSync(skillDir)) fs.mkdirSync(skillDir);
// 自动扫描skills文件夹,注册/加载所有技能
fs.readdirSync(skillDir).forEach(file => {
if (!file.endsWith(".js")) return;
const mod = require(path.join(skillDir, file));
// 整理技能元信息
skills.push({
id: mod.id,
name: mod.name,
parameters: Array.isArray(mod.parameters) ? mod.parameters : [], // 确保为数组
});
// 映射技能ID到执行函数
skillMap[mod.id] = mod.execute;
});
/**
* 执行指定技能
* @param {string} skillId - 技能ID
* @param {Object} params - 技能参数
* @returns {Object} 技能执行结果
*/
async function runSkill(skillId, params) {
try {
const fn = skillMap[skillId];
if (!fn) return { success: false, error: `技能不存在:${skillId}` };
return await fn(params || {});
} catch (e) {
return { success: false, error: e.message };
}
}
module.exports = { skills, runSkill };
5. 日志系统 logger.js
// 日志系统:logger.js(按日期分文件记录)
const fs = require("fs");
const path = require("path");
const logDir = path.join(__dirname, "logs");
// 确保目录存在
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir);
/**
* 记录日志到文件(按日期分文件)
* @param {string} type - 日志类型(如:info, error)
* @param {string} content - 日志内容
*/
function log(type, content) {
const time = new Date().toISOString();
const line = `[${time}] [${type}] ${content}\n`;
const dateStr = new Date().toISOString().slice(0, 10);
const file = path.join(logDir, `${dateStr}.log`);
fs.appendFileSync(file, line, "utf8");
}
module.exports = { log };
6. 权限校验 auth.js
// 权限校验:auth.js(简单API Key校验)
const VALID_KEYS = process.env.API_KEYS ? process.env.API_KEYS.split(",") : ["sk_openclaw_123456"];
/**
* 校验请求是否包含有效API Key
* @param {Object} req - HTTP请求对象
* @returns {boolean} 是否校验通过
*/
function checkAuth(req) {
const key = req.headers["x-api-key"];
return key && VALID_KEYS.includes(key);
}
module.exports = { checkAuth };
五、技能实现:skills文件夹(5个核心文件操作技能)
所有技能遵循统一规范,包含id(唯一标识)、name(展示名)、parameters(参数定义)、execute(执行函数),支持异常捕获和跨系统兼容。
1. 文件重命名 skills/file_rename.js
const fs = require("fs");
const path = require("path");
/**
* 文件重命名
* @param {string} oldPath - 原文件路径
* @param {string} newName - 新文件名
* @returns {Object} 执行结果
*/
module.exports = {
id: "file_rename",
name: "文件重命名",
parameters: [
{
name: "oldPath",
type: "string",
required: true,
description: "原文件完整路径",
example: "./Desktop/旧文件.txt",
},
{
name: "newName",
type: "string",
required: true,
description: "新文件名(含后缀)",
example: "新文件.txt",
},
],
async execute({ oldPath, newName }) {
try {
const newPath = path.join(path.dirname(oldPath), newName);
fs.renameSync(oldPath, newPath);
return { success: true, message: `重命名成功:${oldPath} → ${newName}` };
} catch (e) {
return { success: false, error: e.message };
}
},
};
2. 文件创建 skills/file_create.js
const fs = require("fs");
const path = require("path");
/**
* 创建文件
* @param {string} filePath - 文件路径
* @param {string} content - 文件内容
* @returns {Object} 执行结果
*/
module.exports = {
id: "file_create",
name: "新建文件",
parameters: [
{
name: "filePath",
type: "string",
required: true,
description: "文件完整路径",
example: "./Desktop/新文件.txt",
},
{
name: "content",
type: "string",
required: false,
default: "",
description: "文件内容",
example: "Hello OpenClaw!",
},
],
async execute({ filePath, content }) {
try {
fs.writeFileSync(filePath, content || "");
return { success: true, message: `创建成功:${filePath}` };
} catch (e) {
return { success: false, error: e.message };
}
},
};
3. 文件读取 skills/file_read.js
const fs = require("fs");
const path = require("path");
/**
* 读取文件
* @param {string} filePath - 文件路径
* @returns {Object} 执行结果
*/
module.exports = {
id: "file_read",
name: "读取文件",
parameters: [
{
name: "filePath",
type: "string",
required: true,
description: "文件完整路径",
example: "./Desktop/文件.txt",
},
],
async execute({ filePath }) {
try {
const content = fs.readFileSync(filePath, "utf8");
return { success: true, content, message: "读取成功" };
} catch (e) {
return { success: false, error: e.message };
}
},
};
4. 文件删除 skills/file_delete.js
const fs = require("fs");
const path = require("path");
/**
* 删除文件
* @param {string} filePath - 文件路径
* @returns {Object} 执行结果
*/
module.exports = {
id: "file_delete",
name: "删除文件",
parameters: [
{
name: "filePath",
type: "string",
required: true,
description: "文件完整路径",
example: "./Desktop/文件.txt",
},
],
async execute({ filePath }) {
try {
fs.unlinkSync(filePath);
return { success: true, message: `已删除:${filePath}` };
} catch (e) {
return { success: false, error: e.message };
}
},
};
5. 创建文件夹 skills/dir_create.js
const fs = require("fs");
const path = require("path");
/**
* 创建文件夹
* @param {string} dirPath - 文件夹路径
* @returns {Object} 执行结果
*/
module.exports = {
id: "dir_create",
name: "创建文件夹",
parameters: [
{
name: "dirPath",
type: "string",
required: true,
description: "文件夹完整路径",
example: "./Desktop/新文件夹",
},
],
async execute({ dirPath }) {
try {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
return { success: true, message: `创建成功:${dirPath}` };
} catch (e) {
return { success: false, error: e.message };
}
},
};
六、部署与运行
1. 环境准备
确保已安装Node.js(v20+),执行以下命令初始化项目:
# 初始化package.json
npm init -y
# 安装依赖
npm install express cors axios
七、目录结构
/openclaw-ai
├── server.js # 后端入口
├── agent.js # AI 智能体核心(多轮工具调用)
├── llm.js # 大模型适配(通义/Ollama/OpenAI)
├── skillSystem.js # 技能自动注册
├── logger.js # 日志系统
├── auth.js # 权限/API Key 校验
├── admin.html # 管理后台页面
├── Dockerfile # Docker 配置
├── docker-compose.yml # Docker Compose 配置
├── package.json # 项目依赖
├── skills/ # 技能目录(自动注册)
│ ├── file_rename.js
│ ├── file_create.js
│ ├── file_read.js
│ ├── file_delete.js
│ └── dir_create.js
│── logs/ # 日志目录(自动生成)
└── ChatAI.vue # Vue3前端组件(放入你的Vue项目中)八、启动方式
方式一:直接运行
npm install
node server.js
前端运行
将ChatAI.vue放入你的Vue3项目(如Vite/Vue CLI),启动前端项目后,访问对应页面即可体验完整的AI智能体交互。
方式二:Docker 部署
Dockerfile 文件
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node","server.js"]
docker-compose.yml 文件
version: "3"
services:
openclaw-ai:
build: .
ports:
- "3000:3000"
volumes:
- ./skills:/app/skills
- ./logs:/app/logs
environment:
- NODE_ENV=production
restart: always
命令
docker-compose up -d
访问地址
- API 接口:
http://localhost:3000/api/chat - 管理后台:
http://localhost:3000/admin - 技能列表:
http://localhost:3000/api/skills
API 调用示例
curl -X POST http://localhost:3000/api/chat \
-H "Content-Type: application/json" \
-H "x-api-key: sk_openclaw_123456" \
-d '{"history":[{"role":"user","content":"帮我创建一个测试文件"}]}'
九、核心功能总结
已实现的核心能力
| 功能模块 | 说明 |
|---|---|
| 流式交互 | 前端实时显示 AI 回复,模拟ChatGPT打字效果 |
| Function Calling | 大模型根据用户指令自动选择并执行技能 |
| 多轮工具循环 | 支持连续执行多个技能完成任务(如“创建文件→重命名→读取内容”) |
| 技能自动注册 | 新增技能只需放入 skills 文件夹,无需修改核心代码 |
| 多模型兼容 | 配置文件修改,可一键切换通义千问、Ollama、GPT |
| 日志系统 | 记录所有技能执行日志 |
| 权限校验 | API Key 保护接口安全 |
| 常处理 | 全链路错误捕获,返回友好提示 |
| 前端技能面板 | 直观展示AI可调用的技能列表 |
| Docker 部署 | 编写Dockerfile一键容器化部署 |
扩展方向
本方案完全复刻 OpenClaw 核心架构,代码可直接商用,同时具备极高的扩展性,开发者可根据业务需求快速定制化开发:
- 技能市场:支持技能的在线安装、卸载、更新
- 接入新模型:在 llm.js 中添加新的模型适配方法
- 多模态支持:扩展图片、语音交互能力
- 更多技能扩展:新增网络请求、数据处理、定时任务等技能;
- 后台管理系统:新增技能管理、日志查看、用户权限模块;
- 缓存优化:缓存LLM响应和技能执行结果,提升性能。
十、应用场景
该系统可直接用于以下场景:
- 本地文件管理AI助手:通过自然语言完成文件创建、重命名、读取、删除等操作;
- 自动化办公工具:结合更多技能实现报表生成、邮件发送、数据统计等;
- 私有化AI智能体:基于Ollama部署本地大模型,实现离线可用的AI助手;
- 前端AI项目模板:快速搭建具备工具调用能力的AI应用。
本方案完全复刻 OpenClaw 核心架构,代码可直接商用,同时具备极高的扩展性,开发者可根据业务需求快速定制化开发。