2026-02-13 19:06:28 -07:00
|
|
|
|
# Pulse & Brain Architecture
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
⭐ **The most efficient way to run an agent with proactive monitoring.**
|
|
|
|
|
|
|
|
|
|
|
|
> **Note:** The old Heartbeat system is now **legacy** and disabled by default. Use Pulse & Brain for all new deployments.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🚀 Quick Start
|
|
|
|
|
|
|
|
|
|
|
|
Copy-paste ready setup in under 50 lines:
|
|
|
|
|
|
|
|
|
|
|
|
### Basic Monitoring (Zero-cost pulse, conditional agent)
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from agent import Agent
|
|
|
|
|
|
from pulse_brain import PulseBrain
|
|
|
|
|
|
|
|
|
|
|
|
# Initialize agent (disable old heartbeat)
|
|
|
|
|
|
agent = Agent(provider="claude", enable_heartbeat=False)
|
|
|
|
|
|
|
|
|
|
|
|
# Create Pulse & Brain with 60-second pulse interval
|
|
|
|
|
|
pb = PulseBrain(agent, pulse_interval=60)
|
|
|
|
|
|
|
|
|
|
|
|
# Start monitoring
|
|
|
|
|
|
pb.start()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### With Slack/Telegram Integration
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from adapters.runtime import AdapterRuntime
|
|
|
|
|
|
from adapters.slack.adapter import SlackAdapter
|
|
|
|
|
|
from pulse_brain import PulseBrain
|
|
|
|
|
|
|
|
|
|
|
|
agent = Agent(provider="claude", enable_heartbeat=False)
|
|
|
|
|
|
|
|
|
|
|
|
# Set up messaging adapters
|
|
|
|
|
|
slack = SlackAdapter(bot_token="xoxb-...", channel="C_MONITORING")
|
|
|
|
|
|
runtime = AdapterRuntime(agent)
|
|
|
|
|
|
runtime.add_adapter(slack)
|
|
|
|
|
|
|
|
|
|
|
|
# Create Pulse & Brain with adapter support
|
|
|
|
|
|
pb = PulseBrain(agent, pulse_interval=60)
|
|
|
|
|
|
pb.add_adapter("slack", slack)
|
|
|
|
|
|
|
|
|
|
|
|
# Start both systems
|
|
|
|
|
|
await runtime.start()
|
|
|
|
|
|
pb.start()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Custom Pulse Check (Zero Cost)
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from pulse_brain import PulseCheck, BrainTask, CheckType
|
|
|
|
|
|
|
|
|
|
|
|
def check_my_server():
|
|
|
|
|
|
"""Pure Python check - no agent, no cost."""
|
|
|
|
|
|
import requests
|
|
|
|
|
|
try:
|
|
|
|
|
|
r = requests.get("http://localhost:8000/health", timeout=5)
|
|
|
|
|
|
return {"status": "ok" if r.status_code == 200 else "error"}
|
|
|
|
|
|
except:
|
|
|
|
|
|
return {"status": "error", "message": "Server down"}
|
|
|
|
|
|
|
|
|
|
|
|
# Add to Pulse & Brain
|
|
|
|
|
|
pb = PulseBrain(agent)
|
|
|
|
|
|
pb.add_pulse_check(PulseCheck("my-server", check_my_server, interval_seconds=60))
|
|
|
|
|
|
|
|
|
|
|
|
# Only invoke agent when server is down
|
|
|
|
|
|
pb.add_brain_task(BrainTask(
|
|
|
|
|
|
name="server-fixer",
|
|
|
|
|
|
check_type=CheckType.CONDITIONAL,
|
|
|
|
|
|
prompt_template="Server is down! What should I check?",
|
|
|
|
|
|
condition_func=lambda data: data.get("status") == "error"
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
|
pb.start()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**That's it!** Your agent now monitors your system 24/7 at ~$1-2/month.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
2026-02-13 19:06:28 -07:00
|
|
|
|
|
|
|
|
|
|
## 🎯 The Problem
|
|
|
|
|
|
|
|
|
|
|
|
Running an agent in a loop is expensive:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# ❌ EXPENSIVE: Agent asks "What should I do?" every loop
|
|
|
|
|
|
while True:
|
|
|
|
|
|
response = agent.chat("What should I do?") # Costs tokens!
|
|
|
|
|
|
time.sleep(60)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Cost:** If you check every minute for 24 hours:
|
|
|
|
|
|
- 1,440 API calls/day
|
|
|
|
|
|
- ~50,000 tokens/day
|
|
|
|
|
|
- ~$0.50/day just to ask "nothing to do"
|
|
|
|
|
|
|
|
|
|
|
|
## ✅ The Solution: Pulse & Brain
|
|
|
|
|
|
|
|
|
|
|
|
Think of it like a **security guard**:
|
|
|
|
|
|
|
|
|
|
|
|
- **Pulse (Guard)**: Walks the perimeter every 60 seconds. Checks doors (pure Python). **Cost: $0**
|
|
|
|
|
|
- **Brain (Manager)**: Only called when guard sees a problem or it's time for the morning report. **Cost: Only when needed**
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# ✅ EFFICIENT: Agent only invoked when needed
|
|
|
|
|
|
while True:
|
|
|
|
|
|
# Pulse: Pure Python checks (zero cost)
|
|
|
|
|
|
disk_check = check_disk_space() # $0
|
|
|
|
|
|
log_check = check_for_errors() # $0
|
|
|
|
|
|
task_check = check_stale_tasks() # $0
|
|
|
|
|
|
|
|
|
|
|
|
# Brain: Only if something needs attention
|
|
|
|
|
|
if disk_check.status == "error":
|
|
|
|
|
|
agent.chat("Disk space critical!") # Costs tokens (but only when needed)
|
|
|
|
|
|
|
|
|
|
|
|
if current_time == "08:00":
|
|
|
|
|
|
agent.chat("Morning briefing") # Costs tokens (scheduled)
|
|
|
|
|
|
|
|
|
|
|
|
time.sleep(60)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 📊 Cost Comparison
|
|
|
|
|
|
|
|
|
|
|
|
### Old Heartbeat System (Always Uses Agent)
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Every 30 minutes, agent processes checklist
|
|
|
|
|
|
while True:
|
|
|
|
|
|
response = agent.chat(checklist) # ~1000 tokens
|
|
|
|
|
|
time.sleep(1800) # 30 min
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Cost per day:**
|
|
|
|
|
|
- 48 checks/day
|
|
|
|
|
|
- ~48,000 tokens/day
|
|
|
|
|
|
- ~$0.48/day
|
|
|
|
|
|
|
|
|
|
|
|
### Pulse & Brain (Conditional Agent)
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Every 60 seconds, pure Python checks (zero cost)
|
|
|
|
|
|
# Agent only invoked when:
|
|
|
|
|
|
# 1. Error detected (~2x/day)
|
|
|
|
|
|
# 2. Scheduled briefings (2x/day)
|
|
|
|
|
|
# = ~4 agent calls/day
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Cost per day:**
|
|
|
|
|
|
- 1,440 pulse checks (pure Python) = **$0**
|
|
|
|
|
|
- 4 brain invocations (~4,000 tokens) = **$0.04/day**
|
|
|
|
|
|
|
|
|
|
|
|
**Savings: 92%** 💰
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-13 19:06:28 -07:00
|
|
|
|
## 🏗️ Architecture
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ PULSE LOOP │
|
|
|
|
|
|
│ (Pure Python, $0 cost) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
|
|
|
|
|
|
│ │ Disk Space│ │ Log Errors│ │ Tasks │ │
|
|
|
|
|
|
│ │ Check │ │ Check │ │ Check │ ... │
|
|
|
|
|
|
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
|
|
|
|
|
|
│ │ │ │ │
|
|
|
|
|
|
│ └──────────────┼──────────────┘ │
|
|
|
|
|
|
│ │ │
|
|
|
|
|
|
│ ┌───────▼───────┐ │
|
|
|
|
|
|
│ │ Conditions? │ │
|
|
|
|
|
|
│ └───────┬───────┘ │
|
|
|
|
|
|
│ │ │
|
|
|
|
|
|
│ ┌──────────────┴──────────────┐ │
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
│ ┌────▼────┐ ┌────▼────┐ │
|
|
|
|
|
|
│ │ Error? │ │ 8:00 AM?│ │
|
|
|
|
|
|
│ └────┬────┘ └────┬────┘ │
|
|
|
|
|
|
│ │ YES │ YES │
|
|
|
|
|
|
└────────┼─────────────────────────────┼──────────────┘
|
|
|
|
|
|
│ │
|
|
|
|
|
|
└──────────┬──────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
┌──────────▼──────────┐
|
|
|
|
|
|
│ BRAIN │
|
|
|
|
|
|
│ (Agent/SDK) │
|
|
|
|
|
|
│ COSTS TOKENS │
|
|
|
|
|
|
└─────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
---
|
2026-02-13 19:06:28 -07:00
|
|
|
|
|
|
|
|
|
|
## 📝 Default Checks
|
|
|
|
|
|
|
|
|
|
|
|
### Pulse Checks (Zero Cost)
|
|
|
|
|
|
|
|
|
|
|
|
| Check | Interval | What It Does |
|
|
|
|
|
|
|-------|----------|--------------|
|
|
|
|
|
|
| `disk-space` | 5 min | Checks disk usage, warns >80% |
|
|
|
|
|
|
| `memory-tasks` | 10 min | Counts pending tasks |
|
|
|
|
|
|
| `log-errors` | 1 min | Scans logs for errors |
|
|
|
|
|
|
|
|
|
|
|
|
### Brain Tasks (Uses Tokens)
|
|
|
|
|
|
|
|
|
|
|
|
| Task | Type | Trigger |
|
|
|
|
|
|
|------|------|---------|
|
|
|
|
|
|
| `disk-space-advisor` | Conditional | Disk >90% used |
|
|
|
|
|
|
| `error-analyst` | Conditional | Errors found in logs |
|
|
|
|
|
|
| `morning-briefing` | Scheduled | Daily at 8:00 AM |
|
|
|
|
|
|
| `evening-summary` | Scheduled | Daily at 6:00 PM |
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-13 19:06:28 -07:00
|
|
|
|
## 🎨 Custom Configuration
|
|
|
|
|
|
|
|
|
|
|
|
Create `config/pulse_brain_config.py`:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from pulse_brain import PulseCheck, BrainTask, CheckType
|
|
|
|
|
|
|
|
|
|
|
|
def check_my_server() -> dict:
|
|
|
|
|
|
"""Pure Python check (zero cost)."""
|
|
|
|
|
|
import requests
|
|
|
|
|
|
try:
|
|
|
|
|
|
r = requests.get("http://localhost:8000/health")
|
|
|
|
|
|
return {
|
|
|
|
|
|
"status": "ok" if r.status_code == 200 else "error",
|
|
|
|
|
|
"message": f"Server: {r.status_code}"
|
|
|
|
|
|
}
|
|
|
|
|
|
except:
|
|
|
|
|
|
return {"status": "error", "message": "Server down"}
|
|
|
|
|
|
|
|
|
|
|
|
CUSTOM_PULSE_CHECKS = [
|
|
|
|
|
|
PulseCheck("my-server", check_my_server, interval_seconds=60)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
CUSTOM_BRAIN_TASKS = [
|
|
|
|
|
|
BrainTask(
|
|
|
|
|
|
name="server-medic",
|
|
|
|
|
|
check_type=CheckType.CONDITIONAL,
|
|
|
|
|
|
prompt_template="Server is down! {message}\n\nWhat should I check?",
|
|
|
|
|
|
condition_func=lambda data: data.get("status") == "error"
|
|
|
|
|
|
)
|
|
|
|
|
|
]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-13 19:06:28 -07:00
|
|
|
|
## 🌟 Real-World Examples
|
|
|
|
|
|
|
|
|
|
|
|
### Example 1: Homelab Monitoring (from Gemini)
|
|
|
|
|
|
|
|
|
|
|
|
**The "Morning Briefing"** (Scheduled Brain):
|
|
|
|
|
|
```python
|
|
|
|
|
|
BrainTask(
|
|
|
|
|
|
name="homelab-morning",
|
|
|
|
|
|
check_type=CheckType.SCHEDULED,
|
|
|
|
|
|
schedule_time="08:00",
|
|
|
|
|
|
prompt_template="""Good morning Jordan!
|
|
|
|
|
|
|
|
|
|
|
|
Overnight summary:
|
|
|
|
|
|
- Plex: {plex_status}
|
|
|
|
|
|
- Star Citizen: {game_status}
|
|
|
|
|
|
- UniFi: {network_status}
|
|
|
|
|
|
|
|
|
|
|
|
Any restarts or patches detected?""",
|
|
|
|
|
|
send_to_platform="slack",
|
|
|
|
|
|
send_to_channel="C_HOMELAB"
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Cost:** 1 API call/day = ~$0.01
|
|
|
|
|
|
|
|
|
|
|
|
**The "Medic"** (Conditional Brain):
|
|
|
|
|
|
```python
|
|
|
|
|
|
def check_logs():
|
|
|
|
|
|
"""Pure Python log scanner."""
|
|
|
|
|
|
with open("/var/log/syslog") as f:
|
|
|
|
|
|
recent = f.readlines()[-100:]
|
|
|
|
|
|
errors = [line for line in recent if "ERROR" in line]
|
|
|
|
|
|
return {
|
|
|
|
|
|
"status": "error" if errors else "ok",
|
|
|
|
|
|
"error_lines": errors
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BrainTask(
|
|
|
|
|
|
name="error-medic",
|
|
|
|
|
|
check_type=CheckType.CONDITIONAL,
|
|
|
|
|
|
prompt_template="""Errors detected in logs:
|
|
|
|
|
|
{error_lines}
|
|
|
|
|
|
|
|
|
|
|
|
What does this mean and should I fix it?""",
|
|
|
|
|
|
condition_func=lambda data: data.get("status") == "error"
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Cost:** Only when errors found = ~$0.01 per error
|
|
|
|
|
|
|
|
|
|
|
|
**The "Resource Manager"** (Conditional Brain):
|
|
|
|
|
|
```python
|
|
|
|
|
|
BrainTask(
|
|
|
|
|
|
name="disk-cleanup",
|
|
|
|
|
|
check_type=CheckType.CONDITIONAL,
|
|
|
|
|
|
prompt_template="""Disk space is low: {gb_free:.1f} GB free.
|
|
|
|
|
|
|
|
|
|
|
|
Please:
|
|
|
|
|
|
1. Scan temp folders
|
|
|
|
|
|
2. Recommend what to delete (>7 days old)
|
|
|
|
|
|
3. Provide cleanup commands""",
|
|
|
|
|
|
condition_func=lambda data: data.get("gb_free", 100) < 10
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Cost:** Only when disk < 10GB = ~$0.02 per trigger
|
|
|
|
|
|
|
|
|
|
|
|
### Example 2: Docker Monitoring
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
def check_docker():
|
|
|
|
|
|
import subprocess
|
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
|
["docker", "ps", "--format", "{{.Status}}"],
|
|
|
|
|
|
capture_output=True, text=True
|
|
|
|
|
|
)
|
|
|
|
|
|
unhealthy = sum(1 for line in result.stdout.split("\n")
|
|
|
|
|
|
if "unhealthy" in line)
|
|
|
|
|
|
return {
|
|
|
|
|
|
"status": "error" if unhealthy > 0 else "ok",
|
|
|
|
|
|
"unhealthy_count": unhealthy
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PULSE_CHECK = PulseCheck("docker", check_docker, interval_seconds=60)
|
|
|
|
|
|
|
|
|
|
|
|
BRAIN_TASK = BrainTask(
|
|
|
|
|
|
name="docker-fixer",
|
|
|
|
|
|
check_type=CheckType.CONDITIONAL,
|
|
|
|
|
|
prompt_template="{unhealthy_count} containers unhealthy. What should I do?",
|
|
|
|
|
|
condition_func=lambda data: data.get("unhealthy_count", 0) > 0
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Pulse runs every 60s:** $0
|
|
|
|
|
|
**Brain only when unhealthy:** ~$0.01 per incident
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 💡 Why Pulse & Brain?
|
|
|
|
|
|
|
|
|
|
|
|
### The Evolution of Monitoring
|
|
|
|
|
|
|
|
|
|
|
|
Ajarbot has had **three different monitoring systems**. Here's how they compare:
|
|
|
|
|
|
|
|
|
|
|
|
| Feature | Pulse & Brain ⭐ | TaskScheduler | Old Heartbeat ⚠️ |
|
|
|
|
|
|
|---------|-----------------|---------------|------------------|
|
|
|
|
|
|
| **Cost per day** | ~$0.04 | ~$0.10-0.30 | ~$0.48 |
|
|
|
|
|
|
| **Cost per month** | ~$1.20 | ~$3-9 | ~$14.40 |
|
|
|
|
|
|
| **Agent usage** | Only when needed | Every scheduled task | Every interval |
|
|
|
|
|
|
| **Scheduling** | Cron + Conditional | Cron only | Interval only |
|
|
|
|
|
|
| **Monitoring** | ✅ Zero-cost pulse | ❌ None | ❌ Uses agent |
|
|
|
|
|
|
| **Messaging** | ✅ Slack/Telegram | ✅ Slack/Telegram | ❌ None |
|
|
|
|
|
|
| **Best for** | Production monitoring | Content generation | ⚠️ Legacy (deprecated) |
|
|
|
|
|
|
| **Status** | ✅ Recommended | ✅ Active | ⚠️ Disabled by default |
|
|
|
|
|
|
|
|
|
|
|
|
### Why Pulse & Brain Wins
|
|
|
|
|
|
|
|
|
|
|
|
**1. Zero-Cost Monitoring**
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Pulse checks run constantly at zero cost
|
|
|
|
|
|
Pulse (60s intervals, pure Python):
|
|
|
|
|
|
├─ Check disk space $0
|
|
|
|
|
|
├─ Check log errors $0
|
|
|
|
|
|
├─ Check stale tasks $0
|
|
|
|
|
|
├─ Check server health $0
|
|
|
|
|
|
└─ ... (add infinite checks, still $0)
|
|
|
|
|
|
|
|
|
|
|
|
# Brain only invoked when needed
|
|
|
|
|
|
Brain (Agent/SDK):
|
|
|
|
|
|
├─ Condition: disk > 90% → $0.01 (only if triggered)
|
|
|
|
|
|
├─ Condition: errors found → $0.01 (only if triggered)
|
|
|
|
|
|
├─ Scheduled: 8:00 AM briefing → $0.01 (once per day)
|
|
|
|
|
|
└─ Scheduled: 6:00 PM summary → $0.01 (once per day)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**2. Smarter Than TaskScheduler**
|
|
|
|
|
|
|
|
|
|
|
|
TaskScheduler always invokes the agent, even if there's nothing to report:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# ❌ TaskScheduler: Always uses agent
|
|
|
|
|
|
- 08:00 Weather report → Agent ($0.01) even if no change
|
|
|
|
|
|
- 12:00 Midday standup → Agent ($0.01) even if no updates
|
|
|
|
|
|
- 18:00 Evening summary → Agent ($0.01) even if nothing happened
|
|
|
|
|
|
|
|
|
|
|
|
# ✅ Pulse & Brain: Conditional intelligence
|
|
|
|
|
|
- Pulse checks for changes → Python ($0)
|
|
|
|
|
|
- Brain only if updates → Agent ($0.01) only when needed
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**3. More Flexible Than Old Heartbeat**
|
|
|
|
|
|
|
|
|
|
|
|
Old Heartbeat was simple but wasteful:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# ❌ Old Heartbeat: Every 30 minutes, always uses agent
|
|
|
|
|
|
while True:
|
|
|
|
|
|
agent.chat("Check everything") # ~$0.01
|
|
|
|
|
|
time.sleep(1800) # 48 calls/day = $0.48/day
|
|
|
|
|
|
|
|
|
|
|
|
# ✅ Pulse & Brain: Smart triggers
|
|
|
|
|
|
while True:
|
|
|
|
|
|
# 1,440 pulse checks/day (pure Python) = $0
|
|
|
|
|
|
# Only 4 brain calls/day (when needed) = $0.04/day
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Decision Tree: Which System to Use?
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Start here:
|
|
|
|
|
|
↓
|
|
|
|
|
|
Do you need real-time monitoring? (disk, logs, health checks)
|
|
|
|
|
|
├─ YES → Use Pulse & Brain ⭐
|
|
|
|
|
|
└─ NO → Go to next question
|
|
|
|
|
|
↓
|
|
|
|
|
|
Do you need scheduled content? (weather, summaries, reports)
|
|
|
|
|
|
├─ YES → Use TaskScheduler
|
|
|
|
|
|
└─ NO → Go to next question
|
|
|
|
|
|
↓
|
|
|
|
|
|
Do you need simple periodic checks?
|
|
|
|
|
|
└─ YES → Migrate from old Heartbeat to Pulse & Brain
|
|
|
|
|
|
|
|
|
|
|
|
Most users should: Use Pulse & Brain (+ optionally TaskScheduler for content)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Hybrid Approach (Best of Both)
|
|
|
|
|
|
|
|
|
|
|
|
For maximum efficiency:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Pulse & Brain handles:
|
|
|
|
|
|
# - Health monitoring (disk, logs, tasks)
|
|
|
|
|
|
# - Morning briefing with system status
|
|
|
|
|
|
# - Evening summary
|
|
|
|
|
|
# - Error alerts
|
|
|
|
|
|
pb = PulseBrain(agent, pulse_interval=60)
|
|
|
|
|
|
pb.start()
|
|
|
|
|
|
|
|
|
|
|
|
# TaskScheduler handles ONLY:
|
|
|
|
|
|
# - Weekly newsletter (Friday 5pm)
|
|
|
|
|
|
# - Monthly metrics report (1st of month)
|
|
|
|
|
|
# - Custom scheduled content (unique reports)
|
|
|
|
|
|
scheduler = TaskScheduler(agent)
|
|
|
|
|
|
scheduler.tasks = [weekly_newsletter, monthly_report]
|
|
|
|
|
|
scheduler.start()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Cost: ~$2-3/month** (vs $15/month with old heartbeat) 💰
|
|
|
|
|
|
|
|
|
|
|
|
### Real-World Cost Examples
|
|
|
|
|
|
|
|
|
|
|
|
| Use Case | System | Monthly Cost |
|
|
|
|
|
|
|----------|--------|--------------|
|
|
|
|
|
|
| **Homelab monitoring** | Pulse & Brain only | ~$1-2 |
|
|
|
|
|
|
| **Dev team bot** | Pulse & Brain + TaskScheduler | ~$4-6 |
|
|
|
|
|
|
| **Solo developer** | Pulse & Brain only | ~$0.50-1 |
|
|
|
|
|
|
| **Content bot** | TaskScheduler only | ~$4-8 |
|
|
|
|
|
|
| **Old heartbeat** | ⚠️ Legacy system | ~$15 |
|
|
|
|
|
|
|
|
|
|
|
|
### Migration Guide
|
|
|
|
|
|
|
|
|
|
|
|
**From Old Heartbeat → Pulse & Brain**
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Old (heartbeat.py) ❌
|
|
|
|
|
|
agent = Agent(enable_heartbeat=True)
|
|
|
|
|
|
|
|
|
|
|
|
# New (pulse_brain.py) ✅
|
|
|
|
|
|
agent = Agent(enable_heartbeat=False)
|
|
|
|
|
|
pb = PulseBrain(agent)
|
|
|
|
|
|
pb.start()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Benefit:** 92% cost reduction
|
|
|
|
|
|
|
|
|
|
|
|
**From TaskScheduler → Pulse & Brain**
|
|
|
|
|
|
|
|
|
|
|
|
If your "scheduled tasks" are really monitoring checks:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Old (scheduled_tasks.yaml) ❌
|
|
|
|
|
|
- name: health-check
|
|
|
|
|
|
schedule: "hourly"
|
|
|
|
|
|
prompt: "Check system health"
|
|
|
|
|
|
|
|
|
|
|
|
# New (pulse_brain.py) ✅
|
|
|
|
|
|
def check_health(): # Pure Python, zero cost
|
|
|
|
|
|
return {"status": "ok", "message": "Healthy"}
|
|
|
|
|
|
|
|
|
|
|
|
PulseCheck("health", check_health, interval_seconds=3600)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Benefit:** 96% cost reduction
|
|
|
|
|
|
|
|
|
|
|
|
### Why Not Just Use TaskScheduler?
|
|
|
|
|
|
|
|
|
|
|
|
TaskScheduler is great for **content generation**, but wasteful for monitoring:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Example: Check disk space every hour with TaskScheduler
|
|
|
|
|
|
# Cost: 24 calls/day × 30 days = 720 calls/month = ~$7/month
|
|
|
|
|
|
|
|
|
|
|
|
# Same with Pulse & Brain:
|
|
|
|
|
|
# Pulse checks: Unlimited ($0)
|
|
|
|
|
|
# Brain only if disk > 90%: ~2 calls/month = ~$0.02/month
|
|
|
|
|
|
|
|
|
|
|
|
# Savings: $6.98/month (99.7% reduction)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Why Not Just Use Old Heartbeat?
|
|
|
|
|
|
|
|
|
|
|
|
Old Heartbeat was the original system, but it's:
|
|
|
|
|
|
- **Expensive**: Uses agent for every check
|
|
|
|
|
|
- **Inflexible**: Only interval-based, no conditionals
|
|
|
|
|
|
- **Limited**: No messaging platform integration
|
|
|
|
|
|
- **Deprecated**: Disabled by default, legacy code
|
|
|
|
|
|
|
|
|
|
|
|
**Pulse & Brain replaces it entirely with 92% cost savings.**
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-13 19:06:28 -07:00
|
|
|
|
## 🎯 When to Use What
|
|
|
|
|
|
|
|
|
|
|
|
| System | Best For | Cost |
|
|
|
|
|
|
|--------|----------|------|
|
|
|
|
|
|
| **Pulse & Brain** | Production monitoring | ~$1-2/month |
|
|
|
|
|
|
| **TaskScheduler** | Scheduled content | ~$3-5/month |
|
2026-02-13 21:06:15 -07:00
|
|
|
|
| **Old Heartbeat** | ⚠️ Legacy (don't use) | ~$15/month |
|
2026-02-13 19:06:28 -07:00
|
|
|
|
|
|
|
|
|
|
### Recommended Stack
|
|
|
|
|
|
|
|
|
|
|
|
For maximum efficiency:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Pulse & Brain for monitoring (cheapest)
|
|
|
|
|
|
pb = PulseBrain(agent, pulse_interval=60)
|
|
|
|
|
|
pb.start()
|
|
|
|
|
|
|
|
|
|
|
|
# TaskScheduler for scheduled content only
|
|
|
|
|
|
scheduler = TaskScheduler(agent)
|
|
|
|
|
|
# Only enable specific scheduled tasks
|
|
|
|
|
|
scheduler.start()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-13 19:06:28 -07:00
|
|
|
|
## 📊 Monitoring Your Costs
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
pb = PulseBrain(agent)
|
|
|
|
|
|
pb.start()
|
|
|
|
|
|
|
|
|
|
|
|
# After running for a while
|
|
|
|
|
|
status = pb.get_status()
|
|
|
|
|
|
print(f"Brain invoked {status['brain_invocations']} times")
|
|
|
|
|
|
|
|
|
|
|
|
# Estimate cost
|
|
|
|
|
|
tokens_per_invocation = 1000 # Average
|
|
|
|
|
|
total_tokens = status['brain_invocations'] * tokens_per_invocation
|
|
|
|
|
|
cost = total_tokens * 0.000003 # Claude Sonnet pricing
|
|
|
|
|
|
print(f"Estimated cost: ${cost:.4f}")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 💰 Cost Optimization Tips
|
|
|
|
|
|
|
|
|
|
|
|
1. **Increase pulse interval** if checks don't need to be frequent
|
|
|
|
|
|
```python
|
|
|
|
|
|
pb = PulseBrain(agent, pulse_interval=300) # Every 5 min instead of 60s
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
2. **Use conditional brain tasks** instead of scheduled
|
|
|
|
|
|
```python
|
|
|
|
|
|
# ❌ Expensive: Always runs
|
|
|
|
|
|
BrainTask(schedule="daily 08:00", ...)
|
|
|
|
|
|
|
|
|
|
|
|
# ✅ Cheap: Only if there's news
|
|
|
|
|
|
BrainTask(condition=lambda: has_updates(), ...)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
3. **Batch briefings** instead of multiple schedules
|
|
|
|
|
|
```python
|
|
|
|
|
|
# ❌ Expensive: 3 calls/day
|
|
|
|
|
|
- morning-briefing (08:00)
|
|
|
|
|
|
- midday-update (12:00)
|
|
|
|
|
|
- evening-summary (18:00)
|
|
|
|
|
|
|
|
|
|
|
|
# ✅ Cheaper: 2 calls/day
|
|
|
|
|
|
- morning-briefing (08:00)
|
|
|
|
|
|
- evening-summary (18:00)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
4. **Make pulse checks do more** before invoking brain
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Pulse checks can filter, aggregate, and pre-process
|
|
|
|
|
|
# Brain only gets invoked with actionable data
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-13 19:06:28 -07:00
|
|
|
|
## 🚀 Getting Started
|
|
|
|
|
|
|
|
|
|
|
|
1. **Edit** `config/pulse_brain_config.py` with your checks
|
|
|
|
|
|
2. **Test** your pulse checks (they should return `{"status": "ok|warn|error"}`)
|
|
|
|
|
|
3. **Configure** brain tasks (conditional or scheduled)
|
|
|
|
|
|
4. **Run** `python -m pulse_brain`
|
|
|
|
|
|
5. **Monitor** brain invocation count
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-13 19:06:28 -07:00
|
|
|
|
## 🔥 Pro Tips
|
|
|
|
|
|
|
|
|
|
|
|
1. **Make pulse checks fast** (<1 second each)
|
|
|
|
|
|
2. **Use conditional brain tasks** for errors/warnings
|
|
|
|
|
|
3. **Use scheduled brain tasks** for daily summaries
|
|
|
|
|
|
4. **Test pulse checks** without brain first
|
|
|
|
|
|
5. **Monitor brain invocations** to track costs
|
|
|
|
|
|
|
2026-02-13 21:06:15 -07:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## ⚠️ Legacy System Notice
|
|
|
|
|
|
|
|
|
|
|
|
### Old Heartbeat (Deprecated)
|
|
|
|
|
|
|
|
|
|
|
|
The original Heartbeat system is now **disabled by default**. It has been superseded by Pulse & Brain.
|
|
|
|
|
|
|
|
|
|
|
|
**Why it's deprecated:**
|
|
|
|
|
|
- Uses agent for every check (expensive)
|
|
|
|
|
|
- No conditional logic (always runs)
|
|
|
|
|
|
- No messaging platform integration
|
|
|
|
|
|
- Replaced entirely by Pulse & Brain
|
|
|
|
|
|
|
|
|
|
|
|
**If you're still using it:**
|
|
|
|
|
|
```python
|
|
|
|
|
|
# Old (don't use) ❌
|
|
|
|
|
|
agent = Agent(enable_heartbeat=True)
|
|
|
|
|
|
|
|
|
|
|
|
# New (migrate to this) ✅
|
|
|
|
|
|
agent = Agent(enable_heartbeat=False)
|
|
|
|
|
|
pb = PulseBrain(agent)
|
|
|
|
|
|
pb.start()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Migration benefits:**
|
|
|
|
|
|
- 92% cost reduction
|
|
|
|
|
|
- Conditional intelligence
|
|
|
|
|
|
- Messaging platform support
|
|
|
|
|
|
- More flexible scheduling
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-13 19:06:28 -07:00
|
|
|
|
## 🎉 Summary
|
|
|
|
|
|
|
|
|
|
|
|
**Pulse & Brain is the most cost-effective way to run a proactive agent:**
|
|
|
|
|
|
|
|
|
|
|
|
✅ **Pulse runs constantly** - Zero cost
|
|
|
|
|
|
✅ **Brain only when needed** - Pay for value
|
|
|
|
|
|
✅ **92% cost savings** vs always-on agent
|
|
|
|
|
|
✅ **Smart monitoring** - Python checks + Agent analysis
|
|
|
|
|
|
✅ **Scalable** - Add more checks without increasing cost
|
|
|
|
|
|
|
|
|
|
|
|
**Perfect for:**
|
|
|
|
|
|
- Homelab monitoring
|
|
|
|
|
|
- Server health checks
|
|
|
|
|
|
- Log analysis
|
|
|
|
|
|
- Resource management
|
|
|
|
|
|
- Scheduled briefings
|
|
|
|
|
|
|
|
|
|
|
|
**Result:** An agent that's always watching but only speaks when it has something important to say. 🫀🧠
|