Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ The project publishes 0.x prerelease versions; a stable release line is not yet

## [Unreleased]

### Added

- Onboarding leads with the single-host Compose path: `README.md` gained a
getting-started section covering `generate-env.sh` → `docker compose up -d
--build --wait` → first-user registration → folder creation, and states the
model-stage boundaries of the default `local-fast-v2` profile on that path
(`#109`).

### Changed

- The bare-metal process stack is documented as development-only. `README.md`
and `docs/RUN_LOCAL.md` pin the toolchain to `server/go.mod`,
`worker/uv.lock`, and `web/package-lock.json`, and give a WSL/Linux
equivalent for every Homebrew-leaning step, including a dependency-container
route for hosts without a Homebrew PostgreSQL layout (`#109`).

### Fixed

- Release validation now verifies that every immutable GitHub Action pin used
Expand Down
114 changes: 95 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,50 @@ flowchart LR
> `mem` is experimental. Interfaces, storage schemas, and release artifacts may
> change without notice. Do not use it as the only copy of important data.

## 开始使用

面向第一次部署 mem 的人,主路径是 `deploy/compose`:一份密钥文件、一条 `up`、
一个浏览器。裸机进程栈是**只用于开发**的替代路径,见
[docs/RUN_LOCAL.md](docs/RUN_LOCAL.md)。

前置条件是 Linux 主机或 WSL2 / Docker Desktop 环境,装有 Docker Engine 与
Compose v2、`openssl`;模型无关的单机基线为 4 vCPU / 8 GiB 内存 / 80 GiB 磁盘。

```bash
git clone https://github.com/fullstack-ai-infra/mem.git
cd mem/deploy/compose
./generate-env.sh # 生成 .env(0600,拒绝覆盖已有文件,含随机密钥)
docker compose --env-file .env -f compose.yaml config --quiet
docker compose --env-file .env -f compose.yaml up -d --build --wait
```

起来的是六个常驻服务(PostgreSQL/pgvector、Redis、MinIO、Worker、memd、Web)
加两个一次性任务(`minio-init` 建私有桶、`migrate` 恰好跑一次 schema)。只有 Web
对外暴露:`http://127.0.0.1:8080`,后端网络是 internal,数据库、对象存储与 gRPC
都不发布端口。

1. 打开 `http://127.0.0.1:8080` 注册首个账号。默认
`MEM_REGISTRATION_MODE=first_user` 只放行第一个用户,第二次注册应被拒绝。
2. 在 Web 里新建目录(例如 `/Photos`)并上传文件,确认原件可浏览、可下载。
3. CLI 与 MCP 打的是同一套 API:拿到 token 后 `mem put`、`mem search`、
`mem remember` 指向 `http://127.0.0.1:8080`。

`generate-env.sh` → `up -d --build --wait` → 首用户注册 → 建目录上传这条闭环已在
运维 dogfood 环境跑通,过程记录见
[#109](https://github.com/fullstack-ai-infra/mem/issues/109)。同时说清当前边界:
默认 AI profile `local-fast-v2` 只启用文本 embedding(`ollama:qwen3-embedding:0.6b`,
768 维)并只放行文本/PDF/结构化文本类 MIME,视觉 embedding、LLM、VLM、ASR 与
rerank 阶段是显式禁用的(视觉阶段在 CLIP 权重的安装、完整性与离线缓存探针纳入
模型清单之前保持禁用,避免首次请求时下载权重)。而 Compose 的 `backend` 网络是
`internal`,Worker 容器内的 `OLLAMA_BASE_URL` 又指向容器自身的 `127.0.0.1:11434`,
所以默认容器路径下这些模型阶段不可达:选择 `local-fast-v2` 时的 768 维 embedding
探针会失败并拒绝写入配置(fail-closed,不静默回退)。要让语义检索可用,需要显式
接入可达的 Ollama 或 BYOM provider;文件与原件下载、结构化记忆和词法召回不依赖
模型阶段。

密钥备份、端口与反向代理边界、备份/恢复演练、升级回滚以及多机 Helm 方案见
[生产部署指南](docs/DEPLOYMENT.md)。

## What is in this repository?

| Component | Purpose |
Expand Down Expand Up @@ -178,16 +222,42 @@ forget 需要 `delete` 及允许删除的 workspace 角色。写入时关联
`source_file_id` 还需 `read`。
`source=all` 的调用方必须检查 `partial/warnings`,避免把单路降级当成完整召回。

## 开发环境
## 开发环境(仅开发)

改代码才需要这一节。只是想把 mem 跑起来,请回到[开始使用](#开始使用)。

本地开发依赖:
工具链 pin 与平台无关,两个系统用同一套安装方式;`make bootstrap` 已经带 Linux
分支(Playwright Chromium 在 Linux 上加 `--with-deps`):

- Go 1.25
- Python 3.11+ and [`uv`](https://docs.astral.sh/uv/)
- Node.js 24 and npm
- `protoc` 34.1, `protoc-gen-go` v1.36.11, and
`protoc-gen-go-grpc` v1.6.2 when changing protobuf definitions
- Docker with Compose
| 依赖 | pin | 为什么是这个值 |
| --- | --- | --- |
| Go | 1.25.x | `server/go.mod` 的 `go 1.25.0` |
| Python + `uv` | 3.11 或 3.12,锁在 `worker/uv.lock` | Worker 的 protobuf 与测试依赖按 `uv sync --locked` 还原 |
| Node.js + npm | 24.x,锁在检出的 `web/package-lock.json` | Web 构建与浏览器验收 |
| protobuf 代码生成 | `protoc` 34.1、`protoc-gen-go` v1.36.11、`protoc-gen-go-grpc` v1.6.2 | 只有改 `worker/proto/*.proto` 才需要;两个 Go 插件用 `go install …@v1.36.11` / `@v1.6.2` 安装,平台无关。`make proto-python` 走 `grpcio-tools`,不需要系统 `protoc` |
| Docker 与 Compose v2 | 任意受支持版本 | 跑依赖容器、集成测试和生产 Compose 验收 |

### 依赖服务的平台等价

裸机开发栈(`bash scripts/dev_up.sh` / `bash scripts/dev_down.sh`,运行时数据落在
`.dev/`)在脚本层已经是跨平台的:`BREW_PREFIX` 会解析 `/opt/homebrew`、`/usr/local`
和 `/home/linuxbrew/.linuxbrew`,pgvector 模块同时接受 Linux 的 `vector.so` 与 macOS
的 `vector.dylib`,守护进程在 Linux 上用 `setsid` 脱离会话(`scripts/dev_up.sh:28-37`、
`:96-140`)。它假设 PostgreSQL 来自 **Homebrew 布局**,所以 Linux/WSL2 上要么装
Linuxbrew,要么改用依赖容器:

| 步骤 | macOS | Linux / WSL2 |
| --- | --- | --- |
| PostgreSQL 17 + pgvector | `brew install postgresql@17 pgvector`(keg-only,不需要 sudo) | 装 [Linuxbrew](https://docs.brew.sh/Homebrew-on-Linux) 后同一条命令即可,`dev_up.sh` 的默认前缀就是 `/home/linuxbrew/.linuxbrew`。用 apt 的 `postgresql-17` + `postgresql-17-pgvector` 时 `dev_up.sh` 找不到 keg(`detect_pg` 只查 brew 前缀),改走下一行的依赖容器路径 |
| 不装 Homebrew 的替代 | — | `make up` 起 `pgvector/pgvector:pg16`(`:5432`)、Redis(host `:6479`)、MinIO(host `:9100`),再按 `server/.env.example` 与 `worker/.env.example` 里的同一组默认值显式导出环境变量,用 `make server`、`make worker`、`make web` 以进程方式跑应用 |
| MinIO + `mc` | `brew install minio minio-mc` | 直接取官方 Linux 二进制:`curl -fL -o .dev/bin/minio https://dl.min.io/server/minio/release/linux-amd64/minio`(`mc` 同理,然后 `chmod +x`)。`dev_up.sh` 依次在 `.dev/bin/`、brew、PATH 中查找 |
| Redis | 可不装:`MEM_REDIS_URL` 留空时 memd 用进程内 fallback | 同左;要真队列或 SaaS 防重放时用 `make up` 里那个 `:6479` |
| Worker 依赖 | `cd worker && uv sync --extra clip` | 同一条命令(`--extra clip` 只为可选的图搜图装 CLIP) |
| `memd` / `mem` 二进制 | `make build-memd build-mem` | 同一条命令 |
| Ollama(要验收语义检索时) | 本机监听 `localhost:11434` | WSL2 里 `localhost:11434` 只有在 Docker Desktop WSL2 集成、Windows 侧 Ollama 设了 `OLLAMA_HOST` 监听、或启用镜像网络时才可达。不可达时 `mem profile select local-fast-v2` 的 768 维 probe 会失败并且不写入配置——这不是回退,是 fail-closed |

完整的日常开发流程、seed 数据、profile 边界与排查见 [docs/RUN_LOCAL.md](docs/RUN_LOCAL.md);
可重复的回归门槛见 [docs/TESTING.md](docs/TESTING.md)。

## 为什么是 Agent-Native

Expand Down Expand Up @@ -228,15 +298,18 @@ mem 的壁垒不是绑定某个更大的模型,而是长期积累的、用户

---

## 快速开始
## CLI 闭环示例

项目仍处于 Phase 1 MVP。当前开发体验:
前提:已经有一个跑起来的 mem 实例(推荐按[开始使用](#开始使用)起单机容器),
`mem` 与 `mem-mcp` 已构建,并且登录过:

```bash
mem auth login # 或显式指定实例:mem --server http://127.0.0.1:8080 auth login
```

项目仍处于 Phase 1 MVP。导入、召回与交接的当前体验:

```bash
git clone https://github.com/fullstack-ai-infra/mem.git
cd mem
./scripts/dev_up.sh
mem auth login
mem put ~/Photos --recursive
# 可选:同步端附带可信的拍摄时间、位置和来源;AI 建议稍后在 Web 中确认
mem put ~/Photos/IMG_0001.jpg \
Expand All @@ -256,8 +329,9 @@ mem resume photos/import
mem workspace export --output agent-workspace.membundle
```

生产部署同时提供单机 Compose 和多机 Helm 方案,完整的密钥、迁移、高可用、
备份恢复与升级边界见 [生产部署指南](docs/DEPLOYMENT.md)。默认视觉模型的真实英文/中文边界见
单机 Compose(见上文[开始使用](#开始使用))和多机 Helm 是同一份能力的两种部署
形态;密钥、迁移、高可用、备份恢复与升级边界的完整口径见
[生产部署指南](docs/DEPLOYMENT.md)。默认视觉模型的真实英文/中文边界见
[自然语言搜图基线](docs/acceptance/VISUAL_SEARCH_BASELINE.md)。

---
Expand Down Expand Up @@ -295,7 +369,8 @@ embedding,平台托管 embedding 则使用独立的 workspace 权益和额度
- 托管向量权益、幂等计费与隐私边界:[docs/MANAGED_EMBEDDINGS.md](docs/MANAGED_EMBEDDINGS.md)
- 北极星可重复验收:[docs/acceptance/NORTH_STAR.md](docs/acceptance/NORTH_STAR.md)
- Workspace bundle 决策:[docs/adr/0004-workspace-bundle.md](docs/adr/0004-workspace-bundle.md)
- 本地运行与验证:[docs/RUN_LOCAL.md](docs/RUN_LOCAL.md)
- 部署:单机 Compose 与多机 Helm:[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)
- 开发用裸机进程栈与 smoke test:[docs/RUN_LOCAL.md](docs/RUN_LOCAL.md)
- 项目开发边界:[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)
- 测试环境与回归门槛:[docs/TESTING.md](docs/TESTING.md)
- 参与贡献:[组织贡献基线](https://github.com/fullstack-ai-infra/.github/blob/main/CONTRIBUTING.md)
Expand All @@ -318,8 +393,9 @@ mem/
└── docker-compose.test.yml ← 一次性 PostgreSQL 回归环境
```

完整的本地启动、Worker、Web 和 smoke test 步骤见
[docs/RUN_LOCAL.md](docs/RUN_LOCAL.md)。
完整的本地进程栈启动、Worker、Web 和 smoke test 步骤(**仅开发**,需要
Homebrew 布局的 PostgreSQL)见 [docs/RUN_LOCAL.md](docs/RUN_LOCAL.md);要把 mem
部署起来用,走[开始使用](#开始使用)与 [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)。

Web 开发模式可单独启动:

Expand Down
83 changes: 75 additions & 8 deletions docs/RUN_LOCAL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# 本地运行 mem 全栈(裸机 · 无 Docker)
# 本地运行 mem 全栈(裸机 · 无 Docker · 仅开发)

本文用于启动完整开发栈和手工 smoke。可重复的单元、Race、PostgreSQL 集成、
Web 浏览器验收及其通过标准统一见 [TESTING.md](TESTING.md)。
本文用于**开发**:启动完整开发栈和手工 smoke。只想把 mem 跑起来用,请走单机
Compose 路径([README 开始使用](../README.md#开始使用) 与
[DEPLOYMENT.md](DEPLOYMENT.md));那里不需要 Homebrew,也不需要本地编译。
可重复的单元、Race、PostgreSQL 集成、Web 浏览器验收及其通过标准统一见
[TESTING.md](TESTING.md)。

在没有 Docker 的 macOS 开发环境中,整套栈可用**本地进程**拉起,不走
`docker compose`。
在没有 Docker 的开发环境中,整套栈可用**本地进程**拉起,不走 `docker compose`。
一条命令起、一条命令停,运行时数据全部落在 `.dev/`(已 gitignore)。
`scripts/dev_up.sh` 是跨平台的(brew 前缀、pgvector 的 `vector.so`/`vector.dylib`、
Linux 的 `setsid` 都有分支),但它按 **Homebrew 布局**找 PostgreSQL,所以 Linux 与
WSL2 用户要么装 Linuxbrew,要么改用下面的依赖容器路径——每一步的平台等价见
[依赖服务的平台等价](#依赖服务的平台等价)。

```
┌──────────┐ gRPC ┌──────────┐ HTTP ┌──────────┐
Expand All @@ -28,20 +34,30 @@ Web 浏览器验收及其通过标准统一见 [TESTING.md](TESTING.md)。
> 用 `@17` 而不是 `@16`:brew 的 pgvector bottle 只为 postgresql@17/@18
> 编译了 `vector.so`,装在 @16 上 `CREATE EXTENSION vector` 会失败。
> `dev_up.sh` 会自动探测 @17/@18/@16 中带匹配 pgvector 的版本。
>
> **Linux / WSL2 等价**:装 [Linuxbrew](https://docs.brew.sh/Homebrew-on-Linux)
> 后同一条命令可用,`dev_up.sh` 的默认前缀即 `/home/linuxbrew/.linuxbrew`,
> 并且 Linux bottle 的模块名是 `vector.so`(macOS 是 `vector.dylib`),脚本
> 两种都接受。apt 的 `postgresql-17` + `postgresql-17-pgvector` **不会**被
> `detect_pg` 找到(它只查 brew 前缀),走 apt 请用
> [依赖服务的平台等价](#依赖服务的平台等价) 里的容器依赖路径。
- MinIO server + mc client。推荐用 brew(dl.min.io 在本网络偶发限流/TLS 断连,
brew 走 ghcr.io 更稳):
```bash
brew install minio minio-mc
```
`dev_up.sh` 会自动在 `.dev/bin/`、brew、PATH 中找 `minio`/`mc`。
也可手动下到 `.dev/bin/`(若 dl.min.io 可达):
也可手动下到 `.dev/bin/`(若 dl.min.io 可达)——这也是 Linux / WSL2 的
推荐等价做法,无需 brew:
```bash
mkdir -p .dev/bin
curl -fL -o .dev/bin/minio https://dl.min.io/server/minio/release/linux-amd64/minio
curl -fL -o .dev/bin/mc https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x .dev/bin/minio .dev/bin/mc
```
> `mc` 是可选的:memd 启动时会自己 `MakeBucket` 建桶,没有 mc 也能跑通。
> Linux 上 MinIO 官方包名按架构区分;ARM 机器把上面 URL 里的 `linux-amd64`
> 换成 `linux-arm64`。
- worker Python 依赖(`--extra clip` 为**可选**的图搜图能力安装 CLIP):
```bash
cd worker && uv sync --extra clip && cd ..
Expand Down Expand Up @@ -102,6 +118,50 @@ Web 浏览器验收及其通过标准统一见 [TESTING.md](TESTING.md)。
结构化记忆的 `remember → context --source memory` 只依赖 PostgreSQL 和
memd,不调用 Worker、embedding、VLM 或回答模型,可在这些模型全部离线时验证。

## 依赖服务的平台等价

macOS 与 Linux / WSL2 的差别只在于**依赖服务从哪来**;`make` 目标、CLI 语义和
`.dev/` 布局两边一致。版本 pin 以 `server/go.mod`、`worker/uv.lock`、
`web/package-lock.json` 与 [TESTING.md](TESTING.md) §1 为准。

| 依赖 | 版本 pin | Linux / WSL2 等价与注意点 |
| --- | --- | --- |
| Go | 1.25.x(`server/go.mod` 要求 `go 1.25.0`) | 平台无关:任意安装方式,只要 `go version` 是 1.25.x;两个 protoc 插件用 `go install`,两系统同命令 |
| Python + `uv` | 3.11 或 3.12,锁在 `worker/uv.lock` | 平台无关:`uv sync --locked` 按锁文件还原,Linux 上不需要额外编译依赖(Wheel 与 macOS 同源) |
| Node.js + npm | 24.x,锁在 `web/package-lock.json` | 平台无关:`npm ci`。Playwright 在 Linux 上需系统依赖,`make bootstrap` 已按 `uname -s` 加 `--with-deps` |
| `protoc` | 34.1(仅改 `worker/proto/*.proto` 时需要) | 取 protobuf releases 的 `linux-x86_64` 包解压即可;只改 Python stub 时不需要系统 `protoc`,`make proto-python` 走 `grpcio-tools` |
| PostgreSQL 17 + pgvector | `postgresql@17`(`@16` 无匹配 pgvector bottle) | Linuxbrew 下与 macOS 同命令;apt 的 `postgresql-17` + `postgresql-17-pgvector` 不被 `dev_up.sh` 识别,走下面的容器依赖路径 |
| MinIO + `mc` | 开发栈用 `minio/minio:latest`;生产单机 Compose pin 在 `RELEASE.2025-04-22T22-12-26Z` | 取 `linux-amd64`(ARM 用 `linux-arm64`)官方二进制放 `.dev/bin/`,随 dl.min.io 当前 release;或走容器依赖路径 |
| Redis | 7.x | 可都不装:`MEM_REDIS_URL` 留空走进程内 fallback;要真队列时 `make up` 起的是 host `:6479` |
| Ollama | `localhost:11434` | WSL2 需要 Docker Desktop WSL2 集成、Windows 侧设 `OLLAMA_HOST` 或启用镜像网络;不可达时 profile 选择的 768 维 probe 会 fail-closed,不会静默回退 |

### 不装 Homebrew:依赖容器 + 进程应用

`dev_up.sh` 按 Homebrew 布局找 PostgreSQL,因此 apt 路线改用根
`docker-compose.yml` 提供的依赖(`pgvector/pgvector:pg16` → `:5432`,Redis →
host `:6479`,MinIO → host `:9100`,凭据 `mem` / `mem-minio-password`,桶 `mem`),
应用仍以进程方式跑,端口与上文裸机栈完全相同:

```bash
make up # 只起依赖;memd/worker 在这个文件里是注释掉的,故意如此

# memd 不读取 server/.env(Go 侧没有 dotenv),必须显式导出:
set -a; . server/.env.example; set +a
make server # memd :8787

# Worker 会读 worker/.env(mem_worker/config.py 的 env_file=".env"):
cp worker/.env.example worker/.env
make worker # gRPC :50051

cd web && npm ci && npm run dev # vite 代理 /v1 -> :8787
```

`server/.env.example` 里的 `MEM_REDIS_URL=` 是空值,memd 因此走进程内 fallback:
开发足够,但没有崩溃恢复;生产必须接真 Redis(见
[DEPLOYMENT.md](DEPLOYMENT.md))。这条路径的依赖端口与
`scripts/dev_up.sh` 用的默认值同源(脚本注释即写明"kept in sync with
server/.env.example + worker/.env.example")。

## 用户回来怎么用(日常)

```bash
Expand Down Expand Up @@ -329,12 +389,16 @@ npm run build # 产物在 web/dist/

| 服务 | 端口 | 进程 | 日志 |
|-----------|--------|------------------------------|-----------------------|
| PostgreSQL| 5432 | brew `postgresql@17` | `.dev/logs/postgres.log` |
| PostgreSQL| 5432 | brew `postgresql@17`(Linux 上同样是 brew keg) | `.dev/logs/postgres.log` |
| MinIO | 9100 | `.dev/bin/minio` | `.dev/logs/minio.log` |
| worker | 50051 | `worker/.venv` python gRPC | `.dev/logs/worker.log`|
| memd | 8787 | `bin/memd` (Go) | `.dev/logs/memd.log` |
| Ollama | 11434 | 系统已有 | — |

走[依赖容器路径](#不装-homebrew依赖容器--进程应用)时,端口不变,只是
PostgreSQL / MinIO / Redis 变成容器:日志改用 `docker compose logs -f <svc>`,
`.dev/` 里只剩应用侧产物。

## 关键设计点

- **Redis 跳过**:`MEM_REDIS_URL` 留空 → memd 用进程内 goroutine fallback 做异步索引
Expand Down Expand Up @@ -368,7 +432,10 @@ AI profile 的私有 BYOM workspace。
- `seed_demo_data.sh` 断言超时:CPU 跑 nomic/CLIP 较慢,调大
`MEM_INGEST_TIMEOUT_SEC=600 bash scripts/seed_demo_data.sh`。
- `CREATE EXTENSION vector failed`:当前 postgres 版本没有匹配的 pgvector
`.so`;装 `brew install postgresql@17` 并重跑 `dev_up.sh`。
模块(macOS 是 `vector.dylib`,Linux 是 `vector.so`)。brew/Linuxbrew 路线:
`brew install postgresql@17 pgvector` 并重跑 `dev_up.sh`;依赖容器路线不会出现
这个错误(`pgvector/pgvector:pg16` 镜像自带扩展),如果你手工把 `MEM_DB_URL`
指向了 apt 装的 PostgreSQL,请改回容器端口或改用 brew keg。
- `idempotency_conflict`:同一幂等键已绑定另一份归一化请求;重试原请求或换一个
能稳定标识新事件的 key,不要随机重试冲突请求。
- `path_forbidden`:记忆路径或来源文件超出 Token `paths[]`;缩小目标路径或使用
Expand Down
Loading