Scheduled Tasks & Cron Scheduling Engine
โฐ 1. Role of Scheduled Tasks in the Project
To support periodic auto-inspection, daily report generation, and multi-platform heartbeat detection, Hermes includes a lightweight Cron scheduler. Code assets are centralized in the cron/ directory:
- cron/jobs.py: Task registrator. All automated tasks (e.g., "CEO Hourly Engine", "Strategy Calibration") define their scheduling parameters and execution periods here using standard 5-field Cron syntax (e.g.,
*/5 * * * *for every 5 minutes). - cron/scheduler.py: Background core daemon engine. On system startup, it maintains an infinite Tick polling heartbeat in a separate daemon thread.
๐ 2. Background Task & Frontend Interaction state.db Concurrency Conflict Avoidance
Since Cron tasks often run silently in the background, they may encounter **write conflicts** with frontend user queries from TUI or Feishu message gateway events. If both sides write to the session state table in state.db simultaneously, SQLite Busy Locked crashes can occur. The scheduler uses three lock-avoidance strategies:
- Application-Level Lock Detection: Before updating task execution status to
state.db, Cron attempts to acquire the globalcompression_locksor read the current write transaction. - Jitter Retry Noise Reduction: When a write conflict lock is encountered, the Cron thread automatically releases the Python-level lock and sleeps randomly between
20ms - 150msbefore retrying, spreading lock timing and reducing contention to near zero. - Priority Degradation: If conflicts are severe, Cron temporarily skips non-critical state write-back and directly pushes task results to the gateway, preventing log write-backs from rolling back the LLM's core task.
๐ฉน 3. Multi-Level Fault Tolerance & Log Persistence
Background scheduled tasks typically run unattended, requiring stringent robustness. cron/scheduler.py implements multi-level fault tolerance:
# cron/scheduler.py task scheduling fault tolerance logic
def run_job(self, job):
try:
# Step 1: Verify API connectivity and token budget
self.verify_model_connectivity()
# Step 2: Start task execution
job.execute()
# Step 3: Update success log
self.update_job_status(job.id, "SUCCESS")
except Exception as exc:
# Step 4: Catch exception, log detailed stack trace
logger.error(f"Job '{job.name}' failed: {exc}", exc_info=True)
# Persist error info to state.db
self.update_job_status(job.id, "FAILED", error_detail=str(exc))
# Step 5: Send emergency alert notification to admin gateway channel
self.broadcast_admin_alert(job.name, exc)
Through this self-healing mechanism, even if a Cron task crashes due to network timeout, the entire daemon engine maintains high availability and automatically rebuilds its heartbeat in the next Tick cycle.
๐ In-Depth Subtopic Analysis
For a deeper understanding of the Cron scheduler, we recommend the following technical deep-dives: