Skip to main content
Glama

img.png

recall.select

一个极简的智能体记忆系统 —— 向任意智能体提供一个 URL,它就能获得近乎零配置的长期记忆。基于 Qdrant + FastMCP + FastAPI/Bootstrap 构建。

完整设计及增量构建计划请参见 docs/specs/initial_specification.md,重要变更记录请参见 docs/specs/changelog.md

工作原理

记忆以向量形式存储。每个记忆存储区是一个 Qdrant 集合,与 (用户, 项目)一一映射。这些向量相关的元数据(用户、API 密钥、项目以及每个集合的使用/限制统计)存储在 MongoDB 中。

flowchart LR
    agent[Agent] --> web[FastAPI / MCP]
    web <-->qdrant[Qdrant]
    web <--> mongo[MongoDB]
    web <--> embed[Embedding API]

Qdrant 集合采用惰性创建:在首次将记忆存入 (用户, 项目) 对之前,不会对 Qdrant 进行任何操作。

Related MCP server: LedgerMem MCP Server

架构

  • app/main.py — FastAPI 应用。提供 Bootstrap 着陆页,并在启动时确保 MongoDB 索引存在(容忍冷/远程数据库)。

  • app/mcp_server.py — 记忆链接背后的 MCP 服务器。智能体的 MCP 客户端连接到 {PUBLIC_BASE_URL}/m/{key}(Streamable HTTP,无状态,JSON 响应);路径中的 API 密钥是完整的凭证,并将工具范围限定为密钥所有者的默认项目。基本工具:store_memory / recall_memory / delete_memory。语义层工具(参见 vector_semantics.py):link_memories / unlink_memories / annotate_memory / memory_connections / recall_connected — 连接的智能体在客户端进行关系推理(仅在明确请求时),这些工具负责接收或遍历结果。相同的密钥也可作为 Authorization: Bearer 发送到无密钥的 /mcp 端点,从而将密钥隐藏在 URL/日志之外。{...}/m/{key}.md(位于 app/api/connect.py)提供相应的设置说明(两种形式)。

  • app/dependencies.py — 核心 DI 容器(injector)。构建共享单例(Qdrant 客户端、Mongo 客户端/数据库、远程嵌入器)。FastAPI 依赖项(app/api/deps.py)和启动逻辑从 app_container 解析,而不是自行构建客户端。

  • app/services/ — 服务层(无 HTTP/路由代码,仅 I/O):

    • qdrant_store.py — Qdrant 客户端 + ensure_collection / upsert_memory / search / delete_memory,以及语义层所需的点级原语(neighbors, scroll_points, retrieve_points, set_payload)。

    • vector_semantics.py — 向量记忆工具层:将存储区视为意义图。每个点载荷中保留一个 _semantics 命名空间,用于存放指示词锚点(所有者、存储时间;在存储时写入)、客户端提取的实体以及客户端声明的类型化关系(upsert_relations 进行验证并存储 —— 服务端不调用 LLM)。声明的关系带有两个质量约束:confidence(0-1],缩放边的遍历强度)和 valid_till(ISO 8601;过期的边被所有读取路径忽略,因此陈旧结构会自动退役)。卫生机制:remove_relations 删除错误的边(upsert_relations 的纠正对应项),memory.delete_memory 调用 prune_relations_to 以确保删除记忆后没有悬空边。 可插拔的透镜topical / temporal / entity / declared)推导类型化边;在此基础上构建 semantic_graph(多重图)、spreading_activation(按连接检索)、concept_clusters(涌现本体)和 infer_relation(声明真理优先,几何启发式次之)。性能说明:入边查找(relations_of(include_incoming=True))目前是有限滚动扫描。如果反向遍历成为热点,解决方法是 Qdrant 载荷索引_semantics.relations[].targetcreate_payload_index,关键字模式)和过滤查询替代扫描 —— 同一存储区,仅加索引;模式无需更改。

    • mongo.py — Mongo 客户端,get_db()ensure_indexes()(通过唯一复合索引强制执行 (用户, 项目) 的一对一规则)。

    • users.pyadd_user, get_user, get_user_by_email, update_user

    • api_keys.py — 用户绑定的密钥,以 SHA-256 哈希值存储(明文在 add_api_key 时返回一次,此后从不持久化):add_api_key, delete_api_key, delete_user_keys, list_api_keys, get_labeled_key, get_by_key(对提供的令牌进行哈希,并与摘要匹配;MCP 认证门上的 record_use=True 会标记 last_used_at)。静态存储的每个密钥还保留非秘密显示提示 —— key_prefix + key_last4,通过 masked() 呈现为 rs_ab12…wxyz —— 因此可以列出和区分密钥,而无需重新暴露密钥本身。

    • projects.pyadd_project, get_project, list_projects, update_project, delete_project

    • collections.py(用户, 项目) ↔ Qdrant 集合 注册表。collection_name(user_id, project_id) 是内部命名标准(rs_{user}_{project});跟踪 points_count / calls_count 用于限制和统计。

    • collection_provisioning.py — 双面 create_collection / destroy_collection 步骤。集合仅在它的 MongoDB 注册行其支持的 Qdrant 集合都存在时才存在;此函数将 collections 注册表与 qdrant_store 组合成一个原子、幂等的操作,使两个存储区永不脱节。创建是惰性的,因此唯一的创建调用者是首次记忆写入(memory.store_memory);集合 API 的删除使用 destroy_collection

    • embeddings.pyEmbedder 抽象;embeddings_remote.py — 具体的文本→向量后端(远程嵌入 API,例如 DeepInfra)。

    • monobank.py — 极简的 Monobank 收单客户端(create_invoice, fetch_invoice_status)以及 Webhook 认证(fetch_pubkey / verify_signature,ECDSA-SHA256 对原始主体签名)。复用 mcp-api.net 商户令牌;recall.select 拥有自己的发票/重定向/Webhook。

    • billing.py — 计划目录和以 Monobank 的 invoiceId 为键的付款记录。结账时 record_pendingapply_webhooksuccess一次性将买家的 tier 翻转(对重试/重复具有幂等性);reconcile 处理 Webhook 遗漏的情况(见下文)。层级是有时间限制的grant_tier 是权力发放的唯一入口(付费发票或所有者善意),写入 tier_expires_at 以及 tier_grants 中的审计行;effective_tier(user) 是所有检查必须读取的内容,因为存储的 paid_2x 如果日期已过,则属于免费账户。同时,这是每个层级配额的单一事实来源:call_allowance(tier) / project_allowance(tier)None = 无限;未知层级回退到免费)。

    • usage.py — 月度调用计量器和价格模型门控。每个接受的 store/recall/delete 操作都会计入每个 (用户, 日历月份)usage 行;check_call_allowed 一旦层级月度 call_allowance 耗尽,便会拒绝调用,抛出 QuotaExceeded。在 memory.py 中实施(因此 MCP 工具和 HTTP 记忆 API 均被覆盖),并在 app/main.py 映射为 HTTP 429;MCP 传输将其作为工具错误呈现。与全部时间的 collections.calls_count 分开。

    • account.py — 已登录 /account 页面显示的只读快照(计划、月度使用量、每个项目存储计数以及带创建/上次使用日期的掩码 API 密钥列表),由 billing / usage / projects / collections / api_keys 组合而成。

    • docs.py — 公共 /docs 集成指南的内容。在单个位置构建 MCP 客户端配置(mcp_config / mcp_config_json),文档页面 app/api/connect.py 的每个密钥 .md 均复用,因此两者永不漂移。INTEGRATIONS 是指南注册表(通过添加条目来添加页面)。

公共页面(由 app/main.py 提供,Bootstrap + Jinja,通过 app/translations/*.yml 实现国际化):/ 着陆页、/plans/account(已登录)以及 /docs/integrations 指南。FastAPI 的内置 API 文档已从 /docs 移至 /api/docs/api/redoc/api/openapi.json),以便公共站点拥有 /docs

支付在 HTTP 层处理,位于 app/api/payments.pyPOST /api/me/checkout(已登录)创建发票并返回 Monobank pay_url;已验证的 POST /webhooks/monobank 授予层级;GET /payment/success|fail 是装饰性浏览器返回页面(权利由 Webhook 驱动,绝不依赖这些页面)。

权利不单独依赖 Webhook。 Monobank 每个状态变化发送一次,且从不重发,因此若发生重启或代理故障导致回调丢失,已付款的客户将停留在旧层级,而我们这边无法察觉。因此,应用还会进行拉取:每 PAYMENT_RECONCILE_MINUTES 一次后台扫掠(billing.reconcile_with_monobank,在 app/main.py 生命周期中启动)获取每个仍在等待状态超过五分钟的付款的真实状态,并通过相同的 apply_webhook 转换进行推送。推送和拉取彼此幂等 —— 先到达的授予层级,另一个是空操作。payments 中的行在结账开始时写入,因此 created 表示“打开付款页面”,而非“已付款”;/admin/payments 明确显示该区别。

一次购买持续一个月SUBSCRIPTION_DAYS),而非永久:grant_tier 标记 tier_expires_at,同一个后台循环将过期的账户降级为免费(downgrade_expired),账户页面显示计划到期日期。目前没有自动续订 —— 用户需要再次购买,而在信用期内购买会延长窗口期,而非重新开始。权利通过 effective_tier 读取,因此即使扫掠尚未重写存储字段,过期的授权也会立即停止支付。

由于没有自动续订,应用会主动询问billing.renewal_state(user)/account 上驱动提示 —— 在计划最后 RENEWAL_WARNING_DAYS(7)内显示带一键续订按钮的警告,并在之后 LAPSED_PROMPT_DAYS(30)内显示“您的计划已结束,请续订”提示(降级扫掠记录 lapsed_tier / tier_lapsed_at,以便页面仍能显示已过期的计划)。续订请求发送到计划页面使用的同一个 /api/me/checkout,并预设为其持有的计划。目前没有邮件 —— 提示仅触达访问页面的用户。

每个 CRUD 函数都接受可选的 db= / client= 参数,以便在无需实时后端的情况下在测试中驱动。

配置

通过环境变量设置(本地 .env 会自动加载;切勿提交 —— 参见 .env.example):

变量

默认值

用途

MONGODB_URI

(必填)

远程托管的 MongoDB 连接字符串。

MONGODB_DB

recall_select

数据库名称。

QDRANT_URL

http://qdrant:6333

Qdrant 端点(内部 Compose 网络)。

QDRANT_API_KEY

(本地无;生产环境必填)

应用与 Qdrant 之间的共享密钥。Compose 从该变量设置 Qdrant 的 QDRANT__SERVICE__API_KEY,应用在每次请求中发送该密钥。它是 qdrant.recall.select 仪表盘唯一的访问控制(该仪表盘本身无认证)。

VECTOR_SIZE

768

每个集合的向量维度。远程嵌入器通过 API 的 dimensions 参数被要求返回恰好此大小的向量,以确保两者同步。

EMBEDDING_API_KEY

(必填)

远程嵌入 API 的 API 密钥。

EMBEDDING_BASE_URL

https://api.deepinfra.com/v1

兼容 OpenAI 的嵌入 API 基础 URL。

GOOGLE_CLIENT_ID

(登录必填)

Google OAuth 2.0 Web 客户端 ID。

GOOGLE_CLIENT_SECRET

(登录必填)

Google OAuth 2.0 客户端密钥。

SESSION_SECRET

(开发环境回退)

用于签名会话 Cookie。生产环境请设置一个稳定的值。

PUBLIC_BASE_URL

http://localhost:8000

公共源;用于构建记忆链接和 OAuth 重定向 URI。

FORWARDED_ALLOW_IPS

172.25.0.0/16(Compose)/ 127.0.0.1(uvicorn)

uvicorn 信任的 X-Forwarded-Proto/-For 来源。Compose 默认设置为 caddy_net 子网,以便重定向保持 https 方案,日志显示真实客户端 IP;如果该网络被重建,请使用 docker network inspect caddy_net 检查。

MONOBANK_API_KEY

(支付必填)

Monobank 收单商户令牌。与 mcp-api.net 平台共享——同一商户,同一账户;通过 reference 区分发票。

MONOBANK_REDIRECT_URL

{PUBLIC_BASE_URL}/payment/success

购物者支付后浏览器返回的地址。

MONOBANK_WEBHOOK_URL

{PUBLIC_BASE_URL}/webhooks/monobank

服务器到服务器的回调,用于授予层级。必须可公开访问。

MONOBANK_WEBHOOK_VERIFY

1

验证 webhook 的 X-Sign 与商户公钥。涉及资金流动时请保持开启;仅本地开发时可设为 0

PAYMENT_RECONCILE_MINUTES

15

从 Monobank 拉取进行中支付真实状态的频率,防止丢失 webhook 导致付费客户无法使用。设为 0 可禁用轮询。

ADMIN_SECRET

(未设置 - 区域禁用)

解锁位于 /admin 的所有者管理区域。未设置时,所有 /admin 路由返回 404。

ADMIN_SESSION_HOURS

12

解锁的管理员会话在重新锁定前持续的时间。

所有者管理区域(/admin

一个只读窗口,可查看任何用户的个人区域,用于支持和了解用户所见内容。设置 ADMIN_SECRET(生成方式:python -c "import secrets; print(secrets.token_urlsafe(32))"),重新创建 Web 容器,然后打开 {PUBLIC_BASE_URL}/admin 并在每个会话中输入一次密钥。/admin/users 列出所有账户——可按邮箱、姓名或用户 ID 搜索——每行可打开该用户的套餐、当前周期使用量、项目及其记忆数量,以及掩码形式的记忆链接。

边界是经过设计的:密钥通过 POST 提交(绝不作为 URL 参数,因此不会出现在历史记录和访问日志中),重复错误猜测会锁定客户端五分钟,会话在 ADMIN_SESSION_HOURS 后自动重新锁定,并且此处的任何路由都不会写入数据或泄露记忆文本或密钥机密——所有者看到的是账户的轮廓,而非内容。当 ADMIN_SECRET 未设置时,该区域根本不存在。

认证(Google 登录)

登录是记忆链接的入口:用户通过 Google 登录,然后点击 复制记忆链接 来配置其默认项目 + 集合 + API 密钥,并获取供代理使用的 URL。密钥仅显示一次(仅存储其哈希值):之后,登录页面显示掩码形式的链接(通过 GET /api/me/link),按钮变为明确的、需确认的“获取新链接”——重新生成会使旧链接失效,且不会静默执行。密钥在 /account 上管理:掩码列表、创建/最后使用日期、带标签创建(仅显示一次)、以及撤销。设置 Google 凭据的步骤:

  1. Google Cloud 控制台 → API 和服务 → OAuth 同意屏幕 - 进行配置(外部;在未验证状态下添加你的邮箱作为测试用户)。

  2. 凭据 → 创建凭据 → OAuth 客户端 ID → Web 应用程序。

  3. 添加一个已授权的重定向 URI{PUBLIC_BASE_URL}/auth/callback - 例如本地开发使用 http://localhost:8000/auth/callback,生产环境使用 https://recall.select/auth/callback(如果本地测试,两者都添加)。

  4. 客户端 ID客户端密钥复制到 .env 文件中(GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET),并设置一个稳定的 SESSION_SECRETpython -c "import secrets; print(secrets.token_urlsafe(48))")。

本地运行

通过 Docker Compose 运行完整栈(Web + Qdrant):

cp .env.example .env   # then fill in MONGODB_URI
docker compose up --build
# open http://localhost:8000

或者仅运行应用,使用你自己的 Qdrant/Mongo:

pip install -e ".[dev]"
uvicorn app.main:app --reload

测试

pip install -e ".[dev]"
pytest

CRUD 测试针对内存中的 Mongo(mongomock)运行,Qdrant/嵌入客户端为模拟实现——无需实时后端。

部署

./deploy/deploy.sh

同一命令可在两个位置运行——它会检测运行位置:

  • 从开发机器(或代理的机器):推送本地提交,然后通过 recall-server SSH 别名在服务器上运行部署。

  • 在服务器本身上setti@setti-server:~/recall_select$ ./deploy/deploy.sh):原地部署,无需 SSH 跳转。

两种路径都运行同一个工作脚本——deploy/_server_deploy.shgit 同步 master 分支,重建 Compose 栈(FastAPI web + Qdrant),重新加载共享的 Caddy 代理(为 recall.select 自动 HTTPS),清理旧镜像。MongoDB 是远程托管的,因此服务器上必须存在 auth/MONGODB_URI 环境变量(参见 .env)。

在服务器上运行该脚本的用户需要对仓库具有 GitHub 拉取权限~/.ssh 中有授权的 SSH 密钥)并且是 docker 组的成员——claude-agentsetti 都满足这些条件。工作脚本会自动将仓库注册为 git 的 safe.directory,这样非仓库所有者的部署者不会被“可疑所有权”阻止。

自动部署(CI)

每次推送到 master 分支都会通过 GitHub Actions 自动部署(.github/workflows/deploy.yml)——流程与上述相同,只是由 CI 触发而非人工。该作业通过 SSH 连接到服务器,并将 deploy/_server_deploy.sh 通过标准输入管道传输,因此它运行的是推送提交自身的部署逻辑。部署是串行化的(concurrency),并且运行工作流按钮(workflow_dispatch)允许你按需部署。

一次性设置——在 Settings → Secrets and variables → Actions 下添加:

密钥

必填

用途

DEPLOY_SSH_KEY

私钥,其公钥部分位于部署用户的 ~/.ssh/authorized_keys 中。

DEPLOY_HOST / DEPLOY_USER

服务器地址和要部署的 SSH 用户。

DEPLOY_PORT

SSH 端口(默认 22)。

DEPLOY_KNOWN_HOSTS

固定服务器主机密钥;如果未设置,CI 通过 ssh-keyscan 在首次使用时信任该主机。

应用密钥(MONGODB_URI、OAuth 等)保留在服务器的 .env 文件中——CI 永远不会看到它们。

许可证

根据 GNU Affero General Public License v3.0 许可。如果你以网络服务形式运行修改版本,AGPL 要求你向用户提供其源代码。版权所有 © 2026 Sergii Setti。

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides persistent, local-first AI memory across sessions via MCP tools for storing, searching, and retrieving context from past interactions.
    1
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Provides persistent memory for AI agents via 10 MCP tools that map to the AgentRAM REST API, enabling store, retrieve, search, and share memories across personal and shared namespaces.
    10
    191
    MIT

View all related MCP servers

Related MCP Connectors

  • Shared, governed long-term memory for AI agents across tools and sessions via MCP and REST.

  • Shared long-term memory vault for AI agents with 20 MCP tools.

  • Your memory, everywhere AI goes. Build knowledge once, access it via MCP anywhere.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SergeySetti/recall_select'

If you have feedback or need assistance with the MCP directory API, please join our Discord server