Cron 任务自动化:定时内容生成与部署
TL;DR: This tutorial transforms your Hermes Agent setup from a manual workflow tool into an autonomous content factory. You’ll learn to configure cron-driven pipelines that generate, review, and deploy technical content at scheduled intervals, complete with monitoring, error recovery, and Slack notifications. By the end, your agent will publish daily blog posts without human intervention.
Introduction
In Parts 1-3, you’ve learned to install Hermes Agent, orchestrate multi-role workflows, and build reusable skill libraries. Now it’s time to eliminate the last manual bottleneck: scheduling.
The problem is universal: you’ve built a powerful AI workflow that generates excellent content, but someone still needs to click “run” every morning. For DevOps engineers managing multiple content streams—daily digests, weekly roundups, monthly reports—this manual trigger becomes a productivity sink.
Hermes Agent’s cron integration solves this by treating scheduled tasks as first-class citizens in your automation pipeline. Unlike basic crontab wrappers, Hermes provides:
- Stateful execution tracking with automatic retry on failure
- Conditional workflows that adapt based on previous run outputs
- Integrated monitoring via Slack, email, or webhook
- Resource-aware scheduling that prevents overlapping runs
By the end of this 3000-word deep dive, you’ll have a production-ready cron pipeline that:
- Scrapes trending tech topics from Hacker News and ArXiv
- Generates a blog post using your multi-role workflow from Part 2
- Runs quality checks using your custom skills from Part 3
- Deploys to GitHub Pages or Netlify
- Sends a performance report to your team Slack channel
Prerequisites & Recap
Before diving in, ensure you have:
- Hermes Agent v2.4+ installed (
hermes --versionshould return ≥2.4.0) - Completed Part 3’s skill creation tutorial
- A GitHub personal access token with repo write access
- A Slack webhook URL (optional but recommended)
Quick Recap: In Part 3, you built a content-reviewer skill that validates article quality against 5 metrics: readability score, fact-checking, SEO optimization, code snippet accuracy, and tone consistency. We’ll reuse that skill here as a cron-triggered quality gate.
Understanding Hermes Agent’s Cron Architecture
Hermes doesn’t just wrap system cron—it provides a scheduling layer on top of its workflow engine. The architecture consists of three components:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Cron Scheduler │────▶│ Workflow Engine │────▶│ State Manager │
│ (Go-based) │ │ (Rust runtime) │ │ (SQLite-backed) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ System Cron │ │ Skill Executor│ │ Event Logger │
│ Integration │ │ (Sandboxed) │ │ (JSON Lines) │
└──────────────┘ └──────────────┘ └──────────────┘
Key differences from raw cron:
- Idempotency: Each run gets a unique
run_id. If a workflow fails mid-execution, Hermes can resume from the last successful checkpoint rather than restarting. - Resource Limits: Configure max memory (default 512MB) and timeout (default 30min) per cron job, preventing runaway processes.
- Dependency Graph: Cron jobs can depend on other jobs. Job B won’t start until Job A completes successfully.
- Time Zone Aware: Specify timezone per job, critical for global teams.
Setting Up Your First Cron Job
Let’s start with a minimal example: a daily content generation workflow that runs at 6 AM UTC.
Step 1: Create the Cron Configuration
Create ~/.hermes/cron.yaml:
# ~/.hermes/cron.yaml
# Hermes Agent cron configuration
# Syntax: https://docs.hermes.dev/cron/v2
cron:
# Global settings applied to all jobs
global:
timezone: "UTC"
max_concurrent: 2
retry:
max_attempts: 3
backoff: "exponential" # 1min, 2min, 4min, etc.
jobs:
- name: "daily-blog-generator"
description: "Generate and deploy daily tech blog post"
# Standard cron expression: minute hour day month weekday
schedule: "0 6 * * *" # Every day at 06:00 UTC
# Workflow to execute (from Part 2's multi-role setup)
workflow: "content-pipeline"
# Environment variables specific to this job
env:
CONTENT_TOPIC: "trending-tech"
OUTPUT_DIR: "/tmp/hermes-blog-output"
DEPLOY_TARGET: "github-pages"
# Resource limits
resources:
timeout: "45m" # 45 minutes max
memory: "1GB"
# Notification on completion
notifications:
on_success:
slack:
webhook: "${SLACK_WEBHOOK_URL}"
message: "✅ Daily blog post generated and deployed"
on_failure:
slack:
webhook: "${SLACK_WEBHOOK_URL}"
message: "❌ Blog generation failed: {{ .Error }}"
email:
to: "[email protected]"
subject: "CRITICAL: Hermes cron job failure"
Step 2: Define the Workflow
Create ~/.hermes/workflows/content-pipeline.yaml:
# ~/.hermes/workflows/content-pipeline.yaml
version: "2.4"
name: "content-pipeline"
steps:
- id: "fetch-trending"
skill: "web-scraper"
params:
sources:
- "https://news.ycombinator.com/best"
- "https://arxiv.org/list/cs.AI/recent"
max_items: 10
filter:
min_points: 50 # Hacker News minimum points
max_age_hours: 24
- id: "select-topic"
skill: "topic-selector"
params:
input: "${steps.fetch-trending.output}"
criteria:
- relevance_to_devops
- novelty_score
- controversy_level
output_count: 1
- id: "generate-content"
skill: "multi-role-writer" # From Part 2
params:
topic: "${steps.select-topic.output}"
roles:
- "tech-writer"
- "code-reviewer"
- "seo-specialist"
min_word_count: 1500
include_code_examples: true
- id: "quality-check"
skill: "content-reviewer" # From Part 3
params:
content: "${steps.generate-content.output}"
thresholds:
readability: 60 # Flesch-Kincaid minimum
fact_accuracy: 0.9
seo_score: 75
auto_fix: true
- id: "build-static-site"
skill: "static-site-builder"
params:
content: "${steps.quality-check.output}"
template: "blog-post"
output_dir: "${env.OUTPUT_DIR}"
- id: "deploy-to-github"
skill: "git-deployer"
params:
repo: "[email protected]:username/blog.git"
branch: "main"
source_dir: "${env.OUTPUT_DIR}"
commit_message: "Auto-generated blog post: {{ .Date }}"
push: true
Step 3: Register and Test the Cron Job
# Validate the cron configuration
hermes cron validate ~/.hermes/cron.yaml
# Output: ✅ Cron configuration valid. 1 job(s) defined.
# Register the cron job with the Hermes daemon
hermes cron register --file ~/.hermes/cron.yaml
# List all registered cron jobs
hermes cron list
# Output:
# ┌──────────────────────┬─────────────┬──────────┬────────────┐
# │ Job Name │ Schedule │ Status │ Next Run │
# ├──────────────────────┼─────────────┼──────────┼────────────┤
# │ daily-blog-generator │ 0 6 * * * │ active │ 2026-07-04 │
# └──────────────────────┴─────────────┴──────────┴────────────┘
# Test run immediately (bypasses schedule)
hermes cron run daily-blog-generator --now
# Watch real-time execution
hermes cron logs daily-blog-generator --follow
The --now flag triggers an immediate execution, useful for testing. You’ll see output like:
[2026-07-03T14:30:01Z] Starting job: daily-blog-generator (run_id: abc123)
[2026-07-03T14:30:02Z] Step 1/5: fetch-trending → 10 items fetched
[2026-07-03T14:30:05Z] Step 2/5: select-topic → Selected: "Hermes Agent vs AutoGPT: A 2026 Comparison"
[2026-07-03T14:30:45Z] Step 3/5: generate-content → 1,847 words generated
[2026-07-03T14:31:02Z] Step 4/5: quality-check → Score: 82/100 (pass)
[2026-07-03T14:31:30Z] Step 5/5: deploy-to-github → Pushed to main@username/blog
[2026-07-03T14:31:31Z] Job completed successfully (duration: 1m30s)
Advanced Scheduling Patterns
Pattern 1: Conditional Execution Based on Previous Runs
Sometimes you don’t want to generate content every day—only when there’s enough trending material. Use state-aware scheduling:
# ~/.hermes/cron.yaml (excerpt)
jobs:
- name: "smart-blog-generator"
schedule: "0 6 * * *"
workflow: "content-pipeline"
# Only run if yesterday's fetch had ≥5 trending items
condition:
type: "previous_run_output"
job: "daily-trending-fetcher"
check: "output.items_count >= 5"
max_age_hours: 24
# If condition fails, skip and notify
on_skip:
slack:
message: "⏭️ Skipping blog generation: insufficient trending content"
Pattern 2: Multi-Phase Deployment Pipeline
For production deployments, use dependency chains:
jobs:
- name: "staging-deploy"
schedule: "0 8 * * 1-5" # Mon-Fri at 8 AM
workflow: "deploy-to-staging"
- name: "run-smoke-tests"
schedule: "30 8 * * 1-5" # 30 min after staging
depends_on: ["staging-deploy"]
workflow: "smoke-test-suite"
- name: "production-deploy"
schedule: "0 9 * * 1-5" # 1 hour after staging
depends_on: ["run-smoke-tests"]
condition:
type: "previous_run_output"
job: "run-smoke-tests"
check: "output.passed == true"
workflow: "deploy-to-production"
Pattern 3: Timezone-Aware Content Scheduling
For global audiences, schedule content for optimal reading times:
jobs:
- name: "morning-asia-pacific"
schedule: "0 22 * * *" # 10 PM UTC = 6 AM Singapore
timezone: "Asia/Singapore"
workflow: "content-pipeline"
env:
TARGET_AUDIENCE: "APAC"
CONTENT_TONE: "professional"
- name: "evening-north-america"
schedule: "0 12 * * *" # 12 PM UTC = 8 AM EST
timezone: "America/New_York"
workflow: "content-pipeline"
env:
TARGET_AUDIENCE: "NA"
CONTENT_TONE: "casual"
Monitoring and Observability
Real-Time Dashboard
Hermes provides a built-in monitoring endpoint:
# Start the monitoring server
hermes cron monitor --port 9090 --bind 0.0.0.0
# Access via browser: http://localhost:9090
# Or query via API:
curl http://localhost:9090/api/v1/jobs/daily-blog-generator/status
Response:
{
"job_name": "daily-blog-generator",
"status": "running",
"current_step": "generate-content",
"progress": 0.6,
"started_at": "2026-07-03T06:00:01Z",
"estimated_completion": "2026-07-03T06:45:00Z",
"memory_usage_mb": 342,
"run_count": 127,
"success_rate": 0.94,
"average_duration_seconds": 2700
}
Prometheus Metrics Integration
For Grafana dashboards, enable Prometheus metrics:
# ~/.hermes/config.yaml
monitoring:
prometheus:
enabled: true
port: 9091
metrics:
- hermes_cron_job_duration_seconds
- hermes_cron_job_success_total
- hermes_cron_job_failure_total
- hermes_cron_job_skipped_total
Slack Alerting with Context
Enhance your Slack notifications with run metrics:
notifications:
on_failure:
slack:
webhook: "${SLACK_WEBHOOK_URL}"
template: |
🚨 *Hermes Cron Failure*
*Job:* `{{ .JobName }}`
*Run ID:* `{{ .RunID }}`
*Failed Step:* `{{ .FailedStep }}`
*Error:* `{{ .Error }}`
*Duration:* {{ .Duration }}
*Memory:* {{ .MemoryUsage }}MB
*Logs:* {{ .LogsURL }}
Common Pitfalls and How to Avoid Them
Pitfall 1: Cron Job Overlap
Problem: Long-running jobs overlap with the next scheduled run, causing resource contention.
Solution: Enable the skip_on_overlap flag:
jobs:
- name: "long-running-job"
schedule: "0 */2 * * *" # Every 2 hours
workflow: "heavy-computation"
skip_on_overlap: true # Skip if previous run still active
max_overlap: 0 # Zero tolerance for overlap
Pitfall 2: Environment Variable Leakage
Problem: Hardcoding API keys in cron config files that get committed to version control.
Solution: Use Hermes’ secrets manager:
# Store secrets securely
hermes secrets set GITHUB_TOKEN "ghp_xxxxxxxxxxxx"
hermes secrets set OPENAI_API_KEY "sk-xxxxxxxxxxxx"
# Reference in cron config (never store plaintext)
env:
GITHUB_TOKEN: "${secrets.GITHUB_TOKEN}"
OPENAI_API_KEY: "${secrets.OPENAI_API_KEY}"
Pitfall 3: Network Timeouts in Remote Skills
Problem: Skills that fetch external data timeout during peak hours.
Solution: Implement retry with backoff at the skill level:
# In your skill definition
steps:
- id: "fetch-data"
skill: "http-client"
params:
url: "https://api.example.com/data"
timeout: "30s"
retry:
max_attempts: 5
backoff: "linear"
base_delay: "5s"
Pitfall 4: Disk Space Exhaustion
Problem: Generated content accumulates in /tmp/ without cleanup.
Solution: Configure automatic cleanup:
# ~/.hermes/config.yaml
cron:
cleanup:
enabled: true
max_age_hours: 72
paths:
- "/tmp/hermes-*"
- "${OUTPUT_DIR}/archive/*"
dry_run: false # Set to true first to test
Performance Optimization
Parallel Execution Tuning
For multiple cron jobs, tune the worker pool:
# ~/.hermes/config.yaml
cron:
workers:
max: 4 # Maximum concurrent job executions
queue_size: 10 # Jobs waiting in queue
priority: "round-robin" # Fair scheduling
Caching Frequently Used Data
Reduce API calls by caching trending topics:
# In your workflow
steps:
- id: "fetch-trending"
skill: "web-scraper"
cache:
ttl: "6h" # Cache results for 6 hours
key: "trending-topics-{{ .Date | dateFormat \"2006-01-02\" }}"
storage: "redis" # Requires Redis connection
Production Deployment Checklist
Before deploying to production:
- Test all cron jobs with
--nowflag - Verify Slack/email notifications work
- Set up Prometheus/Grafana dashboards
- Configure log rotation (
hermes cron logs --rotate daily) - Add resource limits to prevent runaway jobs
- Set up dead letter queue for failed jobs
- Document recovery procedures
- Test systemd service auto-restart:
# Create systemd service for Hermes daemon
sudo tee /etc/systemd/system/hermes-cron.service <<EOF
[Unit]
Description=Hermes Agent Cron Daemon
After=network.target
[Service]
Type=simple
User=hermes
ExecStart=/usr/local/bin/hermes cron daemon
Restart=always
RestartSec=10
MemoryMax=2G
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable hermes-cron
sudo systemctl start hermes-cron
Key Takeaways
- Hermes cron is stateful—it tracks execution progress, supports conditional runs, and provides automatic retry with exponential backoff.
- Dependency chains enable complex deployment pipelines where each stage waits for the previous to succeed.
- Monitoring is built-in with Prometheus metrics, Slack alerts, and a real-time dashboard—no third-party tools required.
- Resource limits prevent cascading failures—set memory caps, timeouts, and overlap prevention for production reliability.
- Secrets management is critical—use
hermes secrets setinstead of hardcoding credentials.
FAQ
Q: Can I run Hermes cron jobs on a schedule shorter than 1 minute?
A: Yes, but with caveats. Hermes supports cron expressions down to * * * * * (every minute). For sub-minute intervals, use the every syntax: every: 30s. However, jobs running faster than 60 seconds may experience overlap if not properly configured with skip_on_overlap: true.
Q: How do I handle daylight saving time changes?
A: Hermes uses the IANA timezone database. Jobs scheduled in timezones that observe DST will automatically adjust. For critical jobs, consider using UTC to avoid ambiguity. The timezone field accepts any valid IANA timezone (e.g., America/Chicago, Europe/London).
Q: What happens if the Hermes daemon crashes mid-execution?
A: The SQLite-backed state manager records each step’s completion. On restart, Hermes checks for incomplete runs and can resume from the last successful checkpoint. Enable auto_resume: true in your job config for this behavior. Note: non-idempotent steps (like sending emails) should be wrapped in a transaction.
Q: Can I trigger cron jobs manually without waiting for the schedule?
A: Yes, use hermes cron run <job-name> --now. This bypasses the schedule and runs immediately. The --force flag overrides skip_on_overlap checks. Manual runs are logged separately from scheduled runs in the monitoring dashboard.
Q: How do I debug a cron job that fails intermittently?
A: Enable verbose logging: hermes cron logs <job-name> --follow --verbose. For network-dependent jobs, add the --trace flag to capture HTTP request/response details. Use hermes cron replay <job-name> --run-id <id> to re-execute a specific failed run with the same inputs.
Next Steps
In Part 5, we’ll tackle Error Recovery and Self-Healing Workflows. You’ll learn to:
- Implement circuit breakers for failing API calls
- Create fallback content sources when primary sources fail
- Build automatic rollback mechanisms for failed deployments
- Set up alert escalation policies for critical failures
The skills you’ve built here—scheduling, monitoring, and conditional execution—form the foundation for autonomous, self-healing systems. Part 5 will push your Hermes Agent setup from “automated” to “autonomous.”
This tutorial is part of the Hermes Agent Mastery Series. Part 1: Installation & First Workflow | Part 2: Multi-Role Collaboration | Part 3: Skill System Deep Dive | Part 4: Cron Automation | Part 5: Error Recovery (Coming Soon) | Part 6: Production Deployment (Coming Soon)
Have questions? Join our Discord community or follow us on X.