@tablestore/openclaw-mem0
v0.8.4
Published
Mem0 memory backend for OpenClaw with Alibaba Cloud Tablestore vector store
Readme
@tablestore/openclaw-mem0
Long-term memory for OpenClaw agents, powered by Mem0 with Alibaba Cloud Tablestore as the vector store backend.
Your agent forgets everything between sessions. This plugin fixes that. It watches conversations, extracts what matters, and brings it back when relevant — automatically.
What's New in 0.8.4
- Add
captureFilterPromptso auto-capture and explicitmemory_storecalls can use a narrower extraction prompt without replacing the plugin's global default prompt. - Add
recallFilterPromptin open-source mode so the plugin can decide whether recall is needed and optionally rewrite the search query before memory lookup. - Expose
generateResponse()on the bundled OSSMemoryclass so the plugin can evaluate recall filters with the configured Mem0 LLM. - Preserve the current
0.8.3Tablestore compatibility fixes, per-agent table isolation, batch embedding, and fire-and-forget capture path while porting the prompt-filter feature. - Keep the build flow on plain
npm install --ignore-scriptsand ignore.pnpm-store/artifacts.
What's New in 0.8.3
- Add
legacyMemoryCompatibilityfor Tablestore to support the legacy-memory-compatible row layout with[document_id, tenant_id], string embeddings, andpayload_source/payload/textcolumns. - Fail fast when an existing table uses a different primary key shape than the selected schema mode.
- Downgrade exact-match filters to
payloadterm queries when reusing old mem0 Python search indexes that do not exposemeta_*fields. - Pass
embeddingDimsto OpenAI-compatible embedding requests so DashScope/OpenAI/Azure dimensions stay aligned with the configured Tablestore index dimension. - Keep plugin builds unminified to avoid the runtime regressions seen in minified Tablestore/OpenAPI bundles.
How it works
Auto-Recall — Before the agent responds, the plugin searches Mem0 for memories that match the current message and injects them into context.
Auto-Capture — After the agent responds, the plugin sends the exchange to Mem0. Mem0 decides what's worth keeping — new facts get stored, stale ones updated, duplicates merged.
Both run silently. No prompting, no configuration, no manual calls.
Short-term vs long-term memory
Memories are organized into two scopes:
Session (short-term) — Auto-capture stores memories scoped to the current session via Mem0's
run_id/runIdparameter. These are contextual to the ongoing conversation and automatically recalled alongside long-term memories.User (long-term) — The agent can explicitly store long-term memories using the
memory_storetool (withlongTerm: true, the default). These persist across all sessions for the user.
During auto-recall, the plugin searches both scopes and presents them separately — long-term memories first, then session memories — so the agent has full context.
The agent tools (memory_search, memory_list) accept a scope parameter ("session", "long-term", or "all") to control which memories are queried. The memory_store tool accepts a longTerm boolean (default: true) to choose where to store.
All new parameters are optional and backward-compatible — existing configurations work without changes.
Prompt filters
Two optional prompt-filter settings let you customize capture and recall without replacing the whole plugin configuration:
captureFilterPromptnarrows what gets extracted during auto-capture and explicitmemory_storecalls.recallFilterPromptruns before auto-recall in open-source mode and returns JSON telling the plugin whether to search and what query to search for.
Example:
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "alice",
"captureFilterPrompt": "Only store durable user preferences, project facts, and explicit follow-up commitments.",
"recallFilterPrompt": "Recall memory only when the latest user message depends on past preferences, identity, or active project context. Return JSON with should_recall and query."
}
}Notes:
- In platform mode,
captureFilterPromptoverridescustomInstructionsfor add/capture calls. recallFilterPromptis currently evaluated only in open-source mode. Platform mode logs a warning and falls back to the normal recall path.
Per-agent memory isolation
In multi-agent setups, each agent automatically gets its own memory namespace. Session keys following the pattern agent:<agentId>:<uuid> are parsed to derive isolated namespaces (${userId}:agent:${agentId}). Single-agent deployments are unaffected — plain session keys and agent:main:* keys resolve to the configured userId.
How it works:
- The agent's session key is inspected on every recall/capture cycle
- If the key matches
agent:<name>:<uuid>, memories are stored underuserId:agent:<name> - Different agents never see each other's memories unless explicitly queried
Explicit cross-agent queries:
All memory tools (memory_search, memory_store, memory_list, memory_forget) accept an optional agentId parameter to query another agent's namespace:
memory_search({ query: "user's tech stack", agentId: "researcher" })Resolution priority: explicit agentId > explicit userId > session-derived > configured default.
Setup
openclaw plugins install @tablestore/[email protected]Understanding userId
The userId field is a string you choose to uniquely identify the user whose memories are being stored. It is not something you look up in the Mem0 dashboard — you define it yourself.
Pick any stable, unique identifier for the user. Common choices:
- Your application's internal user ID (e.g.
"user_123","[email protected]") - A UUID (e.g.
"550e8400-e29b-41d4-a716-446655440000") - A simple username (e.g.
"alice")
All memories are scoped to this userId — different values create separate memory namespaces. If you don't set it, it defaults to "default", which means all users share the same memory space.
Tip: In a multi-user application, set
userIddynamically per user (e.g. from your auth system) rather than hardcoding a single value.
Platform (Mem0 Cloud)
Get an API key from app.mem0.ai, then add to your openclaw.json:
// plugins.entries
"openclaw-mem0": {
"enabled": true,
"config": {
"apiKey": "${MEM0_API_KEY}",
"userId": "alice" // any unique identifier you choose for this user
}
}Open-Source (Self-hosted)
No Mem0 key needed. Requires OPENAI_API_KEY for default embeddings/LLM.
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "alice" // any unique identifier you choose for this user
}
}Sensible defaults out of the box. To customize the embedder, vector store, or LLM:
"config": {
"mode": "open-source",
"userId": "your-user-id",
"oss": {
"embedder": { "provider": "openai", "config": { "model": "text-embedding-3-small" } },
"vectorStore": { "provider": "qdrant", "config": { "host": "localhost", "port": 6333 } },
"llm": { "provider": "openai", "config": { "model": "gpt-4o" } }
}
}All oss fields are optional. See Mem0 OSS docs for providers.
Open-Source with Alibaba Cloud Tablestore
Use Alibaba Cloud Tablestore as a persistent, serverless vector store. No external vector database to manage.
Minimal configuration (auto-create Tablestore instance)
If you don't have an existing Tablestore instance, omit endpoint and instanceName — the plugin will automatically create a new Tablestore VCU instance in cn-hangzhou on first use:
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "alice",
"oss": {
"vectorStore": {
"provider": "tablestore",
"config": {
"accessKeyId": "${ALIBABA_CLOUD_ACCESS_KEY_ID}",
"accessKeySecret": "${ALIBABA_CLOUD_ACCESS_KEY_SECRET}"
}
}
}
}
}Minimal configuration (with existing Tablestore instance)
If you already have a Tablestore instance, provide endpoint and instanceName:
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "alice",
"oss": {
"vectorStore": {
"provider": "tablestore",
"config": {
"endpoint": "https://your-instance.cn-hangzhou.ots.aliyuncs.com",
"instanceName": "your-instance-name",
"accessKeyId": "${ALIBABA_CLOUD_ACCESS_KEY_ID}",
"accessKeySecret": "${ALIBABA_CLOUD_ACCESS_KEY_SECRET}",
"tableName": "mem0_vectors", // optional, default: "mem0_vectors"
"indexName": "mem0_vectors_index", // optional, default: "<tableName>_index"
"dimension": 1536, // optional, must match your embedding model
"legacyMemoryCompatibility": true // optional, default: false
}
}
}
}
}Full configuration with Alibaba Cloud Bailian (DashScope)
Use Alibaba Cloud Bailian (DashScope) for both embeddings and LLM, eliminating the need for an OpenAI API key:
"openclaw-mem0": {
"enabled": true,
"config": {
"mode": "open-source",
"userId": "alice",
"oss": {
"embedder": {
"provider": "openai",
"config": {
"apiKey": "${DASHSCOPE_API_KEY}",
"model": "text-embedding-v3",
"baseURL": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"embeddingDims": 1024
}
},
"vectorStore": {
"provider": "tablestore",
"config": {
// endpoint and instanceName are optional — omit to auto-create a new instance
"endpoint": "https://your-instance.cn-hangzhou.ots.aliyuncs.com",
"instanceName": "your-instance-name",
"accessKeyId": "${ALIBABA_CLOUD_ACCESS_KEY_ID}",
"accessKeySecret": "${ALIBABA_CLOUD_ACCESS_KEY_SECRET}",
"dimension": 1024
}
},
"llm": {
"provider": "openai",
"config": {
"apiKey": "${DASHSCOPE_API_KEY}",
"model": "qwen-plus",
"baseURL": "https://dashscope.aliyuncs.com/compatible-mode/v1"
}
}
}
}
}Note: DashScope's
text-embedding-v3model outputs 1024-dimensional vectors by default. Make sure thedimensionin the Tablestore config matches your embedding model's output dimension.
When legacyMemoryCompatibility is enabled, the plugin writes rows in the legacy-memory-compatible Tablestore layout:
- Primary key:
[document_id, tenant_id] - Fixed
tenant_id:"__default" embeddingstored as a JSON string instead of binarypayload_source,payload, andtextcolumns are written alongside the existingmeta_*filter columns- When reusing older mem0 Python-managed search indexes that do not have
meta_*filter fields, exact-match filters are downgraded topayloadmatch queries over the storedkey=valueterms; range filters still require themeta_*fields to exist
The switch is off by default. Existing installations keep using the current single-primary-key schema unless you opt in.
Recommended usage:
- Enable it only for new tables, or point
tableNameto a fresh table - If the target table already exists with the old primary key layout, keep the switch off or use a different
tableName - This switch changes the table primary key shape; the plugin does not auto-migrate existing Tablestore tables
The Tablestore provider automatically creates the data table and search index on first use. The search index includes a VECTOR field for cosine-similarity KNN search and KEYWORD fields for metadata filtering (user_id, agent_id, run_id, etc.).
Building and installing the plugin
Since the Tablestore provider is bundled into a custom build of mem0ai, you need to build the plugin from source:
# 1. Clone the repo and build the plugin tgz
cd mem0-tablestore
bash shell/build-plugin.sh
# 2. Install on any machine (no npm registry needed)
cd openclaw
openclaw plugins install ./tablestore-openclaw-mem0-0.8.4.tgz
# 3. Rebuild native addons (required for better-sqlite3)
cd ~/.openclaw/extensions/openclaw-mem0
npm rebuild better-sqlite3
# 4. Restart the gateway to load the new plugin
openclaw gateway restartThe build script bundles the modified mem0ai and the tablestore SDK into the plugin package, so end-users do not need to install any extra dependencies.
Troubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| Cannot read properties of null (reading 'QueryType') | Tablestore SDK failed to load in the runtime environment | Check that tablestore is in node_modules. Run node -e "console.log(require('tablestore').QueryType)" in the plugin directory to verify. |
| 401 Incorrect API key provided | Embedding requests are being sent to OpenAI instead of DashScope | Ensure baseURL is set in the embedder config (e.g. "baseURL": "https://dashscope.aliyuncs.com/compatible-mode/v1"). |
| Could not locate the bindings file (better-sqlite3) | Native addon not compiled for the current platform | Run cd ~/.openclaw/extensions/openclaw-mem0 && npm rebuild better-sqlite3. |
| OTSObjectNotExist on first use | Normal — the table/index is being created | Wait 5-10 seconds and retry. The provider auto-creates tables on first initialize(). |
| Plugin not loading after install | openclaw config not updated | Ensure openclaw.json has "openclaw-mem0" in plugins.allow and plugins.slots.memory. Restart gateway after config changes. |
Agent tools
The agent gets five tools it can call during conversations:
| Tool | Description |
|------|-------------|
| memory_search | Search memories by natural language. Optional agentId to scope to a specific agent. |
| memory_list | List all stored memories for a user. Optional agentId to scope to a specific agent. |
| memory_store | Explicitly save a fact. Optional agentId to store under a specific agent's namespace. |
| memory_get | Retrieve a memory by ID |
| memory_forget | Delete by ID or by query. Optional agentId to scope deletion to a specific agent. |
CLI
# Search all memories (long-term + session)
openclaw mem0 search "what languages does the user know"
# Search only long-term memories
openclaw mem0 search "what languages does the user know" --scope long-term
# Search only session/short-term memories
openclaw mem0 search "what languages does the user know" --scope session
# Stats
openclaw mem0 stats
# Search a specific agent's memories
openclaw mem0 search "user preferences" --agent researcher
# Stats for a specific agent
openclaw mem0 stats --agent researcherOptions
General
| Key | Type | Default | |
|-----|------|---------|---|
| mode | "platform" | "open-source" | "platform" | Which backend to use |
| userId | string | "default" | Any unique identifier you choose for the user (e.g. "alice", "user_123"). All memories are scoped to this value. Not found in any dashboard — you define it yourself. |
| autoRecall | boolean | true | Inject memories before each turn |
| autoCapture | boolean | true | Store facts after each turn |
| topK | number | 5 | Max memories per recall |
| searchThreshold | number | 0.5 | Min similarity (0–1) |
Platform mode
| Key | Type | Default | |
|-----|------|---------|---|
| apiKey | string | — | Required. Mem0 API key (supports ${MEM0_API_KEY}) |
| orgId | string | — | Organization ID |
| projectId | string | — | Project ID |
| enableGraph | boolean | false | Entity graph for relationships |
| customInstructions | string | (built-in) | Extraction rules — what to store, how to format |
| customCategories | object | (12 defaults) | Category name → description map for tagging |
| captureFilterPrompt | string | — | Optional per-call capture prompt. Overrides customInstructions for add/capture calls |
Open-source mode
Works with zero extra config. The oss block lets you swap out any component:
| Key | Type | Default | |
|-----|------|---------|---|
| customPrompt | string | (built-in) | Extraction prompt for memory processing |
| captureFilterPrompt | string | — | Optional per-call extraction prompt for auto-capture and memory_store |
| recallFilterPrompt | string | — | Optional LLM gate for auto-recall. Open-source mode only |
| oss.embedder.provider | string | "openai" | Embedding provider ("openai", "ollama", etc.) |
| oss.embedder.config | object | — | Provider config: apiKey, model, baseURL, embeddingDims |
| oss.vectorStore.provider | string | "memory" | Vector store ("memory", "qdrant", "chroma", etc.) |
| oss.vectorStore.config | object | — | Provider config: host, port, collectionName, dimension |
| oss.llm.provider | string | "openai" | LLM provider ("openai", "anthropic", "ollama", etc.) |
| oss.llm.config | object | — | Provider config: apiKey, model, baseURL, temperature |
| oss.historyDbPath | string | — | SQLite path for memory edit history |
Everything inside oss is optional — defaults use OpenAI embeddings (text-embedding-3-small), in-memory vector store, and OpenAI LLM. Override only what you need.
Tablestore vector store config
When using "provider": "tablestore" (or "aliyun_tablestore"), the following config keys are available:
| Key | Type | Default | |
|-----|------|---------|---|
| endpoint | string | — | Optional. Tablestore instance endpoint URL. If omitted, a new instance is auto-created. |
| instanceName | string | — | Optional. Tablestore instance name. If omitted, a new instance is auto-created. |
| accessKeyId | string | — | Alibaba Cloud AccessKey ID. Required unless roleName is set. (supports ${ALIBABA_CLOUD_ACCESS_KEY_ID}) |
| accessKeySecret | string | — | Alibaba Cloud AccessKey Secret. Required unless roleName is set. (supports ${ALIBABA_CLOUD_ACCESS_KEY_SECRET}) |
| roleName | string | — | ECS RAM Role name for automatic credential retrieval. When set, accessKeyId and accessKeySecret are fetched from the ECS metadata service and refreshed automatically before expiration. (supports ${TABLESTORE_ROLE_NAME}) |
| stsToken | string | — | Optional STS token for temporary credentials |
| regionId | string | — | Region ID (e.g. "cn-hangzhou"). Required when auto-provisioning. (supports ${TABLESTORE_REGION_ID}) |
| tableName | string | "mem0_vectors" | Data table name |
| indexName | string | "<tableName>_index" | Search index name |
| dimension | number | 1536 | Vector dimension (must match your embedding model) |
| legacyMemoryCompatibility | boolean | false | Use the legacy-memory-compatible row schema with dual primary key, tenant_id="__default", string embeddings, and payload_source/payload/text columns. Older mem0 Python indexes missing meta_* fields fall back to exact-match payload term queries |
Authentication modes
The plugin supports two authentication modes:
1. AccessKey (static credentials)
Provide accessKeyId and accessKeySecret directly in the config or via environment variables. Suitable for development or non-ECS environments.
2. ECS RAM Role (automatic, recommended for ECS)
Set roleName to the name of the RAM role attached to your ECS instance. The plugin will automatically fetch temporary STS credentials from the ECS metadata service (http://100.100.100.200/latest/meta-data/ram/security-credentials/{roleName}), cache them, and refresh before expiration. No AccessKey needed in the config.
"vectorStore": {
"provider": "tablestore",
"config": {
"roleName": "EcsRamRoleForTablestore"
// No accessKeyId or accessKeySecret needed
}
}Environment variable: TABLESTORE_ROLE_NAME
License
Apache 2.0
