buddyMe 心跳系统与 Loop 引擎:让 AI 自己干活,还不花钱
如果 AI 永远不需要休息,会怎样?
想象一下:你有一个 AI 助手,它不光在你聊天的时候才工作。你睡觉了,它还在默默帮你整理笔记、查天气、同步数据——就像一个从不疲倦的私人秘书。
但问题来了:如果让 AI 每隔 30 分钟就执行一次任务,每次都要调用大模型,一个月下来光是 Token 费用就能让你的账单"心跳加速"。
buddyMe 的解决方案很有创意——它设计了一套"心跳系统"来管理后台任务,又发明了一个"Loop 引擎"来实现"录制一次,永久回放"。两个系统配合,把周期性任务的 Token 成本砍掉了 99% 以上。
先看一张全景图,对整个架构有个直觉:
心跳系统 (后台调度)
|
| tick 触发
v
Loop 引擎 (录制 & 回放)
|
+----+----+
| |
v v
有 Skill 无 Skill
| |
v v
确定性回放 LLM 执行
零 Token 降级兜底
| |
+----+----+
|
v
任务完成
一、心跳系统:AI 助手的"自主神经系统"
为什么需要心跳?
你身体的自主神经系统不需要你"想"就能运转——心跳、呼吸、消化,全自动。buddyMe 的心跳系统做的事也类似:让 AI 在后台自动执行一些重复性任务。
比如:
- 记忆更新——每隔一段时间,自动把最近的对话内容整理成笔记
- 每日总结——每天晚上自动生成一份当天的工作摘要
- 定时查天气——你设定好,它自己查,你醒来就能看到
如果没有心跳系统,这些任务要么得你手动触发,要么得写复杂的定时脚本。有了它,AI 真正变成了一个"自己会干活"的助手。
双层设计:数据归数据,执行归执行
心跳系统的设计遵循了一个经典的工程原则:分而治之。它把整个系统分成了两层:
Agent 主进程
|
v
[调度执行层] Agent.tick() ---> 工具 / LLM / Loop 回放
|
v
[数据管理层] HeartbeatManager
|
v
heartbeat.json (配置 & 任务状态)
- 数据管理层(HeartbeatManager)——相当于"档案管理员",只负责存取任务配置和状态,不干任何实际工作
- 调度执行层(Agent.tick())——相当于"执行者",负责判断该不该执行、怎么执行
这就像公司里的分工:HR 只管人员档案(数据层),项目经理负责安排工作(执行层)。谁也不越界,出了问题好排查。
所有配置,一个文件搞定
心跳系统把所有的任务配置存在一个叫 heartbeat.json 的文件里。长这样:
{
"config": {
"interval_minutes": 30,
"active_hours": {"start": "08:00", "end": "23:00"}
},
"tasks": [
{
"id": "memory_update",
"name": "记忆更新",
"interval_minutes": 30,
"enabled": true,
"last_run": "2026-05-10T14:30:00"
}
]
}
翻译成人话就是:每 30 分钟检查一次,但只在早上 8 点到晚上 11 点之间工作(毕竟你也不想 AI 在凌晨 3 点疯狂写日志吧)。
两种触发方式:闹钟 vs 计时器
心跳系统支持两种任务触发方式,就像手机上的两种提醒:
| 模式 | 像什么 | 怎么工作 | 适合做什么 |
|---|---|---|---|
| Interval(间隔模式) | 计时器:每隔 30 分钟响一次 | 距离上次执行够了 30 分钟就触发 | 周期性任务:记忆更新、数据同步 |
| Schedule(定时模式) | 闹钟:每天早上 9 点响 | 到了指定时间(±5 分钟容忍度)就触发,每天最多一次 | 每日固定任务:早间提醒、日报生成 |
心跳节拍:每 60 秒的一次"脉搏"
心跳系统的工作方式特别简单:起一个后台守护线程,每 60 秒做一次检查(叫一次 tick())。每次 tick 就像一次脉搏:
tick() 开始
|
v
加载 heartbeat.json
|
v
在活跃时段? (08:00-23:00)
|
+-- 否 --> 跳过, 等下次
|
是
|
v
遍历所有任务 <-------------------+
| |
v |
该执行吗? (_should_run) |
| |
+-- 否 --> 下一个任务 -------->+
| |
是 |
| |
v |
执行任务 |
| |
v |
原子更新 last_run |
| |
+----> 下一个任务 ------------>+
几个关键的安全设计:
- 守护线程——主程序退出了,它跟着自动退出,不会变成"僵尸"
- 异常隔离——某个任务报错了,不会影响其他任务,也不会把整个心跳线程搞崩
- 超时保护——每个任务都有超时限制,防止一个任务卡住影响全局
二、Loop 引擎:录制一次,永久回放
一个关键洞察
Loop 引擎的发明来自一个简单的观察:
"同一个定时任务,AI 每次调用的工具序列几乎一模一样,变的只是参数。"
举个例子——每 30 分钟查天气:
| 步骤 | 工具 | 第一次 | 第二次 |
|---|---|---|---|
| 1 | 获取当前时间 | 2026-05-10 14:30 | 2026-05-10 15:00 |
| 2 | 查天气 | 北京 晴 25°C | 北京 多云 22°C |
| 3 | 写入文件 | 天气日志+14:30数据 | 天气日志+15:00数据 |
看出来了吗?操作流程完全一样,只是"时间"和"天气数据"不一样。
那为什么要每次都让大模型重新"想"一遍呢?这就好比你已经知道做蛋炒饭的步骤了,不需要每次都翻菜谱。Loop 引擎做的就是:第一次让 AI 认真做一遍并"录下来",之后按录像"回放"就行。
四个阶段:创建 → 录制 → 生成 → 回放
/loop 30m AI 执行 提取模板 按 skill.json
查天气 --> + 录制 --> 生成 skill --> 零Token执行
(创建任务) (录制阶段) (生成阶段) (回放阶段)
阶段一:创建
你只需要说一句话:
/loop 30m 每30分钟查天气
系统会自动创建一个心跳任务,标记为"等待首次执行"。
阶段二:录制
第一次执行时,系统会让 AI 正常完成任务,但会同时录制它的每一步操作:
录制的工具链 (tool_chain):
Step 1: bash -> "date ..." -> "2026-05-10 14:30:00"
Step 2: bash -> "python weather.py" -> "北京 晴 25°C 南风3级"
Step 3: write_file -> weather_log.txt -> "写入成功"
这就像在厨房里架了一台摄像机,把厨师做菜的全过程拍下来。
阶段三:生成
录制完成后,系统会自动分析工具链,把其中"每次都会变"的部分提取出来,替换成模板变量:
| 原始值 | 模板变量 | 含义 |
|---|---|---|
2026-05-10 14:30:00 | {{current_time}} | 每次执行时的真实时间 |
北京 晴 25°C 南风3级 | {{step_1_result}} | 上一步(查天气)的真实结果 |
| 文件原有内容 | {{prev_content}} | 读取文件时的当前内容 |
最终生成的 skill.json 就是一个"菜谱"——告诉你每一步该用什么工具、传什么参数。
阶段四:回放
从第二次执行开始,心跳系统检测到有现成的 Skill 文件,就完全不经过大模型了。它直接按 skill.json 里的步骤,一步步调用工具:
tick 检测到 Loop 任务
|
v
skill.json 存在?
|
+-- 否 --> 降级为 LLM 执行 (Token ~1000)
|
是
|
v
加载 Skill 步骤
|
v
替换模板变量
({{current_time}} -> 真实时间)
({{step_N_result}} -> 真实结果)
|
v
调用工具
|
v
还有步骤? -- 是 --> 回到 "替换模板变量"
|
否
v
返回结果, Token 消耗 = 0
- 用真实的当前时间替换
{{current_time}} - 执行天气查询,拿到真实结果替换
{{step_1_result}} - 把结果写入文件
整个过程零 Token 消耗。
省多少钱?算一笔账
以"每 30 分钟查天气"为例:
| 传统方式(每次调 AI) | Loop 回放方式 | |
|---|---|---|
| 每次 Token 消耗 | ~500-2000 | 0(首次 ~1000 后不再消耗) |
| 每天执行次数 | 48 次 | 48 次 |
| 每天 Token 消耗 | ~24,000 | ~1,000(仅首次) |
| 每月 Token 消耗 | ~720,000 | ~1,000 |
| 节省比例 | — | 99.86% |
一个任务就能省这么多。如果你有 5 个、10 个定时任务呢?Loop 引擎的价值就非常明显了。
为什么禁止 edit_file?
Loop 引擎在录制时有一个特殊规则:拒绝使用 edit_file 工具。
原因是 edit_file 需要"精确匹配"文件中的某段文字才能编辑。但回放时文件内容可能已经变了,之前录制的匹配文本找不到了,就会出错。
解决方案很简单:用 read_file(读取)+ write_file(整个写入)来替代。每次先读取最新内容,拼接修改后再完整写入,这样就不依赖精确匹配了。
万一出了问题?降级策略兜底
系统设计了完善的"安全网",确保任何情况下任务都不会丢:
异常发生
|
v
什么情况?
|
+--> 录制失败 / 工具链不合格
| |
| v
| 不生成 Skill
| |
| v
| 后续 tick 走 LLM 执行
|
+--> 首次执行超时
| |
| v
| 清除 pending 标记
| |
| v
| 后续 tick 走 LLM 执行
|
+--> 回放步骤出错
| |
| v
| 本次跳过
| |
| v
| 下次 tick 重试回放
|
+--> 用户执行 /loop --remove
|
v
删除任务 + Skill 文件, 终止
| 发生了什么 | 系统怎么办 |
|---|---|
| 首次录制失败(工具链不合格) | 不生成 Skill,以后每次用 AI 正常执行 |
| 首次执行超时 | 清除标记,降级为 AI 执行 |
| 回放过程中某一步出错 | 本次跳过,下次心跳再试 |
| 用户主动删除(/loop --remove) | 同步删除任务和 Skill 文件,干净利落 |
核心原则:宁可用 AI 执行,也不让任务中断。
三、完整生命周期:一张图看懂
一个 Loop 任务从创建到结束的完整流程:
用户输入: /loop 30m 查天气
|
v
写入 heartbeat.json (标记: 等待首次执行)
|
v
后台线程首次执行: AI 完成 + 录制工具链
|
v
校验工具链
(非空? 无 edit_file? 有 write_file? 无错误?)
|
+-- 合格 --> 生成 skill.json
| |
| v
| 后续 tick: 确定性回放 (Token = 0)
| |
+-- 不合格 --> 不生成 Skill
|
v
后续 tick: LLM 执行 (Token ~1000/次)
|
v
用户: /loop --remove
|
v
删除任务 + Skill 文件, 干净退出
四、工程亮点:不止是能用,还要稳
原子写入:断电也不怕
所有文件写入都采用"先写临时文件 → 确认写入完成 → 重命名覆盖"的方式。就算写入过程中突然断电,也不会产生半截损坏的文件。这就像寄快递:先把东西包好、确认无误,再贴地址寄出。
Skill 三层加载:按需取用
L1 基础层 L2 指令层 L3 执行层
(元数据, ~100 Token) --> (SKILL.md, 按需) --> (skill.json, 零Token)
名字 / ID 详细指令 回放步骤
日常运行只走 L3,完全不碰 L1 和 L2,最大程度节省资源。
总结
buddyMe 的心跳系统和 Loop 引擎,其实回答了一个关键问题:
"怎么让 AI 在后台持续工作,还不把钱花光?"
怎么让 AI 后台持续工作, 还不把钱花光?
|
+--- 心跳系统 (稳定可靠的后台调度)
| "像身体的自主神经, 不用你操心也能运转"
|
+--- Loop 引擎 (录制一次, 永久回放)
| "像学会做菜, 不需要再翻菜谱"
|
+--- 降级策略 (任何情况都不掉链子)
"像信号差时自动切 2G, 慢但不断线"
|
v
7x24 不间断服务, 账单不会心跳加速
这种"第一次认真学,之后自动重复"的思路,不仅适用于 AI 助手,对任何需要定期执行重复任务的系统都有参考价值。
最终效果:buddyMe 智能体既能响应你的实时请求,又能在后台默默帮你处理重复工作——真正的 7x24 小时不间断服务,而且账单上不会让你心跳加速。