跳转至

心跳系统设计文档

系统内部定期执行的维护任务:对长期记忆(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 中:

defaults:
  heartbeat:
    enabled: true
    interval_minutes: 10   # 每 10 分钟执行一次

也可在 Web UI 的「设置」页面修改。


启动与停止

心跳在 ethan serve(FastAPI 服务)启动时通过 start_heartbeat() 创建后台 asyncio task。服务关闭时调用 stop_heartbeat() 取消 task。

CLI 模式下不启动心跳(心跳更适合长期运行的服务端场景)。


数据流

心跳数据流