Built-in — no extra dependencies required. Schedule tools are included in the core
praisonaiagents package.pre_run so expensive model turns only happen when there’s real work to do. No changes to the Agent class are needed.
Quick Start
The agent will callschedule_add with the appropriate schedule expression, and the job will be persisted to disk.
Available Tools
schedule_add
Add a new scheduled job.| Parameter | Type | Required | Description |
|---|---|---|---|
name | str | Yes | Human-readable name (e.g. "morning-email-check") |
schedule | str | Yes | When to run (see Schedule Expressions) |
message | str | No | Prompt or reminder text to deliver when triggered |
schedule_list
List all scheduled jobs. Takes no parameters. Returns: Formatted string listing every job with id, name, schedule, status, and message.schedule_remove
Remove a scheduled job by name.| Parameter | Type | Required | Description |
|---|---|---|---|
name | str | Yes | Name of the schedule to remove |
Schedule Expressions
| Format | Example | Description |
|---|---|---|
| Keyword | "hourly", "daily" | Predefined intervals |
| Interval | "*/30m", "*/6h", "*/10s" | Custom interval (minutes, hours, seconds) |
| Cron | "cron:0 7 * * *" | 5-field cron expression |
| One-shot | "at:2026-03-01T09:00:00" | ISO 8601 timestamp |
| Relative | "in 20 minutes" | Relative to now |
| Seconds | "3600" | Raw seconds |
Examples
Recurring Schedule
One-Shot Reminder
List and Manage
Using String Tool Names
Storage
Jobs are persisted to~/.praisonai/config.yaml under the schedules key by default via ConfigYamlScheduleStore. The store is:
- Thread-safe for multi-agent scenarios
- Atomic writes (tmp + rename) to prevent corruption
- Auto-created on first use
- Auto-migrates legacy
jobs.jsondata on first load
Custom Store (ScheduleStoreProtocol)
Swap the default file store for any backend that implementsScheduleStoreProtocol:
schedule_add/list/remove calls use your store:
PraisonAIUI and BotOS use the same
config.yaml store. You can also call set_store() to inject any custom backend.Schedule Runner
TheScheduleRunner checks which jobs are due for execution:
Hook Events
Schedule lifecycle events are available via the hook system:| Event | When |
|---|---|
SCHEDULE_ADD | A new schedule is created |
SCHEDULE_REMOVE | A schedule is deleted |
SCHEDULE_TRIGGER | A scheduled job fires |
Execution History
Every scheduled job execution is logged as aRunRecord for auditing:
| Field | Type | Description |
|---|---|---|
job_id | str | Job that was executed |
job_name | str | Human-readable job name |
status | str | "succeeded", "failed", or "skipped" (when a pre_run gate returned run=False) |
result | str | Agent output (truncated) |
error | str | Error message if failed |
duration | float | Execution time in seconds |
delivered | bool | Whether result was delivered to channel |
timestamp | float | Epoch timestamp |
Executing Scheduled Jobs
Schedule tools create and persist jobs, but to actually execute them when they’re due, useScheduleLoop:
See Background Tasks — ScheduleLoop for the full API and combined examples with
BackgroundRunner.Pre-Run Condition Gate
Gate a scheduled tick on a cheap shell check so no model tokens are spent when there’s nothing to do.Every tick, PraisonAI evaluates pre_run before spending tokens
| Exit code | stdout | Outcome |
|---|---|---|
0 | empty | Job runs with original message; no context added |
0 | non-empty | Job runs; stdout appended as context (capped at 8 000 chars) |
| non-zero | — | Job skipped; truncated stderr in reason (max 500 chars) |
| timed out (>30 s) | — | Job skipped; process group killed; reason: pre-run gate timed out |
pre_run is a cost gate (decides whether to run). It is not a safety gate (RunPolicy, which decides what a run may do). Use both when you need both.Real-World Examples
Only triage when new issues exist:Custom Condition Gate
Any object implementingJobConditionProtocol can replace the default shell gate — a Python callable, an MCP probe, a database check.
condition_resolver=False to disable gating entirely. The default resolver automatically activates ShellConditionGate for any job that has a pre_run value.
BotOS Integration
When using BotOS (multi-platform bot orchestrator), scheduled jobs execute automatically — noScheduleLoop needed. BotOS runs its own 30-second schedule tick alongside all bots:
- Agents create jobs via
schedule_addduring conversations - BotOS detects due jobs every 30 seconds
- The originating agent processes the job message
- Results are delivered back to the originating platform (Telegram, Discord, etc.)
Architecture
Schedule tools follow PraisonAI’s core principles:- Agent-centric — tools, not Agent parameters
- Lazy-loaded — zero import cost until used
- Protocol-driven —
ScheduleStoreProtocolmakes stores swappable - No Agent bloat — the
Agentclass is unchanged - Thread-safe — safe for multi-agent workflows
- Pluggable —
set_store()lets any backend replace the default file store
See Also
Background Tasks
Sync wrappers, ScheduleLoop, and combined recipes
Scheduler CLI
24/7 autonomous agent scheduling via CLI
Best Practices
Use cron expressions for precise schedules
Use cron expressions for precise schedules
Cron expressions give exact control over scheduling - prefer them for production use.
Log scheduled job execution
Log scheduled job execution
Add logging to scheduled agent tasks so you can verify they ran and diagnose failures.
Test with short intervals first
Test with short intervals first
Use 1-minute intervals during testing, then switch to production schedules before deployment.
Handle job failures gracefully
Handle job failures gracefully
Scheduled jobs should catch exceptions and report errors rather than silently failing.
Use pre_run to avoid paying for empty ticks
Use pre_run to avoid paying for empty ticks
If a schedule only has work when some external state changes (new emails, new PRs, a queue with pending rows), put the cheap check in
pre_run. Model tokens are spent only for ticks that actually have work to do.Related
Custom Tools
Build your own agent tools
Tools Overview
Browse PraisonAI tool documentation

