Proactive AI Agent with Semantic Memory

Table of content

Most AI assistants wait for you to ask. A proactive agent notices things and reaches out when it matters.

Why Proactivity

Reactive AI: you ask → it answers.

Proactive AI: it observes → decides to speak → says something useful.

The difference:

Proactivity requires:

  1. Heartbeat system — agent wakes up periodically
  2. Semantic memory — agent recalls context by meaning, not keywords
  3. Good judgment — knowing when to speak vs stay silent

Heartbeat System

A cron job wakes the agent every N minutes. The agent decides whether to message.

The Mechanism

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ cron/timer  │───▶│ agent runs  │───▶│ speak or    │
│ every 30min │    │ checks data │    │ stay silent │
└─────────────┘    └─────────────┘    └─────────────┘

HEARTBEAT.md Prompt

Create a file that defines heartbeat behavior:

# Heartbeat Rules

## Check and speak ONLY if:
- Data shows anomaly (stress spike, unusual screen time)
- Pending reminder is due
- Context is right for a deferred question
- Pattern worth noting (3rd day without exercise)

## NEVER do:
- Message every 30 minutes
- Repeat what we just discussed
- Write at night without real reason
- State obvious things ("It's Tuesday")

## Format
- 1-3 sentences max
- Questions beat statements
- Be specific, not vague

## Examples of good messages:
- "BB dropped to 25 after the call — stress pattern?"
- "4h in Cursor today, 0 commits — stuck on something?"
- "Haven't mentioned Project X in a week — dropped or just quiet?"

## Examples of bad messages:
- "Just checking in!" (empty)
- "How are you?" (generic)
- "You've been working" (obvious)

Cron Setup

# Every 30 minutes, 8am-10pm
*/30 8-22 * * * /path/to/agent heartbeat >> /var/log/agent.log 2>&1

The agent reads HEARTBEAT.md, checks available data sources (screen time, health metrics, calendar), and decides.

Semantic Memory

Keyword search fails on meaning. “What did I decide about the React project?” won’t find “chose Vue over React because of team experience.”

Semantic search finds by meaning, not exact words.

Memory Recall Prompt

Add to your agent instructions:

## Memory Recall

Before answering about past decisions, preferences, dates, or prior work:

1. Run semantic search on memory files
2. Pull specific entries that match
3. If search returns nothing, say "checked memory, didn't find"

### When to search:
- "What did we decide about..."
- "When did I..."
- "My preference for..."
- "That thing we discussed..."
- Any reference to past conversations

### Search strategy:
- Start broad: "project decisions"
- Narrow if needed: "React Vue decision January"
- Check multiple memory files if topic spans time

Implementation

Using embeddings (vector search):

## Memory Search Tool

memory_search(query, top_k=5)
- Embeds query
- Finds closest matches by cosine similarity
- Returns matching entries with dates

Example:
memory_search("frontend framework decision")
→ "2025-01-15: Chose React over Vue (team familiarity)"

Simple version without embeddings:

## Memory Search (Simple)

Search memory/*.md files for:
1. Exact phrases
2. Key terms from query
3. Date ranges if mentioned

Return matches with surrounding context (±2 lines)

Memory Structure

Directory Layout

memory/
├── MEMORY.md           # Quick facts, current state
├── decisions/
│   └── 2025-01.md      # Monthly decision log
├── patterns/
│   └── health.md       # Observed patterns
├── projects/
│   └── project-x.md    # Project-specific memory
└── daily/
    └── 2025-01-27.md   # Daily observations

MEMORY.md Format

Fast-access facts:

## Me
- Timezone: UTC-3
- Work hours: 10-19
- Prefer async communication

## Current Focus
- Project X: API redesign (deadline Feb 15)
- Learning: Rust basics

## Preferences
- Code style: explicit over clever
- Meetings: mornings only
- Notifications: batch, not stream

Decision Log Format

# Decisions — January 2025

## 2025-01-27
- **Chose Postgres over SQLite** for Project X
  - Reason: need concurrent writes
  - Revisit if: scale stays under 100 users

## 2025-01-15
- **React over Vue** for dashboard
  - Reason: team knows React
  - Trade-off: Vue's simpler reactivity

Pattern Log Format

# Health Patterns

## Stress
- Spikes after long meetings (>1h)
- Lower on days with morning exercise
- Correlates with screen time >6h

## Energy
- Peak: 10am-1pm
- Dip: 3-4pm (don't schedule calls)
- Second wind: 8-10pm

Real Message Examples

What a proactive agent actually sends:

Health & Metrics

"BB 25 after the standup — meetings still hitting hard?"
"Stress baseline up 15% this week. Anything specific or just accumulation?"
"Sleep debt: 3h over past week. Weekend catchup or push through?"

Productivity

"4h in Slack, 45min in IDE — meeting-heavy day or context-switching?"
"Third day without commits. Stuck, planning, or intentional break?"
"Finished 0 of 5 planned tasks. Priorities shifted or underestimated?"

Patterns & Observations

"You mention 'tired' in evening messages 4x this week. Pattern or just words?"
"Project X deadline in 3 days, no activity in 5. Reprioritized?"
"Last bike ride: 9 days ago. Weather or motivation?"

Context-Aware

"Flight tomorrow at 7am. Alarm set, or should I remind at 10pm?"
"Meeting with [person] in 2h. Last time you wanted to discuss [topic] — still relevant?"

When to Stay Silent

Proactive ≠ chatty. Rules for silence:

## Stay Silent When:

- No meaningful observation
- Would repeat recent message
- User is clearly busy (deep work detected)
- Night time (unless urgent)
- Observation is obvious ("you're working")
- Data is stale (>2h old metrics)

## Speak When:

- Anomaly detected (outside normal range)
- Actionable insight (not just observation)
- Time-sensitive context (upcoming event)
- Pattern formed (3+ data points)
- User explicitly requested proactive check

Frequency Guidelines

ContextFrequencyCondition
Health anomalyImmediate>2 std dev from baseline
Daily summaryOnceEnd of work hours
Pattern observationWhen formed3+ occurrences
ReminderAs scheduledUser-set time
Random check-inNeverDon’t do this

Putting It Together

Agent System Prompt Addition

## Proactive Behavior

You have heartbeat capability. When triggered:

1. Load HEARTBEAT.md rules
2. Check available data sources
3. Search memory for relevant context
4. Decide: speak or stay silent
5. If speaking: 1-3 sentences, specific, question-form preferred

Priority order:
1. Time-sensitive (reminders, upcoming events)
2. Anomalies (health, productivity spikes/dips)
3. Patterns (multi-day observations)
4. Curiosity (open threads worth revisiting)

Default: silence. Speak only when valuable.

Data Sources to Check

SourceWhat to look for
Screen timeUnusual apps, total hours
Health metricsStress, HRV, sleep
CalendarUpcoming events, patterns
Git activityCommits, PR status
Memory filesOpen threads, due reminders

Common Mistakes

Too chatty: Messaging on every heartbeat. Fix: default to silence.

Too vague: “You seem busy.” Fix: be specific or don’t speak.

No memory: Asking things already discussed. Fix: search before speaking.

Wrong timing: Messaging during focus time. Fix: detect work patterns.

Stating obvious: “You’re in a meeting.” Fix: only non-obvious insights.


Next: Memory System

Topics: memory automation proactive semantic-search