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 不匹配,messagescommitextracttaskssearchprofile 都会返回 401

创建业务 user 时,网关直接为该用户创建一个独立 OpenViking account。默认 account_id<user_id>_accountadmin_user_id 是传入的 user_id

POST /api/v1/admin/accounts
{"account_id": "<user_id>_account", "admin_user_id": "<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: <openviking root key>
普通用户 API例如 session/message/commit/task/search X-API-Key: <openviking user key>

Session 与搜索范围

OpenViking session 由请求里的 session_id 创建和提交。/memory-system/search 的 OpenViking 分支固定调用 OpenViking /api/v1/search/searchtarget_uri 可选,默认是 viking://user/memories,并默认传 level: 2score_threshold: 0.8

安装

cd /home/tom/memory-gateway
python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e ".[dev]"

配置

复制配置模板:

cp config.example.yaml config.yaml

主要配置:

server:
  host: "127.0.0.1"
  port: 1934
  api_key: ""

openviking:
  url: "http://127.0.0.1:1933"
  api_key: "your-secret-root-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: "/home/tom/memory-gateway/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

启动

先启动 OpenViking 和 EverOS再启动 Memory System API

python -m memory_system_api.server --config config.yaml --host 0.0.0.0 --port 1934

如果 server.api_key 非空,所有请求还要加:

-H "X-API-Key: <memory-system-api-key>"

API 总览

Base URL

http://127.0.0.1:1934/memory-system

下面的示例默认先设置:

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 后台任务状态 需要
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

检查两个后端是否可访问。

curl -sS "$API/health"

返回字段:

字段 含义
status successpartial_successfailed
backends.openviking OpenViking 健康检查结果
backends.everos EverOS 健康检查结果

POST /users

创建新的 OpenViking admin account并把返回的 admin_user_id / user_key 保存到本地 SQLite。

请求体:

参数 类型 必需 说明
user_id string 要创建的用户 ID

示例:

curl -sS -X POST "$API/users" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"userA"}'

返回中的 account.result.user_key 是后续业务调用要传的 user_key

{
  "status": "success",
  "account": {
    "status": "ok",
    "result": {
      "account_id": "userA_account",
      "admin_user_id": "userA",
      "user_key": "..."
    }
  }
}

POST /messages

写入用户消息和/或助手消息。至少要传 user_messageassistant_message 其中一个。网关会先确保 OpenViking user/session 存在,再把消息写入 OpenViking session同时写入 EverOS。

请求体:

参数 类型 必需 说明
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 预留字段

示例:

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_idEverOS 会 flush 当前 session。

路径参数:

参数 类型 说明
session_id string 要提交的会话 ID

请求体:

参数 类型 必需 说明
user_id string 用户 ID
user_key string /users 返回的 user key

示例:

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

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

GET /api/v1/sessions/{session_id}/context
X-API-Key: <user_key>

同时用同一个 query 调用 EverOS /api/v1/memories/search,返回相关 episodic/profile/raw message 记忆。适合在回答用户问题前,把“当前 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 示例:

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_overviewpre_archive_abstractsmessagesstats
items EverOS 搜索命中的精简记忆结果,含 source_backend: "everos"
backends 两个后端的精简诊断信息,不重复返回完整 OpenViking context 或 EverOS original_data

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 查询

示例:

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"
  }'

POST /resources

上传文件资源到 OpenViking。网关只调用 OpenViking不写 EverOS。

如果 path 是本地路径,文件必须能被 Memory System API 服务进程读取。网关会先调用 OpenViking /api/v1/resources/temp_upload 上传临时文件,取返回的 temp_file_id,再调用 /api/v1/resources 添加资源。

如果 pathhttp://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

本地文件示例:

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 示例:

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

DELETE /api/v1/fs?uri=<OPENVIKING_URI>&recursive=<true|false>
X-API-Key: <user_key>

Query 参数:

参数 类型 必需 说明
user_id string 用户 ID
user_key string /users 返回的 user key
uri string 要删除的 OpenViking URI
recursive bool 是否递归删除,默认 true

示例:

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并合并结果。

请求体:

参数 类型 必需 说明
user_id string 用户 ID
user_key string /users 返回的 user key
session_id string/null 会话 ID用于 EverOS 过滤和鉴权身份
query string 查询文本
use_llm bool 只影响 EverOS 检索方式:false 使用 hybridtrue 使用 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

示例:

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 或查询过滤条件;不再回传完整原始命中和 original_data

GET/POST /users/{user_id}/profile

读取用户画像。该接口需要 user_key,用于确认调用方属于该 user。网关会读取 EverOS profile并用同一个 query 调 OpenViking /api/v1/search/search,固定传 target_uri: viking://user/memorieslevel

请求体:

参数 类型 必需 说明
user_key string /users 返回的 user key
query string OpenViking 搜索文本,默认 用户画像
limit int OpenViking 返回条数,默认 10范围 1 到 100
level int OpenViking search level默认 2

示例:

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 命中列表。

完整测试流程

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

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"
  }'

开发检查

PYTHONPATH=/home/tom/memory-gateway pytest -q
python -m compileall -q memory_system_api plugins eval tests

Hermes Plugin 状态

仓库里仍保留 Hermes memory providerplugins/memory/memory_system

注意Memory System API 已切换为 user_id + user_key 鉴权plugin 代码如需继续使用,需要同步传递这两个字段。

Description
No description provided
Readme 4 MiB
Languages
Python 100%