# Memory Gateway Memory Gateway 是一个轻量级记忆网关项目,用统一的 HTTP API 把上层应用接到两个记忆后端: - OpenViking:负责会话归档、长期记忆抽取、按 `user_id / session_id` 隔离的语义检索。 - EverOS:负责用户画像、episodic memory、profile 查询等补充记忆能力。 当前重点模块是 `memory_system_api`。它不是 OpenViking 的替代品,而是一个更窄的业务网关:上层只面对 user、user key、session 和 message;网关内部为每个业务用户创建独立的 OpenViking admin account。 ## 核心模型 ### 身份字段 | 字段 | 含义 | 是否自定义 | 备注 | |---|---|---:|---| | `user_id` | 真实终端用户 | 是 | 先通过 `/users` 创建,后续写入、查询、commit 时使用 | | `user_key` | OpenViking 返回的用户 key | 否 | `/users` 返回并写入 SQLite;业务调用时随请求体或 query 传入 | | `session_id` | 一次会话 ID | 是 | OpenViking session 容器,同时用于带 session context 的搜索 | 建议 ID 只使用字母、数字、`_`、`-`、`.`、`@`,不要使用空格、斜杠或中文。 ### 先创建用户再使用 除健康检查外,业务调用分两步: 1. 调用 `POST /memory-system/users` 创建 OpenViking user。 2. 保存返回的 `user_key`,后续写入消息、commit、extract、task、search、profile 都传 `user_id + user_key`,不需要传 `account_id`。 如果没有先创建 user,或者 `user_key` 与数据库里的 `user_id` 不匹配,`messages`、`commit`、`extract`、`tasks`、`search`、`profile` 都会返回 `401`。 创建业务 user 时,网关直接为该用户创建一个独立 OpenViking account。默认 `account_id` 是 `_account`,`admin_user_id` 是传入的 `user_id`: ```http POST /api/v1/admin/accounts {"account_id": "_account", "admin_user_id": ""} ``` 网关不再调用 `/api/v1/admin/accounts/admin/users`。所有返回的 `admin_user_id / user_key`、创建过的 `session_id`、commit 返回的 `task_id / archive_uri` 都写入 SQLite,不放在进程内缓存里。 ### 鉴权 Memory Gateway 有两类 key: | Key | 请求头 | 用途 | 是否必需 | |---|---|---|---| | Memory System API key | `X-API-Key` | 保护 Memory Gateway 服务本身 | 仅当 `server.api_key` 配置非空时需要 | | OpenViking user key | 请求体/query 的 `user_key` | 校验具体用户并调用 OpenViking 普通用户 API | 除 `/health` 和 `/users` 外都需要 | OpenViking 的 root key 配在服务端 `config.yaml` 中,由网关内部使用。调用方不需要也不应该在业务请求中传 OpenViking root key。 OpenViking 内部调用遵循: | OpenViking 场景 | 鉴权 | |---|---| | Admin API,例如创建 account | `X-API-Key: ` | | 普通用户 API,例如 session/message/commit/task/search | `X-API-Key: ` | ### Session 与搜索范围 OpenViking session 由请求里的 `session_id` 创建和提交。`/memory-system/search` 的 OpenViking 分支固定调用 OpenViking `/api/v1/search/search`,`target_uri` 可选,默认是 `viking://user/memories`,并默认传 `level: 2`、`score_threshold: 0.8`。 ## 本地三服务安装 Memory Gateway 需要先连上两个后端服务。默认本地端口约定: | 服务 | 默认地址 | 作用 | |---|---|---| | OpenViking | `http://127.0.0.1:1933` | session、archive、long-term memory、resources、semantic search | | EverOS | `http://127.0.0.1:1995` | profile、episodic memory、recall | | Memory Gateway | `http://127.0.0.1:1934` | 对业务方暴露统一 `/memory-system` API | ### 安装 OpenViking OpenViking 可以直接用 `pip install` 安装,不需要 clone 源码。Memory Gateway 只依赖它暴露 HTTP API 和一个服务端 root key: ```bash python -m venv /Users/tom/.venvs/openviking source /Users/tom/.venvs/openviking/bin/activate pip install -U pip pip install openviking --upgrade --force-reinstall ``` 如果本机已经有 OpenViking 源码或已安装的 `openviking-server`,可以跳过安装,直接确认 `openviking-server --help` 可用。 ### 安装 EverOS Memory Gateway 使用当前 EverOS HTTP 服务: ```bash git clone https://github.com/EverMind-AI/EverOS.git /Users/tom/projects/EverOS cd /Users/tom/projects/EverOS # 编辑 .env,至少配置 LLM、multimodal、embedding、rerank 的 model/api_key/base_url。 uv sync --extra dev ``` 如果 EverOS 已经在本机存在,只需要进入 EverOS 根目录并补齐 `.env`。 当前 Memory Gateway 兼容的 EverOS v1 记忆接口是: ```text POST /api/v1/memory/add POST /api/v1/memory/flush POST /api/v1/memory/search POST /api/v1/memory/get ``` ### 安装 Memory Gateway ```bash cd /Users/tom/projects/memory-gateway curl -LsSf https://astral.sh/uv/install.sh | sh export PATH="$HOME/.local/bin:$PATH" uv sync ``` 如果已经安装过 `uv`,只需要运行: ```bash uv sync ``` 如果需要在服务器上跑测试或开发检查,再安装开发依赖: ```bash uv sync --extra dev ``` ## 配置文件 ### OpenViking 配置 Memory Gateway 仓库内提供 OpenViking 配置模板: ```bash mkdir -p ~/.openviking cp /Users/tom/projects/memory-gateway/openviking.ov.conf.example ~/.openviking/ov.conf ``` 然后编辑 `~/.openviking/ov.conf`,至少替换: | 占位符 | 含义 | |---|---| | `` | OpenViking root key;必须和 Memory Gateway `config.yaml` 里的 `openviking.api_key` 一致 | | `` / `` / `` / `` | OpenViking embedding provider 配置 | | `` / `` / `` / `` | OpenViking VLM provider 配置 | | `` / `` / `` / `` | OpenViking rerank provider 配置 | 配置好后先跑一次: ```bash openviking-server doctor ``` `root_api_key` 只放在 OpenViking 和 Memory Gateway 的服务端配置里。业务调用方不要传 root key。 ### EverOS 配置 在 EverOS 根目录创建或编辑 `.env`: ```bash cd /Users/tom/projects/EverOS $EDITOR .env ``` 至少需要配置: ```text EVEROS_LLM__MODEL / EVEROS_LLM__API_KEY / EVEROS_LLM__BASE_URL EVEROS_MULTIMODAL__MODEL / EVEROS_MULTIMODAL__API_KEY / EVEROS_MULTIMODAL__BASE_URL EVEROS_EMBEDDING__MODEL / EVEROS_EMBEDDING__API_KEY / EVEROS_EMBEDDING__BASE_URL EVEROS_RERANK__MODEL / EVEROS_RERANK__API_KEY / EVEROS_RERANK__BASE_URL ``` 大模型较慢时,建议同时设置 `EVEROS_LLM__TIMEOUT_SECONDS` 和 `EVEROS_MULTIMODAL__TIMEOUT_SECONDS`。 ### Memory Gateway 配置 复制 Gateway 配置模板: ```bash cd /Users/tom/projects/memory-gateway cp config.example.yaml config.yaml ``` 然后编辑 `config.yaml`: ```yaml server: host: "127.0.0.1" port: 1934 api_key: "" openviking: url: "http://127.0.0.1:1933" api_key: "" timeout: 30 verify_ssl: true everos: url: "http://127.0.0.1:1995" api_key: "" timeout: 180 verify_ssl: true health_path: "/health" storage: sqlite_path: "./memory_system_api.sqlite3" ``` 环境变量也可以覆盖部分配置: | 环境变量 | 覆盖字段 | |---|---| | `MEMORY_SYSTEM_SERVER_API_KEY` | `server.api_key` | | `MEMORY_SYSTEM_SERVER_HOST` | `server.host` | | `MEMORY_SYSTEM_SERVER_PORT` | `server.port` | | `OPENVIKING_URL` / `OPENVIKING_BASE_URL` | `openviking.url` | | `OPENVIKING_API_KEY` | `openviking.api_key` | | `EVEROS_URL` / `EVEROS_BASE_URL` | `everos.url` | | `EVEROS_API_KEY` | `everos.api_key` | | `MEMORY_SYSTEM_STORAGE_SQLITE_PATH` | `storage.sqlite_path` | ## 启动 按依赖顺序启动三个服务。 终端 1:启动 OpenViking。 ```bash source /Users/tom/.venvs/openviking/bin/activate openviking-server --config ~/.openviking/ov.conf ``` 如果使用 Docker 方式运行 OpenViking,确认对 Memory Gateway 暴露的是 `1933` 端口,避免和 Memory Gateway 的 `1934` 端口冲突。 终端 2:启动 EverOS。 ```bash cd /Users/tom/projects/EverOS uv run everos server start ``` 终端 3:启动 Memory Gateway。 ```bash cd /Users/tom/projects/memory-gateway uv run memory-gateway --config config.yaml --host 0.0.0.0 --port 1934 ``` 健康检查: ```bash curl -sS http://127.0.0.1:1933/health curl -sS http://127.0.0.1:1995/health curl -sS http://127.0.0.1:1934/memory-system/health ``` 如果 `server.api_key` 非空,所有请求还要加: ```bash -H "X-API-Key: " ``` ## API 总览 Base URL: ```text http://127.0.0.1:1934/memory-system ``` 下面的示例默认先设置: ```bash API=http://127.0.0.1:1934/memory-system ``` | 方法 | 路径 | 作用 | User key | |---|---|---|---| | `GET` | `/health` | 检查 OpenViking 和 EverOS 健康状态 | 不需要 | | `POST` | `/users` | 在固定 `admin` 工作区创建 OpenViking user,并保存 user key | 不需要 | | `POST` | `/messages` | 写入一轮或半轮会话消息 | 需要 | | `POST` | `/sessions/{session_id}/commit` | 提交会话,触发 OpenViking commit 和 EverOS flush | 需要 | | `POST` | `/sessions/{session_id}/extract` | 立即触发 OpenViking extract | 需要 | | `GET/POST` | `/sessions/{session_id}/context` | 查询 OpenViking 会话上下文,并用同一 query 搜索 EverOS 记忆 | 需要 | | `GET/POST` | `/openviking/tasks/{task_id}` | 查询 OpenViking 后台任务状态 | 需要 | | `GET` | `/memories` | 列出 OpenViking memory URI | 需要 | | `GET` | `/memories/content` | 读取某条 memory 内容 | 需要 | | `POST` | `/memories` | 创建、覆盖或追加写入 memory | 需要 | | `DELETE` | `/memories` | 删除某条 memory URI | 需要 | | `POST` | `/resources` | 上传本地文件或远程 URL 到 OpenViking resources | 需要 | | `DELETE` | `/resources` | 删除 OpenViking resource URI | 需要 | | `POST` | `/search` | 同时搜索 OpenViking 和 EverOS 记忆 | 需要 | | `GET/POST` | `/users/{user_id}/profile` | 查询 EverOS profile,并补充 OpenViking 用户记忆搜索结果 | 需要 | ## API 详情 ### `GET /health` 检查两个后端是否可访问。 ```bash curl -sS "$API/health" ``` 返回字段: | 字段 | 含义 | |---|---| | `status` | `success`、`partial_success` 或 `failed` | | `backends.openviking` | OpenViking 健康检查结果 | | `backends.everos` | EverOS 健康检查结果 | ### `POST /users` 创建新的 OpenViking admin account,并把返回的 `admin_user_id / user_key` 保存到本地 SQLite。 请求体: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 要创建的用户 ID | 示例: ```bash curl -sS -X POST "$API/users" \ -H "Content-Type: application/json" \ -d '{"user_id":"userA"}' ``` 返回中的 `account.result.user_key` 是后续业务调用要传的 `user_key`: ```json { "status": "success", "account": { "status": "ok", "result": { "account_id": "userA_account", "admin_user_id": "userA", "user_key": "..." } } } ``` ### `POST /messages` 写入用户消息和/或助手消息。至少要传 `user_message` 或 `assistant_message` 其中一个。网关会先确保 OpenViking user/session 存在,再把消息写入 OpenViking session,同时调用 EverOS `POST /api/v1/memory/add`。 请求体: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 已创建的用户 ID | | `user_key` | string | 是 | `/users` 返回的 user key | | `session_id` | string | 是 | 会话 ID | | `user_message` | string/null | 否 | 用户消息 | | `assistant_message` | string/null | 否 | 助手消息 | | `timestamp` | int/null | 否 | 预留字段 | | `metadata` | object | 否 | 预留字段 | 示例: ```bash USER_KEY="..." curl -sS -X POST "$API/messages" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "session_id": "sessionA1", "user_message": "请记住:我喜欢拿铁。", "assistant_message": "好的,我记住了。" }' ``` ### `POST /sessions/{session_id}/commit` 提交会话。OpenViking 会归档 session 并异步抽取长期记忆,通常会返回 `task_id`;EverOS 会调用 `POST /api/v1/memory/flush` flush 当前 session。 路径参数: | 参数 | 类型 | 说明 | |---|---|---| | `session_id` | string | 要提交的会话 ID | 请求体: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 用户 ID | | `user_key` | string | 是 | `/users` 返回的 user key | 示例: ```bash curl -sS -X POST "$API/sessions/sessionA1/commit" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'" }' ``` 如果返回里有 OpenViking `task_id`,需要继续查询任务直到完成。 ### `POST /sessions/{session_id}/extract` 立即触发 OpenViking extract,只作用于 OpenViking,不触发 EverOS flush。适合调试或明确要求“现在就抽取”的场景。 参数同 commit: ```bash curl -sS -X POST "$API/sessions/sessionA1/extract" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'" }' ``` ### `GET/POST /sessions/{session_id}/context` 查询一次会话的当前上下文。网关会调用 OpenViking: ```http GET /api/v1/sessions/{session_id}/context X-API-Key: ``` 同时用同一个 `query` 调用 EverOS `POST /api/v1/memory/search`,请求体使用顶层 `user_id`,并把 `session_id` 放在 `filters.session_id` 中。适合在回答用户问题前,把“当前 session 工作记忆”和“EverOS 相关记忆”一起取回。 路径参数: | 参数 | 类型 | 说明 | |---|---|---| | `session_id` | string | 要查询的会话 ID | 请求体: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 用户 ID | | `user_key` | string | 是 | `/users` 返回的 user key | | `query` | string | 是 | 用于 EverOS search 的查询文本 | | `limit` | int | 否 | EverOS 记忆返回条数,默认 10,范围 1 到 100 | POST 示例: ```bash curl -sS -X POST "$API/sessions/sessionA1/context" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "query": "我喜欢喝什么?", "limit": 10 }' ``` 返回字段: | 字段 | 含义 | |---|---| | `context` | OpenViking `result`,包含 `latest_archive_overview`、`pre_archive_abstracts`、`messages`、`stats` | | `items` | EverOS 搜索命中的精简记忆结果,含 `source_backend: "everos"` | | `backends` | 两个后端的精简诊断信息,不重复返回完整 OpenViking context 或 EverOS 原始大 payload | ### `GET/POST /openviking/tasks/{task_id}` 查询 OpenViking 后台任务状态,例如 commit 返回的任务。 请求体: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 用户 ID | | `user_key` | string | 是 | `/users` 返回的 user key | | `session_id` | string/null | 否 | 会话 ID;传入后会按同一 session identity 查询 | 示例: ```bash curl -sS -X POST "$API/openviking/tasks/${TASK_ID}" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "session_id": "sessionA1" }' ``` ### `GET /memories` 列出 OpenViking memory URI。网关会调用 OpenViking: ```http GET /api/v1/fs/ls?uri=viking://user/memories&recursive=true X-API-Key: ``` Query 参数: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 用户 ID | | `user_key` | string | 是 | `/users` 返回的 user key | | `uri` | string | 否 | 要列出的 memory 根 URI,默认 `viking://user/memories` | | `recursive` | bool | 否 | 是否递归列出,默认 `true` | 示例: ```bash curl -sS -G "$API/memories" \ --data-urlencode "user_id=userA" \ --data-urlencode "user_key=$USER_KEY" \ --data-urlencode "uri=viking://user/memories" \ --data-urlencode "recursive=true" ``` ### `GET /memories/content` 读取某条 memory 内容。先用 `/memories` 或 `/search` 找到 `viking://user/memories/...` URI,再读取: ```bash curl -sS -G "$API/memories/content" \ --data-urlencode "user_id=userA" \ --data-urlencode "user_key=$USER_KEY" \ --data-urlencode "uri=viking://user/memories/preferences/python.md" ``` ### `POST /memories` 创建、覆盖或追加写入 memory。网关会调用 OpenViking `/api/v1/content/write`,写入后由 OpenViking 刷新语义和向量索引。 请求体: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 用户 ID | | `user_key` | string | 是 | `/users` 返回的 user key | | `uri` | string | 是 | 目标 memory URI,例如 `viking://user/userA/memories/preferences/饮食偏好.md` | | `content` | string | 是 | 要写入的 Markdown/text 内容 | | `mode` | `create`/`replace`/`append` | 否 | 写入模式,默认 `create` | | `wait` | bool | 否 | 是否等待索引刷新,默认 `true` | 新增 memory 时传 `mode: "create"`。URI 应直接写到目标分类目录下,不要额外插入 `/user` 目录;例如偏好类 memory 使用 `viking://user/userA/memories/preferences/饮食偏好.md`。 ```bash curl -sS -X POST "$API/memories" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "uri": "viking://user/userA/memories/preferences/饮食偏好.md", "content": "用户喜欢喝茶", "mode": "create", "wait": true }' ``` 覆盖修改时把 `mode` 改为 `replace`: ```bash curl -sS -X POST "$API/memories" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "uri": "viking://user/memories/preferences/python.md", "content": "# Python 偏好\n\n用户偏好使用 Python 做数据分析,常用 pandas。", "mode": "replace", "wait": true }' ``` 追加补充时把 `mode` 改为 `append`。 ### `DELETE /memories` 删除某条 memory。默认非递归删除;如果 OpenViking 提示目标是目录或复合资源,再把 `recursive` 设为 `true`。 ```bash curl -sS -X DELETE -G "$API/memories" \ --data-urlencode "user_id=userA" \ --data-urlencode "user_key=$USER_KEY" \ --data-urlencode "uri=viking://user/memories/preferences/python.md" \ --data-urlencode "recursive=false" ``` 返回中的 `memory` 是 OpenViking 对应接口的原始响应。 ### `POST /resources` 上传文件资源到 OpenViking。网关只调用 OpenViking,不写 EverOS。 如果 `path` 是本地路径,文件必须能被 Memory System API 服务进程读取。网关会先调用 OpenViking `/api/v1/resources/temp_upload` 上传临时文件,取返回的 `temp_file_id`,再调用 `/api/v1/resources` 添加资源。 如果 `path` 是 `http://` 或 `https://` URL,网关会直接调用 OpenViking `/api/v1/resources`,并把 URL 作为 `path` 传给 OpenViking。 请求体: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 用户 ID | | `user_key` | string | 是 | `/users` 返回的 user key | | `path` | string | 是 | 本地文件路径,或 `http://` / `https://` URL | | `to` | string | 是 | 目标 OpenViking resource URI | | `reason` | string/null | 否 | 上传原因,透传给 OpenViking | | `wait` | bool | 否 | 是否等待处理完成,默认 `true` | | `directly_upload_media` | bool | 否 | 是否直接上传媒体,默认 `true` | 本地文件示例: ```bash curl -sS -X POST "$API/resources" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "path": "/home/tom/memory-gateway/tests/大语言模型应用.pdf", "to": "viking://resources/userA/files/大语言模型应用.pdf", "reason": "userA 上传的文件", "wait": true, "directly_upload_media": true }' ``` 远程 URL 示例: ```bash curl -sS -X POST "$API/resources" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "path": "https://example.com/images/photo.png", "to": "viking://resources/userA/images/photo.png", "reason": "userA 上传的远程图片", "wait": true, "directly_upload_media": true }' ``` 返回中的 `resource` 是 OpenViking `/api/v1/resources` 的原始响应,`backends.openviking` 保留调用状态和错误信息。 ### `DELETE /resources` 删除 OpenViking resource URI。网关会调用 OpenViking: ```http DELETE /api/v1/fs?uri=&recursive= X-API-Key: ``` Query 参数: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 用户 ID | | `user_key` | string | 是 | `/users` 返回的 user key | | `uri` | string | 是 | 要删除的 OpenViking URI | | `recursive` | bool | 否 | 是否递归删除,默认 `true` | 示例: ```bash curl -sS -X DELETE -G "$API/resources" \ --data-urlencode "user_id=userA" \ --data-urlencode "user_key=$USER_KEY" \ --data-urlencode "uri=viking://resources/userA/files/大语言模型应用.pdf" \ --data-urlencode "recursive=true" ``` 返回中的 `resource` 是 OpenViking `/api/v1/fs` 删除接口的原始响应。 ### `POST /search` 同时查询 OpenViking 和 EverOS,并合并结果。EverOS 分支调用 `POST /api/v1/memory/search`,请求体形如 `{"user_id": "...", "query": "...", "method": "hybrid|agentic", "top_k": 10, "include_profile": true, "filters": {"session_id": "..."}}`。 请求体: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_id` | string | 是 | 用户 ID | | `user_key` | string | 是 | `/users` 返回的 user key | | `session_id` | string/null | 否 | 会话 ID;用于 EverOS `filters.session_id` 和 OpenViking agent identity | | `query` | string | 是 | 查询文本 | | `use_llm` | bool | 否 | 只影响 EverOS 检索方式:`false` 使用 hybrid,`true` 使用 agentic | | `limit` | int | 否 | 返回条数,默认 10,范围 1 到 100 | | `level` | int | 否 | OpenViking search level,默认 2 | | `score_threshold` | float | 否 | OpenViking 最低分数阈值,默认 0.8,范围 0 到 1 | | `target_uri` | string | 否 | OpenViking 搜索范围,默认 `viking://user/memories` | 示例: ```bash curl -sS -X POST "$API/search" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "session_id": "sessionA1", "query": "我喜欢喝什么?", "use_llm": false, "limit": 10, "level": 2, "score_threshold": 0.8, "target_uri": "viking://user/memories" }' ``` 返回字段: | 字段 | 含义 | |---|---| | `status` | 总体状态 | | `items` | 合并后的记忆结果,含 `source_backend` | | `backends` | 两个后端的精简诊断信息,例如命中数量、query plan 或查询过滤条件;不再回传完整原始命中 | ### `GET/POST /users/{user_id}/profile` 读取用户画像。该接口需要 `user_key`,用于确认调用方属于该 user。网关会调用 EverOS `POST /api/v1/memory/get`,请求体为 `{"user_id": "...", "memory_type": "profile", "page": 1, "page_size": 20}`;同时用同一个 `query` 调 OpenViking `/api/v1/search/search`,固定传 `target_uri: viking://user/memories` 和 `level`。 请求体: | 参数 | 类型 | 必需 | 说明 | |---|---|---:|---| | `user_key` | string | 是 | `/users` 返回的 user key | | `query` | string | 否 | OpenViking 搜索文本,默认 `用户画像` | | `limit` | int | 否 | OpenViking 返回条数,默认 10,范围 1 到 100 | | `level` | int | 否 | OpenViking search level,默认 2 | 示例: ```bash curl -sS -X POST "$API/users/userA/profile" \ -H "Content-Type: application/json" \ -d '{ "user_key": "'"$USER_KEY"'", "query": "我想喝东西", "limit": 10, "level": 2 }' ``` 返回中的 `profile` 是 EverOS profile 原始结果,`items` 是 OpenViking 命中的用户记忆,`backends` 只保留两个后端的状态和计数诊断,不重复返回完整 EverOS profile 或 OpenViking 命中列表。 ## 完整测试流程 ```bash API=http://127.0.0.1:1934/memory-system curl -sS -X POST "$API/users" \ -H "Content-Type: application/json" \ -d '{"user_id":"userA"}' USER_KEY="把上一步返回的 account.result.user_key 填到这里" curl -sS -X POST "$API/messages" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "session_id": "sessionA1", "user_message": "请记住:我在 sessionA1 喜欢拿铁,项目代号 alpha-session。", "assistant_message": "好的。" }' curl -sS -X POST "$API/sessions/sessionA1/commit" \ -H "Content-Type: application/json" \ -d '{"user_id":"userA","user_key":"'"$USER_KEY"'"}' curl -sS -X POST "$API/search" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "session_id": "sessionA1", "query": "我喜欢喝什么?", "limit": 10 }' ``` OpenViking commit 返回 `task_id` 后,轮询到 `completed` 再搜索长期 memory: ```bash curl -sS -X POST "$API/openviking/tasks/" \ -H "Content-Type: application/json" \ -d '{ "user_id": "userA", "user_key": "'"$USER_KEY"'", "session_id": "sessionA1" }' ``` ## 开发检查 ```bash uv run pytest -q uv run python -m compileall -q memory_system_api plugins eval tests ``` ## Hermes Plugin 状态 仓库里仍保留 Hermes memory provider:`plugins/memory/memory_system`。 注意:Memory System API 已切换为 `user_id + user_key` 鉴权,plugin 代码如需继续使用,需要同步传递这两个字段。