# MemWeave
**Repository Path**: Duke_Bit/mem-weave
## Basic Information
- **Project Name**: MemWeave
- **Description**: Let AI coding agents truly “remember” your project.
- **Primary Language**: Unknown
- **License**: BSD-3-Clause
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-06-09
- **Last Updated**: 2026-07-02
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# MemWeave
> 给 AI 智能体(Agent)用的本地优先、可记忆、可推理的记忆与上下文基础设施。
>
> **English version:** [README.en.md](./README.en.md)
---
## 它是什么
MemWeave 是一个让 AI Agent **记住上下文、且记住但不撑爆上下文** 的本地服务。
LLM 的上下文窗口有限,把整条记忆原文塞进 system prompt 是对 token 的浪费——Agent 可能只需要其中一两句。MemWeave 的解法是 **渐进披露(Progressive Disclosure)**:
> **只注入摘要,想要细节就主动拉取。**
```
system prompt → memory id + title + summary ← 低 token 开销
Agent 主动调 → memory_expand(id) → 完整正文 ← 拿到细节才花 token
```
围绕这个核心设计,MemWeave 提供一整套记忆基础设施:
- **结构化记忆**:事实、决策、偏好、事件、经验教训……按 `type` / `tier` 组织在本地 SQLite 中。
- **四层检索**:关键词(BM25)+ 向量语义 + 图谱关系 + 因果链,按需融合排序。
- **记忆整理(Consolidation)**:模拟"睡眠",定期把短期记忆升格为长期、淘汰冷门、发现因果。
- **自动注入**(OpenCode 插件):每次对话/读文件时自动把相关摘要追加到 system prompt,零调用成本。
- **MCP 工具集**(v0.4+):10 个 `memory_*` 工具内嵌在 server 进程,通过 Streamable HTTP 暴露在 `/mcp` 端点,闭环渐进披露。
- **多客户端插件**(v0.4+):OpenCode 插件(自动注入 + 写侧闭环)和 Codex 插件(10 个 `memory_*` 工具 + Stop 钩子),让任何 Agent 都能挂上 MemWeave。
- **Web UI**(Calm Memory Atlas):浏览、搜索、调试、运维整套记忆系统。
- **REST API**:完整的 HTTP 接口,便于脚本和第三方工具调用。
所有数据存储在本地 SQLite(Node 内置 `node:sqlite`,无需编译),**默认启用本地 LLM(Ollama)和本地 Embedding(ONNX)兜底**,零配置即可完整运行。
---
## 渐进披露——核心设计
MemWeave 的检索结果包含**完整的记忆正文**(`content`),但**默认注入给 LLM 的只含摘要**(`title` + `summary`)。这就是渐进披露:在 LLM 上下文窗口有限的前提下,先给最关键的"线索",让 Agent 主动拉取完整细节。
### 三层消费粒度
| 粒度 | 包含 | 何时返回 | Token |
|---|---|---|---|
| **Compact(默认)** | `id` / `type` / `tier` / `title` / `summary` | 注入、搜索(默认 `mode: "compact"`) | 低 |
| **Full record** | 上述 + `content` / `concepts` / `importance` … | `GET /api/v1/memories/:id` / `MCP memory_expand` | 中-高 |
| **Plus neighbors** | 上述 + 关联边、相邻会话、因果链 | `MCP memory_expand`(默认带边) | 高 |
### 注入的 XML
```xml
User prefers strict TypeScript
Always use noImplicitAny, exactOptionalPropertyTypes.
...
```
LLM 看到摘要,需要细节就主动调 `memory_expand({ memoryId: "m_abc" })` 拿完整 `content`。
### 闭环
插件只负责把摘要注入 system prompt(步骤 1);MCP 工具则由内嵌在 `@mem-weave/server` 里的 `/mcp` 端点提供(见上节),OpenCode 通过 `mcp: { memweave: { type: 'remote', url: 'http://127.0.0.1:3131/mcp' } }` 自动连上。其他客户端同理。
> **设计要点**:渐进披露不是"分阶段给不同详略",而是"**始终摘要,主动拉取才给全文**"。所有注入阶段渲染的 XML 是同一粒度,区别只在"哪批记忆"和"多少条"。
---
## 5 分钟上手
### 方式一:全局安装(推荐,OpenCode / IDE 集成需要)
```bash
npm install -g @mem-weave/server
npm install -g @mem-weave/opencode-plugin
memweave init # 生成 memweave.config.jsonc + 数据目录
memweave start # 后台启动(v0.5.7+ 默认),关闭终端不结束
```
打开浏览器访问 [`http://127.0.0.1:3131/ui/`](http://127.0.0.1:3131/ui/) 即可看到 **Calm Memory Atlas** Web UI。
#### 启动是后台的
`memweave start` **默认后台启动**(detach 到独立进程),返回后立即退出;**关闭终端也不会结束 server**。日志写到 `/memweave.log`(默认 `~/.memweave/data/memweave.log`),PID 文件在系统临时目录。
```bash
memweave start # 后台启动(默认)
memweave status # /api/v1/health
memweave stop # 用 PID 文件杀进程
# 调试时前台运行(Ctrl-C 停止)
memweave start -f # 或 --foreground
```
> 历史 workaround `Start-Process -WindowStyle Hidden memweave start` 在 v0.5.7+ **不再需要** —— `start` 本身就是后台的。
OpenCode 客户端:编辑 `~/.config/opencode/opencode.json`:
```jsonc
{
"plugin": ["@mem-weave/opencode-plugin"],
"mcp": {
"memweave": {
"type": "remote",
"url": "http://127.0.0.1:3131/mcp",
"enabled": true
}
}
}
```
> **必须**手填 `mcp` 段。OpenCode 不会调 plugin 的 `config` hook(见
> [OpenCode 插件文档](https://opencode.ai/docs/plugins/),hooks 列表里**没有**
> `config`),所以 plugin 无法自己注入 mcp.memweave。**Plugin 也**带一个
> `.mcp.json` 供 [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent)
> 自动加载,但 oh-my-openagent 实际**依赖** `~/.claude/plugins/installed_plugins.json`
> 这份 Claude Code plugin DB —— **多数用户** 没装 Claude Code,所以 plugin
> `.mcp.json` **不可靠**。**主路径是手填** mcp.memweave 段。
### 方式二:仅 npx 试用
```bash
npx @mem-weave/server init # 生成 memweave.config.jsonc + 数据目录
npx @mem-weave/server start # 后台启动;npx 进程退出后 server 仍存活
```
仅限**临时试用**。OpenCode / IDE 集成需要全局安装 server(方式一),否则 MCP 端点 `http://127.0.0.1:3131/mcp` 在 npx 进程退出后消失。
### 方式二:从源码运行(用于开发)
```bash
git clone memweave
cd memweave
npm install
npm run dev # 构建前端 + 启动服务端(前台)
```
### 健康检查
```bash
curl http://127.0.0.1:3131/api/v1/health
# → {"ok":true,"service":"memweave-server"}
```
---
## 架构一览
```
┌────────────────────────────────────────────────────────────┐
│ Clients │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │ Web UI │ │ CLI │ │ MCP/IDE │ │ OpenCode │ │
│ │ │ │ │ │ │ │ Plugin │ │
│ └────┬────┘ └─────┬────┘ └─────┬────┘ └─────┬──────┘ │
└────────┼─────────────┼─────────────┼─────────────┼──────────┘
│ HTTP │ stdio │ stdio │ HTTP
│ │ │ │ (POST /injection/preview)
└──────────────┴─────┬──────┴─────────────┘
▼
┌────────────────────────────┐
│ memweave-server │
│ (Fastify + TypeScript) │
└────────────┬───────────────┘
▼
┌──────────────────────────────────────┐
│ Retrieval Engine (4-layer fusion) │
│ ┌────────┐ ┌────────┐ ┌──────┐ ┌──┐ │
│ │ BM25 │ │ Vector │ │Graph │ │Caus│ │
│ └────────┘ └────────┘ └──────┘ └──┘ │
└────────────────┬─────────────────────┘
▼
┌─────────────────────┐
│ SQLite (node:sqlite)│
│ + 纯 JS 向量检索 │
└─────────────────────┘
▲
│
┌─────────────────────┐
│ Consolidation Worker│
│ (定期"睡眠"整理) │
└─────────────────────┘
```
详细的设计文档请参考 [`docs/`](./docs/) 目录。
---
## 核心概念
| 概念 | 说明 |
|---|---|
| **Tenant** | 多租户隔离单位(默认 `tenant_default`),每个租户有独立 API key。 |
| **Memory** | 一条结构化记忆,包含 `type`(fact / decision / preference / event / project_context / lesson / code_pattern / bug / workflow)、`tier`(short / medium / long)、`summary`、`details`、`scopes` 等。 |
| **Session** | 一次 Agent 会话,关联到一串 Observation。 |
| **Observation** | 一次用户/工具交互(user / tool / assistant 任一角色)。 |
| **Edge** | 记忆之间的关系(causal / temporal / entity)。 |
| **Consolidation Run** | 一次"睡眠"整理的快照:升格、淘汰、发现的因果。 |
---
## CLI 速查
通过 `npx @mem-weave/server ` 或全局安装后直接 `memweave `(源码开发用 `npm run cli -- `)。
| 命令 | 说明 |
|---|---|
| `init` | 生成默认配置和数据目录 |
| `start` | 后台启动服务(默认)后退出;`start -f` 前台运行(调试用) |
| `stop` | 停止后台服务 |
| `status` | 查看服务状态 |
| `migrate` | 运行数据库迁移 |
| `doctor` | 健康自检(数据库 / 配置 / 端口) |
| `backup` | 备份 SQLite 数据库 |
| `version` | 打印版本号 |
| `help` | 显示帮助 |
> 安装方式及包说明见上方 [5 分钟上手](#5-分钟上手)。
---
## REST API 概览
所有接口前缀 `/api/v1/`,完整 OpenAPI 风格的路由见 `packages/server/src/rest/routes/`。
| 端点 | 方法 | 用途 |
|---|---|---|
| `/health` | GET | 健康检查 |
| `/memories` | GET / POST | 搜索 + 写入记忆 |
| `/memories/:id` | GET / PATCH / DELETE | 记忆详情 / 编辑 / 删除 |
| `/memories/:id/edges` | GET | 记忆的关系图 |
| `/injection/preview` | POST | 生成注入 bundle(XML) |
| `/stats` | GET | 仪表盘统计(KPI、分布) |
| `/sessions` | GET | 会话列表 |
| `/sessions/:id/observations` | GET | 会话观察日志 |
| `/consolidation/runs` | GET | "睡眠"整理历史 |
| `/consolidation/runs/:id` | GET | 整理详情 |
| `/consolidation/runs/latest` | GET | 最近一次整理 |
| `/consolidation/run` | POST | 手动触发一次整理 |
| `/devices` | GET / POST / DELETE | 设备注册与管理 |
| `/settings` | GET | 查看服务端配置(密钥已脱敏) |
---
## MCP 工具集
从 v0.4 开始,10 个 `memory_*` MCP 工具**内置在 `@mem-weave/server` 进程里**,通过 **Streamable HTTP** 暴露在 `/mcp` 端点。**不再需要独立 MCP 包**(`@mem-weave/mcp` 已被移除)。
任何支持 Streamable HTTP 的 MCP 客户端都可以直接连接:
```json
{
"mcpServers": {
"memweave": {
"url": "http://127.0.0.1:3131/mcp"
}
}
}
```
OpenCode 配置(`~/.config/opencode/opencode.json` 的 `mcp` 段):
```json
{
"mcp": {
"memweave": {
"type": "remote",
"url": "http://127.0.0.1:3131/mcp",
"enabled": true
}
}
}
```
> Streamable HTTP 是 MCP 2025-03-26 协议;旧 HTTP+SSE 客户端可能不兼容——请升级到最新客户端。
| 工具 | 用途 |
|---|---|
| `memory_save` / `memory_recall` / `memory_smart_search` | 写/搜/融合搜索 |
| `memory_expand` / `memory_graph_query` / `memory_file_history` | 展开/图查询/文件关联 |
| `memory_sessions` / `memory_patterns` | 会话列表 / 模式发现 |
| `memory_consolidate` / `memory_forget` | 触发整理 / 软删除 |
---
## OpenCode 插件(自动注入 + 自动写入 + 写侧闭环)
插件 `@mem-weave/opencode-plugin` 安装后做三件事(v0.4+):
1. **注入记忆** — 每次对话/读文件时,把相关记忆的摘要追加到 system prompt,无需 LLM 主动调工具
2. **写侧闭环** — 监听 OpenCode 的 `message.updated` 事件,把每条完成的 user / assistant 消息自动上报到 `@mem-weave/server` 写进 `observations` 表(**幂等**:重复消息不会被重复写)
3. **MCP 端点** — MCP 工具(`memory_save` / `memory_recall` / `memory_expand` 等)由 `@mem-weave/server` 内置的 `/mcp` 端点提供,**OpenCode 通过手填的 `mcp.memweave` 段连接**(type **必须**是 `remote` —— OpenCode runtime 的 schema 只接受 `remote`)
**启用方式:**
```bash
npm install -g @mem-weave/opencode-plugin
```
`~/.config/opencode/opencode.json`:
```jsonc
{
"plugin": ["@mem-weave/opencode-plugin"],
"mcp": {
"memweave": {
"type": "remote",
"url": "http://127.0.0.1:3131/mcp",
"enabled": true
}
}
}
```
> **必须**手填 `mcp` 段。`type` 必须是 `"remote"`(OpenCode runtime 的 Effect
> schema 只接受 `"remote"`,不接受 `"http"` / `"sse"` —— 写其他值会被静默丢弃)。
> Plugin 根目录**也**带一个 `.mcp.json` 作为 backup,**但** oh-my-openagent
> 实际依赖 `~/.claude/plugins/installed_plugins.json` 这份 Claude Code
> plugin DB,普通用户**没**装 Claude Code,**.mcp.json 路径不可靠**。**主路径
> 是手填 mcp.memweave 段**。
**写侧闭环数据流:**
```
[OpenCode] user 提完问 → message.updated 事件
↓
[Plugin] event hook 触发 → OpenCode SDK 反查 → 拿 messageId + text
↓
[Plugin] POST /api/v1/sessions → server 幂等建 session
[Plugin] POST /api/v1/observations → server 幂等写 observation (tool_output=text, tool_input=JSON{messageId})
↓
[Server] consolidation worker 周期跑 → 按规则把高质量 observation 升级成 memory
↓
[Server] 下次 system.transform 注入 → LLM 看到摘要 → 主动调 memory_expand 拿全文
```
**注入的 XML 格式:**
```xml
User prefers strict TypeScript
Always use noImplicitAny, exactOptionalPropertyTypes.
...
```
| 环境变量 | 默认值 | 含义 |
|---|---|---|
| `MEMWEAVE_URL` | `http://127.0.0.1:3131` | 服务端地址(含注入 + 写入) |
| `MEMWEAVE_PLUGIN_TIMEOUT` | `10000`(ms) | 单次请求超时(注入 + 写入共用) |
> 插件对所有网络错误**静默容错**,服务端不可用时不会打断 Agent。
## Codex 插件(10 个 `memory_*` 工具 + Stop 钩子自动写回)
`packages/codex-plugin/` 是给 [OpenAI Codex](https://developers.openai.com/codex/plugins) 用的纯配置型插件(**无 SDK、无运行时**),做两件事:
1. **加载 10 个 `memory_*` 工具** — 通过 `.mcp.json` 把 Codex 的 MCP transport 指向已运行的 MemWeave server(`http://127.0.0.1:3131/mcp`),工具以 `mcp__memweave__memory_*` 前缀出现在 agent 工具列表
2. **Stop 钩子自动写回** — `hooks/stop.mjs`(跨平台 Node,`.sh` / `.cmd` 是薄壳)监听 Codex 的 `Stop` 事件,POST `/api/v1/sessions` 幂等建 session + POST `/api/v1/observations` 幂等写 assistant message。`messageId = sha256(sessionId + "turn-" + turnId + assistantContent)` → 同一 turn 多次 Stop 落同一行
**安装:**
```bash
# 1. 先把 MemWeave server 跑起来
npx @mem-weave/server start
# 2. 从本地装插件(Codex CLI)
codex plugin install /path/to/MemWeave/packages/codex-plugin
```
**Codex 配置**(`~/.codex/config.toml`)—— 默认装上即用:
```toml
[plugins."memweave@local".mcp_servers.memweave]
enabled = true
```
**写侧数据流:**
```
[Codex] turn 结束 → Stop 事件 (JSON on stdin, snake_case)
↓
[Plugin] hooks/stop.mjs → POST /api/v1/sessions { source: "codex", title }
↓ POST /api/v1/observations { hookType: "chat.assistant", text, messageId: sha256 }
↓
[Server] consolidation worker 周期跑 → 高质量 observation 升级成 memory
↓
[下个 Codex session] agent 主动调 mcp__memweave__memory_recall / memory_expand 拿
```
| 环境变量 | 默认值 | 含义 |
|---|---|---|
| `MEMWEAVE_SERVER_URL` | `http://127.0.0.1:3131` | MemWeave server 地址(写侧用) |
| `MEMWEAVE_TENANT` | `tenant_default` | 租户 ID(多租户隔离) |
> 钩子对所有网络错误**静默容错** —— server 不可用时 Codex 仍能正常完成 turn,不会被 MemWeave 拖死。
**已知限制**(与 OpenCode 插件的差距):
- Codex 没有 SDK 反查历史消息(不像 OpenCode 有 `session.messages()`),Stop 事件**只**给 `last_assistant_message`,所以**用户消息**和**工具调用**目前**不**被回写。OpenCode 插件能捕获所有 message,Codex 插件只能捕获 assistant 的最后一条。
- session 的 `ended_at` **不**被设值(REST 还没暴露 `end` 端点,OpenCode 插件也**没**设这个值,所以行为一致)。
**Codex MCP transport 注意**:Codex 的 `.mcp.json` 用 `type: "http"`,**和 OpenCode 的 `type: "remote"` 不一样** —— 两个客户端的 schema 是**两套**词汇表,**不可互换**。详见 [docs/superpowers/specs/2026-06-16-codex-plugin-design.md](./docs/superpowers/specs/2026-06-16-codex-plugin-design.md) §1.1。
## Claude Code / zcode 插件(Stop 钩子自动写回)
`packages/claude-code-plugin/` 是给 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) 和 [zcode](https://z.ai) 用的插件。两个客户端都用 `.claude-plugin` 格式 + Claude-Code 风格 hooks(stdin JSON 事件 + command 脚本),所以**一份插件两个平台都能装**。
做一件事:监听 **Stop 事件**(对话回合结束),从 transcript 提取最后一条 assistant 消息,POST 到 MemWeave server 写成 observation。后台 consolidation 会把高价值对话晋升为长期记忆。
**安装:**
```bash
# Claude Code
claude plugin install /path/to/memweave/packages/claude-code-plugin
# zcode:在插件市场添加本地插件,指向 packages/claude-code-plugin 目录
```
**写侧数据流:**
```
[Claude Code / zcode] turn 结束 → Stop 事件 (JSON on stdin)
↓
[Plugin] hooks/stop.mjs
├─ 读 stdin JSON (session_id, cwd, transcript_path)
├─ 从 transcript JSONL 提取最后一条 assistant 消息
├─ POST /api/v1/sessions → 幂等建 session (source: "claude_code" / "zcode")
└─ POST /api/v1/observations → 幂等写 assistant 消息 (hookType: "chat.assistant")
↓
[Server] consolidation worker → LLM 富化 → 晋升为长期记忆
```
| 环境变量 | 默认值 | 含义 |
|---|---|---|
| `MEMWEAVE_SERVER_URL` | `http://127.0.0.1:3131` | MemWeave server 地址 |
| `MEMWEAVE_TENANT` | `tenant_default` | 租户 ID |
> 钩子对所有网络错误**静默容错** —— server 不可用时 agent 正常完成 turn。写入幂等(基于 `(sessionId, messageId)` 哈希),Stop 重放不会产生重复记录。
更多细节见 [`packages/claude-code-plugin/README.md`](./packages/claude-code-plugin/README.md)。
## 写侧去重(服务端自动,零 token 开销)
读侧闭环了,写侧呢?**`memory_save` 不会和当前 context 产生重复内容** —— 因为去重在服务端自动完成,**LLM 零感知、零 token 开销**。
**机制**:`MemoryRepo.create` 在 INSERT 之前先做一次查重:
1. 用新记忆的 `concepts` 字段在 FTS5 索引上做 BM25 查询(同租户、排除已删除)—— < 1ms,零 embedding 开销
2. 取 top-5 候选,对每条计算 **Jaccard 相似度**(concepts 集合的交集/并集)
3. 如果 best Jaccard **≥ 0.8** 且 `type` 相同 → 视为重复,**强化现有记忆**而不是创建新的
**强化的两种行为**:
| 场景 | 行为 |
|---|---|
| 新输入与现有 content 几乎一样(长度差 < 25%) | 只 `recordAccess`:bump `access_count` / `reinforcement_score` / `strength` / `last_reinforced_at` |
| 新输入明显更丰富(长度 > 1.25× 或 importance 更高) | **合并**:content 升级、concepts 取并集、files 取并集、importance 取 `max` |
**设计要点**:
- **零 LLM tokens**:纯服务端 BM25 + 集合相似度计算
- **零额外延迟**:FTS5 在 SQLite 上亚毫秒级
- **type 必须匹配**:`fact` 永远不是 `decision` 的重复
- **调用方零负担**:`create()` 仍返回 `MemoryRecord`;想要 dedup 信号用 `createDetailed()` 拿 `CreateResult { memory, deduped, reinforcedId }`
- **REST 路由保持不变**:`POST /api/v1/memories` 走 `create()`,行为对调用方完全透明
> **没有"prompt 检测 / LLM 查重"**:那种方案每次 save 多花 ~1000 tokens 防御性查重,对高频用户是真实成本。本方案**只在重复确实发生时**消耗 server CPU,**LLM 全程不知情**。
### 写侧配套:输入限额 + 限流 + 后台合并 + 日志
- **`CreateMemoryInputSchema` 硬性限额**(`packages/server/src/core/types.ts`):`content` ≤ 100,000 字符、`concepts` ≤ 50、`files` ≤ 50。**Buggy / 恶意 LLM 也无法塞 10MB 正文或 10k 概念进 DB**。
- **写入限流**(`packages/server/src/server/rate-limiter.ts`):每 API key 一个 token bucket,30 写入/分钟 突发,2/秒 稳态。`POST /api/v1/memories` 超过配额返 `429 Too Many Requests` + `Retry-After` header。
- **后台合并阶段**(`packages/server/src/workers/consolidator.ts`):除了 evict + promote,加了 **Jaccard 合并阶段**—— 复用和实时去重**同一套** Jaccard 公式和阈值,扫所有同租户同类型记忆对,合并 near-duplicate。**实时去重** + **后台合并**形成两级防线。
- **进程级 consolidation mutex**:`runConsolidation` 内的 `consolidationInFlight` 布尔保证同一租户一次只能跑一次。后台 scheduler 和手动 `POST /api/v1/consolidate` 不会撞车。
- **pino 结构化日志**(`packages/server/src/server/logger.ts`):替换全仓 14 处 `console.*` 错误日志。`LOG_LEVEL` 环境变量调级别,默认 `info`。输出 JSON,方便接入 Loki / ELK。
- **dedup 强化也写 audit log**(`packages/server/src/db/repositories/memory-repo.ts`):`reinforceExisting` 触发时往 `access_logs` 插一条 `source: 'dedup_reinforce'` 记录,让"被强化过"在审计追踪里可见。
---
## 本地 Embedding(默认启用)
MemWeave 默认启用本地 ONNX Embedding,无需任何 API key 或外部服务。三种 embedding provider:
| Provider | 配置键 | 外部依赖 | 何时用 |
|---|---|---|---|
| **`local-xenova`**(默认) | `embedding.provider: "local-xenova"` | `@xenova/transformers`(已含预编译 ONNX 二进制,无需编译) | 想用真实语义向量、零配置、不想付 API 钱 |
| **`openai-compatible`** | `embedding.provider: "openai-compatible"` | 任何 OpenAI 兼容 `/v1/embeddings` 端点 | 已有 OpenAI / Voyage / 自托管端点 |
| **`noop`** | `embedding.provider: "noop"` | 无 | 显式关闭向量层(检索回落到 BM25 + 图 + 因果) |
`local-xenova` 是默认值,开箱即用。`@xenova/transformers` 是 `@mem-weave/server` 的可选依赖,其 ONNX runtime 带**预编译二进制**(onnxruntime-node,无 node-gyp)。
**关键行为:**
- 首次 `embed()` 调用会动态加载模型并从 Hugging Face Hub 拉取权重(~545MB,nomic-embed-text-v1,137M 参数),之后缓存在 `node_modules/@xenova/transformers/.cache/`。
- 后续调用**复用**已加载的 pipeline(并发请求共享同一次加载)。
- **自动降级**:如果 `@xenova/transformers` 未装、模型加载失败或推理超时(默认 60s),自动回退到 SHA-256 哈希向量并打印一次警告,保证整个系统不中断。
- 可通过 `fallbackOnError: false` 关闭降级(让错误冒泡,便于排查)。
- 输出维度与配置不匹配时,**截断或补零**到目标维度(与 `vector-search.ts` 的优雅降级一致)。
更多细节见 [`packages/server/src/providers/embedding/local-xenova.ts`](./packages/server/src/providers/embedding/local-xenova.ts) 和 [`packages/server/src/types/xenova.d.ts`](./packages/server/src/types/xenova.d.ts)。
## 本地 LLM 兜底(Ollama,默认启用)
MemWeave 默认启用本地 LLM 兜底,让记忆压缩、价值门控、关联抽取等 LLM 依赖功能在零配置下即可工作。三种 LLM provider:
| Provider | 配置键 | 外部依赖 | 何时用 |
|---|---|---|---|
| **`ollama`**(默认) | `llm.provider: "ollama"` | 本地 [Ollama](https://ollama.com)(需安装) | 零配置本地 LLM,16GB 内存可跑 qwen2.5:3b |
| **`openai-compatible`** | `llm.provider: "openai-compatible"` | 任何 OpenAI 兼容 `/v1/chat/completions` 端点 + apiKey | 已有 OpenAI / 自托管端点 |
| **`noop`** | `llm.provider: "noop"` | 无 | 显式关闭 LLM(记忆整理降级为纯规则) |
**`ollama` 自动管理:**
首次启动时 MemWeave 会:
1. 探测本地 Ollama 服务(`http://127.0.0.1:11434`)
2. 若未运行且 `autoStart: true`,尝试 spawn `ollama serve`
3. 若目标模型未拉取且 `autoPull: true`,执行 `ollama pull qwen2.5:3b`(首次 ~2GB 下载)
全部失败时静默降级为 `noop`,服务正常启动,LLM 依赖功能保持规则模式。`memweave doctor` 会检查 Ollama 状态并给出引导。
`memweave.config.jsonc` 示例:
```jsonc
{
"llm": {
"provider": "ollama",
"model": "qwen2.5:3b",
"ollama": {
"host": "127.0.0.1",
"port": 11434,
"autoStart": true,
"autoPull": true,
"timeoutMs": 120000
}
}
}
```
---
## Web UI 页面导览
访问 `/ui/`,共 5 个一级页面 + 记忆详情 + 图谱:
| 路由 | 名称 | 作用 |
|---|---|---|
| `/ui/` | **Atlas** | 仪表盘:KPI 卡片、tier/type 分布、活跃项目、最近整理 |
| `/ui/memories` | **Memories** | 三栏:过滤栏 + 列表 + 详情(搜索、类型筛选、强度排序) |
| `/ui/injection` | **Injection** | 表单预览注入包(按 token 预算裁剪) |
| `/ui/sleep` | **Sleep** | 整理运行历史 + 升格/淘汰的 git-diff 视图 |
| `/ui/settings` | **Settings** | 服务端配置查看、设备列表、API key 显隐 |
| `/ui/memories/:id` | **Memory Detail** | 记忆详情(正文 / 关系图 / 访问日志) |
| `/ui/graph/:id` | **Graph** | 关系图谱(径向布局) |
主题支持浅色 / 深色切换(右上角)。
---
## 开发
### 目录结构
v0.2 起改为 monorepo 布局(每个子目录都是独立 npm 包):
```
packages/
├── server/ # @mem-weave/server —— Fastify + SQLite + CLI
│ └── src/
│ ├── cli.ts, cli-entry.ts
│ ├── commands/ # 9 个 memweave 子命令
│ ├── core/ # 配置、Zod 枚举、衰减模型
│ ├── db/ # SQLite schema + 9 个仓储
│ ├── injection/ # 注入打包
│ ├── prompts/ # 提示词模板
│ ├── providers/ # Embedding (noop/openai/xenova) / LLM (noop/openai)
│ ├── rest/ # HTTP API(8 个路由文件)
│ ├── retrieval/ # 4 层检索引擎 + RRF
│ ├── server/ # HTTP 启动 + 调度器
│ ├── workers/ # Consolidation 后台任务
│ ├── types/ # ambient .d.ts
│ └── mcp/ # 内嵌 MCP server(Streamable HTTP,/mcp 端点)
│ ├── index.ts, service.ts, registry.ts
│ └── tools/ # 10 个 memory_* 工具
└── opencode-plugin/ # @mem-weave/opencode-plugin —— OpenCode 插件
└── src/
├── index.ts, client.ts
web/ # React 18 + Vite 前端(独立子项目,不在 packages/ 内)
├── src/
│ ├── pages/ # 7 个页面
│ ├── components/ # AppShell + 通用组件
│ ├── api/ # 类型化 fetch 封装
│ ├── lib/ # 格式化 / 工具
│ └── theme/ # CSS 变量(设计令牌)
├── tests/ # vitest + happy-dom
└── vite.config.ts
tests/ # 整个 monorepo 的 vitest 集成 + 单元测试
docs/ # 设计文档
scripts/publish.mjs # 一键构建 + 发布三个 npm 包
```
### 常用脚本
**单仓内(开发)**:
| 命令 | 说明 |
|---|---|
| `npm test` | 运行全部 Vitest 测试(服务端集成 + 单元) |
| `npm run typecheck` | TypeScript 类型检查(monorepo 全部) |
| `npm run build` | 构建前端 + 服务端两个包(tsc) |
| `npm run dev` | 构建前端 + 启动服务端(前台) |
| `npm run web:dev` | 前端 Vite 开发服务器(HMR,端口 5173) |
| `npm run web:build` | 构建前端到 `dist/web/` |
**单包(发布前用)**:
```bash
cd packages/server && npm run build # 仅 server(含内置 mcp)
cd packages/opencode-plugin && npm run build # 仅 opencode-plugin
```
**发布到 npm**(需要 `$NPM_TOKEN`):
```bash
node scripts/publish.mjs --dry-run # 检查 tarball 内容
node scripts/publish.mjs --publish # 实际发布(按 server -> opencode-plugin 顺序)
```
> 前端单元测试在 `web/` 目录下:`cd web && npm test`。
### 质量门槛
修改后请确认:
```bash
npm test # 全部通过
npm run typecheck # 0 错误
npm run build # 成功
```
---
## 常见问题
**Q: 数据存在哪?**
A: SQLite 文件,路径见 `memweave.config.jsonc` 的 `storage.path`,默认 `/memweave.db`。
**Q: 没有外部 LLM / Embedding 服务可以吗?**
A: 可以,**默认就是完全本地的**。零配置启动时:
- **LLM**:默认 `llm.provider: "ollama"`,自动管理本地 Ollama(探测→启动→拉模型)。16GB 内存可跑 qwen2.5:3b,覆盖记忆压缩/价值门控/关联抽取。Ollama 不可用时静默降级为规则模式。
- **Embedding**:默认 `embedding.provider: "local-xenova"`,用本地 ONNX 模型(nomic-embed-text-v1,预编译二进制无需编译)。首次调用从 HuggingFace 拉取权重(~545MB)并缓存。加载失败时降级为哈希向量。
- **外部 API**(可选):把 `llm.provider` / `embedding.provider` 改为 `openai-compatible`,对接任何 OpenAI 兼容端点。
若要显式关闭某个层:设 `llm.provider: "noop"` 或 `embedding.provider: "noop"`。
**Q: 端口被占用?**
A: 修改 `memweave.config.jsonc` 中的 `server.port` 字段。
**Q: 怎么清理全部数据?**
A: 停止服务 → 删除 `dataDir` 指向的目录 → 重新 `init`。
**Q: Web UI 访问 503?**
A: 服务端没找到 `dist/web/`,运行 `npm run web:build` 即可。
---
## 路线图
- [x] **本地 ONNX Embedding** —— `LocalXenovaEmbeddingProvider` 已接通为默认(基于 `@xenova/transformers`,预编译 ONNX 二进制,自动降级到哈希向量)
- [x] **本地 LLM 兜底(Ollama)** —— 零配置自动管理 Ollama(探测/启动/拉模型),16GB 内存可跑 qwen2.5:3b
- [x] **去原生编译依赖** —— better-sqlite3 → node:sqlite(Node 内置),sqlite-vec → 纯 JS 向量检索,安装无需 `--allow-scripts`
- [ ] 多 Embedding provider 适配(OpenAI / Voyage / 更多本地模型)
- [ ] 联邦/同步协议(device-to-device 端到端加密同步)
- [ ] 知识图谱自动抽取
- [ ] 记忆"遗忘权"(GDPR 合规)
---
## 许可证
本仓库暂未声明开源许可证,使用前请联系作者。
---
## 致谢
- [Fastify](https://fastify.io/) — 高性能 HTTP 服务
- [node:sqlite](https://nodejs.org/api/sqlite.html) — Node 内置 SQLite(C 后端,无需编译)
- [@xenova/transformers](https://github.com/xenova/transformers.js) — 本地 ONNX Embedding(预编译二进制)
- [Ollama](https://ollama.com/) — 本地 LLM 运行时
- [Model Context Protocol](https://modelcontextprotocol.io/) — Agent 工具调用标准
- [Vite](https://vitejs.dev/) + [React](https://react.dev/) — 前端