1000行代码实现极简版openclaw(附源码)(1)
TurboClaw 是一个极简的AI Agent框架,使大语言模型具备工具调用、记忆存储和持续对话能力。其架构包含Gateway(控制中心)、Agent(决策核心)、LLM(推理引擎)、Tools(操作执行)、Memory(信息存储)、Channels(交互界面)和Session(对话管理)七大组件。数据流示例展示了从用户输入"创建文件"到Agent通过ReAct循环调用wri
·
00 - TurboClaw 整体架构 github 源码(欢迎star)
目标
从零开始实现一个极简版的类openclaw的AI agent框架,帮助您快速理解openclaw的实现原理。
首先第一节课理解 TurboClaw 的整体设计,明白数据是如何在各个组件之间流动的。
1. 什么是 TurboClaw?
TurboClaw 是一个极简的 AI Agent 框架,它让大语言模型(LLM)能够:
- 使用工具(文件操作、网络请求等)
- 记住信息(长期记忆)
- 持续对话(多轮交互)
核心能力
普通 LLM 对话:
用户:创建一个文件
AI:抱歉,我无法操作文件系统
TurboClaw Agent:
用户:创建一个文件
AI:[调用 write_file 工具]
AI:文件已创建!
2. 架构设计
2.1 整体架构图
┌─────────────────────────────────────────────────────────────────┐
│ TurboClaw │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Gateway │ │
│ │ (控制中心) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Session │ │ Agent │ │ Tools │ │ │
│ │ │ 会话管理 │◄──►│ ReAct核心 │◄──►│ 工具注册表 │ │ │
│ │ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │
│ │ ┌────▼────┐ ┌─────▼─────┐ │ │
│ │ │ LLM │ │ 内置/SKILL│ │ │
│ │ │ 接口 │ │ 工具 │ │ │
│ │ └─────────┘ └───────────┘ │ │
│ │ │ │
│ └──────────────────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────▼─────────────────────────────┐ │
│ │ Channels │ │
│ │ (交互界面) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Terminal │ │ WebSocket │ │ │
│ │ │ 命令行界面 │ │ 网络接口 │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
2.2 组件职责
| 组件 | 职责 | 类比 |
|---|---|---|
| Gateway | 协调所有组件,管理生命周期 | 项目经理 |
| Agent | ReAct 循环,决策核心 | 大脑 |
| LLM | 提供推理能力 | 智囊团 |
| Tools | 执行具体操作 | 手脚/工具 |
| Memory | 长期存储信息 | 笔记本 |
| Channels | 用户交互界面 | 前台/接待 |
| Session | 维护对话上下文 | 谈话记录 |
3. 数据流详解
3.1 完整请求生命周期
以"创建一个文件"为例:
┌──────────┐
│ 用户 │ "创建一个 hello.txt 文件"
└────┬─────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ Step 1: Channel 接收输入 │
│ ───────────────────── │
│ TerminalChannel 读取用户输入 │
│ 创建 Message 对象 │
└────────────────────────┬───────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ Step 2: Gateway 路由 │
│ ───────────────────── │
│ 根据 channel + clientId 找到或创建 Session │
│ 调用 Agent.process(message, session) │
└────────────────────────┬───────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ Step 3: Agent ReAct 循环 (第1轮 - THINK) │
│ ─────────────────────────────────────── │
│ 构建消息历史: [user: "创建文件..."] │
│ 调用 LLM.complete() │
│ │
│ LLM 返回: { │
│ content: "我来创建文件", │
│ toolCalls: [{ │
│ name: "write_file", │
│ parameters: {path: "hello.txt", content: ""} │
│ }] │
│ } │
└────────────────────────┬───────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ Step 4: Agent ReAct 循环 (第1轮 - ACT) │
│ ───────────────────────────────────── │
│ 从 ToolRegistry 获取 write_file 工具 │
│ 执行 tool.execute({path: "hello.txt", content: ""}) │
│ 返回结果: "已写入: hello.txt" │
└────────────────────────┬───────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ Step 5: Agent ReAct 循环 (第1轮 - OBSERVE) │
│ ────────────────────────────────────────── │
│ 添加 assistant 消息(包含 toolCalls)到历史 │
│ 添加 tool 消息(包含执行结果)到历史 │
│ │
│ 历史现在: [ │
│ {role: "user", content: "创建文件..."}, │
│ {role: "assistant", toolCalls: [...]}, │
│ {role: "tool", content: "已写入..."} │
│ ] │
└────────────────────────┬───────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ Step 6: Agent ReAct 循环 (第2轮 - THINK) │
│ ─────────────────────────────────────── │
│ 构建消息历史(包含上一轮结果) │
│ 调用 LLM.complete() │
│ │
│ LLM 看到历史里已经有 write_file 的结果 │
│ 知道任务已完成 │
│ │
│ LLM 返回: { │
│ content: "文件已创建完成!", │
│ toolCalls: [] // 无工具调用 │
│ } │
└────────────────────────┬───────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ Step 7: Agent 返回结果 │
│ ───────────────────── │
│ 没有 toolCalls,循环结束 │
│ 返回 Message 给 Gateway │
└────────────────────────┬───────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ Step 8: Gateway → Channel → 用户 │
│ ──────────────────────────────── │
│ Gateway 调用 channel.send() │
│ Terminal 打印: "文件已创建完成!" │
└────────────────────────────────────────────────────────────────┘
3.2 关键数据结构流转
// 1. 用户输入
const userInput = "创建文件";
// 2. 转换为 Message
const message: Message = {
id: "uuid-1",
role: "user",
content: "创建文件",
timestamp: Date.now()
};
// 3. 加入 Session
session.messages = [message];
// 4. 构建 LLM 请求
const llmRequest = {
model: "deepseek-chat",
messages: [
{role: "system", content: "You are helpful"},
{role: "user", content: "创建文件"}
],
tools: [/* 工具定义 */]
};
// 5. LLM 响应
const llmResponse = {
content: "",
toolCalls: [{
id: "call-1",
name: "write_file",
parameters: {path: "test.txt"}
}]
};
// 6. 执行工具
const result = await tool.execute({path: "test.txt"});
// result: "已写入"
// 7. 更新 Session
session.messages.push(
{role: "assistant", toolCalls: [...]},
{role: "tool", content: "已写入"}
);
// 8. 再次调用 LLM...
// 9. 最终响应给用户
4. 模块关系图
4.1 依赖关系
┌─────────────┐
│ types │
└──────┬──────┘
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ tools │ │ llm │ │ memory │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────┼───────────────┘
│
▼
┌─────────────┐
│ agent │
└──────┬──────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ channels │ │ skills │ │ config │
└──────┬──────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ gateway │
└─────────────┘
说明:
types被所有人依赖(最底层)agent依赖tools、llm、memorygateway协调所有组件(最顶层)
5. 核心设计原则
5.1 单一职责
每个模块只做一件事:
- Agent: 只负责 ReAct 循环
- Tools: 只负责执行操作
- Memory: 只负责存储
- Channel: 只负责交互
5.2 依赖注入
不硬编码依赖,通过构造函数传入:
// 好的设计
class Agent {
constructor(tools: ToolRegistry) {
this.tools = tools;
}
}
// 坏的设计
class Agent {
constructor() {
this.tools = new ToolRegistry(); // 硬编码
}
}
5.3 接口隔离
通过接口定义契约:
interface Channel {
start(): Promise<void>;
stop(): Promise<void>;
send(message: Message): Promise<void>;
}
// 可以有多种实现
class TerminalChannel implements Channel {}
class WebSocketChannel implements Channel {}
更多推荐


所有评论(0)