一个 AI Agent 能自动回答问题、写代码、分析数据,这已经不稀奇了。但如果你想让它每天早上 9 点自动生成工作摘要、每半小时检查一次邮件、出了故障自动告警——而你完全不用盯着它——事情就变得有意思了。
光有一个聪明的 Agent 不够,你还需要回答三个工程问题:什么时候启动?结果发给谁?挂了怎么办?
拆解 OpenClaw 的源码,发现它内部有一个叫 Planning 的模块,就是专门解决这三个问题的。8900 行核心代码,不依赖 Redis、不依赖数据库、不需要消息队列,纯 TypeScript 单进程搞定。
这篇文章拆解一下它背后几个让我觉得”确实讲究”的工程设计。
搞 AI 的同学看到”Planning”这个词,第一反应大概是 Plan-and-Execute——Agent 先推理出执行计划,再逐步完成。
OpenClaw 的 Planning 跟这个没关系。它更接近 Linux 世界里的 crontab,是一个运行在 Gateway 进程内部的持久化任务调度系统。关心的不是”怎么思考”,而是”怎么准时、可靠地把活干了”。
打个比方:Agent 是员工,Planning 是排班表。
系统支持三种调度模式:一次性定时、固定间隔循环、Cron 表达式。前两种直觉上很简单,但固定间隔模式里藏着一个经典陷阱。
假设一个任务设定每 30 分钟执行一次,10:00 创建。最朴素的实现方式是”跑完当前任务,在完成时间上加 30 分钟”。如果第一次跑到 10:20 才结束,下次就是 10:50,再下次 11:20、11:50……
看出问题了吗?执行时间在不断漂移,越来越偏离预期的整点节奏。
OpenClaw 的做法是引入了锚点对齐算法:以任务创建时间为锚点,下次执行时间永远对齐到锚点的整数倍位置。
不管中间每次执行耗时多久,长期运行下时间线永远不会偏。一个很小的设计决策,但体现了做基础设施和做应用层之间的思维差异——基础设施要考虑的是”跑一个月之后还对不对”。
另一个经典问题出现在 Cron 表达式模式下。
假设配了十个任务都在整点触发,到了整点全部同时启动,CPU 和内存瞬间被打满。这在分布式系统领域有个名字叫惊群效应(Thundering Herd)。
OpenClaw 的解法是给每个任务分配一个确定性偏移量:用任务 ID 的 SHA-256 哈希值对 5 分钟窗口取模。
效果很直观:十个整点任务被自动散开到 0~5 分钟的窗口里,各自错峰执行。因为哈希是确定性的,同一个任务每次算出来的偏移量完全一致,不会忽前忽后,也不需要任何人工配置。
在所有设计细节里,让我印象最深的其实是一个已修复的 Bug。
是一个查询任务列表的接口——标准的读操作。但工程师发现,调用它的时候,系统会悄悄把过期任务的下次执行时间往后推。
一个读操作,产生了写的副作用。
这导致了一种”灵异现象”:有些任务莫名其妙被跳过了,但你查一下列表,状态看起来又是”正常”的——因为查询本身就把状态改了。极难复现,极难定位。
修复方案是将重算逻辑分成了两种模式:
这不只是修了一个 Bug。它确立了一条原则:查询操作永远没有副作用。 系统行为因此变得可预测——这是所有可靠基础设施的前提。
当多个操作同时要修改任务状态时,怎么保证不出乱子?
OpenClaw 没用锁,没用消息队列,用了一个极简的方案:Promise 链串行化。
核心代码只有一行逻辑——每个新操作 到前一个操作后面。自然排队,自然串行,不可能死锁。
但 Agent 任务可能执行几分钟甚至几十分钟。如果全程串行,其他所有读操作都会被阻塞。所以系统设计了一个分阶段执行模式:
Phase 3 有一个微妙之处:执行期间,任务的配置可能已经被其他操作修改了。系统的合并策略是”保留执行结果,采用最新配置“,思路类似数据库中的 MVCC(多版本并发控制)
后台跑的 Agent 不可能永远正常。这个模块构建了五层防护网:
还有一个启动场景的考量:Gateway 重启后可能积压了大量错过的任务。如果全部立刻执行,等于自己给自己制造惊群。系统会按过期时间排序,前 5 个立即执行,后续每个错开 5 秒,平滑铺展开。
任务跑完了,结果要送出去。投递支持三种模式:
这里有一个容易被忽略的用户体验细节:如果 Agent 在执行过程中已经通过消息工具主动发送了结果,投递引擎会识别出来,跳过重复发送。用户不会收到两条一模一样的消息。
另外,结果投递和失败告警是独立配置的。即使正常结果不需要推送(),失败时也能通知到指定频道。这种关注点分离的设计,避免了”静默任务出了问题但没人知道”的盲区。
整个系统的持久化方案朴素得令人意外:一个 JSON 文件。
写入采用 Unix 经典的 “临时文件 + rename”原子操作——先写到 文件, 刷盘,再 覆盖目标文件。任何时刻断电或崩溃,你读到的数据都是完整的。
数据加载时会自动执行一套 18 步格式迁移,把各种历史版本的数据统一归一化。所有迁移幂等、无感,老用户升级后数据直接可用,不需要手动跑脚本。
没有 Redis,没有 PostgreSQL,没有 RabbitMQ。对于一个运行在用户本地的 AI 网关来说,这种”够用就好”的选择反而是最务实的架构决策。
最后说一个数字。
这个模块有约 8900 行核心代码,配套的测试代码是 14300 行——测试比实现多了 60%。
61 个测试文件中,有 13 个以上是针对历史 Bug 的专项回归测试。每一个修过的 Bug 都留下了一个测试用例,确保它永远不会复发。
这个比例不是偶然的。能做到测试比代码多,说明每一个设计决策都有验证、每一个边界条件都被覆盖。再加上全套依赖注入(连时间源 都可以替换成假时钟),所有定时逻辑都能在测试里瞬间快进验证,不需要真的等 30 分钟。
回过头看,OpenClaw 的 Planning 模块做的事情并不”酷炫”——没有大模型推理,没有多智能体博弈,就是一个扎扎实实的任务调度系统。
但它解决了一个真实的问题:
让 Agent 从”你盯着用的工具”变成”替你值班的员工”。
8900 行代码,61 行的对外接口,零外部依赖,14300 行测试。这种克制和纪律,可能才是工程能力的真正体现。
一个开放的问题留给大家:随着 Agent 能力越来越强,这种基础设施层的调度与编排,会不会成为比模型能力本身更关键的竞争壁垒?
👉如果你想系统掌握多模态大模型前沿技术与应用,推荐你学习我的精品课程:
📚课程覆盖主流多模态架构、多模态Agent、数据构建、训练流程、评估与幻觉分析,并配套多个项目实战:LLaVA、LLaVA-NeXT、Qwen3-VL、InternLM-XComposer(IXC)、TimeSearch-R视频理解等,包含算法讲解、模型微调/推理、服务部署、核心源码解析。
💡本课程目前正在更新中,你可以在我的个人官网或B站课堂参与学习:
📺B站课堂 Agent 智能体(点击左下角“阅读原文”直接跳转)https://www.bilibili.com/cheese/play/ss33184
🌐官网链接(国内访问需科学上网):https://www.tgltommy.com/p/multimodal-season-1
发布者:Ai探索者,转载请注明出处:https://javaforall.net/275676.html原文链接:https://javaforall.net
