Skip to content

Latest commit

 

History

History
328 lines (245 loc) · 15 KB

File metadata and controls

328 lines (245 loc) · 15 KB

Multi-Memory Agent Guide

AI Agent Quick Reference for Fortemi Multi-Memory Architecture

Quick Reference

Memory = isolated PostgreSQL schema. Select via X-Fortemi-Memory header or MCP select_memory.
Default = public schema. Omitting header = default memory (archive_public).
All CRUD/tag/collection/template/version/attachment operations are memory-scoped.
Search is memory-scoped by default. Use federated search to query across multiple memories.
Max memories configurable via MAX_MEMORIES env var (default: 10, scales with hardware — see configuration docs).

When to Create a New Memory (Decision Matrix)

Scenario Use Memory? Instead Use Notes
Separate client data with legal isolation requirements YES - Hard schema-level isolation
Separate work vs personal YES - If privacy/legal boundary required
Separate by project (>1000 notes per project) YES - If projects are truly independent
Multi-tenant SaaS deployment YES - One memory per tenant
Separate by topic within same project NO Tags or Collections Memories too heavyweight
Temporary grouping of notes NO Collections Memories are permanent structures
Filter search results by category NO Strict tag filtering Search is memory-scoped already
Archive old project data YES - Clone to archive memory
Test/staging environment isolation YES - Separate memories for test data
Separate by time period (quarterly) MAYBE Tags if <5000 notes/quarter Use rolling archives for >10k notes
Different embedding models per domain NO Embedding Sets Each embedding set has its own model config; no need for separate memories
Team knowledge base (single team) NO Single memory + tags Unless >50k notes

Segmentation Strategies

Strategy A: By Client/Tenant (Multi-Tenant SaaS)

Pattern: One memory per client (client-acme, client-globex)

Best for: Multi-tenant deployments, legal data isolation, SaaS platforms

Tradeoffs:

  • Cross-memory search requires federated search (search_memories_federated)
  • Perfect data isolation (schema-level)
  • Simplifies backup/restore per client
  • Overhead: ~1MB per empty memory, scales with data

Naming convention: client-{slug} or tenant-{id}

When to use: Always use for SaaS/multi-tenant scenarios where data isolation is legally or contractually required.

Strategy B: By Project (Independent Work Streams)

Pattern: One memory per major project (project-alpha, project-beta)

Best for: Teams working on distinct, unrelated projects with minimal knowledge overlap

Tradeoffs:

  • Cross-project knowledge discovery requires explicit effort
  • Simplifies project archival (delete entire memory)
  • Search limited to single project context

When to merge: If projects share >30% of the same concepts/tags, use single memory with project tags instead.

Naming convention: project-{name} or proj-{slug}

When to use: Projects with >1000 notes each, minimal shared concepts, or need for independent lifecycle management.

Strategy C: By Knowledge Domain (Content Type)

Pattern: Separate domains (code-docs, research-papers, meeting-notes)

Best for: Different content types with different search patterns, embedding models, or retention policies

Tradeoffs:

  • Same topic across domains requires federated search
  • Risk: Domains often overlap, prefer tags within single memory
  • Domain-specific embedding models can be achieved with embedding sets within a single memory

Naming convention: domain-{type} or kb-{category}

When to use: Only when content types have fundamentally different characteristics (drastically different note sizes, separate retention policies). Note: different embedding models do NOT require separate memories — use embedding sets instead.

Warning: Over-segmentation by domain reduces cross-domain discovery. Prefer single memory with document type metadata.

Strategy D: By Time Period (Rolling Archives)

Pattern: Rolling archives (archive-2025-q4, archive-2026-q1, plus active for current work)

Best for: Compliance, data retention, performance (smaller active set), historical archival

Tradeoffs:

  • Historical search requires federated query across periods (not yet supported)
  • Excellent for compliance (immutable historical archives)
  • Reduces active search space for performance

Operations: Clone current memory → archive at period boundary, then prune active memory.

Naming convention: archive-{YYYY}-q{N} or archive-{YYYY}-{MM}

When to use: Regulatory compliance requirements, >50k notes with clear temporal boundaries, or need to reduce active search space.

Strategy E: Single Memory (Default, Recommended for Most)

Pattern: Everything in default memory, use tags/collections for organization

Best for: Individual users, small teams, <50,000 notes, no regulatory isolation needs

Tradeoffs:

  • No data isolation (soft boundaries via tags only)
  • Larger search space (but still performant <50k notes)
  • Simplest operational model

When to use: Default strategy unless you have specific requirements driving multi-memory adoption.

Recommended when: No regulatory/legal isolation requirements, team size <20, or total notes <50,000.

Tradeoffs Table

Factor Single Memory Multiple Memories
Search speed Scales with total notes Scales with per-memory notes (smaller scope = faster)
Search scope All notes searchable Per-memory by default; use federated search for cross-memory queries
Data isolation Tags only (soft) Schema-level (hard, PostgreSQL enforced)
Storage overhead Baseline +~1MB per memory (all per-memory tables + indexes cloned per schema)
Backup granularity All or nothing Per-memory backup/restore possible
Cross-referencing Links work anywhere Links memory-scoped only (no cross-memory links)
Migration complexity None Export/import required between memories
Max recommended 50,000 notes 50,000 notes per memory
Operational overhead Automatic Automatic (maintenance runs across all schemas)
Embedding configs Per embedding set (multiple models in one memory) Per embedding set per memory
Tag isolation Shared tag namespaces Independent tag/collection namespaces per memory
Template sharing Global templates Templates memory-scoped (duplicate across memories)

API Patterns for Agents

HTTP API (cURL examples)

# Create memory
curl -X POST http://localhost:3000/api/v1/memories \
  -H "Content-Type: application/json" \
  -d '{"name": "project-alpha", "description": "Alpha project knowledge base"}'

# List all memories
curl http://localhost:3000/api/v1/memories

# Get memory stats
curl http://localhost:3000/api/v1/archives/project-alpha/stats

# Create note in specific memory
curl -X POST http://localhost:3000/api/v1/notes \
  -H "Content-Type: application/json" \
  -H "X-Fortemi-Memory: project-alpha" \
  -d '{"title": "Note", "content": "Content", "metadata": {}}'

# Search in specific memory
curl http://localhost:3000/api/v1/search?q=query \
  -H "X-Fortemi-Memory: project-alpha"

# Federated search across multiple memories
curl -X POST http://localhost:3000/api/v1/search/federated \
  -H "Content-Type: application/json" \
  -d '{"query": "search terms", "memories": ["project-alpha", "project-beta"]}'

# Clone memory (backup/snapshot)
curl -X POST http://localhost:3000/api/v1/archives/project-alpha/clone \
  -H "Content-Type: application/json" \
  -d '{"new_name": "project-alpha-backup-2026-02", "description": "Monthly backup"}'

# Delete memory (IRREVERSIBLE, CASCADE)
curl -X DELETE http://localhost:3000/api/v1/memories/project-alpha

MCP Tool Patterns

// Select active memory for MCP session (persists across calls)
await select_memory({ name: "project-alpha" });

// Check current active memory
const active = await get_active_memory();
// Returns: { name: "project-alpha" }

// All subsequent MCP calls use selected memory automatically
await capture_knowledge({
  action: "create",
  title: "Note",
  content: "...",
  metadata: {}
}); // goes to project-alpha

await search({
  action: "text",
  query: "..."
}); // searches project-alpha only

// List all memories (API-only, use GET /api/v1/archives)
// Note: Archive management is API-only; use select_memory for switching contexts

// Create new memory (API-only, use POST /api/v1/archives)

// Delete memory (API-only, use DELETE /api/v1/archives/:name)

Common Mistakes

Mistake Why It's Wrong Do This Instead
Creating memory per tag Memories are heavyweight schema isolation (full table set + indexes per schema) Use tags within single memory
Forgetting X-Fortemi-Memory header in HTTP API Operations go to default memory silently, no error Always set header explicitly or use select_memory in MCP
Searching without selecting memory Search defaults to the public schema if no memory is selected Always select memory first via X-Fortemi-Memory header or select_memory in MCP
Exceeding MAX_MEMORIES limit Each clones the full per-memory table set with indexes Increase MAX_MEMORIES per hardware tier, prefer tags/collections
Manual vacuum scheduling Unnecessary — Fortemi handles maintenance automatically across all memory schemas No action needed; auto-maintenance covers all schemas at normalized intervals
Assuming cross-memory links work Links are memory-scoped, no foreign keys across schemas Export/import notes or use same memory
Using memories for temporary grouping Memories are permanent structures, expensive to delete Use collections for temporary grouping
Over-segmenting by topic Reduces discoverability, creates knowledge silos Use single memory with tags unless >10k notes per topic
Creating memories just for different embedding models Embedding configs are bound to embedding sets, not memories Use multiple embedding sets within a single memory for different models
Expecting auto-migration between memories No built-in migration, must export/import Plan memory structure upfront, migrations are manual

MCP Tool Quick Reference

Tool Purpose Uses Active Memory? Returns Error if Non-Default?
select_memory Set active memory for MCP session Sets it (no active required) No
get_active_memory Check current active memory No (retrieves state) No
capture_knowledge Create/update/delete notes Yes No
search Search notes (text/semantic/combined/federated) Yes No (per-schema pools enable search in all memories)
manage_tags Tag operations (list/add/remove/update) Yes No
manage_collection Collection operations Yes No
record_provenance Provenance tracking Yes No
manage_concepts SKOS concept operations Yes No
manage_embeddings Embedding set CRUD, membership, refresh Yes No

Performance Characteristics

Memory Creation

  • Time: ~200ms (clones all per-memory tables + indexes)
  • Storage: ~1MB empty, scales with data
  • Limit: 10 memories by default (scale via MAX_MEMORIES env var per hardware tier)

Memory Cloning

  • Time: Proportional to data size (indexes rebuilt)
  • Storage: Doubles (full copy including embeddings)
  • Use case: Backups, snapshots, archival

Memory Deletion

  • Time: ~500ms (CASCADE drop)
  • IRREVERSIBLE: No recovery, no soft delete
  • WARNING: Deletes all notes, tags, collections, embeddings, attachments

Search Performance

  • Default memory: <100ms typical for <50k notes
  • Non-default memory: Uses per-schema connection pool; similar performance to default
  • Federated search: Queries multiple memories in parallel with unified ranking

Decision Flow for Agents

START: Does user need multi-memory?
  |
  +--> NO legal/regulatory isolation required?
  |    +--> <50k total notes expected?
  |         +--> Use SINGLE MEMORY (default)
  |
  +--> Multi-tenant SaaS?
  |    +--> YES --> Strategy A: One memory per tenant
  |
  +--> Independent projects with <30% concept overlap?
  |    +--> YES --> Strategy B: One memory per project
  |
  +--> Content types need different retention/chunking policies?
  |    +--> YES --> Strategy C: One memory per domain
  |    (Note: different embedding models do NOT require separate memories — use embedding sets)
  |
  +--> Compliance requires historical immutability?
  |    +--> YES --> Strategy D: Rolling time-based archives
  |
  +--> Default to SINGLE MEMORY, use tags/collections

Migration Path

From Single Memory to Multi-Memory

  1. Create target memory: POST /api/v1/memories
  2. Export notes: Use bulk export with tag filter
  3. Import to new memory: POST /api/v1/notes with X-Fortemi-Memory header
  4. Verify: Check note counts match
  5. Update agents: Change MCP select_memory or API headers
  6. Archive old memory: Clone to backup, then delete (optional)

From Multi-Memory to Single Memory

  1. Export all memories: Per-memory bulk export
  2. Tag for provenance: Add source-memory:{name} tag to all notes
  3. Import to default memory: Omit X-Fortemi-Memory header
  4. Update links: Re-link notes (links don't migrate across memories)
  5. Verify: Check total note counts
  6. Delete old memories: After verification period

Operational Checklist

  • Plan memory structure upfront (migrations are manual)
  • Limit to <50 memories for performance
  • Set X-Fortemi-Memory header explicitly in all HTTP API calls
  • Use select_memory at start of MCP sessions
  • Use search for per-memory search, search_memories_federated for cross-memory
  • Verify auto-maintenance is running (maintenance covers all schemas automatically)
  • Clone memories before risky operations (deletion is irreversible)
  • Monitor memory count (list_archives) against MAX_MEMORIES limit
  • Use tags/collections for organization within memories
  • Document memory naming conventions for team

Future Features (Not Yet Implemented)

  • Cross-memory links: Reference notes across schema boundaries
  • Memory templates: Pre-configured memories with tags/collections
  • Auto-archival: Scheduled cloning to time-based archives
  • Memory-level permissions: Fine-grained access control per memory

Summary

Default recommendation: Use single memory with tags/collections unless you have:

  1. Legal/regulatory data isolation requirements (multi-tenant, client separation)
  2. 50k notes with clear segmentation boundaries (projects, time periods)

Note: Different embedding models do NOT require separate memories — use embedding sets within a single memory instead.

Key principle: Memories are heavyweight. Over-segmentation reduces discoverability. When in doubt, use tags.