Skip to content

Latest commit

 

History

History
834 lines (697 loc) · 33.6 KB

File metadata and controls

834 lines (697 loc) · 33.6 KB

OpenClaw Integration Workflow

This document explains how aimemo works with OpenClaw skills, including the complete architecture, data flow, and runtime behavior.

Table of Contents


Architecture Overview

┌──────────────────────────────────────────────────────────────────┐
│                    OpenClaw Gateway Process                       │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │               Agent Runtime                              │    │
│  │                                                          │    │
│  │  User Request                                            │    │
│  │    ↓                                                     │    │
│  │  [Agent matches request to skill]                       │    │
│  │    ↓                                                     │    │
│  │  ┌─────────────────────────────────────────────┐       │    │
│  │  │  Skill: github-pr-reviewer                  │       │    │
│  │  │  (SKILL.md instructions)                    │       │    │
│  │  │                                              │       │    │
│  │  │  → Load memory:                             │       │    │
│  │  │    memory_context({                         │       │    │
│  │  │      context: "github-pr-reviewer"          │       │    │
│  │  │    })                                        │       │    │
│  │  └──────────────────┬──────────────────────────┘       │    │
│  │                     │                                   │    │
│  │                     │ JSON-RPC over stdio               │    │
│  │                     ▼                                   │    │
│  │  ┌──────────────────────────────────────────────────┐  │    │
│  │  │         MCP Client (Gateway-managed)             │  │    │
│  │  │                                                   │  │    │
│  │  │  Request:                                        │  │    │
│  │  │  {                                               │  │    │
│  │  │    "method": "tools/call",                       │  │    │
│  │  │    "params": {                                   │  │    │
│  │  │      "name": "memory_context",                   │  │    │
│  │  │      "arguments": {                              │  │    │
│  │  │        "context": "github-pr-reviewer"           │  │    │
│  │  │      }                                           │  │    │
│  │  │    }                                             │  │    │
│  │  │  }                                               │  │    │
│  │  └───────────────────┬──────────────────────────────┘  │    │
│  └────────────────────────┼─────────────────────────────────┘    │
│                           │                                      │
│                           │ stdio (stdin/stdout)                 │
│                           │                                      │
└───────────────────────────┼──────────────────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────────────────┐
│                  aimemo serve (MCP Server)                         │
│                  CWD: ~/.openclaw/workspace                        │
│                                                                    │
│  ┌──────────────────────────────────────────────────────────┐    │
│  │  1. Receive MCP request                                  │    │
│  │     {context: "github-pr-reviewer"}                      │    │
│  │                                                           │    │
│  │  2. Call FindProjectDB("github-pr-reviewer")             │    │
│  │     → Walk up from CWD looking for .aimemo/              │    │
│  │     → Found: ~/.openclaw/workspace/.aimemo/              │    │
│  │     → Return: .../memory-github-pr-reviewer.db           │    │
│  │                                                           │    │
│  │  3. Open database                                         │    │
│  │     db.Open(".../memory-github-pr-reviewer.db")          │    │
│  │                                                           │    │
│  │  4. Execute query                                         │    │
│  │     - Calculate importance score (recency + access)      │    │
│  │     - BM25 full-text search                              │    │
│  │     - Return top 20 observations                         │    │
│  │                                                           │    │
│  │  5. Return result                                         │    │
│  │     {                                                     │    │
│  │       "content": [{                                       │    │
│  │         "type": "text",                                   │    │
│  │         "text": "Last session: ...\n                      │    │
│  │                  Preferences: snake_case, no trailing ," │    │
│  │       }]                                                  │    │
│  │     }                                                     │    │
│  └──────────────────────────────────────────────────────────┘    │
│                                                                    │
└────────────────────────────┬───────────────────────────────────────┘
                             │
                             │ JSON-RPC Response
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Agent receives memory context                 │
│                                                                  │
│  → Use loaded preferences to perform task                       │
│  → Store new discoveries:                                       │
│                                                                  │
│    memory_store({                                               │
│      context: "github-pr-reviewer",                             │
│      entities: [{                                               │
│        name: "code-style-preferences",                          │
│        entityType: "preferences",                               │
│        observations: ["User prefers early returns"]             │
│      }]                                                         │
│    })                                                           │
└─────────────────────────────────────────────────────────────────┘

Key Components

  1. OpenClaw Gateway: Manages the agent lifecycle and MCP server connections
  2. Agent Runtime: Executes skills based on user requests
  3. MCP Client: Bridges skills to MCP servers via JSON-RPC
  4. aimemo serve: MCP server providing memory tools
  5. SQLite Database: Persistent storage for each skill's memory

Initialization Flow

One-Time Setup (Per Machine)

┌────────────────────────────────────────────────────────────┐
│ Step 1: Install aimemo                                     │
│                                                             │
│ Linux/macOS:                                               │
│   curl -sSL https://raw.githubusercontent.com/MyAgentHubs/aimemo/main/install.sh | bash           │
│                                                             │
│ Or via Homebrew (macOS):                                   │
│   brew install MyAgentHubs/tap/aimemo                      │
│                                                             │
│ Verify:                                                     │
│   aimemo --version                                         │
└────────────────────────────────────────────────────────────┘
         ↓
┌────────────────────────────────────────────────────────────┐
│ Step 2: Register MCP server with OpenClaw                  │
│                                                             │
│   claude mcp add-json aimemo-memory \                      │
│     '{"command":"aimemo","args":["serve"]}'                │
│                                                             │
│ Or add to ~/.openclaw/openclaw.json:                       │
│   {                                                         │
│     "mcpServers": {                                         │
│       "aimemo-memory": {                                    │
│         "command": "aimemo",                                │
│         "args": ["serve"]                                   │
│       }                                                     │
│     }                                                       │
│   }                                                         │
└────────────────────────────────────────────────────────────┘
         ↓
┌────────────────────────────────────────────────────────────┐
│ Step 3: Initialize workspace memory                        │
│                                                             │
│   cd ~/.openclaw/workspace                                 │
│   aimemo init                                              │
│                                                             │
│ Creates: ~/.openclaw/workspace/.aimemo/memory.db           │
└────────────────────────────────────────────────────────────┘
         ↓
┌────────────────────────────────────────────────────────────┐
│ Step 4: Restart OpenClaw                                   │
│                                                             │
│ The Gateway will now spawn aimemo serve on startup         │
└────────────────────────────────────────────────────────────┘

Per-Skill Setup (Optional)

If a skill wants isolated memory, it should declare in SKILL.md:

## Setup

This skill uses aimemo for persistent memory. The memory is automatically
isolated using the context parameter - no manual setup required.

On first use, aimemo will create:
  ~/.openclaw/workspace/.aimemo/memory-{skill-name}.db

Runtime Call Chain

Request Flow

User says: "Review this PR with my code style preferences"
    ↓
┌─────────────────────────────────────────────────────────┐
│ 1. OpenClaw Agent matches to github-pr-reviewer skill   │
└───────────────────────┬─────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────────┐
│ 2. Skill instruction: Load memory first                 │
│    → Call memory_context({context: "github-pr-..."})   │
└───────────────────────┬─────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────────┐
│ 3. MCP Client sends JSON-RPC request to aimemo serve    │
│    Protocol: stdio (stdin/stdout)                       │
└───────────────────────┬─────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────────┐
│ 4. aimemo serve processes request:                      │
│    a. Parse context: "github-pr-reviewer"               │
│    b. Find DB: memory-github-pr-reviewer.db             │
│    c. Query: SELECT with importance scoring             │
│    d. Return: Top observations + journal entries        │
└───────────────────────┬─────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────────┐
│ 5. Agent receives memory context                        │
│    Example: "Last session: prefer snake_case..."        │
└───────────────────────┬─────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────────┐
│ 6. Agent performs task with loaded context              │
│    → Fetch PR code                                      │
│    → Review with learned preferences                    │
│    → Provide feedback to user                           │
└───────────────────────┬─────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────────┐
│ 7. Agent stores new learnings                           │
│    → Call memory_store({context: "...", entities: ...}) │
└───────────────────────┬─────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────────┐
│ 8. aimemo persists to SQLite                            │
│    → Update observations table                          │
│    → Update access timestamps                           │
│    → Recalculate importance scores                      │
└─────────────────────────────────────────────────────────┘

Timing (Typical)

  • MCP call overhead: < 10ms
  • Memory query (cold): < 50ms
  • Memory query (cached): < 5ms
  • Memory store: < 20ms

File System Layout

~/.openclaw/
├── openclaw.json                  # OpenClaw config (MCP registration)
│   {
│     "mcpServers": {
│       "aimemo-memory": {
│         "command": "aimemo",
│         "args": ["serve"]
│       }
│     }
│   }
│
└── workspace/
    ├── .aimemo/                   # aimemo memory root
    │   ├── memory.db              # Default/shared memory
    │   ├── memory-github-pr-reviewer.db      # ← Skill A
    │   ├── memory-slack-notifier.db          # ← Skill B
    │   └── memory-jira-automation.db         # ← Skill C
    │
    └── skills/                    # OpenClaw skills
        ├── github-pr-reviewer/
        │   └── SKILL.md           # ← Declares aimemo usage
        │
        ├── slack-notifier/
        │   └── SKILL.md
        │
        └── jira-automation/
            └── SKILL.md

Database File Naming Convention

  • No context parameter: memory.db
  • With context parameter: memory-{context}.db

Example:

// Uses memory.db (default/shared)
memory_context({})

// Uses memory-github-pr-reviewer.db (isolated)
memory_context({context: "github-pr-reviewer"})

Context Name Requirements:

  • Context names are sanitized to [a-z0-9-] (lowercase alphanumeric and hyphens)
  • Invalid characters are replaced with -, leading/trailing - are trimmed
  • Empty or invalid context names default to memory.db

Examples:

"My-Skill"      → memory-my-skill.db
"skill_name"    → memory-skill-name.db
"SKILL@123"     → memory-skill-123.db
"---"           → memory.db (sanitizes to empty, uses default)

Memory Isolation

Why Per-Skill Isolation Matters

┌────────────────────────────────────────────────────────┐
│  Skill A: github-pr-reviewer                           │
│  context: "github-pr-reviewer"                         │
│     ↓                                                  │
│  memory-github-pr-reviewer.db                          │
│  ├─ code-style-preferences                             │
│  ├─ review-patterns                                    │
│  └─ user-feedback-history                              │
│                                                         │
│  ✅ Only sees its own data                             │
│  ❌ Cannot see slack-notifier's data                   │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│  Skill B: slack-notifier                               │
│  context: "slack-notifier"                             │
│     ↓                                                  │
│  memory-slack-notifier.db                              │
│  ├─ notification-preferences                           │
│  ├─ sent-message-hashes (dedup)                        │
│  └─ user-timezone-settings                             │
│                                                         │
│  ✅ Only sees its own data                             │
│  ❌ Cannot see github-pr-reviewer's data               │
└────────────────────────────────────────────────────────┘

How Isolation Works

  1. Context Parameter: Each skill passes its unique identifier
  2. Separate Databases: Different .db files prevent cross-contamination
  3. FTS Isolation: Full-text search only queries the skill's database
  4. No Shared State: Skills cannot accidentally read/write each other's memory

When to Share Memory

Use the default database (no context parameter) for:

  • Cross-skill shared knowledge
  • Project-wide facts
  • User preferences that apply globally

Example:

// Shared: all skills can access
memory_store({
  entities: [{
    name: "user-timezone",
    entityType: "preference",
    observations: ["UTC+8"]
  }]
})

// Isolated: only slack-notifier can access
memory_store({
  context: "slack-notifier",
  entities: [{
    name: "notification-times",
    entityType: "schedule",
    observations: ["Send daily digest at 9am"]
  }]
})

Data Flow

Memory Store Flow

Skill discovers new pattern
        ↓
memory_store({
  context: "github-pr-reviewer",
  entities: [{
    name: "code-style",
    entityType: "preferences",
    observations: ["Use early returns"]
  }]
})
        ↓
┌─────────────────────────────────────────┐
│ aimemo MCP server                       │
│                                          │
│ 1. Parse request                         │
│    context = "github-pr-reviewer"       │
│                                          │
│ 2. Open/create DB                       │
│    memory-github-pr-reviewer.db         │
│                                          │
│ 3. Check if entity exists                │
│    SELECT * WHERE name='code-style'     │
│                                          │
│ 4. Upsert entity                         │
│    INSERT OR UPDATE                     │
│                                          │
│ 5. Add observations                      │
│    - Deduplicate                        │
│    - Update timestamps                  │
│    - Recalculate importance             │
│                                          │
│ 6. Update FTS index                      │
│    INSERT INTO fts_observations         │
└─────────────────────────────────────────┘
        ↓
Database persisted

Memory Context Flow

Skill starts work
        ↓
memory_context({
  context: "github-pr-reviewer"
})
        ↓
┌─────────────────────────────────────────┐
│ aimemo MCP server                       │
│                                          │
│ 1. Open DB                               │
│    memory-github-pr-reviewer.db         │
│                                          │
│ 2. Calculate importance scores           │
│    score = 0.6/LOG(hours_ago+2)         │
│          + 0.4*LOG(access_count+1)      │
│                                          │
│ 3. Query recent + important              │
│    - Last 24h by default                │
│    - Top 20 by importance               │
│    - Include journal entries            │
│                                          │
│ 4. Format response                       │
│    - Group by entity                    │
│    - Show relationships                 │
│    - Include metadata                   │
└─────────────────────────────────────────┘
        ↓
Skill receives context

Memory Search Flow

Skill needs specific info
        ↓
memory_search({
  context: "github-pr-reviewer",
  query: "error handling"
})
        ↓
┌─────────────────────────────────────────┐
│ aimemo MCP server                       │
│                                          │
│ 1. Open DB                               │
│    memory-github-pr-reviewer.db         │
│                                          │
│ 2. FTS5 search                           │
│    SELECT * FROM fts_observations       │
│    WHERE fts_observations MATCH 'error' │
│    AND fts_observations MATCH 'handling'│
│                                          │
│ 3. BM25 ranking                          │
│    - Term frequency                     │
│    - Inverse document frequency         │
│    - Document length normalization      │
│                                          │
│ 4. Apply importance weights              │
│    final_score = bm25 * importance      │
│                                          │
│ 5. Return top results                    │
│    - Limit: 10 (default)                │
│    - Include context snippets           │
└─────────────────────────────────────────┘
        ↓
Skill receives search results

Best Practices

For Skill Authors

1. Always Use Context Parameter

✅ GOOD:
memory_context({context: "my-skill-name"})

❌ BAD:
memory_context({})  // Uses shared DB, risks collision

2. Load Memory First

## Instructions

When the user asks you to [do task]:

1. **FIRST**: Load memory
   memory_context({context: "my-skill-name"})

2. Then proceed with the task

3. Store learnings before finishing

3. Store Progressively

// ✅ Store as you learn
memory_store({
  context: "my-skill",
  entities: [{name: "pattern-1", ...}]
})

// Later...
memory_store({
  context: "my-skill",
  entities: [{name: "pattern-2", ...}]
})

// ❌ Don't try to store everything at once
// (risks losing data if interrupted)

4. Write Journal Entries

// At session end
memory_store({
  context: "my-skill",
  journal: "Completed: task X. In progress: task Y. Blocker: Z."
})

5. Use Semantic Entity Names

// ✅ GOOD: Clear, searchable
{name: "user-code-style-preferences", entityType: "preferences"}
{name: "common-bug-patterns", entityType: "knowledge"}

// ❌ BAD: Vague, hard to search
{name: "data", entityType: "stuff"}
{name: "temp", entityType: "thing"}

For OpenClaw Administrators

1. Backup Memory Databases

# Backup all skill memories
tar -czf aimemo-backup-$(date +%Y%m%d).tar.gz \
  ~/.openclaw/workspace/.aimemo/

2. Monitor Database Size

# Check size of all memory databases
du -sh ~/.openclaw/workspace/.aimemo/*.db

3. Inspect Memory (Debugging)

# List memories for a specific skill
aimemo list --context github-pr-reviewer

# Search within a skill's memory
aimemo search "error handling" --context github-pr-reviewer

# Export for inspection
aimemo export --context github-pr-reviewer --format json > memory.json

4. Clean Up Old Memories

# Soft-delete an entity (recoverable)
aimemo forget entity-name --context github-pr-reviewer

# Hard-delete permanently
aimemo forget entity-name --context github-pr-reviewer --permanent

Troubleshooting

Problem: Skill can't find aimemo

Symptoms: Error like "tool 'memory_context' not found"

Solutions:

  1. Verify MCP server registration:

    cat ~/.openclaw/openclaw.json | grep aimemo
  2. Check if aimemo is in PATH:

    which aimemo
  3. Try absolute path in config:

    {
      "mcpServers": {
        "aimemo-memory": {
          "command": "/usr/local/bin/aimemo",
          "args": ["serve"]
        }
      }
    }

Problem: Memory not persisting

Symptoms: Skill seems to forget learnings between sessions

Solutions:

  1. Verify database exists:

    ls -lh ~/.openclaw/workspace/.aimemo/
  2. Check database is writable:

    touch ~/.openclaw/workspace/.aimemo/test && rm ~/.openclaw/workspace/.aimemo/test
  3. Verify skill is using correct context:

    • Check SKILL.md instructions
    • Ensure context parameter matches skill name

Problem: Skills sharing memory unexpectedly

Symptoms: Skill A sees data from Skill B

Solution:

  • Both skills are missing context parameter
  • Add context: "skill-name" to all memory tool calls

Problem: MCP server not starting

Symptoms: OpenClaw logs show "failed to start MCP server"

Solutions:

  1. Test aimemo serve manually:

    cd ~/.openclaw/workspace
    aimemo serve
    # Should wait for stdin, not exit immediately
  2. Check aimemo version:

    aimemo --version
    # Should be v0.4.0 or later for OpenClaw support
  3. Restart OpenClaw Gateway:

    # macOS
    launchctl stop com.openclaw.gateway
    launchctl start com.openclaw.gateway
    
    # Linux
    systemctl --user restart openclaw-gateway

Problem: Database corruption

Symptoms: Error like "database disk image is malformed"

Solutions:

  1. Try SQLite recovery:

    cd ~/.openclaw/workspace/.aimemo
    sqlite3 memory-skill-name.db ".recover" | sqlite3 memory-skill-name-recovered.db
    mv memory-skill-name.db memory-skill-name.db.backup
    mv memory-skill-name-recovered.db memory-skill-name.db
  2. If unrecoverable, restore from backup or reinitialize:

    rm ~/.openclaw/workspace/.aimemo/memory-skill-name.db
    # Skill will auto-create on next use

Problem: Search returns irrelevant results

Symptoms: memory_search returns unrelated observations

Solutions:

  1. Use more specific queries:

    // ❌ Too broad
    memory_search({query: "code"})
    
    // ✅ Specific
    memory_search({query: "error handling patterns"})
  2. Filter by entity type:

    memory_search({
      query: "preferences",
      type: "preferences"  // Only search preferences
    })
  3. Use tags for better organization:

    memory_store({
      entities: [{
        name: "style-rule-1",
        observations: ["..."],
        tags: ["code-style", "naming"]
      }]
    })
    
    // Later search by tag
    memory_search({tags: ["code-style"]})

Advanced Topics

Custom Memory Contexts

You can create hierarchical memory by using dotted context names:

// Project-level memory
memory_context({context: "myproject"})

// Feature-specific memory
memory_context({context: "myproject.auth"})
memory_context({context: "myproject.billing"})

Note: These are still independent databases - aimemo doesn't currently support hierarchical queries.

Sharing Memory Between Skills

If two skills need to share specific knowledge:

  1. Option A: Use a shared context

    // Both skills use same context
    memory_store({
      context: "shared-knowledge",
      entities: [...]
    })
  2. Option B: Use the default database

    // No context = shared
    memory_store({
      entities: [...]
    })

Performance Tuning

For skills with large memories (>1000 entities):

  1. Adjust limit in queries:

    memory_context({
      context: "my-skill",
      limit: 50  // Default is 20
    })
  2. Use targeted searches:

    // Instead of loading all context
    memory_search({
      context: "my-skill",
      query: "specific topic",
      limit: 5
    })
  3. Archive old data:

    # Export old memories
    aimemo export --context my-skill > backup.json
    
    # Clear database
    rm ~/.openclaw/workspace/.aimemo/memory-my-skill.db
    
    # Re-import only recent data
    # (manual filtering required)

See Also