mcpField
M
memstack

memstack

本地服务
github.com/isiomaC记忆与知识01 周前更新

关于 memstack

MemStack 是面向编码 Agent 和自建 Agent 的开源持久记忆管线,可存储、检索、总结和清理记忆。它支持可插拔存储与 LLM,并让 Claude Code 和 Codex 在同一项目中共享记忆。

主要功能

  1. 01跨会话存储和检索记忆
  2. 02自动总结与清理旧记忆
  3. 03按相关性、时效和重要性排序
  4. 04支持可插拔存储与 LLM
  5. 05Claude Code 与 Codex 共享项目记忆
  6. 06凭据检测与记忆范围隔离

适用场景

  1. 01让编码 Agent 在不同会话间保留项目约定
  2. 02让 Claude Code 与 Codex 共享项目决策
  3. 03为自建 Agent 添加记忆管线
  4. 04控制检索上下文的令牌预算

MCP 相关

MCP 服务向 Claude Code 和 Codex 提供记忆存储、检索、读取、删除和统计工具。会话开始钩子可将项目的重要记忆加载到新会话中。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"isiomac-memstack": {
"command": "npx",
"args": [
"-y",
"@memstack/mcp"
]
}
}
}
启动命令npx -y @memstack/mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@memstack/mcp0.8.5

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

MemStack

为 AI Agent 提供持久记忆。Claude Code 和 Codex 共享每个项目的一份记忆,你自己的 Agent 也能使用同一套流程:存储、检索、摘要和清理。

网站和安装指南:memstack.stalewell.com

npm version skills.sh MCP Registry memstack MCP server – quality and maintenance score on Glama CI License: MIT MCP Reference

**告诉一个 Agent 项目如何运作,另一个 Agent 就能直接上手。**Claude Code 学会一条约定;新的 Codex 会话就能回忆起它,并正确完成任务:

Claude Code 将项目约定保存到 MemStack;新的 Codex 会话回忆起该约定,并写出 pnpm install 和 fly deploy

npm install -g @memstack/cli @memstack/mcp better-sqlite3@^11.10.0   # MemStack never installs storage drivers
memstack init                  # LLM provider (optional) + store
memstack connect claude-code
memstack connect codex

之后,每个会话都会以项目的关键记忆为起点,两个 Agent 也都会保存你要求它们记住的内容。了解 Harness Memory 的工作原理。

正在构建自己的 Agent?

# Use MemStack in your application
npm install @memstack/core

# Give your coding agents the MemStack skill (-g: every project; omit it for this project only)
npx skills add isiomaC/memstack -g

@memstack/core 是运行时 SDK;Agent Skill 则教兼容的编码 Agent 如何正确集成和使用 MemStack。不加 -g 时,Skill 会安装到当前项目(.agents/skills/,并为 Claude Code 创建 .claude/skills/ 链接)。之后可通过 npx skills update 更新。

**问题所在:**AI Agent 会遗忘。每次交互都从零开始。你要么把所有内容塞进上下文窗口(成本高、速度慢、输出质量下降),要么 Agent 就没有过去对话的记忆。

**MemStack 的作用:**在 Agent 和大模型之间运行的持久记忆流程。它会存储每次交互,只检索相关内容,总结旧记忆以节省 token,并自动清理过时记忆。只需调用一个方法,无需任何基础设施。

可以把它看作 Mem0 的开源替代方案——存储可插拔,可自带大模型,完全不受供应商绑定。

memstack MCP server – quality and maintenance score on Glama


目录


为什么选择 MemStack

大模型有上下文窗口,而没有记忆。 两者的区别很重要。

方案问题
把所有内容都塞进上下文成本为 O(n²)。100 次对话 = 数千个 token = 每次调用都要花费数美元。“迷失在中间”效应会降低质量。
直接使用向量数据库你能获得相似度搜索,却无法获得摘要、清理、近期性加权、去重或 token 预算管理。你得自行构建整个流水线。
使用 Mem0专有产品,仅支持通过其托管 API 使用云服务。你无法控制数据存储位置。
使用 MemStack完整流水线,所有组件均可插拔。数据归你所有,基础设施由你掌控。开源。

MemStack 能处理而原始向量数据库无法处理的事项:

  • 摘要 — 将 100 条旧交互压缩为一段文字,保留含义并节省 token
  • 近期性加权 — 越新的记忆越重要;MemStack 会将它们排在更前面
  • 重要性评分 — 记忆并非同等重要;高重要性的记忆在清理后仍会保留
  • 去重 — 在组装上下文时,完全相同或近似相同的记忆会合并
  • Token 预算 — compileContext() 会在调用大模型前告知你将消耗多少 token
  • 记忆类型路由 — 检索时会区别处理交互、摘要和观察
  • 自动清理 — 旧的低重要性记忆会自动清理

快速开始

Claude Code 和 Codex

在不同 Agent harness 之间持久共享记忆:你告诉 Claude Code 的内容,Codex 会在同一项目中记起;反过来也一样。

npm install -g @memstack/cli @memstack/mcp better-sqlite3@^11.10.0  # MemStack never installs storage drivers for you
memstack init                  # choose an LLM provider and a store
memstack connect claude-code
memstack connect codex

然后,在 Claude Code 中说:“记住这个项目使用 Hono。”在同一仓库中的 Codex 里问:“这个项目使用什么框架?”Codex 会回答 Hono。有关其工作原理,请参阅Harness 记忆。

作为库使用
npm install @memstack/core
OpenAI
import { MemStack, OpenAILLMAdapter, OpenAIEmbeddingAdapter, InMemoryStorageAdapter } from "@memstack/core";

const llm = new OpenAILLMAdapter({ apiKey: process.env.OPENAI_API_KEY! });

const memstack = new MemStack({
  llm,
  embedding: new OpenAIEmbeddingAdapter({ apiKey: process.env.OPENAI_API_KEY! }),
  storage: new InMemoryStorageAdapter(),
});
DeepSeek(无嵌入)

DeepSeek 提供聊天补全,但没有嵌入 API。请使用带有 baseURL 的 OpenAI 兼容 LLM 适配器,并省略嵌入适配器 — 检索会回退到关键词 + 近期性 + 重要性排序。你仍可使用完整流水线:存储、总结、清理和 compileContext。

import { MemStack, OpenAILLMAdapter, InMemoryStorageAdapter } from "@memstack/core";

const llm = new OpenAILLMAdapter({
  apiKey: process.env.DEEPSEEK_API_KEY!,
  baseURL: "https://api.deepseek.com/v1",
  defaultModel: "deepseek-flash",
});

const memstack = new MemStack({
  llm,
  storage: new InMemoryStorageAdapter(),
  // No embedding adapter — retrieval uses keyword matching
});
OpenRouter / Together AI / 任何兼容 OpenAI 的 API

相同的模式——更改 baseURL 和 defaultModel:

// OpenRouter
const llm = new OpenAILLMAdapter({
  apiKey: process.env.OPENROUTER_API_KEY!,
  baseURL: "https://openrouter.ai/api/v1",
  defaultModel: "openai/gpt-4o-mini",
});

// Together AI
const llm = new OpenAILLMAdapter({
  apiKey: process.env.TOGETHER_API_KEY!,
  baseURL: "https://api.together.xyz/v1",
  defaultModel: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
});

// Gemini (OpenAI-compatible endpoint)
const llm = new OpenAILLMAdapter({
  apiKey: process.env.GEMINI_API_KEY!,
  baseURL: "https://generativelanguage.googleapis.com/v1beta/openai",
  defaultModel: "gemini-2.0-flash",
});
存储与检索
// 1. Store what happened
await memstack.memory.store({
  actorId: "support-bot-42",
  content: "User reports login failing with error 503 on Chrome 125.",
  tags: ["login", "bug", "chrome"],
  importance: 0.8,
});

// 2. Later, retrieve relevant context
const memories = await memstack.memory.retrieve({
  actorId: "support-bot-42",
  query: "login error",
  strategy: "hybrid",
});

// 3. Assemble an LLM-ready context
const ctx = await memstack.memory.compileContext({
  actorId: "support-bot-42",
  maxTokens: 2000,
});

const response = await llm.complete({
  system: `You are a support bot. Here is what you remember:\n${ctx.systemPrompt}`,
  user: "The user is back and still can't log in. What do you do?",
});

console.log(response.text);
// "Based on our history, the user has been experiencing 503 errors on Chrome 125..."

// 4. Every 100 interactions, summarization triggers automatically.
// Old interactions are compressed into a paragraph. Token costs stay flat.

Harness Memory(Claude Code 和 Codex)

编码 Agent 会忘记会话之间的一切,也不会彼此分享学到的内容。MemStack 为 Claude Code 和 Codex 提供每个项目一份记忆:一个 Agent 做出的决定,另一个 Agent 和之后的每个会话都能知晓。

工作原理
  • 保存。 每个 Agent 都会通过 MCP harness 配置文件获得五个 MemStack 工具(memory_store、memory_retrieve、memory_get、memory_delete、memory_stats)。当你说“记住……”,或 Agent 学到一条持久有效的事实、决定、偏好或规则时,它会调用 memory_store。MemStack 会向你的大模型请求几个主题标签,因此“使用 Hono”之后可以通过“用了哪个框架?”找到。如果标签生成失败,记忆仍会保存。
  • 回忆。 会话开始钩子会将项目中最重要的记忆载入每个新会话,让 Agent 一开始就了解这些内容。会话期间,Agent 会使用普通问题调用 memory_retrieve。检索在本地使用关键词排序(带词干提取的 BM25),不会调用大模型,因此速度快且可离线使用。
  • 范围。 记忆属于当前项目。适用于所有项目的偏好可以保存为全局记忆(scope: "global"),并会在每个项目中检索。一个项目永远无法读取或删除其他项目的记忆。
  • 安全。 Agent 无法使用批量操作或破坏性工具,大模型密钥保存在 ~/.memstack/config.json 中(仅你可读取),绝不会出现在 Agent 配置里。Agent 会收到不得存储机密的指示,MemStack 也会进行检查:如果记忆包含凭据(私钥、云服务或代码托管令牌、JWT、连接字符串中的密码、password = "..." 赋值),系统会在大模型或存储看到内容前拒绝该记忆,并指出机密的类型,但不会重复其内容。这项检查基于模式,因此不认识格式的机密会通过检查;MEMSTACK_SECRET_POLICY=redact 会将机密替换为 [REDACTED:<kind>] 后存储记忆,off 则会禁用检查。
命令
命令功能
memstack init选择一个大模型提供商(可选)和存储方式。通过实际请求验证密钥,并写入 ~/.memstack/config.json。没有密钥时,记忆会在不带主题标签的情况下保存,检索功能不受影响。非交互模式选项:--provider、--base-url、--model、--api-key-env <VAR>、--no-llm、--store、--path、--url、--yes。
memstack connect <claude-code|codex>将 MemStack 注册到 Agent。先检查服务能否正常运行;如果某一步失败,则撤销所有更改。--dry-run 会显示所做的更改;--no-hooks 和 --no-agents-md 会跳过对应部分。
memstack disconnect <claude-code|codex>移除 connect 添加的所有内容。记忆会保留。
memstack status显示配置、存储、当前项目,以及各 Agent 已连接的内容。
memstack doctor诊断设置问题,并为每个问题打印修复方法。--live 还会测试大模型密钥。
memstack memories [query]列出或搜索此项目的记忆。使用 --global 查看全局记忆,使用 --delete <id> 删除一条记忆。
memstack project显示此代码仓库的项目 ID。project pin <id> 会将项目 ID 固定在 .memstack.json 中;project merge <old-id> 会从旧 ID 迁移记忆。
connect 会修改的内容

connect 只会修改以下条目,并先备份每个文件;disconnect 会逐个文件原样恢复:

Agent文件修改内容
Claude Code~/.claude.json使用 claude mcp add-json 添加用户级 memstack MCP 服务。
Claude Code~/.claude/settings.json添加一个运行 memstack-mcp hook session-start 的 SessionStart 钩子。
Codex~/.codex/config.toml使用 codex mcp add 添加一个 memstack MCP 服务。
Codex~/.codex/hooks.json添加一个 SessionStart 钩子。你使用 /hooks 批准一次后,Codex 就会运行它。
Codex~/.codex/AGENTS.md在 memstack:begin/memstack:end 标记之间添加一段简短内容,告知 Codex 使用 memory_store 保存记忆。

注册的命令是你安装的 memstack-mcp,通过绝对路径运行,因此即使 Agent 启动时没有你的 shell PATH,它也能正常工作。 重启 Claude Code,或启动新的 Codex 会话,以加载它。

项目

项目 ID 来自仓库本身,不会存储在其他位置,因此在克隆、工作树、远程仓库重命名、文件夹移动、更换计算机和切换存储时都会保持不变:

  1. 仓库根目录 .memstack.json 中固定的 ID (memstack project pin <id>;提交此文件即可与团队共享);
  2. 否则,使用仓库的首次提交;
  3. 对于浅克隆,使用 origin 远程仓库;
  4. 对于没有提交记录的仓库,使用 git 目录;在 git 仓库之外,则使用文件夹。仓库首次提交前保存的记忆会自动迁移到新的 ID。

派生仓库与原始仓库共享首次提交,因此在同一个存储中,两者会共享记忆,直到其中一个运行 memstack project pin。

存储

任何受支持的存储都可以使用;在 memstack init 中选择即可。MemStack 不会安装存储驱动:请将所需驱动与 @memstack/mcp 一起安装。

存储要安装的驱动说明
sqlitebetter-sqlite3@^11.10.0默认选项;使用一个本地文件,可供两个 Agent 同时安全使用。
postgrespostgres@^3.4.9可在多台计算机之间共享。
redisioredis@^5.11.1
disk, markdown无仅限单个进程;两个 Agent 同时使用时,doctor 会发出警告。

切换存储会保留项目 ID;使用 memstack export 和 memstack import 迁移记忆。

故障排除

先运行 memstack doctor;它会检查配置文件及其权限、大模型密钥、存储驱动、各 Agent 的注册、钩子和指引,并启动服务以确认其正常响应。

  • **“未安装 memstack-mcp”或缺少驱动:**运行它显示的 npm install -g … 命令。
  • **Codex 在会话开始时未加载记忆:**在 Codex 中使用 /hooks 批准 MemStack 钩子。
  • **Codex 在收到要求后未保存记忆:**检查 memstack status 是否显示 AGENTS.md 指引已存在;若没有,请重新连接。
  • 似乎缺少记忆:memstack project 会显示你所在的项目。使用 memstack project merge <old-id> 从旧 ID 导入记忆。

MCP 服务本身的详细信息:docs/MCP_SETUP.md。


记忆流水线

MemStack 的核心是一个五阶段流水线。每个阶段都可以独立使用。

1. 存储

每次 Agent 交互都会成为一条 Memory,并附带元数据,用于控制之后如何检索、摘要和清理它。

interface Memory {
  id: string;
  actorId: string;               // Who this memory belongs to (user ID, agent ID, session ID)
  memoryType: MemoryType;        // "interaction" | "summary" | "observation" | "fact" | "reflection" | "preference" | "decision" | "instruction"
  content: string;               // The actual text
  importance: number;            // 0-1 — higher = survives pruning, ranks higher in retrieval
  emotionalValence: number;      // -1 to 1 — for tone-aware retrieval
  tags: string[];                // Filter by tag: "bug", "billing", "urgent", etc.
  embedding?: number[];          // Computed automatically if embedding adapter is configured
  metadata?: Record<string, unknown>;  // Your custom fields
  expiresAt?: Date;              // Auto-pruned after this date
  sourceId?: string;             // Link back to the originating event
  createdAt: Date;
}
// Simple store
await ms.memory.store({
  actorId: "agent-7",
  content: "Customer asked about refund policy for Q2 purchases.",
  tags: ["billing", "refund"],
});

// Batch store — embeddings are batched into one API call for efficiency
await ms.memory.storeBatch([
  { actorId: "agent-7", content: "First interaction" },
  { actorId: "agent-7", content: "Second interaction" },
  { actorId: "agent-7", content: "Third interaction" },
]);
2. 检索

提取相关内容——可按关键词、含义(语义)、时间新近度或重要性检索。

const memories = await ms.memory.retrieve({
  actorId: "agent-7",              // Scope to one actor
  query: "refund policy",          // What to search for
  strategy: "hybrid",              // How to rank: "recent" | "important" | "semantic" | "hybrid"
  limit: 10,                       // Max results
  memoryTypes: ["interaction"],    // Only certain types
  tags: ["billing"],               // Only certain tags
});

策略行为:

策略排序依据是否需要嵌入最适用于
recent从最新到最旧否了解刚刚发生的事
important从最重要到最不重要否过滤噪声,保留有效信息
semantic与查询的余弦相似度是“查找有关 X 的记忆”
hybrid语义与重要性综合排序是兼顾两者优势

没有嵌入适配器?semantic 和 hybrid 会退回到关键词匹配和按重要性排序。无需 API 费用,只是精度会低一些。

3. 编译上下文

compileContext() 会接收检索结果并组装成适用于大模型的系统提示词——去重后按时间新近度和重要性排序,并提供令牌估算,让你在调用大模型前了解成本。

const ctx = await ms.memory.compileContext({
  actorId: "agent-7",
  maxTokens: 2000,               // Budget — assembler stops when it hits this
  memoryTypes: ["interaction", "summary"],
});

// ctx.systemPrompt:
// ## Important Memories
// - The customer has been attempting login for 3 days. (importance: 0.85)
// - Refund was processed for order #4521 on Jan 12. (importance: 0.72)
// 
// ## Recent Interactions
// - Customer asked about refund policy for Q2 purchases.
// - Customer reported login error 503 on Chrome 125.

console.log(ctx.tokenEstimate);  // ~280

// Inject into your LLM call
const currentMessage = "The user is asking about their refund status.";
const response = await llm.complete({
  system: ctx.systemPrompt,
  user: currentMessage,
});

console.log(response.text);

compileContext() 会处理去重和令牌预算,并将上下文拆分为重要内容和近期内容两个部分。如果不使用它,你就只能拼接原始检索结果,并冒着上下文窗口溢出的风险。

4. 总结

当某个角色有数百次交互时,检索成本会增加,上下文也会变得臃肿。总结会使用配置的大模型将旧交互压缩成一段文字。

const { summary, deletedCount } = await ms.memory.summarize({
  actorId: "agent-7",
  olderThan: new Date(Date.now() - 7 * 86400000),  // Older than 7 days
  skipMostRecent: 10,        // Never touch the 10 most recent
  targetCount: 50,           // Summarize at most 50 memories
  memoryTypes: ["interaction"],
  keepOriginals: false,      // Delete originals after summary
});

// summary.content:
// "Over the past week, the customer reported recurring login failures (error 503)
//  on Chrome 125. Multiple troubleshooting attempts including cache clearing and 
//  password reset were unsuccessful. A refund was processed for order #4521."

console.log(deletedCount);   // 47 — 47 interactions compressed into 1 summary memory

**自动总结:**在配置中设置 summarizationThreshold(默认值:100)。角色的每第 100 次交互都会自动触发总结。

警告:keepOriginals: false 会删除已总结的记忆。设置 keepOriginals: true,即可将原记忆与总结一同保留。

自定义总结提示词:

const ms = new MemStack({
  llm,
  defaults: {
    summarizationPrompt:
      "You are an enterprise support memory compressor. Highlight: customer name,
       product, severity, resolution status, and any open issues.",
  },
});
5. 清理

并非所有记忆都值得永久保留。清理会移除价值较低的记忆,以保持存储和检索速度。

// Remove memories older than 30 days
await ms.memory.prune({ type: "byAge", maxAge: 30 * 86400000 });

// Keep only memories above importance 0.3
await ms.memory.prune({ type: "byImportance", minImportance: 0.3 });

// Keep at most 500 memories per actor
await ms.memory.prune({ type: "byCount", maxPerActor: 500 });

// Remove specific types
await ms.memory.prune({ type: "byType", memoryTypes: ["observation"] });

// Custom logic
await ms.memory.prune({
  type: "custom",
  shouldRemove: (memory) => memory.content.includes("[RESOLVED]"),
});

// Dry run first — see what would be removed
const { wouldPrune, count } = await ms.memory.dryRunPrune({
  type: "byAge",
  maxAge: 86400000,
});
console.log(`Would remove ${count} memories:`, wouldPrune);

在配置中设置 pruneStrategy,即可在每次调用 process() 时自动清理:

const ms = new MemStack({
  llm,
  defaults: {
    pruneStrategy: { type: "byImportance", minImportance: 0.05 },
  },
});

实际应用场景

支持 Agent
// detectUrgency and classifyIntent are your own business logic.
// They could be simple keyword matchers, regex, or an LLM call.
function detectUrgency(msg: string): number {
  if (msg.match(/urgent|asap|immediately/i)) return 0.9;
  if (msg.match(/error|fail|broken/i)) return 0.7;
  return 0.5;
}

function classifyIntent(msg: string): string[] {
  const tags: string[] = [];
  if (msg.match(/bill|refund|charge|payment/i)) tags.push("billing");
  if (msg.match(/error|bug|fail|crash/i)) tags.push("bug");
  if (msg.match(/login|password|account/i)) tags.push("account");
  return tags;
}

// Every customer message becomes a memory
async function handleMessage(customerId: string, message: string) {
  await ms.memory.store({
    actorId: `customer:${customerId}`,
    content: message,
    importance: detectUrgency(message),
    tags: classifyIntent(message),
  });

  // Retrieve everything relevant to this customer's history
  const ctx = await ms.memory.compileContext({
    actorId: `customer:${customerId}`,
    maxTokens: 1500,
  });

  const response = await llm.complete({
    system: `You are a support agent. Customer history:\n${ctx.systemPrompt}`,
    user: message,
  });

  return response.text;
}

// Every 100th interaction, old history auto-compresses.
// A customer with 10,000 messages still fits in a $0.02 LLM call.
RAG 流程
// Suppose you have documents from your knowledge base
const documents = [
  { text: "Authentication uses JWT tokens with 15-minute expiry.", url: "/docs/auth", section: "security" },
  { text: "Refunds are processed within 5-10 business days.", url: "/docs/billing", section: "billing" },
];

// Index documents as observation memories
for (const doc of documents) {
  await ms.memory.store({
    actorId: "knowledge-base",
    content: doc.text,
    memoryType: "observation",
    metadata: { source: doc.url, section: doc.section },
  });
}

// Query with semantic search
const relevantDocs = await ms.memory.retrieve({
  actorId: "knowledge-base",
  query: "How does authentication work?",
  strategy: "semantic",
  limit: 5,
});

const ctx = await ms.memory.compileContext({
  actorId: "knowledge-base",
  memoryTypes: ["observation"],
});

// Prompt the LLM with retrieved context
const answer = await llm.complete({
  system: `Answer using only these documents:\n${ctx.systemPrompt}`,
  user: "How does authentication work?",
});
多用户聊天机器人
// Each user gets their own memory space
async function chat(userId: string, message: string) {
  await ms.memory.store({
    actorId: userId,
    content: message,
  });

  const ctx = await ms.memory.compileContext({
    actorId: userId,
    maxTokens: 1000,
  });

  return llm.complete({
    system: `You are a friendly assistant. Conversation history with this user:\n${ctx.systemPrompt}`,
    user: message,
  });
}

// Get stats
const total = await ms.memory.count();
const userCount = await ms.memory.count({ actorId: "user-42" });

记忆类型参考

类型用途示例
interaction默认类型。Agent 与用户或其他 Agent 之间的直接交流。“用户询问了账单问题。”
summary对旧交互的压缩汇总。由 summarize() 创建。“在 3 周内,用户报告了 5 次登录失败……”
observation被动知识——事实、文档以及 Agent 知道但未参与互动的内容。“公司的退款政策是自购买之日起 30 天内。”
fact已验证的知识——Agent 已确认的具体事实。“用户的订阅级别为 Enterprise。”
reflection自我生成的洞见——Agent 对自身经历的思考。“我往往会过度解释账单政策——应该更简洁一些。”
preference用户喜欢的处理方式。“偏好每个小型拉取请求只处理一个问题。”
decision已做出的选择,最好附上原因。“选择 Hono 而非 Express,以支持边缘运行时。”
instruction需要持续遵循的规则。“切勿直接提交到 main。”

类型会控制检索行为——compileContext() 对待 interaction 和 summary 的方式不同于 observation。使用类型区分“发生了什么”和“我知道什么”。


检索策略

四种策略,各有用途:

// "What just happened?" — most recent first
await ms.memory.retrieve({ actorId: "x", strategy: "recent", limit: 3 });

// "What matters most?" — highest importance, ignoring age
await ms.memory.retrieve({ actorId: "x", strategy: "important" });

// "What relates to this query?" — cosine similarity search (needs embeddings)
await ms.memory.retrieve({ actorId: "x", query: "login bug", strategy: "semantic" });

// "Balance relevance and importance" — semantic + importance blend
await ms.memory.retrieve({ actorId: "x", query: "login bug", strategy: "hybrid" });

如何选择策略:

  • 聊天机器人、持续进行的对话以及任何对时效性有要求的场景,使用 recent
  • 对于需要关注信噪比的长期运行 Agent,使用 important
  • 对于 RAG、文档搜索和知识库查询,使用 semantic
  • 对于大多数 Agent 记忆,使用 hybrid——它能平衡语义与重要性
任意存储适配器上的关键词检索

LexicalRetriever 无需嵌入或大模型调用即可回答自然语言问题,并且在每种存储适配器上的行为都相同。它通过 retrieve() 加载给定 actor 的记忆,然后在 MemStack 中使用 BM25,根据内容和标签对记忆进行排序,并支持词干提取和前缀匹配。如果没有匹配项,则返回最重要的记忆。

import { LexicalRetriever } from "@memstack/core";

const retriever = new LexicalRetriever(storage);
const { hits, fallback } = await retriever.recall({
  actorIds: ["project:abc", "global"], // Searched together
  query: "What framework does this project use?",
  limit: 10,                           // Max results
  maxChars: 8000,                      // Max total content; the top hit is always returned
});

每个 actor 最多会对 2,000 条记忆进行排序(candidateLimit)。只有返回的记忆会被标记为已访问。


嵌入

嵌入为语义搜索提供支持。嵌入是可选的——如果不使用嵌入,检索会采用关键词匹配。

使用嵌入与不使用嵌入

使用嵌入(已配置 embedding 适配器):

import { MemStack, OpenAILLMAdapter, OpenAIEmbeddingAdapter, InMemoryStorageAdapter } from "@memstack/core";

const ms = new MemStack({
  llm: new OpenAILLMAdapter({ apiKey: process.env.OPENAI_API_KEY! }),
  embedding: new OpenAIEmbeddingAdapter({ apiKey: process.env.OPENAI_API_KEY! }),
  storage: new InMemoryStorageAdapter(),
});

// store() computes a 1536-dim vector automatically
await ms.memory.store({
  actorId: "agent-7",
  content: "Customer asked about refund policy for Q2 purchases.",
});

// retrieve() with "semantic" or "hybrid" uses cosine similarity
// Query: "refund" finds the refund policy memory even though the word "refund"
// appears differently across stored memories.
const results = await ms.memory.retrieve({
  actorId: "agent-7",
  query: "how do I get my money back",
  strategy: "semantic",
});
// Matches "Customer asked about refund policy" — semantic match, not keyword match.

不使用嵌入(未配置 embedding 适配器):

const ms = new MemStack({
  llm: new OpenAILLMAdapter({ apiKey: process.env.OPENAI_API_KEY! }),
  storage: new InMemoryStorageAdapter(),
  // no embedding adapter
});

// store() works identically, just no vector computed
await ms.memory.store({
  actorId: "agent-7",
  content: "Customer asked about refund policy for Q2 purchases.",
});

// retrieve() with "semantic" or "hybrid" falls back to keyword matching
// plus importance/recency sorting. No API costs, no setup required.
const results = await ms.memory.retrieve({
  actorId: "agent-7",
  query: "refund",
  strategy: "hybrid", // falls back to keyword + importance
});
// Still works — finds "refund" via substring match. Less precise for
// paraphrased queries ("money back" won't match "refund").

批量嵌入: storeBatch() 会在一次嵌入 API 调用中发送所有文本,从而降低成本和延迟。

// Disable auto-embedding if you only need keyword search
const ms = new MemStack({
  llm,
  embedding: new OpenAIEmbeddingAdapter({ apiKey }),
  defaults: { embedOnStore: false },
});
向量维度和模型兼容性

不同的嵌入模型会生成长度不同的向量。余弦相似度只能用于维度相同的向量。如果更换嵌入模型,现有向量将不再兼容——它们无法与新向量进行比较。

适配器默认模型维度
OpenAIEmbeddingAdaptertext-embedding-3-small1536
OpenAIEmbeddingAdaptertext-embedding-3-large3072
CohereEmbeddingAdapterembed-english-v3.01024
CohereEmbeddingAdapterembed-english-light-v3.0384
CohereEmbeddingAdapterembed-english-v2.04096
CohereEmbeddingAdapterembed-multilingual-v3.01024

维度不匹配时会发生什么: 如果使用一个模型存储记忆(例如,1536 维),然后切换到另一个模型(例如,1024 维),存储适配器接收到的查询向量和已存储向量长度就会不同。不同维度向量之间的余弦相似度没有定义——结果取决于存储后端的行为。大多数后端会报错、返回空结果,或产生毫无意义的分数。

建议: 每个存储实例选定一种嵌入模型并始终使用该模型。如果需要切换模型,请创建新的存储实例并从头重新嵌入。

DeepSeek 用户: DeepSeek 没有嵌入 API。如果将 DeepSeek 用作大模型,则必须选择以下方式之一:

  1. 不配置嵌入适配器,使用 "recent" 或 "important" 检索策略(无 API 费用,但精确度较低)
  2. 将 DeepSeek 与单独的嵌入提供方配合使用(例如,用 OpenAI 生成嵌入,用 DeepSeek 进行聊天)

适配器

MemStack 不依赖特定提供方。每个边界都是一个接口——你可以自行选择大模型、嵌入模型和存储后端。

大模型适配器

供 summarize() 和 compileContext() 使用。内置支持 OpenAI、Anthropic、Ollama 和 Groq;通过 baseURL,OpenAI 适配器还可与 任何兼容 OpenAI 的 API 配合使用(DeepSeek、Mistral、Gemini、Together AI、Perplexity、Fireworks、xAI 等数十种服务)。

// OpenAI
import { OpenAILLMAdapter } from "@memstack/core";
const llm = new OpenAILLMAdapter({ apiKey: "..." });

// Any OpenAI-compatible API — just change baseURL
const deepseek = new OpenAILLMAdapter({ apiKey: "...", baseURL: "https://api.deepseek.com/v1" });
const mistral = new OpenAILLMAdapter({ apiKey: "...", baseURL: "https://api.mistral.ai/v1" });
const together = new OpenAILLMAdapter({ apiKey: "...", baseURL: "https://api.together.xyz/v1" });

// Anthropic
import { AnthropicLLMAdapter } from "@memstack/core";
const llm = new AnthropicLLMAdapter({
  apiKey: process.env.ANTHROPIC_API_KEY!,
  defaultModel: "claude-sonnet-4-5-20250929",
});

// Ollama (built-in)
import { OllamaLLMAdapter } from "@memstack/core";
const llm = new OllamaLLMAdapter({
  baseURL: "http://localhost:11434",
  defaultModel: "llama3.2",
});
嵌入适配器

供语义检索使用。内置支持 OpenAI 和 Cohere;通过 baseURL,OpenAI 适配器还可与 任何兼容 OpenAI 的嵌入 API 配合使用(Together AI、Voyage AI、Jina、Nomic 等)。

import { OpenAIEmbeddingAdapter, CohereEmbeddingAdapter } from "@memstack/core";

// OpenAI
new OpenAIEmbeddingAdapter({ apiKey: "...", model: "text-embedding-3-small" }); // 1536 dims

// Cohere
new CohereEmbeddingAdapter({ apiKey: "..." }); // embed-english-v3.0, 1024 dims

// Any OpenAI-compatible embedding API
new OpenAIEmbeddingAdapter({ apiKey: "...", baseURL: "https://api.voyageai.com/v1", model: "voyage-3" });
存储适配器

MemStack 包含 18 种存储适配器实现。其中 12 种从 @memstack/core 导出;另外 6 种仍为实验性源码实现。Core 没有运行时依赖,数据库客户端由调用方注入。

支持级别:

  • 生产就绪表示适配器从公共包导出、包含单元测试覆盖,并作为公共 API 的一部分提供支持。
  • 真实服务 E2E 验证表示适配器还通过了 pnpm test:e2e 中针对实际数据库实现的测试。
  • 模拟测试表示单元测试覆盖使用的是注入的模拟客户端,而非在线云服务。
  • 实验性表示已在源码中实现,但未从已发布的包中导出。
公共包导出

内置(无外部依赖):

适配器后端用例
InMemoryStorageAdapter内存 Map测试、原型开发
DiskStorageAdapter本地 JSON 文件简单的本地持久化
MarkdownStorageAdapter仅追加的 .md 文件易于阅读、可通过 git diff 查看差异、便于调试
HybridStorageAdapter组合任意两个 StorageProviders缓存 + 持久化、边缘端 + 持久化

关系型 / SQL:

适配器后端向量搜索
PostgresStorageAdapterPostgreSQL + pgvector原生 HNSW
SQLiteStorageAdapterSQLite (better-sqlite3)内存中的余弦相似度

向量数据库:

适配器后端
QdrantStorageAdapterQdrant
WeaviateStorageAdapterWeaviate
LanceDBStorageAdapterLanceDB
MongoDBStorageAdapterMongoDB Atlas Vector Search

缓存 / KV:

适配器后端
RedisStorageAdapterRedis (ioredis)

图数据库:

适配器后端
Neo4jStorageAdapterNeo4j
实验性(通过模拟测试或缺少可选的 E2E 能力)

这些实现可供源码贡献者使用,但不属于已发布的包 API。

适配器后端阻碍
TursoStorageAdapterTurso (libsql)仅限云端(需要 Turso 账户)
ChromaStorageAdapterChromaDB依赖嵌入函数
PineconeStorageAdapterPinecone仅限云端(需要 API 密钥)
UpstashStorageAdapterUpstash Redis + Vector仅限云端(需要 API 密钥)
Mem0StorageAdapterMem0 OSS 或 Cloud仅限云端(需要 API 密钥)
ZepStorageAdapterZep Cloud 或 CE仅限云端(需要 API 密钥)

Pinecone、Upstash、Mem0、Zep 和 Turso 的实时云端兼容性仍未经验证。若 Chroma 可选的默认嵌入函数不可用,其真实客户端 E2E 测试套件会跳过。大模型和嵌入提供方测试使用模拟;实时提供方测试需主动选择启用,且不属于 CI 的一部分。

各后端快速入门:

// Postgres
import { PostgresStorageAdapter } from "@memstack/core";
const storage = new PostgresStorageAdapter({ connectionString: "postgres://..." });

// Redis
import Redis from "ioredis";
import { RedisStorageAdapter } from "@memstack/core";
const storage = new RedisStorageAdapter({ redis: new Redis() });

// Markdown (append-only, human-readable)
import { MarkdownStorageAdapter } from "@memstack/core";
const storage = new MarkdownStorageAdapter({ dir: "./memories" });

// Hybrid (Redis cache + Postgres durable)
import { HybridStorageAdapter } from "@memstack/core";
const storage = new HybridStorageAdapter({
  cache: new RedisStorageAdapter({ redis: new Redis() }),
  durable: new PostgresStorageAdapter({ connectionString: "postgres://..." }),
});

自定义存储:

import type { StorageProvider, MemoryStoreInput } from "@memstack/core";

class MyStorage implements StorageProvider {
  async store(input: MemoryStoreInput): Promise<Memory> { /* ... */ }
  async get(id: string): Promise<Memory | null> { /* ... */ }
  async retrieve(query: MemoryRetrieveQuery, embedding?: number[]): Promise<Memory[]> { /* ... */ }
  async count(filter?: MemoryCountFilter): Promise<number> { /* ... */ }
  async delete(id: string): Promise<void> { /* ... */ }
  async deleteMany(ids: string[]): Promise<number> { /* ... */ }
  async storeBatch(inputs: MemoryStoreInput[]): Promise<Memory[]> { /* ... */ }
  async initialize(): Promise<void> { /* ... */ }
  async close(): Promise<void> { /* ... */ }
}

后端对比

后端向量搜索Touch状态
InMemory内存中的余弦相似度是✅ 生产可用
Disk (JSON)关键词 + 重要性是✅ 生产可用
Markdown关键词 + 重要性否✅ 生产可用
Postgrespgvector HNSW是✅ 生产可用
RedisRediSearch KNN(自动检测)是✅ 生产可用
Qdrant原生 ANN否✅ 生产可用
WeaviateBM25 + 向量混合搜索否✅ 生产可用
LanceDB原生 DiskANN否✅ 生产可用
MongoDBAtlas Vector Search否✅ 生产可用
Neo4jNeo4j 向量索引否✅ 生产可用
Hybrid委托给缓存 / 持久化后端若持久化后端支持✅ 生产可用
SQLite内存中的余弦相似度是✅ 生产可用

完整 API 参考

MemStack 客户端
import { MemStack } from "@memstack/core";

const ms = new MemStack({
  llm: LLMProvider,                    // Required — for summarization
  embedding?: EmbeddingProvider,       // Optional — for semantic search
  storage?: StorageProvider,           // Optional — defaults to InMemoryStorageAdapter
  defaults?: {
    summarizationThreshold?: number,   // Auto-summarize every N process() calls. Default: 100
    embedOnStore?: boolean,            // Auto-embed on store(). Default: true
    pruneStrategy?: PruneStrategy,     // Auto-prune during process() (throttled). Default: disabled
    pruneInterval?: number,            // Run auto-prune every N process() calls. Default: 100
    autoImportance?: boolean,          // LLM-score importance in process() when not provided. Default: false
    autoTags?: boolean,                // LLM-extract tags in process() when not provided. Default: false
    summarizationPrompt?: string,      // Custom prompt for the summarizer
  },
  hooks?: {
    onMemoryStored?: (memory: Memory) => void;
    onMemoryPruned?: (ids: string[]) => void;
    onSummaryCreated?: (summary: Memory, deletedCount: number) => void;
    onError?: (error: Error, context: string) => void;
  },
});

自动行为在 process() 中运行,而非 store() 中。 process() 会跟踪每个 actor 的调用次数:每达到 summarizationThreshold 次调用就会执行摘要生成;设置了 pruneStrategy 时,每达到 pruneInterval 次调用就会执行清理。 store() 是底层写入操作,不会触发这些行为。

记忆子系统

所有方法均可通过 ms.memory.* 访问:

// Store
ms.memory.store(input: MemoryStoreInput): Promise<Memory>
ms.memory.storeBatch(inputs: MemoryStoreInput[]): Promise<Memory[]>

// Retrieve
ms.memory.retrieve(query: MemoryRetrieveQuery): Promise<Memory[]>
ms.memory.get(id: string): Promise<Memory | null>

// Context assembly
ms.memory.compileContext(options: ContextOptions): Promise<CompiledContext>

// Lifecycle
ms.memory.summarize(options: SummarizeOptions): Promise<{ summary: Memory; deletedCount: number }>
ms.memory.prune(strategy: PruneStrategy): Promise<{ pruned: string[]; count: number }>
ms.memory.dryRunPrune(strategy: PruneStrategy): Promise<{ wouldPrune: string[]; count: number }>

// Management
ms.memory.count(filter?: MemoryCountFilter): Promise<number>
ms.memory.delete(id: string): Promise<void>
ms.memory.deleteMany(ids: string[]): Promise<number>
ms.memory.touch(id: string): Promise<void>
ms.memory.purgeActor(actorId: string): Promise<number>
ms.memory.merge(ids: string[]): Promise<Memory>
ms.memory.stats(actorId?: string): Promise<MemoryStats>
ms.memory.summarizeStream(options: SummarizeOptions): AsyncIterable<{ chunk: string; text: string }>
导出 / 导入

为持久化、备份或迁移而快照并恢复完整状态:

import * as fs from "node:fs";

// Save
const snapshot = await ms.export();
fs.writeFileSync("state.json", JSON.stringify(snapshot, null, 2));

// Restore
const data = JSON.parse(fs.readFileSync("state.json", "utf-8"));
await ms2.import(data);

导入时会保留每条记忆原始的 createdAt,因此 export → import 可无损往返,适用于备份和跨后端迁移(例如 disk → Postgres)。所有存储适配器都会遵循 store() / storeBatch() 中提供的 createdAt;若未提供,则默认使用当前时间。

健康状态与关闭
const status = await ms.health();
// { storage: true, llm: true, embedding: true }

await ms.close(); // graceful shutdown
Harness Memory API

Harness 功能基于 HarnessMemory 构建,可与任何存储适配器配合使用。使用它来构建自己的 Agent 集成:

import { HarnessMemory, defaultRecallNamespaces, projectNamespace } from "@memstack/core";

const memory = new HarnessMemory({ storage, llm });

await memory.remember({
  namespace: projectNamespace("acme-api"),   // or "global"
  content: "This project uses Hono",
  kind: "decision",                           // fact | preference | decision | instruction | observation | ...
  source: { harness: "my-agent", project: "acme-api" },
});

const { hits, fallback } = await memory.recall({
  namespaces: defaultRecallNamespaces("acme-api"), // project, then global
  query: "Which framework do we use?",
  limit: 10,
  maxChars: 8000,
});

await memory.get(id, namespaces);       // null outside namespaces
await memory.forget(id, namespaces);    // refuses ids outside namespaces
await memory.stats(namespaces);         // { "project:acme-api": 3, global: 1 }
await memory.moveNamespace(from, to);   // e.g. when a project's ID changes

remember 会请求 LLM 提取主题标签(通过 autoTags: false 禁用);其他所有方法都不会调用 LLM。recall 使用 LexicalRetriever。


配置

const ms = new MemStack({
  llm: new OpenAILLMAdapter({ apiKey: "..." }),

  // Defaults control auto-behavior (all applied during process())
  defaults: {
    summarizationThreshold: 50,      // Summarize every 50 process() calls (default: 100)
    embedOnStore: false,             // Don't auto-embed — saves API costs
    pruneStrategy: {                 // Auto-clean during process(), throttled by pruneInterval
      type: "byAge",
      maxAge: 90 * 86400000,         // 90 days
    },
    pruneInterval: 100,              // Run the prune check every 100 process() calls (default: 100)
    autoImportance: true,            // Let the LLM score importance when you don't pass one
    autoTags: true,                  // Let the LLM extract tags when you don't pass any
  },

  // Hooks for observability
  hooks: {
    onMemoryStored: (m) => logger.debug("memory:stored", { id: m.id, actor: m.actorId }),
    onMemoryPruned: (ids) => logger.info("memory:pruned", { count: ids.length }),
    onSummaryCreated: (summary, n) => logger.info("memory:summarized", { count: n }),
    onError: (err, context) => logger.error("memory:error", { context, message: err.message }),
  },
});
Harness 配置文件

CLI 和 MCP harness 配置文件会读取 ~/.memstack/config.json(或 $MEMSTACK_HOME/config.json),该文件由 memstack init 写入,且只有你本人可读取:

{
  "version": 1,
  "llm": { "provider": "openai-compatible", "apiKey": "…", "baseURL": "https://api.deepseek.com", "model": "deepseek-flash" },
  "storage": { "type": "sqlite", "path": "/Users/you/.memstack/memstack.db" }
}

环境变量会按区块覆盖文件内容:任何 LLM 变量(OPENAI_API_KEY、ANTHROPIC_API_KEY、MEMSTACK_OPENAI_BASE_URL、MEMSTACK_LLM_MODEL)都会替换整个 llm 区块,而 MEMSTACK_STORAGE 会替换整个 storage 区块,因此密钥绝不会发送到其他提供商的 URL。


高级用法

自定义存储

为任何数据库实现 StorageProvider。该接口包含 9 个方法。完整契约请参阅上方的参考部分。

以下成员均为可选,无一为必需:

  • capabilities: { multiProcess?, textSearch? } 声明多个进程能否安全地共享存储,以及 search() 是否为原生实现。
  • search(query) 提供原生全文搜索。声明了 textSearch 时,LexicalRetriever 会使用该方法;否则会自行对记忆进行排序。
  • retrieve() 应遵循 touch: false,返回记忆而不将其标记为已访问。

SQLiteStorageAdapter 会启用 WAL 和 5 秒忙碌超时,让多个进程能够共享同一个数据库文件。设置 walMode: false 或调整 busyTimeoutMs 可更改此行为。

自定义 LLM / Embedding

为任何服务实现 LLMProvider 或 EmbeddingProvider:

import type { LLMProvider } from "@memstack/core";

class TogetherAIAdapter implements LLMProvider {
  async complete(req: { system: string; user: string; model?: string }) {
    const res = await fetch("https://api.together.xyz/v1/chat/completions", {
      headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" },
      body: JSON.stringify({ model: req.model, messages: [{ role: "system", content: req.system }, { role: "user", content: req.user }] }),
    });
    const data = await res.json() as any;
    return { text: data.choices[0].message.content, tokens: { prompt: data.usage.prompt_tokens, completion: data.usage.completion_tokens, total: data.usage.total_tokens } };
  }
}
事件钩子

无需修改代码即可监控记忆操作:

const ms = new MemStack({
  llm,
  hooks: {
    onMemoryStored: (m) => metrics.increment("memory.stored"),
    onSummaryCreated: (_, n) => metrics.gauge("memory.summarized_count", n),
    onMemoryPruned: (ids) => metrics.increment("memory.pruned", ids.length),
  },
});


LongMemEval-S 检索基准测试

MemStack 附带一个 Val 基准测试包,用于在公开、清理后的 LongMemEval-S 子集中仅通过检索回忆证据会话。运行器通过 MemStack 的 harness memory API 存储每个问题的会话,使用本地词法检索器和内存存储,不会调用答案模型或评判器。此基准测试仅针对临时内存存储中的公开语料禁用密钥扫描;请勿将运行器用于私有数据。其召回率指标并非端到端 QA 准确率,不能直接与官方 LongMemEval QA 排行榜进行比较。已发布的数据集是公开数据集,并非隐藏的评估集。

本仓库不包含该数据集。请先验证基准测试包,再使用 Val 获取仅明确命名的子集:

val benchmark validate benchmarks/longmemeval/manifest.json
val benchmark fetch benchmarks/longmemeval/manifest.json --split public-evaluation

Val 会打印以 SHA-256 寻址的缓存路径。将其传递给运行器;--limit 表示部分诊断运行,不得作为完整基准测试报告:

pnpm benchmark:longmemeval -- --dataset "$HOME/.cache/val/benchmark-packs/objects/d6f21ea9d60a0d56f34a05b609c79c88a451d2ae03597821ea3d5a9678c3a442" --limit 5
pnpm benchmark:longmemeval -- --dataset "$HOME/.cache/val/benchmark-packs/objects/d6f21ea9d60a0d56f34a05b609c79c88a451d2ae03597821ea3d5a9678c3a442"

运行器在读取文件前会重新核对声明的确切大小和 SHA-256。它会以流式方式读取 JSON 数据集,评估默认的 K 值 1、3、5 和 10,排除上游问题 ID 后缀标记为 _abs 的弃答问题,并输出宏观/微观证据会话召回率、问题命中率,以及检索延迟的中位数和 p95。上游 LongMemEval 的来源说明和许可证记录在清单中;此基准测试包并非官方 LongMemEval 结果。


开发

环境设置与测试
git clone https://github.com/isiomaC/memstack.git
cd memstack
pnpm install

pnpm test             # 407 core tests, no external services needed
pnpm test:packages    # 78 package tests after dependency-ordered builds
pnpm test:e2e         # 80 pass, 1 optional Chroma skip (requires Docker services)
pnpm test:e2e:run     # Start services, run E2E once, preserve failure logs, clean up
pnpm smoke:artifacts  # Built core, CLI, MCP, and server black-box checks
pnpm smoke:packages   # Pack and install publishable tarballs in a clean project
pnpm smoke:docker     # Build and exercise the server image
pnpm verify           # Complete local verification pipeline
pnpm test:watch       # Watch core tests
pnpm build:all        # Build core and all workspace packages
pnpm check:all        # Type-check core and all workspace packages
pnpm check:benchmark  # Type-check the LongMemEval benchmark runner

CI 提供稳定的 verification 作业。请在 GitHub 分支保护设置中,将此作业配置为 main 的必需状态检查。

调试

使用钩子进行可观测性监控——MemStack 没有内置日志记录功能:

const ms = new MemStack({
  llm,
  hooks: {
    onMemoryStored: (m) => console.debug("[memstack] stored:", m.id, m.content.slice(0, 80)),
    onMemoryPruned: (ids) => console.debug("[memstack] pruned:", ids.length),
  },
});

常见问题:

症状原因解决方法
CONFIG_ERROR: LLM provider is required没有 LLM 适配器向配置传入任意 LLMProvider
检索结果为空actorId 错误或未存储任何记忆检查 await ms.memory.count({ actorId })
语义搜索无法使用没有嵌入适配器或 embedOnStore: false添加嵌入适配器,或使用 strategy: "recent"
生产环境中的内存使用量过高使用了 InMemoryStorageAdapter为 Postgres/Redis 等实现 StorageProvider
摘要质量不佳默认提示词与您的领域不匹配在 defaults 配置中使用 summarizationPrompt

在运行时检查状态:

// How much data do we have?
const total = await ms.memory.count();
const perActor = await ms.memory.count({ actorId: "user-42" });

// What does one actor's memory look like?
const snapshot = await ms.export();
const actorMemories = snapshot.memories.filter(m => m.actorId === "user-42");
console.log(`User-42: ${actorMemories.length} memories`);
actorMemories.forEach(m => console.log(`  [${m.memoryType}] ${m.content.slice(0, 60)} (imp: ${m.importance})`));

发布到 npm

# Bump version, then:
pnpm build && pnpm check && pnpm test
npm login
npm publish --access public

@memstack 作用域需要 --access public。


参与贡献

目前最需要的贡献:

  • LLM 适配器:Google Gemini(原生)、Amazon Bedrock、Vertex AI
  • 嵌入适配器:本地推理(transformers.js、ONNX)
  • 基准测试:检索质量、延迟、成本对比
  • Python 移植版:pip install memstack

请在 github.com/isiomaC/memstack 提交 issue 或 PR。


许可证

MIT © MemStack

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。