Skip to main content

Context Memory System — Architecture

Passive intelligence layer that listens to Slack channel messages, extracts structured facts using Claude Haiku, stores them as vector-embedded observations, and synthesizes per-entity summaries nightly. The bot uses these at query time to inject relevant organizational context into LLM prompts.

1. Data Flow — End to End


2. Entity Relationship Diagram


3. Ingestion Detail

Message Flow

  1. Bot receives every Slack channel message (not DMs)
  2. Fires POST /context/ingest with organizationId, channelId, channelName, platformUserId, messageTs, threadTs, text
  3. Message inserted into channel_messages with processedAt = null
  4. URLs extracted from message text via regex, cleaned of Slack formatting (<url|display> -> url)
  5. Each URL queued as process-url BullMQ job -> Jina Reader API fetches content -> Claude Haiku summarizes -> stored as observation

Batch Triggers

Extraction Pipeline

  1. Fetch unprocessed messages (limit BATCH_SIZE, default 20)
  2. Resolve platformUserId -> user names via platform_identities + users tables
  3. Group messages by channel, format with timestamps and display names
  4. Send to Claude Haiku 4.5 with extraction prompt
  5. Parse JSON array of { entityType, entityId, category, observation, confidence }
  6. For each observation: generate embedding via OpenAI, insert into context_observations
  7. Mark source messages as processed (processedAt = now)
  8. Evaluate proactive actions via context-actions.ts rules

4. Query Flow — How the Bot Retrieves Context

Context String Format

Capped at ~2000 characters (~500 tokens) to avoid bloating the LLM prompt.

5. Entity Types and Observation Categories

Entity Types

Observation Categories

Confidence Calibration

  • 0.9+: Directly stated fact (“I finished the newsletter”)
  • 0.7-0.9: Strong inference (“Rohan always does QC after content — likely a handoff pattern”)
  • 0.5-0.7: Weak inference (tone-based mood signals)
  • Below 0.5: Not stored

6. Nightly Summarization

Summary Storage

  • summaryType = "profile" — one per entity, upserted nightly
  • Unique index on (entity_type, entity_id, summary_type) — ensures one profile per entity
  • Embedding generated for the summary text itself (enables semantic search on summaries)
  • Old summaries are overwritten, not versioned

7. Cleanup & Retention


8. Backfill Architecture

For new orgs or channels, historical messages can be backfilled from Slack:
  • Fetches last 1 year of history
  • 2-second delay between Slack API calls (rate limit safe)
  • Thread replies fetched for threaded messages
  • Uses backfill_state table to track progress per channel

9. Auto Mood Detection

Context observations with category = "mood_signal" feed into automatic mood scoring:

10. API Endpoints


11. Environment Variables


12. Key Files


13. Database Indexes

Requires pgvector/pgvector:pg16 Docker image and CREATE EXTENSION vector migration.