diff --git a/CHANGELOG.md b/CHANGELOG.md index 681af24..6df24b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index aabe0ce..31c1c3a 100644 --- a/README.md +++ b/README.md @@ -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 | @@ -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 @@ -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 \ @@ -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)。 --- @@ -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) @@ -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 开发模式可单独启动: diff --git a/docs/RUN_LOCAL.md b/docs/RUN_LOCAL.md index 14ad3ce..2a89388 100644 --- a/docs/RUN_LOCAL.md +++ b/docs/RUN_LOCAL.md @@ -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 ┌──────────┐ @@ -28,13 +34,21 @@ 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 @@ -42,6 +56,8 @@ Web 浏览器验收及其通过标准统一见 [TESTING.md](TESTING.md)。 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 .. @@ -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 @@ -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 `, +`.dev/` 里只剩应用侧产物。 + ## 关键设计点 - **Redis 跳过**:`MEM_REDIS_URL` 留空 → memd 用进程内 goroutine fallback 做异步索引 @@ -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[]`;缩小目标路径或使用