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:

By the end of this 3000-word deep dive, you’ll have a production-ready cron pipeline that:

  1. Scrapes trending tech topics from Hacker News and ArXiv
  2. Generates a blog post using your multi-role workflow from Part 2
  3. Runs quality checks using your custom skills from Part 3
  4. Deploys to GitHub Pages or Netlify
  5. Sends a performance report to your team Slack channel

Prerequisites & Recap

Before diving in, ensure you have:

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:

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:

# 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

  1. Hermes cron is stateful—it tracks execution progress, supports conditional runs, and provides automatic retry with exponential backoff.
  2. Dependency chains enable complex deployment pipelines where each stage waits for the previous to succeed.
  3. Monitoring is built-in with Prometheus metrics, Slack alerts, and a real-time dashboard—no third-party tools required.
  4. Resource limits prevent cascading failures—set memory caps, timeouts, and overlap prevention for production reliability.
  5. Secrets management is critical—use hermes secrets set instead 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:

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.