Skip to main content
Glama
README.md
# mem

本地中文语义记忆工具 —— 给 AI coding agent(ZCode / 任意支持 MCP 的客户端)提供「按需召回、不全量注入」的事实型长期记忆。

## 为什么用它

- **中文语义召回好**:embedding 走硅基流动 `BAAI/bge-m3`(1024 维),比本地 MiniLM 对中文友好得多(实测"换说法"查询相似度 0.71 vs 0.42)。
- **不全量注入**:不像 `MEMORY.md` 每轮全量塞进上下文,而是 agent 用到时才 `mem_search`,只把命中的几条注入。
- **自动召唤(hook)**:会话开始注入概览、每次提问自动召回、回合结束提醒沉淀 —— 三个 hook 形成闭环。
- **能整理**:支持 `update`(改过时内容)、`merge`(合并重复)、`link`(Obsidian 式双链)。
- **零运维**:单 SQLite 文件 + 单入口脚本,无外部服务。

## 安装

```bash
cd ~/code
git clone https://github.com/cyhano/mem.git
cd mem
pnpm install
pnpm build
```

需要 Node.js ≥ 18(推荐 20+)和 pnpm。

## 配置

写 `~/.config/mem/config.json`(或设环境变量 `MEM_CONFIG_PATH` 指向它):

```json
{
  "dbPath": "/Users/you/.local/share/mem/knowledge.db",
  "embedding": {
    "provider": "siliconflow",
    "baseUrl": "https://api.siliconflow.cn/v1",
    "apiKey": "你的硅基流动 key(或环境变量 SILICONFLOW_API_KEY)",
    "model": "BAAI/bge-m3",
    "dim": 1024
  }
}
```

> `apiKey` 也支持通过环境变量 `SILICONFLOW_API_KEY` 注入,避免明文落盘。

## CLI 用法

```bash
node dist/cli.js save "标题" "正文" [--kind fact|decision|config|command] [--project 项目名]
node dist/cli.js search "自然语言查询" [--top 5] [--project 项目名]
node dist/cli.js list [--project 项目名]
node dist/cli.js count
node dist/cli.js delete <id>

# 整理
node dist/cli.js update <id> [--title xx] [--content xx] [--kind xx] [--project xx|全局]
node dist/cli.js merge <目标id> <源id>          # 源并入目标后删除源

# 链接(Obsidian 式单向链 + 反向链接)
node dist/cli.js link <A的id> <B的id>          # A → B
node dist/cli.js unlink <A的id> <B的id>
node dist/cli.js links <id>                    # 看出链 + 反链
```

`project` 语义:**不传 = 全局记忆**(任何项目都能搜到);显式传 `--project` = 归属某项目。全局单库,`search`/`list` 可加 `--project` 过滤。

## 接入 ZCode

### MCP

在 `~/.zcode/cli/config.json` 的 `mcp.servers` 里加:

```json
{
  "mcp": {
    "servers": {
      "mem": {
        "type": "stdio",
        "command": "/绝对路径/node",
        "args": ["/绝对路径/mem/dist/mcp.js"]
      }
    }
  }
}
```

提供四个工具:`mem_save`、`mem_search`、`mem_list`、`mem_delete`(以及 `mem_update`、`mem_merge`)。

### hook(自动召回/沉淀)

在 `~/.zcode/cli/config.json` 的 `hooks.events` 里注册(三选一或全要):

```json
{
  "hooks": {
    "enabled": true,
    "events": {
      "SessionStart": [
        { "hooks": [{ "type": "process", "command": "/绝对路径/node",
          "args": ["/绝对路径/mem/hooks/auto-status.mjs"], "timeoutMs": 15000 }] }
      ],
      "UserPromptSubmit": [
        { "hooks": [{ "type": "process", "command": "/绝对路径/node",
          "args": ["/绝对路径/mem/hooks/auto-recall.mjs"], "timeoutMs": 20000 }] }
      ],
      "Stop": [
        { "hooks": [{ "type": "command",
          "command": "python3 /绝对路径/mem/hooks/auto-capture.py", "timeout": 10 }] }
      ]
    }
  }
}
```

> 注意:hook 的 `matcher` 字段不要写成空字符串 `""`,要么省略、要么给 ≥1 字符 —— 空串会导致整个配置解析失败。

三个 hook 的作用:

| 事件 | 脚本 | 作用 |
|------|------|------|
| `SessionStart` | `auto-status.mjs` | 会话开始注入记忆概览(总数 + 最近标题) |
| `UserPromptSubmit` | `auto-recall.mjs` | 每次提问自动召回相关记忆注入上下文 |
| `Stop` | `auto-capture.py` | 回合结束检测运维/配置类产出,引导存记忆 |

## 架构

```
src/
├── config.ts    # 配置加载 + 默认值
├── embed.ts     # embedding(硅基流动 /v1/embeddings)+ 余弦相似度
├── store.ts     # SQLite 存储(memories 表 + links 表)
├── core.ts      # save/search/update/merge/link 核心逻辑
├── server.ts    # MCP server(stdio)
├── cli.ts       # 命令行
└── hook-*.ts    # hook 专用输出端
hooks/           # 三个 hook 脚本
```

- **存储**:better-sqlite3 单文件,`memories` 表存正文 + blob 向量,`links` 表存单向链接。
- **召回**:查询向量化后与所有记忆暴力算余弦相似度,取 topK(<1 万条时足够快)。
- **链接**:单向存(`from → to`),反链自动算,对齐 Obsidian 双链语义;删记忆级联清理链接。

## 许可证

MIT