Initial commit: Ajarbot with optimizations
Features: - Multi-platform bot (Slack, Telegram) - Memory system with SQLite FTS - Tool use capabilities (file ops, commands) - Scheduled tasks system - Dynamic model switching (/sonnet, /haiku) - Prompt caching for cost optimization Optimizations: - Default to Haiku 4.5 (12x cheaper) - Reduced context: 3 messages, 2 memory results - Optimized SOUL.md (48% smaller) - Automatic caching when using Sonnet (90% savings) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
370
docs/PULSE_BRAIN.md
Normal file
370
docs/PULSE_BRAIN.md
Normal file
@@ -0,0 +1,370 @@
|
||||
# Pulse & Brain Architecture
|
||||
|
||||
The **most efficient** way to run an agent with proactive monitoring.
|
||||
|
||||
## 🎯 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 │
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
## 🔧 Usage
|
||||
|
||||
### Basic Setup
|
||||
|
||||
```python
|
||||
from agent import Agent
|
||||
from pulse_brain import PulseBrain
|
||||
|
||||
agent = Agent(provider="claude", enable_heartbeat=False)
|
||||
|
||||
# Create Pulse & Brain
|
||||
pb = PulseBrain(agent, pulse_interval=60) # Pulse every 60 seconds
|
||||
|
||||
# Start
|
||||
pb.start()
|
||||
```
|
||||
|
||||
### With Messaging Platforms
|
||||
|
||||
```python
|
||||
from adapters.runtime import AdapterRuntime
|
||||
from pulse_brain import PulseBrain
|
||||
|
||||
# Set up runtime with adapters
|
||||
runtime = AdapterRuntime(agent)
|
||||
runtime.add_adapter(slack_adapter)
|
||||
|
||||
# Create Pulse & Brain
|
||||
pb = PulseBrain(agent)
|
||||
pb.add_adapter("slack", slack_adapter)
|
||||
|
||||
# Start both
|
||||
await runtime.start()
|
||||
pb.start()
|
||||
```
|
||||
|
||||
## 📝 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
|
||||
|
||||
## 🎯 When to Use What
|
||||
|
||||
| System | Best For | Cost |
|
||||
|--------|----------|------|
|
||||
| **Pulse & Brain** | Production monitoring | ~$1-2/month |
|
||||
| **TaskScheduler** | Scheduled content | ~$3-5/month |
|
||||
| **Old Heartbeat** | Simple health checks | ~$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}")
|
||||
```
|
||||
|
||||
## 🚀 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
|
||||
|
||||
## 🎉 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. 🫀🧠
|
||||
Reference in New Issue
Block a user