Files
ajarbot/docs/PULSE_BRAIN.md
Jordan Ramos ab3a5afd59 Documentation cleanup and consolidation
Removed redundant/outdated files:
- docs/HEARTBEAT_HOOKS.md (legacy, disabled by default)
- docs/QUICK_START_PULSE.md (merged into PULSE_BRAIN.md)
- docs/MONITORING_COMPARISON.md (merged into PULSE_BRAIN.md)

Consolidated monitoring docs:
- Merged 3 monitoring files into comprehensive PULSE_BRAIN.md
- Added Quick Start section to PULSE_BRAIN.md
- Added "Why Pulse & Brain?" comparison section
- Added deprecation notices for Heartbeat system

Updated README.md:
- Clarified Haiku is default model (12x cheaper)
- Added prompt caching info (90% savings on Sonnet)
- Removed duplicate setup instructions
- Linked to SETUP.md for detailed instructions
- Added model switching commands section

Simplified WINDOWS_QUICK_REFERENCE.md:
- Reduced from 224 lines to ~160 lines
- Removed redundant development/deployment sections
- Kept essential quick commands
- Added model switching commands

Updated docs/README.md navigation:
- Removed references to deleted files
- Added deprecation notice for Heartbeat
- Updated learning paths
- Cleaned up file organization section

Result:
- Removed 300+ lines of redundant documentation
- Consolidated 3 monitoring files into 1
- Improved accuracy and clarity
- Easier navigation and maintenance

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 21:06:15 -07:00

683 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Pulse & Brain Architecture
**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.
---
## 🎯 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%** 💰
---
## 🏗️ 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 │
└─────────────────────┘
```
---
## 📝 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 |
---
## 🎨 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"
)
]
```
---
## 🌟 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
---
## 💡 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.**
---
## 🎯 When to Use What
| System | Best For | Cost |
|--------|----------|------|
| **Pulse & Brain** | Production monitoring | ~$1-2/month |
| **TaskScheduler** | Scheduled content | ~$3-5/month |
| **Old Heartbeat** | ⚠️ Legacy (don't use) | ~$15/month |
### 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()
```
---
## 📊 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}")
```
---
## 💰 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
```
---
## 🚀 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
---
## 🔥 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
---
## ⚠️ 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
---
## 🎉 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. 🫀🧠