351 lines
21 KiB
Markdown
351 lines
21 KiB
Markdown
,
|
||
|
||

|
||
|
||
做Agent开发这两年,踩过的坑不少。[做了两年AI Agent,我发现99%的AI Agent项目都死在了Message Flow设计上](https://mp.weixin.qq.com/s?__biz=Mzg4MzAzNTA5Ng==&mid=2247484194&idx=1&sn=5db2099f5930874735b8e44ae12964de&scene=21#wechat_redirect)
|
||
|
||
LangChain的抽象层确实强大,但复杂度也上来了。AutoGPT的循环控制一直是个难题。自己搭框架?维护成本摆在那。
|
||
|
||
OpenClaw走红后就第一时间部署在海外vps上,每天用下来,确实不全是吹牛逼,是有真东西。
|
||
|
||

|
||
|
||
于是开始研究OpenClaw的源码和技术架构,感觉自己像回到了刚毕业写代码那会儿——很多设计第一眼看过去,觉得简直是“乱来”。
|
||
|
||
文件系统当数据库、无锁并发、插件化Channel...第一眼看上去有点"反常识"。
|
||
|
||
但转念一想,结合我自己做AtomStorm这几个月的教训[全球首个Skills Vibe Agents,AtomStorm技术揭秘:我是怎么用Context Engineering让Agent不"变傻"的](https://mp.weixin.qq.com/s?__biz=Mzg4MzAzNTA5Ng==&mid=2247485151&idx=1&sn=13206f54618166c276507e91e322ccab&scene=21#wechat_redirect),即使我不完全认同它的每一个选择,我必须承认:
|
||
|
||
这些设计背后的工程直觉,太精准了,恰到好处。
|
||
|
||
今天就拆解这8个设计,聊聊它们为什么能跑通。
|
||
|
||
* * *
|
||
|
||
## Part 1:文件系统当数据库——最"蠢"的方案反而最聪明
|
||
|
||
第一次看到OpenClaw用文件系统做持久化,我的第一反应是:"这不是开倒车吗?"
|
||
|
||
2024年了,谁还用文件系统存数据?Redis、PostgreSQL、MongoDB...一堆成熟方案摆在那,为什么要回到"石器时代"?
|
||
|
||
但深入研究后,我发现这个设计简直是天才。
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
### 传统方案的隐藏成本
|
||
|
||
我们先看传统Agent框架怎么做持久化:
|
||
|
||
```
|
||
用户消息 → 序列化 → 写入数据库 → ORM映射 → 缓存层 → 读取 → 反序列化 → 喂给LLM
|
||
```
|
||
|
||
这条链路看起来"专业",但每一步都在增加复杂度:
|
||
|
||
- • 数据库连接池管理
|
||
- • ORM的N+1查询问题
|
||
- • 缓存一致性维护
|
||
- • 序列化/反序列化开销
|
||
- • 数据库迁移脚本
|
||
|
||
更要命的是,这些数据对人类完全不可读。你想看Agent到底记住了什么?对不起,先写SQL查询,再解析JSON。
|
||
|
||
### OpenClaw的文件系统方案
|
||
|
||
OpenClaw直接把记忆存成Markdown文件:
|
||
|
||
```
|
||
agent_memory/├── SOUL.md # Agent的核心身份├── USER.md # 用户画像├── MEMORY.md # 短期记忆└── memory/ ├── 2024-01-15-project-discussion.md └── 2024-01-16-code-review.md
|
||
```
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
这个设计的天才之处在于:
|
||
|
||
**1\. 人类可读**
|
||
|
||
打开`SOUL.md`,你能直接看到Agent的完整人格设定。不需要写SQL,不需要解析JSON,直接用任何文本编辑器就能看。
|
||
|
||
**2\. Git友好**
|
||
|
||
所有记忆都是纯文本,可以直接用Git管理。你能看到Agent的"记忆演化史",甚至可以回滚到某个历史状态。
|
||
|
||
**3\. 零依赖**
|
||
|
||
不需要安装数据库,不需要配置连接池,不需要写迁移脚本。复制文件夹就是完整备份。
|
||
|
||
**4\. OS级别的一致性保证**
|
||
|
||
文件系统的锁机制是操作系统级别的,比你自己写的分布式锁靠谱得多。
|
||
|
||
### 这个方案的适用边界
|
||
|
||
当然,文件系统不是银弹。如果你的Agent需要:
|
||
|
||
- • 每秒处理上万次查询
|
||
- • 复杂的关联查询
|
||
- • 分布式部署
|
||
|
||
那还是老老实实用数据库。
|
||
|
||
但对于大多数Agent应用场景——个人助理、项目管理、知识库——文件系统完全够用,而且更简单。
|
||
|
||
**简单就是最大的优势。**
|
||
|
||
* * *
|
||
|
||
## Part 2:Lane-Based并发——无锁设计的工程美学
|
||
|
||
如果说文件系统是"反常识",那Lane并发模型就是"反直觉"。
|
||
|
||
传统的并发控制,都是围绕"锁"展开的。多个线程要访问共享资源?加锁。担心死锁?加超时。担心性能?加读写锁。
|
||
|
||
结果就是代码里到处都是`lock.acquire()`和`lock.release()`,稍不注意就死锁,调试起来简直是噩梦。
|
||
|
||
OpenClaw的Lane模型,直接把"锁"这个概念消灭了。
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
### 传统线程池的问题
|
||
|
||
我们先看传统方案:
|
||
|
||
```
|
||
# 传统线程池(伪代码)global_queue = Queue()global_lock = Lock()def worker(): while True: task = global_queue.get() with global_lock: # 每个任务都要抢锁 process(task)
|
||
```
|
||
|
||
这个模型的问题在于:
|
||
|
||
- • 所有任务共享一个队列,必须加锁
|
||
- • 锁的粒度难以控制(太粗影响性能,太细容易死锁)
|
||
- • 主会话和子会话混在一起,互相干扰
|
||
|
||
我在AtomStorm早期就踩过这个坑。用户在主会话里聊天,同时触发了一个深度研究的子任务。结果子任务的高负载直接把主会话拖垮了,用户等了30秒才看到回复。
|
||
|
||
### OpenClaw的Lane隔离方案
|
||
|
||
OpenClaw的做法是:**物理隔离,各自调度**。
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
Main Lane处理主会话,Sub Lane处理子任务。两个Lane完全独立,各自维护自己的任务队列,互不干扰。
|
||
|
||
这个设计的精妙之处在于:
|
||
|
||
**1\. 消除了特殊情况**
|
||
|
||
不需要判断"这个任务是主会话还是子会话",不需要加锁,不需要处理死锁。数据结构本身就保证了隔离。
|
||
|
||
**2\. 资源保护**
|
||
|
||
主会话永远有保底的并发资源。哪怕Sub Lane上跑着10个深度研究任务,用户在主会话里发消息,依然能秒回。
|
||
|
||
**3\. 可配置的弹性**
|
||
|
||
Main Lane和Sub Lane的并发数可以独立调整。你的Agent主要做对话?把Main调大。主要跑后台任务?把Sub调大。怎么调?把配置文本直接发给它就可以了,参考下面:
|
||
|
||
```
|
||
Main=4, Sub=8 → 偏对话场景,~50 req/minMain=8, Sub=16 → 均衡场景,~100 req/minMain=16, Sub=32 → 偏后台任务,~200 req/min
|
||
```
|
||
|
||
Linus Torvalds说过一句话:"好的设计通过数据结构消除特殊情况,而非通过if/else补丁。"
|
||
|
||
Lane模型就是这句话的完美实践。
|
||
|
||
* * *
|
||
|
||
## Part 3:协议归一化——消灭if/else地狱
|
||
|
||
做过多平台接入的人,一定对这种代码深恶痛绝:
|
||
|
||
```
|
||
if (platform === 'feishu') {const text = msg.content.text;const userId = msg.sender.user_id;sendFeishuReply(userId, response);} elseif (platform === 'slack') {const text = msg.text;const userId = msg.user;sendSlackReply(userId, response);} elseif (platform === 'discord') {const text = msg.content;const userId = msg.author.id;sendDiscordReply(userId, response);}// 每加一个平台,这坨代码就膨胀一次...
|
||
```
|
||
|
||
每个平台的消息格式不同,字段名不同,API不同。你的核心业务逻辑被平台差异"污染"了,改一个功能要改N个分支。
|
||
|
||
OpenClaw的Channel系统,用一层薄薄的适配层,把这个问题彻底解决了。
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
每个Channel插件只做一件事:把平台特定格式转成统一格式。
|
||
|
||
```
|
||
// OpenClaw的做法(伪代码)// Feishu Channel插件convertToUnifiedFormat(rawMsg) { return { type: 'text', content: rawMsg.content.text, sender: { id: rawMsg.sender.user_id } }}// Gateway核心代码 —— 永远不需要改const message = channel.convertToUnifiedFormat(rawMessage);gateway.handleMessage(message);
|
||
```
|
||
|
||
新增一个平台?写一个Channel插件,`npm install`装上,完事。核心代码一行不动。
|
||
|
||
这个设计还有一个很妙的细节:**能力声明**。
|
||
|
||
每个Channel会主动告诉Gateway自己支持什么能力——文本、图片、文件、语音。Gateway根据能力自动降级。比如Agent想发一张图,但当前Channel只支持文本,Gateway会自动把图片转成描述文字。
|
||
|
||
不需要你写一行判断逻辑。
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
* * *
|
||
|
||
## Part 4:另外5个让我放弃抵抗的设计
|
||
|
||
前面三个是我觉得最"反常识"的,接下来快速过一下另外5个同样精彩的设计。
|
||
|
||
### 4.1 多Provider模型系统——模型切换像换衣服
|
||
|
||
OpenClaw支持Anthropic、OpenAI、BigModel等多个Provider,切换模型只需要改一个字符串:
|
||
|
||
```
|
||
# 从Claude切到GPT,改一行配置model: "anthropic/claude-4.5-sonnet"# ↓model: "openai/gpt-5.2"
|
||
```
|
||
|
||
背后是一套三级解析机制:直接引用 → 别名查找 → Provider查找。运行时热切换,不需要重启。
|
||
|
||
这意味着你可以在不同任务上用不同模型——复杂推理用Claude,简单对话用GPT,代码生成用DeepSeek——而核心代码完全不用改。
|
||
|
||
### 4.2 自描述的Skill系统——代码即文档
|
||
|
||
传统插件系统有个老大难问题:文档和代码分离,文档永远是过时的。
|
||
|
||
OpenClaw的Skill系统要求每个Skill自带一个`SKILL.md`,放在代码同目录下。这个文件不是给人看的装饰品——Agent会读它来理解这个Skill能做什么、怎么调用。
|
||
|
||
```
|
||
skills/├── deep_research/│ ├── SKILL.md ← Agent读这个来决定是否调用│ └── src/index.ts ← 实现代码├── ppt_creation/│ ├── SKILL.md│ └── src/index.ts
|
||
```
|
||
|
||
文档和代码在同一个目录,改了代码必须同步改文档,否则Agent就会调用出错。这种"强制绑定"比任何代码审查规范都管用。
|
||
|
||
而且Skill在沙箱环境里执行,一个Skill崩了不会影响其他Skill。
|
||
|
||
### 4.3 配置热更新——改完1秒生效
|
||
|
||
传统方案改配置要重启服务,OpenClaw用`fs.watch`监听配置文件变更,检测到变化后自动热重载。
|
||
|
||
```
|
||
修改配置 → fs.watch检测 → 停止旧Channel → 重新加载 → 启动新Channel └──────── < 1秒完成 ────────┘
|
||
```
|
||
|
||
配置文件用Zod做类型校验,写错了直接报错,不会带着错误配置启动。
|
||
|
||
### 4.4 可控的Agent执行循环——防止失控
|
||
|
||
Agent最怕的就是陷入无限循环。OpenClaw用`maxTurns`做硬限制:
|
||
|
||
```
|
||
Turn 1: 调用工具A → 成功Turn 2: 调用工具B → 成功Turn 3: 调用工具C → 失败,重试...Turn 10: 达到maxTurns → 强制停止,返回当前结果
|
||
```
|
||
|
||
同时支持流式响应,用户能实时看到Agent在做什么,不用干等。
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
### 4.5 Cron/Heartbeat——Agent终于有了"主动性"
|
||
|
||
传统Agent都是被动的——你问它才答。OpenClaw给Agent加了定时任务能力:
|
||
|
||
- • **Cron**:精确时间触发,比如每天10点生成日报
|
||
- • **Heartbeat**:条件触发,每小时检查一次,只在需要时执行
|
||
|
||
而且支持三种唤醒模式:立即执行、下次心跳执行、手动触发。重启后执行状态不丢失。
|
||
|
||
这让Agent从"被动应答机器"变成了"主动工作伙伴"。
|
||
|
||
* * *
|
||
|
||
## Part 5:这些设计背后的工程哲学
|
||
|
||
研究完这8个设计,我发现OpenClaw的架构决策背后遵循了软件工程的优秀设计哲学,作者是无疑是资深程序员。
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
### Good Taste:消除特殊情况
|
||
|
||
Linus Torvalds有句名言:"好的设计通过数据结构消除特殊情况,而非通过if/else补丁。"
|
||
|
||
OpenClaw的Lane模型、Channel系统、Provider抽象,都是这个哲学的体现。不是用代码去"处理"差异,而是用数据结构让差异"消失"。
|
||
|
||
### 简洁执念:复杂性是万恶之源
|
||
|
||
能用文件系统就不用数据库,能用配置就不写代码,能用插件就不改核心。
|
||
|
||
每个模块都保持在可理解的复杂度内——函数不超过50行,嵌套不超过3层,文件不超过1000行。
|
||
|
||
### Never Break Userspace:向后兼容是铁律
|
||
|
||
配置格式演进时,新版本兼容旧格式。API扩展时,不破坏现有接口。插件更新时,支持热更新。
|
||
|
||
这让OpenClaw可以持续演进,而不会让用户的代码突然跑不起来。
|
||
|
||
### 数据结构优先:设计决定实现
|
||
|
||
先设计Session Key的格式(`platform:user:chat`),再设计路由逻辑。先设计统一消息格式,再设计Channel适配。
|
||
|
||
好的数据结构让算法自然简洁,糟糕的数据结构让代码充满补丁。
|
||
|
||
* * *
|
||
|
||
## Part 6:OpenClaw适合你吗?
|
||
|
||
看到这里,你可能会问:OpenClaw这么好,是不是所有场景都适合?
|
||
|
||
答案是:不是。
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
OpenClaw的设计哲学是:**为80%的常见场景提供最优雅的解决方案,而不是为100%的场景提供平庸的方案。**
|
||
|
||
如果你的需求刚好落在那80%里,OpenClaw会让你觉得"这就是我想要的"。如果落在那20%里,也别硬上,选择更合适的工具。
|
||
|
||
* * *
|
||
|
||
## 写在最后:从OpenClaw到AtomStorm
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
研究完OpenClaw,我对Agent架构的理解又深入了一层。
|
||
|
||
以前我觉得Agent开发的核心是Prompt Engineering——怎么写好System Prompt,怎么设计Few-shot。
|
||
|
||
现在我的认知,**架构设计才是决定Agent能力上限的关键**。
|
||
|
||
再好的Prompt,也救不了一个Context爆炸的Agent。再强的模型,也发挥不出在一个充满if/else的系统里的能力。
|
||
|
||
这也是为什么我在做AtomStorm时,花了大量时间在Context Engineering和架构设计上。
|
||
|
||
AtomStorm对比OpenClaw,加入了:
|
||
|
||
- • **Context Engineering**:分层注入、工具意图预判、Just-in-Time RAG
|
||
- • **Skills Vibe Agents**:渐进式披露、按需加载、二级分发
|
||
- • **流式渲染**:实时生成PPT和架构图,状态分离
|
||
- • **MCP深度集成**:20+个MCP工具,智能路由
|
||
|
||
本文的配图均由AtomStorm生成。
|
||
|
||
如果你对Agent这个方向感兴趣,欢迎来试试AtomStorm(**studio.atomstorm.ai**),目前开放少量内测名额,计划本月正式上线海外,私信我或者加群获取。为保证群质量,其他两个技术交流的群,超过200人会有50的红包门槛,介意勿加。
|
||
|
||
' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
|
||
|
||
* * *
|
||
|
||
**我是栗子KK,还在路上的AI产品Founder。studio.atomstorm.ai**
|
||
|
||
山远路险,鞋里有沙。
|
||
|
||
这篇文章如果对你有帮助,哪怕是解决了一个小疑惑,也麻烦点个"在看"或者转发给同样对OpenClaw感兴趣的朋友。
|
||
|
||
创作不易,感谢阅读 🙏\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||
|
||
往期精彩推荐:
|
||
|
||
[AI编程正式进入"团战时代":Claude Code Agent Teams,我等了两年的功能终于来了](https://mp.weixin.qq.com/s?__biz=Mzg4MzAzNTA5Ng==&mid=2247485253&idx=1&sn=0722908710926fbfa1b59b0507f103fa&scene=21#wechat_redirect)
|
||
|
||
[全球首个Skills Vibe Agents,AtomStorm技术揭秘:我是怎么用Context Engineering让Agent不"变傻"的](https://mp.weixin.qq.com/s?__biz=Mzg4MzAzNTA5Ng==&mid=2247485151&idx=1&sn=13206f54618166c276507e91e322ccab&scene=21#wechat_redirect)
|
||
|
||
[4个月烧了2万刀Token,全球首款Skills Vibe Agent终于开启邀请内测,我也终于敢说:Sam Altman预言的超级个体,可能真的来了](https://mp.weixin.qq.com/s?__biz=Mzg4MzAzNTA5Ng==&mid=2247485129&idx=1&sn=efea9accb62a7502b464fa872b1cacdd&scene=21#wechat_redirect)
|
||
|
||
[做了两年AI Agent,我发现99%的AI Agent项目都死在了Message Flow设计上](https://mp.weixin.qq.com/s?__biz=Mzg4MzAzNTA5Ng==&mid=2247484194&idx=1&sn=5db2099f5930874735b8e44ae12964de&scene=21#wechat_redirect)
|
||
|
||
[精准爆破,拆解Claude Skills完整技术架构](https://mp.weixin.qq.com/s?__biz=Mzg4MzAzNTA5Ng==&mid=2247484919&idx=1&sn=e2d548fad5501c6dd2e763aa732938e9&scene=21#wechat_redirect)
|
||
|
||
[ClaudeCode工程师亲述:为什么你的AI Agent总是"智障"?问题可能出在工具设计上](https://mp.weixin.qq.com/s?__biz=Mzg4MzAzNTA5Ng==&mid=2247484568&idx=1&sn=01e06b28c3c8d126d35a8390cfadeb00&scene=21#wechat_redirect)
|
||
|
||
[Claude 多智能体架构深度拆解:90.2%性能提升背后的工程真相](https://mp.weixin.qq.com/s?__biz=Mzg4MzAzNTA5Ng==&mid=2247484579&idx=1&sn=b0985523386d3550fe8ef2ae4554cab6&scene=21#wechat_redirect) |