心跳系统设计文档¶
系统内部定期执行的维护任务:对长期记忆(facts)做去重合并,并运行用户定义的周期性指令(heartbeat.md)。
与 Scheduler 的区别¶
| 维度 | Scheduler (APScheduler) | Heartbeat |
|---|---|---|
| 创建方式 | 用户通过对话或 CLI 主动创建 | 系统自动运行,不暴露给用户管理 |
| 任务内容 | 任意用户业务任务(提醒、报告等) | 系统维护:facts 去重 + heartbeat.md |
| 配置项 | job_id / cron / interval_minutes | config.defaults.heartbeat |
| 持久化 | ~/.ethan/scheduler.db |
不持久化,每次随服务启动 |
| 可见性 | Web UI /schedule 页面可见 |
不在调度器页面显示 |
简单说:Scheduler 是给用户用的,Heartbeat 是系统自己的看门狗。
架构¶
文件:ethan/core/heartbeat.py

两个维护动作¶
1. Facts 去重(_consolidate_facts)¶
触发条件:facts.json 中活跃 fact 数量 ≥ 10。
流程:
1. 读取所有活跃 facts
2. 用廉价模型(Haiku / Flash Lite)对其做合并、去重、矛盾修正
3. 将原有 facts 全部标记为 superseded
4. 写入整理后的新 facts(置信度 0.85,source="heartbeat_consolidation")
廉价模型选取逻辑与 Consolidator 相同(见 memory.md)。
2. heartbeat.md 任务(_run_heartbeat_md)¶
触发条件:~/.ethan/system/heartbeat.md 文件存在且内容非空。
流程:
1. 读取 heartbeat.md 完整内容作为 prompt(前缀 [Heartbeat] 正在执行系统心跳任务:heartbeat.md)
2. 启动一个完整 Agent 实例(加载全量工具和 Skills)
3. 用 stream_chat() 执行,完整记录工具步骤 / 思考过程 / token usage
4. 每次心跳创建一个独立的 [心跳] <时间戳> Session(如 [心跳] 2026-06-21 12:33),便于在 Web 会话列表里独立查看每次心跳的执行过程
5. 仅当 heartbeat.md 有实质内容时才执行(空文件不产生无意义 Session)
heartbeat.md 是一个普通 Markdown 文件,所有内容都作为 prompt 发给 Agent,包括标题行。示例:
配置¶
在 ~/.ethan/config.yaml 中:
也可在 Web UI 的「设置」页面修改。
启动与停止¶
心跳在 ethan serve(FastAPI 服务)启动时通过 start_heartbeat() 创建后台 asyncio task。服务关闭时调用 stop_heartbeat() 取消 task。
CLI 模式下不启动心跳(心跳更适合长期运行的服务端场景)。
数据流¶
