跳转到内容

06 - 调度器集成与热重载机制

学习目标

理解 CowAgent 如何集成 Cron 定时任务引擎,以及 MCP、技能、配置的热重载机制。

项目实践

调度器集成

CowAgent 使用 croniter 库解析 Cron 表达式驱动定时任务:

关键设计:

  • 模块级锁_scheduler_init_lock 防止并发 session 初始化时重复创建 SchedulerService
  • 隔离 session:每个定时任务使用 scheduler_ 前缀的独立 session 执行,不污染用户对话

调度器任务执行流程

防递归设计:当 is_scheduled_task=True 时,scheduler 工具被排除,防止调度任务内部再次调用 scheduler 创建新任务导致无限递归。

MCP 热重载

MCP 配置变更检测使用文件签名(mtime + sha256):

def _read_mcp_json_signature(self):
"""返回 (mtime, sha256_of_bytes) 元组"""
mtime = os.path.getmtime(path)
with open(path, "rb") as f:
digest = hashlib.sha256(f.read()).hexdigest()
return (mtime, digest)
def refresh_mcp_if_changed(self):
new_sig = self._read_mcp_json_signature()
if new_sig == self._mcp_signature:
return # 快速路径:未变更
# 解析新配置,对比旧配置
added = [n for n in new_by_name if n not in old_by_name]
removed = [n for n in old_by_name if n not in new_by_name]
changed = [n for n in new_by_name if n in old_by_name and new_by_name[n] != old_by_name[n]]
# 关闭已移除/编辑的,启动新增/编辑的

技能热刷新

每次构建系统提示时调用 skill_manager.refresh_skills()

def refresh_skills(self):
self.skills = self.loader.load_all_skills(...) # 重新扫描目录
self._sync_skills_config() # 同步配置

技能的热刷新比 MCP 简单——只需重新扫描目录,不涉及后台进程。

配置热重载

EnvConfig 工具管理 API Key 和技能的动态配置:

def _refresh_conditional_tools(agent):
# 重新加载 .env 文件
load_dotenv(env_file, override=True)
# 刷新所有 Agent 实例的技能和条件工具
agent.skill_manager.refresh_skills()
# 重新评估条件工具(如 web_search 依赖 API Key)
if WebSearch.is_available() and not has_tool:
agent.tools.append(WebSearch()) # 注入
elif not WebSearch.is_available() and has_tool:
agent.tools = [t for t in agent.tools if t.name != "web_search"] # 移除

问题与规避

问题CowAgent 的规避策略
调度任务递归创建新任务执行时排除 scheduler 工具
调度任务 session 内存膨胀更小的恢复窗口 + 滑动窗口修剪
并发初始化重复创建 SchedulerService模块级锁 + 双重检查
MCP 文件签名变更但内容不变Diff 对比配置 dict,逻辑不变则不重载
.env 修改后技能/工具未生效EnvConfig 工具触发全局刷新
调度任务结果注入用户 session 膨胀scheduler_inject_max_per_session(默认 3 条)滑动窗口

设计取舍

为什么调度器使用 croniter 而非 APScheduler?

croniter 是轻量级的 Cron 表达式解析库,无额外依赖。APScheduler 功能更丰富但引入重量依赖。CowAgent 定位为轻量部署,选择 croniter + 自定义扫描线程的方案足够满足需求。

为什么 MCP 重载使用 diff 而非全量重启?

diff 式重载确保未变更的 MCP 服务器不中断。如果用户编辑了 mcp.json 中的某个服务器配置,全量重启会导致所有服务器(包括未修改的)都断开重连。diff 式重载只影响变更的服务器。

参考来源