1. HEARTBEAT.md到底在解决什么:AI代理的“过夜失忆症”
事情得从一次让我印象深刻的翻车说起。那天我用Qclaw跑一个要跨夜的批处理任务,睡觉前把上下文调校得清清楚楚,给项目配好了workspace context,还专门叮嘱了几条硬性约束。结果第二天早上起来一看,Qclaw像是被格式化了——它完全不记得昨晚约定过的规则,按默认逻辑把任务执行得面目全非,还振振有词地给我交付了一份“看似合理但方向全错”的结果。
那一刻我意识到一个很根本的问题:对话式AI工具的记忆,本质上是脆弱的。它依赖的聊天上下文窗口一旦被截断、清理或者超时,前一晚的全部约定就归零了。项目还在,代码还在,但那颗“大脑”已经换了人。这跟人失忆不一样,人忘了还能靠笔记想起来,AI忘了就是真忘了,它连“自己忘过”这件事都不一定知道。
后来我在团队的workspace context里看到一条规则,原文是:Read HEARTBEAT.md if it exists (workspace context). Follow it strictly。当时还觉得这行字挺唬人,仔细研究了一下才明白,这就是用来治“AI代理失忆症”的:让Qclaw在执行任何工作之前,先去工作区找一份叫HEARTBEAT.md的文件,如果它存在,就当作最高优先级指令来执行。
这个设计思路特别朴素,但特别有效——把“聊天中的口头约定”落盘成“项目里的实体文件”。Qclaw本身是一个高度依赖工作区上下文的AI编码代理,它读取项目文件、分析代码结构、执行任务,而HEARTBEAT.md就是给它的一张小抄,让它每次醒来都知道自己是谁、之前在干嘛、接下来要干嘛。
本质上,这不是什么黑科技,而是用工程手段解决模型的天然短板。我们不需要模型真的拥有无限记忆,只需要让它养成“行动之前先看一眼笔记”的习惯。这就好比一个记性不好的实习生,你不可能去改造他的大脑,但你可以强制要求他每天早晨先翻一遍工作日志再动手。
我后来把这个习惯固化成了所有Qclaw项目的标配,团队里几个人也一直在用。这篇文章就把这套玩法彻底拆开:HEARTBEAT.md是什么,为什么要用文件名带个heartbeat,内容怎么写才能被Qclaw严格遵循,实测中又会遇到哪些坑。
它适合谁看?用Qclaw或者其他AI编程代理做项目、又受不了“模型忘事”的人。不管你是本地部署了Qclaw、接的是开源模型,还是直接用在线版本,这套思路都能直接落到项目里。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 一份能被“严格遵循”的HEARTBEAT.md应该长什么样
2.1 命名里的门道:为什么叫HEARTBEAT而不是NOTES
先聊聊命名这件事。有人可能会问,这不就是一份备忘录吗?叫CONTEXT.md、NOTES.md、RULES.md不也一样?我一开始也是这么想的,后来被团队里一个老哥一句话点醒:名字决定了Qclaw在workspace context里如何被检索、如何被估值。
“HEARTBEAT”这个词在工程语境里有明确隐喻——心跳,意味着定期的、周期性的、不可跳过的状态信号。如果一份文件叫NOTES.md,Qclaw在扫描工作区时,大概率会把它当作“参考资料”、“辅助信息”来对待,优先级不高。但HEARTBEAT.md这个名字暗示着“这是一个活跃的、需要持续同步的状态载体”,再加上你的上下文里写着“Follow it strictly”,模型对它的权重判断会完全不同。
另外一个更实际的原因是:如果你同时有README.md、CONTEXT.md、NOTES.md,模型在进入工作区时会对“先看谁”产生犹豫,甚至各自读一遍后按时间顺序混乱执行。而一个有着明确动作指令的文件,配合一行强制规则,会极大降低模型在“读文件”和“执行任务”之间的决策成本。
我在实际使用中发现,Qclaw的上下文系统对文件名是敏感的。它扫描workspace context的时候,HEARTBEAT.md在排序和权重上几乎总能排在普通文件前。这倒不是说引擎里有什么硬编码,而是模型在训练数据里见过太多“heartbeat”作为状态同步文件的案例,自然会赋予更高注意力。
所以第一经验是:如果你想复制这套机制,文件名别乱改。叫STATE.md、PLAN.md可能也有效,但HEARTBEAT.md是目前所有命名里,被模型理解成本最低、遵循度最高的一个。
2.2 文件内容的结构化设计:状态区、指令区、追踪区
我在几十个项目里测试过不同写法的HEARTBEAT.md,踩了不少坑之后,总结出一套稳定好用的结构。它分成三个区块:状态区、指令区、追踪区。这三个区块各管一件事,不能混。
状态区是“我从哪里来”。它要写清楚这个项目的阶段、当前实现到了哪里、最近一次改动了哪些关键文件。这部分不需要长篇大论,几句话就够,但必须具体。
指令区是“我要到哪里去”。这是整份文件里权重最高的区域,也是Qclaw唯一允许“无条件遵循”的内容。它写的是接下来要执行的硬性约束:不要动哪些文件、优先实现哪个模块、代码风格必须保持什么标准。凡是你不希望代理自作主张的事情,都要写在这里。别指望Qclaw能猜出你的想法,你写什么它就守什么。
追踪区是“问题与偏差记录”。这个区域是给我自己看的,也是给模型看的。每当Qclaw在执行中偏离了预期,我会让它把偏离的原因、修正措施追加到追踪区。这样下次任务启动时,模型会直接看到“上次踩过的坑”,不需要重新犯一遍错误。
这三个区块写在一起,但用清晰的标题分隔。Qclaw在解析Markdown时对标题结构很敏感,规范的三级标题能确保它不会混淆“状态”和“指令”的语义。如果你用一段长文本糊在一起,模型经常会把状态描述误解成指令去执行,后果就是它对着过时的代码状态做一些莫名其妙的动作。
2.3 一份可以直接抄的模板
下面我把我目前用的模板直接放出来,你可以复制过去改一改就能用。这份模板是我迭代了好几轮之后的形态,兼顾了信息密度和模型的解析友好度。
markdown复制# HEARTBEAT - 项目状态与执行规则
> 最后更新: 2025-xx-xx
> 当前模型会话: 不固定,每次启动时读取
## 状态快照
- 当前阶段: 核心逻辑实现中 / 重构阶段 / 测试准备
- 最近完成: [一句话描述最近一个可运行的状态]
- 当前分支: [分支名]
- 涉及核心文件: [文件列表]
## 硬性指令
1. 本文件为最高优先级规则,与用户口头指令冲突时,以本文件为准。
2. 严禁修改 src/legacy/ 目录下的任何文件。
3. 所有网络请求必须走统一的apiClient封装,禁止裸axios调用。
4. 测试文件命名必须使用 *.test.ts 格式。
5. 每次完成一个任务后,必须在本文件"任务进度"追加一行记录。
## 任务进度
- [x] 完成登录模块重构
- [ ] 完成支付回调的幂等处理(当前阻塞项)
- [ ] 补充单元测试
## 已知问题与偏差记录
- 2025-xx-xx: Qclaw尝试修改了legacy目录的配置文件,被规则拦截后改用复制方案,已将方案写入docs/decision.md
注意几个细节。第一,最前面的“最后更新”日期非常重要,它让Qclaw在判断信息新鲜度时有了依据。第二,“硬性指令”区里用了编号列表,而不是无序列表,模型在执行规则时倾向按编号逐条检核。第三,“任务进度”区我要求它每次完成任务后必须追加,这会让文件持续保持活性,而不是写一次就变成死文档。
3. 在Qclaw里把HEARTBEAT.md跑通的接入步骤
3.1 初始化阶段:让Qclaw知道这份文件的存在
光把文件放到项目根目录是没用的,你必须在Qclaw的workspace context里显式声明它的存在。这步很多人忽略,结果文件放了好几天,Qclaw压根没看过一眼。
Qclaw的workspace context配置,本质上是“启动指令集”。你可以在配置里加上类似这样的声明:
code复制项目上下文:
- 项目根目录存在 HEARTBEAT.md,这是项目的心跳文件。
- 每次任务启动前,必须先读取 HEARTBEAT.md(如果存在)。
- HEARTBEAT.md 中所有规则均为最高优先级,必须严格遵循。
- 任务执行中若发现规则不适用,不得自行改变规则,需向用户报告并请示。
这几行声明的好坏,直接决定后续所有行为。我在第一版配置里写的是“参考HEARTBEAT.md”,结果Qclaw真的就只是“参考”了一下,然后按自己的理解干活。改成“必须先读取”和“必须严格遵循”之后,行为才真正扭转过来。这跟大模型的提示词敏感度有关,措辞强度直接关联指令权重,弱指令被无视是常态。
3.2 约定“读取-遵循-更新”的强制循环
接入HEARTBEAT.md的第二步,是建立闭环。很多人的失败在于只把HEARTBEAT.md当成“读入规则”,但忘了要求模型“更新文件”。如果你不在workspace context里做强制约定,Qclaw几乎不会主动回写文件——它完成任务就收工了,不会想着“哦我应该把进度写到那个心跳文件里”。
所以我在workspace context里额外加了两条约定,把文件生命周期盘活:
code复制- 每个可交付阶段完成后,必须更新 HEARTBEAT.md 中的任务进度区,将已完成项标记为 [x],并追加本次关键操作摘要。
- 若本次任务与 HEARTBEAT.md 中任何规则冲突,必须在"已知问题与偏差记录"区追加冲突说明,再决定是否继续。
这两条放进去之后,HEARTBEAT.md才真正从“静态规则文件”变成了“活体状态文件”。每个任务跑完,它都会被刷新一遍。下一次会话开始时,Qclaw读到的不是一个旧快照,而是一个刚刚同步过的状态。
这个闭环非常重要,我甚至可以说,它是整个机制的灵魂。没有闭环,HEARTBEAT.md就是一张写了字的废纸;有了闭环,它才成为团队协作、人机协作的中枢。
3.3 本地部署Qclaw时文件路径与模型参数的处理
既然热搜里也有人在问“本地的qclaw怎么部署”,这里多聊几句本地部署场景下HEARTBEAT.md需要注意的差异。
本地部署Qclaw时,最常见的问题不是功能不支持,而是工作目录的路径不统一。有的人跑在项目根目录,有的人跑在子目录,还有人直接用docker起容器,把项目目录挂载到容器里的固定路径。HEARTBEAT.md的读取规则是相对路径还是绝对路径,在本地环境里差别很大。
我建议在workspace context里明确写清楚“项目内相对路径”,而不是依赖绝对路径。例如:
code复制- HEARTBEAT.md 位于工作区根目录,使用相对路径 ./HEARTBEAT.md 读取。
- 工作区扫描范围仅限当前目录,不递归读取子目录中的同名文件。
为什么这么写?因为本地部署时模型的工作目录一旦切换,绝对路径就会失效,而相对路径的一致性显然更高。另外,有些复杂项目里子目录也塞了同名文件,Qclaw偶尔会误读,明确“仅限根目录”能规避这层风险。
另一个本地部署特有的坑是模型上下文长度的限制。本地跑的模型往往比在线版参数量小,上下文窗口也没那么大。如果HEARTBEAT.md写得太长,动辄上千行,模型读文件就会占掉大量上下文预算,留给实际编码推理的空间就少了。对应策略是严格控制文件长度,状态区不超过100字,指令区不超过10条,任务进度区只保留最近20条,旧记录滚动清理。
4. 实测中最容易翻车的三种失败模式与修正
4.1 失败模式一:文件写得像论文,代理抓不住重点
我见过不少队友写的HEARTBEAT.md,开头要先讲一整段项目的起源、背景、愿景,洋洋洒洒三五百字。这种写法在给人看时很友好,但给模型看时就是灾难。
Qclaw读取HEARTBEAT.md跟人不一样,它不会“通读全文抓住主旨”,而是更依赖Markdown的标题层级和关键词来分配注意力。一段背景故事写得再动情,也只会占据上下文空间,并且可能诱导模型把“背景描述”误判成“任务目标”。
修正方法很直接:把说明书和操作手册分开。真正需要模型知道的内容,控制在200字以内,全部用短句和列表。要讲故事,请写到README.md里,别混进HEARTBEAT.md。
我自己的经验是,HEARTBEAT.md的字数越少,被遵循的概率越高。规则从10条精简到5条,执行偏差率能下降一半——这不是玄学,是模型注意力资源有限,写得越精简,每条规则的相对权重就越高。
4.2 失败模式二:更新时机没有约束,读到一份过期状态
这个问题比第一个隐蔽得多,但后果也更严重。因为我早期的配置里只说了“读取”和“遵循”,没约定“更新时机”,导致HEARTBEAT.md经常停留在几天前的状态。Qclaw拿到过期信息,会信心满满地按旧规则操作,然后在已经重构掉的文件上做无谓的修改。
典型场景:你昨天刚从单体仓库拆出了独立模块,今天Qclaw还对着旧路径发请求,它能不报错吗?等它报错了,你一看HEARTBEAT.md,最后更新还是三天前,连新模块的名字都没出现。
要根治这个问题,我的做法是在workspace context里增加一条“生命周期约定”:
code复制- 当前任务的任何实质性进展(新文件创建、核心函数修改、模块拆分)落地后,必须立即将摘要追加到 HEARTBEAT.md 的任务进度区。
- 若任务中断,必须将中断时的现场状态记录到状态快照区,保障下次会话可无缝恢复。
执行上也不用真的“每条都实时写”,而是让Qclaw每次完成一个内部子步骤后就把变更同步一次。这样虽然多了几次文件写入,换来的是每次会话读到的状态基本保真。
4.3 失败模式三:把HEARTBEAT.md当成万能记忆库,什么信息都往里塞
第三个坑,是过度依赖。有位同事恨不得把每次对话的完整纪要都贴进HEARTBEAT.md,最后那份文件膨胀到快两千行。到了这种体量,Qclaw读起来吃力,写起来也吃力,而且大量琐碎信息会稀释真正指令的权重——模型分不清哪些是规则、哪些是闲聊记录。
这就好比一个人每天都写几十页日记,但从来不标重点,到了执行任务时反而找不着他昨天写的三件紧要事。
解决方式是把信息分层。具体到我现在的实践,是有三类信息分工:
- HEARTBEAT.md:只放状态、规则、进度、偏差,核心使命是支撑“下一步行动”。
- docs/decision.md:放重要的技术决策记录,为什么这么选、备选方案是什么。
- docs/CHANGELOG.md:放按时间排序的变更日志。
Qclaw的workspace context里我明确写:HEARTBEAT.md是行动依据,其他文档是背景参考。这样模型不会再把一堆背景信息当指令来执行,也不会因为HEARTBEAT.md过长而丢失关键规则。
5. 从“单文件心跳”到多文件状态机的进阶玩法
5.1 多项目、多分支场景下的HEARTBEAT归并策略
如果只是单项目、单分支,一个HEARTBEAT.md完全够用。但当你同时在维护两三个项目,或者一个项目里并行开发多个feature时,单一文件就有点捉襟见肘了。
我的做法是按分支拆文件,再在根目录的HEARTBEAT.md里留一个索引。例如:
text复制./HEARTBEAT.md
./heartbeats/HEARTBEAT-main.md
./heartbeats/HEARTBEAT-feature-payment.md
./heartbeats/HEARTBEAT-refactor-utils.md
根目录的HEARTBEAT.md只放一条规则:先判断当前分支名,然后读取对应分支的heartbeat文件。workspace context里也同步调整,让Qclaw先看根文件,再决定去读哪个分支文件。
这样做的收益是:切换分支时,不需要清理上一分支的状态残留。Qclaw读到feature-payment分支的HEARTBEAT时,只会看到一个聚焦在支付模块的状态快照,不会被主分支的几十条历史进度干扰。
5.2 自动摘要与文件轮换:避免HEARTBEAT.md无限膨胀
文件越用越长是必然趋势,即使我前面说“只留最近20条进度”,任务量大了还是会撑不住。进阶玩法是给HEARTBEAT.md加“轮换机制”——不是等它爆炸了再手动截断,而是主动归档旧内容、合并重复信息。
具体做法是每完成一个里程碑,就触发一次归档操作。把HEARTBEAT.md里“任务进度”区中已完成的项目浓缩成一条“里程碑概述”,挪到docs/CHANGELOG.md里,并清空“已知问题与偏差记录”区中已经解决掉的项。这样HEARTBEAT.md永远保持轻盈。
我在workspace context里会写一条轮换规则,让Qclaw自动执行:
code复制- 每次会话结束前,若任务进度区条目超过20条,执行归档:将已完成条目压缩为一行摘要,追加至 docs/CHANGELOG.md,并从 HEARTBEAT.md 移除。
这个规则跑起来很顺,基本可以做到人工零干预。长跑几个月的项目,HEARTBEAT.md的体重依然能稳定控制在几十行。
5.3 多模型切换时的心跳适配
最后分享一个比较新的场景:如果你本地部署了Qclaw,并且会在不同模型之间切换(比如日常用轻量模型跑快速任务,复杂重构时切到更强的模型),那HEARTBEAT.md需要考虑不同模型的遵循能力差异。
轻量模型上下文小,指令遵循能力弱,HEARTBEAT.md就必须极其精简、直白,用词不能绕弯。强模型则可以承受稍复杂一点的规则表达,比如带条件判断的规则“如果xxx,则yyy”。
我的做法是在HEARTBEAT.md开头加一段“模型适配提示”:
markdown复制## 模型适配提示
- 当前推荐模型: [模型名]
- 阅读本文件时,请特别注意"硬性指令"区的第1、3、5条,它们是本次项目的高风险约束。
这个提示本身不给模型加新规则,只是把“哪些规则最容易违反”高亮出来,让模型在行动前多一分注意。
我实测下来,加了这么一小段之后,轻量模型对核心规则的遵循率提升明显,那种“全部规则都看了但高频规则还是漏掉”的情况少多了。
回到最初那条线——Read HEARTBEAT.md if it exists (workspace context). Follow it strictly。它看起来只是句配置说明,但实际上是把AI代理从“有问必答的对话工具”变成“有状态、有记忆、有规矩的虚拟协作者”的关键开关。我用了大半年,最深的感受是:模型本身的能力当然重要,但怎么给它搭建一套“工作规范”,往往才是项目能不能顺利推进的分水岭。如果你也在用Qclaw跑正经项目,别犹豫,花二十分钟搭一个HEARTBEAT.md,回来你会上瘾的。
