Heartbeat Tasks
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
Active Tasks
Completed
三个区块各司其职: - **`# Heartbeat Tasks`**:文件主标题,同时是空内容判定时被跳过的标题行(见下文 `_is_heartbeat_empty` 的过滤逻辑); - **`## Active Tasks`**:待执行的周期性任务列表,用户在此行下方添加任务条目; - **`## Completed`**:已完成任务归档区。模板注释明确给出两种维护方式——把已完成任务移动到该区,或直接删除。 ## 二、心跳服务的运行原理 ### 2.1 一次完整的心跳周期 HeartbeatService 的核心逻辑在 [bot/vikingbot/heartbeat/service.py](https://link.gitcode.com/i/ed0292dadc72c74d5a92e8a26285cc54) 中。服务启动后进入异步循环 `_run_loop`:每经过 `interval_s` 秒休眠,调用一次 `_tick()` 执行单轮巡检: ```python async def _run_loop(self) -> None: while self._running: try: await asyncio.sleep(self.interval_s) if self._running: await self._tick() except asyncio.CancelledError: break except Exception as e: logger.exception(f"Heartbeat error: {e}")_tick()是单轮巡检的核心,其流程可以概括为"收集工作区 → 读取任务清单 → 唤醒 Agent → 判定结果"四步:
- 通过
_get_all_workspaces()枚举所有可巡检的会话工作区; - 对每个工作区调用
_read_heartbeat_file()读取其中的HEARTBEAT.md; - 用
_is_heartbeat_empty()判定清单是否"无可执行内容",为空则跳过该工作区; - 对非空清单调用
on_heartbeat回调,把预置提示词与heartbeat元数据交给 Agent,最后用is_heartbeat_noop_response()判定返回值是否代表"无操作"。
2.2 预置提示词与 HEARTBEAT_OK 协议
每次唤醒 Agent 时,服务注入的固定提示词定义在源码常量HEARTBEAT_PROMPT中:
Read HEARTBEAT.md in your workspace (if it exists). Follow any instructions or tasks listed there. IMPORTANT: Use the 'message' tool to send any results or updates to the user. If nothing needs attention, reply with just: HEARTBEAT_OK这条协议规定了心跳轮次中 Agent 的两个行为出口:
- 有可汇报的更新:使用
message工具主动把结果发送给用户; - 无事可做:原样回复令牌
HEARTBEAT_OK。
服务端对回复做宽松匹配。normalize_heartbeat_response()会把响应内容去掉首尾空白、转大写、并剥离下划线与空格:
def normalize_heartbeat_response(content: str | None) -> str: if not content: return "" return content.strip().upper().replace("_", "").replace(" ", "")随后is_heartbeat_noop_response()将其与规范化后的HEARTBEAT_OK(HEARTBEATOK)比对。因此HEARTBEAT_OK、heartbeat_ok、HEARTBEAT OK等变体都会被识别为无操作响应,容错性较好。Agent 循环在 bot/vikingbot/agent/loop.py 中结合消息元数据里的heartbeat标记,跳过对这类无操作响应的常规记录,避免心跳轮次污染会话历史。
2.3 空文件判定逻辑
_is_heartbeat_empty()实现了对"无任务"的保守判定。它逐行扫描内容,跳过以下几类行后若仍无有效内容,即判定为空:
- 空行;
- 以
#开头的标题行; - 以
<!--开头的 HTML 注释行; - 空复选框条目:
- [ ]、* [ ]、- [x]、* [x]。
这意味着默认模板(只有标题、注释和空复选框)被视为"空清单",心跳会直接跳过该工作区,不会白白消耗一次模型调用。反过来,只要清单中出现任何一行实质任务文本,Agent 就会被唤醒。
三、配置文件与运行参数
3.1 HeartbeatConfig 配置项
心跳的启停与间隔由 bot/vikingbot/config/schema.py 中的HeartbeatConfig定义:
class HeartbeatConfig(BaseModel): """Heartbeat service configuration.""" enabled: bool = True interval_seconds: int = 10 * 60 # Default: 5 minutes对应到配置文件即bot.heartbeat.*前缀。根据 README_CN.md 的配置总表:
| 配置 | 默认值 | 说明 |
|---|---|---|
bot.heartbeat.enabled | true | 是否周期检查HEARTBEAT.md |
bot.heartbeat.interval_seconds | 600 | 心跳间隔 |
需要说明的是,配置 schema 层的默认值是 600 秒(10 分钟);而 heartbeat/service.py 中作为服务构造参数兜底的DEFAULT_HEARTBEAT_INTERVAL_S = 30 * 60(30 分钟)。实际生效值取决于装配时传入的配置——CLI 装配逻辑(见下)总是显式传入config.heartbeat.interval_seconds,因此线上行为以配置值为准,默认即 600 秒。
3.2 CLI 装配链路
心跳服务在 CLI 启动流程中被组装,见 bot/vikingbot/cli/commands.py 的prepare_heartbeat():
async def on_heartbeat( prompt: str, session_key: SessionKey | None = None, metadata: dict | None = None, ) -> str: return await agent_loop.process_direct( prompt, session_key=session_key, metadata=metadata, ) heartbeat = HeartbeatService( workspace=config.workspace_path, on_heartbeat=on_heartbeat, interval_s=config.heartbeat.interval_seconds, enabled=config.heartbeat.enabled, sandbox_mode=config.sandbox.mode, session_manager=session_manager, )关键点是on_heartbeat回调直接复用了 Agent 主循环的process_direct,因此心跳唤醒的本质上就是一次"定向注入提示词的 Agent 执行",与普通对话共用同一套工具与安全边界。启动时控制台会打印✓ Heartbeat: every {interval}s或禁用提示(commands.py)。
四、工作区枚举与跳过规则
4.1 会话与工作区的映射
_get_all_workspaces()通过session_manager.list_sessions()枚举会话,再根据沙箱模式解析每个会话对应的工作区路径:
shared模式:所有会话共享workspace / "shared"目录下的同一个 HEARTBEAT.md;- 其他模式:调用
resolve_workspace_path()(位于 bot/vikingbot/utils/session_paths.py)按会话维度解析独立工作区。
4.2 三类被跳过的会话
从_get_all_workspaces()与_is_session_stale()的实现可以归纳出三类不会触发心跳的会话:
- 显式禁用:会话元数据中带
skip_heartbeat: true则跳过。设置入口在 bot/vikingbot/session/manager.py 的get_or_create(key, skip_heartbeat=False);Agent 主循环在创建会话时对 CLI/直连会话默认开启跳过(loop.py 中session_key.type == "cli"); - 长期不活跃:会话的
updated_at/created_at距当前超过STALE_SESSION_THRESHOLD = timedelta(days=2)即视为失活(service.py); - 空任务清单:HEARTBEAT.md 不存在或
_is_heartbeat_empty()判定为空。
这类"三级跳过"保证了心跳只在确有任务、确有活跃用户的工作区上消耗模型预算。
五、Heartbeat 与 Cron 的边界
心跳机制常与定时任务混淆,TOOLS.md 与 02-agent-capabilities.md 均明确了两者的分工:
| 维度 | Cron | Heartbeat |
|---|---|---|
| 触发方式 | at(一次性)、every_seconds(固定间隔)、cron_expr(日历表达式) | 周期读取HEARTBEAT.md |
| 精度 | 精确时间点/间隔 | 尽力而为(best-effort),不适合精确到秒的提醒 |
| 适用场景 | 一次性提醒、固定时刻任务 | 持续巡检一组可能变化的任务 |
| 内容来源 | 调度时指定任务 | 工作区中的HEARTBEAT.md文件 |
同一份文档还给出了使用边界:只有用户明确要求"持续周期检查"时才应修改 HEARTBEAT.md;任务指令应当具体、安全、幂等,过时任务要及时移除而非永久悬挂。这正呼应了模板中"Move completed tasks here or delete them"的维护约定。
六、实战:编写你的心跳任务清单
在活动 Workspace(首次使用时从 bot/workspace 模板复制)中编辑 HEARTBEAT.md,把周期任务写入## Active Tasks区块。一个可直接套用的示例:
# Heartbeat Tasks ## Active Tasks - [ ] 检查工作区临时目录,删除超过 7 天的构建产物 - [ ] 汇总最近 24 小时来自各渠道的待办消息,通过 message 工具汇报 - [ ] 巡检 OpenViking 资源索引,标记失效资源 ## Completed <!-- Move completed tasks here or delete them --> - [x] 清理上周的过期日志(已完成,2026-09-01)【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考